User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Spring   ›   SpringCloud   ›   Hystrix   ›   正文
Hystrix

SpringCloud—Hystrix(五)请求合并

2020-08-24 17:53:46
736  0 0
参考目录 隐藏
1) 服务提供者接口
2) 服务消费者
3) BookService
4) BookBatchCommand
5) BookCollapseCommand
6) 测试
7) 通过注解实现请求合并
8) 总结

阅读完需:约 8 分钟

在微服务架构中,我们将一个项目拆分成很多个独立的模块,这些独立的模块通过远程调用来互相配合工作,但是,在高并发情况下,通信次数的增加会导致总的通信时间增加,同时,线程池的资源也是有限的,高并发环境会导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们需要来了解Hystrix的请求合并。

Hystrix中的请求合并,就是利用一个合并处理器,将对同一个服务发起的连续请求合并成一个请求进行处理(这些连续请求的时间窗默认为10ms),在这个过程中涉及到的一个核心类就是HystrixCollapser,OK,接下来我们就来看看如何实现Hystrix的请求合并。

服务提供者接口

我需在在服务提供者中提供两个接口供服务消费者调用,如下:

@RequestMapping("/getbook6")
public List<Book> book6(String ids) {
    System.out.println("ids>>>>>>>>>>>>>>>>>>>>>" + ids);
    ArrayList<Book> books = new ArrayList<>();
    books.add(new Book("《李自成》", 55, "姚雪垠", "人民文学出版社"));
    books.add(new Book("中国文学简史", 33, "林庚", "清华大学出版社"));
    books.add(new Book("文学改良刍议", 33, "胡适", "无"));
    books.add(new Book("ids", 22, "helloworld", "haha"));
    return books;
}

@RequestMapping("/getbook6/{id}")
public Book book61(@PathVariable Integer id) {
    Book book = new Book("《李自成》2", 55, "姚雪垠2", "人民文学出版社2");
    return book;
}

第一个接口是一个批处理接口,第二个接口是一个处理单个请求的接口。在批处理接口中,服务消费者传来的ids参数格式是1,2,3,4…这种格式,正常情况下我们需要根据ids查询到对应的数据,然后组装成一个集合返回,我这里为了处理方便,不管什么样的请求统统都返回一样的数据集;处理单个请求的接口就比较简单了,不再赘述。


服务消费者

OK,服务提供者处理好之后,接下来我们来看看服务消费者要怎么处理。


BookService

首先在BookService中添加两个方法用来调用服务提供者提供的接口,如下:

public Book test8(Long id) {
    return restTemplate.getForObject("http://HELLO-SERVICE/getbook6/{1}", Book.class, id);
}

public List<Book> test9(List<Long> ids) {
    System.out.println("test9---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
    Book[] books = restTemplate.getForObject("http://HELLO-SERVICE/getbook6?ids={1}", Book[].class, StringUtils.join(ids, ","));
    return Arrays.asList(books);
}

test8用来调用提供单个id的接口,test9用来调用批处理的接口,在test9中,我将test9执行时所处的线程打印出来,方便我们观察执行结果,另外,在RestTemplate中,如果返回值是一个集合,我们得先用一个数组接收,然后再转为集合。


BookBatchCommand

OK,BookService中的方法准备好了后,我们就可以来创建一个BookBatchCommand,这是一个批处理命令,如下:

public class BookBatchCommand extends HystrixCommand<List<Book>> {
    private List<Long> ids;
    private BookService bookService;

    public BookBatchCommand(List<Long> ids, BookService bookService) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CollapsingGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("CollapsingKey")));
        this.ids = ids;
        this.bookService = bookService;
    }

    @Override
    protected List<Book> run() throws Exception {
        return bookService.test9(ids);
    }
}

这个类实际上和我们以前介绍的类差不多,都是继承自HystrixCommand,用来处理合并之后的请求,在run方法中调用BookService中的test9方法。


BookCollapseCommand

接下来我们需要创建BookCollapseCommand继承自HystrixCollapser来实现请求合并。如下:

public class BookCollapseCommand extends HystrixCollapser<List<Book>, Book, Long> {
    private BookService bookService;
    private Long id;

    public BookCollapseCommand(BookService bookService, Long id) {
        super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("bookCollapseCommand")).andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));
        this.bookService = bookService;
        this.id = id;
    }

    @Override
    public Long getRequestArgument() {
        return id;
    }

    @Override
    protected HystrixCommand<List<Book>> createCommand(Collection<CollapsedRequest<Book, Long>> collapsedRequests) {
        List<Long> ids = new ArrayList<>(collapsedRequests.size());
        ids.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));
        BookBatchCommand bookBatchCommand = new BookBatchCommand(ids, bookService);
        return bookBatchCommand;
    }

    @Override
    protected void mapResponseToRequests(List<Book> batchResponse, Collection<CollapsedRequest<Book, Long>> collapsedRequests) {
        System.out.println("mapResponseToRequests");
        int count = 0;
        for (CollapsedRequest<Book, Long> collapsedRequest : collapsedRequests) {
            Book book = batchResponse.get(count++);
            collapsedRequest.setResponse(book);
        }
    }
}

关于这个类,我说如下几点:

1.首先在构造方法中,我们设置了请求时间窗为100ms,即请求时间间隔在100ms之内的请求会被合并为一个请求。
2.createCommand方法主要用来合并请求,在这里获取到各个单个请求的id,将这些单个的id放到一个集合中,然后再创建出一个BookBatchCommand对象,用该对象去发起一个批量请求。
3.mapResponseToRequests方法主要用来为每个请求设置请求结果。该方法的第一个参数batchResponse表示批处理请求的结果,第二个参数collapsedRequests则代表了每一个被合并的请求,然后我们通过遍历batchResponse来为collapsedRequests设置请求结果。

OK,所有的这些操作完成后,我们就可以来测试啦。


测试

我们在服务消费者端创建访问接口,来测试合并请求,测试接口如下:

@RequestMapping("/test7")
@ResponseBody
public void test7() throws ExecutionException, InterruptedException {
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    BookCollapseCommand bc1 = new BookCollapseCommand(bookService, 1l);
    BookCollapseCommand bc2 = new BookCollapseCommand(bookService, 2l);
    BookCollapseCommand bc3 = new BookCollapseCommand(bookService, 3l);
    BookCollapseCommand bc4 = new BookCollapseCommand(bookService, 4l);
    Future<Book> q1 = bc1.queue();
    Future<Book> q2 = bc2.queue();
    Future<Book> q3 = bc3.queue();
    Book book1 = q1.get();
    Book book2 = q2.get();
    Book book3 = q3.get();
    Thread.sleep(3000);
    Future<Book> q4 = bc4.queue();
    Book book4 = q4.get();
    System.out.println("book1>>>"+book1);
    System.out.println("book2>>>"+book2);
    System.out.println("book3>>>"+book3);
    System.out.println("book4>>>"+book4);
    context.close();
}

关于这个测试接口我说如下两点:

1.首先要初始化HystrixRequestContext
2.创建BookCollapseCommand类的实例来发起请求,先发送3个请求,然后睡眠3秒钟,再发起1个请求,这样,前3个请求就会被合并为一个请求,第四个请求因为间隔的时间比较久,所以不会被合并,而是单独创建一个线程去处理。

OK,我们来看看执行结果,如下:


通过注解实现请求合并

OK,上面这种请求合并方式写起来稍微有一点麻烦,我们可以使用注解来更优雅的实现这一功能。首先在BookService中添加两个方法,如下:

@HystrixCollapser(batchMethod = "test11",collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "100")})
public Future<Book> test10(Long id) {
    return null;
}

@HystrixCommand
public List<Book> test11(List<Long> ids) {
    System.out.println("test9---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
    Book[] books = restTemplate.getForObject("http://HELLO-SERVICE/getbook6?ids={1}", Book[].class, StringUtils.join(ids, ","));
    return Arrays.asList(books);
}

在test10方法上添加@HystrixCollapser注解实现请求合并,用batchMethod属性指明请求合并后的处理方法,collapserProperties属性指定其他属性。

OK,在BookService中写好之后,直接调用就可以了,如下:

@RequestMapping("/test8")
@ResponseBody
public void test8() throws ExecutionException, InterruptedException {
    HystrixRequestContext context = HystrixRequestContext.initializeContext();
    Future<Book> f1 = bookService.test10(1l);
    Future<Book> f2 = bookService.test10(2l);
    Future<Book> f3 = bookService.test10(3l);
    Book b1 = f1.get();
    Book b2 = f2.get();
    Book b3 = f3.get();
    Thread.sleep(3000);
    Future<Book> f4 = bookService.test10(4l);
    Book b4 = f4.get();
    System.out.println("b1>>>"+b1);
    System.out.println("b2>>>"+b2);
    System.out.println("b3>>>"+b3);
    System.out.println("b4>>>"+b4);
    context.close();
}

和前面的一样,前三个请求会进行合并,第四个请求会单独执行,OK,执行结果如下:


总结

请求合并的优点已经看到了,多个请求被合并为一个请求进行一次性处理,可以有效节省网络带宽和线程池资源,但是,有优点必然也有缺点,设置请求合并之后,本来一个请求可能5ms就搞定了,但是现在必须再等10ms看看还有没有其他的请求一起的,这样一个请求的耗时就从5ms增加到15ms了,不过,如果我们要发起的命令本身就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了,另外高并发也是请求合并的一个非常重要的场景。

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

0 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 593552浏览

随机文章
SpringSecurity—两种资源放行策略
4年前
Java—并发编程—自旋锁、排队自旋锁、MCS锁、CLH锁
4年前
Flowable—流程引擎(待修缮)
3年前
SpringBoot —构建 RESTful 风格应用
5年前
Java—并发编程(七)JUC集合 – (6) ConcurrentSkipListSet
3年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 593552 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付