spring cloud --- Feign --- 心得

1.前言

什么是Feign?

      为了简化我们的开发,Spring Cloud Feign出现了!它基于 Netflix Feign 实现,整合了 Spring Cloud Ribbon 与 Spring Cloud Hystrix,

除了整合这两者的强大功能之外,它还提 供了声明式的服务调用(不再通过RestTemplate)。

  事实上很早Feign就已经停止维护了 ,spring推出了 open Feign ,原理基本一样 ,但是 Feign还是有必要了解怎么使用的。

2.操作

(1)引入依赖

spring cloud --- Feign  --- 心得

 

 

 完成的pom.xml

spring cloud --- Feign  --- 心得
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.atguigu.springcloud</groupId>
        <!--    父级maven模块的工程名字-->
        <artifactId>microservicecloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo-my-cen-feign</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo-my-cen-feign</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Ribbon相关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

        <!-- 修改后立即生效,热部署 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <!--feign依赖包-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>



    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
View Code

(2)新建一个装 Feign 接口的文件夹 ,我这里设为 feignInter

目录结构截图

spring cloud --- Feign  --- 心得

 

 

 (3)编写一个目标微服务的接口 FeignServuce1  ,名字随意 ,一般一个微服务实例则装载一个文件里或者一个一个文件夹里面

spring cloud --- Feign  --- 心得
package com.example.demomycenfeign.feignInter;


import com.example.demomycenfeign.feignInter.myFallbackFactory.FeignServuce1FallbackFactory;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


//注入服务的应用名
@FeignClient(value = "MICROSERVICECLOUD-DEPT1", fallbackFactory = FeignServuce1FallbackFactory.class)
public interface FeignServuce1 {


    @RequestMapping(value = "/ask", method = RequestMethod.GET)
    public String ask();


}
View Code

spring cloud --- Feign  --- 心得

 

 

 注解 @FeignClient 是用来标记这个接口是用来映射微服务接口的 ,参数 value 是为微服务提供者的应用名 ,在eureka可以查到 ,fallbackFactory 是当服务熔断后调用这个类的方法,

接口具体的路径和参数应该与服务提供者的一样。

(3)编写熔断抛出的类 FeignServuce1FallbackFactory,类似于抛出异常操作

spring cloud --- Feign  --- 心得
package com.example.demomycenfeign.feignInter.myFallbackFactory;

import com.example.demomycenfeign.feignInter.FeignServuce1;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * feign使用断路器【熔断器】 ,当熔断发生后,运行这里的方法。类似于异常抛出
 * 这里主要是处理异常出错的情况(降级/熔断时服务不可用,fallback就会找到这里来)
 */
@Component // 不要忘记添加,不要忘记添加,不加则无法使用熔断器
public class FeignServuce1FallbackFactory implements FallbackFactory<FeignServuce1> {
    @Override
    public FeignServuce1 create(Throwable throwable) {
        return new FeignServuce1() {
            @Override
            public String ask() {
                return "feign使用了断路器【熔断器】,限制服务处于熔断状态,运行了类似于抛出异常的方法,时间=" + new Date();
            }
        };
    }
}
View Code

spring cloud --- Feign  --- 心得

 

 

 注解 @Component 很重要,否则熔断后无法找到,请求会一直在等待

开启熔断器,还有关键的一步,去applicable.properties 配置  ,因为feign 底层熔断器就是使用 Hystrix

spring cloud --- Feign  --- 心得

 

 

 (4)默认客户端负载均衡策略是轮询策略 ,想要修改,有3种办法 ,这里只介绍一种 ,使用注解 @Configuration

 

新建一个配置类

spring cloud --- Feign  --- 心得

完成配置源码

spring cloud --- Feign  --- 心得
package com.example.demomycenfeign.cfgBean;

import com.netflix.loadbalancer.BestAvailableRule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


//注解@Configuration千万千万不要忘记添加 ,不然无法使用这个静态配置类
@Configuration
public class ConfigBean {

    //设置负载均衡策略
    @Bean
    public IRule myRule() {
        //其他看看 https://www.cnblogs.com/htyj/p/10705472.html
        //

        //轮询策略,其实里面就是一个计数器
//        return new RoundRobinRule();

        //该策略通过遍历负载均衡器中维护的所有实例,会过滤调故障的实例,并找出并发请求数最小的一个,所以该策略的特征是选择出最空闲的实例
        //如果集群有个服务器挂了,就可以过略的他,防止访问了故障服务器
        return new BestAvailableRule();

    }

}
View Code

 

 spring cloud --- Feign  --- 心得

 

 

 现在去启动类开启 Feign 和设置负载均衡策略

spring cloud --- Feign  --- 心得
package com.example.demomycenfeign;

import com.example.demomycenfeign.cfgBean.ConfigBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
//
//
@EnableFeignClients(basePackages = {"com.example.demomycenfeign.feignInter"})
//开启客户端负载均衡自定义策略,参数name是该服务器的应用名字 ,configuration设置 策略配置类
@RibbonClient(name = "520love" ,configuration = ConfigBean.class)
//
public class DemoMyCenFeignApplication {

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

}
View Code

spring cloud --- Feign  --- 心得

 

 

 

 注解 @ EnableFeignClients 是开启 Feign ,参数  basePackages 是指 装有 目标微服务的接口 的文件夹 ,用于Feign扫描接口才能知道在服务接口在哪里

注解 @RibbonClient 开启客户端负载均衡自定义策略,参数name是该服务器的应用名字 ,configuration设置 策略配置类

(5)到了关键的一步,怎么使用 Feign 调用服务呢?

很简单

spring cloud --- Feign  --- 心得

 

 

 

spring cloud --- Feign  --- 心得
package com.example.demomycenfeign.GGController;




import com.example.demomycenfeign.feignInter.FeignServuce1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
//@PropertySource("classpath:my.properties")
public class GGController {

//    private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT1";

    @Autowired
    private FeignServuce1 feignServuce1;


    @RequestMapping("/bb")
    public String bb(){

//        Food d = new Food();
//        d.setApple("苹果");
//        d.setEgg("鸡蛋");
//        System.out.println(d);
//        try {
//            Thread.sleep(5000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

//        System.out.println("调用服务,开启负载均衡Ribbon");
        //使用restTemplate 直接调用 ,postForObject 是post请求方式 ,getForObject是get请求方式,根据服务提供者的接口选择,这个是需要提前知道服务提供者的接口格式的
//        return restTemplate.getForObject(REST_URL_PREFIX + "/ask", String.class) +"===========消费者端口是"+port;
//        try {
//            Thread.sleep(5000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        return feignServuce1.ask()+"===========我的消费者端口是"+port;

    }


    @Value("${server.port}")
    private String port;

//    public String myFallback(){
//        return "服务繁忙,已经开启了Hystrix";
//    }

    @RequestMapping("/cc")
    public String otherService() {
        return "我是其他服务";
    }




}
View Code

3.测试

(1)提前准备 并开启  1个注册中心端口7001 , 2个服务提供者端口8001,8003 【集群】,一个消费者端口520

spring cloud --- Feign  --- 心得

 

 

 (2)端口520 ,浏览器输入 http://localhost:520/bb ,可以正常访问

spring cloud --- Feign  --- 心得

 

 

 浏览器输入 http://localhost:520/cc ,可以正常访问

spring cloud --- Feign  --- 心得

 

 (3)现在使用JMeter 工具做压力测试 ,建立2000个线程,同时访问http://localhost:520/bb

spring cloud --- Feign  --- 心得

 

 (4)端口520 ,再次浏览器输入 http://localhost:520/bb ,可以看到服务熔断了,对服务做降级保护,运行了fallbackFactory的类方法

spring cloud --- Feign  --- 心得

 

 再次访问其他端口 ,  浏览器输入 http://localhost:520/cc ,可以正常访问,接口并没有崩溃 ,但是。。。。多访问几次,会发现端口520崩了。。。/cc访问不到

spring cloud --- Feign  --- 心得

 

 结论是:2000线程访问http://localhost:520/bb ,导致 端口520崩溃 ,

猜测,feign 熔断只是对远程服务接口负责,当服务不可用或者服务响应超时,则会熔断/服务降级

//需要验证一下

 

4.第二次测试

(1)只开一个服务端口8001 ,

端口520 ,浏览器输入 http://localhost:520/bb ,可以正常访问服务

spring cloud --- Feign  --- 心得

 

 

(2)使用JMeter 工具做压力测试 ,建立2000个线程,同时访问http://localhost:8001/ask ,导致服务8001端口崩了

端口520 ,浏览器输入 http://localhost:520/bb ,发现接口熔断了 

spring cloud --- Feign  --- 心得

 

 

(3)关闭2000线程后,端口520 ,浏览器输入 http://localhost:520/bb,有可以调用接口了

spring cloud --- Feign  --- 心得

 

 

结论:feign 熔断只是对远程服务接口负责,当服务不可用或者服务响应超时,则会熔断/服务降级,消费者端口不可再调用服务

最简单的证明就是把服务提供者服务器直接关掉,服务消费的调用接口直接熔断,无法使用,展示这里就不写了

 

上一篇:学无止境,学海无涯


下一篇:矩阵化一维