当前位置:网站首页>OpenFeign使用示例
OpenFeign使用示例
2022-08-09 13:43:00 【帅喵】
背景介绍
OpenFeign是Spring Cloud的一个声明性HTTP客户端(出自于Netflix的Feign),它简化了我们与其他服务交互的方式。Spring Cloud对OpenFeign进行了增强,使得Spring Cloud OpenFeign支持Spring MVC注解。同时,Spring Cloud整合了Ribbon和 Eureka注册中心(Nacos也可以),这让 Spring Cloud OpenFeign的使用更加方便。
Spring Cloud OpenFeign是一个声明式的 HTTP客户端,它简化了HTTP客户端的开发,使编写Web服务的客户端变得更容易。
Spring Cloud OpenFeign能够帮助我们定义和实现依赖服务接口。在Spring Cloud OpenFeign的帮助下,只需要创建一个接口并用注解方式配置它,就可以完成服务提供方的接口绑定,减少在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
注册中心
首先搭建注册中心,本文以单机版Eureka作为注册中心
依赖文件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.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lwy.it</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot Service</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
application.yml配置如下:
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
# 是否将自己作为一个服务注册(这里是集群部署互相注册,所以需要作为一个服务注册), 默认为true
register-with-eureka: false
# 是否从其他eureka服务拉取已注册的服务信息, 默认为true
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
启动服务后访问:http://localhost:8761/
服务提供者
引入依赖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.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lwy.it</groupId>
<artifactId>demo-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-service</name>
<description>Demo project for Spring Boot Service</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.3</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
服务端提供一个BookVO,作为服务出入参:
package com.lwy.it.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class BookVO implements Serializable {
private int bookId;
private String bookName;
private double bookPrice;
private String bookDescription;
}
同时提供一个BookController,包含表单,RequestBody,PathVariable等参数方法:
package com.lwy.it.controller;
import com.lwy.it.vo.BookVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@RestController
@RequestMapping("/book")
@Slf4j
public class BookController {
/** * 模拟数据库返回结果 */
private List<BookVO> database() {
BookVO bookVO = new BookVO();
bookVO.setBookId(1);
bookVO.setBookName("MySQL实战教程");
bookVO.setBookPrice(99.8);
bookVO.setBookDescription("数据库,是一个程序员的必备技能。而MySQL作¬为时下最流行的关系型数据库管理系统,甚至在可以预见的未来MySQL都将是最流行的关系型数据库管理系统。");
BookVO bookVO1 = new BookVO();
bookVO1.setBookId(2);
bookVO1.setBookName("愿你的青春不负梦想");
bookVO1.setBookPrice(56.9);
bookVO1.setBookDescription("梦想导师俞敏洪暌违两年,写给千万年轻人的诚意励志新作!");
BookVO bookVO2 = new BookVO();
bookVO2.setBookId(3);
bookVO2.setBookName("大话设计模式");
bookVO2.setBookPrice(66.6);
bookVO2.setBookDescription("设计模式的趣味解读,面向对象的深入剖析。在诙谐与温馨中做一次面向对象编程思维的体操。");
return Arrays.asList(bookVO, bookVO1, bookVO2);
}
/** * 无参数GET请求 */
@GetMapping("/list")
public List<BookVO> getAllBooks() {
return database();
}
/** * Path路径参数GET请求 */
@GetMapping("/{bookId}")
public BookVO getBookById(@PathVariable Integer bookId) {
List<BookVO> bookVOS = database();
return bookVOS.parallelStream().filter((BookVO bookVO) -> {
if (bookVO.getBookId() == bookId.intValue()) {
return true;
}
return false;
}).findAny().get();
}
/** * 表单参数提交 */
@GetMapping("/login")
public String login(String username, Integer password) {
log.info("参数为,userName:{},password:{}", username, password);
if (Objects.equals(username, "admin") && Objects.equals(password, 123456)) {
return "SUCCESS";
}
return "FAILURE";
}
/** * JSON格式请求体参数 */
@PutMapping("/saveBook")
public BookVO saveBook(@RequestBody BookVO bookVO) {
log.info("存储Book为:{}", bookVO);
return bookVO;
}
/** * RequestParam格式请求参数 */
@GetMapping("/param")
public BookVO getBook(BookVO bookVO) {
log.info("获取到的Book为:{}", bookVO);
return bookVO;
}
}
注册到Eureka注册中心配置,application.properties:
spring.application.name=demo-server
server.port=8088
server.servlet.context-path=/server
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
服务调用者
引入依赖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.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lwy.it</groupId>
<artifactId>demo-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-service</name>
<description>Demo project for Spring Boot Service</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.3</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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
客户端配置,注意此处开启了GZIP。GZIP是一种数据格式,采用deflate算法压缩数据,是一种流行的文件压缩算法,应用十分广泛,当压缩—个纯文本文件时,效果是非常明显的,大约可以减少70%以上的文件大小。
spring.application.name=demo-client
server.port=8086
server.servlet.context-path=/client
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# Spring Boot 默认日志级别是info,feign的debug日志级别就不会输入
logging.level.com.lwy.it.feign=debug
# 开启请求GZIP
feign.compression.request.enabled=true
# 开启响应GZIP
feign.compression.response.enabled=true
# 设置支持GZIP压缩的MIME类型,即请求/响应类型
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置启动压缩数据量的最小阈值,单位字节。默认为2014
feign.compression.request.min-request-size=1024
使用GZIP的优点在于网络数据经过压缩后实际上降低了网络传输的子节数,可以加快网页加载的速度。网页加载可以节省流量,改善用户的浏览体验。
OpenFeign日志级别配置,OpenFeign可以开启请求响应详细日志打印,方便我们调试程序,在构建客户端、方法执行器的时候,都可以看到设置了日志类及日志级别。注意配置文件中配置日志级别为debug才可以看到。
package com.lwy.it.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
// @Configuration
// 注意:此处使用@Configuration注解就会全局生效,如果想指定对应微服务生效,就不能配置
public class FeignConfiguration {
/** * 日志级别: * NONE:默认值,性能最佳,适用于生产环境,不记录任何日志 * BASIC: 适用于生产环境问题追踪,仅记录请求方法、URL、响应状态代码以及执行时间 * HEADERS:在BASIC基础上增加请求和响应header * FULL:比较适合开发及测试环境定位,记录请求和响应的header、body和元数据 */
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
使用Spring Cloud OpenFeign,只需要创建一个接口并注解,就能很容易地调用各服务提供的HTTP接口。
通过OpenFeign Client访问服务端代码BookFeignService类,注意与服务提供方Controller对比异同点:
package com.lwy.it.feign;
import com.lwy.it.config.FeignConfiguration;
import com.lwy.it.vo.BookVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/** * name 指定调用rest接口所对应的服务名 * path 指定调用rest接口所在Controller指定的@RequestMapping * configuration 局部配置,让调用的微服务生效,在@FeignClient注解中指定使用的配置类 */
@FeignClient(name = "demo-server", path = "/server/book", configuration = FeignConfiguration.class)
public interface BookFeignService {
// 声明需要调用rest接口对应的方法
@GetMapping("/list")
List<BookVO> getAllBooks();
@GetMapping("/{bookId}")
BookVO getBookById(@PathVariable("bookId") Integer id);
/** * 在OpenFeign中方法参数前如果没有注解,默认添加@RequestBody注解,最多只能有一个不带注解的参数 * 普通表单参数必须添加@RequestParam注解,如果变量名和参数名称对应可以不写name */
@GetMapping("/login")
String login(@RequestParam("username") String username, @RequestParam("password") Integer password);
@PutMapping("/saveBook")
BookVO saveBook(@RequestBody BookVO bookVO);
@GetMapping("/param")
BookVO getBook(@RequestParam int bookId, @RequestParam String bookName, @RequestParam String bookDescription, @RequestParam double bookPrice);
}
这里的BookVO与服务提供方代码一致。
启动类,启动类开启Feign Clients,否则报BookFeignService引入错误。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class DemoClientApplication {
public static void main(String[] args) {
SpringApplication.run(DemoClientApplication.class, args);
}
}
写一个调用验证的Controller(Spring Cloud OpenFeign能够帮助我们定义和实现依赖服务接口。在Spring Cloud OpenFeign的帮助下,只需要创建一个接口并用注解方式配置它,就可以完成服务提供方的接口绑定,减少在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。):
package com.lwy.it.controller;
import com.lwy.it.feign.BookFeignService;
import com.lwy.it.vo.BookVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ClientController {
// 直接引入BookFeignService,分别调用方法
@Autowired
private BookFeignService bookFeignService;
@GetMapping("/books")
public List<BookVO> getFirstBook() {
return bookFeignService.getAllBooks();
}
@GetMapping("/book-id")
public BookVO getBookById() {
return bookFeignService.getBookById(2);
}
@GetMapping("/login")
public String login() {
return bookFeignService.login("admin", 123456);
}
@GetMapping("/book-save")
public BookVO saveBook() {
BookVO bookVO = new BookVO();
bookVO.setBookId(100);
bookVO.setBookPrice(199.99);
bookVO.setBookName("OpenFeign教程");
bookVO.setBookDescription("Spring Cloud出品,OpenFeign默认将Ribbon作为负载均衡器,直接内置了Ribbon。在导入OpenFeign依赖后无需专门导入Ribbon依赖。");
return bookFeignService.saveBook(bookVO);
}
@GetMapping("/book-param")
public BookVO getBook() {
return bookFeignService.getBook(100, "OpenFeign教程", "Spring Cloud出品,OpenFeign默认将Ribbon作为负载均衡器,直接内置了Ribbon。在导入OpenFeign依赖后无需专门导入Ribbon依赖。", 199.99);
}
}
访问http://localhost:8086/client/books等链接进行验证。
Spring Cloud OpenFeign基于OpenFeign实现,它除了提供声明式的 HTTP客户端外,还整合了Spring Cloud Hystrix,能够轻松实现熔断器模型。
推荐学习资料:https://blog.csdn.net/qq_43437874/category_11612066.html
边栏推荐
猜你喜欢
随机推荐
From the Dutch flag problem to the optimization and upgrade of quick row
dpkg:错误:无法新建文件 ‘/var/lib/dpkg/info/format-new’: 没
*1-4 OJ 605 Gray Code
【LeetCode】1413. 逐步求和得到正数的最小值
Assembly language learning (4)
ELK deployment
*1-1 OJ 56 Hamming Distance
关于舵机的漂移与不听指挥乱动的问题
Kotlin入门,应从“这些”学起。
C语言 猜数字游戏 (含代码并详细注释)
Three kinds of ThreadLocal, play with thread variable storage and transmission
Recursive implementation of the Tower of Hanoi problem
RHCE课程总结
Assembly language learning (1)
C语言 函数问题
阿里云PAI与香港大学合作论文入选INFOCOM 2022,有效减少大规模神经网络训练时间
C语言 求一个整数存储在内存中的二进制中1的个数(多种方法详解)
*4-2 CCF 2014-12-2 Z字形扫描
add-apt-repository命令详解
元气森林“0糖”背后的百亿推手









