以封装 api-gateway-core 为目的,搭建 SpringBoot Starter 组件,用于服务注册发现的相关内容处理。
设计
本章最大目的在于搭建起用于封装网关算力服务的 api-gateway-core 系统为目的,提供网关服务注册发现能力的组件。那么之所以要开发一个这样的组件,也就是 SpringBoot Starter,是因为我们希望把这样的统一公用能力进行一致的管理,如果没有这样的组件服务,那么将需要每一个 SpringBoot 服务都要做类似这样的事情,整体来看就会耗费很大的成本,所以要把这样的功能进行收口。
- api-gateway-core 是网关的算力服务,api-gateway-center 是网关的注册中心,那么为了把这块服务链接起来,中间则需要一套 api-gateway-engin 网关的引擎,用于启动网关的算力服务。
- 但由于启动网关的算力服务还需要一些功能的整合,来包装网关算力到注册中心的连接,所以这部分需要整合到 api-gateway-assist 这个辅助组件,它是一个 SpringBoot Starter ,起到包装和连接的作用。
通常我们使用一个公用的 starter 的时候,只需要将相应的依赖添加的Maven的配置文件当中即可,免去了自己需要引用很多依赖类,并且 SpringBoot 会自动进行类的自动配置。而我们自己开发一个 starter 也需要做相应的处理;
- SpringBoot 在启动时会去依赖的 starter 包中寻找
resources/META-INF/spring.factories
文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的Jar包,这类似于 Java 的 SPI 机制。
- 根据
spring.factories
配置加载 AutoConfigure 类。
- 根据
@Conditional
注解的条件,进行自动配置并将 Bean 注入 Spring Context 上下文当中。也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class})
指定自动配置哪些类。
- 日常使用的 Spring 官方的 Starter 一般采取 spring-boot-starter-{name} 的命名方式,如 spring-boot-starter-web 。而非官方的 Starter ,官方建议 artifactId 命名应遵循 {name}-spring-boot-starter 的格式。 例如:door-spring-boot-starter 。
实现
GatewayServiceProperties 配置文件
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
| package cn.ray.gateway.assist.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("api-gateway") public class GatewayServiceProperties {
private String address; private String groupId; private String gatewayId; private String gatewayName; private String gatewayAddress;
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String getGroupId() { return groupId; }
public void setGroupId(String groupId) { this.groupId = groupId; }
public String getGatewayId() { return gatewayId; }
public void setGatewayId(String gatewayId) { this.gatewayId = gatewayId; }
public String getGatewayName() { return gatewayName; }
public void setGatewayName(String gatewayName) { this.gatewayName = gatewayName; }
public String getGatewayAddress() { return gatewayAddress; }
public void setGatewayAddress(String gatewayAddress) { this.gatewayAddress = gatewayAddress; } }
|
在 SpringBoot Starter 的组件开发中,需要使用注解 @ConfgurationProperties("'api-gateway")
标记出作为配置的文件类。在类中添加属性信息,这些属性信息就是最后的配置到 yml 中的配置属性。
RegisterGatewayService 网关注册服务
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
| package cn.ray.gateway.assist.service;
import cn.hutool.http.HttpUtil; import cn.ray.gateway.assist.GatewayException; import cn.ray.gateway.assist.common.Result; import com.alibaba.fastjson.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.Map;
public class RegisterGatewayService {
private Logger logger = LoggerFactory.getLogger(RegisterGatewayService.class);
public void doRegister(String address, String groupId, String gatewayId, String gatewayName, String gatewayAddress) { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("groupId", groupId); paramMap.put("gatewayId", gatewayId); paramMap.put("gatewayName", gatewayName); paramMap.put("gatewayAddress", gatewayAddress); String resultStr = HttpUtil.post(address, paramMap, 350); Result result = JSON.parseObject(resultStr, Result.class); logger.info("向网关中心注册网关算力服务 gatewayId:{} gatewayName:{} gatewayAddress:{} 注册结果:{}", gatewayId, gatewayName, gatewayAddress, resultStr); if (!"0000".equals(result.getCode())) throw new GatewayException("网关服务注册异常 [gatewayId:" + gatewayId + "] 、[gatewayAddress:" + gatewayAddress + "]"); } }
|
在 RegisterGatewayService 注册网关类中,利用 hutool 包中的 HTTPUtil#post 调用 api-gateway-center 提供的服务发现接口,向网关中心注册网关算力节点。这是第一步非常重要的关联作用,有了这块逻辑的处理,才能打通整个网关算力和网关注册中心。
GatewayAssistantApplication 应用监听
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
| package cn.ray.gateway.assist.applicaton;
import cn.ray.gateway.assist.config.GatewayServiceProperties; import cn.ray.gateway.assist.service.RegisterGatewayService; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent;
public class GatewayAssistantApplication implements ApplicationListener<ContextRefreshedEvent> {
private GatewayServiceProperties properties; private RegisterGatewayService registerGatewayService;
public GatewayAssistantApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService) { this.properties = properties; this.registerGatewayService = registerGatewayService; }
@Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { registerGatewayService.doRegister(properties.getAddress(), properties.getGroupId(), properties.getGatewayId(), properties.getGatewayName(), properties.getGatewayAddress()); } }
|
这段代码是一个网关应用程序的类,它实现了 Spring 框架的ApplicationListener
接口,监听ContextRefreshedEvent
事件。在 Spring 容器初始化完成后,会触发ContextRefreshedEvent
事件,进而调用onApplicationEvent
方法。
该类的作用是与 Spring 框架进行连接,并调用网关注册和接口拉取的功能。
构造函数接收两个参数:GatewayServiceProperties
和RegisterGatewayService
。GatewayServiceProperties
是网关服务的配置属性,RegisterGatewayService
是用于注册网关服务的服务类。
在onApplicationEvent
方法中,通过调用registerGatewayService
的doRegister
方法,将网关服务注册到配置中心。方法的参数包括网关服务的地址、组ID、网关ID、网关名称和网关地址等信息。
总体来说,这段代码实现了在 Spring 容器初始化完成后自动注册网关服务的功能。
因为我们开发的是 SpringBoot Starter 组件,与 Spring 关联。所以这里会用到 Spring 提供的监听类 ApplicationListener 监听容器刷新实践后则调用网关注册中心将网关服务注册上去。重复注册则是标记服务启动。
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
| package cn.ray.gateway.assist.config;
import cn.ray.gateway.assist.applicaton.GatewayAssistantApplication; import cn.ray.gateway.assist.service.RegisterGatewayService; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @EnableConfigurationProperties(GatewayServiceProperties.class) public class GatewayAutoConfig {
@Bean public RegisterGatewayService registerGatewayService() { return new RegisterGatewayService(); }
@Bean public GatewayAssistantApplication gatewayApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService) { return new GatewayAssistantApplication(properties, registerGatewayService); }
}
|
测试
ApiTest
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
| package cn.ray.gateway.assist.test;
import cn.hutool.http.HttpUtil; import cn.ray.gateway.assist.common.Result; import com.alibaba.fastjson.JSON; import org.junit.Test;
import java.util.HashMap; import java.util.Map;
public class ApiTest {
@Test public void test_register_gateway() { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("groupId", "10001"); paramMap.put("gatewayId", "api-gateway-g4"); paramMap.put("gatewayName", "电商配送网关"); paramMap.put("gatewayAddress", "127.0.0.1");
String resultStr = HttpUtil.post("http://localhost:80/wg/admin/config/registerGateway", paramMap, 2000); System.out.println(resultStr);
Result result = JSON.parseObject(resultStr, Result.class); System.out.println(result.getCode()); } }
|
测试结果
思考
- 通常像 MyBatis 的 ORM 框架、RPC 的 Dubbo 服务,在接入到 SpringBoot 都是有自己的 Starter 组件的。因为这些组件可以封装一些共性共用的逻辑处理,一次开发处处使用的目的。
- 所以我们要做这样的处理,方便在网关引擎中启动网关算力。可以想象这是把算力注入到引擎中的纽带开发
- 本章其实就是在实现:通过获取配置文件相应的配置,调用 center 中的注册网关接口将其配置信息持久化到数据库中。