1

Kubernetes で実行されている Spring Boot サービスがあり、RestTemplate で Spring Cloud Kubernetes Load Balancer 機能を使用して、他の Spring Boot サービスを呼び出しています。これを導入した主な理由の 1 つは歴史的なものです。以前は、サービス ディスカバリに Eureka を使用して EC2 でサービスを実行し、移行後、Spring ディスカバリ クライアント/クライアント側のロード バランシングを維持しました (依存関係の更新など)。 Spring Cloud Kubernetes プロジェクトで動作するようにします)

ターゲット ポッドの 1 つがダウンすると、一定期間、リクエストに対して複数の失敗が発生するという問題があります。java.net.NoRouteToHostExceptionつまり、スプリング ロード バランサがまだそのポッドに送信しようとしているということです。

だから私はこれについていくつか質問があります:

  • これが発生した場合、ターゲット インスタンスは自動的に削除されるべきではありませんか? ということは、一度は起こるかもしれませんが、その後、ターゲット ポッド リストは修復されますか?

  • または、そうでない場合、これを処理するために追加する必要がある他の構成があります-たとえば、再試行/サーキットブレーカーなど?

  • より一般的な質問は、Spring のクライアント側の負荷分散が Kubernetes にもたらす利点は何ですか? それがなくても、私たちのサービスは Kubernetes 組み込みサービス/負荷分散機能を使用して他のサービスを呼び出すことができ、ポッドが自動的にダウンする問題を処理する必要があります。Spring のドキュメントでは、POD モードから SERVICE モードに切り替えることができることについても説明しています ( https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html#loadbalancer-for-kubernetes)。しかし、このサービス モードは Kubernetes が自動的に行うものではないでしょうか? ここでの最も簡単な解決策は、Spring Load Balancer を完全に削除することではないのでしょうか? その時、私たちは何を失うでしょうか?

4

2 に答える 2

0

これに関する更新:spring-retry依存関係はありましたが、デフォルトでは GET に対してのみ機能し、ほとんどの呼び出しは POST であるため、再試行は機能しませんでした (ただし、再度呼び出しても問題ありません)。構成を追加することでspring.cloud.loadbalancer.retry.retryOnAllOperations: trueこれが修正されたため、2 回目の試行で代替インスタンスを使用して再試行することで、これらの失敗のほとんどを回避する必要があります。

また、特定の接続例外でサービスのロード バランサー キャッシュをクリアする RetryListener も追加しました。

@Configuration
public class RetryConfig {

    private static final Logger logger = LoggerFactory.getLogger(RetryConfig.class);
    
    // Need to use bean factory here as can't autowire LoadBalancerCacheManager -
    // - it's set to 'autowireCandidate = false' in LoadBalancerCacheAutoConfiguration
    @Autowired
    private BeanFactory beanFactory;
    
    @Bean 
    public CacheClearingLoadBalancedRetryFactory cacheClearingLoadBalancedRetryFactory(ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
        return new CacheClearingLoadBalancedRetryFactory(loadBalancerFactory);
    }
    
    // Extension of the default bean that defines a retry listener
    public class CacheClearingLoadBalancedRetryFactory extends BlockingLoadBalancedRetryFactory {

        public CacheClearingLoadBalancedRetryFactory(ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
            super(loadBalancerFactory);
        }

        @Override
        public RetryListener[] createRetryListeners(String service) {
            
            RetryListener cacheClearingRetryListener = new RetryListener() {
                
                @Override
                public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { return true; }
                
                @Override
                public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {}

                @Override
                public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                    
                    logger.warn("Retry for service {} picked up exception: context {}, throwable class {}", service, context, throwable.getClass());
                    
                    if (throwable instanceof ConnectTimeoutException || throwable instanceof NoRouteToHostException) {
                
                        try {   
                            LoadBalancerCacheManager loadBalancerCacheManager = beanFactory.getBean(LoadBalancerCacheManager.class);                                        
                            Cache loadBalancerCache = loadBalancerCacheManager.getCache(CachingServiceInstanceListSupplier.SERVICE_INSTANCE_CACHE_NAME);            
                            if (loadBalancerCache != null) {                    
                                boolean result = loadBalancerCache.evictIfPresent(service);
                                logger.warn("Load Balancer Cache evictIfPresent result for service {} is {}", service, result);                             
                            }                           
                        } catch(Exception e) {
                            logger.error("Failed to clear load balancer cache", e);
                        }
                    }
                }                               
            };
                
            return new RetryListener[] { cacheClearingRetryListener };              
        }
    }
}

このアプローチに問題はありますか? このようなものを組み込み機能に追加できますか?

于 2021-12-21T11:51:00.713 に答える