服务调用 Feign


yudao-spring-boot-starter-rpc (opens new window)技术组件,基于 Feign 实现服务之间的调用。

为什么不使用 Dubbo 呢?

Feign 通用性更强,学习成本更低,对于绝大多数场景,都能够很好的满足需求。虽然 Dubbo 提供的性能更强,特性更全,但都是非必须的。

目前国内 95% 左右都是采用 Feign,而 Dubbo 的使用率只有 5% 左右。所以,我们也选择了 Feign。

如果你对 Feign 了解较少,可以阅读 《芋道 Spring Cloud 声明式调用 Feign 入门》 (opens new window)系统学习。

#1. RPC 使用规约

本小节,我们来讲解下项目中 RPC 使用的规约。

#1.1 API 前缀

API 使用 HTTP 协议,所有的 API 前缀,都以 /rpc-api (opens new window)开头,方便做统一的全局处理。

#1.2 API 权限

服务之间的调用,不需要进行权限校验,所以需要在每个服务的 SecurityConfiguration 权限配置类中,添加如下配置:

// RPC 服务的安全配置
registry.antMatchers(ApiConstants.PREFIX + "/**").permitAll();

#1.3 API 全局返回

所有 API 接口返回使用 CommonResult (opens new window)返回,和前端 RESTful API 保持统一。例如说:

public interface DeptApi {

    @GetMapping(PREFIX + "/get")
    @Operation(summary = "获得部门信息")
    @Parameter(name = "id", description = "部门编号", required = true, example = "1024")
    CommonResult<DeptRespDTO> getDept(@RequestParam("id") Long id);
    
}

#1.4 用户传递

服务调用时,已经封装 Feign 将用户信息通过 HTTP 请求头 login-user 传递,通过 LoginUserRequestInterceptor (opens new window)类实现。

这样,被调用服务,可以通过 SecurityFrameworkUtils 获取到用户信息,例如说:

  • #getLoginUser() 方法,获取当前用户。
  • #getLoginUserId() 方法,获取当前用户编号。

#2. 如何定义一个 API 接口

本小节,我们来讲解下如何定义一个 API 接口。以 AdminUserApi 提供的 getUser 接口来举例子。

#2.1 服务提供者

AdminUserApi 由 system-server 服务所提供。

#2.1.1 ApiConstants

在 yudao-module-system-api 模块,创建 ApiConstants (opens new window)类,定义 API 相关的枚举。代码如下:

public class ApiConstants {

    /**
     * 服务名
     *
     * 注意,需要保证和 spring.application.name 保持一致
     */
    public static final String NAME = "system-server";

    public static final String PREFIX = RpcConstants.RPC_API_PREFIX +  "/system";

    public static final String VERSION = "1.0.0";

}

#2.1.2 AdminUserApi

在 yudao-module-system-api 模块,创建 AdminUserApi (opens new window)类,定义 API 接口。代码如下:

@FeignClient(name = ApiConstants.NAME) // ① @FeignClient 注解
@Tag(name = "RPC 服务 - 管理员用户") // ② Swagger 接口文档
public interface AdminUserApi {

    String PREFIX = ApiConstants.PREFIX + "/user";

    @GetMapping(PREFIX + "/get") // ③ Spring MVC 接口注解
    @Operation(summary = "通过用户 ID 查询用户")  // ② Swagger 接口文档
    @Parameter(name = "id", description = "部门编号", required = true, example = "1024") // ② Swagger 接口文档
    CommonResult<AdminUserRespDTO> getUser(@RequestParam("id") Long id);
    
}

另外,需要创建 AdminUserRespDTO (opens new window)类,定义用户 Response DTO。代码如下:

@Data
public class AdminUserRespDTO {

    /**
     * 用户ID
     */
    private Long id;
    /**
     * 用户昵称
     */
    private String nickname;
    /**
     * 帐号状态
     *
     * 枚举 {@link CommonStatusEnum}
     */
    private Integer status;

    /**
     * 部门ID
     */
    private Long deptId;
    /**
     * 岗位编号数组
     */
    private Set<Long> postIds;
    /**
     * 手机号码
     */
    private String mobile;

}

#2.1.3 AdminUserRpcImpl

在 yudao-module-system-biz 模块,创建 AdminUserRpcImpl (opens new window)类,实现 API 接口。代码如下:

@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class AdminUserApiImpl implements AdminUserApi {

    @Resource
    private AdminUserService userService;

    @Override
    public CommonResult<AdminUserRespDTO> getUser(Long id) {
        AdminUserDO user = userService.getUser(id);
        return success(UserConvert.INSTANCE.convert4(user));
    }
    
}

#2.2 服务消费者

bpm-server 服务,调用了 AdminUserApi 接口。

#2.2.1 引入依赖

在 yudao-module-bpm-biz 模块的 pom.xml (opens new window),引入 yudao-module-system-api 模块的依赖。代码如下:

<dependency>
    <groupId>cn.iocoder.cloud</groupId>
    <artifactId>yudao-module-system-api</artifactId>
    <version>${revision}</version>
</dependency>

#2.2.2 引用 API

在 yudao-module-bpm-biz 模块,创建 RpcConfiguration (opens new window)配置类,注入 AdminUserApi 接口。代码如下:

@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {AdminUserApi.class.class})
public class RpcConfiguration {
}

#2.2.3 调用 API

例如说,BpmTaskServiceImpl (opens new window)调用了 AdminUserApi 接口,代码如下:

@Service
public class BpmTaskServiceImpl implements BpmTaskService {

    @Resource
    private AdminUserApi adminUserApi;

    @Override
    public void updateTaskExtAssign(Task task) {
        // ... 省略非关键代码
        AdminUserRespDTO startUser = adminUserApi.getUser(id).getCheckedData();
    }
}

LazzMan 2024年4月30日 10:22 收藏文档