2015년 11월 19일 목요일

Spring에서 Redis 서버 설정

이번 포스팅에서는 Spring 에서 Redis를 이용하기 위한 설정에 대해서 설명한다.

 Redis 설치는 아래의 사이트를 참고하자.

레디스 공식 홈 : http://redis.io/download

Redis를 처음 설치하면 redis cli를 사용하기 위해서 bash_profile에 다음과 같이 등록해주자.




제대로 설치가 됐는지 확인하기 위해 먼저 Redis 서버를 실행하자.

실행명령은 다음과 같다.
[solrslave@localhost redis-3.0.3]$ redis-server 




위와 같이 스탠드 얼론 모드로 실행됐다는 메시지가 나오면 정상적으로 실행된 것이다.

Cli 인터페이스를 이용하여 접속해보자.
(별도의 conf 파일을 지정하지 않으면 6379 포트가 기본 포트)

[solrslave@localhost redis-3.0.3]$ redis-cli -p 8000 

정상적으로 접속되면 문제 없이 설치된 것이다.

Spring Tool Suite 도구를 열고 Spring MVC로 간단한 Project를 생성한다. 


Maven Pom 파일에 아래의 라이브러리를 추가

  <dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>2.4.0</version>
  </dependency>
  <!-- spring data for jedis -->
  <dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-redis</artifactId>
   <version>1.2.1.RELEASE</version>
  </dependency>

 
Redis xml configuration 파일을 추가
<!-- JDBC 템플릿 설정과 유사하다. -->
 <bean id="connectionFactory"
  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  <property name="hostName" value="192.168.0.105" />
  <property name="port" value="8000" />
  <property name="poolConfig" ref="jedisPoolConfig"></property>
 </bean>
 <!-- 여기서는 일반적인 RedisTemplate을 사용한다. key value가 String 인 경우 String Redis Template을 사용하자. -->
 <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
 </bean>
 <!-- 기존의 database connection 관련 Factory 클래스와 유사하게 connection pool 이 내부에 구현되어 있다. 참고하자. -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
     <property name="maxTotal" value="20"></property>
    </bean>

이제 Spring에서 Redis를 사용할 준비가 완료되었다.

다음 포스팅에서 사용방법을 확인해보자.



2015년 11월 18일 수요일

Spring AOP + AspectJ annotation + Redis 샘플 #2

이번 포스팅에서는 예제에서 사용한 Redis 설정에 대해서 기술한다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">

 <bean id="connectionFactory"
  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  <property name="hostName" value="#{redisProp['hostName']}" />
  <property name="port" value="#{redisProp['port']}" />
  <property name="poolConfig" ref="jedisPoolConfig"></property>
 </bean>
 <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
  <property name="connectionFactory" ref="connectionFactory" />
 </bean>

 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  <property name="maxTotal" value="20"></property>
 </bean>
 <context:component-scan base-package="com.example.redis" />
 <util:properties id="redisProp" location="classpath:/properties.xml" >
 </util:properties>
</beans>

Util: properties 와 SpEL 사용방법
http://secondmemory.kr/271
Resources경로에 아래의 xml 프로퍼티를 추가하자.



 192.168.0.105
 8000

Method 정보를 저장하는 Dto 생성
package com.example.redis;

public class RankDto {
 private int rank;
 private String methodName;
 private int callCount;

 public int getRank() {
  return this.rank;
 }

 public void setRank(int rank) {
  this.rank = rank;
 }

 public String getMethodName() {
  return this.methodName;
 }

 public void setMethodName(String methodName) {
  this.methodName = methodName;
 }

 public int getCallCount() {
  return this.callCount;
 }

 public void setCallCount(int callCount) {
  this.callCount = callCount;
 }
}

호출되는 함수 Dto를 저장할 Repository 인터페이스
public abstract interface ActionHistoryRepo<Key, Value> {
 public abstract void addAction(Value paramValue);

 public abstract List<RankDto> getRank();

 public abstract void deleteAction(Key paramKey);
}

Repository 인터페이스의 구현 클래스
이 예제에서는 실행되는 함수의 실행 횟수를 저장하고 통계를 내기 위해서 Redis Zset 타입을 이용하여 구현한다.
ZSet 사용법은 아래의 사이트를 참고하자
http://www.tutorialspoint.com/redis/redis_sorted_sets.htm
@Repository("actionHistory")
public class HistorySaver implements ActionHistoryRepo<String, String> {
       //Redis ZSet을 사용하기 위한 고정키
 private final String redisKey = "MethodRank";
 
 @Autowired
 private RedisTemplate<String, String> stringRedisTemplate;

 public void addAction(String value) {
  int score = 1;
     // 개체가 없으면 자동으로 생성 한 후 스코어에 +1         
  this.stringRedisTemplate.opsForZSet().incrementScore("MethodRank",
    value, score);
 }

 public List<RankDto> getRank() {
                //Range를 사용하면 내림차순이 기본으로 설정된다. Reverse Range를 사용하여 오름차순 순서로 뽑아내자.
  Set<String> rankset = this.stringRedisTemplate.opsForZSet()
    .reverseRange(redisKey , 0L, -1L);
  List<RankDto> rankList = new ArrayList();

  Iterator<String> iters = rankset.iterator();
  int ranky = 1;
  while (iters.hasNext()) {
   RankDto dto = new RankDto();
   dto.setMethodName((String) iters.next());

   Double rank = this.stringRedisTemplate.opsForZSet().score(
     redisKey , dto.getMethodName());
   dto.setCallCount(rank.intValue());
   dto.setRank(ranky);
   rankList.add(dto);
   ranky++;
  }
  return rankList;
 }

 public void deleteAction(String key) {
  this.stringRedisTemplate.delete(key);
 }
}
구현된 결과물을 Tomcat was를 이용해서 실행해 보자.
http://localhost:8080/aop/act1

http://localhost:8080/aop/rank

소스코드 주소는 다음과 같다.
https://github.com/wargen99/wargen-repo.git

아주 간단하게 AOP를 이용하여 사이트 내의 서비스 메소드가 호출되는 통계를 저장하는 예제를 만들어 보았다.
아주 기초적인 예제지만 이를 응용하여 다양한 관점에서 데이터를 저장하고 이를 재활용할 수 있는 점을 확인하면 좋을 듯 하다.

Spring AOP + AspectJ annotation + Redis 샘플 #1



이 샘플 코드는 Redis 저장소를 이용하여 web app을 이용할 때 app 에서 제공하는 API 호출의 횟수 별 순위를 확인하는 간단한 예제이다.






AOP + RedisTemplate을 이용하여 구현하였다.


기본 AOP 어노테이션을 확인하자.
@Before – 지켜보고 있는 메소드의 실행 전
@After – 메소드의 결과 값이 리턴된 후
@AfterReturning – 2번 어노테이션과 동일하나 정상적으로 실행됐을 때만 동작.
@AfterThrowing – 오류가 났을 경우 동작
@Around – Proceed 호출 시점을 기준으로 앞과 뒤에서 동작


프로젝트는 spring mvc 프로젝트로 시작한다.

   org.springframework
   spring-aop
   ${org.springframework-version}
  
  
   org.aspectj
   aspectjrt
   1.6.11
  

  
   org.aspectj
   aspectjweaver
   1.6.11
  
  
  
   redis.clients
   jedis
   2.4.0
  
  
  
   org.springframework.data
   spring-data-redis
   1.2.1.RELEASE
  
  
   com.google.code.gson
   gson
   2.4
  
관점의 대상이 되는 인터페이스 및 상속 클래스 구현
public abstract interface Action {
 public abstract void doAct();

 public abstract void doActString(String paramString);

 public abstract void doActWithParam(String paramString, int paramInt);
}
@Component
public class ActImpl implements Action {
 public void doAct() {
  System.out.println("do Act");
 }

 public void doActString(String actParam) {
  System.out.println(actParam + " do act");
 }

 public void doActWithParam(String actParam, int actnum) {
  System.out.println(actParam + actnum + " do act");
 }
}
AspectJ , AOP xml 선언
 
 // AspectJ 기반으로 AOP 구동 시 필요
 
 
 

 
 
  
  
 
 
 
 
Aspect Class 구현 종류별로 필요한 관점을 모두 테스트 해볼 것을 추천한다.
@Aspect
public class ActionAspect {
        // action의 호출 횟수를 저장하는 저장소 주입
 @Autowired
 private ActionHistoryRepo saver;
        
        // AspectJ로 do로 시작하는 모든 메소드를 감시
 @Before("execution(* com.example.aop.ActImpl.do*(..))")
 public void actBefore(JoinPoint joinpoint) {
                //호출된 함수의 시그네처를 확인 후 저장
  String signature = joinpoint.getSignature().toShortString();
  System.out.println("captured before");
  this.saver.addAction(signature);
 }

 @After("execution(* com.example.aop.ActImpl.do*(..))")
 public void actAfter(JoinPoint joinpoint) {
 }
}

이 예제는 AOP 를 사용 시에 기존의 로그인 체크나 권한 체크 외에도 다양한 용도로 사용할 수 있다는 것을 확인 하고 싶어서 만든 예제이다.
횡단 관심사의 분리와 관점을 기준으로 상상력을 발휘할 수 있어야 하지 않을까 싶다.
 참고 : http://www.mkyong.com/spring3/spring-aop-aspectj-annotation-example/