Springboot 整合 SpringCloud组件-Feign(Ribbon/Hystrix) (三)

简介: Springboot 整合 SpringCloud组件-Feign(Ribbon/Hystrix) (三)

直到上一篇,咱们已经把Eureka Server注册中心搭建完毕,也把微服务成功注册到了注册中心里去了。

接下来,我们整合的是,非常核心实用的组件-Feign,

Feign除了解决了微服务之间调用,里面还囊括了 Ribbon负载均衡以及Hystrix 熔断降级。

Ribbon 负载均衡:image.pngHystrix 熔断降级:image.png


同样,创建一个springboot项目,起名feign作为一个微服务:


image.png


(同样,我们这里选用的springcloud版本是:Finchley.RELEASE)


既然作为一个微服务,那自然也是需要注册到注册中心去的,所以pom.xml里核心的依赖包为:


  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>


application.yml:


eureka:
  instance:
    preferIpAddress: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8765
spring:
  application:
    name: feign
feign:
  hystrix:
    enabled: true


(fegin: hystrix : enabled : true   这个配置项是指,开启熔断机制,也许在yml里显示没有相关依赖,但是不用慌,也许是因为版本问题,这个设置项加上就行,是起作用的)

然后我们在启动类上加上各种注解:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard
@EnableFeignClients
public class FeignApplication {
  public static void main(String[] args) {
    SpringApplication.run(FeignApplication.class, args);
  }
}


OK,基本的条条框框,我们已经做完了,接下来进行 使用Feign去调用我们之前创建的微服务client-test。


我们来看看,我们准备调用的client-test里面的接口:


  @Value("${server.port}")
    String port;
    @RequestMapping("/haveatry")
    public String home(@RequestParam(value = "name", defaultValue = "forezp") String name) {
        return "微服务 client-test被调用, " + "name为:"+name + " ,被调用的服务端口 port:" + port;
    }


那么我们在feign服务里,要这么写,才能成功调用:


先创建一个interface ,SchedualServiceHi.java:


import com.cloud.feign.service.impl.SchedualServiceHiHystric;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 14:42 2019/2/3
 */
@FeignClient(value = "client-test",fallback = SchedualServiceHiHystric.class)
@Component
public interface SchedualServiceHi {
    @RequestMapping(value = "/haveatry",method = RequestMethod.GET)
    String sayHiFromClientOne(@RequestParam(value = "name") String name);
}


可以看到,


1. 需要保证@FeignClient 里面,value的值是 调用的接口所属微服务名称;

2.需要保证请求的Url是一致的;

3.需要保证接口请求方式是一致的;

4.需要保证接口传参是一致的;

5.需要使用@RequestMapping这种方式(避免使用GetMapping/PostMapping)

6.额外提醒,只要是传参,请都加上@RequestParam("XXXX");如果传对象就加上@RequestBody; 这样你就不用踩Feign的传参的坑了,不管是使用Feign的服务还是提供接口的client服务,请都加上这些传参注解保持一致;

看的仔细地人已经看到了fallback后面的类,这个类就是用于熔断降级的,SchedualServiceHiHystric.java:


import com.cloud.feign.service.SchedualServiceHi;
import org.springframework.stereotype.Component;
/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 14:48 2018/2/3
 */
@Component
public class SchedualServiceHiHystric implements SchedualServiceHi {
    @Override
    public String sayHiFromClientOne(String name) {
        return "sorry! 网络异常,服务暂时无法访问。 请求的name为:"+name;
    }
}


OK,到这里,我们其实已经打通了跨服务直接的接口调用,那么我们写一个属于feign服务的接口(因为feign也是一个微服务已经注册到了注册中心,跨服务调用接口直接因为我们分布式架构按照业务把服务拆分了,业务需求需要从feign服务这里的接口调用到client-test服务接口,所以就需要用到了Feign组件):

HiController.java:


import com.cloud.feign.service.SchedualServiceHi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 14:44 2019/2/3
 */
@RestController
public class HiController {
    @Autowired
    SchedualServiceHi schedualServiceHi;
    @GetMapping(value = "/feignTest")
    public String sayHi(@RequestParam String name) {
        return schedualServiceHi.sayHiFromClientOne(name);
    }
}


好,现在我们先将项目跑起来(前提注册中心以及client-test也是正在运行的),看看跨服务调用效果(熔断和负载均衡一会略详细讲讲,不慌):


image.png


我们调下feign的接口  http://localhost:8765/feignTest?name=1323


image.png


可以看到,跨服务间调用已经成功。


然后,下面我们直接把 client-test服务关掉(狠心),feign的接口  http://localhost:8765/feignTest?name=1323


image.png


看到返回值,显示的内容, 没错就是我们刚刚配置的fallback类:


image.png


这里可以根据使用场景,扩展做数据存储、降级调用等等。

到这里,基本feign的使用也七七八八, 就剩一个负载均衡的使用:

那么我们继续, 既然是负载均衡,那么也先得有东西均衡,所以我们直接再创建一个微服务,这个微服务跟上一篇的client-test微服务一模一样,依赖、配置全都保持一样, 唯一的改变是 端口!

上一篇的client-test微服务端口是8762, 我们这个是8763(注意,服务名保持一样 client-test,因为负载均衡是通过获取注册中心的服务注册信息,根据服务名去匹配的):


server:
  port: 8763
spring:
  application:
    name: client-test
  zipkin:
    base-url: http://localhost:9411
    sender:
      type: web
  sleuth:
    sampler:
      probability: 1.0
eureka:
#以IP地址方式显示在注册中心
  instance:
    preferIpAddress: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/


然后也将这个新的client-test微服务跑起来,先访问注册中心看看情况 http://localhost:8761/


image.png


OK,负载均衡的准备已经完成,那么我们连续调下feign的接口  http://localhost:8765/feignTest?name=1323


image.png


从调用的返回值我们可以得知,已经负载均衡了,默认的负载机制是轮换地调用,我们只需横向扩展需要实现负载均衡的服务实例。

到了这里,讲道理是已经整合完毕了,但是,也许细心思考的人会有以下的想法:

又熔断降级、又负载均衡,那么如果现在有3个client-test服务实例, 其中有一个挂掉或者访问响应比较慢,那么负载和均衡直接是怎么样的处理结果呢?

接下来我们用client-test服务实例 来简单模拟下场景,

首先我们刚刚试过,2个client-test服务实例 都正常访问,响应都是正常的时候, 不可能出现熔断降级,然后负载均衡是,轮流一次次地访问到了这两个实例;

OK,我们在8762这个服务实例的接口上,加上一个sleep(5000):


image.png


然后连续调下feign的接口  http://localhost:8765/feignTest?name=1323 :

结果是优先负载均衡轮流调用实例,调用到正常运行的服务则正常调用;若调用到时间久的,就会出现熔断降级。

那么这种情况,其实我们是可以设置feign调用服务的连接超时时间的,

我们创建一个FeignConfiugure.java:


image.png

import feign.Request;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 10:56 2019/2/3
 */
@Configuration
public class FeignConfigure {
    public static int connectTimeOutMillis = 7000;//超时时间
    public static int readTimeOutMillis =7000;
    @Bean
    public Request.Options options() {
        return new Request.Options(connectTimeOutMillis, readTimeOutMillis);
    }
    @Bean
    public Retryer feignRetryer() {
        return new Retryer.Default();
    }
}


注意这里设置的其实是负载均衡的连接超时时间,熔断默认是1000ms,也就是说连接超过1000ms都直接熔断了,根本不会负载均衡。 所以在这里建议,负载均衡的超时时间设置最好是不要大于熔断超时时间, 这样才是符合逻辑的。

那么熔断时间怎么设置呢,

我们可以直接在yml设置(下面的设置项也许在yml文件里面也会显示不存在,但是其实是有效的):


### Hystrix 配置
hystrix:
  # 这样将会自动配置一个 Hystrix 并发策略插件的 hook,这个 hook 会将 SecurityContext 从主线程传输到 Hystrix 的命令。
  # 因为 Hystrix 不允许注册多个 Hystrix 策略,所以可以声明 HystrixConcurrencyStrategy
  # 为一个 Spring bean 来实现扩展。Spring Cloud 会在 Spring 的上下文中查找你的实现,并将其包装在自己的插件中。
  shareSecurityContext: true
  command:
    default:
      circuitBreaker:
        # 当在配置时间窗口内达到此数量的失败后,进行短路。默认20个
        requestVolumeThreshold: 1
        # 触发短路的时间值,当该值设为5000时,则当触发 circuit break 后的5000毫秒内都会拒绝request
        # 也就是5000毫秒后才会关闭circuit。默认5000
        sleepWindowInMilliseconds: 15000
        # 强制打开熔断器,如果打开这个开关,那么拒绝所有request,默认false
        forceOpen: false
        # 强制关闭熔断器 如果这个开关打开,circuit将一直关闭且忽略,默认false
        forceClosed: false
      execution:
        isolation:
          thread:
            # 熔断器超时时间,默认:1000/毫秒
            timeoutInMilliseconds: 10000


我这边是故意设置熔断时间为10s,负载均衡时间为7秒, 然后接口sleep是5s,然后调用接口访问,就可以明显观察,不会存在熔断返回值只是访问特别久才有返回结果,因为sleep是5秒;

OK,两个时间咱们都可以设置,具体就根据业务需求去设置吧。

Springboot整合SpringCloud -Feign的详细介绍就到此。

以下是我自己设置各个时间后,调用接口的简述(可以无视,自己亲自去试试时间的设置搭配):

我们可以再把熔断时间改为4秒,负载均衡时间为7秒,然后接口sleep是5s,调用接口访问,可以看到直接触发了熔断效果;

我们可以再把熔断时间改为7秒,负载均衡时间为3秒,然后接口sleep是5s,调用接口访问,

可以看到当调用到超过负载时间的接口时,并没直接返回熔断结果(因为还没到熔断的7s),而是返回了正常没有设置sleep的另一个实例的接口返回值(因为这时候超过了负载的3秒,所以组件帮我们去正常的服务了)。

其实这种情况才是最符合我们使用场景的,也就是设置的熔断时间比负载均衡时间长就行了,

当服务调用进行负载,发现如果有的服务连接不上或者时间太久,就会去寻找能正常在负载均衡连接时间范围内访问的服务,去调用接口,然后如果一直都找不到能符合条件的,那么就是熔断场景。

相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
4月前
|
Java
SpringBoot集成RestTemplate组件
SpringBoot集成RestTemplate组件
43 0
|
3月前
|
Dubbo Java 应用服务中间件
微服务框架(十六)Spring Boot及Dubbo zipkin 链路追踪组件埋点
此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。 本文第一部分为调用链、OpenTracing、Zipkin和Jeager的简述;第二部分为Spring Boot及Dubbo zipkin 链路追踪组件埋点
|
3月前
|
Java 应用服务中间件 容器
SpringBoot之Web原生组件注入
SpringBoot之Web原生组件注入
|
2月前
|
XML 缓存 算法
SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)
SpringBoot2 | SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析(九)
27 0
|
1天前
|
机器学习/深度学习 负载均衡 Java
【SpringBoot系列】微服务远程调用Open Feign深度学习
【4月更文挑战第9天】微服务远程调度open Feign 框架学习
|
18天前
|
Nacos
SpringCloud Feign使用
SpringCloud Feign使用
23 1
|
1月前
【十九】搭建SpringCloud项目三(Feign)
【十九】搭建SpringCloud项目三(Feign)
22 0
|
1月前
|
存储 负载均衡 Java
【Spring底层原理高级进阶】微服务 Spring Cloud 的注册发现机制:Eureka 的架构设计、服务注册与发现的实现原理,深入掌握 Ribbon 和 Feign 的用法 ️
【Spring底层原理高级进阶】微服务 Spring Cloud 的注册发现机制:Eureka 的架构设计、服务注册与发现的实现原理,深入掌握 Ribbon 和 Feign 的用法 ️
|
1月前
|
负载均衡 算法 Java
SpringCloud负载均衡源码解析 | 带你从表层一步步剖析Ribbon组件如何实现负载均衡功能
SpringCloud负载均衡源码解析 | 带你从表层一步步剖析Ribbon组件如何实现负载均衡功能
|
4月前
|
Java API
SpringBoot集成Swagger2组件
SpringBoot集成Swagger2组件
28 1