feign漫谈

feign的简单使用。

一. 什么是feign

远程调用框架

二. 准备工作

需要nacos环境:

涉及到feign调用,就没法抛开注册中心,接下来我们使用主流的nacos作为注册中心进行搭建本地环境。具体参见之前的nacos系列:https://blog.csdn.net/imVainiycos/article/details/122917022

三. 如何使用

3.1 定义pom文件

  1. 新建一个maven项目作为父工程

image-20230116160204225

父工程的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>
  1. 新建三个子模块,模拟一个超市购物行为和结账会员身份识别场景,分别是:
  • 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:
  # 定义另一个模块的启动端口为8082
  port: 8081

spring:
  application:
  	# 定义另一个模块的值为market
    name: customer
  cloud:
    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 {

    @Autowired
    private MarketService marketService;

    @GetMapping("/getUser/{id}")
    public String getById(@PathVariable("id") Integer id){
        return "获取id=" + id + "的用户";
    }
}

调用本服务进行测试,访问http://localhost:8081/customer/getUser/1,结果如下表示通过:

image-20230116155257137

我们到core模块下定义一个feign接口CustomerService,在类名上加注解@FeignClient(name = “customer”),该注解name的值需要注意与调用服务方配置文件重的application.name保持一致,比如这里是customer:

image-20230116171123037

完整的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 {

    @Autowired
    private CustomerService customerService;

    @GetMapping("/buy")
    public String order(String things, Integer id){
        String customerById = customerService.getCustomerById(id);
        return customerById + "购买了" +things;
    }
}

我们启动这两个服务之后,观察nacos的服务列表发现已经成功注册上去了:

image-20230117100055168

我们访问http://localhost:8082/market/buy?things=苹果电脑&id=1,结果如下表示成功进行了跨服务间的调用:

image-20230116171607683

到这里,完整的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 {

    @Autowired
    private 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 {

    @Autowired
    private 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,结果如下:

image-20230116172124042

四. 部署

我们在项目工程的根目录下执行如下命令,关于打包的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个实例。

image-20230117105425049

这里我们就实现了服务的简单扩展,只要启动的三个实例有一个还活着就不会导致整个服务挂掉,但是还没有实现如何将请求均匀的分摊到各个实例上,以及其中一个服务的所有实例都挂掉之后该怎么处理,这个课题我们留到之后再来探讨。

以上代码示例可参考该仓库:https://gitee.com/dearvainycos/demo-spring-cloud.git

参考资料:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值