SpringCloud系列(五) 使用Feign进行远程调用

  • Post author:
  • Post category:其他




公众号

关注公众号和我一起学习哦!

公众号




一、什么是Feign


Feign

是一个声明式的模板化的

HTTP

客户端,它使得写 Http 客户端变得更简单。使用 Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用 Feign 注解和 JAX-RS 注解。Feign 支持可插拔的编码器和解码器。Feign 默认集成了 Ribbon,并和 Eureka 结合,默认实现了

负载均衡

的效果

  • Feign 采用的是基于接口的注解

  • Feign 整合了

    ribbon

  • Feign 整合了

    Hystrix



二、创建公共模块


为了减少代码的冗余 提升代码精简程度


1. 创建一个新的模块 选择 Mavne项目


spring-cloud-netflix-common


2. 添加Lombok的依赖

什么是Lombok ? 传送门:

地址

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>


3. 创建 User 实体类

package cn.yufire.pojo;
import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
    private static final long serialVersionUID = -3064610191893595688L;
    
    private String userName;
    
    private String passWord;
    
    private Integer age;

}



4. 创建 UserService接口

package cn.yufire.service;
import cn.yufire.pojo.User;
import java.util.List;

public interface UserService {
    /**
     * 创建查询用户的接口
     *
     * @return
     */
    List<User> queryAll();
}


5. 打包 并 安装到本地

mvn install



二、创建服务提供者


1. 创建一个新的模块 选择SpringBoot项目 名称为:


spring-cloud-netflix-user-provider


本次以用户服务提供者为例子 不涉及操作数据库


操作数据库的步骤 之前怎么写现在就这么写



2. 添加用户服务提供者所需的依赖 pom.xml

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.yufire</groupId>
    <artifactId>spring-cloud-netflix-user-provider</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>spring-cloud-netflix-user-provider</name>
    <description>用户服务提供者</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--添加SpringBoot的Web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--添加 Eureka的客户端依赖 注意后边是 client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--依赖公共模块-->
        <dependency>
            <groupId>cn.yufire</groupId>
            <artifactId>spring-cloud-netflix-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <!--Spring Cloud 父级依赖-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


与创建 Eureka客户端的依赖一致 需要其他功能自行添加依赖



3. 修改配置文件

spring:
  application:
    name: user-provider # 配置服务名称
eureka:
  instance:
    lease-renewal-interval-in-seconds: 1=
    lease-expiration-duration-in-seconds: 2 
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 配置当前服务在 注册中心中显示的 ip + 端口号
  client:
    service-url: # 可以在路径中配置用户名密码
      defaultZone: http://root:root@127.0.0.1:9988/eureka
server:
  port: 9000 # 客户端运行的端口


4. 开启Eureka客户端

package cn.yufire.spring.cloud.netflix.user.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication
public class SpringCloudNetflixUserProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudNetflixUserProviderApplication.class, args);
    }

}



5. 编写用户Service的实现类


由于不需要连接数据库 所以不需要编写 Mapper接口

package cn.yufire.spring.cloud.netflix.user.provider.serviceimpl;

import cn.yufire.pojo.User;
import cn.yufire.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class UserServiceImpl implements UserService {

    @RequestMapping("/queryAll")
    @Override
    public List<User> queryAll() {
        List<User> users = new ArrayList<>();
        // 添加假数据
        for (int i = 1; i < 4; i++) {
            User user = new User();
            user.setUserName("张" + i);
            user.setPassWord(String.valueOf((Math.random() * 1000)));
            user.setAge(i);
            users.add(user);
        }
        return users;
    }
}


由于Feign是使用HTTP进行远程调用的 所以需要把用户Service实现类 使用 @RestController进行标注 才可以被外界访问到



6. 查看Eureka注册中心是否有我们的 用户服务提供者

img



7. 由于Feign是使用Rest方式进行调用所以可以直接测试访问该接口


地址

img


成功 !



三、创建服务消费者


1. 创建一个新的模块 选择SpringBoot项目 名称为:


spring-cloud-netflix-user-consumer


2. 添加所需要的依赖 pom.xml

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.yufire</groupId>
    <artifactId>spring-cloud-netflix-user-consumer</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>spring-cloud-netflix-user-consumer</name>
    <description>用户服务消费者</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--添加SpringBoot的Web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--添加 Eureka的客户端依赖 注意后边是 client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--添加 openFeign的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <!--Spring Cloud 父级依赖-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


主要添加了 OpenFeign的依赖

<!--添加 openFeign的依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>


3. 修改配置文件

spring:
  application:
    name: user-consumer
eureka:
  instance:
    lease-renewal-interval-in-seconds: 1 # 每间隔1s,向服务端发送一次心跳,证明自己依然存活
    lease-expiration-duration-in-seconds: 2 # 告诉服务端,如果我2s之内没有给你发心跳,就代表我挂掉了,将我踢出掉。
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 配置当前服务在 注册中心中显示的 ip + 端口号
  client:
    service-url: # 可以在路径中配置用户名密码
      defaultZone: http://root:root@127.0.0.1:9988/eureka
server:
  port: 10000 # 运行在 10000端口


4. 配置启动类

package cn.yufire.spring.cloud.netflix.user.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;

@EnableEurekaClient // 开启 Eureka客户端
@EnableFeignClients // 开启 Feign客户端
@SpringBootApplication 
public class SpringCloudNetflixUserConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudNetflixUserConsumerApplication.class, args);
    }

}



5. 在Consumer中创建 UserService


我们需要使用 这个UserService来调用 provider中的UserService实现类

package cn.yufire.spring.cloud.netflix.user.consumer.service;

import cn.yufire.pojo.User;
import cn.yufire.spring.cloud.netflix.user.consumer.fullback.UserServiceFullBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;
// 使用 FeignClient注解来标注这个类是Fegin客户端 name属性为 provider项目的 application.name 一定要一致 否则将调用错误
@FeignClient(name = "user-provider") 
public interface UserService {
    @RequestMapping("/queryAll")
    List<User> queryAll();

}



6. 创建 Controller



正常调用即可


注意 UserService的导包 是Consumer中的userService 不是 公共类的UserService

package cn.yufire.spring.cloud.netflix.user.consumer.controller;

import cn.yufire.pojo.User;
import cn.yufire.spring.cloud.netflix.user.consumer.service.UserService; // 注意包
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/queryAll")
    public List<User> queryAll() {
        return userService.queryAll();
    }

}



7. 启动项目


8. 测试访问


访问地址

img


出现返回的数据即为成功 !




四、传递参数


在我们访问接口的时候肯定是需要传递参数的


传递参数我分为了以下两种情况 :

  • 基本Java数据类型
  • 引用数据类型



1. 传递Java数据类型


1. 在公共模块的 Service 中添加一个新的接口

   /**
     * 传递Java数据类型参数
     *
     * @param content
     * @return
     */
String sayHi(String content, Integer age);


2. 在provider中实现该接口


需要在形参前 添加

@RequertParam


注解进行标注 才可以正常的传递参数

@Override
@RequestMapping("/sayHi")
public String sayHi(@RequestParam("content") String content, @RequestParam("age") Integer age) {
    return "Hello Spring Cloud Netflix!" + content + "\t" + age;
}


3. 在 consumer 中的 UserService中添加该接口


需要在形参前 添加

@RequertParam


注解进行标注 才可以正常的传递参数


如果只有一个参数 可以忽略

@RequertParam

注解

@RequestMapping("/sayHi")
String sayHi(@String content, Integer age);


4. 在UserController中注册请求接口


正常接收参数即可

@GetMapping("/sayHi")
public String sayHi(String content, Integer age) {
    return userService.sayHi(content, age);
}


5. 重启项目


6. 测试访问


访问地址

img


出现以上内容即为成功 !




2. 传递引用数据类型


我们在传递数据的时候 可能会遇到要同时传递多个参数的情况


针对这种情况 我们可以使用 Map 或者 对象来进行数据的传递



1. 传递一个引用类型

在公共模块的UserService中添加两个接口

 /**
     * 传递Map类型的参数
     *
     * @param paramMap
     * @return
     */
String paramMap(Map<String, Object> paramMap);
 /**
     * 传递对象参数
     * @param user
     * @return
     */
User getUserInfo(User user);


2. 在provider中实现该接口


由于传递的参数是引用类型 我们将接收参数的注解改为

@RequestBody


使用JSON格式传递

/**
     * 使用 Map 作为参数传递
     * 获取 用户名 密码
     * @param paramMap
     * @return
     */
@Override
@RequestMapping("/paramMap")
public String paramMap(@RequestBody Map<String, Object> paramMap) {
    System.out.println(paramMap.get("username"));
    System.out.println(paramMap.get("password"));
    return (paramMap.get("username").toString() + paramMap.get("password").toString());
}

/**
     * 使用 User 作为参数传递
     * 获取用户信息
     *
     * @param user
     * @return
     */
@Override
@RequestMapping("/getUserInfo")
public User getUserInfo(@RequestBody User user) {
    System.out.println(user);
    return user;
}


3. 在 Consumer中的UserService中添加这两个接口

 /**
     * 传递Map类型的参数
     *
     * @param paramMap
     * @return
     */
String paramMap(Map<String, Object> paramMap);
 /**
     * 传递对象参数
     * @param user
     * @return
     */
User getUserInfo(User user);


4. 在Controller中创建请求接口

@GetMapping("/paramMap")
public String paramMap(String username, String pwd) {
    Map paramMap = new HashMap<>();
    paramMap.put("username", username);
    paramMap.put("password", pwd);
    System.out.println(paramMap);
    return userService.paramMap(paramMap);
}

@RequestMapping("/getUserInfo")
public User getUserInfo(User user) {
    return userService.getUserInfo(user);
}


5. 测试访问

paramMap

接口


地址


测试结果 返回的是 用户名和密码

img



6. 测试访问

getUserInfo

接口


地址


测试结果

img




小结


1. 传递 Java数据类型的时候 只需要添加

@RequestParam

注解即可


2. 传递引用类型的时候 只需要添加

@RequestBodu

注解即可正常传递