OpenFeign实战

  OpenFeignNetflix开发的声明式、模板化的http请求客户端,作用和RestTemplate差不多,只不过OpenFeign可以更加便捷、优雅地调用http api
  OpenFeign可以将提供者提供的http接口伪装为Java接口进行消费,消费者只需使用 接口 + 注解 的方式便可直接调用提供者提供的http接口,而无需再使用RestTemplate

  1. OpenFeignFeign

Spring Cloud Dalston 版及之前的版本使用的是 Feign,而该项目现已更新为了 OpenFeign,新版本中的依赖也发生了变化。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</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-openfeign</artifactId> </dependency>

Feign本身不支持Spring MVC的注解,它有一套自己的注解;OpenFeignSpring CloudFeign的基础上支持了Spring MVC的注解,如@RequesMapping等等。 OpenFeign@FeignClient可以解析SpringMVC@RequestMapping注解下的接口, 并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

  1. OpenFeignRibbon

RibbonNetflix的一个开源的负载均衡项目,是一个客户端负载均衡器,运行在消费者端。OpenFeign也是运行在消费者端的,并且使用Ribbon进行负载均衡,所以OpenFeign直接内置了Ribbon,即在导入 OpenFeign依赖后,无需再导入Ribbon依赖了。

一、OpenFeign项目搭建

1. 提供者应用

image.png

  • pom.xml文件
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.10.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>user-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<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.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<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>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>user-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>



    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <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.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>



        <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>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

    </dependencies>


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

</project>
<?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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.10.RELEASE</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>user-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>8</java.version> <spring-cloud.version>Hoxton.SR12</spring-cloud.version> </properties> <dependencyManagement> <dependencies> <dependency> <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.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <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>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
  • application.properties
server.port=8081
#
eureka.client.service-url.defaultZone=http://admin:123@ek1.com:7901/eureka/,http://admin:123@ek2.com:7902/eureka/
#
# 客户端在注册中心中的名称
eureka.instance.instance-id=user-provider-8081
#
# 设置当前 client 每5秒向 server 发送一次心跳,默认 30s
eureka.instance.lease-renewal-interval-in-seconds=5
#
# 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认为90
eureka.instance.lease-expiration-duration-in-seconds=90
#
# 当前服务名称
spring.application.name=user-provider
#
# 表示将自己的ip注册到Eureka Server上。不配置,表示将操作系统的 hostname 注册到server
eureka.instance.prefer-ip-address=true
#
# eureka 服务名,默认值 unknown;如果没有配置,则取 spring.application.name
#eureka.instance.appname=user-provider
#
# 实例的虚拟主机名称,默认值 unknown;如果没有配置,则取 spring.application.name
#eureka.instance.virtual-host-name=user-provider
#
# 对外开放所有监控端点
management.endpoints.web.exposure.include=*
#
# 是否将自己注册到其他Eureka Server,默认为true
eureka.client.register-with-eureka=true
#
# 是否从eureka server获取注册信息, 需要
eureka.client.fetch-registry=true
server.port=8081
#
eureka.client.service-url.defaultZone=http://admin:123@ek1.com:7901/eureka/,http://admin:123@ek2.com:7902/eureka/

#

# 客户端在注册中心中的名称

eureka.instance.instance-id=user-provider-8081
#


# 设置当前 client 每5秒向 server 发送一次心跳,默认 30s
eureka.instance.lease-renewal-interval-in-seconds=5
#

# 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认为90秒
eureka.instance.lease-expiration-duration-in-seconds=90
#

# 当前服务名称
spring.application.name=user-provider
#

# 表示将自己的ip注册到Eureka Server上。不配置,表示将操作系统的 hostname 注册到server
eureka.instance.prefer-ip-address=true
#

# eureka 服务名,默认值 unknown;如果没有配置,则取 spring.application.name
#eureka.instance.appname=user-provider
#

# 实例的虚拟主机名称,默认值 unknown;如果没有配置,则取 spring.application.name
#eureka.instance.virtual-host-name=user-provider
#

# 对外开放所有监控端点
management.endpoints.web.exposure.include=*
#

# 是否将自己注册到其他Eureka Server,默认为true
eureka.client.register-with-eureka=true
#

# 是否从eureka server获取注册信息, 需要
eureka.client.fetch-registry=true
server.port=8081 # eureka.client.service-url.defaultZone=http://admin:123@ek1.com:7901/eureka/,http://admin:123@ek2.com:7902/eureka/ # # 客户端在注册中心中的名称 eureka.instance.instance-id=user-provider-8081 # # 设置当前 client 每5秒向 server 发送一次心跳,默认 30s eureka.instance.lease-renewal-interval-in-seconds=5 # # 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认为90秒 eureka.instance.lease-expiration-duration-in-seconds=90 # # 当前服务名称 spring.application.name=user-provider # # 表示将自己的ip注册到Eureka Server上。不配置,表示将操作系统的 hostname 注册到server eureka.instance.prefer-ip-address=true # # eureka 服务名,默认值 unknown;如果没有配置,则取 spring.application.name #eureka.instance.appname=user-provider # # 实例的虚拟主机名称,默认值 unknown;如果没有配置,则取 spring.application.name #eureka.instance.virtual-host-name=user-provider # # 对外开放所有监控端点 management.endpoints.web.exposure.include=* # # 是否将自己注册到其他Eureka Server,默认为true eureka.client.register-with-eureka=true # # 是否从eureka server获取注册信息, 需要 eureka.client.fetch-registry=true
  • 实体bean
public class User implements Serializable {
private String idCard;
private String username;
public User() {
}
public User(String idCard, String username) {
this.idCard = idCard;
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "User{" +
"idCard='" + idCard + ''' +
", username='" + username + ''' +
'}';
}
}
public class User implements Serializable {

    private String idCard;
    private String username;





    public User() {
    }

    public User(String idCard, String username) {
        this.idCard = idCard;
        this.username = username;
    }










    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }


    public String getIdCard() {
        return idCard;
    }



    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString() {
        return "User{" +
                "idCard='" + idCard + ''' +
                ", username='" + username + ''' +
                '}';
    }
}
public class User implements Serializable { private String idCard; private String username; public User() { } public User(String idCard, String username) { this.idCard = idCard; this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getIdCard() { return idCard; } public void setIdCard(String idCard) { this.idCard = idCard; } @Override public String toString() { return "User{" + "idCard='" + idCard + ''' + ", username='" + username + ''' + '}'; } }
  • controller接口
@RestController
@RequestMapping("/user")
public class UserController {
private final static Logger log = LoggerFactory.getLogger(UserController.class);
@PostMapping("/save")
public Boolean saveUser(@RequestBody User user) {
log.info("save user success : {}", JSON.toJSONString(user));
return Boolean.TRUE;
}
@DeleteMapping("/del/{id}")
public Boolean deleteUser(@PathVariable("id") Long id) {
log.info("delete user success, user id : {}", id);
return Boolean.TRUE;
}
@GetMapping("/list")
public List<User> getUserList() {
User user = new User("110", "大宝");
log.info("getUserList result : " + user);
return Lists.newArrayList(user);
}
@GetMapping("/get")
public User getUserById(@RequestParam(value = "id", required = true) Long id) {
User user = new User("111", "大宝");
log.info("getUserById result : {} , id : {}", user, id);
return user;
}
}
@RestController


@RequestMapping("/user")
public class UserController {





    private final static Logger log = LoggerFactory.getLogger(UserController.class);




    @PostMapping("/save")


    public Boolean saveUser(@RequestBody User user) {
        log.info("save user success : {}", JSON.toJSONString(user));
        return Boolean.TRUE;
    }










    @DeleteMapping("/del/{id}")

    public Boolean deleteUser(@PathVariable("id") Long id) {
        log.info("delete user success, user id : {}", id);
        return Boolean.TRUE;
    }




    @GetMapping("/list")

    public List<User> getUserList() {
        User user = new User("110", "大宝");
        log.info("getUserList result : " + user);
        return Lists.newArrayList(user);
    }



    @GetMapping("/get")
    public User getUserById(@RequestParam(value = "id", required = true) Long id) {
        User user = new User("111", "大宝");
        log.info("getUserById result : {} , id : {}", user, id);
        return user;
    }
}
@RestController @RequestMapping("/user") public class UserController { private final static Logger log = LoggerFactory.getLogger(UserController.class); @PostMapping("/save") public Boolean saveUser(@RequestBody User user) { log.info("save user success : {}", JSON.toJSONString(user)); return Boolean.TRUE; } @DeleteMapping("/del/{id}") public Boolean deleteUser(@PathVariable("id") Long id) { log.info("delete user success, user id : {}", id); return Boolean.TRUE; } @GetMapping("/list") public List<User> getUserList() { User user = new User("110", "大宝"); log.info("getUserList result : " + user); return Lists.newArrayList(user); } @GetMapping("/get") public User getUserById(@RequestParam(value = "id", required = true) Long id) { User user = new User("111", "大宝"); log.info("getUserById result : {} , id : {}", user, id); return user; } }
  • 启动类
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceProviderApplication.class, args);
}
}
@SpringBootApplication

@EnableDiscoveryClient

public class UserServiceProviderApplication {





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



}
@SpringBootApplication @EnableDiscoveryClient public class UserServiceProviderApplication { public static void main(String[] args) { SpringApplication.run(UserServiceProviderApplication.class, args); } }

2. 消费者应用

image.png

  • pom.xml
<?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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.10.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>open-feign-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>open-feign-consumer</name>
<description>open-feign-consumer</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<?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>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.3.10.RELEASE</version>
      <relativePath/>
   </parent>







   <groupId>com.example</groupId>
   <artifactId>open-feign-consumer</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>open-feign-consumer</name>
   <description>open-feign-consumer</description>



   <properties>
      <java.version>8</java.version>
      <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
   </properties>

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


      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>


      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>



   </dependencies>

   <dependencyManagement>
      <dependencies>
         <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
         </dependency>
      </dependencies>
   </dependencyManagement>


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

</project>
<?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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.10.RELEASE</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>open-feign-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>open-feign-consumer</name> <description>open-feign-consumer</description> <properties> <java.version>8</java.version> <spring-cloud.version>Hoxton.SR12</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
  • application.properties
server.port=7072
# 注册中心地址
eureka.client.service-url.defaultZone=http://admin:123@ek1.com:7901/eureka/,http://admin:123@ek2.com:7902/eureka/
#
# 客户端在注册中心中的名称
eureka.instance.instance-id=open-feign-consumer-7072
#
# 当前服务对外暴露的名称
spring.application.name=open-feign-consumer
#
# 指定 feign 从请求到获取提供者响应的超时时间
feign.client.config.default.read-timeout=5000
#
# 指定 feign 连接提供者的超时时间
feign.client.config.default.connect-timeout=5000
#
# 设置当前 client 每5秒向 server 发送一次心跳,默认 30s
eureka.instance.lease-renewal-interval-in-seconds=5
#
# 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认为90
eureka.instance.lease-expiration-duration-in-seconds=90
#
# 表示将自己的ip注册到Eureka Server上。不配置,表示将操作系统的 hostname 注册到 server
eureka.instance.prefer-ip-address=true
#
# 是否将自己注册到其他Eureka Server
eureka.client.register-with-eureka=true
#
# 是否从eureka server获取注册信息
eureka.client.fetch-registry=true
#
# 表示eureka client间隔多久去拉取服务注册信息,默认为30秒,如果要迅速获取服务注册状态,可以缩小该值,比如5
eureka.client.registry-fetch-interval-seconds=5
server.port=7072
# 注册中心地址
eureka.client.service-url.defaultZone=http://admin:123@ek1.com:7901/eureka/,http://admin:123@ek2.com:7902/eureka/

#

# 客户端在注册中心中的名称

eureka.instance.instance-id=open-feign-consumer-7072
#


# 当前服务对外暴露的名称
spring.application.name=open-feign-consumer
#

# 指定 feign 从请求到获取提供者响应的超时时间
feign.client.config.default.read-timeout=5000
#

# 指定 feign 连接提供者的超时时间
feign.client.config.default.connect-timeout=5000
#

# 设置当前 client 每5秒向 server 发送一次心跳,默认 30s
eureka.instance.lease-renewal-interval-in-seconds=5
#

# 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认为90秒
eureka.instance.lease-expiration-duration-in-seconds=90
#

# 表示将自己的ip注册到Eureka Server上。不配置,表示将操作系统的 hostname 注册到 server
eureka.instance.prefer-ip-address=true
#

# 是否将自己注册到其他Eureka Server
eureka.client.register-with-eureka=true
#

# 是否从eureka server获取注册信息
eureka.client.fetch-registry=true
#

# 表示eureka client间隔多久去拉取服务注册信息,默认为30秒,如果要迅速获取服务注册状态,可以缩小该值,比如5秒
eureka.client.registry-fetch-interval-seconds=5
server.port=7072 # 注册中心地址 eureka.client.service-url.defaultZone=http://admin:123@ek1.com:7901/eureka/,http://admin:123@ek2.com:7902/eureka/ # # 客户端在注册中心中的名称 eureka.instance.instance-id=open-feign-consumer-7072 # # 当前服务对外暴露的名称 spring.application.name=open-feign-consumer # # 指定 feign 从请求到获取提供者响应的超时时间 feign.client.config.default.read-timeout=5000 # # 指定 feign 连接提供者的超时时间 feign.client.config.default.connect-timeout=5000 # # 设置当前 client 每5秒向 server 发送一次心跳,默认 30s eureka.instance.lease-renewal-interval-in-seconds=5 # # 表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认为90秒 eureka.instance.lease-expiration-duration-in-seconds=90 # # 表示将自己的ip注册到Eureka Server上。不配置,表示将操作系统的 hostname 注册到 server eureka.instance.prefer-ip-address=true # # 是否将自己注册到其他Eureka Server eureka.client.register-with-eureka=true # # 是否从eureka server获取注册信息 eureka.client.fetch-registry=true # # 表示eureka client间隔多久去拉取服务注册信息,默认为30秒,如果要迅速获取服务注册状态,可以缩小该值,比如5秒 eureka.client.registry-fetch-interval-seconds=5
  • 创建实体
public class User implements Serializable {
private String idCard;
private String username;
public User() {
}
public User(String idCard, String username) {
this.idCard = idCard;
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "User{" +
"idCard='" + idCard + ''' +
", username='" + username + ''' +
'}';
}
}
public class User implements Serializable {




    private String idCard;
    private String username;


    public User() {
    }



    public User(String idCard, String username) {
        this.idCard = idCard;
        this.username = username;
    }



    public String getUsername() {
        return username;
    }



    public void setUsername(String username) {
        this.username = username;
    }

    public String getIdCard() {
        return idCard;
    }



    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }


    @Override
    public String toString() {
        return "User{" +
                "idCard='" + idCard + ''' +
                ", username='" + username + ''' +
                '}';
    }
}
public class User implements Serializable { private String idCard; private String username; public User() { } public User(String idCard, String username) { this.idCard = idCard; this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getIdCard() { return idCard; } public void setIdCard(String idCard) { this.idCard = idCard; } @Override public String toString() { return "User{" + "idCard='" + idCard + ''' + ", username='" + username + ''' + '}'; } }
public class ResultBody<T> implements Serializable {
private boolean status;
// 响应码
private String code;
// 响应描述信息
private String message;
// 响应数据
private T data;
public ResultBody() {
}
private ResultBody(T data) {
this.data = data;
}
private ResultBody(String code, String msg) {
this.code = code;
this.message = msg;
}
public static <T> ResultBody<T> success() {
ResultBody<T> result = new ResultBody<>();
result.setCode("1");
result.setStatus(Boolean.TRUE);
result.setMessage("成功");
return result;
}
public static <T> ResultBody<T> success(T data) {
ResultBody<T> result = new ResultBody<>();
result.setCode("1");
result.setStatus(Boolean.TRUE);
result.setMessage("成功");
result.setData(data);
return result;
}
public static <T> ResultBody<T> error(String code, String message) {
ResultBody<T> result = new ResultBody<>(code, message);
result.setStatus(Boolean.FALSE);
return result;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
public class ResultBody<T> implements Serializable {



    private boolean status;
    // 响应码
    private String code;
    // 响应描述信息
    private String message;
    // 响应数据
    private T data;

    public ResultBody() {
    }



    private ResultBody(T data) {
        this.data = data;
    }



    private ResultBody(String code, String msg) {
        this.code = code;
        this.message = msg;
    }

    public static <T> ResultBody<T> success() {
        ResultBody<T> result = new ResultBody<>();
        result.setCode("1");
        result.setStatus(Boolean.TRUE);
        result.setMessage("成功");
        return result;
    }


    public static <T> ResultBody<T> success(T data) {
        ResultBody<T> result = new ResultBody<>();
        result.setCode("1");
        result.setStatus(Boolean.TRUE);
        result.setMessage("成功");
        result.setData(data);
        return result;
    }



    public static <T> ResultBody<T> error(String code, String message) {
        ResultBody<T> result = new ResultBody<>(code, message);
        result.setStatus(Boolean.FALSE);
        return result;
    }

    public boolean isStatus() {
        return status;
    }


    public void setStatus(boolean status) {
        this.status = status;
    }


    public String getCode() {
        return code;
    }


    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
public class ResultBody<T> implements Serializable { private boolean status; // 响应码 private String code; // 响应描述信息 private String message; // 响应数据 private T data; public ResultBody() { } private ResultBody(T data) { this.data = data; } private ResultBody(String code, String msg) { this.code = code; this.message = msg; } public static <T> ResultBody<T> success() { ResultBody<T> result = new ResultBody<>(); result.setCode("1"); result.setStatus(Boolean.TRUE); result.setMessage("成功"); return result; } public static <T> ResultBody<T> success(T data) { ResultBody<T> result = new ResultBody<>(); result.setCode("1"); result.setStatus(Boolean.TRUE); result.setMessage("成功"); result.setData(data); return result; } public static <T> ResultBody<T> error(String code, String message) { ResultBody<T> result = new ResultBody<>(code, message); result.setStatus(Boolean.FALSE); return result; } public boolean isStatus() { return status; } public void setStatus(boolean status) { this.status = status; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
  • @FeignClient定义接口
/**
* 定义http请求接口
*/
@FeignClient(value = "user-provider/user")
public interface OpenFeignUserService {
@PostMapping("/save")
Boolean saveUser(@RequestBody User user);
@DeleteMapping("/del/{id}")
public Boolean deleteUser(@PathVariable("id") Long id);
@GetMapping("/list")
List<User> getUserList();
@GetMapping("/get")
User getUserById(@RequestParam(value = "id", required = true) Long id);
}
/**
 * 定义http请求接口
 */
@FeignClient(value = "user-provider/user")
public interface OpenFeignUserService {




    @PostMapping("/save")


    Boolean saveUser(@RequestBody User user);


    @DeleteMapping("/del/{id}")
    public Boolean deleteUser(@PathVariable("id") Long id);







    @GetMapping("/list")
    List<User> getUserList();



    @GetMapping("/get")
    User getUserById(@RequestParam(value = "id", required = true) Long id);
}
/** * 定义http请求接口 */ @FeignClient(value = "user-provider/user") public interface OpenFeignUserService { @PostMapping("/save") Boolean saveUser(@RequestBody User user); @DeleteMapping("/del/{id}") public Boolean deleteUser(@PathVariable("id") Long id); @GetMapping("/list") List<User> getUserList(); @GetMapping("/get") User getUserById(@RequestParam(value = "id", required = true) Long id); }

注意: 如果这里你要使用@RequestMapping注解的时候,你必须说明请求方式,例如:@RequestMapping(value = "/save", method = RequestMethod.POST)

  • controller定义
@RestController
@RequestMapping("/open-feign")
public class OpenFeignController {
@Resource
private OpenFeignUserService userService;
@PostMapping("/save")
public ResultBody saveUser(@RequestBody User user) {
Boolean result = userService.saveUser(user);
return ResultBody.success(result);
}
@DeleteMapping("/del/{id}")
public ResultBody deleteUser(@PathVariable("id") Long id) {
Boolean result = userService.deleteUser(id);
return ResultBody.success(result);
}
@GetMapping("/list")
public ResultBody getUserList() {
List<User> userList = userService.getUserList();
return ResultBody.success(userList);
}
@GetMapping("/get")
public ResultBody getUserById(@RequestParam(value = "id", required = true) Long id) {
User user = userService.getUserById(id);
return ResultBody.success(user);
}
}
@RestController


@RequestMapping("/open-feign")

public class OpenFeignController {
    @Resource
    private OpenFeignUserService userService;




    @PostMapping("/save")


    public ResultBody saveUser(@RequestBody User user) {
        Boolean result = userService.saveUser(user);
        return ResultBody.success(result);
    }










    @DeleteMapping("/del/{id}")

    public ResultBody deleteUser(@PathVariable("id") Long id) {
        Boolean result = userService.deleteUser(id);
        return ResultBody.success(result);
    }




    @GetMapping("/list")

    public ResultBody getUserList() {
        List<User> userList = userService.getUserList();
        return ResultBody.success(userList);
    }



    @GetMapping("/get")
    public ResultBody getUserById(@RequestParam(value = "id", required = true) Long id) {
        User user = userService.getUserById(id);
        return ResultBody.success(user);
    }

}
@RestController @RequestMapping("/open-feign") public class OpenFeignController { @Resource private OpenFeignUserService userService; @PostMapping("/save") public ResultBody saveUser(@RequestBody User user) { Boolean result = userService.saveUser(user); return ResultBody.success(result); } @DeleteMapping("/del/{id}") public ResultBody deleteUser(@PathVariable("id") Long id) { Boolean result = userService.deleteUser(id); return ResultBody.success(result); } @GetMapping("/list") public ResultBody getUserList() { List<User> userList = userService.getUserList(); return ResultBody.success(userList); } @GetMapping("/get") public ResultBody getUserById(@RequestParam(value = "id", required = true) Long id) { User user = userService.getUserById(id); return ResultBody.success(user); } }
  • 启动类定义, @EnableFeignClients启用 OpenFeign
@SpringBootApplication
@EnableDiscoveryClient
// 开启 Feign 客户端,指定service接口所在的包
@EnableFeignClients(basePackages = "com.example.openfeign.consumer.service")
public class OpenFeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(OpenFeignConsumerApplication.class, args);
}
}
@SpringBootApplication

@EnableDiscoveryClient

// 开启 Feign 客户端,指定service接口所在的包
@EnableFeignClients(basePackages = "com.example.openfeign.consumer.service")
public class OpenFeignConsumerApplication {




    public static void main(String[] args) {
        SpringApplication.run(OpenFeignConsumerApplication.class, args);
    }
}
@SpringBootApplication @EnableDiscoveryClient // 开启 Feign 客户端,指定service接口所在的包 @EnableFeignClients(basePackages = "com.example.openfeign.consumer.service") public class OpenFeignConsumerApplication { public static void main(String[] args) { SpringApplication.run(OpenFeignConsumerApplication.class, args); } }
  • IDEAHttpClient 测试文件TestUser.http
###
POST http://127.0.0.1:7072/open-feign/save
Content-Type: application/json
{
"idCard":"123",
"username":"Kate"
}
###
DELETE http://127.0.0.1:7072/open-feign/del/110
###
GET http://127.0.0.1:7072/open-feign/list
###
GET http://127.0.0.1:7072/open-feign/get?id=111
###
POST http://127.0.0.1:7072/open-feign/save
Content-Type: application/json





{
   "idCard":"123",
   "username":"Kate"
}


###
DELETE http://127.0.0.1:7072/open-feign/del/110







###
GET http://127.0.0.1:7072/open-feign/list



###
GET http://127.0.0.1:7072/open-feign/get?id=111
### POST http://127.0.0.1:7072/open-feign/save Content-Type: application/json { "idCard":"123", "username":"Kate" } ### DELETE http://127.0.0.1:7072/open-feign/del/110 ### GET http://127.0.0.1:7072/open-feign/list ### GET http://127.0.0.1:7072/open-feign/get?id=111

二、 超时和重试

OpenFeign默认支持RibbonRibbon的重试机制和OpenFeign的重试机制有冲突,所以源码中默认关闭了OpenFeign的重试机制,使用Ribbon重试机制。

1. 超时

  • 服务提供者接口
private AtomicLong atomicLong = new AtomicLong();
@GetMapping("/retry")
public User retryUser() {
try {
log.info("超时模拟 ...");
Thread.sleep(6000);
} catch (Exception e) {
log.info("执行异常");
}
long i = atomicLong.getAndIncrement();
log.info("retryUser 接口第 {} 次调用", i);
User user = new User("111", "大宝");
return user;
}
private AtomicLong atomicLong = new AtomicLong();



@GetMapping("/retry")
public User retryUser() {


    try {
        log.info("超时模拟 ...");
        Thread.sleep(6000);
    } catch (Exception e) {
        log.info("执行异常");
    }










    long i = atomicLong.getAndIncrement();
    log.info("retryUser 接口第 {} 次调用", i);
    User user = new User("111", "大宝");
    return user;
}
private AtomicLong atomicLong = new AtomicLong(); @GetMapping("/retry") public User retryUser() { try { log.info("超时模拟 ..."); Thread.sleep(6000); } catch (Exception e) { log.info("执行异常"); } long i = atomicLong.getAndIncrement(); log.info("retryUser 接口第 {} 次调用", i); User user = new User("111", "大宝"); return user; }
  • 客户端设置业务超时时间
# 业务超时时间
ribbon.ReadTimeout=2000
# 业务超时时间
ribbon.ReadTimeout=2000
# 业务超时时间 ribbon.ReadTimeout=2000
@FeignClient(value = "user-provider/user")
public interface OpenFeignUserService {
@GetMapping("/retry")
User retryUser();
}
@FeignClient(value = "user-provider/user")
public interface OpenFeignUserService {
    @GetMapping("/retry")
    User retryUser();
}
@FeignClient(value = "user-provider/user") public interface OpenFeignUserService { @GetMapping("/retry") User retryUser(); }
@RestController
@RequestMapping("/open-feign")
public class OpenFeignController {
@GetMapping("/retry")
public ResultBody retryUser() {
User user = userService.retryUser();
return ResultBody.success(user);
}
}
@RestController


@RequestMapping("/open-feign")

public class OpenFeignController { 
    @GetMapping("/retry")
    public ResultBody retryUser() {
        User user = userService.retryUser();
        return ResultBody.success(user);
    }
}
@RestController @RequestMapping("/open-feign") public class OpenFeignController { @GetMapping("/retry") public ResultBody retryUser() { User user = userService.retryUser(); return ResultBody.success(user); } }

image.png

1. 重试

客户端设置重试次数

# 同一台实例最大重试次数,不包括首次调用
ribbon.MaxAutoRetries=3
#
# 重试负载均衡其他实例最大此时,不包括首次调用
ribbon.MaxAutoRetriesNextServer=3
#
# 是否所有操作都重试
ribbon.okToRetryOnAllOperations=false
# 同一台实例最大重试次数,不包括首次调用
ribbon.MaxAutoRetries=3
#
# 重试负载均衡其他实例最大此时,不包括首次调用
ribbon.MaxAutoRetriesNextServer=3
#
# 是否所有操作都重试
ribbon.okToRetryOnAllOperations=false
# 同一台实例最大重试次数,不包括首次调用 ribbon.MaxAutoRetries=3 # # 重试负载均衡其他实例最大此时,不包括首次调用 ribbon.MaxAutoRetriesNextServer=3 # # 是否所有操作都重试 ribbon.okToRetryOnAllOperations=false

三、日志配置

properties文件中配置日志级别,方便本地调试,参考官方文档:docs.spring.io/spring-clou…

# none:不记录任何日志,默认值;
# basic:仅记录请求方法,url,响应状态码,执行时间;
# headers:在basic基础上,记录header信息;
# full:记录请求和响应的header,body,元数据
#
feign.client.config.default.logger-level=full
#
# logger-level只对 debug 级别日志做出响应
logging.level.com.example.openfeign.consumer=debug
# none:不记录任何日志,默认值;
# basic:仅记录请求方法,url,响应状态码,执行时间;
# headers:在basic基础上,记录header信息;
# full:记录请求和响应的header,body,元数据
#
feign.client.config.default.logger-level=full
#


# logger-level只对 debug 级别日志做出响应
logging.level.com.example.openfeign.consumer=debug
# none:不记录任何日志,默认值; # basic:仅记录请求方法,url,响应状态码,执行时间; # headers:在basic基础上,记录header信息; # full:记录请求和响应的header,body,元数据 # feign.client.config.default.logger-level=full # # logger-level只对 debug 级别日志做出响应 logging.level.com.example.openfeign.consumer=debug

如果不想通过feign.client.config.default.logger-level的方式配置,也可通过Java代码的方式来配置

@Configuration
public class FeiginConfig {
@Bean
Logger.Level logLevel(){
return Logger.Level.FULL;
}
}
@Configuration
public class FeiginConfig {
  @Bean
  Logger.Level logLevel(){  
    return Logger.Level.FULL;
  }
}
@Configuration public class FeiginConfig { @Bean Logger.Level logLevel(){ return Logger.Level.FULL; } }

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYdUeBGr' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片