Spring Cloud之远程调用OpenFeign最佳实践
目录
OpenFeign最佳实践
问题引入
Feign 继承方式
创建Module
引入依赖
编写接口
打Jar包
服务提供方
服务消费方
启动服务并访问
Feign 抽取方式
创建Module
引入依赖
编写接口
打Jar包
服务消费方
启动服务并访问
服务部署
修改pom.xml文件
观察Nacos控制台
远程访问
OpenFeign最佳实践
问题引入
最佳实践, 其实也就是经过历史的迭代, 在项⽬中的实践过程中, 总结出来的最好的使⽤⽅式.
通过观察, 我们也能看出来, Feign的客户端与服务提供者的controller代码⾮常相似:
Feign客户端
@FeignClient(value = "product-service",path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
服务提供方Controller
@RequestMapping("/product")
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId){
return productService.selectProductById(productId);
}
}
那么有没有⼀种⽅法可以简化这种写法呢?
Feign 继承方式
Feign ⽀持继承的⽅式, 我们可以把⼀些常⻅的操作封装到接⼝⾥.
我们可以定义好⼀个接⼝, 服务提供⽅实现这个接⼝, 服务消费⽅编写Feign 接⼝的时候, 直接继承这个接口。
创建Module
接⼝可以放在⼀个公共的Jar包⾥, 供服务提供⽅和服务消费⽅使⽤.
引入依赖
org.springframework.boot spring-boot-starter-weborg.springframework.cloud spring-cloud-starter-openfeign
编写接口
把之前ProductApi的内容移动到Module中的ProductInterface接口中:
package api;
import model.ProductInfo;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
public interface ProductInterface {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p2")
String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
}
把之前ProductInfo的内容移动到Module中:
package model;
import lombok.Data;
import java.util.Date;
@Data
public class ProductInfo {
private Integer id;
private String productName;
private Integer productPrice;
private Integer state;
private Date createTime;
private Date updateTime;
}
目录结构如下:
打Jar包
通过Maven打包
观察Maven本地仓库, Jar包是否打成功:
服务提供方
服务提供⽅实现接口 ProductInterface
package product.controller;
import api.ProductInterface;
import model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import product.service.ProductService;
@RequestMapping("/product")
@RestController
public class ProductController implements ProductInterface {
@Autowired
private ProductService productService;
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId){
return productService.selectProductById(productId);
}
@RequestMapping("/p1")
public String p1(Integer id){
return "product-service 接收到参数, id:"+id;
}
@RequestMapping("/p2")
public String p2(Integer id, String name){
return "product-service 接收到参数, id:"+id+",name:"+name;
}
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return "product-service 接收到参数: productInfo"+productInfo.toString();
}
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return "product-service 接收到参数: productInfo"+productInfo.toString();
}
}
服务消费方
服务消费⽅继承ProductInterface
package order.api;
import api.ProductInterface;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(value = "product-service",path = "/product")
public interface ProductApi extends ProductInterface {
}
启动服务并访问
Feign 抽取方式
官⽅推荐Feign的使⽤⽅式为继承的⽅式, 但是企业开发中, 更多是把Feign接⼝抽取为⼀个独⽴的模块(做法和继承相似, 但理念不同).
操作⽅法:
将Feign的Client抽取为⼀个独⽴的模块, 并把涉及到的实体类等都放在这个模块中, 打成⼀个Jar. 服务消费⽅只需要依赖该Jar包即可. 这种⽅式在企业中⽐较常⻅, Jar包通常由服务提供⽅来实现.
创建Module
引入依赖
org.springframework.cloud spring-cloud-starter-openfeign
编写接口
把之前ProductApi的内容移动到Module中的ProductInterface接口中:
package api;
import model.ProductInfo;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
public interface ProductInterface {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p2")
String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
}
把之前ProductInfo的内容移动到Module中:
package model;
import lombok.Data;
import java.util.Date;
@Data
public class ProductInfo {
private Integer id;
private String productName;
private Integer productPrice;
private Integer state;
private Date createTime;
private Date updateTime;
}
目录结构如下:
打Jar包
通过Maven打包
观察Maven本地仓库, Jar包是否打成功:
服务消费方
删除ProductInfo和ProductApi
引入依赖
com.wmh product-api1.0-SNAPSHOT compile
指定扫描类
下面我们使用@EnableFeignClients(clients = {ProductApi.class})来指定扫描类,
当然也可以使用@EnableFeignClients(basePackages = {"api"})指定扫描类。
package order;
import api.ProductApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients(clients = {ProductApi.class})
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
如果不指定扫描类的话,运行程序会失败并报错:
原因是因为order-service的启动类OrderServiceApplication只会扫描启动类所在目录,而ProductApi并不在其扫描路径内,因此需要指定扫描类。
启动服务并访问
服务部署
1. 修改数据库, Nacos等相关配置
2. 对两个服务进⾏打包
Maven打包默认是从远程仓库下载的, product-api 这个包在本地, 有以下解决⽅案:
◦ 上传到Maven中央仓库(⽐较⿇烦)[不推荐]
◦ 搭建Maven私服, 上传Jar包到私服[企业推荐]
◦ 从本地读取Jar包[个⼈学习阶段推荐]
前两种⽅法⽐较复杂, 咱们使⽤第三种⽅式。
修改pom.xml文件
如果不配置上图所示的一下内容,项目启动会失败并报错:
true
3. 上传jar到Linux服务器
4. 启动Nacos
启动前最好把data数据删除掉.
5. 启动服务
#后台启动order-service, 并设置输出⽇志到logs/order.log
nohup java -jar order-service.jar >logs/order.log &
#后台启动product-service, 并设置输出⽇志到logs/order.log
nohup java -jar product-service.jar >logs/product-9090.log &
#启动实例, 指定端⼝号为9091
nohup java -jar product-service.jar --server.port=9091 >logs/product-9091.log &