在网关注册中心提供网关算力节点的注册服务接口,便于后续网关启动后向网关中心注册信息。
设计
网关注册中心首先要接收来自各个网关服务的注册,任何一组用于处理 HTTP 协议请求的网关算力节点,都要注册到网关中心进行统一维护和管理。只有注册到网关中心才能把 RPC 服务分配到各个网关算力节点上进行使用。
实现
【infrastructure层】网关配置仓储服务
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 77 78 79 80 81 82
| package cn.ray.gateway.center.infrastructure.repository;
import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerDetailVO; import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerVO; import cn.ray.gateway.center.domain.manage.repository.IConfigManageRepository; import cn.ray.gateway.center.infrastructure.dao.IGatewayServerDao; import cn.ray.gateway.center.infrastructure.dao.IGatewayServerDetailDao; import cn.ray.gateway.center.infrastructure.pojo.GatewayServer; import cn.ray.gateway.center.infrastructure.pojo.GatewayServerDetail; import org.springframework.stereotype.Repository;
import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;
@Repository public class ConfigManageRepository implements IConfigManageRepository {
@Resource private IGatewayServerDao gatewayServerDao;
@Resource private IGatewayServerDetailDao gatewayServerDetailDao;
@Override public List<GatewayServerVO> queryGateServerList() { List<GatewayServer> gatewayServers = gatewayServerDao.queryGatewayServerList(); List<GatewayServerVO> gatewayServerVOList = new ArrayList<>(gatewayServers.size()); for (GatewayServer gatewayServer : gatewayServers) { GatewayServerVO gatewayServerVO = new GatewayServerVO(); gatewayServerVO.setGroupId(gatewayServer.getGroupId()); gatewayServerVO.setGroupName(gatewayServer.getGroupName()); gatewayServerVOList.add(gatewayServerVO); } return gatewayServerVOList; }
@Override public boolean registerGatewayServerNode(String groupId, String gatewayId, String gatewayName, String gatewayAddress, Integer available) { GatewayServerDetail gatewayServerDetail = new GatewayServerDetail(); gatewayServerDetail.setGroupId(groupId); gatewayServerDetail.setGatewayId(gatewayId); gatewayServerDetail.setGatewayName(gatewayName); gatewayServerDetail.setGatewayAddress(gatewayAddress); gatewayServerDetail.setStatus(available); gatewayServerDetailDao.insert(gatewayServerDetail); return true; }
@Override public GatewayServerDetailVO queryGatewayServerDetail(String gatewayId, String 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 updateGatewayStatus(String gatewayId, String gatewayAddress, Integer available) { GatewayServerDetail gatewayServerDetail = new GatewayServerDetail(); gatewayServerDetail.setGatewayId(gatewayId); gatewayServerDetail.setGatewayAddress(gatewayAddress); gatewayServerDetail.setStatus(available); return gatewayServerDetailDao.updateGatewayStatus(gatewayServerDetail); } }
|
【domain层】网关配置服务
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
| package cn.ray.gateway.center.domain.manage.service;
import cn.ray.gateway.center.application.IConfigManageService; import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerDetailVO; import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerVO; import cn.ray.gateway.center.domain.manage.repository.IConfigManageRepository; import cn.ray.gateway.center.infrastructure.common.Constants; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import java.util.List;
@Service public class ConfigManageService implements IConfigManageService {
@Resource private IConfigManageRepository configManageRepository;
@Override public List<GatewayServerVO> queryGatewayServerList() { return configManageRepository.queryGateServerList(); }
@Override public boolean registerGatewayServerNode(String groupId, String gatewayId, String gatewayName, String gatewayAddress) { GatewayServerDetailVO gatewayServerDetailVO = configManageRepository.queryGatewayServerDetail(gatewayId, gatewayAddress); if (null == gatewayServerDetailVO) { try { return configManageRepository.registerGatewayServerNode(groupId, gatewayId, gatewayName, gatewayAddress, Constants.GatewayStatus.Available); } catch (DuplicateKeyException e) { return configManageRepository.updateGatewayStatus(gatewayId, gatewayAddress, Constants.GatewayStatus.Available); } } else { return configManageRepository.updateGatewayStatus(gatewayId, gatewayAddress, Constants.GatewayStatus.Available); } } }
|
注册时主要要判断下当前网关是否注册过,如果没有注册,则直接进行添加注册。如果注册过则进行更新。是否注册过根据网关ID和对应的IP地址一起做联合索引。为了避免服务端重启注册故障,加了一段唯一索引判断,如果数据库已经存在数据,抛出索引冲突的异常。冲突后则直接更新,从而避免服务端发起连续调用,导致网关重复注册。
【interfaces层】网关配置管理;服务分组、网关注册、服务关联
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
| package cn.ray.gateway.center.interfaces;
import cn.ray.gateway.center.application.IConfigManageService; import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerVO; import cn.ray.gateway.center.infrastructure.common.ResponseCode; import cn.ray.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;
@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> gatewayServerVOList = configManageService.queryGatewayServerList(); return new Result<>(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getInfo(), gatewayServerVOList); } catch (Exception e) { logger.error("查询网关服务配置项信息异常", 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 isSuccess = configManageService.registerGatewayServerNode(groupId, gatewayId, gatewayName, gatewayAddress); return new Result<>(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getInfo(), isSuccess); } catch (Exception e) { logger.error("注册网关服务节点异常", e); return new Result<>(ResponseCode.UN_ERROR.getCode(), e.getMessage(), false); } } }
|
测试
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 33 34 35 36 37 38 39 40 41 42
| package cn.ray.gateway.center.test;
import cn.ray.gateway.center.application.IConfigManageService; import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerVO; import com.alibaba.fastjson.JSON; 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;
@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"); } }
|
测试结果
思考
- 由于 core 本身是一个 Netty 服务,不希望直接关联到 center 中,经典加一层的思路,可以利用 SpringBoot 将其作为 starter 组件进行包装,需要添加逻辑时在 starter 添加即可,避免 center 中叠加太多逻辑,造成污染。
- 网关分配时不考虑端口,只考虑IP地址,分配不同IP下的同一端口,一台虚拟机分配两台tomcat没有必要,将一个虚拟机分配的小一点,尽可能让一个算力服务完整,而不是被两台 tomcat 分配,但可以使用多个虚拟机构造多个服务实例。
当自研网关组件进行网关分配时,一般不考虑端口的原因可能是以下几点:
- 简化配置和管理:将端口考虑纳入网关分配会增加配置和管理的复杂性。网关通常用于连接不同的网络,并在网络层上进行路由和转发操作。如果还需要考虑端口,那么配置和管理网关的工作将更加繁琐。简化配置和管理可以提高系统的可用性和可靠性。
- 重点在网络层级别:自研网关组件通常专注于网络层级别的功能,如路由选择、数据包转发和地址转换等。这些功能与端口无关,主要依赖于IP地址和子网掩码等网络层信息。因此,不考虑端口可以使自研网关组件更专注于实现网络层级别的功能,提高性能和效率。
- 端口处理由其他组件负责:不考虑端口可以提供更大的灵活性和扩展性。通过将端口的管理交给上层的应用层或服务层处理,可以更容易地实现新的应用程序或服务的添加和更改。这样可以避免对网关进行频繁的配置和调整,而只需在应用层或服务层进行相应的配置即可。在自研网关组件之外,通常还有其他组件负责处理端口相关的功能。例如,防火墙或负载均衡器等组件可能会负责端口的过滤、映射或负载均衡等任务。将端口处理交给专门的组件负责可以更好地分工和解耦,使各组件能够独立进行功能开发和优化。
需要注意的是,具体的系统设计和实现可能会因组织和场景的不同而有所差异。在某些情况下,自研网关组件可能需要考虑端口分配,以满足特定的需求。因此,在设计自研网关组件时,需要综合考虑系统的特点、功能需求和性能要求,以确定是否需要考虑端口分配。