Sentinel通过各种规则对资源进行保护,即进行服务容错。Feign使得调用远程服务就像调用本地服务一样简单。但是Feign在调用远程服务的时候,由于远程服务的原因,可能会产生异常。就需要进行相应的容错设计。
下面介绍Feign整合Sentinel实现服务容错:
1 引入sentinel的依赖
<!--Sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2 在配置文件中开启Feign对Sentinel的支持
feign:
sentinel:
enabled: true
3 创建容错类
容错类的创建有两种方案:
- 直接继承被容错的接口,并为每个方法实现容错方案
- 实现FallbackFactory接口
推荐使用第2种方案,实现FallbackFactory接口。这样可以在容错工厂类中拿到具体的错误,便于后期排查问题。
3.1 方案一:直接继承被容错的接口,并为每个方法实现容错方案
package cn.jack.fallback;
import cn.jack.domain.Product;
import cn.jack.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ProductServiceFallback implements ProductService {
@Override
public Product findByPid(Long pid) {
Product product = new Product();
product.setPid(-100);
product.setPname("远程服务调用出现问题了。");
return product;
}
}
3.2 方案二:实现FallbackFactory接口
package cn.jack.fallback;
import cn.jack.domain.Product;
import cn.jack.service.ProductService;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ProductServiceFallbackFactory implements FallbackFactory<ProductService> {
@Override
public ProductService create(Throwable throwable) {
// lambda表达式可以用于简化创建匿名内部类对象(前提是该接口为函数式接口,即只有一个抽象方法)
return (Long pid) -> {
log.error("{}", throwable);
Product product = new Product();
product.setPid(-100);
product.setPname("远程服务调用出现问题了。 --byFactory");
return product;
};
}
}
4 为被容错的interface指定容错类(容错工厂类)
package cn.jack.service;
import cn.jack.domain.Product;
import cn.jack.fallback.ProductServiceFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Primary;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
// value是nacos下的服务名
// fallbackFactory用于指定容错工厂类
// fallback用于指定容错类
@FeignClient(value = "service-product",
fallbackFactory = ProductServiceFallbackFactory.class)
@Primary
public interface ProductService {
@RequestMapping(value = "/product/{pid}") // @FeignClient + @RequestMapping 就是一个完整的请求路径: http://service-product/product/{pid}
Product findByPid(@PathVariable("pid") Long pid);
}
5 修改controller
package cn.jack.controller;
import cn.jack.domain.Order;
import cn.jack.domain.Product;
import cn.jack.service.OrderService;
import cn.jack.service.ProductService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private ProductService productService;
/**
* 下单 -- 基于Feign实现服务调用
* @param pid 商品id
* @return
*/
@RequestMapping("/order/prod