Spring Cloud로 개발하는 마이크로서비스 3

Seung jun Cha·2022년 7월 11일

1. 설정정보의 암호화

1-1 대칭키를 이용한 암호화

  • encryption 할 때와 decryption 할 때 같은 키를 사용
  • text값을 127.0.0.1:8888/encrypt로 post하면 text내용이 encrypt된 랜덤 값으로 나옴
    encrypt 된 값을 127.0.0.1:8888/decrypt로 post하면 원래의 text 내용이 나옴
  1. 서비스의 yml에 설정되어 있던 database 정보를 config service에
    해당 서비스와 관련된 별도의 yml을 만들어서 따로 입력

  2. bootstrap.yml에 encrypt.key설정
    -> 지금은 bootstrap.yml을 지원하지 않음
    따라서 application.properties 또는 application.yml 에
    spring.config.import=optional:configserver:http://localhost:8888 속성을 추가해주는 것으로 대신한다

spring:

  config:

    import: optional:configserver: http://localhost:8888
  1. Micro service에 입력되어 있던 database 정보를 지우고 config service의 yml에 옮긴다. => micro service의 yml에 설정된 spring.cloud.config.urispring.cloud.config.name 을 보고, 해당하는 config를 cloud config에서 가지고 옴
 datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem://localhost/~/test;MODE=MYSQL
    username: sa
    password: 1234 -> 1234를 '{cipher} encrypt 값' 으로 변경
    (작음 따옴표도 같이)
  1. password인 1234를 127.0.0.1:8888/encrypt로 post하면 encrypt 된 text가 나옴. {cipher} 값을 password에 입력
    -> {cipher} 은 암호화 된 값임을 명시

1-2 비대칭키를 이용한 암호화

  • encryption 할 때와 decryption 할 때 다른 키를 사용
    -> private Key, public Key 생성 (JDK Keytool 이용)
$ mkdir ${user.home}/Desktop/Work/keystore

$ keytool -genkeypair -alias apiEncryptionKey -keyalg RSA \

         -dname "CN=Kenneth Lee, OU=API Development, O=joneconsulting.co.kr, L=Seoul, C=KR" \

         -keypass "1q2w3e4r" -keystore apiEncryptionKey.jks -storepass "1q2w3e4r"

2. 마이크로서비스 간의 통신

2-1 Rest Template

  • Rest Template 은 통신하는 다른 서비스를 명시할 수 있음
   @Bean
   @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    
    
     @Override
    public UserDto getUserByUserId(String userId) {
     User user= userRepository.findByUserId(userId);

        ModelMapper modelMapper = new ModelMapper();
        UserDto userDto = modelMapper.map(user, UserDto.class);

        List<ResponseOrder> orders = new ArrayList<>();

        String orderUrl = "http://127.0.0.1:8000/order-service/%s/orders";
		=>order-service 컨트롤러에서 User의 Id로 주문정보를 가져오는 url , 
 //  @GetMapping("/{userId}/orders")
 // public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId)
        
       order url은 cloud config에 설정하는 것이 좋음
->order_service.url: http://order-service(마이크로서비스 이름)/order-service/%s/orders
 
 =>String orderUrl = String.format(env.getProperty("order_service.url"), userId);
        

        
        ResponseEntity<List<ResponseOrder>> orderListResponse = restTemplate.exchange(orderUrl, HttpMethod.GET, null, new ParameterizedTypeReference<List<ResponseOrder>>() {
        });

        List<ResponseOrder> orderList = orderListResponse.getBody();
        userDto.setOrders(orderList);

        return userDto;
    }

2-2 FeignClient

  • Rest call을 추상화 한 Spring cloud NetFlix 라이브러리
  • 단점 : 각각의 마이크로서비스 로직을 파악하고 있어야 사용이나 이해가 용이하다.
  1. spring-cloud-starter-feign 의존성 사용
  2. 메인 클래스에 @EnableFeignClients 사용
  3. 인터페이스에 @FeignClient(name = ) 선언
  4. 지금의 경우 user-service와 oredr-service 간 연결을 하려고 함
    통신하려고 하는 메서드의 Mapping와 반환값을 고려해서 메서드를 생성
 - user-service 애플리케이션에 인터페이스로 생성

@FeignClient(name = "order-service")
public interface OrderServiceClient {

    @GetMapping("/order-service/{userId}/orders")
    public List<ResponseOrder> getOrders(@PathVariable String userId);
    
    
- user-service의 serviceImpl
 @Override
    public UserDto getUserByUserId(String userId) {
     List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
     => OrderServiceClient 인터페이스에 있는 Mapping 정보를 사용해서 값을 가져옴
}

2-2-1 FeignException 처리

  • ErrorDecoder를 이용한 예외처리 : 모든 에러를 FeignException로 처리
- user-service의 yml( @FeignClient가 있는 서비스)

logging:
	level:
    	com.example.userservice.client: DEBUG
        
        
  - 빈으로 등록
  @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
    
  @Bean
    public FeignErrorDecoder getFeignErrorDecoder(){
        return new FeignErrorDecoder();
    }
    
    
- 따로 클래스를 만들어서 에러처리
@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")) {
                // 404에러가 발생했는데 메서드 이름에 getOrders가 포함된 경우 작동
                
                    return new ResponseStatusException(HttpStatus.valueOf(response.status()),
                            env.getProperty("order_service.exception.orders_is_empty"));
                } // cloud config에 설정된 에러메시지를 가져옴
                break;
            default:
                return new Exception(response.reason());
        }

        return null;
    }
}
  • FeignErrorDecoder는 @FeignClient와 관계된 메서드에 에러가 발생했을 때 자동으로 사용되므로, 따로 의존성 주입을 할 필요 없다.

3. 데이터 동기화

  • 같은 서비스가 두 개 이상 실행될 때, 데이터도 각 서비스의 DB별로 데이터가 분산되어 저장된다. 따라서 같은 사용자가 데이터를 저장해도, 여러 개의 DB에 데이터가 나누어 저장될 수 있다.
  1. 하나의 데이터베이스 사용
  2. 데이터베이스 간의 동기화
    => Kafka + DB

3-1 Apache Kafka

  • Scalar언어로 된 오픈 소스 메시지 브로커
    (브로커는 하나의 서비스에서 다른 서비스로 메시지를 전달할 때 사용)

  • Broker ID와 Controller ID 등 메타 데이터를 저장하는 ZooKeeper와 연동되어 있으며 여러 개의 브로커 중 1대는 브로커에게 파티션을 할당하고 모니터링 하는 controller 기능을 수행한다

  • 실시간 데이터 피드를 관리하기 위해 통일된 높은 처리량, 낮은 지연 시간을 가진 플랫폼 제공

  • RabbitMQ도 같은 기능을 하지만 데이터 용량과 안정성이 훨씬 앞선다

  • 기동 순서

1. Zookeeper 서버 기동
bin/zookeeper-server-start.sh config/zookeeper.properties (메인포트 2181)
2. kafka 서버 기동
bin/kafka-server-start.sh config/server.properties (메인포트 9092)
3. topic 생성
bin/kafka-topics.sh create topic (토픽이름)
4. 메시지 생산
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic quickstart-events(토픽 이름)
5. 메시지 소비
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092(메시지를 가지고 올 서버) --topic quickstart-events--from-beginning(모든 메시지를 처음부터 얻어오는 옵션)
-> producer에서 메시지를 생산하면 topic으로 넘어가고 topic에 저장된 메시지를 consumer가 자동으로 가져감

3-1-1 Kafka Connect

  • 코드없이 Configuration으로 데이터를 DB간에 import, export 가능하게 만드는 기능
    Restful API를 통해 지원, Stream 또는 Batch 형태로 데이터 전송 가능

    Connect Source가 데이터를 가져오고 Sink가 전달

Order -> Catalog 동기화

0개의 댓글