SpringCloudAlibaba技术整理(二)服务间调用feign

Nacos服务端已经启动, 服务提供者UserInfoProvider1已经注册到nacos, 接下来进行服务间调用的实践.

主流服务间调用主要有两种: openfeign和dubbo

openfeign:

  • 声明式rest调用
  • 基于http协议, 处于应用层, 传输消耗资源高, 并发请求较高时存在性能瓶颈
  • 消息可读可调试
  • 松耦合, 方便对接其他服务/系统

dubbo:

  • rpc调用
  • 基于tcp协议传输字节, 处于传输层, 性能较高
  • 消息不可读
  • 耦合度较高, 用于系统内通信

服务间调用-openfeign

一. 创建服务消费者模块UserInfoConsumer1

image.png

  • UserInfoConsumer1模块pom文件引入依赖

<packaging>jar</packaging>

    <dependencies>
        <!-- mvc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring boot actuator 端点监控, spring cloud alibaba 中必须引入, 否则启动报错 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- 服务发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--引入feign进行服务调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
  • UserInfoConsumer1模块启动类

package cn.zack;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @EnableDiscoveryClient 开启服务注册发现
 * @EnableFeignClients 开启feign声明式调用
 * <p>
 * userInfo服务消费者1
 * 通过feign进行服务调用
 */
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class UserInfoConsumer1Application {
    public static void main(String[] args) {
        SpringApplication.run(UserInfoConsumer1Application.class, args);
    }
}
  • 编辑application.yml配置文件

server:
  port: 8091

spring:
  application:
    # 服务消费者应用名
    name: userInfoConsumer
  cloud:
    nacos:
      discovery:
        # nacos注册中心地址
        server-addr: 127.0.0.1:8848

# 将所有节点注册到nacos
management:
  endpoints:
    web:
      exposure:
        include: "*"
  • 编写UserInfoProviderFeign接口

package cn.zack.feign;

import cn.zack.feign.impl.UserInfoProviderFeignImpl;
import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * 使用@FeignClient进行rest调用, 并指定要调用哪个服务, 并指定容错处理方法
 */
@FeignClient(name = "userInfoProvider", fallback = UserInfoProviderFeignImpl.class)
@Component
public interface UserInfoProviderFeign {

    /**
     * 此服务下的方法
     *
     * @return
     */
    @GetMapping(path = "fun1")
    JSONObject fun1(@RequestParam Integer param);
}
  • 编写UserInfoProviderFeignImpl容错处理

package cn.zack.feign.impl;

import cn.zack.feign.UserInfoProviderFeign;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

@Component
public class UserInfoProviderFeignImpl implements UserInfoProviderFeign {
    @Override
    public JSONObject fun1(Integer param) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("result", "UserInfo Fall Back");
        return jsonObject;
    }
}
  • 编写UserInfoConsumerController模拟业务调用

package cn.zack.controller;

import cn.zack.feign.UserInfoProviderFeign;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserInfoConsumerController {

    /**
     * 注入userInfoProvider的feign接口, 通过feign进行调用
     */
    @Autowired
    private UserInfoProviderFeign userInfoProviderFeign;

    @GetMapping(path = "testFun1")
    public JSONObject testFun1(@RequestParam Integer param) {
        JSONObject result = userInfoProviderFeign.fun1(param);
        return result;
    }
}
  • 启动UserInfoConsumer1模块

在nacos服务列表中可以看到消费者服务注册成功
image.png
直接调用UserInfoConsumer1中的接口
http://127.0.0.1:8091/testFun1?param=1
由于UserInfoConsumer1通过feign调用UserInfoProvider1, 可以正常获得服务提供者UserInfoProvider的响应报文
{"result":"调用了userInfoProvider1, 方法: fun1"}


软负载均衡-ribbon

Feign 默认集成了 Ribbon,Nacos 也很好的兼容了 Feign,默认实现了负载均衡的效果
Feign 采用的是基于接口的注解
Feign 整合了 ribbon

一. 复制UserInfoProvider1模块为UserInfoProvider2

image.png

  • 修改端口号为8082

server:
  port: 8082

spring:
  application:
    # 服务提供者应用名
    name: userInfoProvider
  cloud:
    nacos:
      discovery:
        # nacos注册中心地址
        server-addr: 127.0.0.1:8848

# 将所有节点注册到nacos
management:
  endpoints:
    web:
      exposure:
        include: "*"
  • 修改UserInfoProvider2的Controller代码

package cn.zack.controller;

import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 支持rest调用
 */
@RestController
public class UserInfoProviderController {

    @GetMapping(path = "fun1")
    public JSONObject fun1(@RequestParam Integer param) {
	int a = 1 / param;
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("result", "调用了userInfoProvider2, 方法: fun1");
        return jsonObject;
    }
}
  • 启动UserInfoProvider2模块

在nacos服务列表中可以看到服务userInfoProvider提供者新增成功
服务提供者实例数为2, 服务消费者实例数为1
image.png
直接调用UserInfoProvider1中的接口http://127.0.0.1:8082/fun1?param=1
可以正常响应:
{"result":"调用了userInfoProvider2, 方法: fun1"}

多次调用服务消费者的接口http://127.0.0.1:8091/testFun1?param=1
观察响应报文为以下两份报文轮流切换, 说明此服务消费者是轮流调用两个服务提供者
{"result":"调用了userInfoProvider1, 方法: fun1"}
{"result":"调用了userInfoProvider2, 方法: fun1"}