Hystrix是一個簡單易用的熔斷中間件,本篇文章會介紹下常規的使用方式。java
fallbackgit
先貼代碼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
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... 後續也會整理對應的配置介紹文章。
commandKey分組內惟一,HystrixCommand和分組、線程池是多對1的關係。分組和線程池不要緊。
同步
從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()); }
先貼代碼
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就是當HystrixCommand 執行失敗的時候走的後備邏輯,只要實現HystrixCommand 的fallback方法便可
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,當備用方案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(); } }
這裏探討的是 主Command裏串行執行 多個Command時的fallback執行邏輯
這裏就不貼代碼了,fallback的跳轉也比較好理解,次command,無論任何一個執行失敗都認爲主command的run執行失敗,進而進入主command的fallback
上面的章節主要在解釋如何使用HystrixCommand,可是咱們在開發中都已經分好了各類業務的servie,如何套入這個Hystrix?
假設咱們要加載商品詳情頁,須要加載商品信息、用戶信息、店鋪信息
接入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,並分配線程池,線程池的容量也就是能下發請求的能力,防止雪崩
使用的介紹先到這裏了,後續你們有什麼建議或者想法能夠一塊兒交流、碰撞。