diff --git a/README.md b/README.md index ca689f6..900e482 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,34 @@ springCloudBus -- 메시지 발행/수신 확인 [POST] http://localhost:8090/member/assu +-- 레디스 설치확인 +C:\Users\ju>netstat -an|findstr 6379 + TCP 127.0.0.1:6379 0.0.0.0:0 LISTENING +C:\Users\ju>redis-cli +127.0.0.1:6379> ping +PONG +127.0.0.1:6379> set key value +OK +127.0.0.1:6379> get key +"value" +127.0.0.1:6379> + +-- 레디스 실행 +redis-server.bat 실행 + +-- 모든 키 확인 +127.0.0.1:6379> keys * +1) "member" + +-- key-value 확인 (get 은 String 만 다루므로 여기선 에러로 표시) +127.0.0.1:6379> get member +(error) WRONGTYPE Operation against a key holding the wrong kind of value + +-- 모든 키 삭제 +127.0.0.1:6379> flushall +OK + + ``` diff --git a/event-service/src/main/java/com/assu/cloud/eventservice/EventServiceApplication.java b/event-service/src/main/java/com/assu/cloud/eventservice/EventServiceApplication.java index 7de67e1..524103f 100644 --- a/event-service/src/main/java/com/assu/cloud/eventservice/EventServiceApplication.java +++ b/event-service/src/main/java/com/assu/cloud/eventservice/EventServiceApplication.java @@ -67,7 +67,6 @@ public class EventServiceApplication { /** * 레디스 서버에 작업 수행 시 사용할 RedisTemplate 객체 생성 - * @return */ @Bean public RedisTemplate redisTemplate() { diff --git a/event-service/src/main/java/com/assu/cloud/eventservice/client/MemberCacheRestTemplateClient.java b/event-service/src/main/java/com/assu/cloud/eventservice/client/MemberCacheRestTemplateClient.java new file mode 100644 index 0000000..a9ae88e --- /dev/null +++ b/event-service/src/main/java/com/assu/cloud/eventservice/client/MemberCacheRestTemplateClient.java @@ -0,0 +1,89 @@ +package com.assu.cloud.eventservice.client; + +import com.assu.cloud.eventservice.config.CustomConfig; +import com.assu.cloud.eventservice.model.Member; +import com.assu.cloud.eventservice.repository.MemberRedisRepository; +import com.assu.cloud.eventservice.utils.CustomContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +/** + * 회원 데이터 필요 시 회원 서비스 호출 전 레디스 캐시 먼저 확인 + */ +@Component +public class MemberCacheRestTemplateClient { + + private static final Logger logger = LoggerFactory.getLogger(MemberCacheRestTemplateClient.class); + + private final RestTemplate restTemplate; + private final MemberRedisRepository memberRedisRepository; + private final CustomConfig customConfig; + + public MemberCacheRestTemplateClient(RestTemplate restTemplate, MemberRedisRepository memberRedisRepository, CustomConfig customConfig) { + this.restTemplate = restTemplate; + this.memberRedisRepository = memberRedisRepository; + this.customConfig = customConfig; + } + + String URL_PREFIX = "/api/mb/member/"; // 회원 서비스의 주울 라우팅경로와 회원 클래스 주소 + + /** + * 회원 아이디로 레디스에 저장된 Member 클래스 조회 + */ + private Member checkRedisCache(String userId) { + try { + return memberRedisRepository.findMember(userId); + } catch (Exception e) { + logger.error("======= Error encountered while trying to retrieve member {} check Redis Cache., Exception {}", userId, e); + return null; + } + } + + /** + * 레디스 캐시에 데이터 저장 + */ + private void cacheMemberObject(Member member) { + try { + memberRedisRepository.saveMember(member); + } catch (Exception e) { + e.printStackTrace(); + logger.error("======= Unable to cache member {} in Redis. Exception {}", member.getId(), e); + } + } + + public Member getMember(String userId) { + + Member member = checkRedisCache(userId); + + // 레디스에 데이터가 없다면 원본 데이터에서 데이터를 조회하기 위해 회원 서비스 호출 + if (member != null) { + logger.debug("======= Successfully retrieved an Member {} from the redis cache: {}", userId, member); + return member; + } + + logger.debug("======= Unable to locate member from the redis cache: {}", userId); + + ResponseEntity restExchange = + restTemplate.exchange( + "http://" + customConfig.getServiceIdZuul() + URL_PREFIX + "{userId}", // http://localhost:5555/api/mb/member/userInfo/rinda + HttpMethod.GET, + null, + Member.class, + userId + ); + + // 캐시 레코드 저장 + member = restExchange.getBody(); + + // 조회한 객체를 캐시에 저장 + if (member != null) { + cacheMemberObject(member); + } + + return member; + } +} diff --git a/event-service/src/main/java/com/assu/cloud/eventservice/controller/EventController.java b/event-service/src/main/java/com/assu/cloud/eventservice/controller/EventController.java index aaede61..04f1fa5 100644 --- a/event-service/src/main/java/com/assu/cloud/eventservice/controller/EventController.java +++ b/event-service/src/main/java/com/assu/cloud/eventservice/controller/EventController.java @@ -1,8 +1,10 @@ package com.assu.cloud.eventservice.controller; +import com.assu.cloud.eventservice.client.MemberCacheRestTemplateClient; import com.assu.cloud.eventservice.client.MemberRestTemplateClient; import com.assu.cloud.eventservice.client.MemberFeignClient; import com.assu.cloud.eventservice.config.CustomConfig; +import com.assu.cloud.eventservice.model.Member; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -15,11 +17,14 @@ public class EventController { private final CustomConfig customConfig; private final MemberFeignClient memberFeignClient; private final MemberRestTemplateClient memberRestTemplateClient; + private final MemberCacheRestTemplateClient memberCacheRestTemplateClient; - public EventController(CustomConfig customConfig, MemberFeignClient memberFeignClient, MemberRestTemplateClient memberRestTemplateClient) { + public EventController(CustomConfig customConfig, MemberFeignClient memberFeignClient, MemberRestTemplateClient memberRestTemplateClient, + MemberCacheRestTemplateClient memberCacheRestTemplateClient) { this.customConfig = customConfig; this.memberFeignClient = memberFeignClient; this.memberRestTemplateClient = memberRestTemplateClient; + this.memberCacheRestTemplateClient = memberCacheRestTemplateClient; } @GetMapping(value = "name/{nick}") @@ -49,6 +54,14 @@ public class EventController { return "[EVENT] Gift is " + gift; } + /** + * 레디스 캐싱 데이터 사용 + */ + @GetMapping(value = "{userId}") + public Member userInfo(@PathVariable("userId") String userId) { + return memberCacheRestTemplateClient.getMember(userId); + } + /*@GetMapping("userInfo/{name}") public String userInfo(@PathVariable("name") String name) { return "[EVENT-MEMBER] " + memberRestTemplateClient.userInfo(name); diff --git a/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepository.java b/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepository.java index d6e1d9e..86f89a7 100644 --- a/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepository.java +++ b/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepository.java @@ -2,6 +2,9 @@ package com.assu.cloud.eventservice.repository; import com.assu.cloud.eventservice.model.Member; +/** + * 레디스에 액세스해야 하는 클래스에 주입된 인터페이스 + */ public interface MemberRedisRepository { void saveMember(Member member); void updateMember(Member member); diff --git a/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepositoryImpl.java b/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepositoryImpl.java index b2e0b4a..4d87d01 100644 --- a/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepositoryImpl.java +++ b/event-service/src/main/java/com/assu/cloud/eventservice/repository/MemberRedisRepositoryImpl.java @@ -1,30 +1,38 @@ package com.assu.cloud.eventservice.repository; import com.assu.cloud.eventservice.model.Member; +import com.netflix.discovery.converters.Auto; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Repository; import javax.annotation.PostConstruct; +/** + * 부트스트랩 클래스에서 정의한 RedisTemplate 빈을 사용하여 레디스 서버와 통신 + */ @Repository public class MemberRedisRepositoryImpl implements MemberRedisRepository { private static final String HASH_NAME = "member"; // 회원 데이터가 저장되는 레디스 서버의 해시명 - private RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; private HashOperations hashOperations; // HashOperation 클래스는 레디스 서버에 데이터 작업을 수행하는 스프링 헬퍼 메서드의 집합 - public MemberRedisRepositoryImpl() { - super(); - } - - public MemberRedisRepositoryImpl(RedisTemplate redisTemplate) { + public MemberRedisRepositoryImpl(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @PostConstruct public void init() { hashOperations = redisTemplate.opsForHash(); + + // 키와 값을 명시적으로 직렬화해주지 않으면 default serializer 로 JdkSerializationRedisSerializer 를 사용하는데 + // 그러면 \xac\xed\x00\x05t\x00\x06member 이런 식으로 저장됨 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); } @Override diff --git a/event-service/src/main/resources/application.yaml b/event-service/src/main/resources/application.yaml index 0c87706..060de7e 100644 --- a/event-service/src/main/resources/application.yaml +++ b/event-service/src/main/resources/application.yaml @@ -15,5 +15,5 @@ spring: brokers: localhost #redis redis: - server: redis + server: localhost port: 6379 \ No newline at end of file diff --git a/member-service/src/main/java/com/assu/cloud/memberservice/controller/MemberController.java b/member-service/src/main/java/com/assu/cloud/memberservice/controller/MemberController.java index 9698651..e9e9a21 100644 --- a/member-service/src/main/java/com/assu/cloud/memberservice/controller/MemberController.java +++ b/member-service/src/main/java/com/assu/cloud/memberservice/controller/MemberController.java @@ -3,6 +3,9 @@ package com.assu.cloud.memberservice.controller; import com.assu.cloud.memberservice.client.EventRestTemplateClient; import com.assu.cloud.memberservice.config.CustomConfig; import com.assu.cloud.memberservice.event.source.SimpleSourceBean; +import com.assu.cloud.memberservice.model.Member; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import javax.servlet.ServletRequest; @@ -11,6 +14,8 @@ import javax.servlet.ServletRequest; @RequestMapping("/member") public class MemberController { + private static final Logger logger = LoggerFactory.getLogger(MemberController.class); + private final CustomConfig customConfig; private final EventRestTemplateClient eventRestTemplateClient; private final SimpleSourceBean simpleSourceBean; @@ -60,4 +65,17 @@ public class MemberController { simpleSourceBean.publishMemberChange("SAVE", userId); } + /** + * 이벤트 서비스에서 캐시 용도로 회원 데이터 조회 + */ + @GetMapping("{userId}") + public Member userInfoCache(@PathVariable("userId") String userId) { + logger.debug("====== 회원 서비스 호출!"); + + // DB 를 조회하여 회원 데이터 조회 (간편성을 위해 아래와 같이 리턴함) + Member member = new Member(); + member.setId(userId); + member.setName("rinda"); + return member; + } } diff --git a/member-service/src/main/java/com/assu/cloud/memberservice/model/Member.java b/member-service/src/main/java/com/assu/cloud/memberservice/model/Member.java new file mode 100644 index 0000000..94a7bdf --- /dev/null +++ b/member-service/src/main/java/com/assu/cloud/memberservice/model/Member.java @@ -0,0 +1,24 @@ +package com.assu.cloud.memberservice.model; + +import java.io.Serializable; + +public class Member implements Serializable { + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +}