java吞吐_java SpringMVC异步编程:提升系统吞吐能力

java SpringMVC异步编程:提升系统吞吐能力

使用spring boot搭建web项目时,spring boot会内置一个tomcat作为web服务器,每一次请求落到tomcat上时,tomcat都会把请求封装成一个servlet,在传统的开发过程中servlet的生命周期会从tomcat创建它开始一直到业务逻辑处理完成并返回处理结果,换句话说在一次请求过程中servlet是独占的。

tomcat的默认并发量maxThreads=200,意味着tomcat同时只能支持200个并发量,当并发量大于200时,就会出现等待、延迟等情况。现在有很多种方法可以解决并发量问题,如集群、负载等,今天我介绍一个在代码层面优化的方法来解决tomcat吞吐量的方法。

如果在处理请求时,tomcat主线程把业务逻辑交给其他线程执行,自身则立刻释放,这样tomcat的线程就会大大减少tomcat线程的被占用事件,增加tomcat的吞吐能力,从而提高并发量。有两种方式可以实现这个思路:

通过Callable创建线程替代tomcat主线程

DeferredResult方式返回数据

通过这两种方式可以立即释放tomcat主线程,如果我们的代码里用到了ThreadLocal线程内部存储类,ThreadLocal里面的数据会在线程切换的时候不被传递,Spring提供了一个子类InheritableThreadLocal以支持父子线程间的值传递。 实例源码:https://gitee.com/topduang/test-service.git

test-servic/web/src/main/java/com/yc/web/controller/AsyncController.java

1. 首先创建2个方法,手动延时2s,代表2次复杂的业务处理

@Override

public UserVo getByIdAsync(int id) throws Exception {

Long startTime = System.currentTimeMillis();

System.out.println("start getByIdAsync");

UserVo userVo = new UserVo();

userVo.setId(id);

userVo.setName("YC");

Thread.sleep(2000);

System.out.println("end getByIdAsync:" + (System.currentTimeMillis() - startTime));

return userVo;

}

@Override

public void getByIdDeff(int id, DeferredResult deferredResult) throws Exception {

Long startTime = System.currentTimeMillis();

System.out.println("start getByIdDeff");

UserVo userVo = new UserVo();

userVo.setId(id);

userVo.setName("YC");

Thread.sleep(2000);

deferredResult.setResult(ResultInfo.Success(userVo));

System.out.println("end getByIdDeff:" + (System.currentTimeMillis() - startTime));

}

2. 分别通过Callable和DeferredResult创建两个API

在controller层创建方法调用上述业务方法:

@ApiOperation(response = ResultInfo.class, value = "获取分页列表", notes = "获取分页列表")

@GetMapping(value = "/getByIdAsync")

public Callable getByIdAsync(@ApiParam(value = "id") @RequestParam int id) throws Exception {

System.out.println("start main thread:" + Thread.currentThread());

Callable result = () -> ResultInfo.Success(asyncService.getByIdAsync(id));

System.out.println("end main thread:" + Thread.currentThread());

return result;

}

@ApiOperation(response = ResultInfo.class, value = "获取分页列表", notes = "获取分页列表")

@GetMapping(value = "/getByIdDeff")

public DeferredResult getByIdDeff(@ApiParam(value = "id") @RequestParam int id) throws Exception {

System.out.println("start main thread:" + Thread.currentThread());

DeferredResult deferredResult = new DeferredResult(3000L, "fail");

new Thread(() -> {

try {

asyncService.getByIdDeff(id, deferredResult);

} catch (Exception e) {

e.printStackTrace();

}

}).start();

System.out.println("end main thread:" + Thread.currentThread());

return deferredResult;

}

3. 测试

分别对两种异步方法进行测试:

调用getByIdAsync,看到控制台打印日志,可以看到main thread被返回之后才处理了业务逻辑:

start main thread:Thread[http-nio-10000-exec-3,5,main]

end main thread:Thread[http-nio-10000-exec-3,5,main]

start getByIdAsync

end getByIdAsync:2000

调用getByIdDeff,同理:

start main thread:Thread[http-nio-10000-exec-7,5,main]

end main thread:Thread[http-nio-10000-exec-7,5,main]

start getByIdDeff

end getByIdDeff:2000

后记

在DeferredResult方式中还可以把动态创建线程的函数用线程池代替,进一步优化因线程创建释放浪费的资源。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值