OpenFeign学习

问题引入

JAVA项目中如何实现接口调用?

HttpClient:

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的的支持Http协议的客户端编程工具包,并且它支持 HTTP协议最新版本和建议。HttpClient相比传统JDK自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。

Okhttp:

一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square公司贡献,用于替代HttpUrlConnection和Apache HttpClient。OkHttp拥有简洁的API、高效的性能,并支持多种协议(HTTP/2和SPDY)。

HttpURLConnection:

HttpURLConnection是Java的标准类,它继承自URLConnection,可用于向指定网站发送:GET 请求、POST 请求。HttpURLConnection 使用比较复杂,不像HttpClient那样容易使用。

RestTemplate WebClient:

RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程HTTP 服务的方法,能够大大提高客户端的编写效率。

背景介绍

OpenFeign是Spring Cloud的一个声明性HTTP客户端(出自于Netflix的Feign,Feign是Netflix开发的声明式模板化的HTTP客户端),它简化了我们与其他服务交互的方式。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注册中心示例

首先搭建注册中心,本文以单机版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-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo-client</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.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- 引入hystrix依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
                <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;
    }
}

或者通过配置文件修改日志级别:

feign:
  client:
    config:
      # 对应微服务
      demo-server:
        loggerLevel: 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.youkuaiyun.com/qq_43437874/category_11612066.html

Nacos注册中心示例

注册中心搭建参考:https://blog.youkuaiyun.com/liwenyang1992/article/details/126475927

示例通过父工程来控制依赖版本,父工程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>
    <groupId>com.lwy.it</groupId>
    <artifactId>springcloudalibaba</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <modules>
        <module>order-service</module>
        <module>stock-service</module>
    </modules>
    <!-- springboot的版本管理器,以后公司中开发就可以使用parent去继承公司自定义的父maven-->

    <name>springcloudalibaba</name>
    <description>springcloudalibaba</description>
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.8.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
    </dependencies>

    <!-- 在 dependencyManagement 中添加如下配置,用于版本管理使用,格式如下 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring Cloud Alibaba 版本管理 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Boot 版本管理 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Spring Cloud 版本管理 -->
            <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>
服务提供者

库存服务stock-service,引入依赖如下:

<?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>springcloudalibaba</artifactId>
        <groupId>com.lwy.it</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>stock-service</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

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

        <!-- Nacos服务注册发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

application.yml配置如下:

server:
  port: 8090
# 应用名称 Nacos会将该名称当作服务名称
spring:
  application:
    name: stock-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
        namespace: springcloudalibaba

提供库存服务示例:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@RestController
public class StockController {
    private final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

    @GetMapping("/stock")
    public String getStock() {
        return "Stock Service Time : " + dateTimeFormat.format(LocalDateTime.now());
    }
}

访问:http://localhost:8848/nacos注册中心,可以看到服务已经注册。

访问:http://localhost:8090/stock服务,可以看到返回结果。

服务调用者

引入OpenFeign依赖:

<?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>springcloudalibaba</artifactId>
        <groupId>com.lwy.it</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>order-service</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

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

        <!-- Nacos服务注册发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- 引入OpenFeign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

</project>

application.yml配置

server:
  port: 8082
# 应用名称 Nacos会将该名称当作服务名称
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
        namespace: public

ribbon:
  eager-load:
    # 开启饥饿加载模式
    enabled: true
    # 配置stock-service使用饥饿加载,多个使用逗号分隔
    clients: stock-service

OpenFeign客户端:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * name 指定调用rest接口所对应的服务名
 * path 指定调用rest接口所在context-path路径 + @RequestMapping路径
 */
@Component
@FeignClient(name = "stock-service")
public interface StockOpenFeignService {
    // 声明需要调用的rest接口对应的方法
    @GetMapping("/stock")
    String getStock();
}

示例Controller:

import com.lwy.it.feign.StockOpenFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

    @Autowired
    private StockOpenFeignService stockOpenFeignService;

    @GetMapping("/test")
    public String getOrders() {
        return "Order Service ---> " + stockOpenFeignService.getStock();
    }
}

启动类:

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

@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
契约配置

Spring Cloud在Feign的基础上做了扩展,使用SpringMVC的注解来完成Feign的功能。原生的Feign是不支持 Spring MVC注解的,如果你想在Spring Cloud中使用原生的注解(@RequestLine等注解)方式来定义客户端也是可以的,通过配置契约来改变这个配置,Spring Cloud中默认的是SpringMvcContract。

Spring Cloud 1的早期版本就是用的原生Fegin。随着netflix的停更才替换成了Openfeign。

使用配置类

    /**
     * 修改契约配置,支持Feign原生的注解 @RequestLine 等
     */
    @Bean
    public Contract feignContract() {
        return new Contract.Default();
    }

使用配置文件:

feign:
  client:
    config:
      # 对应微服务
      stock-service:
        # 还原成原生注解
        contract: feign.Contract.Default
@Component
@FeignClient(name = "stock-service")
public interface StockOpenFeignService {
    // 使用Feign原生注解
    @RequestLine("GET /stock")
    String getStock();
}

注意,配置使用原生注解后,SpringMVC注解不再生效

超时时间配置

全局配置:

    @Bean
    public Request.Options options() {
        return new Request.Options(30, TimeUnit.SECONDS, 30, TimeUnit.SECONDS, true);
    }

yml中配置:

feign:
  client:
    config:
      # 对应微服务
      stock-service:
        # 连接超时时间,默认2秒
        connectTimeout: 5000
        # 请求超时时间,默认5s
        readTimeout: 10000

备注:Feign底层使用的是Ribbon,但超时时间以Feign配置为准

自定义拦截器实现认证逻辑
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CustomFeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        log.info("Feign拦截器");
    }
}

配置文件生效:

feign:
  client:
    config:
      # 对应微服务
      stock-service:
        # 配置拦截器
        requestInterceptors[0]:
          com.lwy.it.interceptor.CustomFeignInterceptor

全局生效:

    @Bean
    public RequestInterceptor requestInterceptor() {
        return new CustomFeignInterceptor();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值