Hystrix經常使用功能介紹

Hystrix是一個簡單易用的熔斷中間件,本篇文章會介紹下常規的使用方式。java

目錄

  • helloWorld初窺Hystrix
  • HystrixCommand基本配置、同步和異步執行
  • request cache的使用
  • fallbackgit

    • default fallback
    • 單級fallback
    • 多級fallback
    • 主次多HystrixCommand fallback
  • 接入現有業務
  • 總結

helloWorld初窺Hystrix

先貼代碼github

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;

public class CommandHelloWorld extends HystrixCommand<String> {

    private final String name;

    public CommandHelloWorld(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
    }

    @Override
    protected String run() {
        return "Hello " + name + "!";
    }
}

代碼很簡單,聲明一個類CommandHelloWorld,集成HystrixCommand, HystrixCommand攜帶泛型,泛型的類型就是咱們的執行方法run()返回的結果的類型。邏輯執行體就是run方法的實現。
構造方法至少要傳遞一個分組相關的配置給父類才能實現實例化,具體用來幹什麼的後面會描述。緩存

下面測試一下異步

public class CommandHelloWorldTest {
    @Test
    public void test_01(){
        String result = new CommandHelloWorld("world").execute();
        Assert.assertEquals("Hello world!",result);
    }
}

就這樣第一個hellworld就跑起來,so easyide

HystrixCommand基本配置、同步和異步執行

1.HystrixCommand、Group、ThreadPool 配置

Hystrix把執行都包裝成一個HystrixCommand,並啓用線程池實現多個依賴執行的隔離。
上面的代碼集成了HystrixCommand並實現了相似分組key的構造方法,那麼分組是用來作什麼呢?還有沒有其餘相似的東西?怎麼沒有看到線程配置呢?測試

Hystrix每一個command都有對應的commandKey能夠認爲是command的名字,默認是當前類的名字getClass().getSimpleName(),每一個command也都一個歸屬的分組,這兩個東西主要方便Hystrix進行監控、報警等。
HystrixCommand使用的線程池也有線程池key,以及對應線程相關的配置ui

下面是代碼的實現方式
自定義command keythis

HystrixCommandKey.Factory.asKey("HelloWorld")

自定義command groupspa

HystrixCommandGroupKey.Factory.asKey("ExampleGroup")

那麼線程池呢?

HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")

Hystrix command配置有熔斷閥值,熔斷百分比等配置,ThreadPoll有線程池大小,隊列大小等配置,如何設置?
Hystrix的配置能夠經過Setter進行構造

public CommandHelloWorld(){
        super(Setter
                //分組key
                .withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloWorldGroup"))

                //commandKey
                .andCommandKey(HystrixCommandKey.Factory.asKey("commandHelloWorld"))
                //command屬性配置
                .andCommandPropertiesDefaults(HystrixPropertiesCommandDefault.Setter().withCircuitBreakerEnabled(true).withCircuitBreakerForceOpen(true))

                //線程池key
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("helloWorld_Poll"))
                //線程池屬性配置
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(20).withMaxQueueSize(25))
        );
    }

其它的詳細配置可參考https://github.com/Netflix/Hy... 後續也會整理對應的配置介紹文章。

2.HystrixCommand和分組、線程池三者的關係

clipboard.png
commandKey分組內惟一,HystrixCommand和分組、線程池是多對1的關係。分組和線程池不要緊。

3.HystrixCommand如何執行?同步?異步?

同步
從helloWorld的例子能夠看到,咱們實例化了咱們的HelloWorldCommand,調用了execute方法,從而執行了command的Run()。這種是同步的執行方式。

異步執行
在實際業務中,有時候咱們會同時觸發多個業務依賴的調用,而這些業務又相互不依賴這時候很適合並行執行,咱們可使用Future方式,調用command的queue()方法。
咱們能夠再寫一個helloWorld2

public class CommandHelloWorld2 extends HystrixCommand<String> {

    private final String name;

    public CommandHelloWorld2(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.name = name;
    }

    @Override
    protected String run() {
        return "Hello " + name + "!";
    }
}

具體異步調用

@Test
    public void test_02() throws ExecutionException, InterruptedException {
        Future<String> future1 = new CommandHelloWorld("world").queue();
        Future<String> future2 = new CommandHelloWorld2("world").queue();

        Assert.assertEquals("Hello world!",future1.get());
        Assert.assertEquals("Hello world!",future2.get());
    }

request cache的使用

先貼代碼

public class CachedCommand extends HystrixCommand<String> {

    private String key;

    private static final HystrixCommandKey COMMANDKEY = HystrixCommandKey.Factory.asKey("CachedCommand_cmd");

    protected CachedCommand(String key){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CachedCommand"))
                .andCommandKey(COMMANDKEY));
        this.key = key;
    }

    @Override
    protected String getCacheKey() {
        return this.key;
    }

    public static void flushCache(String key) {
        HystrixRequestCache.getInstance(COMMANDKEY,
                HystrixConcurrencyStrategyDefault.getInstance()).clear(key);
    }

    @Override
    protected String run() throws Exception {
        return "hello "+ key +" !";
    }
}

Hystrix的cache,我的的理解就是在上下文中,屢次請求同一個command,返回值不會發生改變的時候可使用。cache若是要生效,必須聲明上下文

HystrixRequestContext context = HystrixRequestContext.initializeContext();

....... command 的調用 ........

context.shutdown();

清緩存,就是先得到到command而後把對應的key刪除

HystrixRequestCache.getInstance(COMMANDKEY,
                HystrixConcurrencyStrategyDefault.getInstance()).clear(key);

接下來看下完整的調用

@Test
    public void test_no_cache(){
        HystrixRequestContext context = HystrixRequestContext.initializeContext();

        String hahahah = "hahahah";
        CachedCommand cachedCommand = new CachedCommand(hahahah);
        Assert.assertEquals("hello hahahah !", cachedCommand.execute());
        Assert.assertFalse(cachedCommand.isResponseFromCache());

        CachedCommand cachedCommand2 = new CachedCommand(hahahah);
        Assert.assertEquals("hello hahahah !", cachedCommand2.execute());
        Assert.assertTrue(cachedCommand2.isResponseFromCache());


        //清除緩存
        CachedCommand.flushCache(hahahah);

        CachedCommand cachedCommand3 = new CachedCommand(hahahah);
        Assert.assertEquals("hello hahahah !", cachedCommand3.execute());
        Assert.assertFalse(cachedCommand3.isResponseFromCache());

        context.shutdown();
    }

fallback

1.單個fallback

fallback就是當HystrixCommand 執行失敗的時候走的後備邏輯,只要實現HystrixCommand 的fallback方法便可

clipboard.png

public class CommandWithFallBack extends HystrixCommand<String> {

    private final boolean throwException;

    public CommandWithFallBack(boolean throwException) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.throwException = throwException;
    }

    @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "success";
        }
    }

    @Override
    protected String getFallback() {
        return "I'm fallback";
    }


}

測試結果

@Test
    public void testSuccess() {
        assertEquals("success", new CommandWithFallBack(false).execute());
    }

    @Test
    public void testFailure() {
        try {
            assertEquals("I'm fallback", new CommandWithFallBack(true).execute());
        } catch (HystrixRuntimeException e) {
            Assert.fail();
        }
    }
2.多級fallback

當咱們執行業務的時候,有時候會有備用方案一、備用方案2,當備用方案1失敗的時候啓用備用方案2,因此可使用多級fallback。
多級fallback沒有名字那麼神祕,說到底其實就是HystrixCommand1執行fallback1, fallback1的執行嵌入HystrixCommand2,當HystrixCommand2執行失敗的時候,觸發HystrixCommand2的fallback2,以此循環下去實現多級fallback,暫未上限,只要你的方法棧撐的起。

多級依賴調用鏈路

代碼實現

command1

public class CommandWithMultiFallBack1 extends HystrixCommand<String> {

    private final boolean throwException;

    public CommandWithMultiFallBack1(boolean throwException) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.throwException = throwException;
    }

    @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "success";
        }
    }

    @Override
    protected String getFallback() {
        return new CommandWithMultiFallBack2(true).execute();
    }


}

command2

public class CommandWithMultiFallBack2 extends HystrixCommand<String> {

    private final boolean throwException;

    public CommandWithMultiFallBack2(boolean throwException) {
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        this.throwException = throwException;
    }

    @Override
    protected String run() {
        if (throwException) {
            throw new RuntimeException("failure from CommandThatFailsFast");
        } else {
            return "I'm fallback1";
        }
    }

    @Override
    protected String getFallback() {
        return "I'm fallback2";
    }

調用測試

@Test
    public void testMultiFailure(){
        try {
            assertEquals("I'm fallback2", new CommandWithMultiFallBack1(true).execute());
        } catch (HystrixRuntimeException e) {
            Assert.fail();
        }
    }
3.主次多HystrixCommand fallback

這裏探討的是 主Command裏串行執行 多個Command時的fallback執行邏輯

clipboard.png

這裏就不貼代碼了,fallback的跳轉也比較好理解,次command,無論任何一個執行失敗都認爲主command的run執行失敗,進而進入主command的fallback

接入現有業務

上面的章節主要在解釋如何使用HystrixCommand,可是咱們在開發中都已經分好了各類業務的servie,如何套入這個Hystrix?

1.模擬業務場景

假設咱們要加載商品詳情頁,須要加載商品信息、用戶信息、店鋪信息
接入Hystrix前的代碼(代碼有點天真,只是爲了表述下意思)

//主功能類
public class GoodsService {

    private UserService userService = new UserService();
    private ShopService shopService = new ShopService();

    /**
     * 獲取商品詳情
     * @return
     */
    public GoodsDetailFrontModel getGoodsFrontDetail(){
        GoodsDetailFrontModel goodsDetailFrontModel = new GoodsDetailFrontModel();
        goodsDetailFrontModel.setTitle("這是一個測試商品");
        goodsDetailFrontModel.setPrice(10000L);

        UserModel userInfo = userService.getUserInfo(1000001L);
        ShopModel shopInfo = shopService.getShopInfo(2001L);

        goodsDetailFrontModel.setShopModel(shopInfo);
        goodsDetailFrontModel.setUserModel(userInfo);

        return goodsDetailFrontModel;
    }

}

//依賴的用戶類
public class UserService {

    /**
     * 獲取用戶信息
     * @param userId
     * @return
     */
    public UserModel getUserInfo(Long userId){
        return new UserModel();
    }
}

下面咱們對用戶服務套入Hystrix,爲了避免侵入咱們依賴的服務,咱們新建一個門戶類,包裝Hystrix相關的代碼

public class UserServiceFacade extends HystrixCommand<UserModel> {

    //原業務service
    private UserService userService = new UserService();

    private Long userId;

    protected UserServiceFacade() {
        super(HystrixCommandGroupKey.Factory.asKey("UserServiceFacade"));
    }

    @Override
    protected UserModel run() throws Exception {
        return userService.getUserInfo(userId);
    }


    /**
      *若是執行失敗則返回遊客身份
     **/
    @Override
    protected UserModel getFallback() {
        UserModel userModel = new UserModel();
        userModel.setName("遊客");
        return userModel;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }
}

而後咱們再看下主執行類

GoodsService

   /**
     * 獲取商品詳情
     * @return
     */
    public GoodsDetailFrontModel getGoodsFrontDetail(){
        GoodsDetailFrontModel goodsDetailFrontModel = new GoodsDetailFrontModel();
        goodsDetailFrontModel.setTitle("這是一個測試商品");
        goodsDetailFrontModel.setPrice(10000L);

        //原寫法
        //UserModel userInfo = userService.getUserInfo(1000001L);

        //這裏替換成了調用用戶門面類
        UserServiceFacade userServiceFacade = new UserServiceFacade();
        userServiceFacade.setUserId(1000001L);
        UserModel userInfo = userServiceFacade.execute();


        ShopModel shopInfo = shopService.getShopInfo(2001L);

        goodsDetailFrontModel.setShopModel(shopInfo);
        goodsDetailFrontModel.setUserModel(userInfo);

        return goodsDetailFrontModel;
    }

上面的代碼提供一個套入的思路,官方原生的Hystrix就是這樣接入的,這裏注意一點,HystrixCommand每次執行都須要new一個,不能使用單例,一個command實例只能執行一次,上面的代碼也就是咱們的userServiceFacade,每次執行都須要new一個新的對象。

總結

上面介紹了Hystrix的常規用法,也是咱們公司目前的使用方式,官網還有HystrixObservableCommand的使用方式介紹,主要是rxjava的使用方式,獲取到observable能夠進行更加靈活的處理,這裏就不介紹了。

回顧下,Hystrix能給咱們帶來什麼好處
1.多業務依賴隔離,不會相互影響,並能夠根據須要給不一樣的依賴分不一樣的線程資源
2.業務依賴fail-fast
3.依賴服務恢復,能合理感知並恢復對服務的依賴
4.對依賴服務限流,Hystrix對每一個業務的依賴都包裝成了一個command,並分配線程池,線程池的容量也就是能下發請求的能力,防止雪崩

使用的介紹先到這裏了,後續你們有什麼建議或者想法能夠一塊兒交流、碰撞。

相關文章
相關標籤/搜索