SA实战 ·《SpringCloud Alibaba实战》第10章(二)
为远程调用实现容错
(1)需要在订单微服务shop-order中,为远程调用接口实现容错方法。这里,先为用户微服务实现容错。在订单微服务中新建io.binghe.shop.order.Feign.fallback 包,并在 io.binghe.shop.order.Feign.fallback包下创建UserServiceFallBack类实现UserService接口,用于调用用户微服务的容错类,如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 用户服务容错类
*/
@Component
public class UserServiceFallBack implements UserService {
@Override
public User getUser(Long uid) {
User user = new User();
user.setId(-1L);
return user;
}
}
注意:容错类需要实现一个被容错的接口,并实现这个接口的方法。
接下来,在订单微服务的io.binghe.shop.order.Feign.UserService接口上的@FeignClient注解上指定容错类,如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 调用用户微服务的接口
*/
@FeignClient(value = "server-user", fallback = UserServiceFallBack.class)
public interface UserService {
@GetMapping(value = "/user/get/{uid}")
User getUser(@PathVariable("uid") Long uid);
}
(2)在订单微服务中的 io.binghe.shop.order.Feign.fallback包下创建ProductServiceFallBack类实现ProductService接口,用于调用商品微服务的容错类,如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 商品微服务的容错类
*/
@Component
public class ProductServiceFallBack implements ProductService {
@Override
public Product getProduct(Long pid) {
Product product = new Product();
product.setId(-1L);
return product;
}
@Override
public Result<Integer> updateCount(Long pid, Integer count) {
Result<Integer> result = new Result<>();
result.setCode(1001);
result.setCodeMsg("触发了容错逻辑");
return result;
}
}
接下来,在订单微服务的io.binghe.shop.order.fegin.ProductService接口的@FeignClient注解上指定容错类,如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 调用商品微服务的接口
*/
@FeignClient(value = "server-product", fallback = ProductServiceFallBack.class)
public interface ProductService {
/**
* 获取商品信息
*/
@GetMapping(value = "/product/get/{pid}")
Product getProduct(@PathVariable("pid") Long pid);
/**
* 更新库存数量
*/
@GetMapping(value = "/product/update_count/{pid}/{count}")
Result<Integer> updateCount(@PathVariable("pid") Long pid, @PathVariable("count") Integer count);
}
(3)修改订单微服务的业务实现类中提交订单的业务方法,这里修改的方法位于io.binghe.shop.order.service.impl.OrderServiceV6Impl类中,同时需要将类上的@Service注解中指定bean的名称为orderServiceV6。
@Slf4j
@Service("orderServiceV6")
public class OrderServiceV6Impl implements OrderService {
//省略所有代码
}
在提交订单的业务方法中,修改前的代码片段如下所示。
User user = userService.getUser(orderParams.getUserId());
if (user == null){
throw new RuntimeException("未获取到用户信息: " + JSONObject.toJSONString(orderParams));
}
Product product = productService.getProduct(orderParams.getProductId());
if (product == null){
throw new RuntimeException("未获取到商品信息: " + JSONObject.toJSONString(orderParams));
}
//#####################省略N行代码##########################
Result<Integer> result = productService.updateCount(orderParams.getProductId(), orderParams.getCount());
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("库存扣减失败");
}
修改后的代码片段如下所示。
User user = userService.getUser(orderParams.getUserId());
if (user == null){
throw new RuntimeException("未获取到用户信息: " + JSONObject.toJSONString(orderParams));
}
if (user.getId() == -1){
throw new RuntimeException("触发了用户微服务的容错逻辑: " + JSONObject.toJSONString(orderParams));
}
Product product = productService.getProduct(orderParams.getProductId());
if (product == null){
throw new RuntimeException("未获取到商品信息: " + JSONObject.toJSONString(orderParams));
}
if (product.getId() == -1){
throw new RuntimeException("触发了商品微服务的容错逻辑: " + JSONObject.toJSONString(orderParams));
}
//#####################省略N行代码##########################
Result<Integer> result = productService.updateCount(orderParams.getProductId(), orderParams.getCount());
if (result.getCode() == 1001){
throw new RuntimeException("触发了商品微服务的容错逻辑: " + JSONObject.toJSONString(orderParams));
}
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("库存扣减失败");
}
可以看到,修改后的提交订单的业务方法主要增加了服务容错的判断逻辑。
(4)在io.binghe.shop.order.controller.OrderController中注入bean名称为orderServiceV6的OrderService对象,如下所示。
@Autowired
@Qualifier(value = "orderServiceV6")
private OrderService orderService;
至此,我们在项目中使用Sentinel实现了服务容错的功能。
测试服务容错
(1)停掉所有的商品微服务(也就是只启动用户微服务和订单微服务),在浏览器中访问http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1,结果如下所示。
返回的原始数据如下所示。
{"code":500,"codeMsg":"执行失败","data":"触发了商品微服务的容错逻辑: {\"count\":1,\"empty\":false,\"productId\":1001,\"userId\":1001}"}
说明停掉所有的商品微服务后,触发了商品微服务的容错逻辑。
(2)停掉所有的用户微服务(也就是只启动商品微服务和订单微服务)在浏览器中访问http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1,结果如下所示。
返回的原始数据如下所示。
{"code":500,"codeMsg":"执行失败","data":"触发了用户微服务的容错逻辑: {\"count\":1,\"empty\":false,\"productId\":1001,\"userId\":1001}"}
(3)停掉所有的用户微服务和商品微服务(也就是只启动订单微服务),在浏览器中访问http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1,结果如下所示。
返回的原始数据如下所示。
{"code":500,"codeMsg":"执行失败","data":"触发了用户微服务的容错逻辑: {\"count\":1,\"empty\":false,\"productId\":1001,\"userId\":1001}"}
说明项目集成Sentinel成功实现了服务的容错功能。
容错扩展
如果想要在订单微服务中获取到容错时的具体信息时,可以按照如下方式实现容错方案。
实现容错时获取异常
(1)在订单微服务shop-order中新建io.binghe.shop.order.fegin.fallback.factory包,在io.binghe.shop.order.fegin.fallback.factory包中新建UserServiceFallBackFactory类,并实现FallbackFactory接口,FallbackFactory接口的泛型指定为UserService,源码如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 用户微服务容错Factory
*/
@Component
public class UserServiceFallBackFactory implements FallbackFactory<UserService> {
@Override
public UserService create(Throwable cause) {
return new UserService() {
@Override
public User getUser(Long uid) {
User user = new User();
user.setId(-1L);
return user;
}
};
}
}
(2)在订单微服务的 io.binghe.shop.order.fegin.UserService 接口上的@FeignClient注解上指定fallbackFactory属性,如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 调用用户微服务的接口
*/
//@FeignClient(value = "server-user", fallback = UserServiceFallBack.class)
@FeignClient(value = "server-user", fallbackFactory = UserServiceFallBackFactory.class)
public interface UserService {
@GetMapping(value = "/user/get/{uid}")
User getUser(@PathVariable("uid") Long uid);
}
(3)在io.binghe.shop.order.fegin.fallback.factory包中新建ProductServiceFallBackFactory类,并实现FallbackFactory接口,FallbackFactory接口的泛型指定为ProductService,源码如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 商品微服务容错Factory
*/
@Component
public class ProductServiceFallBackFactory implements FallbackFactory<ProductService> {
@Override
public ProductService create(Throwable cause) {
return new ProductService() {
@Override
public Product getProduct(Long pid) {
Product product = new Product();
product.setId(-1L);
return product;
}
@Override
public Result<Integer> updateCount(Long pid, Integer count) {
Result<Integer> result = new Result<>();
result.setCode(1001);
result.setCodeMsg("触发了容错逻辑");
return result;
}
};
}
}
(4)在订单微服务的 io.binghe.shop.order.fegin.ProductService 接口上的@FeignClient注解上指定fallbackFactory属性,如下所示。
/**
* @author binghe (公众号:冰河技术)
* @version 1.0.0
* @description 调用商品微服务的接口
*/
//@FeignClient(value = "server-product", fallback = ProductServiceFallBack.class)
@FeignClient(value = "server-product", fallbackFactory = ProductServiceFallBackFactory.class)
public interface ProductService {
/**
* 获取商品信息
*/
@GetMapping(value = "/product/get/{pid}")
Product getProduct(@PathVariable("pid") Long pid);
/**
* 更新库存数量
*/
@GetMapping(value = "/product/update_count/{pid}/{count}")
Result<Integer> updateCount(@PathVariable("pid") Long pid, @PathVariable("count") Integer count);
}
测试服务容错
与“Feign整合Sentinel实现容错-测试服务容错”中的测试方法相同,这里不再赘述。
至此,使用Sentinel实现限流和容错的功能就完成了。
最后,需要注意的是:使用Sentinel实现服务容错时,fallback和fallbackFactory只能使用其中一种方式实现服务容错,二者不能同时使用。
文章转自公众号:冰河技术