流程设计
网关注册中心,是一个多边服务,管理的是RPC服务向网关通信层的关联注册,它相当于中间的桥梁。
网关注册中心首先要接收来自各个网关服务的注册,任何一组用于处理 HTTP 协议请求的网关算力节点(这里我们的算力定义为:运行HTTP协议转换到调用RPC这样的一个过程),都要注册到网关中心进行统一维护和管理。因为只有注册到网关中心才能把 RPC 服务分配到各个网关算力节点上进行使用。
本节的主要内容就是来先简单实现网关通信层的关联注册这块的功能
其中
网关中心维护网关算力节点的库表:
gateway_server
、gateway_server_detail
两个表来维护数据。
那么本章我们先来开发这样一块的功能接口,允许外部通过 HTTP 接口进行注册服务。
目前先不用引入 zookeeper 这样的注册中心,探活服务。后期功能额外完成后再进行陆续补充。
其实本章节的功能也就是一个关于相关库表尤其是gateway_server_detail
的插入,更新,查询操作。api-gateway-core
发起相关注册请求,将相关数据在库表中进行查询,插入,更新的操作。
流程实现
由于api-gateway-center
模块是一个DDD结构,因此这块的代码不同于MVC结构,会有许多中间接口,这里只展示主要的实现代码
对外HTTP接口
首先是interface下的对外HTTP暴露接口
GatewayConfigManage.java
package com.zshunbao.gateway.center.interfaces;
import com.zshunbao.gateway.center.application.IConfigManageService;
import com.zshunbao.gateway.center.domain.manage.model.vo.GatewayServerVO;
import com.zshunbao.gateway.center.infrastructure.common.ResponseCode;
import com.zshunbao.gateway.center.infrastructure.common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @program: api-gateway-center
* @ClassName GatewayConfigManage
* @description: 网关配置管理;服务分组、网关注册、服务关联
* 1. 查询网关服务配置项信息:/wg/admin/config/queryServerConfig
* 2. 注册网关服务节点:/wg/admin/config/registerGateway
* @author: zs宝
* @create: 2025-08-19 16:10
* @Version 1.0
**/
@RestController
@RequestMapping("/wg/admin/config")
public class GatewayConfigManage {
private Logger logger = LoggerFactory.getLogger(GatewayConfigManage.class);
@Resource
private IConfigManageService configManageService;
@GetMapping(value = "queryServerConfig", produces = "application/json;charset=utf-8")
public Result<List<GatewayServerVO>> queryServerConfig() {
try {
logger.info("查询网关服务配置项信息");
List<GatewayServerVO> gatewayServerVOS = configManageService.queryGatewayServerList();
return new Result<>(ResponseCode.SUCCESS.getCode(),ResponseCode.SUCCESS.getInfo(), gatewayServerVOS);
}catch (Exception e){
logger.info("查询网关服务配置项信息异常", e);
return new Result<>(ResponseCode.UN_ERROR.getCode(), e.getMessage(), null);
}
}
@PostMapping(value = "registerGateway")
public Result<Boolean> registerGatewayServerNode(@RequestParam String groupId, @RequestParam String gatewayId, @RequestParam String gatewayName, @RequestParam String gatewayAddress) {
try {
logger.info("注册网关服务节点 gatewayId:{} gatewayName:{} gatewayAddress:{}", gatewayId, gatewayName, gatewayAddress);
boolean done = configManageService.registerGatewayServerNode(groupId, gatewayId, gatewayName, gatewayAddress);
return new Result<>(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getInfo(), done);
}catch (Exception e){
logger.error("注册网关服务节点异常", e);
return new Result<>(ResponseCode.UN_ERROR.getCode(), e.getMessage(), false);
}
}
}
充血模型
然后是本节对应的充血模型
其中使用到了的VO有
GatewayServerDetailVO.java
package com.zshunbao.gateway.center.domain.manage.model.vo;
/**
* @program: api-gateway-center
* @ClassName GatewayServerDetailVO
* @description: 网关服务明细VO
* @author: zs宝
* @create: 2025-08-19 16:28
* @Version 1.0
**/
public class GatewayServerDetailVO {
/** 网关标识 */
private String gatewayId;
/** 网关名称 */
private String gatewayName;
/** 网关地址 */
private String gatewayAddress;
/** 服务状态 */
private Integer status;
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;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
GatewayServerVO.java
package com.zshunbao.gateway.center.domain.manage.model.vo;
/**
* @program: api-gateway-center
* @ClassName GatewayServerVO
* @description: 网关服务VO
* @author: zs宝
* @create: 2025-08-19 16:07
* @Version 1.0
**/
public class GatewayServerVO {
/** 分组标识 */
private String groupId;
/** 分组名称 */
private String groupName;
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
然后是本章服务对应的充血模型实现
ConfigManageService.java
package com.zshunbao.gateway.center.domain.manage.service;
import com.zshunbao.gateway.center.application.IConfigManageService;
import com.zshunbao.gateway.center.domain.manage.model.vo.GatewayServerDetailVO;
import com.zshunbao.gateway.center.domain.manage.model.vo.GatewayServerVO;
import com.zshunbao.gateway.center.domain.manage.repository.IConfigManageRepository;
import com.zshunbao.gateway.center.infrastructure.common.Constants;
import com.zshunbao.gateway.center.interfaces.GatewayConfigManage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
/**
* @program: api-gateway-center
* @ClassName ConfigManageService
* @description:
* @author: zs宝
* @create: 2025-08-19 16:10
* @Version 1.0
**/
@Service
public class ConfigManageService implements IConfigManageService {
private Logger logger = LoggerFactory.getLogger(ConfigManageService.class);
@Resource
IConfigManageRepository configManageRepository;
@Override
public List<GatewayServerVO> queryGatewayServerList() {
return configManageRepository.queryGatewayServerList();
}
@Override
public boolean registerGatewayServerNode(String groupId, String gatewayId, String gatewayName, String gatewayAddress) {
logger.info("开始注册网关节点,groupId:{}, gatewayId:{}, gatewayName:{}, gatewayAddress:{}",groupId,gatewayId,gatewayName,gatewayAddress);
//首先查询下这个对应的算力节点在库表中是否存在
GatewayServerDetailVO gatewayServerDetailVO=configManageRepository.queryGatewayServerDetail(gatewayId,gatewayAddress);
if(null==gatewayServerDetailVO){
//若不存在则插入
try {
logger.info("插入网关服务节点,groupId:{}, gatewayId:{}, gatewayName:{}, gatewayAddress:{}",groupId,gatewayId,gatewayName,gatewayAddress);
return configManageRepository.registerGatewayServerNode(groupId, gatewayId, gatewayName, gatewayAddress, Constants.GatewayStatus.Available);
}catch (DuplicateKeyException e){
logger.info("网关服务节点 唯一索引冲突 ,groupId:{}, gatewayId:{}, gatewayName:{}, gatewayAddress:{}",groupId,gatewayId,gatewayName,gatewayAddress);
//若有重复的,同样更新其状态值
return configManageRepository.updateGatewayStatus(gatewayId, gatewayAddress, Constants.GatewayStatus.Available);
}
}else{
logger.info("节点状态已经存在于数据库表,groupId:{}, gatewayId:{}, gatewayName:{}, gatewayAddress:{}",groupId,gatewayId,gatewayName,gatewayAddress);
//存在则更新状态
return configManageRepository.updateGatewayStatus(gatewayId, gatewayAddress, Constants.GatewayStatus.Available);
}
}
}
本充血模型其中的仓储借口这里就不展示了,其实现类将在下面的infrastructure包下展示
仓储服务
针对上述的充血模型调用的仓储层服务,实现为
ConfigManageRepository.java
package com.zshunbao.gateway.center.infrastructure.repository;
import com.zshunbao.gateway.center.domain.manage.model.vo.GatewayServerDetailVO;
import com.zshunbao.gateway.center.domain.manage.model.vo.GatewayServerVO;
import com.zshunbao.gateway.center.domain.manage.repository.IConfigManageRepository;
import com.zshunbao.gateway.center.domain.manage.service.ConfigManageService;
import com.zshunbao.gateway.center.infrastructure.dao.IGatewayServerDao;
import com.zshunbao.gateway.center.infrastructure.dao.IGatewayServerDetailDao;
import com.zshunbao.gateway.center.infrastructure.po.GatewayServer;
import com.zshunbao.gateway.center.infrastructure.po.GatewayServerDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @program: api-gateway-center
* @ClassName ConfigManageRepository
* @description: 网关配置仓储服务
* @author: zs宝
* @create: 2025-08-19 16:32
* @Version 1.0
**/
@Component
public class ConfigManageRepository implements IConfigManageRepository {
private Logger logger = LoggerFactory.getLogger(ConfigManageRepository.class);
@Resource
IGatewayServerDao gatewayServerDao;
@Resource
IGatewayServerDetailDao gatewayServerDetailDao;
@Override
public List<GatewayServerVO> queryGatewayServerList() {
logger.info("GatewayServer 仓储层查询服务");
List<GatewayServer> gatewayServers=gatewayServerDao.queryGatewayServerList();
List<GatewayServerVO> gatewayServerVOS=new ArrayList<>();
for(GatewayServer gatewayServer:gatewayServers){
GatewayServerVO gatewayServerVO=new GatewayServerVO();
gatewayServerVO.setGroupId(gatewayServer.getGroupId());
gatewayServerVO.setGroupName(gatewayServer.getGroupName());
gatewayServerVOS.add(gatewayServerVO);
}
return gatewayServerVOS;
}
@Override
public GatewayServerDetailVO queryGatewayServerDetail(String gatewayId, String gatewayAddress) {
logger.info("GatewayServerDetail 仓储层查询节点, gatewayId:{}, gatewayAddress:{}",gatewayId,gatewayAddress);
GatewayServerDetail req = new GatewayServerDetail();
req.setGatewayId(gatewayId);
req.setGatewayAddress(gatewayAddress);
GatewayServerDetail gatewayServerDetail = gatewayServerDetailDao.queryGatewayServerDetail(req);
if(null==gatewayServerDetail){
return null;
}
GatewayServerDetailVO gatewayServerDetailVO = new GatewayServerDetailVO();
gatewayServerDetailVO.setGatewayId(gatewayServerDetail.getGatewayId());
gatewayServerDetailVO.setGatewayName(gatewayServerDetail.getGatewayName());
gatewayServerDetailVO.setGatewayAddress(gatewayServerDetail.getGatewayAddress());
gatewayServerDetailVO.setStatus(gatewayServerDetail.getStatus());
return gatewayServerDetailVO;
}
@Override
public boolean registerGatewayServerNode(String groupId, String gatewayId, String gatewayName, String gatewayAddress, Integer status) {
logger.info("GatewayServerDetail 仓储层插入节点,groupId:{}, gatewayId:{}, gatewayName:{}, gatewayAddress:{}",groupId,gatewayId,gatewayName,gatewayAddress);
GatewayServerDetail gatewayServerDetail = new GatewayServerDetail();
gatewayServerDetail.setGroupId(groupId);
gatewayServerDetail.setGatewayId(gatewayId);
gatewayServerDetail.setGatewayName(gatewayName);
gatewayServerDetail.setGatewayAddress(gatewayAddress);
gatewayServerDetail.setStatus(status);
gatewayServerDetailDao.insert(gatewayServerDetail);
return true;
}
@Override
public boolean updateGatewayStatus(String gatewayId, String gatewayAddress, Integer status) {
logger.info("GatewayServerDetail 仓储层更新节点, gatewayId:{}, gatewayAddress:{}",gatewayId,gatewayAddress);
GatewayServerDetail gatewayServerDetail = new GatewayServerDetail();
gatewayServerDetail.setGatewayId(gatewayId);
gatewayServerDetail.setGatewayAddress(gatewayAddress);
gatewayServerDetail.setStatus(status);
gatewayServerDetailDao.updateGatewayStatus(gatewayServerDetail);
return false;
}
}
本章节还涉及到一些常量的定义,返回类的包装,也都在infrastructure包下,有:
Constants.java
package com.zshunbao.gateway.center.infrastructure.common;
/**
* @program: api-gateway-center
* @ClassName Constants
* @description: 基础信息
* @author: zs宝
* @create: 2025-08-19 16:14
* @Version 1.0
**/
public class Constants {
public static final class GatewayStatus {
// 0;不可用
public static final Integer NotAvailable = 0;
// 1; 可使用
public static final Integer Available = 1;
}
}
ResponseCode.java
package com.zshunbao.gateway.center.infrastructure.common;
/**
* @program: api-gateway-center
* @ClassName ResponseCode
* @description: 响应码
* @author: zs宝
* @create: 2025-08-19 16:14
* @Version 1.0
**/
public enum ResponseCode {
SUCCESS("0000", "成功"),
UN_ERROR("0001", "未知失败"),
ILLEGAL_PARAMETER("0002", "非法参数"),
INDEX_DUP("0003", "主键冲突"),
NO_UPDATE("0004", "SQL操作无更新");
private String code;
private String info;
ResponseCode(String code, String info) {
this.code = code;
this.info = info;
}
public String getCode() {
return code;
}
public String getInfo() {
return info;
}
}
Result.java
package com.zshunbao.gateway.center.infrastructure.common;
import com.alibaba.fastjson.JSON;
import java.io.Serializable;
/**
* @program: api-gateway-center
* @ClassName Result
* @description: 统一返回结果对象
* @author: zs宝
* @create: 2025-08-19 16:15
* @Version 1.0
**/
public class Result <T> implements Serializable {
private static final long serialVersionUID = -3826891916021780628L;
private String code;
private String info;
private T data;
public Result(String code, String info, T data) {
this.code = code;
this.info = info;
this.data = data;
}
public String getCode() {
return code;
}
public String getInfo() {
return info;
}
public T getData() {
return data;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
最后还剩下本节使用到的sql
mapper实现
gateway_server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zshunbao.gateway.center.infrastructure.dao.IGatewayServerDao">
<resultMap id="gatewayServerMap" type="com.zshunbao.gateway.center.infrastructure.po.GatewayServer">
<id column="id" property="id"/>
<id column="group_id" property="groupId"/>
<id column="group_name" property="groupName"/>
</resultMap>
<select id="queryGatewayServerList" resultMap="gatewayServerMap">
SELECT id, group_id, group_name FROM gateway_server
</select>
</mapper>
gateway_server_detail.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zshunbao.gateway.center.infrastructure.dao.IGatewayServerDetailDao">
<resultMap id="gatewayServerDetailMap" type="com.zshunbao.gateway.center.infrastructure.po.GatewayServerDetail">
<id column="id" property="id"/>
<id column="group_id" property="groupId"/>
<id column="gateway_id" property="gatewayId"/>
<id column="gateway_name" property="gatewayName"/>
<id column="gateway_address" property="gatewayAddress"/>
<id column="status" property="status"/>
<id column="create_time" property="createTime"/>
<id column="update_time" property="updateTime"/>
</resultMap>
<insert id="insert" parameterType="com.zshunbao.gateway.center.infrastructure.po.GatewayServerDetail">
INSERT INTO gateway_server_detail(group_id, gateway_id, gateway_name, gateway_address, status, create_time, update_time)
VALUES (#{groupId}, #{gatewayId}, #{gatewayName}, #{gatewayAddress}, #{status}, NOW(), NOW());
</insert>
<select id="queryGatewayServerDetail" parameterType="java.lang.String" resultMap="gatewayServerDetailMap">
SELECT gateway_id, gateway_name, gateway_address, status
FROM gateway_server_detail
WHERE gateway_id = #{gatewayId} AND gateway_address = #{gatewayAddress}
</select>
<update id="updateGatewayStatus" parameterType="com.zshunbao.gateway.center.infrastructure.po.GatewayServerDetail">
UPDATE gateway_server_detail
SET status = #{status}
WHERE gateway_id = #{gatewayId} AND gateway_address = #{gatewayAddress}
</update>
</mapper>
测试
package com.zshunbao.gateway.center.test;
import com.alibaba.fastjson.JSON;
import com.zshunbao.gateway.center.application.IConfigManageService;
import com.zshunbao.gateway.center.domain.manage.model.vo.GatewayServerVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
/**
* @program: api-gateway-center
* @ClassName ApiTest
* @description: 单元测试
* @author: zs宝
* @create: 2025-08-19 16:59
* @Version 1.0
**/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiTest {
private Logger logger = LoggerFactory.getLogger(ApiTest.class);
@Resource
private IConfigManageService configManageService;
@Test
public void test_queryGatewayServerList() {
List<GatewayServerVO> gatewayServerVOS = configManageService.queryGatewayServerList();
logger.info("测试结果:{}", JSON.toJSONString(gatewayServerVOS));
}
@Test
public void test_registerGatewayServerNode() {
configManageService.registerGatewayServerNode("10001", "api-gateway-g1", "电商支付网关", "127.0.0.196");
configManageService.registerGatewayServerNode("10001", "api-gateway-g2", "电商支付网关", "127.0.0.197");
configManageService.registerGatewayServerNode("10001", "api-gateway-g3", "电商配送网关", "127.0.0.198");
}
}