以封装 api-gateway-core 为目的,搭建 SpringBoot Starter 组件,用于服务注册发现的相关内容处理。
设计
本章内容属于注册中心所需提供的接口,因为在服务发现模块中需要从网关注册中心拉取服务配置。这个服务配置其实就是各个 RPC 服务配置【系统、接口、方法】把这些信息拉取下来,注册到网关算力中,完成 RPC 映射的过程。
首先通过 gatewaydistribution 表,把网关和 RPC 应用服务关联起来,方便知道哪个网关算力处理哪些RPC映射管理。有了这个映射关系后,就可以把对应的 application_interface、application interface method、application systemn 三个表维护应用的配置信息关联起来了。
实现
【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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
| package cn.ray.gateway.center.infrastructure.repository;
import cn.ray.gateway.center.domain.manage.model.vo.*; import cn.ray.gateway.center.domain.manage.repository.IConfigManageRepository; import cn.ray.gateway.center.infrastructure.dao.*; import cn.ray.gateway.center.infrastructure.pojo.*; 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;
@Resource private IGatewayDistributionDao gatewayDistributionDao;
@Resource private IApplicationSystemDao applicationSystemDao;
@Resource private IApplicationInterfaceDao applicationInterfaceDao;
@Resource private IApplicationInterfaceMethodDao applicationInterfaceMethodDao;
@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); }
@Override public List<String> queryGatewayDistributionSystemIdList(String gatewayId) { return gatewayDistributionDao.queryGatewayDistributionSystemIdList(gatewayId); }
@Override public List<ApplicationSystemVO> queryApplicationSystemList(List<String> systemIdList) { List<ApplicationSystem> applicationSystemList = applicationSystemDao.queryApplicationSystemList(systemIdList); List<ApplicationSystemVO> applicationSystemVOList = new ArrayList<>(); for (ApplicationSystem applicationSystem : applicationSystemList) { ApplicationSystemVO applicationSystemVO = new ApplicationSystemVO(); applicationSystemVO.setSystemId(applicationSystem.getSystemId()); applicationSystemVO.setSystemName(applicationSystem.getSystemName()); applicationSystemVO.setSystemRegistry(applicationSystem.getSystemRegistry()); applicationSystemVO.setSystemType(applicationSystem.getSystemType()); applicationSystemVOList.add(applicationSystemVO); } return applicationSystemVOList; }
@Override public List<ApplicationInterfaceVO> queryApplicationInterfaceList(String systemId) { List<ApplicationInterface> applicationInterfaceList = applicationInterfaceDao.queryApplicationInterfaceList(systemId); List<ApplicationInterfaceVO> applicationInterfaceVOList = new ArrayList<>(); for (ApplicationInterface applicationInterface : applicationInterfaceList) { ApplicationInterfaceVO applicationInterfaceVO = new ApplicationInterfaceVO(); applicationInterfaceVO.setSystemId(applicationInterface.getSystemId()); applicationInterfaceVO.setInterfaceId(applicationInterface.getInterfaceId()); applicationInterfaceVO.setInterfaceName(applicationInterface.getInterfaceName()); applicationInterfaceVO.setInterfaceVersion(applicationInterface.getInterfaceVersion()); applicationInterfaceVOList.add(applicationInterfaceVO); } return applicationInterfaceVOList; }
@Override public List<ApplicationInterfaceMethodVO> queryApplicationInterfaceMethodList(String systemId, String interfaceId) { ApplicationInterfaceMethod req = new ApplicationInterfaceMethod(); req.setSystemId(systemId); req.setInterfaceId(interfaceId); List<ApplicationInterfaceMethod> applicationInterfaceMethods = applicationInterfaceMethodDao.queryApplicationInterfaceMethodList(req); List<ApplicationInterfaceMethodVO> applicationInterfaceMethodVOList = new ArrayList<>(applicationInterfaceMethods.size()); for (ApplicationInterfaceMethod applicationInterfaceMethod : applicationInterfaceMethods) { ApplicationInterfaceMethodVO applicationInterfaceMethodVO = new ApplicationInterfaceMethodVO(); applicationInterfaceMethodVO.setSystemId(applicationInterfaceMethod.getSystemId()); applicationInterfaceMethodVO.setInterfaceId(applicationInterfaceMethod.getInterfaceId()); applicationInterfaceMethodVO.setMethodId(applicationInterfaceMethod.getMethodId()); applicationInterfaceMethodVO.setMethodName(applicationInterfaceMethod.getMethodName()); applicationInterfaceMethodVO.setParameterType(applicationInterfaceMethod.getParameterType()); applicationInterfaceMethodVO.setUri(applicationInterfaceMethod.getUri()); applicationInterfaceMethodVO.setHttpCommandType(applicationInterfaceMethod.getHttpCommandType()); applicationInterfaceMethodVO.setAuth(applicationInterfaceMethod.getAuth()); applicationInterfaceMethodVOList.add(applicationInterfaceMethodVO); } return applicationInterfaceMethodVOList; } }
|
【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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package cn.ray.gateway.center.domain.manage.service;
import cn.ray.gateway.center.application.IConfigManageService; import cn.ray.gateway.center.domain.manage.model.aggregates.ApplicationSystemRichInfo; import cn.ray.gateway.center.domain.manage.model.vo.*; 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); } }
@Override public ApplicationSystemRichInfo queryApplicationSystemRichInfo(String gatewayId) { List<String> systemIdList = configManageRepository.queryGatewayDistributionSystemIdList(gatewayId); if (null == systemIdList || systemIdList.size() == 0) { return null; } List<ApplicationSystemVO> applicationSystemVOList = configManageRepository.queryApplicationSystemList(systemIdList); for (ApplicationSystemVO applicationSystemVO : applicationSystemVOList) { List<ApplicationInterfaceVO> applicationInterfaceVOList = configManageRepository.queryApplicationInterfaceList(applicationSystemVO.getSystemId()); for (ApplicationInterfaceVO applicationInterfaceVO : applicationInterfaceVOList) { List<ApplicationInterfaceMethodVO> applicationInterfaceMethodVOS = configManageRepository.queryApplicationInterfaceMethodList(applicationInterfaceVO.getSystemId(), applicationInterfaceVO.getInterfaceId()); applicationInterfaceVO.setMethodList(applicationInterfaceMethodVOS); } applicationSystemVO.setInterfaceList(applicationInterfaceVOList); } return new ApplicationSystemRichInfo(gatewayId, applicationSystemVOList); } }
|
- 将 gateway_id 作为参数,获取表 gateway_distribution 中的网关和 RPC 应用服务绑定关系
- 从绑定信息中获取 system_id
- 根据 system_id 获取 RPC 应用服务信息、应用服务的接口信息
- 根据 systemid 和 interface id 获取接口方法信息
【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 61 62 63 64 65 66 67 68 69 70 71 72 73
| package cn.ray.gateway.center.interfaces;
import cn.ray.gateway.center.application.IConfigManageService; import cn.ray.gateway.center.domain.manage.model.aggregates.ApplicationSystemRichInfo; 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); } }
@PostMapping(value = "queryApplicationSystemRichInfo", produces = "application/json;charset=utf-8") public Result<ApplicationSystemRichInfo> queryApplicationSystemRichInfo(@RequestParam String gatewayId) { try { logger.info("查询分配到网关下的待注册系统信息(系统、接口、方法) gatewayId:{}", gatewayId); ApplicationSystemRichInfo applicationSystemRichInfo = configManageService.queryApplicationSystemRichInfo(gatewayId); return new Result<>(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getInfo(), applicationSystemRichInfo); } catch (Exception e) { logger.error("查询分配到网关下的待注册系统信息(系统、接口、方法)异常 gatewayId:{}", gatewayId, e); return new Result<>(ResponseCode.UN_ERROR.getCode(), e.getMessage(), null); } } }
|
测试
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 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| package cn.ray.gateway.center.test;
import cn.ray.gateway.center.application.IConfigManageService; import cn.ray.gateway.center.application.IRegisterManageService; import cn.ray.gateway.center.domain.manage.model.aggregates.ApplicationSystemRichInfo; import cn.ray.gateway.center.domain.manage.model.vo.GatewayServerVO; import cn.ray.gateway.center.domain.register.model.vo.ApplicationInterfaceMethodVO; import cn.ray.gateway.center.domain.register.model.vo.ApplicationInterfaceVO; import cn.ray.gateway.center.domain.register.model.vo.ApplicationSystemVO; 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;
@Resource private IRegisterManageService registerManageService;
@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"); }
@Test public void test_registerApplication() { ApplicationSystemVO applicationSystemVO = new ApplicationSystemVO(); applicationSystemVO.setSystemId("api-gateway-test"); applicationSystemVO.setSystemName("网关测试系统"); applicationSystemVO.setSystemType("RPC"); applicationSystemVO.setSystemRegistry("127.0.0.1"); registerManageService.registerApplication(applicationSystemVO); }
@Test public void test_registerApplicationInterface() { ApplicationInterfaceVO applicationInterfaceVO = new ApplicationInterfaceVO(); applicationInterfaceVO.setSystemId("api-gateway-test"); applicationInterfaceVO.setInterfaceId("cn.ray.gateway.rpc.IActivityBooth"); applicationInterfaceVO.setInterfaceName("活动平台"); applicationInterfaceVO.setInterfaceVersion("v1.0.0"); registerManageService.registerApplicationInterface(applicationInterfaceVO); }
@Test public void test_registerApplicationInterfaceMethod() { ApplicationInterfaceMethodVO applicationInterfaceVO01 = new ApplicationInterfaceMethodVO(); applicationInterfaceVO01.setSystemId("api-gateway-test"); applicationInterfaceVO01.setInterfaceId("cn.ray.gateway.rpc.IActivityBooth"); applicationInterfaceVO01.setMethodId("sayHi"); applicationInterfaceVO01.setMethodName("测试方法"); applicationInterfaceVO01.setParameterType("java.lang.String"); applicationInterfaceVO01.setUri("/wg/activity/sayHi"); applicationInterfaceVO01.setHttpCommandType("GET"); applicationInterfaceVO01.setAuth(0); registerManageService.registerApplicationInterfaceMethod(applicationInterfaceVO01);
ApplicationInterfaceMethodVO applicationInterfaceVO02 = new ApplicationInterfaceMethodVO(); applicationInterfaceVO02.setSystemId("api-gateway-test"); applicationInterfaceVO02.setInterfaceId("cn.ray.gateway.rpc.IActivityBooth"); applicationInterfaceVO02.setMethodId("insert"); applicationInterfaceVO02.setMethodName("插入方法"); applicationInterfaceVO02.setParameterType("cn.ray.gateway.rpc.dto.XReq"); applicationInterfaceVO02.setUri("/wg/activity/insert"); applicationInterfaceVO02.setHttpCommandType("POST"); applicationInterfaceVO02.setAuth(1); registerManageService.registerApplicationInterfaceMethod(applicationInterfaceVO02); }
@Test public void test_queryApplicationSystemRichInfo(){ ApplicationSystemRichInfo result = configManageService.queryApplicationSystemRichInfo("api-gateway-g4"); logger.info("测试结果:{}", JSON.toJSONString(result)); } }
|
测试结果
思考
网关注册中心除了一些核心的权重分配算法以外,其实大部分都在完成数据的接收写入库中和从库中读取配置到各个组件中进行使用。
在这段代码中,每次循环都去查询数据库,效率比较低,可以通过批量查询和适当的数据结构优化,减少查询次数。
1 2 3 4 5 6 7 8
| for (ApplicationSystemVO applicationSystemVO : applicationSystemVOList) { List<ApplicationInterfaceVO> applicationInterfaceVOList = configManageRepository.queryApplicationInterfaceList(applicationSystemVO.getSystemId()); for (ApplicationInterfaceVO applicationInterfaceVO : applicationInterfaceVOList) { List<ApplicationInterfaceMethodVO> applicationInterfaceMethodVOS = configManageRepository.queryApplicationInterfaceMethodList(applicationInterfaceVO.getSystemId(), applicationInterfaceVO.getInterfaceId()); applicationInterfaceVO.setMethodList(applicationInterfaceMethodVOS); } applicationSystemVO.setInterfaceList(applicationInterfaceVOList); }
|
以下是一些优化的方法:
批量查询接口和方法信息:首先,收集需要查询的系统ID和接口ID,并使用批量查询语句一次性获取所有接口和方法信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| List<String> systemIdList = new ArrayList<>(); List<String> interfaceIdList = new ArrayList<>();
for (ApplicationSystemVO applicationSystemVO : applicationSystemVOList) { systemIdList.add(applicationSystemVO.getSystemId());
for (ApplicationInterfaceVO applicationInterfaceVO : applicationSystemVO.getInterfaceList()) { interfaceIdList.add(applicationInterfaceVO.getInterfaceId()); } }
List<ApplicationInterfaceVO> applicationInterfaceVOList = configManageRepository.queryApplicationInterfaceListBySystemIds(systemIdList);
List<ApplicationInterfaceMethodVO> applicationInterfaceMethodVOList = configManageRepository.queryApplicationInterfaceMethodListByInterfaceIds(systemIdList, interfaceIdList);
|
构建数据结构:根据查询结果构建合适的数据结构,以便快速查找和关联接口和方法信息。
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
| Map<String, ApplicationInterfaceVO> interfaceMap = new HashMap<>(); for (ApplicationInterfaceVO applicationInterfaceVO : applicationInterfaceVOList) { interfaceMap.put(applicationInterfaceVO.getInterfaceId(), applicationInterfaceVO); }
for (ApplicationInterfaceMethodVO applicationInterfaceMethodVO : applicationInterfaceMethodVOList) { String interfaceId = applicationInterfaceMethodVO.getInterfaceId(); ApplicationInterfaceVO interfaceVO = interfaceMap.get(interfaceId);
if (interfaceVO != null) { interfaceVO.getMethodList().add(applicationInterfaceMethodVO); } }
for (ApplicationSystemVO applicationSystemVO : applicationSystemVOList) { for (ApplicationInterfaceVO applicationInterfaceVO : applicationSystemVO.getInterfaceList()) { ApplicationInterfaceVO interfaceVO = interfaceMap.get(applicationInterfaceVO.getInterfaceId()); if (interfaceVO != null) { applicationInterfaceVO.setMethodList(interfaceVO.getMethodList()); } } }
|
通过上述优化,首先进行批量查询获取所有接口和方法信息,然后根据查询结果构建数据结构,将接口和方法信息关联起来。这样可以避免多次循环查询,提高查询效率。
请注意,上述代码仅提供了一种优化思路,具体实现可能需要根据业务需求和数据结构进行适当的调整。
不要总想着把问题留到最后
很多时候我会把编程和生活类比,会发现编程的问题其实也是抽象后的生活问题。就像我们并不能在生活总是想着把问题放到最后处理,人无远虑必有近忧,总是把问题放到最后,最后也就处理不过来了。
对于编程开发又何尝不是,如果你不停的从这复制点代码,从那粘贴点逻辑,想当然的编写着意想能一把梭哈的逻辑。那么最后就会是一片片的报错。即使在好用的单元测试在这个时候也没有太大作用,你只能花费大量的时候去梳理,你可能已经忘记了一天前写的代码逻辑。
问题越小才越容易被理解和处理。
生活如此,编程亦是如此。