目录

一、Feign简述

二、Feign来源与宗旨

三、Feign的使用案例

1. 创建  父工程及公共api 见 第三章

2. 创建 Eureka 服务 见 第三章

3. 创建 订单(order-service )服务

       3.1 修改pom文件如下

       3.2 订单服务中创建接口 IOrderFeignService  

       3.3 订单服务中创建 OrderController

       3.4 配置订单服务的配置文件

       3.5 创建订单服务的主启动类

4 创建商品(product-service )服务集群

       4.1 创建商品(product-service )服务

       4.2 创建productServer服务的主启动类

       4.3、配置 productServer 配置文件

       4.4、创建 ProductController

       4.5 创建商品服务提供者集群

5、进行测试

6、其他(了解)


一、Feign简述

       参考官网: https://github.com/OpenFeign/feign

微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client,Spring的RestTemplate。但是,用起来最方便的还是要属Feign了。

       Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

简单说:Feign是一个声明式的Web服务客户端的负载均衡, 使得编写Web服务客户端变得非常容易,只需要创建一 个接口,然后在上面添加注解即可。

二、Feign来源与宗旨

       前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。

       所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可), 即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。

       开发人员习惯面向接口编程规范,比如webservice接口,比如我们的DAO接口。为了让大家调用微服务方便,提供两种调用微服务的方式。

  • 通过微服务名字获得调用地址进行负载均衡

         private static final String REST_URL_PREFIX = "http://PRODUCT-SERVICE";

  • 通过 接口+注解,获得我们的调用服务 

创建一个接口和一个Feign的注解。

三、Feign的使用案例

1. 创建  父工程及公共api 见 第三章

2. 创建 Eureka 服务 见 第三章

3. 创建 订单(order-service )服务

        注意:此时无需在ConfigBean中创建 RestTeamplate 组件,也不需要使用RestTemplate组件进行远端调用。。因为使用 openfeign 只需要写调用远端服务的接口和注解即可。

OpenFeign有负载均衡吗 openfeign 自定义负载均衡_微服务

       3.1 修改pom文件如下

        新增 openfeign 负载均衡 依赖,修改pom文件,如下:        

  <!-- openfeign相关 开始 -->        <!--告诉微服务注册中心我是微服务的client端 openfeign 需要和eureak整合-->        <dependency>           <groupId>org.springframework.cloud</groupId>           <artifactId>spring-cloud-starter-openfeign</artifactId>        </dependency>        <!-- openfeign相关 结束 -->

     完整pom文件如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springcloudbase</artifactId> <groupId>com.hwadee.springcloud2022</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.hwadee.springcloud</groupId> <artifactId>orderServer9000</artifactId> <dependencies> <!--告诉微服务注册中心我是微服务的client端 Feign 需要和eureak整合--> <!-- Feign相关 开始 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- Feign相关 结束 --> <!--web依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 管理公共api --> <dependency> <groupId>com.hwadee.springboot2022</groupId> <artifactId>springcloud-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!--Eureka Client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> </project>

 注意:

       如果引入 spring-cloud-starter-openfeign 后,使用 Ribbon 是客户端负载均衡器 则无需引入额外依赖,因为引入的 spring-cloud-starter-openfeign 依赖中集成了 Ribbon。

       3.2 订单服务中创建接口 IOrderFeignService  

        在订单服务中的service目录中创建 调用远端服务的接口 IOrderFeignService  ,在接口上使用注解 @FeignClient("PRODUCT-SERVICE"),表示 controller 调用接口方法时候,使用负载均衡机制,进行寻找服务名为 "PRODUCT-SERVICE" 的远端服务,接口中的方法和controller中的方法写法类似。代码如下:

import com.hwadee.springcloud.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Component // 让 spring 可以识别,不加也行,但是在注入的时候 IDEA 会报错,不会影响运行,但有条红线让自己不舒服
@FeignClient("PRODUCT-SERVICE")
public interface IOrderFeignService {
    @RequestMapping(value = "/product/buy/{id}")
    Product findOrderById(@PathVariable Long id);

    @RequestMapping(value = "/product/delete/{id}")
    Product deleteOrderById(@PathVariable Long id);
}

      小结:

  •  Feign面向接口编程
  •  Ribbon面向RestTemplate编程

    【Feign通过接口的方法调用Rest服务(之前是Ribbon+ RestTemplate),请求发送给Eureka服务器(http://CONSUMER/DEPT/dept/list),由于Feign在融合了Ribbon技术,所以也支持负载均衡作用,所以通过Feign进行服务调用的时候,Feign在eureka服务列表中直接找到服务接口。】

    【Feign集成Ribbon,利用Ribbon维护了SERVICE-PROVIDER的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。】

       3.3 订单服务中创建 OrderController

        在订单服务中创建 OrderController ,在 OrderController 中注入IOrderFeignService  接口,通过 IOrderFeignService 接口方法 调用远端服务,代码如下:

import com.hwadee.springcloud.entity.Product;
import com.hwadee.springcloud.service.IOrderFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    IOrderFeignService orderFeignService;

    @RequestMapping("/buy/{id}")
    public Product buy(@PathVariable Long id) {
        System.out.println("进入OrderController orderFeignService 准备调用远端接口");
        Product product = orderFeignService.findOrderById(id);
        return product;
    }

    @RequestMapping(value = "/delete/{id}")
    Product deleteOrderById(@PathVariable Long id){
        System.out.println("进入OrderController orderFeignService 准备调用远端接口");
        Product product = orderFeignService.deleteOrderById(id);
        return product;
    }

}

       3.4 配置订单服务的配置文件

        配置订单服务(order-service)中的配置文件application.yml,修改如下:

server:
  port: 9000
spring:
  application:
    name: order-service # 为当前订单服务命名为 order-service

# 配置eureka客户端信息
eureka:
  client:
    service-url:
      # 配置eureka客户端服务order-service的注册地址,与 eureka-server 中暴露地址要保持一致
      defaultZone: http://localhost:8000/eureka/
  instance:
    prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
    # instance-id: order-service  # 实例 id,服务的唯一标识,会自动的找到order-service的ip和端口
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置

       3.5 创建订单服务的主启动类

        创建订单服务的主启动类 OrderServerApplication ,并增加注解 @EnableFeignClients 来启动 openFeign。代码如下:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
@EnableFeignClients  // 启动 feign
public class OrderServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServerApplication.class, args);
    }
}

4 创建商品(product-service )服务集群

       4.1 创建商品(product-service )服务

      创建项目:productServer9001,引 入 web 启动器和 eureka 客户端,修改 pom 文件如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>springcloudbase</artifactId>
    <groupId>com.hwadee.springcloud2022</groupId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.hwadee.springcloud</groupId>
  <artifactId>productServer9001</artifactId>
  <version>0.0.1-SNAPSHOT</version>


  <dependencies>
     <!--web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

    <!-- 管理公共api -->
    <dependency>
      <groupId>com.hwadee.springboot2022</groupId>
      <artifactId>springcloud-api</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <!--Eureka Client-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  </dependencies>
</project>

       4.2 创建productServer服务的主启动类

        创建productServer服务的主启动类,并使用注解 @SpringBootApplication 和@EnableEurekaClient// 启动 eureka 客户端

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
public class ProductServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServerApplication.class, args);
    }
}

       4.3、配置 productServer 配置文件

        配置 productServer 配置文件 application.yml 信息:

server:
  port: 9001
spring:
  application:
    name: product-service # 为当前商品服务命名
eureka:
  client:
    service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致
      defaultZone: http://localhost:8000/eureka
  instance:
    prefer-ip-address: true  # 是否使用 IP 地址注册,默认 false
    # instance-id: product-service  # 实例 id,服务的唯一标识
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
    lease-renewal-interval-in-seconds: 5  # 发送心跳的间隔,单位秒,默认 30
    lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90

       4.4、创建 ProductController

        创建ProductController,方法如下:

import com.hwadee.springcloud.entity.Product;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
@RequestMapping("/product")
public class ProductController {
    //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
    @Value("${server.port}")
    private String port;
    @Value("${spring.cloud.client.ip-address}")
    private String ip;

    @RequestMapping("/buy/{id}")
    public Product findById(@PathVariable Long id) {
        Product product = new Product();
        product.setId(id);
        // 后面需要测试负载均衡,所以返回 ip 地址及端口号
        product.setName("当前访问服务地址:" + ip + ":" + port+"  "+"查询商品订单,订单号:"+id);
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);
        return product;
    }

    @RequestMapping(value = "/delete/{id}")
    Product deleteOrderById(@PathVariable Long id){
        Product product = new Product();
        product.setId(id);
        // 后面需要测试负载均衡,所以返回 ip 地址及端口号
        product.setName("当前访问服务地址:" + ip + ":" + port+"  "+"从购物车删除订单,订单号:"+id);
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);
        return product;
    }

}

       4.5 创建商品服务提供者集群

  • 方式一

     为了搭建多个产品(product-service )服务,将创建的商品(product-service )服务复制2份,即三个产品(product-service)服,服务名统一为:product-service,项目名分别为:productServer9001,productServer9002,productServer9003,端口号分别设置为:9001、9002、9003,在application.yml中配置如下:

OpenFeign有负载均衡吗 openfeign 自定义负载均衡_OpenFeign有负载均衡吗_02

  • 方式二

      使用如下方式:不用创建项目复制多个项目。

OpenFeign有负载均衡吗 openfeign 自定义负载均衡_客户端_03

5、进行测试

  •  首先启动注册中心服务:eurekaServer 主启动类 EurekaServerApplication
  • 其次启动订单服务:order-service 主启动类 OrderServerApplication
  • 启动商品服务productServer9001:product-servie 的主启动类 ProductServerApplication
  • 启动商品服务productServer9002:product-servie 的主启动类 ProductServerApplication
  • 启动商品服务productServer9003:product-servie 的主启动类 ProductServerApplication

   多次访问订单服务: http://localhost:9000/order/buy/1 查看返回的 ip 和 端口信息

OpenFeign有负载均衡吗 openfeign 自定义负载均衡_spring_04

OpenFeign有负载均衡吗 openfeign 自定义负载均衡_客户端_05

6、其他(了解)

       Ribbon 是客户端负载均衡器,在商品、订单服务中,订单服务是商品服务的客户端,所以需要在 order-service 中配置 Ribbon。

       由于之前引入的 spring-cloud-starter-openfeign 依赖中集成了 ribbon,所以不需要额外引入依赖,上面的代码不变,只需要直接在 订单服务 的配置文件中添加如下配置即可,修改如下:

product-service: # 服务名
   ribbon:
     NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  # 选择负载均衡策略,默认为轮询方式,当前配置为随机方式
     ConnectTimeout: 250                 # 连接超时时间
     ReadTimeout: 1000                   # ribbon 读取超时时间
     OkToRetryOnAllOperations: true      # 是否对所有操作都进行重试
     MaxAutoRetriesNextServer: 1         # 切换实例的重试次数
     MaxAutoRetries: 1                   # 对当前实例的重试次数

OpenFeign有负载均衡吗 openfeign 自定义负载均衡_spring_06

第四章:Ribbon负载均衡

第六章:Hystrix断路器详解+环境搭建