同一列相同内容合并Java逻辑_高并发场景-请求合并(一)SpringCloud中Hystrix请求合并...

本文介绍了在高并发环境下,如何利用SpringCloud Hystrix的注解方式实现请求合并,以减少数据库连接次数和CPU压力。通过自定义HystrixCollapser和HystrixCommand,将多个单个查询合并为批量查询,减少了对数据库的访问频率。文章详细讲解了配置、实现逻辑以及测试方法。
摘要由CSDN通过智能技术生成

背景

在互联网的高并发场景下,请求会非常多,但是数据库连接池比较少,或者说需要减少CPU压力,减少处理逻辑的,需要把单个查询,用某些手段,改为批量查询多个后返回。

如:支付宝中,查询“个人信息”,用户只会触发一次请求,查询自己的信息,但是多个人同时这样做就会产生多次数据库连接。为了减少连接,需要在JAVA服务端进行合并请求,把多个“个人信息”查询接口,合并为批量查询多个“个人信息”接口,然后以个人信息在数据库的id作为Key返回给上游系统或者页面URL等调用方。

目的

减少访问数据库的次数

单位时间内的多个请求,合并为一个请求。让业务逻辑层把单个查询的sql,改为批量查询的sql。或者逻辑里面需要调用redis,那批量逻辑里面就可以用redis的pipeline去实现。

主要解决手段

SpringCloud的Hystrix的自定义HystrixCollapse和HystrixCommand

SpringCloud的Hystrix注解方式。

没有服务治理框架时,利用JDK队列、定时任务线程池处理。

鉴于现在大部分都有SpringCloud,所以先说第种的注解方式,后续再说第3种,不用第1种是因为注解方式比较方便。

交互流程

735215d1e5fddb40348a388ef805ca80.png

主思路是接收请求后,从上一次计数开始累计等待200ms

一次过处理200ms内的接口入参

然后以id为key,批量查询多个id的结果

批量查询完后,以id为key,返回给上游系统的单个查询

测试手段

Postman

在本地系统创建单元测试方式,调用自己启动的服务

建立上游系统工程来调用

手动在页面请求多次

Jmeter生成多线程请求

选其一种。建议1、2

开发

本文主要使用Hystrix注解的方式去实现,还有另外一种办法实现的就是编码自定义HystrixCollapser,那种方法是建立两个类,一个继承HystrixCollapser,另一个继承HystrixCommand,这个方法比较显式的编码声明有助于理解,但是不够Hystrix方式便捷。

自定义HystrixCollapser方式和Hystrix注解方式实现请求合并的优劣

虽然Hystrix注解方式比较快,但是不能做到实时更改等待的单位时间,那个超时时间是放在注解上,如果要更改单位时间,其实都需要重启服务或者重新编译打包。

用自定义HystrixCollapser比较好的地方就是可以在运行过程中,读字典表去更改单位时间,这样线上出问题了就不用重启了。

但是自定义HystrixCollapser方式缺点还是有的,因为绑定一个批量方法就要建立一个HystrixCommand类,如果有多个请求合并的情况,就只能建立多个HystrixCommand类了。

1. 添加POM

声明springboot 和springcloud版本

我以前做的工程使用了1.4.7.RELEASE,Camden.SR2。

其实大家可以用新版本的,只是新版本的eureka、Feign依赖的artifactId改变了,但是后续使用方式是一样的。

org.springframework.boot

spring-boot-starter-parent

1.4.7.RELEASE

org.springframework.cloud

spring-cloud-dependencies

Camden.SR2

pom

import

复制代码

添加关键依赖

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-starter-eureka

org.springframework.cloud

spring-cloud-starter-hystrix

io.springfox

springfox-swagger2

2.2.2

io.springfox

springfox-swagger-ui

2.2.2

复制代码

2. 启动注解

除了SpringCloud客户端所基本需要的@SpringBootApplication @EnableEurekaClient,主要加上@EnableCircuitBreaker。因为使用到hystrix的都必须声明这个注解,为了启动断路器的意思,如熔断的时候也会使用,熔断也是通过hystrix来实现的。

这个比较关键,不启动的话,后续编码怎么弄都不生效的

@SpringBootApplication

@EnableDiscoveryClient

//使用hystrix必须增加

@EnableCircuitBreaker

@EnableEurekaClient

@EnableSwagger2

public class ProviderRequestMergeApplication {

public static void main(String[] args) {

SpringApplication.run(ProviderRequestMergeApplication.class, args);

}

}

复制代码

3. 请求接口Controller

编写两个接口,user方法是没有经过合并请求的样例,在本案例实际没有作用,只是用于校验合并与不合并的效果。

userbyMerge方法在合并请求的方法,其作为请求接口入口,合并请求的逻辑,并不需要在Controller里面实现,使得Controller只作为请求这一层,不耦合其他功能。

/**

*

* @author kelvin.cai

*

*/

@RestController

public class UserController {

@Autowired

private UserBatchServiceImpl userBatchServiceImpl;

@RequestMapping(method = RequestMethod.POST,value = "/user/{id}")

public User user(@PathVariable Long id) {

User book = new User( 55L, "姚雪垠2");

return book;

}

@RequestMapping(method = RequestMethod.GET,value = "/userbyMerge/{id}")

public User userbyMerge(@PathVariable Long id) throws InterruptedException, ExecutionException {

Future userFu = this.userBatchServiceImpl.getUserById(id);

User user = userFu.get();

return user;

}

}

复制代码

4. 编写请求合并逻辑

/**

*

* @author kelvin.cai

*

*/

@Component

public class UserBatchServiceImpl {

@HystrixCollapser(batchMethod = "getUserBatchById",scope=Scope.GLOBAL,

collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "2000")})

public Future getUserById(Long id) {

throw new RuntimeException("This method body should not be executed");

}

@HystrixCommand

public List getUserBatchById(List ids) {

System.out.println("进入批量处理方法"+ids);

List ps = new ArrayList();

for (Long id : ids) {

User p = new User();

p.setId(id);

p.setUsername("dizang"+ids);

ps.add(p);

}

return ps;

}

}

复制代码

这里有几个关键点(如果没生效可以看看)

@HystrixCollapser参数batchMethod 的值为批量处理的方法的名字,批量处理方法必须在同一个类中。

单个处理方法和批量处理方法必须要同一个基本类型,只是批量方法需要使用List去包裹

单个处理方法,建议用Future,这个是jdk线程异步获取的那个类,用于异步获取结果。其实有另外的返回类型,让调用getUserById使用同步阻塞的方式去使用,但是不是很建议。

scope有两个值一个是Scope.REQUEST,意思就是当次请求接口内调用UserBatchServiceImpl.getUserById多次才会合并。想想看,如果我一个接口内,调用多次单个插叙,为何不直接使用一个批量查询呢?我没想到有什么场景会需要这个值。

scope有另外一个值Scope.GLOBAL,就是样例所示的值,意思就是,所有请求接口进来都合并。大家回顾一下需求目的,就比较符合要求了,如多个支付宝用户查询自己的信息时就是合并全局请求。

@HystrixProperty填合并请求的单位时间,debug时可以把他设置为5秒,比较好测试。

这里有个包路径的建议

这个合并请求类UserBatchServiceImpl 不建议放在业务逻辑层,为了保持业务逻辑service层代码是干净的只保留业务逻辑,所以这个UserBatchServiceImpl 类建议放在另外一个包collapser下,让这个包路径只是用于处理请求合并的事情。

因为这个类是利用springcloud框架实现,万一以后不用springcloud来做合并请求而用原始队列加线程池怎么办?

而且有些工程设计时,是建立server工程只做请求和服务治理,搞另外一个工程专门写domain领域下的东西,不包含其他框架的,这样为了第三个工程叫job定时任务工程可以直接使用domain工程的依赖。

这个领域驱动设计,请看我之前的文章。

测试方法

1. 触发测试

swagger-ui

如果你有添加swagger,那你打开http://localhost:7902/swagger-ui.html,对接口填一下参数请求两次。8ba0774c4b251f7f8e445c801f83639c.png

2. 结果输出

下图中,console日志已经输出了两次请求的入参2659d5657d5621720994255f0a59f8b2.png

3. PostMan测试

其实还可以使用Postman来同时请求相同接口。做法暂时不描述。

总结

到这里相信大家都已经完成了合并请求了,其实原理还是基于原始做法,利用队里存入参,然后利用线程池定时的获取队列的入参,再批量处理,利用线程的Future,异步返回结果。大致流程是这样的就不再描述了,如果有空会继续弄原始方法的请求合并。

大家还可以去看看Hystrix合并请求的其他参数,搜索相关信息来扩展hystrix功能。

本文Demo

都在我springcloud的demo里面了,看provider-hystrix-request-merge这个工程下的内容。

欢迎关注公众号,文章更快一步

我的公众号 :地藏思维

掘金:地藏Kelvin

简书:地藏Kelvin

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[高并发场景-请求合并(一)SpringCloud中Hystrix请求合并]http://www.zyiz.net/tech/detail-121733.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值