十七章 核心通信组件管理和处理服务映射

引入通信组件 api-gateway-core 到 api-gateway-assist 中进行创建和使用,并拉取自注册中心的映射信息注册到本地的网关通信组件中。

设计

17-设计

第17章是在第15章的基础上继续完善服务发现的相关功能,把从注册中心拉取的网关映射信息【系统、接口、方法】映射到本地通信组件中。这样就算完成了注册中心到本地服务的一个打通处理,映射完成后就可以通过 HTTP 请求到网关通信层,完成对RPC的泛化调用。

首先在实现中要引入 api-gateway-core 到 api-gateway-assist 中,通过 GatewayAutocontg 配置类对网关通信组件进行 Bean 对象的创建和启动。

之后就可以在 GatewayAssistantApplication 中处理从网关注册中心拉取到的接口信息进行注册操作。也就是把接口向 api-gateway-core 完成注册映射操作。

实现

引入 core 网关通信组件

1
2
3
4
5
6
7
8
<!-- 引入网关通信组件 -->
<dependencies>
<dependency>
<groupId>cn.ray.gateway</groupId>
<artifactId>api-gateway-core-09</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

在 api-gateway-core-09 中进行 install 打包,之后引入到 api-gateway-assist-03 中进行使用。

另外这里还有一个配置是让 api-gateway-core-09 可以打包到 api-gateway-assist 中的配置。

如下:

1
2
3
4
5
6
7
<configuration>
<artifactSet>
<includes>
<include>cn.ray.gateway:api-gateway-core-09:jar:</include>
</includes>
</artifactSet>
</configuration>

这个操作主要是因为将来把 api-gateway-assist 给外部使用时,不用在额外引用 api-gatewav-core了。

GatewayAutoConfig 服务创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package cn.ray.gateway.assist.config;

import cn.ray.gateway.assist.applicaton.GatewayAssistantApplication;
import cn.ray.gateway.assist.domain.service.RegisterGatewayService;
import cn.ray.gateway.core.session.defaults.DefaultGatewaySessionFactory;
import cn.ray.gateway.core.socket.GatewaySocketServer;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* @author Ray
* @date 2023/5/25 10:15
* @description 网关服务配置
*/
@Configuration
@EnableConfigurationProperties(GatewayServiceProperties.class)
public class GatewayAutoConfig {

private Logger logger = LoggerFactory.getLogger(GatewayAutoConfig.class);

@Bean
public RegisterGatewayService registerGatewayService() {
return new RegisterGatewayService();
}

@Bean
public GatewayAssistantApplication gatewayApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService, cn.ray.gateway.core.session.Configuration configuration) {
return new GatewayAssistantApplication(properties, registerGatewayService, configuration);
}

/**
* 创建网关配置对象;Configuration 是用于贯穿整个网关核心通信服务的。
*/
@Bean
public cn.ray.gateway.core.session.Configuration gatewayCoreConfiguration(GatewayServiceProperties gatewayServiceProperties) {
cn.ray.gateway.core.session.Configuration configuration = new cn.ray.gateway.core.session.Configuration();
String[] split = gatewayServiceProperties.getGatewayAddress().split(":");
configuration.setHostName(split[0].trim());
configuration.setPort(Integer.parseInt(split[1].trim()));
return configuration;
}

/**
* 初始化网关服务;创建服务端 Channel 对象,方便获取和控制网关操作。
*/
@Bean
public Channel initGateway(cn.ray.gateway.core.session.Configuration configuration) throws ExecutionException, InterruptedException {
DefaultGatewaySessionFactory gatewaySessionFactory = new DefaultGatewaySessionFactory(configuration);
GatewaySocketServer gatewaySocketServer = new GatewaySocketServer(configuration, gatewaySessionFactory);
Future<Channel> future = Executors.newFixedThreadPool(2).submit(gatewaySocketServer);
Channel channel = future.get();
if (null == channel) throw new RuntimeException("api gateway core netty server start error channel is null");
while (!channel.isActive()) {
logger.info("api gateway core netty server gateway start Ing ...");
Thread.sleep(500);
}
logger.info("api gateway core netty server gateway start Done! {}", channel.localAddress());
return channel;
}
}

在 GatewayAutoConfg 自动创建 Bean 对象类中,创建 cn.ray.gateway.core.session.Contguration 配置,以及启动网关服务。启动后就可以接收服务接口的注册操作了。

接口注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package cn.ray.gateway.assist.applicaton;

import cn.ray.gateway.assist.config.GatewayServiceProperties;
import cn.ray.gateway.assist.domain.model.aggregates.ApplicationSystemRichInfo;
import cn.ray.gateway.assist.domain.model.vo.ApplicationInterfaceMethodVO;
import cn.ray.gateway.assist.domain.model.vo.ApplicationInterfaceVO;
import cn.ray.gateway.assist.domain.model.vo.ApplicationSystemVO;
import cn.ray.gateway.assist.domain.service.RegisterGatewayService;
import cn.ray.gateway.core.mapping.HttpRequestType;
import cn.ray.gateway.core.mapping.HttpStatement;
import cn.ray.gateway.core.session.Configuration;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import java.util.List;

/**
* @author Ray
* @date 2023/5/25 10:11
* @description 网关应用 与 Spring 链接,调用网关注册和接口拉取
*/
public class GatewayAssistantApplication implements ApplicationListener<ContextRefreshedEvent> {

private Logger logger = LoggerFactory.getLogger(GatewayAssistantApplication.class);

private GatewayServiceProperties properties;

private RegisterGatewayService registerGatewayService;

private Configuration configuration;

public GatewayAssistantApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService, Configuration configuration) {
this.properties = properties;
this.registerGatewayService = registerGatewayService;
this.configuration = configuration;
}

@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// 1. 注册网关服务;每一个用于转换 HTTP 协议泛化调用到 RPC 接口的网关都是一个算力,这些算力需要注册网关配置中心
registerGatewayService.doRegister(properties.getAddress(),
properties.getGroupId(),
properties.getGatewayId(),
properties.getGatewayName(),
properties.getGatewayAddress());

// 2. 拉取网关配置;每个网关算力都会在注册中心分配上需要映射的RPC服务信息,包括;系统、接口、方法
ApplicationSystemRichInfo applicationSystemRichInfo = registerGatewayService.pullApplicationSystemRichInfo(properties.getAddress(), properties.getGatewayId());

List<ApplicationSystemVO> applicationSystemVOList = applicationSystemRichInfo.getApplicationSystemVOList();
for (ApplicationSystemVO system : applicationSystemVOList) {
List<ApplicationInterfaceVO> interfaceList = system.getInterfaceList();
for (ApplicationInterfaceVO interfaceVO : interfaceList) {
// 2.1 创建配置信息加载注册
configuration.registerConfig(system.getSystemId(), system.getSystemRegistry(), interfaceVO.getInterfaceId(), interfaceVO.getInterfaceVersion());
List<ApplicationInterfaceMethodVO> methodList = interfaceVO.getMethodList();
// 2.2 注册系统服务接口信息
for (ApplicationInterfaceMethodVO method : methodList) {
HttpStatement httpStatement = new HttpStatement(
system.getSystemId(),
interfaceVO.getInterfaceId(),
method.getMethodId(),
method.getUri(),
HttpRequestType.valueOf(method.getHttpCommandType()),
method.getParameterType(),
method.isAuth());
configuration.addMapper(httpStatement);
logger.info("网关服务注册映射 系统:{} 接口:{} 方法:{}", system.getSystemId(), interfaceVO.getInterfaceId(), method.getMethodId());
}
}
}
}
}

onApplicationEvent 方法中的第2步,就是从注册中心获取的属于当前网关需要完成映射操作的系统信息。包括这个系统的接口和方法。通过循环遍历查询到的信息进行注册处理。

在这里大家也可以提前思考下,如果网关服务已经启动后,新增的接口怎么添加到注册里。

测试

  1. 启动 Dubbo和 Zookeeper ,也就是 Dubbo 的注册中心。
  2. 启动 RPC 应用服务。
  3. 启动 center 注册中心,assist 需要从注册中心拉取接口信息。
  4. 修改 assist-test pom 文件,引入 assist-03。
  5. 启动 assist-test。

测试结果

17-测试-1

启动的过程包括启动网关服务,注册网关信息,拉取接口映射配置等操作。

GET

17-测试-2

POST

17-测试-3

思考

本章内容主要在网关助手中扩展了对服务的创建、启动和注册操作。在这样的一个类中完成了注册中心、通信服务、接口调用等内容的链接处理。

问题一:无法引入对应的 jar 包

1
Failed to execute goal on project xxx: Could not resolve dependencies for project com.jd.ka:xxx:war:0.0.1-SNAPSHOT: The following artifacts could not be resolved: com.jd.ka:ka-explore-service:jar:0.0.1-SNAPSHOT, com.jd.ka:ka-explore-rpc:jar:0.0.1-SNAPSHOT: Failure to find com.jd.ka:ka-explore-service:jar:0.0.1-SNAPSHOT in http://artifactory.jd.com/libs-snapshots-local was cached in the local repository, resolution will not be reattempted until the update interval of snapshots_local has elapsed or updates are forced -> [Help 1]

解决方案:

  • 首先,相信既然你犯了这样的错误,那么你的项目应该也是多个module的。

17-思考

  1. 我这里就举个例子,对于 assist 以及 center,下面都存在着多个子项目。

  2. 在模块中 assist-03 依赖于 core-09,在 core-09 中执行完 clean 和 install 之后,本地仓库也存在依赖,但是在 assist-03 中进行install就会出现

    1
    Failed to execute goal on project …: Could not resolve dependencies for project …

    这样的错误,最后发现原来是自己没有首先对父项目也就是 core 项目进行 clean 和 install。

  3. 总结:在父项目下的子项目首次运行 clean 和 install 前应该先运行父项目的 clean 和 install。

如果网关服务已经启动后,新增的接口怎么添加到注册里?

目前的想法是在通过某个组件自定义注解进行注册。后续需要添加接口就直接利用自定义注解向注册中心和网关添加对应的信息。