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

目录

OpenFeign最佳实践

问题引入

[Feign 继承方式](#Feign 继承方式)

创建Module

引入依赖

编写接口

打Jar包

服务提供方

服务消费方

启动服务并访问

[Feign 抽取方式](#Feign 抽取方式)

创建Module

引入依赖

编写接口

打Jar包

服务消费方

启动服务并访问

服务部署

修改pom.xml文件

观察Nacos控制台

远程访问


OpenFeign最佳实践

问题引入

最佳实践, 其实也就是经过历史的迭代, 在项⽬中的实践过程中, 总结出来的最好的使⽤⽅式.

通过观察, 我们也能看出来, Feign的客户端与服务提供者的controller代码⾮常相似:

Feign客户端

java 复制代码
@FeignClient(value = "product-service",path = "/product")
public interface ProductApi {
    @RequestMapping("/{productId}")
    ProductInfo getProductById(@PathVariable("productId") Integer productId);
}

服务提供方Controller

java 复制代码
@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接口中:

java 复制代码
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中:

java 复制代码
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

java 复制代码
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

java 复制代码
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接口中:

java 复制代码
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中:

java 复制代码
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"})指定扫描类。

java 复制代码
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>
  1. 上传jar到Linux服务器

  2. 启动Nacos

启动前最好把data数据删除掉.

  1. 启动服务

#后台启动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控制台
远程访问
相关推荐
铁弹神侯7 分钟前
Maven相关名词及相关配置
java·maven
会飞的皮卡丘EI20 分钟前
关于Blade框架对数字类型的null值转为-1问题
java·spring boot
雷渊23 分钟前
如何保证数据库和Es的数据一致性?
java·后端·面试
fjkxyl24 分钟前
Spring的启动流程
java·后端·spring
极客先躯26 分钟前
高级java每日一道面试题-2025年4月06日-微服务篇[Nacos篇]-如何诊断和解决Nacos中的常见问题?
java·开发语言·微服务
东锋1.334 分钟前
Spring AI 发布了它的 1.0.0 版本的第七个里程碑(M7)
java·人工智能·spring
liwulin05061 小时前
【JAVAFX】自定义FXML 文件存放的位置以及使用
java
2401_890665861 小时前
免费送源码:Java+ssm+MySQL 基于PHP在线考试系统的设计与实现 计算机毕业设计原创定制
java·hadoop·spring boot·python·mysql·spring cloud·php
逸风尊者1 小时前
开发可掌握的知识:基于事件驱动实现状态机
java
佩奇的技术笔记1 小时前
Java学习手册:Java线程安全与同步机制
java