Hystrix 叫做斷路器/熔斷器。微服務(wù)系統(tǒng)中,整個系統(tǒng)出錯的概率非常高,因為在微服務(wù)系統(tǒng)中,涉及到的模塊太多了,每一個模塊出錯,都有可能導(dǎo)致整個服務(wù)出現(xiàn)故障,當(dāng)所有模塊都穩(wěn)定運(yùn)行時,整個服務(wù)才算是穩(wěn)定運(yùn)行。
我們希望當(dāng)整個系統(tǒng)中,某一個模塊無法正常工作時,能夠通過我們提前配置的一些東西,來使得整個系統(tǒng)正常運(yùn)行,即單個模塊出問題,不影響整個系統(tǒng)。
學(xué)習(xí)到這筆者已經(jīng)搭建了三個服務(wù)了。eureka、app-user、app-pay,接下來筆者將創(chuàng)建一個新的服務(wù)app-hystrix。廢話不多說,開干。
創(chuàng)建項目
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
先加入web依賴和Erueka客戶端依賴。暫時不加入Hystrix依賴,我們先來實(shí)現(xiàn)沒有Hystrix的時,發(fā)送異常情況。
項目創(chuàng)建成功之后,在配置文件加入以下配置:
spring:
application:
name: app-hystrix
server:
port: 3000
eureka:
client:
service-url:
defaultZone: http://localhost:1999/eureka
instance:
hostname: localhost
instance-id: ${eureka.instance.hostname}:${server.port}
prefer-ip-address: true
然后,在項目啟動類上添加如下注解,提供一個 RestTemplate 實(shí)例:
@SpringBootApplication
@EnableEurekaClient
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
接下來提供 Hystrix 的接口。
@Service
public class HystrixService {
@Autowired
RestTemplate resTtemplate;
//調(diào)用appUser提供的接口
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}
}
@RestController
public class HystrixController {
@Autowired
HystrixService hystrixService;
@GetMapping("/getUser")
public String getUser() {
return hystrixService.getUser();
}
}
我們在這里需要啟動的工程有如下一些。
? eureka工程: 服務(wù)注冊中心, 端口為1999。
? appUser工程:兩個實(shí)例啟動端口2000和2999。
? appHystrix工程:使用 ribbon 實(shí)現(xiàn)的服務(wù)消費(fèi)者, 端口為3000。
在未加入斷路器之前, 關(guān)閉2999的實(shí)例, 發(fā)送GET請求到http://localhost:3000/getUser, 可以獲得下面的輸出:

下面我們開始引入Spring Cloud Hystrix
- 在appHystrix工程的pom.xml中引入spring-cloud-starter-netflix-hystrix依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在appHystrix工程的主類使用@EnableCircuitBreaker注解開啟斷路器功能
@SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaClient
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
注意:啟動類上的注解,也可以使用 @SpringCloudApplication 代替, 該注解的具體定義如下所示。 可以看到, 該注解中包含了上述我們所引用的三個注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
- 改造服務(wù)消費(fèi)方式,在HystrixService類的getUser函數(shù)上增加
@HystrixCommand注解來指定回調(diào)方法:
/**
* 在這個方法中,我們將發(fā)起一個遠(yuǎn)程調(diào)用,去調(diào)用 appUser 中提供的接口
*
* 但是,這個調(diào)用可能會失敗。
*
* 我們在這個方法上添加 @HystrixCommand 注解,配置 fallbackMethod 屬性,這個屬性表示
* 該方法調(diào)用失敗時的臨時替代方法
*/
@HystrixCommand(fallbackMethod = "error")
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}
/**
* 注意,這個方法名字要和 fallbackMethod 一致
* 方法返回值也要和對應(yīng)的方法一致
* @return
*/
public String error(){
return "error";
}
重新啟動 appHystrix 服務(wù),調(diào)用http://localhost:3000/getUser。當(dāng)輪詢到 2999 服務(wù)端時,輸出內(nèi)容為 error, 不再是之前的錯誤內(nèi)容,Hystrix 的服務(wù)回調(diào)生效。除了通過斷開具體的服務(wù)實(shí)例來模擬某個節(jié)點(diǎn)無法訪問的情況之外, 我們還可以模擬一下服務(wù)阻塞(長時間未響應(yīng))的情況。 我們對 appUser 的/getUser接口做一些修改,具體如下:
@GetMapping
public String getUser() throws InterruptedException {
System.out.println(new Date() + ">>>" );
//讓處理線程等待幾秒鐘
int sleepTime = new Random().nextInt(3000);
System.out.println("sleepTime:" + sleepTime);
Thread.sleep(sleepTime);
return "調(diào)用成功: =====" + port;
}
重啟appUser,這次筆者就不啟動多個實(shí)例了,就啟動2020這個實(shí)例。
通過 Thread. sleep ()函數(shù)可讓/getUser接口的處理線程不是馬上返回內(nèi)容,而是在
阻塞幾秒之后才返回內(nèi)容。 由于 Hystrix 默認(rèn)超時時間為 1000 毫秒, 所以這里采用了 0至3000 毫秒的隨機(jī)數(shù)以讓處理過程有一定概率發(fā)生超時來觸發(fā)斷路器。
工作原理
流程圖

一、創(chuàng)建HystrixCommand或HystrixObservableCommand對象
首先,構(gòu)建一個HystrixCommand或是HystrixObservableCommand對象,用來表示對依賴服務(wù)的操作請求, 同時傳遞所有需要的參數(shù)。 從其命名中我們就能知道它采用了 “命令模式” 來實(shí)現(xiàn)對服務(wù)調(diào)用操作的封裝。 而這兩個 Command 對象分別針對不同的應(yīng)用場景。
? HystrixCommand: 用在依賴的服務(wù)返回單個操作結(jié)果的時候。
? HystrixObservableCommand: 用在依賴的服務(wù)返回多個操作結(jié)果的時候。
二、命令執(zhí)行
從圖中我們可以看到一共存在4種命令的執(zhí)行方式,而 Hystrix在執(zhí)行 時 會根據(jù)創(chuàng)建的
Command對象以及具體的情況來選擇一個執(zhí)行。其中HystrixComrnand實(shí)現(xiàn)了下面兩個
執(zhí)行方式。
- execute (): 同步執(zhí)行,從依賴的服務(wù)返回一個單一的結(jié)果對象, 或是在發(fā)生錯誤
的時候拋出異常。 - queue (): 異步執(zhí)行,直接返回一個Future對象, 其中包含了服務(wù)執(zhí)行結(jié)束時要
返回的單一結(jié)果對象。
String execute = (String) myHystrixCommand.execute();
Future queue = myHystrixCommand2.queue();
而HystrixObservableCommand實(shí)現(xiàn)了另外兩種執(zhí)行方式。
- observe() : 返回Observable對象,它代表了操作的多個結(jié)果,它是一個Hot
Observable。 - toObservable(): 同樣會返回Observable對象, 也代表了操作的多個結(jié)果,
但它返回的是 一個Cold Observable。
Observable observe = command.observe();
Observable observable = command.toObservable();
3、結(jié)果是否被緩存
若當(dāng)前命令的請求緩存功能是被啟用的, 并且該命令緩存命中, 那么緩存的結(jié)果會立即以O(shè)bservable 對象的形式返回。
4、斷路器是否打開
在命令結(jié)果沒有緩存命中的時候, Hystrix在執(zhí)行命令前需要檢查斷路器是否為打開狀態(tài):
- 如果斷路器是打開的,那么Hystrix不會執(zhí)行命令,而是轉(zhuǎn)接到fallback處理邏輯(對
應(yīng)下面第8步)。 - 如果斷路器是關(guān)閉的, 那么Hystrix跳到第5步,檢查是否有可用資源來執(zhí)行命令。
5、線程池\請求隊列\(zhòng)信號量是否占滿
如果與命令相關(guān)的線程池和請求隊列,或者信號量(不使用線程池的時候)已經(jīng)被占
滿, 那么Hystrix也不會執(zhí)行命令,而是轉(zhuǎn)接到fallback處理邏輯(對應(yīng)下面第8步)。
需要注意的是,這里Hystrix所判斷的線程池并非容器的線程池,而是每個依賴服務(wù)的
專有線程池。 Hystrix為了保證不會因為某個依賴服務(wù)的問題影響到其他依賴服務(wù)而采用了“艙壁模式" (Bulkhead Pattern)來隔離每個依賴的服務(wù)。
6、HystrixObservableCommand.construct()或HystrixCommand.run()
Hystrix會根據(jù)我們編寫的方法來決定采取什么樣的方式去請求依賴服務(wù)。
- HystrixCommand.run(): 返回一個單一的結(jié)果,或者拋出異常。
- HystrixObservableCommandconstruct(): 返回一個Observable對象來發(fā)射多個結(jié)果,或通過onError發(fā)送錯誤通知。
如果run()或construet()方法的執(zhí)行時間超過了命令設(shè)置的超時闕值, 當(dāng)前處理
線程將會拋出一個TimeoutException (如果該命令不在其自身的線程中執(zhí)行,則會通過單獨(dú)的計時線程來拋出)。在這種情況下,Hystrix會轉(zhuǎn)接到fallback處理邏輯(第8步)。
同時, 如果當(dāng)前命令沒有被取消或中斷, 那么它最終會忽略run()或者construct ()方法的返回。
如果命令沒有拋出異常并返回了結(jié)果,那么Hystrix在記錄一些日志并采集監(jiān)控報告之后將該結(jié)果返回。在使用run()的情況下,Hystrix會返回一個Observable, 它發(fā)射單個結(jié)果并產(chǎn)生onCompleted的結(jié)束通知; 而在使用construct ()的情況下,Hystrix會直接返回該方法產(chǎn)生的Observable對象。
7、計算斷路器的健康度
Hystrix會將 “ 成功 ”、 “ 失敗”、 “ 拒絕”、 “ 超時 ” 等信息報告給斷路器,而斷路器會維護(hù)一組計數(shù)器來統(tǒng)計這些數(shù)據(jù)。
斷路器會使用這些統(tǒng)計數(shù)據(jù)來決定是否要將斷路器打開,來對某個依賴服務(wù)的請求進(jìn)行 “熔斷/短路 ”,直到恢復(fù)期結(jié)束。 若在恢復(fù)期結(jié)束后, 根據(jù)統(tǒng)計數(shù)據(jù)判斷如果還是未達(dá)到健康指標(biāo),就再次 “ 熔斷/短路 ”。
8、fallback處理
當(dāng)命令執(zhí)行失敗的時候, Hystrix會進(jìn)入fallback嘗試回退處理, 我們通常也稱該操作為 “ 服務(wù)降級”。而能夠引起服務(wù)降級處理的清況有下面幾種:
- 第4步, 當(dāng)前命令處于 “熔斷\短路 ” 狀態(tài), 斷路器是打開的時候。
- 第5步, 當(dāng)前命令的線程池、 請求隊列或者信號量被占滿的時候。
- 第6步,HystrixObservableCommand..construct()或HystrixCommand.run()
拋出異常的時候。
9.、返回成功的響應(yīng)
當(dāng)Hystrix命令執(zhí)行成功之后, 它會將處理結(jié)果直接返回或是以O(shè)bservable 的形式
返回。
請求命令
Hystrix 命令就是我們之前所說的 HystrixCommand, 它用來封裝具體的依賴服務(wù)調(diào)用邏輯。
我們以繼承類的方式來替代前面的注解方式, 比如:
先在appUser服務(wù)中新增方法getName():
@GetMapping("/getName")
public String getName(String name) {
System.out.println(new Date() + ">>>" + name);
return "調(diào)用成功: =====" + name;
}
在appHystrix服務(wù)創(chuàng)建MyHystrixCommand類并繼承HystrixCommand
public class MyHystrixCommand extends HystrixCommand {
private RestTemplate restTemplate;
private String name;
public MyHystrixCommand(Setter setter,RestTemplate restTemplate,String name) {
super(setter);
this.restTemplate = restTemplate;
this.name = name;
}
@Override
protected Object run() {
String forObject = restTemplate.getForObject("http://appUser/api/user/getName?name={1}", String.class, name);
return forObject;
}
}
通過上面實(shí)現(xiàn)的 MyHystrixCommand, 我們既可以實(shí)現(xiàn)請求的同步執(zhí)行也可以實(shí)現(xiàn)異步執(zhí)用邏輯。
@GetMapping("/getUser2")
public void getUser2() throws ExecutionException, InterruptedException {
//同步執(zhí)行
MyHystrixCommand myHystrixCommand = new MyHystrixCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("gongj")), restTemplate,"哈哈");
String execute = (String) myHystrixCommand.execute(); //直接執(zhí)行
System.out.println(execute);
//異步執(zhí)行
MyHystrixCommand myHystrixCommand2 = new MyHystrixCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("gongj")), restTemplate,"哈哈哈哈");
Future queue = myHystrixCommand2.queue(); //先入隊,后執(zhí)行
//調(diào)用get 方法來獲取結(jié)果
String o = (String) queue.get();
System.out.println(o);
}
另外, 也可以通過@HystrixCommand 注解來更為優(yōu)雅地實(shí)現(xiàn) Hystrix 命令的定義,比如:
@HystrixCommand
public String getName(){
String result = resTtemplate.getForObject("http://appUser/api/user/getName?name={1}", String.class,"嘿嘿");
return result;
}
雖然@HystrixCommand 注解可以非常優(yōu)雅地定義 Hystrix 命令的實(shí)現(xiàn), 但是如上定義的 getName 方式只是同步執(zhí)行的實(shí)現(xiàn),若要實(shí)現(xiàn)異步執(zhí)行則還需另外定義,比如:
定義如下方法,返回 Future<String>
//注解異步執(zhí)行
@HystrixCommand
public Future<String> getName2(){
return new AsyncResult<String>() {
@Override
public String invoke() {
String forObject = resTtemplate.getForObject("http://appUser/api/user/getName?name={1}", String.class, "嘿嘿");
return forObject;
}
};
}
然后,調(diào)用該方法:
@GetMapping("/getUser3")
public void getUser3() throws ExecutionException, InterruptedException {
//異步執(zhí)行
Future<String> name2 = hystrixService.getName2();
//調(diào)用get方法來獲取結(jié)果
String o = name2.get();
System.out.println(o);
}
通過繼承的方式使用 Hystrix,如何實(shí)現(xiàn)服務(wù)容錯/降級?重寫繼承類的 getFallback 方法即可:
public class MyHystrixCommand extends HystrixCommand {
private RestTemplate restTemplate;
private String name;
public MyHystrixCommand(Setter setter,RestTemplate restTemplate,String name) {
super(setter);
this.restTemplate = restTemplate;
this.name = name;
}
@Override
protected Object run() {
String forObject = restTemplate.getForObject("http://appUser/api/user/getName?name={1}", String.class, name);
return forObject;
}
@Override
protected Object getFallback() {
return "error";
}
}
在 HystrixObservableCommand 實(shí)現(xiàn)的 Hystrix 命令中, 我們可以通過重載resumeWithFallback 方法來實(shí)現(xiàn)服務(wù)降級邏輯。 該方法會返回 一個 Observable 對
象, 當(dāng)命令執(zhí)行失敗的時候, Hystrix 會將 Observable中的結(jié)果通知給所有的訂閱者。
若要通過注解實(shí)現(xiàn)服務(wù)降級只需要使用@HystrixCommand 中的 fallbackMethod參數(shù)來指定具體的服務(wù)降級實(shí)現(xiàn)方法, 如下所示:
@HystrixCommand(fallbackMethod = "error")
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}
public String error(){
return "error" ;
}
在使用注解來定義服務(wù)降級邏輯時, 我們需要將具體的Hystrix 命令與 fallback 實(shí)現(xiàn)函數(shù)定義在同一個類中, 并且 fallbackMethod 的值必須與實(shí)現(xiàn)fallback 方法的名字相同。 由于必須定義在一個類中, 所以對于 fallback 的訪問修飾符沒有特定的要求, 定義為private、 protected、 public 均可。
在上面的例子中,error方法將在 getUser執(zhí)行時發(fā)生錯誤的情況下被執(zhí)
行。若 error方法實(shí)現(xiàn)的并不是一個穩(wěn)定邏輯,它依然可能會發(fā)生異常, 那么我們也可以為它添加@HystrixCommand 注解以生成 Hystrix 命令, 同時使用 fallbackMethod來指定服務(wù)降級邏輯, 比如:
@HystrixCommand(fallbackMethod = "error")
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}
@HystrixCommand(fallbackMethod = "error1")
public String error(){
return resTtemplate.getForObject("http://appUser/api/user", String.class);
}
public String error1(){
return "error1";
}
@HystrixCommand(fallbackMethod = "error",ignoreExceptions = {ArrayIndexOutOfBoundsException.class})
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}
異常處理
異常傳播
在 HystrixComrnand 實(shí)現(xiàn)的 run() 方法中拋出異常時, 除了 HystrixBadRequestException 之外,其他異常均會被 Hystrix 認(rèn)為命令執(zhí)行失敗并觸發(fā)服務(wù)降級的處理邏輯,所以當(dāng)需要在命令執(zhí)行中拋出不觸發(fā)服務(wù)降級的異常時來使用它。而在使用注冊配置實(shí)現(xiàn) Hystrix 命令時,它還支持忽略指定異常類型功能, 只需要通過設(shè)置 @HystrixComrnand 注解的 ignoreExceptions 參數(shù), 比如:@HystrixCommand(ignoreExceptions = {ArrayIndexOutOfBoundsException.class})。當(dāng)方法拋出了類型為 ArrayIndexOutOfBoundsException的異常時, Hystrix 會將它包裝在 HystrixBadRequestException 中拋出, 這樣就不會觸發(fā)后續(xù)的 fallback 邏輯。
@HystrixCommand(fallbackMethod = "error",ignoreExceptions = {ArrayIndexOutOfBoundsException.class})
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}
異常獲取
當(dāng) Hystrix 命令因為異常(除了 HystrixBadRequestException 的異常)進(jìn)入服務(wù)降級邏輯之后, 往往需要對不同異常做針對性的處理, 那么我們?nèi)绾蝸慝@取當(dāng)前拋出的異常呢?在以傳統(tǒng)繼承方式實(shí)現(xiàn)的 Hystrix 命令中, 我們可以用 getFallback ()方法通過 getExecutionException() 方法來獲取具體的異常, 通過判斷來進(jìn)入不同的處理邏輯。
@Override
protected Object getFallback() {
Throwable executionException = getExecutionException();
System.out.println(executionException.getMessage() + "===");
executionException.printStackTrace();
return "error";
}
除了傳統(tǒng)的實(shí)現(xiàn)方式之外,注解配置方式也同樣可以實(shí)現(xiàn)異常的獲取。 它的實(shí)現(xiàn)也非常簡單, 只需要在 fallback 實(shí)現(xiàn)方法的參數(shù)中增加 Throwable e 對象的定義, 這樣在方法內(nèi)部就可以獲取觸發(fā)服務(wù)降級的具體異常內(nèi)容了, 比如:
public String error1(Throwable t){
return "error1" + t.getMessage();
}
命令名稱、 分組以及線程池劃分
一、繼承方式
以繼承方式實(shí)現(xiàn)的 Hystrix 命令使用類名作為默認(rèn)的命令名稱,我們也可以通過 Setter 靜態(tài)類來設(shè)置, 比如:
// 本例中 commandKey為MyHystrixCommand
// threadPoolKey為gongj
// commandGroupKey為gongj
MyHystrixCommand myHystrixCommand = new MyHystrixCommand(HystrixCommand.Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("gongj"))
.andCommandKey(HystrixCommandKey.Factory.asKey("gongjie")), restTemplate,"哈哈");
從上面 Setter 的使用中可以看到, 我們并沒有直接設(shè)置命令名稱, 而是先調(diào)用了
withGroupKey 來設(shè)置命令組名, 然后才通過調(diào)用 andCommandKey來設(shè)置命令名稱。 這是因為在 Setter 的定義中, 只有 withGroupKey 靜態(tài)函數(shù)可以創(chuàng)建 Setter 的實(shí)例,所以 GroupKey 是每個 Setter 必需的參數(shù), 而 CommandKey 則是一個可選參數(shù)。
通過設(shè)置命令組, Hystrix 會根據(jù)組來組織和統(tǒng)計命令的告警、 儀表盤等信息。 那么為什么一定要設(shè)置命令組呢?因為除了根據(jù)組能實(shí)現(xiàn)統(tǒng)計之外, Hystrix 命令默認(rèn)的線程劃分也是根據(jù)命令分組來實(shí)現(xiàn)的。默認(rèn)情況下, Hystrix 會讓相同組名的命令使用同一個線程池,所以我們需要在創(chuàng)建 Hystrix 命令時為其指定命令組名來實(shí)現(xiàn)默認(rèn)的線程池劃分。(組名與線程池的名稱是一致的)
如果 Hystrix 的線程池分配僅僅依靠命令組來劃分, 那么它就顯得不夠靈活了, 所以Hystrix 還提供了 HystrixThreadPoolKey 來對線程池進(jìn)行設(shè)置, 通過它我們可以實(shí)現(xiàn)更細(xì)粒度的線程池劃分, 比如:
// 本例中 commandKey為gongjie
// threadPoolKey為gongjiePoolKey
// commandGroupKey為gongj
MyHystrixCommand myHystrixCommand = new MyHystrixCommand(
HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("gongj"))
.andCommandKey(HystrixCommandKey.Factory.asKey("gongjie"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("gongjiePoolKey")), restTemplate,"");
如果在沒有特別指定 HystrixThreadPoolKey 的情況下,依然會使用命令組的方式
來劃分線程池。通常情況下,盡量通過 HystrixThreadPoolKey 的方式來指定線程池的劃分, 而不是通過組名的默認(rèn)方式實(shí)現(xiàn)劃分, 因為多個不同的命令可能從業(yè)務(wù)邏輯上來看屬于同一個組, 但是往往從實(shí)現(xiàn)本身上需要跟其他命令進(jìn)行隔離。
二、注解方式
默認(rèn)情況下,@HystrixCommand 注解標(biāo)注的方法名即為命令名稱。命令組名為 @HystrixCommand 注解方法所在類的名稱。線程池名與命令組名一致。如果需要進(jìn)行修改只需設(shè)置@HystrixCommand 注解的 commandKey、 groupKey 以及 threadPoolKey 屬性即可, 它們分別表示了命令名稱、 分組以及線程池劃分, 比如我們可以像下面這樣進(jìn)行設(shè)置:
@HystrixCommand(fallbackMethod = "error",
ignoreExceptions = {ArrayIndexOutOfBoundsException.class},
commandKey = "getUsercommandKey",
groupKey = "getUsergroupKey",
threadPoolKey="getUserthreadPoolKey")
public String getUser(){
String result = resTtemplate.getForObject("http://appUser/api/user", String.class);
return result;
}