介绍:  

  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. jQuery源码解析资源便签

    最近开始解读jQuery源码,下面的链接都是搜过来的,当然妙味课堂 有相关的一系列视频,长达100多期,就像一只蜗牛慢慢爬, 至少品读三个框架,以后可以打打怪,自己造造轮子. 完全理解jQuery源代 ...

  2. Koa2和相关资料

    koa2是什么我就不介绍,这里只是收集一些有用的资料,koa这里默认就指koa2了额. koa介绍 koa(GitHub) koa(npm) 文档 Usage Guide Error Handling ...

  3. Ubuntu 搭建简单的git server

    Git 可以使用四种主要的协议来传输资料:本地协议(Local),HTTP 协议,SSH(Secure Shell)协议及 Git 协议. 在此,我们将会讨论那些协议及哪些情形应该使用(或避免使用)他 ...

  4. [转]解析多级json数据为list中嵌套一级字典的形式

    #!/usr/bin/env python # encoding: utf-8 import json def read(obj,key): collect = list() for k in obj ...

  5. 一段shell脚本分析

    工作中碰到这样的需求: 1.每天定时要执行python脚本生成excel 2.将生成的excel拷贝到特定目录下 3.通过python发送脚本发送给特定的接收者 因为之前没有接触过shell脚本,同事 ...

  6. 线上系统/tmp 目录不断增长分析与总结

    1.问题描述 系统配置为单核4G, web 工程配置堆2G,  /tmp目录 二进制文件不断增加,平均一天增加20G, 手动清理/tmp目录,重启系统,问题依旧. 2.分析 /tmp 目录存放系统运行 ...

  7. 阻止a标签的默认事件及延伸

    先贴一段代码 <html lang="en"> <head> <meta charset="UTF-8"> <meta ...

  8. Nexus 6P 解锁+TWRP+CM

    // 这是一篇导入进来的旧博客,可能有时效性问题. 1. 需要用到的文件:Google USB驱动:adb和fastboot工具二进制文件(如果解锁时提示命令无效说明版本过低,需下载使用支持nexus ...

  9. python网络数据采集(伴奏曲)

    这里是前章,我们做一下预备.之前太多事情没能写博客~..             (此博客只适合python3x,python2x请自行更改代码) 首先你要有bs4模块 windows下安装:pip3 ...

  10. “玲珑杯”ACM比赛 Round #19题解&源码【A,规律,B,二分,C,牛顿迭代法,D,平衡树,E,概率dp】

    A -- simple math problem Time Limit:2s Memory Limit:128MByte Submissions:1599Solved:270 SAMPLE INPUT ...