seata踩坑:zipkin整合seata的时候导致远程调用服务连接不上

问题:在项目整合了集成了seata来做分布式事务,后面整合sleuth来做服务追踪后发现消费者使用feign调不通提供者服务了,当时检查了提供者服务都是否启动了,提供者是否注册nacos成功了,也检查了feign调用的提供者的在nacos上注册的服务名,这些都没有问题,就是使用feign调不通服务,就像提供者服务没有启动一样,我引入的依赖是spring-cloud-alibaba-seata和spring-cloud-starter-zipkin,一直报错:

Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: 192.168.248.1
  at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
  at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
  at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
  at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.Observable.subscribe(Observable.java:10423) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.Observable.subscribe(Observable.java:10390) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443) ~[rxjava-1.3.8.jar:1.3.8]
  at rx.observables.BlockingObservable.single(BlockingObservable.java:340) ~[rxjava-1.3.8.jar:1.3.8]
  at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
  at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:83) ~[spring-cloud-openfeign-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
... 120 common frames omitted

后面在seata官网的issue发现使用sleuth和seata同时存在,会导致feign调用报错,seata官网issue链接:

https://github.com/seata/seata/issues/2544

官网建议:

Spring Cloud Sleuth默认通过TraceFeignClientAutoConfiguration提供feign的集成,可以设置spring.sleuth.feign.enabled为false来使其无效

但是这种修改方式把sleuth关闭了,导致在zipkin控制台看到的接口调用栈都是没有任何关联的,

如果你需要使用seata,但是又不想关闭sleuth,则本篇博客就来解决这个问题。

分布式系统中seata如何传递xid:

在seata的官方文档中业务系统集成seata客户端,有五个步骤。在最后一个步骤:

步骤五:实现xid跨服务传递
    手动 参考源码integration文件夹下的各种rpc实现 module
    自动 springCloud用户可以引入spring-cloud-alibaba-seata,内部已经实现xid传递

这个意思也就是:需要把全局事务的xid,透传到构成全局事务的各个子服务中。

    自动透传xid,可以直接引用 spring-cloud-alibaba-seata  (由于这个包和sleuth冲突,所以我们主要看手动透传xid的方案。)

    手动透传xid,可以参考官方源码的实现: https://github.com/seata/seata/tree/develop/integration

解决思路:

这里我就以http的实现思路来简单说一下:

    如果构成全局事务的各个自服务是使用http协议的,比如使用的是SpringCloud。对于http协议透传xid的方式很简单:

    在consumer端,只需要获取到xid然后把xid放到请求头中;

    在provider端解析http的请求头,然后使用RootContext.bind(xid)绑定xid即可。

解决问题:

在consumer端:

1.修改启动类,加(exclude = {SeataFeignClientAutoConfiguration.class})

@SpringBootApplication(exclude = {SeataFeignClientAutoConfiguration.class})

 2.可以在每个feign请求头中将全局事务的xid传过去,本人推荐使用全局请求的头中加上xid,如:

import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class SeataFeignInterceptor implements RequestInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(SeataFeignInterceptor.class);

    @Override
    public void apply(RequestTemplate request) {
        String xid = RootContext.getXID();

        if (xid != null && !xid.trim().isEmpty()) {
            logger.info("seata - feign add seata-xid http request header [{}]", xid);
            request.header(RootContext.KEY_XID, xid);
        }
    }
}

 注册该拦截器

import com.db.cloud.serviceconsume.common.handle.SeataFeignInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CommonConfig {

    @Bean
    public SeataFeignInterceptor seataFeignInterceptor() {
        return new SeataFeignInterceptor();
    }
}

在provider端,是否实现了WebMvcConfigurer,如果是,请参考com.alibaba.cloud.seata.web.SeataHandlerInterceptorConfiguration#addInterceptors的方法.把SeataHandlerInterceptor加入到你的拦截链路中.因为在consumer在每个请求头加入了全局事务的xid,所以需要在拦截器中使用该xid,而seata使用http请求时使用的是SeataHandlerInterceptor,此时需在拦截链路中加入该拦截器,如何使用xid可以看看SeataHandlerInterceptor的源码。

WebMvcConfigurer配置添加SeataHandlerInterceptor如下:

import com.alibaba.cloud.seata.web.SeataHandlerInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SeataHandlerInterceptor()).addPathPatterns(new String[]{"/**"});
    }

}

参考链接:

https://github.com/seata/seata/issues/2544

https://seata.io/zh-cn/docs/overview/faq.html

https://github.com/seata/seata/tree/develop/integration/http/src/main/java/io/seata/integration/http

 

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页