SpringCloud:整合Eureka、Ribbon、Hystrix、Feign
1、创建父工程
因为SpringCloud项目中会有很多的服务模块,每个服务模块都有自己的项目依赖,为了统一项目中每个服务模块的项目依赖版本,那么便可以在搭建具体项目前先创建一个父工程,让项目的每个服务模块作为子工程继承与这个父工程,因此创建父工程的目的就是:利用Maven项目中父子工程依赖的继承特性,对项目中的子工程的项目依赖进行版本和配置的统一管理。
注意:在搭建SpringCloud的项目中,要注意SpringCloud的版本需要和SpringBoot版本相对应。而此次的项目实战我选用的SpringCloud的版本为:Greenwish,而对应的SpringBoot版本为:2.1.5.RELEASE。
创建一个Maven项目,该项目为父工程,创建的步骤在这里就不做具体演示了,不会的小伙伴可以参考小编的另一个博文:“了解Java项目的管理工具——Maven”中关于IDEA如何创建Maven项目的内容。因为父工程就是做简单的依赖版本控制,所以创建最简单的JavaSE类型的Maven项目即可。
父工程中Pom文件需要添加的配置如下:
<!--规定SpringBoot的版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/>
</parent>
<!--规定其他依赖的版本-->
<properties>
<!--JDK版本-->
<java.version>1.8</java.version>
<!--SpringCound版本-->
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
<!--tk-MyBatis版本-->
<mapper.starter.version>2.0.2</mapper.starter.version>
<!--mysql版本-->
<mysql.version>5.1.46</mysql.version>
</properties>
<dependencyManagement>
<dependencies>
<!--springCloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--tk-mybatis依赖-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mapper.starter.version}</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--SpringBoot测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
在Pom文件中加入上述的配置依赖后,父工程便搭建成功了。项目结如图所示:
红框框住的就是父工程的项目结构,其他的就是项目中实现各种功能的子工程。
2、创建Eureka注册中心模块
在父工程中新建一个Module,该Module也是一个Maven项目,该模块是进行项目中所有服务的统一管理。
创建Eureka注册中心模块的步骤如下:
将光标停在父项目上,就是上图的SpringCloud的地方,然后右键鼠标,选择new后在再选择Module,开始创建新的Module,该Module也还是JavaSE类型的Maven项目。在创建时需要注意下图所示:
- 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示:
- 展开eurekaServer模块,在该模块的Pom文件中添加如下配置:
<dependencies>
<!--开启Eureka服务的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- 构建相应的项目结构,结构如图所示 其中启动类的编写如下:
/**
* @Author gyx
* @SpringBootApplication 标识为SpringBoot的启动类
* @EnableEurekaServer 声明当前启用eureka服务
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
配置文件的编写如下:
server:
port: 9999 # 服务端口配置
spring:
application:
name: eureka-server # 服务名配置
eureka:
server:
enable-self-preservation: false # 关闭自我保护机制(默认是true)
eviction-interval-timer-in-ms: 10000 # 1秒的扫描失效,默认是60秒
client:
service-url:
defaultZone: HTTP://127.0.0.1:9999/eureka # eureka服务地址
fetch-registry: false # 不拉取服务,默认是true,开启的,集群时开启
register-with-eureka: false # 不进行eureka注册,默认是true,开启的,集群时开启
注意:在编写配置文件时一定要注意缩进对齐的问题,不可以多缩进也不可以少缩进,不然启动时会报错,或是出现功能不可用的问题。
- 编写好以上内容以后,便可以运行启动类启动Eureka的服务,启动后便可以在浏览器输入配置好的Eureka服务地址进行网站访问,能将服务启动成功且访问到Eureka的网站便是成功配置好了Eureka注册中心服务模块。启动的效果图如下:
3、创建服务提供者模块
在父工程中新建一个Module,该Module也是一个Maven项目,该模块便是项目中提供具体服务的,比如说:进行数据库相关操作。那此次的实战只是演示项目的搭建过程,所以服务提供者就实现按ID号查询数据表中某一条数据的功能。
创建服务提供者模块的步骤:
- 创建新的模块,创建的步骤和上面创建Eureka注册中心服务模块一致。
- 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示:
3.展开userService模块,在该模块的Pom文件中添加如下配置:
<dependencies>
<!--SpringMVC依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--tk-mybatis依赖-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Eureka客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--开启config配置中心依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--SpringCloudBus相关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
- 构建相应的项目结构,结构如图所示: 其中启动类的编写如下:
/**
* @Author gyx
* @EnableEurekaClient 开启eureka客户端发现功能
* @MapperScan("com.kkb.mapper") 开启扫描数据库操作接口的注解,
* 注意:使用@MapperScan该注解要导入tk-mybais的依赖包,如下:
* import tk.mybatis.spring.annotation.MapperScan;
*
*/
@SpringBootApplication
@EnableEurekaClient
@MapperScan("com.kkb.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
其中配置文件的编写如下
server:
port: 8888 # 自定义服务端口配置
spring:
datasource: # 链接数据库的相关配置
driver-class-name: com.mysql.jdbc.Driver
# 记得将其中的springcloud改成自己的数据库名
url: jdbc:mysql://localhost:3306/springcloud?useEncode=true&characterEncoding=utf-8
# 改成自己的用户名
username: root
# 改成自己的密码
password: 357159
application:
name: user-service # 给服务起一个名称
# Eureka注册中心配置
eureka:
instance:
ip-address: 127.0.0.1 # ip地址
prefer-ip-address: true # 更倾向于IP地址,而不是host名
lease-expiration-duration-in-seconds: 5 # 5秒的服务失效时间,默认值:90
lease-renewal-interval-in-seconds: 5 # 5秒的心跳机制验证,默认值:30
client:
service-url:
defaultZone: HTTP://127.0.0.1:9999/eureka # eureka注册中心的服务地址
其中实体类的编写:
/**
* @Author: Gyx
* @Date: 2021/4/19 14:54
* @Table(name = "tb_user") 当类名与表名不一致时,用来关联数据库中数据表的表名
* @Id 标识该属性对应数据表中的主键列
* @GeneratedValue(strategy = GenerationType.IDENTITY) 标识该主键列是自动增长
* @Column(name = "user_name") 当属性名与表的列名不一致时,用来关联数据库中数据表的类名
* 注意:如果整合的是MyBatis,该注解@Column(name = "user_name")必须得加,
* 但整合的是tk-mybatis,只要列名的格式是:user_name,那么属性名可以使用userName,
* 就是列名中下划线加小写字母的部分,属性名可以用对应的大写字母代替,这就是我把注解注释了的原因
*/
@Table(name = "tb_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//@Column(name = "user_name")
private String userName;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated;
private String note;
}
注意:你们在编写实体类的时候记得给实体类中的每个属性加上Get和Set方法,我这是演示所以就不展示太多。
其中UserController类的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/19 15:05
* @RestController 标识该类是请求处理类的注解
* @RequestMapping("/user") 请求的地址配置注解
* @Resource 自动注入的注解
* @GetMapping("/{id}") 请求的地址配置注解
* 访问的地址为:http://172.0.0.1:8888/user/2
* 127.0.0.1:8888 为userService的服务地址
* /user/2 为@RequestMapping("/user")和@GetMapping("/{id}")的配置组合
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
/**
*@PathVariable("id") 该注解作用是:获取{}占位符中的值
*/
@GetMapping("/{id}")
public User findById(@PathVariable("id") Long id){
return userService.findById(id);
}
}
其中UserMapper接口的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/19 14:58
* 该类需要继承Mapper接口,传入实体类作为泛型。
* 如进行的数据库操作是简单的增、删、改、查操作,则无需编写方法,tk-mybatis中提供了这些操作的方法。
* 如果需要进行复杂查询就需要自己参考SpringBoot整合mybatis的相关配置进行mapper.xml文件的配置
*/
public interface UserMapper extends Mapper<User> {
}
其中UserService的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/19 15:02
*/
public interface UserService {
/**
* 按ID查询用户信息
* @param id
* @return
*/
User findById(Long id);
}
其中UserServiceImpl实现类的编写如下
/**
* @Author: Gyx
* @Date: 2021/4/19 15:02
* @Service 表示给类的Service层的实现类
* @Resource 自动注入的注解
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper mapper;
/**
* 按ID查询用户信息
*
* @param id
* @return
*/
@Override
public User findById(Long id) {
return mapper.selectByPrimaryKey(id);
}
}
数据表文件的内容如下,在创建数据表之前,要先创建数据库,自定义:
# 创建表的语句
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`sex` int(11) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`created` date DEFAULT NULL,
`updated` date DEFAULT NULL,
`note` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
# 添加数据的语句
INSERT INTO `tb_user` VALUES ('1', 'zhangsan', '1', '张三a', '18', '1', '2019-02-27', '2019-02-27', '2019-02-27', '在学习Java...');
INSERT INTO `tb_user` VALUES ('2', 'lisi', '1', '李四ab', '18', '1', '2019-02-27', '2019-02-27', '2019-02-27', '在学习Java...');
INSERT INTO `tb_user` VALUES ('3', 'wangwu', '1', '王五abc', '18', '1', '2019-02-27', '2019-02-27', '2019-02-27', '在学习Java...');
INSERT INTO `tb_user` VALUES ('4', 'fanbingbing', '1', '范冰冰aa', '18', '2', '2019-02-27', '2019-02-27', '2019-02-27', '在学习Java...');
INSERT INTO `tb_user` VALUES ('5', 'guodegang', '1', '郭德纲bb', '18', '1', '2019-02-27', '2019-02-27', '2019-02-27', '在学习Java...');
INSERT INTO `tb_user` VALUES ('6', null, null, '周星驰cc', '18', null, '2020-06-01', '2020-06-01', null, null);
配置好以上的内容后便可以运行启动类启动该模块了,启动后需要观察两个效果,一个是userService服务是注册到了Eureka中心上,另一个是发出访问请求是否能得到想要的数据。完成后的效果图如下: 访问地址根据你自己配置的来,能出现数据就表示userService模块搭建成功了。
4、创建服务调用者模块
在父工程中新建一个Module,该Module也是一个Maven项目,该模块便是项目中调用服务的模块。该模块本来在实际项目中属于前端页面的模块,但我这是项目搭建演示,就不做页面的设计了,直接通过网址的方式发起请求。
创建服务调用者模块的步骤:
- 创建新的模块,创建的步骤和上面创建Eureka注册中心服务模块一致。
- 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示:
- 展开consumerService模块,在该模块的Pom文件中添加如下配置:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka注册中心客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
- 构建相应的项目结构,结构如图所示: 没框起来feign文件夹和ConsumerFeignContorller类现在还无需理会,之后讲到Feign组件的引入后会涉及到。
其中启动类的编写如下
/**
* @Author gyx
* @SpringBootApplication
* @EnableEurekaClient 开启eureka客户端
*/
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
/**
* 提供restful风格请求的服务
* @return
*/
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
其中配置文件的编写如下:
server:
port: 8899
spring:
application:
name: consumer-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:9999/eureka
registry-fetch-interval-seconds: 10 #10秒向注册中心拉取数据,默认值:30
其中实体类的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/19 15:23
* @Component 标注需要交给Spring容器创建对象的类
*/
@Component
public class User {
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private Integer sex;
private Date birthday;
private Date created;
private Date updated;
private String note;
}
注意:该实体类中的属性也是要加Get和Set方法,你们自行添加。
其中consumerController类的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/19 15:24
*/
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
/**
* 使用负载均衡前获取地址的方法
* @param id
* @return
*/
@GetMapping("/{id}")
public User findById(@PathVariable("id") Long id){
//使用eureka注册中心后获取地址的方法
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
ServiceInstance serviceInstance = instances.get(0);
url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/"+id;
return restTemplate.getForObject(url,User.class);
}
配置好上述的内容之后,便可以运行启动类启动服务了,启动后需要观察两个效果,一个是consumerService服务是注册到了Eureka中心上,另一个是发出访问请求是否能得到想要的数据。完成后的效果图如下: 访问地址根据你自己配置的来,能出现数据就表示consumerService模块搭建成功了。
5、在服务调用者模块加入负载均衡组件
在服务调用者模块加入负载均衡组件的目的就是:让服务调用者可以高效的访问集群服务,将选择访问集群哪一服务的操作交给负载均衡组件来决定,无需我们自己编写相应的功能代码来实现。
添加负载均衡组件的步骤:
1.更改服务调用者的启动类,更改如下:
/**
* @Author gyx
* @SpringBootApplication
* @EnableEurekaClient 开启eureka客户端
*/
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
/**
* 提供restful风格请求的服务
* @LoadBalanced 拉取服务时开启负载均衡
* @return
*/
@Bean
@LoadBalanced //更改点
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.在服务调用者的ConsumerController类中添加下列的方法:
/**
* 使用负载均衡后获取地址的方法
* @param id
* @return
*/
@GetMapping("/ribbon/{id}")
public User findByIdForRibbon(@PathVariable("id") Long id){
String url = "http://user-service/user/" +id;
return restTemplate.getForObject(url,User.class);
}
3.选择性在服务调用者的配置文件:application.yml 中添加以下配置,注意:下列配置都可以不加,因为都有默认值:
user-service: # 服务提供者的服务名
ribbon:
ReadTimeout: 2000 #读取超时常
ConnectTimeout: 1000 #建立链接超时时长
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #更改負載均衡规则:从轮询更改为随机
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 0 # 重试多少次服务
OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
更改好上面的配置后,需要重启consumerService服务,之后进行请求访问看看能否获得数据,效果如下: 访问地址根据你自己配置的来,能出现数据就表示consumerService模块整合Ribbon组件成功了。
然后你再访问原来没有加入Ribbon组件的地址会发现无法在访问了,这是因为在启动类中的RestTemplate方法上添加了 @LoadBalanced注解,让服务调用者在进行调用服务时是按负载均衡算法来获取服务地址,因此无法再使用自己所编写的代码来获取服务地址。访问效果如下:
6、在服务调用者模块加入服务熔断组件
在服务调用者模块加入服务熔断组件的目的就是:避免服务器发生雪崩效应,也就是让服务请求降级,不会发生线程阻塞,缓解服务器在服务出现问题后带来的压力。
加入服务熔断组件后实现服务降级的方式有两种,第一种为局部服务降级,第二种为全局服务降级。
添加服务熔断组件实现第一种局部服务降级的步骤:
需要在服务调用者的Pom文件中加入如下配置:
<!--hystrix熔断机制依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
注意:是添加在以下标签之内,千万被加错了,不然会报错:
<dependencies>
...
</dependencies>
更改服务调用者的启动类,更改如下
/**
* @Author gyx
*
* @SpringBootApplication
* @EnableEurekaClient 开启eureka客户端
* @EnableCircuitBreaker 开启服务熔断机制
* 上面三个注解可以直接由 @SpringCloudApplication 代替
*/
@SpringCloudApplication //更改点
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
/**
* 提供restful风格请求的服务
* @LoadBalanced 拉取服务时开启负载均衡
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
在服务调用者的ConsumerController类中添加下列的方法:
/**
* 使用负载均衡后获取地址的方法,并开启局部服务熔断
* @param id
* @return
* @HystrixCommand(fallbackMethod = "fallBack")
* 开启局部服务降级注解,并指定一个局部服务降级方法,
*/
@GetMapping("/hystrix1/{id}")
@HystrixCommand(fallbackMethod = "fallBack")
public String findByIdForHystrix1(@PathVariable("id") Long id){
if(id==1){
throw new RuntimeException("太忙了");
}
String url = "http://user-service/user/" +id;
return restTemplate.getForObject(url,String.class);
}
/**
* 局部服务降级方法
* @param id
* @return
*/
public String fallBack(Long id){
log.error("查询用户失败,id:{}",id);
return "对不起,网络太拥挤了";
}
选择性在服务调用者的配置文件:application.yml 中添加以下配置,注意:下列配置都可以不加,因为都有默认值
#熔断机制的配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 #默认值为1秒
circuitBreaker:
errorThresholdPercentage: 50 # 触发熔断错误比例阈值,默认值50%
sleepWindowInMilliseconds: 10000 # 熔断后休眠时长,默认值5秒
requestVolumeThreshold: 10 # 熔断触发最小请求次数,默认值是20
更改好上面的配置后,需要重启consumerService服务,之后进行请求访问看看能否获得数据,效果如下: 请求地址中id为1的请求会被降级,这是因为在ConsumerController类中添加了下图的逻辑,当Id=1是会抛出异常,模拟服务发生问题,在该情况下就会发生服务降级,出现上面展示图的效果: 当然你访问其他的Id值是没有问题的,如下所示: 但是当你的请求发送超过10次(配置文件中 requestVolumeThreshold 的配置),且其中错误请求的次数占总请求次数的50%以上(配置文件中 errorThresholdPercentage 的配置)时,访问其他的Id值也会发生服务降级,如下所示 当然也不是永久的无法访问,当停止1秒(配置文件中 sleepWindowInMilliseconds 的配置 )向服务器请求后,其他的Id值又可以正常访问。
添加服务熔断组件实现第二种全局服务降级的步骤:
- 需要在服务调用者的Pom文件中加入配置,添加的和局部服务降级一致,即无需再次添加。
- 更改服务调用者的启动类。更改和局部服务降级一致,即无需再次更改。
- 在服务调用者的ConsumerController类中添加下列的方法:
/**
* 使用负载均衡后获取地址的方法,并开启全局服务熔断
* @param id
* @return
* @HystrixCommand 开启全局服务降级注解后无需指定局部服务降级方法
*/
@GetMapping("/hystrix2/{id}")
@HystrixCommand
public String findByIdForHystrix2(@PathVariable("id") Long id){
if(id==1){
throw new RuntimeException("太忙了");
}
String url = "http://user-service/user/" +id;
return restTemplate.getForObject(url,String.class);
}
/**
* 全局服务降级方法
* @return
*/
public String defaultFallBack(){
return "全局提示:对不起,网络太拥挤了";
}
还需要在类上加上以下注解:
@DefaultProperties(defaultFallback = "defaultFallBack") 选择性在服务调用者的配置文件:application.yml 中添加以下配置,与局部服务降级的配置一致,即无需再次添加。 更改好上面的配置后,需要重启consumerService服务,之后进行请求访问看看能否获得数据,效果如下: 全局服务降级的效果是和局部服务降级的原理是差不多的,就不在多做解释了,二者的区别在于:局部服务降级的效果可以根据不同状况而定,而全局服务降级就是规定了所有的服务降级都一致。
7、在服务调用者模块加入伪装器组件
在服务调用者模块加入伪装器组件的目的就是:伪装器Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做,正是该组件的引入大大简化了项目中服务间进行相互访问的操作。
添加伪装器组件的步骤:
需要在服务调用者的Pom文件中加入如下配置:
<!--feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
注意:是添加在以下标签之内,千万被加错了,不然会报错:
<dependencies>
...
</dependencies>
更改服务调用者的启动类,更改如下:
/**
* @Author gyx
*
* @SpringBootApplication
* @EnableEurekaClient 开启eureka客户端
* @EnableCircuitBreaker 开启服务熔断机制
* 上面三个注解可以直接由 @SpringCloudApplication 代替
* @EnableFeignClients 开启feign代理功能,自带负载均衡处理,可以不编写RestTemplate
*/
@SpringCloudApplication
@EnableFeignClients //更改点
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
在服务调用者的配置文件:application.yml 中添加以下配置:
#开启Feign的熔断功能
feign:
hystrix:
enabled: true # 开启,默认是false关闭的
需要在consumerService模块下添加几个结构,添加结构如下: 其中UserFeign接口类的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/20 17:58
* @FeignClient(value = "user-service",fallback = UserFeignImpl.class)
* 开启feign服务,并指定服务提供者(user-service:服务提供者的服务名)
* 和服务降级类(该接口类的实现类)
*/
@FeignClient(value = "user-service",fallback = UserFeignImpl.class)
public interface UserFeign {
/**
* 伪装访问请求的方法
* @GetMapping("/user/{id}") 与服务提供者的地址一致
* @param id
* @return
*/
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
其中UserFeign接口类的实现类UserFeignImpl类的编写如下:
/**
* 服务降级类
* @Author: Gyx
* @Date: 2021/4/20 18:15
*/
@Component
public class UserFeignImpl implements UserFeign {
@Resource
private User user;
@Override
public User findById(Long id) {
user.setId(id);
user.setName("用户异常");
return user;
}
}
其中ConsumerFeignController类的编写如下:
/**
* @Author: Gyx
* @Date: 2021/4/20 18:01
*/
@RestController
@RequestMapping("/consumer")
public class ConsumerFeignController {
@Resource
private UserFeign userFeign;
@GetMapping("/feign/{id}")
public User findById(@PathVariable("id") Long id){
return userFeign.findById(id);
}
}
配置好上面的内容后,需要重启consumerService模块的服务,之后进行请求访问看看能否获得数据,效果如下: 当你把服务提供者userService模块停止服务后,consumerService服务就会发生熔断,调用服务降级方法,效果如下: 当服务提供者userService模块重启服务后,consumerService服务又可以正常工作了。