坑:异步调用其他服务避免使用AsyncRestTemplate

现象

 

坑:异步调用其他服务避免使用AsyncRestTemplate

 

订单服务的几个流程推动接口,出现了大量异常。持续20分钟。

 

排查

 

初步查看日志,发现这段时间有大量异常抛出,全部都是

java.lang.IllegalStateException: No instances available for travel-push。如图:

坑:异步调用其他服务避免使用AsyncRestTemplate

 

 

比较奇怪的是push全都是异步调用的。按理说不能影响到主流程才对。

查代码发现,用的异步调用,是用的org.springframework.web.client.AsyncRestTemplate

 

坑:异步调用其他服务避免使用AsyncRestTemplate

AsyncRestTemplate这个还是标注成废弃的

 

坑:异步调用其他服务避免使用AsyncRestTemplate

AsyncRestTemplate处理异步层面比较靠近调用,所以当被调用服务不可达的时候,这里会抛出异常,还是挺危险的。

并且这种方式不能传递traceId。

结论

 

可以自己写异步替代AsyncRestTemplate,利用CompletableFuture显式处理成功和失败。

同时可以指定executor来传递traceId。详见 异步线程带上traceId

坑:异步调用其他服务避免使用AsyncRestTemplate

附:异步线程带上traceId

我们在做异步处理的时候。用logger打日志以及http跨模块调用时,traceId都会因为线程改变而丢失原来的跟踪信息。在排查问题看日志时很不方便。

traceId其实是腾讯tsf基于spring cloud sleuth搞的,是trace和span中trace的id。

在其官方文档中,提供了几种异步传递trace/span的机制。

坑:异步调用其他服务避免使用AsyncRestTemplate

 

原地址:https://cloud.spring.io/spring-cloud-sleuth/reference/html/#asynchronous-communication

 

 

举个例子,

我们比较常用的是通过executor去创建一个异步线程,可以用LazyTraceExecutor去包装这个executor即可。

首先根据需要的场景,指定线程数等参数配置一个executor的bean。

@Bean(name = "myExecutorService")

public Executor getMyExecutorService() {

    BasicThreadFactory threadFactory = new BasicThreadFactory.Builder()

            .namingPattern("shunfeng-pool-%d")

            .daemon(true)

            .build();

 

    ThreadPoolExecutor threadPoolExecutor =

            new ThreadPoolExecutor(threadPoolConfig.getCorePoolSize(),

                    threadPoolConfig.getMaxPoolSize(),

                    threadPoolConfig.getKeepAliveSeconds(),

                    TimeUnit.SECONDS,

                    new LinkedBlockingQueue<>(1024),

                    threadFactory,

                    new ThreadPoolExecutor.AbortPolicy());

 

    return new LazyTraceExecutor(beanFactory, threadPoolExecutor);

}

 

在需要的地方注入,然后按原来的方式使用即可。

 

@Autowired

@Qualifier("myExecutorService")

Executor executor;

 

 

private void asyncDoSomething1() {

    log.info("async.");

    CompletableFuture.runAsync(() -> doSomething(), executor);

}

 

private void asyncDoSomething2() {

    log.info("async.");

    executor.execute(() -> doSomething());

}