feign的简单使用。
文章目录
- 一. 什么是feign
- 二. 准备工作
- 三. 如何使用
- 3.1 定义pom文件
- 3.2 定义配置文件及启动类注解
- 3.3 定义feign接口
- 四. 部署
一. 什么是feign
远程调用框架
二. 准备工作
需要nacos环境:
涉及到feign调用,就没法抛开注册中心,接下来我们使用主流的nacos作为注册中心进行搭建本地环境。具体参见之前的nacos系列:https://blog.csdn.net/imVainiycos/article/details/122917022
三. 如何使用
3.1 定义pom文件
- 新建一个maven项目作为父工程
父工程的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.vainycos</groupId><artifactId>demo-spring-cloud</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version></parent><modules><module>core</module><module>customer</module><module>supermarket</module></modules><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><springboot.version>2.2.5.RELEASE</springboot.version><spring.cloud.version>Hoxton.SR3</spring.cloud.version><spring.cloud.alibaba.version>2.2.1.RELEASE</spring.cloud.alibaba.version></properties><dependencyManagement><dependencies><!--支持Spring Boot 2.1.X--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${springboot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring.cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- alibaba-cloud --><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></dependencies></dependencyManagement><dependencies><!--服务调用--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId></dependency><!--注册中心--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency></dependencies>
</project>
- 新建三个子模块,模拟一个超市购物行为和结账会员身份识别场景,分别是:
- core - 核心模块,feign接口层或者其他核心类
- customer - 会员模块,依赖[core]模块,通过core模块的feign统一调用[supermarket]服务
- supermarket - 超市模块,依赖[core]模块,通过core模块的feign统一调用[customer]服务
core模块的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><packaging>jar</packaging><parent><groupId>com.vainycos</groupId><artifactId>demo-spring-cloud</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>com.vainycos</groupId><artifactId>core</artifactId><version>0.0.1-SNAPSHOT</version><name>core</name><description>core</description><properties><java.version>8</java.version></properties><dependencies><!-- spring-boot-starter-web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></project>
customer模块的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><packaging>jar</packaging><parent><groupId>com.vainycos</groupId><artifactId>demo-spring-cloud</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>com.vainycos</groupId><artifactId>customer</artifactId><version>0.0.1-SNAPSHOT</version><name>customer</name><description>customer</description><properties><java.version>8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.vainycos</groupId><artifactId>core</artifactId><version>0.0.1-SNAPSHOT</version></dependency></dependencies><build><finalName>customer</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
supermarket模块的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><packaging>jar</packaging><parent><groupId>com.vainycos</groupId><artifactId>demo-spring-cloud</artifactId><version>1.0-SNAPSHOT</version></parent><groupId>com.example</groupId><artifactId>supermarket</artifactId><version>0.0.1-SNAPSHOT</version><name>supermarket</name><description>supermarket</description><properties><java.version>8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.vainycos</groupId><artifactId>core</artifactId><version>0.0.1-SNAPSHOT</version><scope>compile</scope></dependency></dependencies><build><finalName>market</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
3.2 定义配置文件及启动类注解
假设你本地的nacos已经启动完成了,启动端口为默认的8848。
两个模块的配置均大同小异,这里以customer模块为例。
这里需要注意由于使用微服务的配置需要优先加载,所以我们不在原来的application.yaml或者application.yml上进行配置,而是新建一个bootstrap.yml文件进行配置,配置内容如下:
server:# 定义另一个模块的启动端口为8082port: 8081spring:application:# 定义另一个模块的值为marketname: customercloud:nacos:discovery:server-addr: localhost:8848
在启动类CustomerApplication上加注解@EnableDiscoveryClient以及@EnableFeignClients(basePackageClasses = {CustomerApplication.class, CoreApplication.class}),这里需要依赖core模块,所以把core的启动类也注入。
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = {CustomerApplication.class, CoreApplication.class})
@SpringBootApplication
public class CustomerApplication {public static void main(String[] args) {SpringApplication.run(CustomerApplication.class, args);}}
3.3 定义feign接口
首先我们在customer模块定义一个接口,根据id查询客户信息:
@RequestMapping("/customer")
@RestController
public class CustomerController {@Autowiredprivate MarketService marketService;@GetMapping("/getUser/{id}")public String getById(@PathVariable("id") Integer id){return "获取id=" + id + "的用户";}
}
调用本服务进行测试,访问http://localhost:8081/customer/getUser/1,结果如下表示通过:
我们到core模块下定义一个feign接口CustomerService,在类名上加注解@FeignClient(name = “customer”),该注解name的值需要注意与调用服务方配置文件重的application.name保持一致,比如这里是customer:
完整的feign接口定义如下,这里需要注意每个入参都需要定义入参格式,比如这里是路径参数即加上@PathVariable,其他的还有@RequestParameter,@RequestBody等等,并且@GetMapping的请求地址需要与对应模块的请求地址保持一致:
package com.vainycos.core.feign.customer;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(name = "customer")
public interface CustomerService {@GetMapping("/customer/getUser/{id}")String getCustomerById(@PathVariable("id") Integer id);
}
这个时候我们模拟一个场景,即在supermarket模块结账的时候需要调用customer模块获取会员信息。
在supermarket模块中定义controller,把上面的feign接口注入进来即可调用:
package com.example.supermarket.controller;import com.vainycos.core.feign.customer.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author: Vainycos* @description* @date: 2023/1/16 13:30*/
@RequestMapping("/market")
@RestController
public class MarketController {@Autowiredprivate CustomerService customerService;@GetMapping("/buy")public String order(String things, Integer id){String customerById = customerService.getCustomerById(id);return customerById + "购买了" +things;}
}
我们启动这两个服务之后,观察nacos的服务列表发现已经成功注册上去了:
我们访问http://localhost:8082/market/buy?things=苹果电脑&id=1,结果如下表示成功进行了跨服务间的调用:
到这里,完整的feign调用链路就已经搭建完成了。
同理,我们也可以在supermarket定义获取商品列表的服务,然后提供给customer调用:
package com.example.supermarket.controller;import com.vainycos.core.feign.customer.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author: Vainycos* @description* @date: 2023/1/16 13:30*/
@RequestMapping("/market")
@RestController
public class MarketController {@Autowiredprivate CustomerService customerService;/*** 获取商品列表* @return*/@GetMapping("/listMarket")public String listMarket(){return "啤酒,瓜子,花生米";}@GetMapping("/buy")public String order(String things, Integer id){// 调用feign接口String customerById = customerService.getCustomerById(id);return customerById + "购买了" +things;}}
然后在core模块下定义feign接口
package com.vainycos.core.feign.supermarket;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;/*** @author: Vainycos* @description* @date: 2023/1/16 16:18*/
@FeignClient(name = "market")
public interface MarketService {@GetMapping("/market/listMarket")String listMarket();
}
最后在customer模块中注入feign接口MarketService进行使用:
package com.vainycos.customer.controller;import com.vainycos.core.feign.supermarket.MarketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author: Vainycos* @description* @date: 2023/1/16 13:27*/
@RequestMapping("/customer")
@RestController
public class CustomerController {@Autowiredprivate MarketService marketService;@GetMapping("/listMarket/{id}")public String listMarket(@PathVariable("id") Integer id){// 调用feign接口String goods = marketService.listMarket();return "用户id=" + id + "查询商品列表:" + goods;}@GetMapping("/getUser/{id}")public String getById(@PathVariable("id") Integer id){return "获取id=" + id + "的用户";}}
访问http://localhost:8081/customer/listMarket/1,结果如下:
四. 部署
我们在项目工程的根目录下执行如下命令,关于打包的build节点配置可以参考该文:maven的build节点配置:
mvn clean install -Dmaven.test.skip=true
在supermarket\target以及customer\target目录下即可看到对应的jar包,我们可以部署多份jar包作为微服务的扩展,只需要启动的时候指定不同的端口即可
java -jar customer.jar --server.port=8081
java -jar customer.jar --server.port=8091
java -jar customer.jar --server.port=8101
同理,market也分别设置指定三个启动端口为8082,8092,8102
java -jar market.jar --server.port=8082
java -jar market.jar --server.port=8092
java -jar market.jar --server.port=8102
我们观察nacos上的服务列表,就可以发现对应的market和customer服务分别都有了3个实例。
这里我们就实现了服务的简单扩展,只要启动的三个实例有一个还活着就不会导致整个服务挂掉,但是还没有实现如何将请求均匀的分摊到各个实例上,以及其中一个服务的所有实例都挂掉之后该怎么处理,这个课题我们留到之后再来探讨。
以上代码示例可参考该仓库:https://gitee.com/dearvainycos/demo-spring-cloud.git
参考资料:
- Feign的使用
- 什么是Feign?
- 【微服务】分布式组件 Nacos 结合 Feign 的使用