声明式服务调用Feign+熔断机制Hystrix
Posted MyAzhe0ci3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了声明式服务调用Feign+熔断机制Hystrix相关的知识,希望对你有一定的参考价值。
- 在以往的单工程项目,所有代码都融合在一起,业务相互调用只需要引入共有的工具类或者对应模块的service。
- 但是到了微服务时代,已然不能使用这种方式,我们需要寻找新的解决方案。
- 若每个模块都把需要调用模块的service拷贝一份,那会令代码非常冗余,影响整个工程的健壮性。
- 稍大一些的系统,会分成多个库,比如用户库、订单库分开,订单服务想要取到用户的相关信息,由于不能连接到用户的库,所以无法直接新建数据库查询以达到目的。
- 这个时候,远程调用方案出现,订单服务只需调用用户服务的API,就可以获取所需信息,非常方便。
我们下面来学习,如何使用SpringCloud的Feign来进行微服务远程调用。
创建blade_blog表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for blade_blog
-- ----------------------------
DROP TABLE IF EXISTS `blade_blog`;
CREATE TABLE `blade_blog` (
`id` bigint(64) NULL DEFAULT NULL,
`blog_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`blog_content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`is_deleted` int(2) NULL DEFAULT 0,
`blog_date` datetime NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of blade_blog
-- ----------------------------
INSERT INTO `blade_blog` VALUES (1, '测试1', '内容1', 0, '2021-07-30 16:03:15');
INSERT INTO `blade_blog` VALUES (1421018570775953409, '标题测试', '内容测试', 0, '2021-07-30 16:03:23');
SET FOREIGN_KEY_CHECKS = 1;
创建实体类
package org.springblade.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.sql.Date;
@Data
@TableName("blade_blog")
public class Blog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.ID_WORKER)
private Long id;
/**
* 标题
*/
private String blogTitle;
/**
* 内容
*/
private String blogContent;
/**
* 时间
*/
@JsonFormat(shape=JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
private Date blogDate;
/**
* 是否已删除
*/
@TableLogic
private Integer isDeleted;
}
blogDate变量使用了字符串类型,接口接收参数后将该字符串转为日期时,发生错误,项目中使用fastjson来处理json数据。
此问题解决方法
在接收参数实体类的createtime字段添加JsonFormat注解
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
使用的持久层框架是mabatisplus,默认你们会使用,就不把步骤列出来了
创建DemoController
package org.springblade.demo.controller;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.demo.entity.Blog;
import org.springblade.demo.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* DemoController
*/
@Slf4j //日志
@RestController//@responseBody+@Controller的组合
@RequestMapping("api")//请求路径
@AllArgsConstructor//labmda表达式简洁写法 使用该注解不用注入自动创建
public class DemoController {
private BlogService blogService;
/**
* 详情
*/
@GetMapping("/detail")
public R<Blog> detail(String id) {
Blog detail = blogService.getById(id);
return R.data(detail);
}
}
创建BlogClient接口
package org.springblade.demo.feign;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(
//定义Feign指向的service-id 也就是指向了demoApplication微服务模块的id
value = "blade-demo"
)
public interface BlogClient{
/**
* 接口前缀
*/
String API_PREFIX = "/api/blog";
/**
* 获取详情
*
* @param id 主键
* @return
*/
@GetMapping(API_PREFIX + "/detail")
R<Blog> detail(@RequestParam("id") Integer id);
}
如果@FeignClient指向错误会报
com.netflix.zuul.exception.ZuulException: Forwarding error Caused by:
com.netflix.client.ClientException: Load balancer does not have
available server for client: service-xxx异常
创建feign的实现类BlogClientImpl
package org.springblade.demo.feign;
import lombok.AllArgsConstructor;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import org.springblade.demo.service.BlogService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AllArgsConstructor
public class BlogClientImpl implements BlogClient{
private BlogService service;
@Override
@GetMapping(API_PREFIX+"/detail")
public R<Blog> detail(Integer id) {
return R.data(service.getById(id));
}
}
什么是熔断机制
- hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制。
- 这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用了对应的卡通形象做作为logo。
- 在一个分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,如何能够保证在一个依赖出问题的情况下,不会导致整体服务失败,这个就是Hystrix需要做的事情。
- Hystrix提供了熔断、隔离、Fallback、cache、监控等功能,能够在一个、或多个依赖同时出现问题时保证系统依然可用。
创建Hystrix类BlogClientFallback
同样实现 BlogClient 接口,只是此时只需返回对应数据即可,不需要再定义为 Controller
package org.springblade.demo.feign;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import java.time.LocalDateTime;
public class BlogClientFallback implements BlogClient {
@Override
public R<Blog> detail(Integer id) {
Blog blog = new Blog();
blog.setBlogTitle("Hystrix");
blog.setBlogContent("FallBack Success");
blog.setBlogDate(LocalDateTime.now());
blog.setIsDeleted(0);
return R.data(blog);
}
}
修改BlogClient,增加Hystrix配置
package org.springblade.demo.feign;
import org.springblade.common.constant.CommonConstant;
import org.springblade.core.tool.api.R;
import org.springblade.demo.entity.Blog;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(
//定义Feign指向的service-id
value = "blade-demo",
//定义hystrix配置类
fallback = BlogClientFallback.class
)
public interface BlogClient{
/**
* 接口前缀
*/
String API_PREFIX = "/api/blog";
/**
* 获取详情
*
* @param id 主键
* @return
*/
@GetMapping(API_PREFIX + "/detail")
R<Blog> detail(@RequestParam("id") Integer id);
}
增加 FallBack 自动配置(不新建配置,直接在BlogClientFallback类上加@Component注解也可以)
package org.springblade.demo.config;
import org.springblade.demo.feign.BlogClientFallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* feign失败配置
*
*/
@Configuration
public class BlogFeignConfiguration {
@Bean
public BlogClientFallback blogClientFallback(){
return new BlogClientFallback();
}
}
这时我们去 blade-demo 的 BlogClientImpl 模拟异常
@RestController
@AllArgsConstructor
public class BlogClientImpl implements BlogClient {
private BlogService service;
@Override
@GetMapping(API_PREFIX + "/detail")
public R<Blog> detail(Integer id) {
int cnt = 100 / 0;
return R.data(service.getById(id));
}
}
OK打完收工
以上是关于声明式服务调用Feign+熔断机制Hystrix的主要内容,如果未能解决你的问题,请参考以下文章
Spring Cloud中HystrixRibbon及Feign的熔断关系
SpringCloud系列之二---Feign实现服务间调用,集成Hystrix熔断器Hystrix-Dashboard仪表盘