把网关在注册和拉取时的异常抛出来,交给容器管理做关闭动作,以及处理网关的服务关闭。
设计
在 api-gateway-assist 启动的过程中,我们希望它所发生的一些动作,包括启动中的异常、拉取接口信息的失败以及容器关闭后优雅的处理网关通信的关闭。
- 这里需要把网关的注册和拉取配置操作,放到 ApplicationContextAware 接口对应的 setApplicationContext 方法中。这样可以在注册服务以及拉取配置的过程中出现失败情况时,则直接抛异常关闭容器。
- 另外这里还需要做一个容器关闭的监听动作 ApplicationListener\ 容器关闭时则把网关中的通信模块下的 Netty 服务也一起关闭掉。
实现
感知容器以及监听关闭事件
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
| package cn.ray.gateway.assist.applicaton;
import cn.ray.gateway.assist.config.GatewayServiceProperties; import cn.ray.gateway.assist.domain.model.aggregates.ApplicationSystemRichInfo; import cn.ray.gateway.assist.domain.model.vo.ApplicationInterfaceMethodVO; import cn.ray.gateway.assist.domain.model.vo.ApplicationInterfaceVO; import cn.ray.gateway.assist.domain.model.vo.ApplicationSystemVO; import cn.ray.gateway.assist.domain.service.RegisterGatewayService; import cn.ray.gateway.core.mapping.HttpRequestType; import cn.ray.gateway.core.mapping.HttpStatement; import cn.ray.gateway.core.session.Configuration; import com.alibaba.fastjson.JSON; import io.netty.channel.Channel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import java.util.List;
public class GatewayAssistantApplication implements ApplicationContextAware, ApplicationListener<ContextClosedEvent> {
private Logger logger = LoggerFactory.getLogger(GatewayAssistantApplication.class);
private GatewayServiceProperties properties;
private RegisterGatewayService registerGatewayService;
private Configuration configuration;
private Channel gatewaySocketServerChannel;
public GatewayAssistantApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService, Configuration configuration, Channel gatewaySocketServerChannel) { this.properties = properties; this.registerGatewayService = registerGatewayService; this.configuration = configuration; this.gatewaySocketServerChannel = gatewaySocketServerChannel; }
@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { try { registerGatewayService.doRegister(properties.getAddress(), properties.getGroupId(), properties.getGatewayId(), properties.getGatewayName(), properties.getGatewayAddress());
ApplicationSystemRichInfo applicationSystemRichInfo = registerGatewayService.pullApplicationSystemRichInfo(properties.getAddress(), properties.getGatewayId());
List<ApplicationSystemVO> applicationSystemVOList = applicationSystemRichInfo.getApplicationSystemVOList(); for (ApplicationSystemVO system : applicationSystemVOList) { List<ApplicationInterfaceVO> interfaceList = system.getInterfaceList(); for (ApplicationInterfaceVO interfaceVO : interfaceList) { configuration.registerConfig(system.getSystemId(), system.getSystemRegistry(), interfaceVO.getInterfaceId(), interfaceVO.getInterfaceVersion()); List<ApplicationInterfaceMethodVO> methodList = interfaceVO.getMethodList(); for (ApplicationInterfaceMethodVO method : methodList) { HttpStatement httpStatement = new HttpStatement( system.getSystemId(), interfaceVO.getInterfaceId(), method.getMethodId(), method.getUri(), HttpRequestType.valueOf(method.getHttpCommandType()), method.getParameterType(), method.isAuth()); configuration.addMapper(httpStatement); logger.info("网关服务注册映射 系统:{} 接口:{} 方法:{}", system.getSystemId(), interfaceVO.getInterfaceId(), method.getMethodId()); } } } } catch (Exception e) { logger.error("网关服务启动失败,停止服务。{}", e.getMessage(), e); throw e; } }
@Override public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { try { if (gatewaySocketServerChannel.isActive()) { logger.info("应用容器关闭,Api网关服务关闭。localAddress:{}", gatewaySocketServerChannel.localAddress()); gatewaySocketServerChannel.close(); } } catch (Exception e) { logger.error("应用容器关闭,Api网关服务关闭失败", e); } }
}
|
这是一个Java类,名为GatewayAssistantApplication
,用于将网关应用与Spring框架连接起来,并进行网关注册和接口拉取的操作。
该类实现了ApplicationContextAware
接口和ApplicationListener<ContextClosedEvent>
接口,以便获取应用程序上下文和监听应用程序关闭事件。
以下是该类的主要方法和功能:
setApplicationContext(ApplicationContext applicationContext)
: 实现了ApplicationContextAware
接口的方法,用于在应用程序上下文加载完毕后执行操作。在该方法中,首先调用registerGatewayService.doRegister()
方法进行网关服务注册,然后调用registerGatewayService.pullApplicationSystemRichInfo()
方法拉取网关配置信息,并根据配置信息注册系统服务接口信息。
onApplicationEvent(ContextClosedEvent contextClosedEvent)
: 实现了ApplicationListener<ContextClosedEvent>
接口的方法,用于监听应用程序关闭事件。在该方法中,检查网关Socket服务通道是否处于活动状态,如果是,则关闭该通道。
该类的构造函数接受四个参数:
properties
: 网关服务的配置属性,包括地址、组ID、网关ID、网关名称和网关地址等信息。
registerGatewayService
: 注册网关服务的对象,用于执行网关注册和接口拉取操作。
configuration
: 网关的配置对象,用于注册配置信息和映射关系。
gatewaySocketServerChannel
: 网关Socket服务的通道。
整体来说,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 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
| package cn.ray.gateway.assist.domain.service;
import cn.hutool.http.HttpUtil; import cn.ray.gateway.assist.GatewayException; import cn.ray.gateway.assist.common.Result; import cn.ray.gateway.assist.domain.model.aggregates.ApplicationSystemRichInfo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; 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 = null; try { resultStr = HttpUtil.post(address + "/wg/admin/config/registerGateway", paramMap, 2000); } catch (Exception e) { logger.error("网关服务注册异常,链接资源不可用:{}", address + "/wg/admin/config/registerGateway"); throw e; } Result<Boolean> result = JSON.parseObject(resultStr, new TypeReference<Result<Boolean>>(){}); 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) { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("gatewayId", gatewayId); String resultStr = null; try { resultStr = HttpUtil.post(address + "/wg/admin/config/queryApplicationSystemRichInfo", paramMap, 2000); } catch (Exception e) { logger.error("网关服务拉取异常,链接资源不可用:{}", address + "/wg/admin/config/queryApplicationSystemRichInfo"); throw e; } Result<ApplicationSystemRichInfo> result = JSON.parseObject(resultStr, new TypeReference<Result<ApplicationSystemRichInfo>>(){}); logger.info("从网关中心拉取应用服务和接口的配置信息到本地完成注册。gatewayId:{}", gatewayId); if (!"0000".equals(result.getCode())) throw new GatewayException("从网关中心拉取应用服务和接口的配置信息到本地完成注册异常 [gatewayId:" + gatewayId + "]"); return result.getData(); } }
|
RegisterGatewayService 类所提供的服务需要从注册中心的接口拉取,如果注册中心接口暂时不可用,那么则需要抛出异常。这个异常就是给容器启动过程中的一个通知,以此来决定是否关闭服务。
思考
ApplicationContextAware
在Spring框架中,ApplicationContextAware
接口是一个回调接口,用于在Bean实例化后,自动获取应用程序上下文(ApplicationContext)的引用。通过实现该接口,可以在Bean中访问应用程序上下文以获取其他Bean或执行特定的操作。
当一个实现了ApplicationContextAware
接口的Bean被Spring容器创建时,Spring会自动调用该Bean的setApplicationContext()
方法,并将当前的应用程序上下文作为参数传递进去。通过这个回调方法,Bean就可以持有对应用程序上下文的引用,并可以使用它来访问Spring容器中的其他Bean或执行与应用程序上下文相关的操作。
通过ApplicationContextAware
接口,Bean可以实现以下功能:
- 访问其他Bean:可以通过应用程序上下文引用访问容器中的其他Bean,获取它们的实例并进行相应的操作,例如调用它们的方法、获取属性值等。
- 获取应用程序上下文信息:可以获取应用程序上下文的元数据和属性信息,如应用程序的环境配置、配置文件路径、Bean的定义信息等。
- 执行特定的操作:可以在Bean初始化完成后执行一些特定的操作,如注册监听器、添加自定义的Bean后置处理器等。
需要注意的是,使用ApplicationContextAware
接口需要谨慎,因为过度使用该接口可能导致代码与Spring容器的耦合增加。应该优先考虑使用依赖注入(Dependency Injection)来获取所需的Bean引用,而不是直接依赖于应用程序上下文。
大体流程
- 启动 zookeeper 和真正的服务提供者 api-gateway-test,暴露RPC服务
- 启动网关注册中心 api-gateway-center。
- 启动 api-gateway-engine ,而因为其中内嵌了 assist 和 core 网关通信组件,所以整个网关引擎工程此时可以充当一个具有自动配置(服务拉取,注册,初始化)的算力节点。(相当于 assist+core 的一个胖jar)
- 当用户访问网关监听地址时,HTTP请求会打到 engine 中的 Netty 服务端线程,触发监听事件,并根据 uri 从缓存中取出对应的 Dubbo 配置,向 RPC 服务提供者请求并响应给用户。
当网关服务启动后,如果前端操作增添接口,后台拿到接口数据需要
- 向注册中心 center(相当于数据库)的注册接口发送请求,完成数据库中接口信息的写入。
- 获取到 engine 中的交给Spring管理的 Contguration Bean 实例(@Autowired),然后通过手动式编程将当前接口信息加入配置缓存,完成注册。