spring-cloud-feign负载均衡组件

Feign简介:

  Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便.它具有可插拔注释支持,包括Feign注解和JAX-RS注解、Feign还支持可插拔编码器和解码器、Spring Cloud增加了对Spring MVC注释的支持,并HttpMessageConverters在Spring Web中使用了默认使用的相同方式。Spring Cloud集成了Ribbon和Eureka,在使用Feign时提供负载平衡的http客户端。Fegin对Robbin进行了封装,如果需要配置自己的负载算法,可以自定义Ribbon的算法即可。 Spring Cloud Feign 提供的声明式服务绑定功能来实现对该服务接口的调用。

1.创建 一 个 Spring Boot 基础工程,对pom文件修改

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR3</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <!-- SpringCloud 所有子项目 版本集中管理. 统一所有SpringCloud依赖项目的版本依赖-->
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin><!-- SpringBoot 项目打jar包的Maven插件 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2.application.yml

server:
  port: 9012

spring:
  application:
    name: feign-server #服务注册到Eureka上使用的名称

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
  instance:
    instance-id: feign-server-9012
    prefer-ip-address: true #访问路径显示IP地址

3.修改主启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.wuzz.demo"})
public class FeignApp
{
    public static void main( String[] args )
    {
        SpringApplication.run(FeignApp.class, args);
    }
}

4.负载均衡配置

@Configuration
public class ConfigBean {
    
//    Eureka是基于REST(Representational State Transfer)服务,
//    主要以AWS云服务为支撑,提供服务发现并实现负载均衡和故障转移。
//    我们称此服务为Eureka服务。
//    Eureka提供了Java客户端组件,Eureka Client,方便与服务端的交互。
//    客户端内置了基于round-robin实现的简单负载均衡。
//    在Netflix,为Eureka提供更为复杂的负载均衡方案进行封装,
//    以实现高可用,它包括基于流量、资源利用率以及请求返回状态的加权负载均衡。

    /**
     * Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法。
    Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试等。
    简单的说,Ribbon是一个客户端负载均衡器,
    我们可以在配置文件中列出load Balancer后面所有的机器,
    Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,
    我们也很容易使用Ribbon实现自定义的负载均衡算法
     * 
     */
//    Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便。 
//    它具有可插拔注释支持,包括Feign注解和JAX-RS注解、Feign还支持可插拔编码器和解码器、
//    Spring Cloud增加了对Spring MVC注释的支持,
//    并HttpMessageConverters在Spring Web中使用了默认使用的相同方式。
//    Spring Cloud集成了Ribbon和Eureka,在使用Feign时提供负载平衡的http客户端。    
    
    @Bean
    @LoadBalanced // ribbon是客户端 的负载均衡工具
    //默认算法是轮询算法 核心组件IRule
    public  RestTemplate getRestTemplate() {
        
        return new RestTemplate();
    }
    
    @Bean
    public IRule myRule() { // 负载均衡算法。。。。
        
//        return new RoundRobinRule();
        return new RandomRule();
    }
}

5.声明式服务类

@FeignClient(value ="cloud-provider")
public interface ClientService {
  //如果feign代理的是get请求,必须用@RequestMapping 不能用@GetMapping
  // 每个参数必须带上@RequestParam,否则会报post not support! 
  @RequestMapping(value = "/hello", method = RequestMethod.GET)
   String hello(@RequestParam("id") String id) ; 
}

6.controller

@RestController
public class FeignController {
    
    @Autowired
    private ClientService service;
    
    @RequestMapping(value ="/feign/hello")
    public String hello() {
        
        return service.hello("1");
    }
}

  这样就配置好了。

指定服务配置:

  大多数情况下,我们对于服务调用的超时时间可能会根据实际服务的特性做 一 些调整,所以仅仅依靠默认的全局配置是不行的。 在使用SpringCloud Feign的时候,针对各个服务客户端进行个性化配置的方式与使用SpringCloud Ribbon时的配置方式是 一 样的, 都采用<client>. ribbon.key=value 的格式进行 设置。在定义Feign客户端的时候, 我们使用了@FeignClient注解。 在初始化过程中,SpringCloud Feign会根据该注解的name属性或value属性指定的服务名, 自动创建一 个同名的Ribbon客户端。也就是说,在之前的示例中,使用@FeignClient(value= "cloud-provider")来创 建 Feign 客 户 端 的 时 候 , 同时也创建了一个 名为cloud-provider的Ribbon客户端。 既然如此, 我们就可以使用@FeignClient注解中的name或value属性值来设置对应的Ribbon参数, 比如:

cloud-provider.ribbon.ConnectTimeout = 500 //请求连接的超时时间。
cloud-provider.ribbon.ReadTimeout = 2000  //请求处理的超时时间。
cloud-provider.ribbon.OkToRetryOnAllOperations = true //对所有操作请求都进行重试。
cloud-provider.ribbon.MaxAutoRetriesNextServer = 2 //切换实例的重试次数。
cloud-provider.ribbon.MaxAutoRetries = 1 //对当前实例的重试次数。

重试机制:

  在 Spring Cloud Feign 中默认实 现了 请 求 的重 试 机 制 , 而上面我 们对 于 cloud-provider 客户端的配置内容就是对于请求超时以及重试机制配置的详情.

  这里需要注意一 点, Ribbon的超时 与Hystrix的超时是两个概念。 为了让上述实现有效,我们需要 让Hystrix的超时时间大于Ribbon的超时时间, 否则Hystrix命令超时后, 该命令直接熔断, 重试机制就 没有任何意义了。

  要么设置 Hystrix 的超时时间比Ribbon大(hystrix.command.default.execution.isolation.thread. timeoutInMilliseconds = 5000),要么直接关闭 Hystrix 超时配置(feign.hystrix.enabled= false).配置的时候不提示没关系,配置上就有效果。

  如果不想全局关闭可以只针对服务进行关闭:@FeignClient(name="HELLO - SERVICE", configuration = DisableHystrixConfiguration.class)

  在 feign-server应用中增加上文中提到的重试配置参数。其中,由于 cloud-provider.ribbon.MaxAutoRetries 设置为 1, 所以重试策略先尝试访问首选实例 一 次, 失败后才更换实例访问, 而更换实例访问的次数通过 cloud-provider.ribbon.MaxAutoRetriesNextServer 参数 设置为 2, 所以会尝试更换两次实例进行重试。

  最后, 启动这些应用, 并尝试访问几次 http://localhost:9012/feign­/hello 接口。可以通过 cloud-provider 服务打印请求信息,修改对应/hello接口如下

public String hello(String id) throws InterruptedException {
        // 测试fegin 超时重试代码开始
        List<ServiceInstance> instances = client.getInstances("feign-server");
        //测试超时
        int sleepTime = new Random().nextInt(3000);
        System.out.println("sleepTime:" + sleepTime);
        Thread.sleep(sleepTime);
        System.out.println("/hello, host:" + instances.get(0).getHost() +
                instances.get(0).getServiceId());
        // 测试fegin 超时重试代码结束
         return  "Hello Eureka Provider1";
    }

  然后控制台来查看重试的日志。

  从控制台输出中,我们可以看到这次访问的第 一 次请求延迟时间为 2088毫秒,由于超时时间设置为 2000 毫秒, Feign 客户端发起了重试,第二次请求的延迟为 1338 秒,没有超时 。Feign客户端在进行服务调用时, 虽然经历了一 次失败,但是通过重试机制, 最终还是获得了请求结果。所以, 对于 重试机制的实现, 对于构建高可用的 服务集群来说非常重要, 而 SpringCloud Feign也为其提供了足够的支持。

其他配置:

  请求压缩。Spring Cloud Feign支持对请求与响应进行 GZIP 压缩,以减少通信过程中的性能损耗 。我们只需通过下面两个参数设置, 就能开启请求与响应的压缩功能:

feign.compression.request.enabled = true
feign.compression.response.enabled = true

  同时, 我们还能对请求压缩做 一 些更细致的设置, 比如下面的配置内容指定了压缩的请求数据类型, 并设置了请求压缩的大小下限, 只有超过这个大小的请求才会对其进行压缩。

feign.compression.request.enabled = true
feign.compression.request.mime-types = text/xml,application/xml,application/json //默认值
feign.compression.request.min-request-size=2048 //默认值

日志配置:

  Spring Cloud Feign 在构建被 @FeignClient 注解修饰的服务客户端时,会为每 一 个客户端都创建 一 个 feign.Logger 实例,我们可以利用该日志对象的 DEBUG 模式来帮助分析 Feign 的请求细节。可以在 application.properties 文件中使用 logging.level.<FeignClient> 的参数配置格式来开启指定 Feign 客户端的 DEBUG 日志, 其中<FeignClient> 为 Feign 客户端定义接口的完整路径, 比如针对本文中我们实现的 HelloService 可以按如下配置开启:

logging.level.com.wuzz.demo.HelloService = DEBUG

  但是, 只是添加了如上配置, 还无法实现对 DEBUG 日志的输出。 这时由于 Feign 客户端默认的 Logger.Level 对象定义为 NONE 级别, 该级别不会记录任何 Feign 调用过程中的信息, 所以我们需要调整它的级别, 针对全局的日志级别, 可以在应用主类中直接加入 Logger.Level 的 Bean 创建, 具体如下:

@Bean
Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
}

  或者添加个配置类:

@Configuration
public class FullLogConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
}
@FeignClient(name = "cloud-provider", configuration = FullLogConfiguration.class)
public interface HelloService {
    。。。。。。
}

  在调整日志级别为 FULL 之后, 我们可以再访问一 下之前的http://localhost:9012/feign/hello 接口,这时我们在 feign-server的控制台中就可以看到对应的日志。类似如下信息:

  对于 Feign 的 Logger 级别主要有下面 4 类, 可根据实际需要进行调整使用。

  • NONE: 不记录任何信息。
  • BASIC: 仅记录请求方法、URL以及响应状态码和执行时间。
  • HEADERS: 除了记录BASIC级别的信息之外, 还会记录请求和响应的头信息。
  • FULL: 记录所有请求与响应的明细, 包括头信息、 请求体、 元数据等。

服务降级(Feign客户端):

  整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开起会来。所谓降级,一般是从整体负荷考虑,就是当某个服务熔断后,服务器将不再被调用,此时客户端可以准备自己本地的一个fallback回调,返回一个缺省值,这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,服务降级是在客户端进行的.

  1. 根据目标接口,创建一个实现了FallbackFactory的类 

@Component
public class HystrixClientService implements FallbackFactory<ClientService> {
    @Override
    public ClientService create(Throwable throwable) {
        return new ClientService() {
            @Override
            public String hello() {
                return "服务降级。。。。";
            }
        };
    }
}

  2. 在目标接口上的@FeignClient中添加fallbackFactory属性值

@FeignClient(value ="cloud-provider", fallbackFactory = HystrixClientService.class)
public interface ClientService {

    @RequestMapping(value ="/hello",method= RequestMethod.GET)
    String hello() ;
}

  3.修改 application.yml ,添加一下

feign: 
   hystrix: 
    enabled: true

  这样就完成了服务降级的基本配置,可以进入测试。。。。

Ribbon和 Feign的区别:

  • Ribbon添加maven依赖 spring-starter-ribbon    使用@RibbonClient(value="服务名称")    使用RestTemplate调用远程服务对应的方法。feign添加maven依赖 spring-starter-feign    服务提供方提供对外接口  调用方使用    在接口上使用    @FeignClient("指定服务名")
  • Ribbon和Feign都是用于调用其他服务的,不过方式不同。
  • 启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。
  • 服务的指定位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。
  • 调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。
  • Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。
posted @ 2019-04-22 16:23  吴振照  阅读(3925)  评论(0编辑  收藏  举报