一、Spring Cloud Feign + Hystrix使用
前面两篇已经讲过了直接HTTP调用,和用feign,用erueka注册中心做RPC调用。
这张加上Hystrix做熔断降级的使用方式。不整个项目截图了,就做在前面两张的基础之前需要做什么修改和注意什么。
首先是服务A调用服务B,那么实在服务A中加入Hystrix相关的东西
对应到demo就是在order订单系统里面加Hystrix,不需要在item商品系统做修改
依赖需要加入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
applicatiom.yml需要加入
#开启hystrix断路器
feign:
hystrix:
enabled: true
此次还能有其他的hystrix配置,这里没做介绍,只是用默认的,thread线程隔离
启动类需要加入
@EnableHystrix
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
@SpringBootApplication
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class, args);
}
@Bean
/**
* 如果要使用ribbon负载需要加上@LoadBalanced
* */
@LoadBalanced
public RestTemplate restTemplate() {
/**
* 向Spring容器中定义RestTemplate对象 这里就是表示用OKHTTP
* @return
*/
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
2.在服务中使用hystrix有三种方式
方法1
1.新建一个Fallback类,继承feignclient类。
在这个类里面写的逻辑就是服务降级的时候执行的逻辑
package com.sid.rpc.fallback;
import com.sid.rpc.model.Item;
import com.sid.rpc.feignClient.ItemFeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 此类中的方法专门用于服务降级,该类一般要实现调用远程服务的接口(这样保证方法名一致)
*/
@Component
public class ItemServiceFallback implements ItemFeignClient {
/**
* 服务降级的方法要和原方法一致(名称、参数列表)
* @param id
* @return
*/
@Override
public Item queryItemById(@PathVariable("id") Long id) {
return new Item(null, "服务降级方法queryItemById", null);
}
}
2.在@FeignClient中指定fallback
@FeignClient(value = "app-item",fallback = ItemServiceFallback.class)
package com.sid.rpc.feignClient;
import com.sid.rpc.fallback.ItemServiceFallback;
import com.sid.rpc.model.Item;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(value = "app-item",fallback = ItemServiceFallback.class)
public interface ItemFeignClient {
@RequestMapping(value = "/item/{id}", method = RequestMethod.GET)
Item queryItemById(@PathVariable("id") Long id);
}
3.该方法的缺点
缺点: 无法得到throwable
RPC调用的throwable得不到
比如说,A调用B,B里面有个除以零的异常,A这边是不知道发生了啥,只知道调用服务B发生了服务降级,不知道是连接超时吗 B返回了异常吗 B报错了吗,A什么都不知道
Hystrix封装后的异常也得不到
方法2
这里的代码例子是business服务调用storage服务
1.在RPC调用方法上加上注解
@HystrixCommand(fallbackMethod = "deductFallbackMethod")
这就就指定了该方法的降级方法是哪个
2.写这个降级方法deductFallbackMethod的逻辑
package com.sid.rpc.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.sid.rpc.feignClient.StorageFeignClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class StorageService {
@Resource
StorageFeignClient storageFeignClient;
/**
* 这里要注意一下,如果使用@HystrixCommand方式指定降级的方法,那么即使
* feign:
* hystrix:
* enabled: false
* RPC调用的时候也会使用Hystrix的线程池hystrix-StorageService-2,而不是http-nio-8085-exec-4
* */
@HystrixCommand(fallbackMethod = "deductFallbackMethod")
public String deduct(String commodityCode, int count){
return storageFeignClient.deduct(commodityCode,count);
}
/**
* 请求失败执行的方法
* fallbackMethod的方法参数个数类型要和原方法一致
*
* 这里看到的Throwable的异常,是Hystrix封装过的,比如HystrixTimeOutException,HystrixRunTimeException
* * 不会显示storage服务到底返回了什么内容
* * 且会显示
* deductFallbackMethod throwable:com.netflix.hystrix.exception.HystrixRuntimeException: StorageFeignClient#deduct(String,int) failed and no fallback available.
*
* @param commodityCode
* @param count
* @return
*/
public String deductFallbackMethod(String commodityCode, int count, Throwable throwable){
System.out.println("deductFallbackMethod throwable:" + throwable.fillInStackTrace());
return "StorageService.deduct fail 服务降级";
}
}
3.缺点
缺点1.如果使用@HystrixCommand方式指定降级的方法,那么即使 feign.hystrix.enabled=false RPC调用的时候也会使用Hystrix的线程池hystrix-StorageService-2,而不是http-nio-8085-exec-4
缺点2. 这里看到的Throwable的异常,是Hystrix封装过的,比如HystrixTimeOutException,HystrixRunTimeException
不会显示storage服务到底返回了什么内容
且会显示deductFallbackMethod throwable:com.netflix.hystrix.exception.HystrixRuntimeException: StorageFeignClient#deduct(String,int) failed and no fallback available.
方法3
这个例子是order服务调用Account服务
1.新建一个类实现FallbackFactory接口
package com.sid.rpc.fallback;
import com.sid.rpc.feignClient.AccountFeignClient;
import feign.hystrix.FallbackFactory;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class AccountServiceFallback implements FallbackFactory<AccountFeignClient> {
private static final Logger logger = LoggerFactory.getLogger(AccountServiceFallback.class);
@Override
public AccountFeignClient create(Throwable throwable) {
String msg = throwable == null ? "" : throwable.getMessage();
/**
* 这里就能看到account的debit方法被调用里面抛出了除以0的异常
*
* 2021-03-19 10:53:12.016 ERROR[hystrix-app-account-2]com.sid.rpc.fallback.AccountServiceFallback.create:39 -AccountServiceFallback 中有方法降级,throwable.getMessage():status 500 reading AccountFeignClient#debit(String,Double); content:
* {"timestamp":"2021-03-19T02:53:12.008+0000","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/account/debit"}
* */
if (!StringUtils.isEmpty(msg)) {
logger.error("AccountServiceFallback 中有方法降级,throwable.getMessage():" + msg);
}
return new AccountFeignClient() {
@Override
public String debit(String userId, Double money) {
return "AccountService.debit fail 服务降级";
}
};
}
}
2.在@FeignClient中加入fallbackFactory = AccountServiceFallback.class,指定降级方法的实现类
package com.sid.rpc.feignClient;
import com.sid.config.FeignConfiguration;
import com.sid.rpc.fallback.AccountServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "app-account",configuration = FeignConfiguration.class,fallbackFactory = AccountServiceFallback.class)
public interface AccountFeignClient {
@RequestMapping(value = "/account/debit", method = RequestMethod.POST)
String debit(@RequestParam("userId") String userId,@RequestParam("money") Double money);
}
3.优点
这里就能看到account的debit方法被调用里面抛出了除以0的异常
2021-03-19 10:53:12.016 ERROR[hystrix-app-account-2]com.sid.rpc.fallback.AccountServiceFallback.create:39 -AccountServiceFallback 中有方法降级,throwable.getMessage():status 500 reading
AccountFeignClient#debit(String,Double); content: * {"timestamp":"2021-03-19T02:53:12.008+0000","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/account/debit"}