Spring Cloud 学习笔记 —— Hystrix 请求合并

Hystrix 请求合并

  • Hystrix 请求合并,是把接口相同、参数不同的请求封装成一个请求,比如 A 、B 、C 三个请求调用一个接口,相隔时间 200 毫秒,那完全可一起发送,提供发送效率

(1)请求命令方式

  • 先在 provider 中提供一个接口:
@RestController
public class UserController {
    @GetMapping("/user/{ids}")
    public List<User> getUserByIds(@PathVariable String ids){
        System.out.println(ids);
        String[] split = ids.split(",");
        List<User> users = new ArrayList<>();
        for (int i = 0; i < split.length; i++) {
            User u = new User();
            u.setId(Integer.parseInt(split[i]));
            users.add(u);
        }
        return users;
    }
}
  • 这个接口,可以处理多个请求,也可以处理一个请求(List 中只有一个 User 对象)
  • 定义UserService,构造方法封装请求需要的内容
@Service
public class UserService {
    @Autowired
    RestTemplate restTemplate;

    public List<User> getUserByIds(List<Integer> ids){
        String idsStr = StringUtils.join(ids, ",");
        System.out.println(idsStr);
        User[] forObject = restTemplate.getForObject("http://provider/user/{1}", User[].class, idsStr);
        return Arrays.asList(forObject);
    }
}
  • 定义普通的请求命令类 UserBatchCommand
/*请求命令的command*/
public class UserBatchCommand extends HystrixCommand<List<User>> {
    private List<Integer> ids;
    private UserService userService;

    public UserBatchCommand( List<Integer> ids, UserService userService) {
        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("batchCommand")).
                andCommandKey(HystrixCommandKey.Factory.asKey("batchKey")));
        this.ids = ids;
        this.userService = userService;
    }

    @Override
    protected List<User> run() throws Exception {
        return userService.getUserByIds(ids);
    }
}
  • 到这里只是,普通的请求命令方式,接下来定义请求和并的UserCollapseCommand
public class UserCollapseCommand extends HystrixCollapser<List<User>, User, Integer> {
   private UserService userService;
   private Integer id;

   public UserCollapseCommand( UserService userService, Integer id) {
       super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapseCommand"))
               .andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(200)));
       this.userService = userService;
       this.id = id;
   }

   /*请求中的 id 参数*/
   @Override
   public Integer getRequestArgument() {
       return id;
   }
   /*请求合并方法*/
   @Override
   protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Integer>> collection) {
       List<Integer> ids = new ArrayList<>(collection.size());
       for (CollapsedRequest<User, Integer> userIntegerCollapsedRequest : collection) {
           ids.add(userIntegerCollapsedRequest.getArgument());

       }
       return new UserBatchCommand(ids,userService);
   }
   /*请求结果分发*/
   @Override
   protected void mapResponseToRequests(List<User> users, Collection<CollapsedRequest<User, Integer>> collection) {
       int count = 0;
       for (CollapsedRequest<User, Integer> request : collection) {
           request.setResponse(users.get(count++));
       }
   }
}
  • Setter 中定义withTimerDelayInMilliseconds(200),意思是相隔 200 毫秒内的请求,如果请求的接口相同,那么可以一起发送,然后等 200 毫秒,如果还有相同的接口,那么合并稍后一起发送,然后再等 200 毫秒,依此类推,如果 200 毫秒内没有相同请求,那么就合并一起发送
  • 在这里插入图片描述
  • 继承类的参数 HystrixCollapser<BatchReturnType, ResponseType, RequestArgumentType>,BatchReturnType 是 provider 中接口的返回类型,ResponseType 是每个请求没有合并前,单独返回给前台的数据类型,RequestArgumentType 是每个请求的的参数类型
  • createCommand是请求合并的方法,返回的HystrixCommand类型,就是普通请求命令方式的类,Collection<CollapsedRequest<User, Integer>>封装的是每个请求,从请求中处理参数
  • mapResponseToRequests是请求结果分发的方法,Collection<CollapsedRequest<User, Integer>>封装的是每个请求,把请求的结果分发给每个请求
  • 定义调用方法,注意要用 HystrixRequestContext 的 initializeContext() 、close()方法包围起来
@GetMapping("/hello5")
    public void hello5() throws ExecutionException, InterruptedException {
        HystrixRequestContext cxt = HystrixRequestContext.initializeContext();

        UserCollapseCommand command1 = new UserCollapseCommand(userService, 99);
        UserCollapseCommand command2 = new UserCollapseCommand(userService, 98);
        UserCollapseCommand command3 = new UserCollapseCommand(userService, 97);
        Future<User> q1 = command1.queue();
        Future<User> q2 =  command2.queue();
        Future<User> q3 = command3.queue();
        User u1 = q1.get();
        User u2 = q2.get();
        User u3 = q3.get();
        Thread.sleep(2000);
        UserCollapseCommand command4 = new UserCollapseCommand(userService, 96);
        Future<User> q4 = command4.queue();
        User u4 = q4.get();
        System.out.println(u1);
        System.out.println(u2);
        System.out.println(u3);
        System.out.println(u4);
        cxt.close();
    }
  • 启动服务
    在这里插入图片描述
    在这里插入图片描述

  • 调用 hello5 请求,,查看 provider 中的日志:
    在这里插入图片描述

  • 前三次的请求被封装成一个请求,请求了一次,而后线程暂停了 2 秒,第 4 个请求独立发送,一共请求了两次。

(2)注解方式

  • 注解方式真简单,定义 getUserById 方法
@Service
public class UserService {
    @Autowired
    RestTemplate restTemplate;
    @HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {@HystrixProperty(name="timerDelayInMilliseconds",value = "200")})
    public Future<User> getUserById(Integer id){
        return null;
    }
    @HystrixCommand
    public List<User> getUserByIds(List<Integer> ids){
        String idsStr = StringUtils.join(ids, ",");
        System.out.println(idsStr);
        User[] forObject = restTemplate.getForObject("http://provider/user/{1}", User[].class, idsStr);
        return Arrays.asList(forObject);
    }
}
  • 注意返回值是 Future<User>,参考请求命令方式调用的 queue()入队方法;
    @HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {@HystrixProperty(name="timerDelayInMilliseconds",value = "200")})的意思是把调用这个方法的请求都合并一下,合并完调用 getUserByIds方法,请求等待的是时间间隔 200 毫秒,getUserByIds是我们定义一般调用接口的方式,即调用 provider 服务的方式,这个方法上要加上@HystrixCommand注解
  • 定义出发注解来处理请求合并的请求 hello6 ,注意要用 HystrixRequestContext 的 initializeContext() 、close()方法包围起来
@GetMapping("hello6")
    public void hello6() throws ExecutionException, InterruptedException {
        HystrixRequestContext cxt = HystrixRequestContext.initializeContext();

        Future<User> q1 = userService.getUserById(99);
        Future<User> q2 = userService.getUserById(98);
        Future<User> q3 = userService.getUserById(97);
        User u1 = q1.get();
        User u2 = q2.get();
        User u3 = q3.get();
        Thread.sleep(2000);
        Future<User> q4 = userService.getUserById(96);
        User u4 = q4.get();
        System.out.println(u1);
        System.out.println(u2);
        System.out.println(u3);
        System.out.println(u4);
        cxt.close();
    }
  • 重启服务,访问 hello6 接口,查看 provider 服务的控制台日志
    在这里插入图片描述

在这里插入图片描述

  • 与请求命令的结果一样

总结一下

至此,两种方式实现请求合并就完成了,推荐使用注解方式,因为目前使用来看,注解的代码量少,功能比请求命令要完善,强大。推荐优先使用注解方式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值