Spring Cloud-hystrix使用例子(七)

 

继承方式

HystrixCommand

public class UserSelectAllCommand extends HystrixCommand<List<User>> {
    private RestTemplate restTemplate;

    /**
     * 设置线程组 和命令名用于仪表盘统计信息
     * 设置线程组 可以使同一个组使用同一个线程池
     * .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("ThreadPoolKey") 可以跟细粒度的线程池划分
     * @param restTemplate
     */
    public  UserSelectAllCommand(RestTemplate restTemplate){
        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("YouGroupName"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("YouCommandName"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionTimeoutInMilliseconds(10000)//设置超时时间。我这边全局设置无效 就对应设置

                ));
        this.restTemplate=restTemplate;
    }
    @Override
    protected List<User> run() throws Exception {
        return restTemplate.getForObject("http://PROVIDER/user/findAll",List.class);
    }
}
@Controller
@RequestMapping("/UserHystrixCommand")
public class UserHystrixCommandContorller {
    @Autowired
    RestTemplate restTemplate;

    //同步执行
    @RequestMapping("/findAll")
    @ResponseBody
    public List<User> findAll() {
        UserSelectAllCommand userSelectAllCommand = new UserSelectAllCommand(restTemplate);
        return userSelectAllCommand.execute();
    }

    //异步
    @RequestMapping("/findAllAsyn")
    @ResponseBody
    public List<User> findAllAsyn() throws ExecutionException, InterruptedException {
        UserSelectAllCommand userSelectAllCommand = new UserSelectAllCommand(restTemplate);
        return userSelectAllCommand.queue().get();
    }

}

调用execute同步执行  queue 返回Future 异步执行

还可以通过执行

 Observable observable= userSelectAllCommand.toObservable();//订阅的时候发起请求
 Observable observable=userSelectAllCommand.observe();//立即发起请求

通过订阅获得请求结果

    observable.subscribe(new Subscriber() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onNext(Object o) {

            }
        });

HystrixObservableCommand

与HystrixCommand是可以发射多次结果

public class UserSelectObservableCommand extends HystrixObservableCommand<User> {
    /**
     * 设置线程组 和命令名用于仪表盘统计信息
     * 设置线程组 可以使同一个组使用同一个线程池
     * .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("ThreadPoolKey") 可以跟细粒度的线程池划分
     * @param restTemplate
     */
    @Autowired
    RestTemplate restTemplate;
    private  List<Integer> ids;
    public UserSelectObservableCommand(List<Integer> ids, RestTemplate restTemplate) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("YouGroupName"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("YouCommandName"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionTimeoutInMilliseconds(10000)//设置超时时间。我这边全局设置无效 就对应设置

                ));
        this.restTemplate=restTemplate;
        this.ids=ids;
    }

    @Override
    protected Observable<User> construct() {
        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscriber<? super User> subscriber) {
                try{
                    if(!subscriber.isUnsubscribed()){
                        for (Integer id:
                                ids) {

                            MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
                            map.add("id",id.toString());
                            HttpHeaders headers = new HttpHeaders();
                            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                            HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
                            //调用多次服务
                            User user=restTemplate.postForEntity("http://PROVIDER/user/findById", request,User.class).getBody();
                            subscriber.onNext(user);
                        }
                    }
                    subscriber.onCompleted();
                }catch (Exception e){
                    e.printStackTrace();
                    subscriber.onError(e);
                }


            }
        }).subscribeOn(Schedulers.io());
    }

    /**
     * 服务降级执行逻辑
     * 错误 超时 线程池拒绝 断路器熔断 执行
     * @return
     */
    @Override
    protected Observable<User> resumeWithFallback() {
        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscriber<? super User> observer) {
                try {
                    if (!observer.isUnsubscribed()) {
                        User u = new User();
                        observer.onNext(u);
                        observer.onCompleted();
                    }
                } catch (Exception e) {
                    observer.onError(e);
                }
            }
        }).subscribeOn(Schedulers.io());
    }
}
 //异步
    @RequestMapping("/process")
    @ResponseBody
    public void process() throws ExecutionException, InterruptedException {
        UserSelectObservableCommand  userSelectObservableCommand=new UserSelectObservableCommand(Arrays.asList(new Integer[]{1,2,3,4,6}),restTemplate);
        Observable<User> observable= userSelectObservableCommand.observe();
        observable.subscribe(new Subscriber<User>(){
            List<User> users=new ArrayList<User>();

            @Override
            public void onCompleted() {
                users.stream().forEach(c->{
                    System.out.println(c.getName());
                });

            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onNext(User user) {
                users.add(user);
            }
        });

    }

注解方式

@Component
public class UserService {
    @Autowired
    RestTemplate restTemplate;
    
    @HystrixCommand(groupKey = "userService",commandKey = "findAll")
    public List<User> findAll(){
        return restTemplate.getForObject("http://PROVIDER/user/findAll",List.class);
    }

    @HystrixCommand
    public Future<List<User>> findAllAsyn(){
        return new AsyncResult<List<User>>() {
            @Override
            public List<User> invoke() {
               return findAll();
            }
        };
    }

    /**
     * ObservableExecutionMode.EAGER observe ()
     *  ObservableExecutionMode.LAZY toObservable ()
     *  ignoreExceptions 排除异常
     * @param id
     * @return
     */
    @HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER,ignoreExceptions = BusinessException.class)
    public Observable<User> findUserByIdA(Integer id){

       return Observable.create(new Observable.OnSubscribe<User>(){

           @Override
           public void call(Subscriber<? super User> subscriber) {
               //判断是否取消订阅
               if(subscriber.isUnsubscribed()){
                   User user=restTemplate.getForObject("http://PROVIDER/user/findById",User.class);
                   subscriber.onNext(user);
                   subscriber.onCompleted();
               }
           }
       });
    }

}
@Controller
@RequestMapping("/UserHystrixCommandAnotation")
public class UserHystrixCommandAnotationContorller {
    @Autowired
    UserService userService;
    //同步执行
    @RequestMapping("/findAll")
    @ResponseBody
    public List<User> findAll(){
        return userService.findAll();
    }

    //异步
    @RequestMapping("/findAllAsyn")
    @ResponseBody
    public List<User> findAllAsyn() throws ExecutionException, InterruptedException {
       return userService.findAllAsyn().get();
    }



}

 请求缓存

继承方式

public class UserSelectAllCommand extends HystrixCommand<List<User>> {
    private RestTemplate restTemplate;
    public static final   HystrixCommandKey hystrixCommandKey=HystrixCommandKey.Factory.asKey("findAll");;

    /**
     * 设置线程组 和命令名用于仪表盘统计信息
     * 设置线程组 可以使同一个组使用同一个线程池
     * .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("ThreadPoolKey") 可以跟细粒度的线程池划分
     * @param restTemplate
     */
    public  UserSelectAllCommand(RestTemplate restTemplate){

        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("UserService"))
                .andCommandKey(hystrixCommandKey)
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionTimeoutInMilliseconds(10000)//设置超时时间。我这边全局设置无效 就对应设置

                ));
        this.restTemplate=restTemplate;
    }
    @Override
    protected List<User> run() throws Exception {
        System.out.println("执行了....");
        return restTemplate.getForObject("http://PROVIDER/user/findAll",List.class);
    }

    /**
     * 只需要重写这个方法 将开启缓存
     * @return
     */
    @Override
    protected String getCacheKey() {
        return  "UserSelectAllCommand";//因为没有参数所以key是用类名
    }

    /**
     * 清除缓存
     */
    public static  void  clearKey(){
        HystrixRequestCache.getInstance(UserSelectAllCommand.hystrixCommandKey,HystrixConcurrencyStrategyDefault.getInstance() ).clear("UserSelectAllCommand");
    }

}
@Controller
@RequestMapping("/UserHystrixCommand")
    public class UserHystrixCommandContorller {
    @Autowired
    RestTemplate restTemplate;

    //同步执行
    @RequestMapping("/findAll")
    @ResponseBody
    public List<User> findAll() {
        //开启缓存后 必须初始化一个context
        HystrixRequestContext.initializeContext();
        UserSelectAllCommand userSelectAllCommand = new UserSelectAllCommand(restTemplate);
        userSelectAllCommand.execute();
        userSelectAllCommand = new UserSelectAllCommand(restTemplate);
        userSelectAllCommand.execute();
        userSelectAllCommand = new UserSelectAllCommand(restTemplate);
        //清空缓存
        UserSelectAllCommand.clearKey();
        return userSelectAllCommand.execute();
    }

}

注意每个HystrixCommand命令只能调用一次 多次调用会报错

注解方式

@CacheResult 标识结果被缓存 必须配合@HystrixCommand使用 可以使用cacheKeyMethod或者CacheKey设置缓存key
cacheKeyMethod 标识获得缓存key的方法名 参数形式必须与目标方法一致
@CacheRemove 标识将清除指定key的缓存 commandKey 必须指定 用于定位到清除指定命令的缓存cacheKeyMethod 指定清除缓存key或者使用CacheKey指定

cacheKeyMethod 获得清除缓存key的方法名 参数形式必须与目标方法一致

 commandKey 指定需要清除指定命令的缓存

@Cachekey 标识指定目标为缓存的key优先级比cacheKeyMethod低  

指定缓存key的几种方式

@Component
public class UserService {
    @Autowired
    RestTemplate restTemplate;

    /**
     * 如果没有指定cacheKey 则默认是参数
     *
     * @param id
     * @return
     */
    @CacheResult
    @HystrixCommand(ignoreExceptions = BusinessException.class)
    public User findUserById(Integer id) {
        System.out.println("执行了。。。。");
        User user = restTemplate.getForObject("http://PROVIDER/user/findById?id={id}", User.class, id);
        return user;
    }

}
 @RequestMapping("/findById")
    @ResponseBody
    public User findById(Integer id){
        //开启缓存后 必须初始化一个context 可以在过滤器统一实现
        HystrixRequestContext.initializeContext();
        userService.findUserById(id);
        userService.findUserById(id);
        userService.findUserById(id);
        return userService.findUserById(id);
    }

使用cacheMethod定义缓存key

    /**
     * 如果没有指定cacheKey 则默认是参数
     * @param id
     * @return
     */
    @CacheResult(cacheKeyMethod= "getFindUserByIdKey")
    @HystrixCommand(ignoreExceptions = BusinessException.class)
    public User findUserById(Integer id) {
        System.out.println("执行了。。。。");
        User user = restTemplate.getForObject("http://PROVIDER/user/findById?id={id}", User.class, id);
        return user;
    }

    /**
     * 参数要与上面指定方法的一致
     * @param id
     * @return
     */
    public String getFindUserByIdKey(Integer id){
        return  String.valueOf(id);
    }

使用cacheKey定义缓存的key

   /**
     * 如果没有指定cacheKey 则默认是参数
     * @param id
     * @return
     */
    @CacheResult
    @HystrixCommand(ignoreExceptions = BusinessException.class)
    public User findUserById(@CacheKey Integer id) {
        System.out.println("执行了。。。。");
        User user = restTemplate.getForObject("http://PROVIDER/user/findById?id={id}", User.class, id);
        return user;
    }

如果是对象可以使用

 @CacheResult
    @HystrixCommand(ignoreExceptions = BusinessException.class)
    public User findUserById(@CacheKey("{属性名字}") User user) {
        System.out.println("执行了。。。。");
        User user = restTemplate.getForObject("http://PROVIDER/user/findById?id={id}", User.class, id);
        return user;
    }

清除缓存

   /**
     * CacheRemove.commandKey必须指定  通过他能够找到缓存的位置然后通过key删除
     *
     * @param user
     * @return
     */
    @CacheRemove(commandKey = "findUserById")
    @HystrixCommand(ignoreExceptions = BusinessException.class)
    public boolean saveEdit(@CacheKey("id") User user) {
        return true;
    }

commandKey必须指定 用于定位某个命令的key  没显式指定命令 则为方法名    几种指定的key的方式和缓存一致

请求合并 

继承的方式

1.准备一个批量查询的Service

@Service
public class UserService {
    @Autowired
 private RestTemplate restTemplate;

    public List<User> findAll(List<Long> ids){
        List<User> users=restTemplate.getForObject("http://PROVIDER/user?ids={1}", List.class, StringUtils.join(ids,","));
        return  users;
    }
}

2.准备一个批处理Command

public class UserBatchCommand extends HystrixCommand<List<User>> {
    UserService userService;
    List<Long> ids;
    public UserBatchCommand(UserService userService,List<Long> userIds){
        super(Setter.withGroupKey(asKey("userServiceCommand")).andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                .withExecutionTimeoutInMilliseconds(10000)));//设置超时时间。我这边全局设置无效 就对应设置
        this.userService=userService;
        this.ids=userIds;
    }

    @Override
    protected List<User> run() throws Exception {
        return userService.findAll(ids);
    }
}

3.定义请求合并器

/**
 * 第一个泛型参数 为批量处理的请求的返回类型
 * 第二个泛型参数 为单个请求的返回类型
 * 第三个泛型参数 为参数类型
 */
public class UserCollapseCommand extends HystrixCollapser<List<User>,User ,Long> {
    UserService userService;
    Long userId;

    /**
     * 用于获取请求参数
     * @return
     */
    @Override
    public Long getRequestArgument() {
        return userId;
    }
    public  UserCollapseCommand(UserService userService,Long userId){
        super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("userCollapsercommand"))
                .andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));
        this.userService=userService;
        this.userId=userId;
    }


    /**
     * 合并请求产生批量处理的方法
     * @param collection
     * @return
     */
    @Override
    protected HystrixCommand<List<User>> createCommand(Collection<CollapsedRequest<User, Long>> collection) {
        List<Long> userIds=new ArrayList<Long>(collection.size());
        userIds.addAll(collection.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList()));
        return new UserBatchCommand(userService,userIds);
    }

    /**
     * 批量请求获得结果后 将结果拆分 返回给各个原子请求
     * @param users
     * @param collection
     */
    @Override
    protected void mapResponseToRequests(List<User> users, Collection<CollapsedRequest<User, Long>> collection) {
        int count=0;
        ObjectMapper objectMapper=new ObjectMapper();
        for(CollapsedRequest<User,Long> collapsedRequest:collection) {
            User user =objectMapper.convertValue(users.get(count++),User.class);
            collapsedRequest.setResponse(user);
        }
    }
}

4.测试

@RunWith(SpringJUnit4ClassRunner .class)
@SpringBootTest(classes={SpringcloudConsumerApplication.class, hystrixCollapse.hystrixCollapserTest.class})
public class hystrixCollapserTest {

    @Autowired
    UserService userService;
    @Test

     public void  simpleTest() throws ExecutionException, InterruptedException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();

        List<Future<User>> user=new ArrayList<Future<User>>();
        for(long id=0;id<10;id++){
            UserCollapseCommand userCollapseCommand=new UserCollapseCommand(userService,id);
            User user1=userCollapseCommand.queue().get();
            System.out.print(user1.getId());
        }
        Thread.sleep(4000);

     }


}

当在一定窗口期内 的请求 会合并成一个请求   通过HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100) 默认100毫秒

注解方式

@Service
public class UserSerivice {
    @Autowired
    private RestTemplate restTemplate;

    @HystrixCollapser(batchMethod = "findAll",collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds",value="100")})
    public User find(Long id){
        return restTemplate.getForObject("http://PROVIDER/user/{1}", User.class, id);
    }

    /**
     * 直接返回list会转为linkendHashMap 所以这里手动转了一次 正式环境 都用包装类封装一次 ProcessResult7yt  g
     * @param ids
     * @return
     */
    @HystrixCommand(commandProperties={
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000")})
    public List<User> findAll(List<Long> ids){
        List<User> users=restTemplate.getForObject("http://PROVIDER/user?ids={1}", List.class, StringUtils.join(ids,","));
        ObjectMapper objectMapper=new ObjectMapper();
        List<User> userList=new ArrayList<User>();
        for (Object obj:users
             ) {
            userList.add(objectMapper.convertValue(obj,User.class));
        }
        return  userList;
    }
}

 合并器原理图

未使用合并器

使用合并器后

请求合并器虽然能节省线程池的开销  但是因为有窗口期  如果窗口10m  一个请求耗时需要5m  也会等到窗口期过才发起请求

窗口期内有3个以上请求 才推荐使用请求合并

posted @ 2018-12-18 20:38  意犹未尽  阅读(1087)  评论(0编辑  收藏  举报