1. 程式人生 > 實用技巧 >基礎篇——Spring Cloud Hystrix

基礎篇——Spring Cloud Hystrix

一、Hystrix使用之建立請求命令

  繼承HystrixCommand:

public class UserCommand extends HystrixCommand<User> {

    private RestTemplate restTemplate;

    private Long id;

    public UserCommand(Setter setter, RestTemplate restTemplate, Long id) {
        super(setter);
        this.restTemplate = restTemplate;
        
this.id = id; } @Override protected User run() { return restTemplate.getForObject("http://USER-SERVICE/user?id={1}", User.class, id); } }

  注:同步執行,User user = new UserCommand(restTemplate, 1L).execute();

    非同步執行,Future<User> futureUser = new UserCommand(restTemplate, 1L).queue();

    Hot Observable,Observable<String> ho = new UserCommand(restTemplate, 1L).observe();

    Cold Observable,Observable<String> go = new UserCommand(restTemplate, 1L).toObservable();

  註解@HystrixCommand實現同步執行:

public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand
    
public User getUserById(Long id) { return restTemplate.getForObject("http://USER-SERVICE/user?id={1}", User.class, id); } }

  註解@HystrixCommand實現非同步執行:

public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand
    public Future<User> getUserByIdAsync(final String id) {
        return new AsyncResult<User>() {
            @Override
            public User invoke() {
                return restTemplate.getForObject("http://USER-SERVICE/user?id= 
                {1}", User.class, id);
            }
        };
    }
}

  繼承HystrixObservableCommand:

public class UserObservableCommand extends HystrixObservableCommand<User> {

    private RestTemplate restTemplate;

    private Long id;

    public UserObservableCommand(Setter setter, RestTemplate restTemplate, Long id) {
        super(setter);
        this.restTemplate = restTemplate;
        this.id = id;
    }

    @Override
    protected Observable<User> construct() {
        return Observable.create(new Observable.OnSubscribe<User>(){
            @Override
            public void call(Subscribe<? super User> observable) {
                try {
                    if (!observable.isSubscribed()) {
                        User user = restTemplate.getForObject("http://USER-SERVICE/user?id={1}", User.class, id);
                        observable.onNext(user);
                        observable.onCompleted();
                    }
                } catch (Exception e) {
                    observable.onError(e);
                }
            }
        });
    }
}

  註解@HystrixCommand實現響應式命令:

public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand
    public Observable<User> getUserById(final String id) {
        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscribe<? super User> observable) {
                try {
                    if (!observable.isSubscribe()) {
                        User user = restTemplate.getForObject("http://USER-SERVICE/user?id={1}", User.class, id);
                        observable.onNest(user);
                        observable.onCompleted();
                    }
                } catch (Exception e) {
                    observable.onError(e);
                }
            }
        });
    }
}

二、定義服務降級

  對於有返回值的服務,也就是說呼叫者需要請求服務獲取到某些有用的資訊,如果在HystrixCommand或HystrixObservableCommand執行出現錯誤、超時、執行緒池拒絕、斷路器熔斷等情況,需要進行服務降級處理來保障呼叫者的體驗。對於繼承HystrixCommand執行請求服務,需要重寫getFallBack方法;

public class UserCommand extends HystrixCommand<User> {

    @Override
    protected User getFallBack() {
        return new User();
    }
}

  對於繼承HystrixObservableCommand執行請求服務,需要重寫resumeWithFallBack方法;

public class UserObservableCommand extends HystrixObservableCommand<User> {

    @Override
    protected User resumeWithFallBack() {
        return new User();
    }
}

  對於註解@HystrixCommand執行請求服務,需要使用屬性fallBackMethod="方法名",且“方法名”必須在同一類中;

public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(fallBackMethod="defaultUserById")
    public Observable<User> getUserById(final String id) {
        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscribe<? super User> observable) {
                try {
                    if (!observable.isSubscribe()) {
                        User user = restTemplate.getForObject("http://USER-SERVICE/user?id={1}", User.class, id);
                        observable.onNest(user);
                        observable.onCompleted();
                    }
                } catch (Exception e) {
                    observable.onError(e);
                }
            }
        });
    }

    protected User defaultUserById() {
        return new User();
    }
}

  當然對於一些不需要服務返回必須有用資訊的請求,可以選擇不使用服務降級處理,可以直接將錯誤資訊返回給呼叫者即可。例如:使用HystrixCommand請求寫操作的服務,如果寫入不成功,直接將錯誤返回給呼叫者,通知其稍後重試即可。

三、異常處理

  Hystrix執行請求服務的異常有兩種,HystrixBadRequestException和其他。因為服務降級策略的存在,除了由HystrixBadRequestException包裝的異常外,其他的所有異常都會觸發服務降級策略。註解@HystrixCommand通過屬性ignoreExceptions實現指定異常向HystrixBadRequestException的包裝,當服務請求出現指定異常時,不觸發服務降級策略。

  當發生服務降級時,獲取詳細的異常服務,在繼承類HystrixCommand或HystrixObserableCommand中,通過其父類方法getExecutionException獲得;

    @Override
    protected User getFallBack() {
        Throwable e = getExecutionException();
        return new User();
    }

  註解@HystrixCommand屬性fallBackMethod指定方法中接收引數Throwable e;

    protected User defaultUserById(Throwable e) {
        return new User();
    }

四、命令名稱、分組以及執行緒池劃分

  分組、命令名稱和執行緒池劃分是Hystrix對服務請求的細粒化分配,即此次服務請求被分配到某組某個執行緒池的某個命令,能夠更好的統計和分配,在繼承方式中設定組名、命令名稱和劃分的執行緒池名稱;

    public UserCommand(RestTemplate restTemplate, Long id) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupName"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("CommandName"))
                .andThreadPoolKey(HystrixCommandThreadPoolKey.Factory.asKey("ThreadPoolKey")));
        this.restTemplate = restTemplate;
        this.id = id;
    }

  註解@HystrixCommand通過屬性groupKey、commandKey、threadPoolKey來設定服務請求的組、命令和執行緒池劃分;

    @HystrixCommand(fallBackMethod="defaultUserById", groupKey="UserGroup", commandKey="getUserById", threadPoolKey="userThreadPool")
    public Observable<User> getUserById(final String id) {
        return Observable.create(new Observable.OnSubscribe<User>() {
            @Override
            public void call(Subscribe<? super User> observable) {
                try {
                    if (!observable.isSubscribe()) {
                        User user = restTemplate.getForObject("http://USER-SERVICE/user?id={1}", User.class, id);
                        observable.onNest(user);
                        observable.onCompleted();
                    }
                } catch (Exception e) {
                    observable.onError(e);
                }
            }
        });
    }