觉得不错请按下图操作,掘友们,哈哈哈!!!
一:前言
在目前比较常见的网站中,首页、活动页、商品详情页等系绕承载了网站的大部分流量,而这些系统的主要职责包括数据的聚合拼裝、热点数据统计、缓存、下游功能降级开关、兜底数据等。其中数据聚合需要调用多个其他服务获取数据、拼裝数据以及相应模板,然后返回给前端,聚合数据的数据源主要有来源于依赖系统活着服务、级存、数据库等。而系统之间的调用可以通过如HTTP 接口调用 (如 HttpClient)、RPC 服务调用(如dubbo、thrift), 等实现。 在我们比较常见的Java应用中,比如使用 Tomcat,一个请求会分配一个线程进行请求处理,该线程负责 获取数据、拼装数据,然后返回给前端。在同步调用获取数据接口的情况下(等待依赖系统返回数据),整个线程是一直被占用并阻塞的。如果有大量的这种请求,则每个请求占用一个线程,但线程一直处于阻塞,降低了系统的吞吐量,这将导致应用的吞吐量下降。我们希望,在调用依赖的服务响应比较慢时,应该让出线程和 CPU 来处理下一个请求,当依赖的服务返回后再分配相应的线程来继续处理。而这应该有更好的解决方案:异步/协程。而 Java 是不支持协程的(虽然有些 Java 框架号称支持,但还是高层 API 的封裝),Python支持比较好,因此,在Java 中我们可以使用异步来提升吞吐量。目前大部分 Java开源框架 (HttpAsynClient、 Dubbo、thrift 等)都支持。 另外,应用中一个服务可能会调用多个依赖服务来处理业务,而这些依赖服务是可以同时调用的。如果顺序调用的话需要耗时 500ms,而并发调用只需要 50ms, 那么可以使用 Java 并发机制来并发调用依赖服务,从而降低该服务的响应时间,后边的异步编排会讲的很清晰。
在实际开发应用系统过程中,通过异步并发并不能使响应变得更快,更多是为了提升吞吐量、对请求更细粒度控制,或是通过多依赖服务并发调用降低服务响应时间。当一个线程在处理任务时,通过Fork多个线程来处理任务并等待这些线程的处理结果,这种应用并不是真正的异步。异步是针对CPU和IO来说的的,当一个线程的IO没有就绪时要让出CPU来处理其他任务,这才是异步。在这里就不介绍异步并发实现原理,主要介绍在Java应用中如何运用这些技术,而且大多数场景并不是真正的异步化,在Java中真正实现异步化是非常难的事情,如MySQLJDBC驱动等很多都是BIO设计,大多数情况下说的异步并发是通过线程池模拟实现。 发是通过线程池模拟 实现。
二:同步阻塞调用
虽然很简单,但是还是搞出来一个例子比较直观:
public class BlogCode {
//即串行调用,响应时间为所有依赖服务的响应时间总和。
public static void main(String[] args) throws Exception {
RpcService rpcService = new RpcService();
HttpService httpService = new HttpService();
//耗时为10ms
Map<String, String> result = rpcService.getRpcResult();
//耗时为20ms
Map<String, String> result2 = httpService.getHttpResult();
//总耗时为30ms
}
static class RpcService {
public Map<String, String> getRpcResult() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
static class HttpService {
public Map<String, String> getHttpResult() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
}
三:异步Future
线程池配合Future实现,但是阻塞主请求线程,高并发时依然会造成线程数过多、CPU上下文切换。通过Future可以并发发出N个请求,然后等待最慢的一个返回,总响应时问为最慢的一个请求返回的用时。如下请求如果并发访问,则响应可以在30ms后返回。
public void BlogFuture() {
RpcService rpcService = new RpcService();
HttpService httpService = new HttpService();
Future<Map<String, String>> futureRpc = null;
Future<Map<String, String>> futureHttp = null;
try {
futureRpc = executor.submit(() -> rpcService.getRpcResult());
futureHttp = executor.submit(() -> httpService.getHttpResult());
// 耗时为10ms
Map<String, String> resultRpc = futureRpc.get(300, TimeUnit.MILLISECONDS);
// 耗 时 为2 0 ms
Map<String, String> resultHttp = futureHttp.get(300, TimeUnit.MILLISECONDS);
// 总耗时为20毫秒
} catch (Exception e) {
if (futureRpc != null) {
futureRpc.cancel(true);
}
if (futureHttp != null) {
futureHttp.cancel(true);
}
throw new RuntimeException(e);
}
}
static class RpcService {
public Map<String, String> getRpcResult() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
static class HttpService {
public Map<String, String> getHttpResult() {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
四:异步CallBack
再一种通过回调机制实现,即首先发出对应服务请求,当服务返回时回调相关方法,如HttpAsyncClien 使用基于N1O的异步I/O模型实现,它实现了Rcactor模式,摒弃阻塞IO模型one thread per connection ,采用线程池分发事件通知,从而有效支撑大量并发连接。这种机制并不能提升性能,而是为了支撑大量并发连接或者提升吞吐量。
public static HttpAsyncClient httpAsyncClient;
public static CompletableFuture<String> getHttpData (String url) {
CompletableFuture asyncFuture = new CompletableFuture () ;
HttpAsyncRequestProducer producer = HttpAsyncMethods.create(new HttpPost(url));
BasicAsyncResponseConsumer consumer = new BasicAsyncResponseConsumer();
FutureCallback callback = new FutureCallback<HttpResponse> () {
@Override
public void completed(HttpResponse response) {
asyncFuture.complete(response);
}
@Override
public void failed(Exception e) {
asyncFuture.completeExceptionally(e);
}
@Override
public void cancelled() {
asyncFuture.cancel (true);
}
};
httpAsyncClient.execute(producer,consumer,callback);
return asyncFuture;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> feature = getHttpData("www.baidu.com");
String data = feature.get();
}
这种异步实现可以配合CompletableFuture 达到半异步的效果。
五:异步编排
JDK8 CompletableFuture提供了新的异步编程思路,可以对多个异步处理进行编排,实现更复杂的异步处理。其内部使用ForkJoinPool实现异步处理。使用CompletableFuture可以把回调方式的实现转变为同步调用实现。CompletableFuture提供了50多个API,可以满足各种所需场景的异步处理编排,具体场景使用在我另外一篇文章中有详细demo。
Java CompletableFuture实现多线程异步编排
六:异步web服务
借助Servlet 3、CompletableFuture 实现异步Web 服务。如下是整个处理流程。
Servlet 容器接收到请求之后,Tomcat 需要先解析请求体,然后通过异步Servlet 将 请求交给异步线程池来完成业务处理,Tomcat 线程释放回容器。通过异步机制可以提升 Tomcat 容器的吞吐量。
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Author xiaomayi
* @Date 2023/6/28
*/
@Slf4j
@Component
public class OneLevelAsyncContext implements InitializingBean {
private final String URI = "uri";
private final String PARAMS = "params";
private AsyncListener asyncListener;
private LinkedBlockingDeque<Runnable> queue;
private ThreadPoolExecutor executor;
// public void submitFuture(final HttpServletRequest req, final Callable<Object> task) {
// final String uri = req.getRequestURI();
// final Map<String, String[]> params = req.getParameterMap();
// final AsyncContext asyncContext = req.startAsync(); //开启异步上下文
// asyncContext.getRequest().setAttribute("uri", uri);
// asyncContext.getRequest().setAttribute("params", params);
// asyncContext.setTimeout(2 * 1000);
// if(asyncListener != null) {
// asyncContext.addListener(asyncListener);
// }
// executor.submit((Runnable) new CanceledCallable(asyncContext) {
// //提交任务给业务线程池
// @Override
// public Object call() throws Exception {
// Object o = task.call(); //业务处理调用
// if(o == null) {
// callBack(asyncContext, o, uri, params); //业务完成后,响应处理
// }
// if(o instanceof CompletableFuture) {
// CompletableFuture<Object> future = (CompletableFuture<Object>)o;
// future.thenAccept(resultObject -> callBack(asyncContext, resultObject, uri, params))
// .exceptionally(e -> {
// callBack(asyncContext, "", uri, params);
// return null;
// });
// } else if(o instanceof String) {
// callBack(asyncContext, o, uri, params);
// }
// return null;
// }
// });
// }
//
// private void callBack(AsyncContext asyncContext, Object result, String uri, Map<String, String[]> params) {
// HttpServletResponse resp = (HttpServletResponse) asyncContext.getResponse();
// try {
// if(result instanceof String) {
//// write(resp, (String)result);
// } else {
//// write(resp, JSONUtils.toJSONString(result));
// }
// } catch (Throwable e) {
// resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); //程序内部错误
// try {
// log.error("get info error, uri : {}, params : {}", uri, JSONUtils.toJSON(params), e);
// } catch (Exception ex) {
// }
// } finally {
// asyncContext.complete();
// }
// }
public Object submitFuture(final HttpServletRequest request, final Callable<Object> task) throws ExecutionException, InterruptedException {
// 获取请求URI
final String uri = request.getRequestURI();
// 获取请求参数
final Map<String, String[]> params = request.getParameterMap();
// 开启异步上下文
final AsyncContext asyncContext = request.startAsync();
asyncContext.getRequest().setAttribute(URI, uri);
asyncContext.getRequest().setAttribute(PARAMS, params);
// 超时设置
asyncContext.setTimeout(2 * 1000);
if (Objects.nonNull(asyncListener)) {
asyncContext.addListener(this.asyncListener);
}
// 线程池处理业务
Future<Object> future = executor.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
// 业务处理
Object result = task.call();
return result;
}
});
// 完成异步上下文,否则报超时等异常
asyncContext.complete();
return future.get();
}
// 完成初始化配置
@Override
public void afterPropertiesSet() throws Exception {
// 线程池大小
int corePoolSize = Integer.parseInt("100");
// 最大线程池大小
int maxNumPoolSize = Integer.parseInt("200");
// 任务队列
queue = new LinkedBlockingDeque<Runnable>();
// 创建线程池
executor = new ThreadPoolExecutor(corePoolSize, maxNumPoolSize, 100, TimeUnit.MILLISECONDS, queue);
executor.allowCoreThreadTimeOut(true);
// 线程池饱和处理
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (r instanceof CanceledCallable) {
CanceledCallable cc = ((CanceledCallable) r);
AsyncContext asyncContext = cc.asyncContext;
try {
ServletRequest request = asyncContext.getRequest();
String uri = (String) request.getAttribute(URI);
Map params = (Map) request.getAttribute(PARAMS);
log.error(String.format("async request %s, uri:%s, params:%s", "rejectedExecution", uri, JSON.toJSONString(params)));
} catch (Exception ex) {
}
try {
HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
asyncContext.complete();
}
}
}
});
// 创建监听器
if (Objects.isNull(asyncListener)) {
asyncListener = new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
AsyncContext asyncContext = asyncEvent.getAsyncContext();
try {
ServletRequest request = asyncContext.getRequest();
String uri = (String) request.getAttribute(URI);
Map params = (Map) request.getAttribute(PARAMS);
log.error(String.format("async request timeout, uri:%s, params:%s", uri, JSON.toJSONString(params)));
} catch (Exception e) {
}
try {
HttpServletResponse resp = (HttpServletResponse) asyncContext.getResponse();
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
asyncContext.complete();
}
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
AsyncContext asyncContext = asyncEvent.getAsyncContext();
try {
ServletRequest request = asyncContext.getRequest();
String uri = (String) request.getAttribute(URI);
Map params = (Map) request.getAttribute(PARAMS);
log.error(String.format("async request error, uri:%s, params:%s", uri, JSON.toJSONString(params)));
} catch (Exception e) {
}
try {
HttpServletResponse resp = (HttpServletResponse) asyncContext.getResponse();
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
asyncContext.complete();
}
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
}
};
}
}
}
import javax.servlet.AsyncContext;
import java.util.concurrent.Callable;
/**
* @Author xiaomayi
* @Date 2023/6/28
*/
public class CanceledCallable implements Runnable, Callable<Object> {
public AsyncContext asyncContext;
public CanceledCallable(AsyncContext asyncContext){
this.asyncContext = asyncContext;
}
@Override
public void run() {
}
@Override
public Object call() throws Exception {
return null;
}
}
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* @Author xiaomayi
* @Date 2023/6/28
*/
@RestController
public class OneLevelController {
@Resource
private XiaoMaYiService xiaoMaYiService;
@Resource
private OneLevelAsyncContext oneLevelAsyncContext;
@PostMapping("/oneLevelAsyncContext")
public Response<Object> testOneLevelAsyncContext(HttpServletRequest request, XiaoMaYi tab) {
try {
Object result = oneLevelAsyncContext.submitFuture(request, () -> xiaoMaYiService.queryAll(tab));
return Response.success(result);
} catch (Exception e) {
e.printStackTrace();
}
return Response.error("请稍后重试");
}
}
往期经典:
设计模式:
JYM 设计模式系列- 单例模式,适配器模式,让你的代码更优雅!!!
JYM 设计模式系列- 责任链模式,装饰模式,让你的代码更优雅!!!
JYM 设计模式系列- 策略模式,模板方法模式,让你的代码更优雅!!!
Spring相关:
Spring源码解析-老生常谈Bean ⽣命周期 ,但这个你值得看!!!
Spring 源码解析-JYM你值得拥有,从源码角度看bean的循环依赖!!!
本文正在参加「金石计划」