MSAのトラブルシューティングと分散トレース
👏障害処理(Circuit Breaker)
order-serivce
リクエストが現在実行されていない場合、getOrders()の呼び出し中にエラーが発生し、user-service
から500個のエラーが返されます.ある意味でgetOrders()メソッドは間違っているようですが、500は正しいのですが、1つのサービスが停止したためにすべてのサービスを停止することはできませんか?そう思ってもいいです.そこで、Microservice間の呼び出しでエラーが発生した場合の対処法をCircuitBreakerで知りたいと思います.
🔨応用Resilientce 4 J <!-- Resilientce4J -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
依存性を追加します.@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService{
private final UserRepository userRepository;
private final BCryptPasswordEncoder pwdEncoder;
//private final RestTemplate restTemplate;
private final OrderServiceClient orderServiceClient; //FeignClient의 interface 받기
private final Environment env;
private final CircuitBreakerFactory circuitBreakerFactory;
...
@Override
public UserDto getUserByUserId(String userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
if(userEntity == null) throw new UsernameNotFoundException("user name not found!");
UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);
// List<ResponseOrder> orderList = new ArrayList<>(); //이전에 빈 배열을 반환하던 값
/* Using as RestTemplate */
// 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();
/* Using as FeignClient */
//List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
/* FeignClient exception handling*/
// List<ResponseOrder> orderList = null;
// try {
// orderList = orderServiceClient.getOrders(userId);
// }catch (FeignException e){
// log.error(e.getMessage());
// }
/* 기존 getOrders() */
//List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker");
List<ResponseOrder> orderList = circuitBreaker.run(() ->
orderServiceClient.getOrders(userId),
throwable -> new ArrayList<>() //Error가 발생할 경우 비어있는 리스트를 반환
);
userDto.setOrders(orderList);
return userDto;
}
}
circuitBreakerを使用してコードを変更し、次のようにサーバ再要求を実行します.
サービス自体をエラーとしてマークするよりも、空の配列値に戻して、正常に返されていることを確認することもできます.
ログにはエラーログも表示されます.
🔨カスタムCircuitBreaker
デバッガとしてCircuitBreakerを直接使用することもできますが、リクエスト回数やリクエスト待ち時間など、ユーザーが自分で使用方法を変更することもできます.@Configuration
public class Resilience4JConfig {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> customCinfiguration(){
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(4) // 100번중 4번만 실패해도 실행
.waitDurationInOpenState(Duration.ofMillis(1000)) //1초 동안 circuitbreaker 사용
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) //횟수를 기준
.slidingWindowSize(2) //2개의 데이터를 저장
.build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(4)) //4초 대기
.build();
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(timeLimiterConfig)
.circuitBreakerConfig(circuitBreakerConfig)
.build()
);
}
}
このようにしてプロファイルを作成し、beanとして登録して使用できます.あまりカスタマイズされていない場合は、私のプロジェクトでそれを使用しますか?そう思います.
Customizerをインポートする場合は、Resilience 4 Jパッケージからインポートする必要があります!
オーバーシュートトレース(Sleuth+Zipkin)
👉Zipkinのインストール
https://zipkin.io/pages/quickstart.htmlホームページに移動して、自分でインストールしやすい方法でインストールすればいいです.git clone https://github.com/openzipkin/zipkin
cd zipkin
# Build the server and also make its dependencies
./mvnw -DskipTests --also-make -pl zipkin-server clean install
# Run the server
java -jar .\zipkin-server\target\zipkin-server-2.23.17-SNAPSHOT-exec.jar
curl-sslコマンドは聞こえなかったのでgitに直接インストールしました.しかも、gitでインストールするのにもっと時間がかかります!!!ううう
インストールされたバージョンに応じてzipkin jarファイル名を変更するには、適切な変更やコマンドで名前を変更するだけです.
正常に動作すると、次の画面が表示され、9411ポートで動作します.
これは正常に動作している画面です.
👉Zipkin設定
user-service
で設定<!-- sleuth -->
<!-- sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
まず依存項目を追加します.spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0 #전달할 log의 퍼센트 1.0 -- 100퍼센터
設定を追加します. log.info("Before Call Order Service");
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker");
List<ResponseOrder> orderList = circuitBreaker.run(() ->
orderServiceClient.getOrders(userId),
throwable -> new ArrayList<>() //Error가 발생할 경우 비어있는 리스트를 반환
);
log.info("After Call Order Service");
次に、UserServiceImplでfaignClientのセクションを実行し、ログを次のように記録して表示しやすくします.order-service
で設定<!-- sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
依存性を追加します.spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0 #전달할 log의 퍼센트 1.0 -- 100퍼센터
設定がそっくりです.package com.example.orderservice.controller;
import com.example.orderservice.dto.OrderDto;
import com.example.orderservice.jpa.OrderEntity;
import com.example.orderservice.messagequeue.KafkaOrderProducer;
import com.example.orderservice.messagequeue.KafkaProducer;
import com.example.orderservice.service.OrderService;
import com.example.orderservice.vo.RequestOrder;
import com.example.orderservice.vo.ResponseOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/order-service")
@RequiredArgsConstructor
@Slf4j
public class OrderController {
private final Environment env;
private final OrderService orderService;
private final KafkaProducer kafkaProducer; //kafka producer 주입
private final KafkaOrderProducer kafkaOrderProducer; //주문 전송 producer 주입
...
@PostMapping("/{userId}/orders")
public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder requestOrder){
log.info("Before Add Order Data");
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
//기존의 jpa 로직
OrderDto orderDto = mapper.map(requestOrder, OrderDto.class);
orderDto.setUserId(userId);
OrderDto createOrder = orderService.createOrder(orderDto);
ResponseOrder responseOrder = mapper.map(createOrder, ResponseOrder.class);
// OrderDto orderDto = mapper.map(requestOrder, OrderDto.class);
// orderDto.setUserId(userId);
//kafka 주문 로직 추가
// orderDto.setOrderId(UUID.randomUUID().toString());
// orderDto.setTotalPrice(requestOrder.getQty() * requestOrder.getUnitPrice());
//kafka 메세지 전송
// kafkaProducer.orderSend("example-catalog-topic", orderDto);
// kafkaOrderProducer.orderSend("orders", orderDto);
// ResponseOrder responseOrder = mapper.map(orderDto, ResponseOrder.class);
log.info("After Added Order Data");
return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
}
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId){
log.info("Before Retrieve Order Data");
Iterable<OrderEntity> orderList = orderService.getOrderByUserId(userId);
List<ResponseOrder> result = new ArrayList<>();
orderList.forEach(v -> {
result.add(new ModelMapper().map(v, ResponseOrder.class));
});
log.info("After Receive Order Data");
return ResponseEntity.ok().body(result);
}
}
サービスにもlogを追加します.
注文を登録します
次のようにtrace idとspan idを表示する必要があります.trace id
というzipkinで追跡する場合、ユーザの1回の要求単位をtrace idと呼ぶ.span id
は、span idと呼ばれるユーザの1回の要求単位内部の追加要求のmicroservice単位である.
その後、ユーザー情報を注文のURLにロードすると、結果が表示されます.
trace idとspan idの違いがわかりますが、現在のtrace idはuser-serviceで146472b7356d64ef
値、order-serviceでも
同じ値であることを確認できます.
zipkinでも処理の過程を確認できます.
Find a traceでサービス名を検索することもできます.
また、定期的に検索して、サービスが正常に通信しているかどうかを確認することもできます.
故意にエラーが発生した場合、エラーは赤で表示して確認することができます.
zipkinサーバによって格納されたデータ履歴を理解し、スキップし、springbootのログデータをsleuthライブラリで処理します.
Reference
この問題について(MSAのトラブルシューティングと分散トレース), 我々は、より多くの情報をここで見つけました
https://velog.io/@ililil9482/MSA-장애-처리와-분산-추척
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
<!-- Resilientce4J -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
依存性を追加します.@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService{
private final UserRepository userRepository;
private final BCryptPasswordEncoder pwdEncoder;
//private final RestTemplate restTemplate;
private final OrderServiceClient orderServiceClient; //FeignClient의 interface 받기
private final Environment env;
private final CircuitBreakerFactory circuitBreakerFactory;
...
@Override
public UserDto getUserByUserId(String userId) {
UserEntity userEntity = userRepository.findByUserId(userId);
if(userEntity == null) throw new UsernameNotFoundException("user name not found!");
UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);
// List<ResponseOrder> orderList = new ArrayList<>(); //이전에 빈 배열을 반환하던 값
/* Using as RestTemplate */
// 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();
/* Using as FeignClient */
//List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
/* FeignClient exception handling*/
// List<ResponseOrder> orderList = null;
// try {
// orderList = orderServiceClient.getOrders(userId);
// }catch (FeignException e){
// log.error(e.getMessage());
// }
/* 기존 getOrders() */
//List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker");
List<ResponseOrder> orderList = circuitBreaker.run(() ->
orderServiceClient.getOrders(userId),
throwable -> new ArrayList<>() //Error가 발생할 경우 비어있는 리스트를 반환
);
userDto.setOrders(orderList);
return userDto;
}
}
circuitBreakerを使用してコードを変更し、次のようにサーバ再要求を実行します.サービス自体をエラーとしてマークするよりも、空の配列値に戻して、正常に返されていることを確認することもできます.
ログにはエラーログも表示されます.
🔨カスタムCircuitBreaker
デバッガとしてCircuitBreakerを直接使用することもできますが、リクエスト回数やリクエスト待ち時間など、ユーザーが自分で使用方法を変更することもできます.@Configuration
public class Resilience4JConfig {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> customCinfiguration(){
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(4) // 100번중 4번만 실패해도 실행
.waitDurationInOpenState(Duration.ofMillis(1000)) //1초 동안 circuitbreaker 사용
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) //횟수를 기준
.slidingWindowSize(2) //2개의 데이터를 저장
.build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(4)) //4초 대기
.build();
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(timeLimiterConfig)
.circuitBreakerConfig(circuitBreakerConfig)
.build()
);
}
}
このようにしてプロファイルを作成し、beanとして登録して使用できます.あまりカスタマイズされていない場合は、私のプロジェクトでそれを使用しますか?そう思います.
Customizerをインポートする場合は、Resilience 4 Jパッケージからインポートする必要があります!
オーバーシュートトレース(Sleuth+Zipkin)
👉Zipkinのインストール
https://zipkin.io/pages/quickstart.htmlホームページに移動して、自分でインストールしやすい方法でインストールすればいいです.git clone https://github.com/openzipkin/zipkin
cd zipkin
# Build the server and also make its dependencies
./mvnw -DskipTests --also-make -pl zipkin-server clean install
# Run the server
java -jar .\zipkin-server\target\zipkin-server-2.23.17-SNAPSHOT-exec.jar
curl-sslコマンドは聞こえなかったのでgitに直接インストールしました.しかも、gitでインストールするのにもっと時間がかかります!!!ううう
インストールされたバージョンに応じてzipkin jarファイル名を変更するには、適切な変更やコマンドで名前を変更するだけです.
正常に動作すると、次の画面が表示され、9411ポートで動作します.
これは正常に動作している画面です.
👉Zipkin設定
user-service
で設定<!-- sleuth -->
<!-- sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
まず依存項目を追加します.spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0 #전달할 log의 퍼센트 1.0 -- 100퍼센터
設定を追加します. log.info("Before Call Order Service");
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker");
List<ResponseOrder> orderList = circuitBreaker.run(() ->
orderServiceClient.getOrders(userId),
throwable -> new ArrayList<>() //Error가 발생할 경우 비어있는 리스트를 반환
);
log.info("After Call Order Service");
次に、UserServiceImplでfaignClientのセクションを実行し、ログを次のように記録して表示しやすくします.order-service
で設定<!-- sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
依存性を追加します.spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0 #전달할 log의 퍼센트 1.0 -- 100퍼센터
設定がそっくりです.package com.example.orderservice.controller;
import com.example.orderservice.dto.OrderDto;
import com.example.orderservice.jpa.OrderEntity;
import com.example.orderservice.messagequeue.KafkaOrderProducer;
import com.example.orderservice.messagequeue.KafkaProducer;
import com.example.orderservice.service.OrderService;
import com.example.orderservice.vo.RequestOrder;
import com.example.orderservice.vo.ResponseOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/order-service")
@RequiredArgsConstructor
@Slf4j
public class OrderController {
private final Environment env;
private final OrderService orderService;
private final KafkaProducer kafkaProducer; //kafka producer 주입
private final KafkaOrderProducer kafkaOrderProducer; //주문 전송 producer 주입
...
@PostMapping("/{userId}/orders")
public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder requestOrder){
log.info("Before Add Order Data");
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
//기존의 jpa 로직
OrderDto orderDto = mapper.map(requestOrder, OrderDto.class);
orderDto.setUserId(userId);
OrderDto createOrder = orderService.createOrder(orderDto);
ResponseOrder responseOrder = mapper.map(createOrder, ResponseOrder.class);
// OrderDto orderDto = mapper.map(requestOrder, OrderDto.class);
// orderDto.setUserId(userId);
//kafka 주문 로직 추가
// orderDto.setOrderId(UUID.randomUUID().toString());
// orderDto.setTotalPrice(requestOrder.getQty() * requestOrder.getUnitPrice());
//kafka 메세지 전송
// kafkaProducer.orderSend("example-catalog-topic", orderDto);
// kafkaOrderProducer.orderSend("orders", orderDto);
// ResponseOrder responseOrder = mapper.map(orderDto, ResponseOrder.class);
log.info("After Added Order Data");
return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
}
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId){
log.info("Before Retrieve Order Data");
Iterable<OrderEntity> orderList = orderService.getOrderByUserId(userId);
List<ResponseOrder> result = new ArrayList<>();
orderList.forEach(v -> {
result.add(new ModelMapper().map(v, ResponseOrder.class));
});
log.info("After Receive Order Data");
return ResponseEntity.ok().body(result);
}
}
サービスにもlogを追加します.
注文を登録します
次のようにtrace idとspan idを表示する必要があります.trace id
というzipkinで追跡する場合、ユーザの1回の要求単位をtrace idと呼ぶ.span id
は、span idと呼ばれるユーザの1回の要求単位内部の追加要求のmicroservice単位である.
その後、ユーザー情報を注文のURLにロードすると、結果が表示されます.
trace idとspan idの違いがわかりますが、現在のtrace idはuser-serviceで146472b7356d64ef
値、order-serviceでも
同じ値であることを確認できます.
zipkinでも処理の過程を確認できます.
Find a traceでサービス名を検索することもできます.
また、定期的に検索して、サービスが正常に通信しているかどうかを確認することもできます.
故意にエラーが発生した場合、エラーは赤で表示して確認することができます.
zipkinサーバによって格納されたデータ履歴を理解し、スキップし、springbootのログデータをsleuthライブラリで処理します.
Reference
この問題について(MSAのトラブルシューティングと分散トレース), 我々は、より多くの情報をここで見つけました
https://velog.io/@ililil9482/MSA-장애-처리와-분산-추척
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
@Configuration
public class Resilience4JConfig {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> customCinfiguration(){
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(4) // 100번중 4번만 실패해도 실행
.waitDurationInOpenState(Duration.ofMillis(1000)) //1초 동안 circuitbreaker 사용
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) //횟수를 기준
.slidingWindowSize(2) //2개의 데이터를 저장
.build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(4)) //4초 대기
.build();
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(timeLimiterConfig)
.circuitBreakerConfig(circuitBreakerConfig)
.build()
);
}
}
👉Zipkinのインストール
https://zipkin.io/pages/quickstart.htmlホームページに移動して、自分でインストールしやすい方法でインストールすればいいです.
git clone https://github.com/openzipkin/zipkin
cd zipkin
# Build the server and also make its dependencies
./mvnw -DskipTests --also-make -pl zipkin-server clean install
# Run the server
java -jar .\zipkin-server\target\zipkin-server-2.23.17-SNAPSHOT-exec.jar
curl-sslコマンドは聞こえなかったのでgitに直接インストールしました.しかも、gitでインストールするのにもっと時間がかかります!!!うううインストールされたバージョンに応じてzipkin jarファイル名を変更するには、適切な変更やコマンドで名前を変更するだけです.
正常に動作すると、次の画面が表示され、9411ポートで動作します.
これは正常に動作している画面です.
👉Zipkin設定
user-service
で設定<!-- sleuth -->
<!-- sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
まず依存項目を追加します.spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0 #전달할 log의 퍼센트 1.0 -- 100퍼센터
設定を追加します. log.info("Before Call Order Service");
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker");
List<ResponseOrder> orderList = circuitBreaker.run(() ->
orderServiceClient.getOrders(userId),
throwable -> new ArrayList<>() //Error가 발생할 경우 비어있는 리스트를 반환
);
log.info("After Call Order Service");
次に、UserServiceImplでfaignClientのセクションを実行し、ログを次のように記録して表示しやすくします.order-service
で設定<!-- sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- zipkin -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
依存性を追加します.spring:
zipkin:
base-url: http://127.0.0.1:9411
enabled: true
sleuth:
sampler:
probability: 1.0 #전달할 log의 퍼센트 1.0 -- 100퍼센터
設定がそっくりです.package com.example.orderservice.controller;
import com.example.orderservice.dto.OrderDto;
import com.example.orderservice.jpa.OrderEntity;
import com.example.orderservice.messagequeue.KafkaOrderProducer;
import com.example.orderservice.messagequeue.KafkaProducer;
import com.example.orderservice.service.OrderService;
import com.example.orderservice.vo.RequestOrder;
import com.example.orderservice.vo.ResponseOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/order-service")
@RequiredArgsConstructor
@Slf4j
public class OrderController {
private final Environment env;
private final OrderService orderService;
private final KafkaProducer kafkaProducer; //kafka producer 주입
private final KafkaOrderProducer kafkaOrderProducer; //주문 전송 producer 주입
...
@PostMapping("/{userId}/orders")
public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder requestOrder){
log.info("Before Add Order Data");
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
//기존의 jpa 로직
OrderDto orderDto = mapper.map(requestOrder, OrderDto.class);
orderDto.setUserId(userId);
OrderDto createOrder = orderService.createOrder(orderDto);
ResponseOrder responseOrder = mapper.map(createOrder, ResponseOrder.class);
// OrderDto orderDto = mapper.map(requestOrder, OrderDto.class);
// orderDto.setUserId(userId);
//kafka 주문 로직 추가
// orderDto.setOrderId(UUID.randomUUID().toString());
// orderDto.setTotalPrice(requestOrder.getQty() * requestOrder.getUnitPrice());
//kafka 메세지 전송
// kafkaProducer.orderSend("example-catalog-topic", orderDto);
// kafkaOrderProducer.orderSend("orders", orderDto);
// ResponseOrder responseOrder = mapper.map(orderDto, ResponseOrder.class);
log.info("After Added Order Data");
return ResponseEntity.status(HttpStatus.CREATED).body(responseOrder);
}
@GetMapping("/{userId}/orders")
public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId){
log.info("Before Retrieve Order Data");
Iterable<OrderEntity> orderList = orderService.getOrderByUserId(userId);
List<ResponseOrder> result = new ArrayList<>();
orderList.forEach(v -> {
result.add(new ModelMapper().map(v, ResponseOrder.class));
});
log.info("After Receive Order Data");
return ResponseEntity.ok().body(result);
}
}
サービスにもlogを追加します.注文を登録します
次のようにtrace idとspan idを表示する必要があります.
trace id
というzipkinで追跡する場合、ユーザの1回の要求単位をtrace idと呼ぶ.span id
は、span idと呼ばれるユーザの1回の要求単位内部の追加要求のmicroservice単位である.その後、ユーザー情報を注文のURLにロードすると、結果が表示されます.
trace idとspan idの違いがわかりますが、現在のtrace idはuser-serviceで
146472b7356d64ef
値、order-serviceでも同じ値であることを確認できます.
zipkinでも処理の過程を確認できます.
Find a traceでサービス名を検索することもできます.
また、定期的に検索して、サービスが正常に通信しているかどうかを確認することもできます.
故意にエラーが発生した場合、エラーは赤で表示して確認することができます.
zipkinサーバによって格納されたデータ履歴を理解し、スキップし、springbootのログデータをsleuthライブラリで処理します.
Reference
この問題について(MSAのトラブルシューティングと分散トレース), 我々は、より多くの情報をここで見つけました https://velog.io/@ililil9482/MSA-장애-처리와-분산-추척テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol