티스토리 뷰
저번 시간에는 FeginClient를 활용한 Error를 처리하는 과정을 거쳤습니다.
다만 Error가 발생할경우 커스텀을 통해서 저희가 원하는 값을 내고 싶을 땐 어떻게 해야할까요?
그럴때 사용하는 것이 ErrorDecoder입니다.
#개요
따로 추가해야할 Dependency는 없습니다. Feign 라이브러리가 해당 인터페이스를 이미 제공하고 있으니까요!, 이번 포스트에서는, ErrorDecoder를 활용해서 특정 에러가 발생하면 특정 동작이 실행되도록 코드를 작성하려 합니다.
#FeignErrorDecoder.java
ErrorDecoder를 작성하기에 앞서, 먼저
error 패키지 생성 후 FeignErrorDecoder라는 클래스를 생성합니다.
FeignErrorDecoder는 ErrorDecoder를 Implements하는 클래스입니다.
package com.example.userservice.error;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()){
case 400:
break;
case 404:
if(methodKey.contains("getOrders")){
return new ResponseStatusException(HttpStatus.valueOf(response.status()),
"User's orders is empty.");
}
break;
default:
return new Exception(response.reason());
}
return null;
}
}
404 Error같은 경우에는 저희가 Exception처리를 해줬었죠? 만약 methodKey에 getOrders가 포함이 된다면, User's orders is empty 를 출력해, 주문정보를 불러올 수 없음을 출력하도록 설계가 되어있습니다.
#UserServiceApplication.java
등록된 Decoder를 Application에 @Bean을 통해 등록해줍니다.
package com.example.userservice;
import com.example.userservice.error.FeignErrorDecoder;
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){return new RestTemplate();}
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
@Bean
public FeignErrorDecoder getFeignErrorDecoder(){
return new FeignErrorDecoder();
}
}
#UserServiceImpl
마지막으로 UserServiceImpl를 다음과 같이 변경해줍니다.
@Override
public UserDto getUserByUserId(String userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
if (userEntity == null )
throw new UsernameNotFoundException("User not found");
UserDto userDto = new ModelMapper().map(userEntity,UserDto.class);
String orderUrl = String.format(env.getProperty("order_service.url"),userId);
/* ErrorDecoder */
List<ResponseOrder> orderList =orderServiceClient.getOrders(userId);
userDto.setOrders(orderList);
return userDto;
}
끝이에요. 정말로요! @Bean에 등록한 시점부터 404에러가 뜨면 자동으로 FeignErrorDecoder에 영향을 받습니다.
정말 그런지 확인 해볼까요?
#TEST 1
( 회원 등록, 로그인 , 주문등록은 미리 진행 한 뒤에 테스트 해주세요. )
저희가 입력한 Message가 정상출력 됩니다!
#user-service.yml
언제나 그렇지만, env.getProperty를 통해 활용 할 수 있는 값은 yml파일을 통해 뺴주도록 합시다.
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
username: sa
password: '{cipher}AQA3Wlcwu01k0p9/hdLhSv5L/WKKo452yh576T+I/AV5kiNckUu6PBPdmetCB0zecb7AkoktLfqZPt6ziznW3slrKHkIb39vyQjFcn0wKTdp6t38e84uoQbWp5SH7BXsT3pTYrmSwPGDvgr3K3NgPAnfZ0zOntmjR/ImO1YetawqFD3OQUqVxMYB93l6z6lG92vSbZpRRp2tJ9lke1u6zeV+vG3tEeFuezk3nb/r8dk8SGtRjru41tUD/g0fQ9R3Rf/i/0Sly9eN9Q2NAAVJ1Be6ohOMexth1oSQoeKQqmwPVCXxwglpDWvntxfanmsK5oavLeOeglkoXFg9JWVst2A9hJHPke6jyTabzuiz+kv3gHcNrih9bJ9RrEifJ4Xl2a8='
token:
expiration_time: 864000000
secret: '{cipher}AQAIOA+faH9XEcX5usyT6e+7ahXOvdmcZtZHMHQyDznJQ21eXzGq11eXqWh7F8/zo2xywBQhSmCz3YbyT181IvPhUB9djzSiYyndedY/k1XX/1lr8a1eZ5QRJbQeuAawgPmeZpr2xb+/7egZw2uUZtzSZKS7xZHh9cXV218sAWteTOKP9Y0AzWuz0ZiV7CJhK9OuZvTBShIlF3Hfy1Umxdmy2AwSqNIEndH8RQer17CaI7fF/sdj41v6YECyL39jP5143aaUyxTt/qgw2RDgh9WrDNdH4LwavG4Pl7+9ob5PDcpZekmScPsXFouJTjGYaX4ICTk04FhoOgxt9ZSB0lBdruNTgzdEaDTE519BaF1EY8noii/4mQDDTIfkX/Gz4oZfHcz6JwhDHeHgKFmxRc4v'
gateway:
ip: 0.0.0.0
order_service:
url: http://ORDER-SERVICE/order-service/%s/orders
exception:
order_is_empty: User's orders is empty.
message 부분을 yml에 등록해줍니다.
#FeignErrorDecoder.java
그럼 Yml파일의 환경정보를 가져오기 위해서 먼저 Envirment를 등록해줍니다. @Autowired를 통해서 생성자를 통한 주입도 잊지 말아주세요. 그리고 해당 클래스는 @Componet를 통해 컴포넌트로 취급할 예정입니다. 그럼 이제 env.getProperty 를 통해서 yml파일에 있는 설정정보를 가져올수 있습니다!
package com.example.userservice.error;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
@Component
public class FeignErrorDecoder implements ErrorDecoder {
Environment env;
@Autowired
public FeignErrorDecoder(Environment env){
this.env = env;
}
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()){
case 400:
break;
case 404:
if(methodKey.contains("getOrders")){
return new ResponseStatusException(HttpStatus.valueOf(response.status()),
env.getProperty("order_service.exception.order_is_empty"));
}
break;
default:
return new Exception(response.reason());
}
return null;
}
}
하지만 여기까지 진행하시면 아마 관련 클래스에서 오류가 발생하게 됩니다.
#UserServiceApplication.java
해당 오류가 발생하는 것은 생성자에 입력인자와 @Bean으로 등록한 생성자의 입력인자가 달라서인데, 이미 @Component로 등록되어 있으니 해당 @Bean은 삭제처리 해줍니다.
package com.example.userservice;
import com.example.userservice.error.FeignErrorDecoder;
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){return new RestTemplate();}
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
그러면 오류가 해결이 됩니다! 야호
#TEST 2
마지막으로 다시 테스트를 해볼게요.
일단 Message를 보니 정상적으로 값을 받아온 것 같습니다. 해당 값은 yml파일을 참조하고 있으니, 값 변경을 통해서 yml파일로 바꾸면 좋은이유를 여기서 다시 보여드릴 수 있겟네요
user-service.yml의 값을 다음과 같이 바꿨습니다.
서비스를 재시작하지 않았으니 Spring-Cloud-Bus를 활용해 Refresh 해봅시다!
http://localhost:8000/user-service/actuator/busrefresh
그럼 다시 주문정보를 확인해볼까요?
잘 바뀐 것을 확인 할 수 있습니다.
마치며..
이번 시간에는 ErrorDecoder를 활용해서 에러 메시지를 커스텀 하는 시간이었습니다.이와 동시에 다시한번 Yml파일을 사용해서 env로 값을 가져오면 서비스를 재시작 할 필요없이, 간단하게 값을 바꿀 수 있어 다시한번 장점을 느낄 수 있었네요.
감사합니다
'웹 프로그래밍 > MSA 학개론' 카테고리의 다른 글
[MSA] Apache Kafka란? Kafka 개념 핥아먹기 (0) | 2022.04.27 |
---|---|
[MSA] 잠시 쉬어가는 MSA - 데이터 동기화 문제- (0) | 2022.04.27 |
[MSA] FeignClient의 예외처리 (0) | 2022.04.27 |
[MSA] FeignClient 를 활용한 Micro Service 간의 통신 (0) | 2022.04.27 |
[MSA] RestTemplate를 활용한 MSA 서비스간의 통신 - 1 - (0) | 2022.04.27 |
- Total
- Today
- Yesterday
- prometheus
- zipkin
- elasticSearch
- JWT
- github
- docker
- Kafka Connect
- 운동일기
- MariaDB
- 운동
- producer
- consumer
- Feign
- Logstash to ElasticSearch
- Spring + ELK
- 미래의나에게동기부여
- 루틴기록
- git
- UserService
- MSA
- Gateway
- 오늘저녁 삼겹살
- ACTUATOR
- kafka
- config
- springcloud
- LoadBalancer
- 빅-오
- Logstash 활용
- rabbitmq
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |