业务流程

在上一节中,我们实现了网关注册中心的对外的网关聚合配置信息的查询接口,有了这个接口,我们的网关算力节点(api-gateway-core)就可以通过这个接口获得映射信息。但是这个映射信息我们在之前几章的流程梳理过,不能直接由算力节点拉取,而应该由对它进行包装的starter进行拉取,这个过程就像starter自动帮算力节点进行注册一样。所以本章节的主要内容就是,接着开发api-gateway-assist这个包装算力节点的starter,让其可以自动从网关注册中心拉取映射信息。后续章节我们再处理映射操作,因为映射还需要把网关核心算力引入到助手组件中进行包装使用。如下图(图片来自于xfg

业务实现

项目结构如下

这里的开发其实和第13章的开发几乎一模一样,只是由于我们接收到的信息是从第14章来的聚合对象,因此要将14章的聚合对象和VO引入进来。同时我们更新了项目结构,创建了domain层,将之前的服务移入其中

添加向网关注册中心发起拉取映射信息请求的接口RegisterGatewayService#pullApplicationSystemRichInfo,几乎和之前的自动注册一样,只是请求的网关注册中心的接口变了,那么入参也就变了,返回的结果也变了。

 package com.zshunbao.gateway.assist.domain.service;
 ​
 import cn.hutool.http.HttpUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.TypeReference;
 import com.zshunbao.gateway.assist.GatewayException;
 import com.zshunbao.gateway.assist.common.Result;
 import com.zshunbao.gateway.assist.domain.model.aggregates.ApplicationSystemRichInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 ​
 import java.util.HashMap;
 import java.util.Map;
 ​
 /**
  * @program: api-gateway-assist
  * @ClassName RegisterGatewayService
  * @description: 网关注册服务
  * @author: zs宝
  * @create: 2025-08-21 15:17
  * @Version 1.0
  **/
 public class RegisterGatewayService {
     private Logger logger = LoggerFactory.getLogger(RegisterGatewayService.class);
 ​
     /**
      * 进行向api-gateway-center网关注册中心发起注册的服务
      * @param address 注册中心地址
      * @param groupId api-gateway-core分组ID
      * @param gatewayId 网关ID
      * @param gatewayName 网关名称
      * @param gatewayAddress 网关地址
      */
     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);
         //调用hutool工具包发送请求
         String resultStr = HttpUtil.post(address+"/wg/admin/config/registerGateway", paramMap, 1200);
         //将返回结果用自定义的结果类包装
         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 + "]");
         }
     }
 ​
     public ApplicationSystemRichInfo pullApplicationSystemRichInfo(String address, String gatewayId) {
         logger.info("开始向网关注册中心拉取配置 gatewayId:{} ",gatewayId);
         //封装请求参数
         Map<String,Object> paramMap=new HashMap<>();
         paramMap.put("gatewayId",gatewayId);
         String resultStr=HttpUtil.post(address+"/wg/admin/config/queryApplicationSystemRichInfo", paramMap, 1200);
         Result<ApplicationSystemRichInfo> result = JSON.parseObject(resultStr, new TypeReference<Result<ApplicationSystemRichInfo>>(){});
         if (!"0000".equals(result.getCode()))
             throw new GatewayException("从网关中心拉取应用服务和接口的配置信息到本地完成注册异常 [gatewayId:" + gatewayId + "]");
         logger.info("向网关注册中心拉取配置成功 gatewayId:{} 配置信息 {}",gatewayId,resultStr);
         return result.getData();
     }
 ​
 ​
 }

最后由于要让starter自动拉取,还得如之前的注册一样,添加监听事件GatewayApplication.java.相当于添加了一个额外的扩展调用,等下个章节继续完成调用后的注册映射处理。HTTP->RPC

 package com.zshunbao.gateway.assist.application;
 ​
 import com.alibaba.fastjson.JSON;
 import com.zshunbao.gateway.assist.config.GatewayServiceProperties;
 import com.zshunbao.gateway.assist.domain.model.aggregates.ApplicationSystemRichInfo;
 import com.zshunbao.gateway.assist.domain.service.RegisterGatewayService;
 import org.springframework.context.ApplicationListener;
 import org.springframework.context.event.ContextRefreshedEvent;
 ​
 /**
  * @program: api-gateway-assist
  * @ClassName GatewayApplication
  * @description: 网关应用;与 Spring 链接,调用网关注册和接口拉取
  * @author: zs宝
  * @create: 2025-08-21 15:26
  * @Version 1.0
  **/
 public class GatewayApplication implements ApplicationListener<ContextRefreshedEvent> {
     private GatewayServiceProperties properties;
     private RegisterGatewayService registerGatewayService;
 ​
     public GatewayApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService) {
         this.properties = properties;
         this.registerGatewayService = registerGatewayService;
     }
 ​
     //当所有容器加载完毕时,即监听到了上下文事件更新,然后调用注册方法,向网关注册中心发起注册
     @Override
     public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
         // 1. 注册网关服务;每一个用于转换 HTTP 协议泛化调用到 RPC 接口的网关都是一个算力,这些算力需要注册网关配置中心
         registerGatewayService.doRegister(properties.getAddress(),
                 properties.getGroupId(),
                 properties.getGatewayId(),
                 properties.getGatewayName(),
                 properties.getGatewayAddress());
 ​
         //2、从注册中心拉取映射信息配置
         ApplicationSystemRichInfo applicationSystemRichInfo = registerGatewayService.pullApplicationSystemRichInfo(properties.getAddress(), properties.getGatewayId());
         System.out.println(JSON.toJSONString(applicationSystemRichInfo));
     }
 }
 ​

到此本节的业务逻辑上的代码已经完成,我们再补充下上节的聚合类和VO类吧

ApplicationSystemRichInfo.java

 package com.zshunbao.gateway.assist.domain.model.aggregates;
 ​
 import com.zshunbao.gateway.assist.domain.model.vo.ApplicationSystemVO;
 ​
 import java.util.List;
 ​
 /**
  * @program: api-gateway-assist
  * @ClassName ApplicationSystemRichInfo
  * @description: 网关算力配置信息->即每个api-gateway-core的映射信息
  * @author: zs宝
  * @create: 2025-08-22 15:59
  * @Version 1.0
  **/
 public class ApplicationSystemRichInfo {
     /** 网关ID */
     private String gatewayId;
     /** 系统列表 */
     private List<ApplicationSystemVO> applicationSystemVOList;
 ​
     public ApplicationSystemRichInfo(String gatewayId, List<ApplicationSystemVO> applicationSystemVOList) {
         this.gatewayId = gatewayId;
         this.applicationSystemVOList = applicationSystemVOList;
     }
     public ApplicationSystemRichInfo(){
 ​
     }
 ​
     public String getGatewayId() {
         return gatewayId;
     }
 ​
     public void setGatewayId(String gatewayId) {
         this.gatewayId = gatewayId;
     }
 ​
     public List<ApplicationSystemVO> getApplicationSystemVOList() {
         return applicationSystemVOList;
     }
 ​
     public void setApplicationSystemVOList(List<ApplicationSystemVO> applicationSystemVOList) {
         this.applicationSystemVOList = applicationSystemVOList;
     }
 }
 ​

ApplicationSystemVO.java

 package com.zshunbao.gateway.assist.domain.model.vo;
 ​
 import java.util.List;
 ​
 /**
  * @program: api-gateway-center
  * @ClassName ApplicationSystemVO
  * @description: 注册系统VO
  * @author: zs宝
  * @create: 2025-08-20 14:29
  * @Version 1.0
  **/
 public class ApplicationSystemVO {
     /** 系统标识 */
     private String systemId;
     /** 系统名称 */
     private String systemName;
     /** 系统类型;RPC、HTTP*/
     private String systemType;
     /** 注册中心;zookeeper://127.0.0.1:2181*/
     private String systemRegistry;
 ​
     private List<ApplicationInterfaceVO> interfaceList;
 ​
     public String getSystemId() {
         return systemId;
     }
 ​
     public void setSystemId(String systemId) {
         this.systemId = systemId;
     }
 ​
     public String getSystemName() {
         return systemName;
     }
 ​
     public void setSystemName(String systemName) {
         this.systemName = systemName;
     }
 ​
     public String getSystemType() {
         return systemType;
     }
 ​
     public void setSystemType(String systemType) {
         this.systemType = systemType;
     }
 ​
     public String getSystemRegistry() {
         return systemRegistry;
     }
 ​
     public void setSystemRegistry(String systemRegistry) {
         this.systemRegistry = systemRegistry;
     }
 ​
 ​
     public List<ApplicationInterfaceVO> getInterfaceList() {
         return interfaceList;
     }
 ​
     public void setInterfaceList(List<ApplicationInterfaceVO> interfaceList) {
         this.interfaceList = interfaceList;
     }
 }
 ​

ApplicationInterfaceVO.java

 package com.zshunbao.gateway.assist.domain.model.vo;
 ​
 import java.util.List;
 ​
 /**
  * @program: api-gateway-center
  * @ClassName ApplicationInterfaceVO
  * @description: 注册接口VO
  * @author: zs宝
  * @create: 2025-08-20 14:29
  * @Version 1.0
  **/
 public class ApplicationInterfaceVO {
     /** 系统标识 */
     private String systemId;
     /** 接口标识 */
     private String interfaceId;
     /** 接口名称 */
     private String interfaceName;
     /** 接口版本 */
     private String interfaceVersion;
 ​
     private List<ApplicationInterfaceMethodVO> methodList;
 ​
     public String getSystemId() {
         return systemId;
     }
 ​
     public void setSystemId(String systemId) {
         this.systemId = systemId;
     }
 ​
     public String getInterfaceId() {
         return interfaceId;
     }
 ​
     public void setInterfaceId(String interfaceId) {
         this.interfaceId = interfaceId;
     }
 ​
     public String getInterfaceName() {
         return interfaceName;
     }
 ​
     public void setInterfaceName(String interfaceName) {
         this.interfaceName = interfaceName;
     }
 ​
     public String getInterfaceVersion() {
         return interfaceVersion;
     }
 ​
     public void setInterfaceVersion(String interfaceVersion) {
         this.interfaceVersion = interfaceVersion;
     }
 ​
     public List<ApplicationInterfaceMethodVO> getMethodList() {
         return methodList;
     }
 ​
     public void setMethodList(List<ApplicationInterfaceMethodVO> methodList) {
         this.methodList = methodList;
     }
 }
 ​

ApplicationInterfaceMethodVO.java

 package com.zshunbao.gateway.assist.domain.model.vo;
 ​
 /**
  * @program: api-gateway-center
  * @ClassName ApplicationInterfaceMethodVO
  * @description: 注册函数VO
  * @author: zs宝
  * @create: 2025-08-20 14:29
  * @Version 1.0
  **/
 public class ApplicationInterfaceMethodVO {
     /** 系统标识 */
     private String systemId;
     /** 接口标识 */
     private String interfaceId;
     /** 方法标识 */
     private String methodId;
     /** 方法名称 */
     private String methodName;
     /** 参数类型(RPC 限定单参数注册);new String[]{"java.lang.String"}、new String[]{"cn.bugstack.gateway.rpc.dto.XReq"} */
     private String parameterType;
     /** 网关接口 */
     private String uri;
     /** 接口类型;GET、POST、PUT、DELETE */
     private String httpCommandType;
     /** 是否鉴权;true = 1是、false = 0否 */
     private Integer auth;
 ​
     public String getSystemId() {
         return systemId;
     }
 ​
     public void setSystemId(String systemId) {
         this.systemId = systemId;
     }
 ​
     public String getInterfaceId() {
         return interfaceId;
     }
 ​
     public void setInterfaceId(String interfaceId) {
         this.interfaceId = interfaceId;
     }
 ​
     public String getMethodId() {
         return methodId;
     }
 ​
     public void setMethodId(String methodId) {
         this.methodId = methodId;
     }
 ​
     public String getMethodName() {
         return methodName;
     }
 ​
     public void setMethodName(String methodName) {
         this.methodName = methodName;
     }
 ​
     public String getParameterType() {
         return parameterType;
     }
 ​
     public void setParameterType(String parameterType) {
         this.parameterType = parameterType;
     }
 ​
     public String getUri() {
         return uri;
     }
 ​
     public void setUri(String uri) {
         this.uri = uri;
     }
 ​
     public String getHttpCommandType() {
         return httpCommandType;
     }
 ​
     public void setHttpCommandType(String httpCommandType) {
         this.httpCommandType = httpCommandType;
     }
 ​
     public Integer getAuth() {
         return auth;
     }
 ​
     public void setAuth(Integer auth) {
         this.auth = auth;
     }
 }
 ​

测试

本次测试,我们新增加了 api-gateway-assist-00 的测试工程,它是一个 SpringBoot 的应用工程,用于测试 api-gateway-assist 组件实现。为什么要这么做呢?我们之前一直梳理到,我们的api-gateway-assist是一个对于api-gateway-core的包装,相当于一个starter,未来这个starter是要用于引入到api-gateway-engin中去的,就像SpringBoot会引入SpringBoot starter一样,这个相当于我们专门为我们的API网关做的starter。这个starter一定是要有放在其它的项目中引入测试其功能的。

来看,这个测试项目就是一个啥功能都没有的 SpringBoot 的应用工程,但是它的pom文件中一定要引入我们的starter

 package com.zshunbao.gateway.assist;
 ​
 import org.springframework.beans.factory.annotation.Configurable;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 ​
 /**
  * @program: api-gateway-assist
  * @ClassName Application
  * @description:
  * @author: zs宝
  * @create: 2025-08-22 16:26
  * @Version 1.0
  **/
 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
 @Configurable
 public class Application {
     public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
     }
 }

其pom文件为

 <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 ​
     <groupId>com.zshunbao</groupId>
     <artifactId>api-gateway-assist-00</artifactId>
     <version>1.0-SNAPSHOT</version>
 ​
     <packaging>jar</packaging>
 ​
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.3.5.RELEASE</version>
         <relativePath/>
     </parent>
 ​
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
             <version>1.2.78</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
             <version>3.8</version>
         </dependency>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.12</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-all</artifactId>
             <version>5.5.0</version>
         </dependency>
 ​
         <dependency>
             <groupId>com.zshunbao</groupId>
             <artifactId>api-gateway-assist-02</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>
 ​
     </dependencies>
 ​
     <build>
         <finalName>api-gateway-assist</finalName>
         <resources>
             <resource>
                 <directory>src/main/resources</directory>
                 <filtering>true</filtering>
                 <includes>
                     <include>**/**</include>
                 </includes>
             </resource>
         </resources>
         <testResources>
             <testResource>
                 <directory>src/test/resources</directory>
                 <filtering>true</filtering>
                 <includes>
                     <include>**/**</include>
                 </includes>
             </testResource>
         </testResources>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.12.4</version>
                 <configuration>
                     <skipTests>true</skipTests>
                 </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-resources-plugin</artifactId>
                 <version>2.5</version>
                 <configuration>
                     <encoding>${project.build.sourceEncoding}</encoding>
                 </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>2.3.2</version>
                 <configuration>
                     <source>1.8</source>
                     <target>1.8</target>
                     <encoding>${project.build.sourceEncoding}</encoding>
                 </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-source-plugin</artifactId>
                 <version>2.1.2</version>
                 <executions>
                     <execution>
                         <id>attach-sources</id>
                         <goals>
                             <goal>jar</goal>
                         </goals>
                     </execution>
                 </executions>
             </plugin>
         </plugins>
     </build>
 ​
 </project>

其中一定要包含对我们api-gateway-assist这个starter的引入

         <dependency>
             <groupId>com.zshunbao</groupId>
             <artifactId>api-gateway-assist-02</artifactId>
             <version>1.0-SNAPSHOT</version>
         </dependency>

最后,我们在我们的starter中对于注册信息的相关配置是放于配置文件中,因此这里还要配置相关的application.yml

 server:
   port: 8002
 ​
 ​
 api-gateway:
   address: http://localhost:8001  # 注册中心;从这里获取接口信息以及完成注册网关操作
   groupId: 10001                  # 网关分组;每一个网关通信组件都分配一个对应的分组
   gatewayId: api-gateway-g4       # 网关标识;
   gatewayName: 电商配送网关         # 网关名称
   gatewayAddress: 127.0.0.1:7399  # 网关服务;网关的通信服务Netty启动时使用IP和端口

最后先启动网关注册中心api-gateway-center,再启动这个测试项目,在starter中打上断点,debug运行

而在网关注册中心中有

测试应用放行

最后从本章开始,我们网关项目会开始涉及到多个模块的功能开发联调测试,随着后续开发SDK等都引入后,会增加更多的内容,每个章节一定要把握好每章的核心开发功能需求是什么。

参考资料