Spring Cloud之远程调用OpenFeign最佳实践

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包⾥, 供服务提供⽅和服务消费⽅使⽤.

引入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
编写接口

把之前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
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
编写接口

把之前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

引入依赖

<dependency> <groupId>com.wmh</groupId> <artifactId>product-api</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency>

指定扫描类

下面我们使用@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文件

如果不配置上图所示的一下内容,项目启动会失败并报错:

<configuration> <includeSystemScope>true</includeSystemScope> </configuration>

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 &
观察Nacos控制台
远程访问

Read more

PostgreSQL:简介与安装部署

PostgreSQL:简介与安装部署

🧑 博主简介:ZEEKLOG博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。 技术合作请加本人wx(注明来自ZEEKLOG):foreast_sea

By Ne0inhk
FastChat 架构拆解:打造类 ChatGPT 私有化部署解决方案的基石

FastChat 架构拆解:打造类 ChatGPT 私有化部署解决方案的基石

🐇明明跟你说过:个人主页 🏅个人专栏:《深度探秘:AI界的007》 🏅 🔖行路有良友,便是天堂🔖 目录 一、FastChat 介绍 1、大语言模型本地部署的需求 2、FastChat 是什么 3、FastChat 项目简介 二、FastChat 系统架构详解 1、controller 2、model_worker 3、openai_api_server 4、web UI 前端 一、FastChat 介绍 1、大语言模型本地部署的需求 为什么明明有 ChatGPT、Claude 这些在线服务可用,大家还要花大力气去做 大语言模型本地部署 呢?🤔 其实就像吃饭一样,有人喜欢外卖(云服务)

By Ne0inhk

Windows/Linux双平台保姆教程:用DDNS-GO v6.7.6实现免费内网穿透(替代花生壳)

从零构建你的专属动态域名服务:告别付费内网穿透,拥抱开源DDNS-GO 最近和几个独立开发者朋友聊天,大家普遍吐槽的一个点就是内网穿透服务。无论是为了远程调试家里的NAS,还是想临时给客户演示一个部署在本地开发机的Web应用,传统的方案要么像花生壳这类工具需要付费且流量受限,要么配置复杂得让人望而却步。更别提一些云服务商提供的穿透服务,按流量计费的模式对于高频测试来说,成本完全不可控。其实,如果你手头有一个公网IP(哪怕是动态变化的),或者你的IPv6环境是通畅的,完全没必要依赖第三方付费服务。今天,我们就来深入聊聊如何利用一个名为 DDNS-GO 的开源神器,亲手搭建一套稳定、免费且完全自控的动态域名解析系统,彻底摆脱对商业内网穿透工具的依赖。 DDNS-GO 的核心价值在于它的“桥梁”作用。它持续监测你本地网络的公网IP地址(包括IPv4和IPv6),一旦发现IP发生变化,就立刻调用云解析服务商(如阿里云、腾讯云DNSPod、Cloudflare等)的API,自动将你指定的域名更新解析到新的IP上。这样一来,无论你的网络环境如何变动,通过一个固定的域名,你总能从外网访问到家里的

By Ne0inhk