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文件中加入上述的配置依赖后,父工程便搭建成功了。项目结如图所示: alt

红框框住的就是父工程的项目结构,其他的就是项目中实现各种功能的子工程。

2、创建Eureka注册中心模块

  在父工程中新建一个Module,该Module也是一个Maven项目,该模块是进行项目中所有服务的统一管理。

  创建Eureka注册中心模块的步骤如下:

将光标停在父项目上,就是上图的SpringCloud的地方,然后右键鼠标,选择new后在再选择Module,开始创建新的Module,该Module也还是JavaSE类型的Maven项目。在创建时需要注意下图所示: alt

  1. 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示: alt
  2. 展开eurekaServer模块,在该模块的Pom文件中添加如下配置:
    <dependencies>
        <!--开启Eureka服务的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

  1. 构建相应的项目结构,结构如图所示 alt 其中启动类的编写如下:
/**
 * @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,开启的,集群时开启

注意:在编写配置文件时一定要注意缩进对齐的问题,不可以多缩进也不可以少缩进,不然启动时会报错,或是出现功能不可用的问题。

  1. 编写好以上内容以后,便可以运行启动类启动Eureka的服务,启动后便可以在浏览器输入配置好的Eureka服务地址进行网站访问,能将服务启动成功且访问到Eureka的网站便是成功配置好了Eureka注册中心服务模块。启动的效果图如下: alt alt

3、创建服务提供者模块

  在父工程中新建一个Module,该Module也是一个Maven项目,该模块便是项目中提供具体服务的,比如说:进行数据库相关操作。那此次的实战只是演示项目的搭建过程,所以服务提供者就实现按ID号查询数据表中某一条数据的功能。

  创建服务提供者模块的步骤:

  1. 创建新的模块,创建的步骤和上面创建Eureka注册中心服务模块一致。
  2. 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示: alt

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>

  1. 构建相应的项目结构,结构如图所示: alt 其中启动类的编写如下:
/**
 * @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中心上,另一个是发出访问请求是否能得到想要的数据。完成后的效果图如下: alt alt 访问地址根据你自己配置的来,能出现数据就表示userService模块搭建成功了。

4、创建服务调用者模块

在父工程中新建一个Module,该Module也是一个Maven项目,该模块便是项目中调用服务的模块。该模块本来在实际项目中属于前端页面的模块,但我这是项目搭建演示,就不做页面的设计了,直接通过网址的方式发起请求。

创建服务调用者模块的步骤:

  1. 创建新的模块,创建的步骤和上面创建Eureka注册中心服务模块一致。
  2. 创建好以后就会在父工程的项目结构中出现你创建的新Module,如下图所示: alt
  3. 展开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>

  1. 构建相应的项目结构,结构如图所示: alt 没框起来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中心上,另一个是发出访问请求是否能得到想要的数据。完成后的效果图如下: alt alt 访问地址根据你自己配置的来,能出现数据就表示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服务,之后进行请求访问看看能否获得数据,效果如下: alt 访问地址根据你自己配置的来,能出现数据就表示consumerService模块整合Ribbon组件成功了。

  然后你再访问原来没有加入Ribbon组件的地址会发现无法在访问了,这是因为在启动类中的RestTemplate方法上添加了 @LoadBalanced注解,让服务调用者在进行调用服务时是按负载均衡算法来获取服务地址,因此无法再使用自己所编写的代码来获取服务地址。访问效果如下: alt alt

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服务,之后进行请求访问看看能否获得数据,效果如下: alt 请求地址中id为1的请求会被降级,这是因为在ConsumerController类中添加了下图的逻辑,当Id=1是会抛出异常,模拟服务发生问题,在该情况下就会发生服务降级,出现上面展示图的效果: alt 当然你访问其他的Id值是没有问题的,如下所示: alt 但是当你的请求发送超过10次(配置文件中 requestVolumeThreshold 的配置),且其中错误请求的次数占总请求次数的50%以上(配置文件中 errorThresholdPercentage 的配置)时,访问其他的Id值也会发生服务降级,如下所示 alt 当然也不是永久的无法访问,当停止1秒(配置文件中 sleepWindowInMilliseconds 的配置 )向服务器请求后,其他的Id值又可以正常访问。

  添加服务熔断组件实现第二种全局服务降级的步骤:

  1. 需要在服务调用者的Pom文件中加入配置,添加的和局部服务降级一致,即无需再次添加。
  2. 更改服务调用者的启动类。更改和局部服务降级一致,即无需再次更改。
  3. 在服务调用者的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") alt 选择性在服务调用者的配置文件:application.yml 中添加以下配置,与局部服务降级的配置一致,即无需再次添加。   更改好上面的配置后,需要重启consumerService服务,之后进行请求访问看看能否获得数据,效果如下: alt alt 全局服务降级的效果是和局部服务降级的原理是差不多的,就不在多做解释了,二者的区别在于:局部服务降级的效果可以根据不同状况而定,而全局服务降级就是规定了所有的服务降级都一致。

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模块下添加几个结构,添加结构如下: alt 其中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模块的服务,之后进行请求访问看看能否获得数据,效果如下: alt 当你把服务提供者userService模块停止服务后,consumerService服务就会发生熔断,调用服务降级方法,效果如下: alt 当服务提供者userService模块重启服务后,consumerService服务又可以正常工作了。

全部评论

相关推荐

努力学习的小绵羊:我反倒觉得这种挺好的,给不到我想要的就别浪费大家时间了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务