Spring Cloud系列--Feign整合(二)

之前讲了简单的Feign整合,接下来我们趁热打铁,把Ribbon和Hystrix整合进来。

Feign和Ribbon

在学习Feign的时候,我们都了解到 Feign的客户端负载均衡是通过Spring Cloud Ribbon 实现的。具体的实现和源码,以后会有介绍,在这里就简单的介绍负载均衡组件装配的关系。

我们通过查找spring-cloud-openfeign-core源码包中的 /META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration

我们打开第一个要自动装配文件。

//类路径下是否存在负载均衡接口
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })  
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
//引入其他配置组件
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
      OkHttpFeignLoadBalancedConfiguration.class,
      DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {

   //创建所需的工厂组件
   @Bean
   @Primary
   @ConditionalOnMissingBean
   @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
   public CachingSpringLoadBalancerFactory cachingLBClientFactory(
         SpringClientFactory factory) {
      return new CachingSpringLoadBalancerFactory(factory);
   }
  //创建所需的工厂组件
   @Bean
   @Primary
   @ConditionalOnMissingBean
   @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
   public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
         SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
      return new CachingSpringLoadBalancerFactory(factory, retryFactory);
   }

   @Bean
   @ConditionalOnMissingBean
   public Request.Options feignRequestOptions() {
      return LoadBalancerFeignClient.DEFAULT_OPTIONS;
   }

}

重点是 在注解@import中引入的 DefaultFeignLoadBalancedConfiguration.class

/**
 * @author Spencer Gibb
 */
@Configuration
class DefaultFeignLoadBalancedConfiguration {

   @Bean
   @ConditionalOnMissingBean
   public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
         SpringClientFactory clientFactory) {
      return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
            clientFactory);
   }

}

从这我们可以看出,创建了一个LoadBalancerFeignClient组件。

我们再来深入的看一下这个类。

/**
 * @author Dave Syer
 *
 */
public class LoadBalancerFeignClient implements Client {
    //省略部分代码
        @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            //获得RibbonRequest
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            //进行使用RibbonRequest负载均衡请求调用
            return lbClient(clientName)
                    .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }
//...
}

到这里 应该也算是对Feign和Ribbon之间的关系有所了解。具体的两者之间的关系,有时间,会进一步深入的研究。

整合Hystrix

整合Hystrix其实不需要改变之前的 注册中心(spring-cloud-test-register)。只需要修改客户端(myribbon)和服务提供方(provider)就可以了。

客户端(myribbon)

添加StudentServiceFallBack 熔断降级处理类
/**
 * @ClassName StudentServiceFallBack
 * @Description 熔断回调函数接口
 * @Author Neal
 * @Date 2019/4/29 21:39
 * @Version 1.0
 */
@Component
public class StudentServiceFallBack implements StudentService {
    @Override
    public String getAllStudent() {
        return "getAllStudent()  超时降级处理";
    }

    @Override
    public String saveStudent(Student student) {
        return "saveStudent() 超时降级处理";
    }
}

我们可以看到,这个类很简单,首先追加 @Component注解,然后实现StudentService接口。将实现的方法改为降级处理的逻辑即可。

Feign API接口添加降级回调
/**
 * @ClassName StudentService
 * @Description feign API接口
 * @Author Neal
 * @Date 2019/4/29 14:36
 * @Version 1.0
 */

@FeignClient(name = "RIBBON-SERVICE",fallback = StudentServiceFallBack.class)  //服务端ID
public interface StudentService {

    /**
     * 获取所有学生列表
     * @return
     */
    @GetMapping(value = "/myfeign/student")
    String getAllStudent();

    /**
     * 添加学生
     * @param student
     * @return
     */
    @PostMapping(value = "/myfeign/student")
    String saveStudent(@RequestBody Student student);
}
启动类上添加 @EnableHystrix注解
@EnableEurekaClient
@SpringBootApplication
@EnableHystrix
@EnableFeignClients(clients = StudentService.class)  //开启
public class MyribbonApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyribbonApplication.class, args);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

好了,现在代码就这么多了。是不是很简单。我们接着来操作一下外部化配置。

application-consumer.properties

spring.application.name=ribbon-consumer
server.port=1114
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
#允许feign使用 hystrix
feign.hystrix.enabled=true
#降级默认超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=50

服务提供方(provider)

只需要修改API调用方法,增加模拟延迟处理逻辑即可。同时添加了 外部化配置 的端口。方便查看负载均衡。

/**
 * @ClassName StudentController
 * @Description TODO
 * @Author Neal
 * @Date 2019/4/29 14:58
 * @Version 1.0
 */
@RestController
public class StudentController implements  StudentService{

    @Autowired
    @Qualifier("iStudentServiceImp")
    StudentService studentService;

    /**
     * 默认服务端口,用来观察负载均衡
     */
    @Value("${server.port}")
    private String port;

    /**
     * 随机数生成类
     */
    private static Random random = new Random();


    /**
     * 增加延迟逻辑处理
     * @return
     */
    @Override
    public String getAllStudent() {
        long executeTime = random.nextInt(100);
        System.out.println("execute Time : " + executeTime);
        try {
            TimeUnit.MILLISECONDS.sleep(executeTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return studentService.getAllStudent() + "port:" + port;
    }

    @Override
    public String saveStudent(Student student) {
        return studentService.saveStudent(student);
    }

    /**
     * 熔断策略的回调方法
     * @return
     */
    public String fallBackHelloWorld() {
        return "@HystrixCommand : Time out 100 ms";
    }
}

这里的服务端口我在properties文件中设置的。由于要实现两个服务端,所以分了两个properties文件,如何启动,可以参照之前的文章。

application-provider1.properties

spring.application.name=ribbon-service
#端口
server.port=1112
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

application-provider2.properties

spring.application.name=ribbon-service
#端口
server.port=1113
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

运行

基础的代码就这么多,下面让我们跑一下程序来看看是否实现了 负载均衡和熔断降级吧。

启动程序,顺序为:

spring-cloud-test-register-> provider(provider1provider2) -> myribbon

首先查看注册中心,是否注册了所有服务。

Spring Cloud系列--Feign整合(二)_第1张图片
注册.png

我们可以看到 服务端的两个服务以及消费者已经都注册到了注册中心上。

我们可以先简单插入一条数据(跟上一篇一样,这里我就不重复操作了),然后请求查看所有学生的API接口,看看是否有熔断降级和负载均衡。

http://localhost:1114/myfeign/student

p1.png
Spring Cloud系列--Feign整合(二)_第2张图片
r1.png
Spring Cloud系列--Feign整合(二)_第3张图片
p2.png
Spring Cloud系列--Feign整合(二)_第4张图片
r2.png

我们可以看到 provider1和provider2 实现了负载均衡处理,并且 请求处理的时间都不超过50毫秒,所以不会有降级操作。

熔断降级

Spring Cloud系列--Feign整合(二)_第5张图片
h1.png
Spring Cloud系列--Feign整合(二)_第6张图片
p3.png

我们也可以看到,由于处理时间超过了50毫秒,客户端自行进行了熔断降级处理。

总结

Feign的基本内容已经介绍完了。如果真的感兴趣或者想知道更细致的操作,可以参考官方的文档,比网上其他的博客靠谱的多。

你可能感兴趣的:(Spring Cloud系列--Feign整合(二))