介绍:  

  Hystrix的请求合并就是把重复的请求批量的用一个HystrixCommand命令去执行,以减少通信消耗和线程数的占用。Hystrix的请求合并用到了HystrixCollapser这个抽象类,它在HystrixCommand之前前放置一个合并处理器,将处于一个很短的时间窗(默认10ms)内对同一依赖服务的多个请求进行整合并以批量方式发起请求的功能(服务提供方也需要提供相应的匹狼实现接口)。下面我们通过一个例子来看看怎么使用。

示例:

  这个示例是基于spring cloud的,对此不熟悉的可以参考这里了解。

1.首先我们需要一个EurekaServer来作为注册中心。这个没什么特别的说明,可以参考代码示例中的 eureka-server工程

2.新建一个服务提供者工程eureka-server-service

  2.1 新建一个User.java。这个model必须要有一个无参的默认构造器,否则后面的实验会报错

package org.hope.model;

public class User {
//必须要有一个无参的构造器
public User(){} public User(Long id, String name) {
this.id = id;
this.name = name;
} private Long id;
private String name; setters()&getters();
}

  2.2 新建一个提供服务的controller

package org.hope.web;

import org.hope.model.User;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList;
import java.util.List; @RestController
public class UserBatchController { @RequestMapping(value = "/users", method = RequestMethod.GET)
public List<User> batchUser(String ids) {
System.out.println("ids===:" + ids);
List<User> lists = new ArrayList<User>();
lists.add(new User(1l, "小明"));
lists.add(new User(2l, "小红"));
lists.add(new User(3l, "小张"));
lists.add(new User(4l, "小王"));
lists.add(new User(5l, "小李")); return lists;
} @RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public User singleUser(@PathVariable("id") String id) {
User user = new User(100L, "大王");
return user;
} }

  2.3 springboot的main函数

package org.hope;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; @SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceApplication { public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
} }

  2.4 配置文件application.yml

eureka:
client:
serviceUrl:
#注册中心的地址
defaultZone: http://localhost:8761/eureka/
server:
#当前服务端口号
port: 8762
spring:
application:
#当前应用名称
name: service-batch

3.新建一个服务消费者工程ribbon-hystrix,在这个工程里我们测试hystrix批量服务调用的请求合并功能

  3.1新建一个model User.java。这个model一定要有无参的构造器

package org.hope.lee.model;

public class User {
//一定要有无参的构造器
public User(){} public User(Long id, String name) {
this.id = id;
this.name = name;
} private Long id;
private String name; getters()&setters();
}

  3.2 service负责调用服务

package org.hope.lee.service;

import org.hope.lee.model.User;

import java.util.List;

public interface UserService {
public User find(Long id); public List<User> findAll(List<Long> ids);
}
package org.hope.lee.service;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.hope.lee.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import java.util.Arrays;
import java.util.List; @Service("userService")
public class UserServiceImpl implements UserService{
@Autowired
private RestTemplate restTemplate; @Override
public User find(Long id) {
return restTemplate.getForObject("http://localhost:8762/users/{1}",User.class, id);
} @Override
public List<User> findAll(List<Long> ids) {
System.out.println("finaAll request:---------" + ids + "Thread.currentThread().getName():-------" + Thread.currentThread().getName());
User[] users = restTemplate.getForObject("http://localhost:8762/users?ids={1}", User[].class, StringUtils.join(ids, ","));
return Arrays.asList(users);
}
}

  3.3 HystrixCommand命令执行请求

package org.hope.lee.command;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import org.hope.lee.model.User;
import org.hope.lee.service.UserService; import java.util.ArrayList;
import java.util.List; public class UserBatchCommand extends HystrixCommand<List<User>> { UserService userService;
List<Long> userIds; public UserBatchCommand(UserService userService, List<Long> userIds) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")).
andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));
this.userService = userService;
this.userIds = userIds;
} @Override
protected List<User> run() throws Exception {
return userService.findAll(userIds);
} @Override
protected List<User> getFallback() {
List<User> users = new ArrayList<User>();
users.add(new User(99L, "失败者"));
return new ArrayList<User>(users);
}
}

  3.4 HystrixCollapser命令来做请求合并

package org.hope.lee.command;

import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCollapserKey;
import com.netflix.hystrix.HystrixCollapserProperties;
import com.netflix.hystrix.HystrixCommand;
import org.hope.lee.model.User;
import org.hope.lee.service.UserService; import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

  /**
  * Created by lisen on 2017/12/27.
  * 通过看HystrixCollapser类的源码:
  * public abstract class HystrixCollapser<BatchReturnType, ResponseType, RequestArgumentType>
  * 我们可以知道List<User>表示:合并后批量请求的返回类型
  * User表示:单个请求返回的类型
  * Long表示:请求参数类型
  */

public class UserCollapseCommand extends HystrixCollapser<List<User>, User, Long> {

    private UserService userService;

    private Long userId;

    public UserCollapseCommand(UserService userService, Long userId) {
super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapseCommand")).
andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));
this.userService = userService;
this.userId = userId;
} @Override
public Long getRequestArgument() {
return userId;
} /**
*
* @param collapsedRequests 保存了延迟时间窗中收集到的所有获取单个User的请求。通过获取这些请求的参数来组织
* 我们准备的批量请求命令UserBatchCommand实例
* @return
*/
@Override
protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collapsedRequests) {
List<Long> userIds = new ArrayList<>(collapsedRequests.size());
userIds.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));
return new UserBatchCommand(userService, userIds);
} /**
* 在批量请求命令UserBatchCommand实例被触发执行完成后,该方法开始执行,
* 在这里我们通过批量结果batchResponse对象,为collapsedRequests中每个合并前的单个请求设置返回结果。
* 来完成批量结果到单个请求结果的转换
* @param batchResponse 保存了createCommand中组织的批量请求命令的返回结果
* @param collapsedRequests 代表了每个合并的请求
*/
@Override
protected void mapResponseToRequests(List<User> batchResponse, Collection<CollapsedRequest<User, Long>> collapsedRequests) {
System.out.println("mapResponseToRequests========>");
int count = 0;
for(CollapsedRequest<User, Long> collapsedRequest : collapsedRequests) {
User user = batchResponse.get(count++);
collapsedRequest.setResponse(user);
}
}
}

  3.5写一个controller来辅助测试

package org.hope.lee.web;

import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import org.hope.lee.command.UserCollapseCommand;
import org.hope.lee.model.User;
import org.hope.lee.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.Future; @RestController
public class CollapseCommandController { @Autowired
private UserService userService; @RequestMapping(value = "/collapse", method = RequestMethod.GET)
public void requestCollapse() {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
Future<User> f1 = new UserCollapseCommand(userService, 1L).queue();
Future<User> f2 = new UserCollapseCommand(userService, 2L).queue();
Future<User> f3 = new UserCollapseCommand(userService, 3L).queue(); Thread.sleep(3000); Future<User> f4 = new UserCollapseCommand(userService, 4L).queue();
Future<User> f5 = new UserCollapseCommand(userService, 5L).queue(); User u1 = f1.get();
User u2 = f2.get();
User u3 = f3.get(); User u4 = f4.get();
User u5 = f5.get();
System.out.println(u1.getName());
System.out.println(u2.getName());
System.out.println(u3.getName());
System.out.println(u4.getName());
System.out.println(u5.getName());
} catch (Exception e) {
e.printStackTrace();
}finally {
context.close();
}
} }

  3.6 spring boot main方法

package org.hope.lee;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate; @SpringBootApplication
@EnableDiscoveryClient
public class ServiceRibbonApplication { public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
} @Bean
RestTemplate restTemplate() {
return new RestTemplate();
} }

  3.7.配置文件applicationi.properties

eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
server.port=8763
spring.application.name=batch-customer

测试:

  1.运行eureka-server启动注册中心

  2.运行eureka-server-service启动服务提供者

  3.运行ribbon-hystrix启动服务消费者

  4.在浏览器输入: http://localhost:8763/collapse

输出结果:从结果中我们看到前3次请求合并为一个请求,后面2次请求合并为了一个请求

遇到的问题:

  model的实体类一定要定义一个无参数的默认构造器。否则就会报错。

使用注解实现请求合并器

package org.hope.lee.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.apache.commons.lang.StringUtils;
import org.hope.lee.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future; @Service("peopleService")
public class PeopleServiceImpl implements PeopleService {
@Autowired
private RestTemplate restTemplate; @HystrixCollapser(batchMethod = "findAll",
collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")})
public Future<User> find(Long id) {
throw new RuntimeException("This method body should not be executed");
} @HystrixCommand
public List<User> findAll(List<Long> ids) {
System.out.println("Annotation---------" + ids + "Thread.currentThread().getName():" + Thread.currentThread().getName());
User[] users = restTemplate.getForObject("http://localhost:8762/users?ids={1}", User[].class, StringUtils.join(ids, ","));
return Arrays.asList(users);
} }

https://gitee.com/huayicompany/Hystrix-learn/tree/master/hystrix-request-collapsing

参考:

[1]博客,https://segmentfault.com/a/1190000011468804,Spring Cloud中Hystrix的请求合并

[2]官网,https://github.com/Netflix/Hystrix/wiki/How-To-Use#Collapsing, Request Collapsing

[3]《SpringCloud微服务实战》,电子工业出版社,翟永超

Hystrix-request collapsing(请求合并)的更多相关文章

  1. (转)GitHub中PR(Pull request)操作 - 请求合并代码

    转:https://www.jianshu.com/p/b365c743ec8d 前言 本文尽量使用图形工具介绍如何向开源项目提交 Pull Request,一次亲身经历提交 PR 1.fork 项目 ...

  2. hystrix,request collapser,请求合并

    多个商品,需要发送多次网络请求,调用多次接口,才能拿到结果 可以使用HystrixCollapser将多个HystrixCommand合并到一起,多个command放在一个command里面去执行,发 ...

  3. SpringCloud实战-Hystrix线程隔离&请求缓存&请求合并

    接着上一篇的Hystrix进行进一步了解. 当系统用户不断增长时,每个微服务需要承受的并发压力也越来越大,在分布式环境中,通常压力来自对依赖服务的调用,因为亲戚依赖服务的资源需要通过通信来实现,这样的 ...

  4. SpringCloud实战4-Hystrix线程隔离&请求缓存&请求合并

    接着上一篇的Hystrix进行进一步了解. 当系统用户不断增长时,每个微服务需要承受的并发压力也越来越大,在分布式环境中,通常压力来自对依赖服务的调用,因为亲戚依赖服务的资源需要通过通信来实现,这样的 ...

  5. Spring-cloud (九) Hystrix请求合并的使用

    前言: 承接上一篇文章,两文本来可以一起写的,但是发现RestTemplate使用普通的调用返回包装类型会出现一些问题,也正是这个问题,两文没有合成一文,本文篇幅不会太长,会说一下使用和适应的场景. ...

  6. hystrix中request cache请求缓存

    有一个概念,叫做reqeust context,请求上下文,一般来说,在一个web应用中, 我们会在一个filter里面,对每一个请求都施加一个请求上下文,就是说,tomcat容器内,每一次请求,就是 ...

  7. hystrix 请求合并(6)

    hystrix支持N个请求自动合并为一个请求,这个功能在有网络交互的场景下尤其有用,比如每个请求都要网络访问远程资源,如果把请求合并为一个,将使多次网络交互变成一次,极大节省开销.重要一点,两个请求能 ...

  8. 高并发场景-请求合并(一)SpringCloud中Hystrix请求合并

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

  9. 笔记:Spring Cloud Hystrix 异常处理、缓存和请求合并

    异常处理 在 HystrixCommand 实现的run方法中抛出异常,除了 HystrixBadRequestException之外,其他异常均会被Hystrix 认为命令执行失败并触发服务降级处理 ...

随机推荐

  1. Exception: Unexpected End Of File(crontab)

    Exception: Unexpected End Of File [solphire@hadoop02 tools]$ crontab -l 1 * * * * source /etc/profil ...

  2. Micropython教程之TPYBoard制作蓝牙+红外循迹小车

    1.实验目的 学习在PC机系统中扩展简单I/O接口的方法. 进一步学习编制数据输出程序的设计方法. 学习蓝牙模块的接线方法及其工作原理. 学习L298N电机驱动板模块的接线方法. 学习蓝牙控制小车的工 ...

  3. angular4.0命令行汇总

    查看ng命令行 ng help 创建项目 ng new projectName ng new projectName --routing[--routing表示创建带路由的项目] 配置依赖 npm i ...

  4. jQuery实现移动端评测问卷功能

    效果图: 需求: 1.有10道测试题目,单选,选中答案之后,500ms后自动跳转至下一题 2.如果当前题目没有选择答案,将弹窗提示"请选择答案!" 3.点击"上一题&qu ...

  5. 骗子网站,X毛都没有,骗我九十九

    前言 这几天在A市和B市奔波着,眼瞅着自己就要毕业了,必须得出来找份工作了. 和小伙伴在A市兜兜转转了几天,要不就是不合适没下文,要不就是给了offer,工资是在太低.心很累,然后就下B市了,看看B市 ...

  6. PHP读取excel中地址实现多文件下载

    PHP文件下载有单文件和多文件之分,如果是单文件写个方法可以实现,但是如果想循环下载多个文件我试验是没有成功.先说单文件的下载,方法如下: function downfile($fileurl) { ...

  7. Jerry的ABAP, Java和JavaScript乱炖

    写这个系列的初衷是SAP Chengdu office有越来越多的应届毕业生加入,这些新同事通过在大学的专业学习,具备了Java和JavaScript背景,但是进入SAP之后大家觉得ABAP没有Jav ...

  8. linux中使用Python IDE pycharm教程

    今天使用vim编辑Python 并在linux中终端调试的时候,发现每次不是自己想要输出结果的时候,就要用vim编辑代码,再重新回到终端,比较浪费时间.搜索发现pycharm这一个Python ide ...

  9. Chris Richardson微服务翻译:微服务架构中的服务发现

    Chris Richardson 微服务系列翻译全7篇链接: 微服务介绍 构建微服务之使用API网关 构建微服务之微服务架构的进程通讯 微服务架构中的服务发现(本文) 微服务之事件驱动的数据管理 微服 ...

  10. 【SmartOS】轻量级多任务调度系统

    SmartOS是一个完全由新生命团队设计的嵌入式操作系统,主要应用于智能家居.物联网.工业自动化控制等领域. ARM Cortex-M系列微处理器几乎全都做成单核心,对于业务逻辑较复杂的物联网就显得难 ...