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
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.2version>
<relativePath/>
parent>
<groupId>com.lwy.itgroupId>
<artifactId>eurekaartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>eurekaname>
<description>Demo project for Spring Boot Servicedescription>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>2021.0.3spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
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
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.2version>
<relativePath/>
parent>
<groupId>com.lwy.itgroupId>
<artifactId>demo-serviceartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>demo-servicename>
<description>Demo project for Spring Boot Servicedescription>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>2021.0.3spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
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:
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.7.2version>
<relativePath/>
parent>
<groupId>com.lwy.itgroupId>
<artifactId>demo-serviceartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>demo-servicename>
<description>Demo project for Spring Boot Servicedescription>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>2021.0.3spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
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