提供应用服务注册的组件,采集 RPC 服务启动时已经配置了标记注解的对象,摘取出类、接口、方法,用于后续注册到网关中心。
设计
网关的注册中心维护着网关和RPC接口的信息,用于把RPC接口分配到网关算力上使用。那么前面的章节已经实现了网关算力的自动注册,同样 RPC 接口也需要自动注册,否则都是人工手动维护这个成本还是非常大的。
应用注册组件的目的就是提供给 RPC 接口生产的服务使用,通过 RPC 生产者服务引入 SDK 组件,并使用注解配置的方式作为接口标记。当服务启动的时候,SDK组件会采集这些被标记了注解的接口和方法,把这些信息收集后向网关注册中心注册,而 assist 就可以将其注入通信组件的 Configuration 配置中,后续通信组件对其进行一系列处理后,返回泛化调用的结果。
实现
自定义注解
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
| package cn.ray.gateway.sdk.annotation;
import java.lang.annotation.*;
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface ApiProducerClazz {
String interfaceName() default "";
String interfaceVersion() default "";
}
|
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
| package cn.ray.gateway.sdk.annotation;
import java.lang.annotation.*;
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface ApiProducerMethod {
String methodName() default "";
String uri() default "";
String HttpRequestType() default "GET";
int auth() default 0;
}
|
应用配置
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
| package cn.ray.gateway.sdk.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("api-gateway-sdk") public class GatewaySDKServiceProperties {
private String address; private String systemId; private String systemName; private String systemRegistry;
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
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 getSystemRegistry() { return systemRegistry; }
public void setSystemRegistry(String systemRegistry) { this.systemRegistry = systemRegistry; } }
|
注册中心一般都是固定的,如果统一系统用多个注册中心,那么相互之间就不太好通信了,系统标识和名称一般是从统一的上线系统中获取,从对应的配置中获取。这里方便测试,也是为了配置的灵活性,增加了从配置文件中读取相应信息的操作。
信息提取
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
| package cn.ray.gateway.sdk.application;
import cn.ray.gateway.sdk.annotation.ApiProducerClazz; import cn.ray.gateway.sdk.annotation.ApiProducerMethod; import cn.ray.gateway.sdk.config.GatewaySDKServiceProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.Method;
public class GatewaySDKApplication implements BeanPostProcessor {
private Logger logger = LoggerFactory.getLogger(GatewaySDKApplication.class);
private GatewaySDKServiceProperties properties;
public GatewaySDKApplication(GatewaySDKServiceProperties properties) { this.properties = properties; }
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { ApiProducerClazz apiProducerClazz = bean.getClass().getAnnotation(ApiProducerClazz.class); if (null == apiProducerClazz) { return bean; } logger.info("\n应用注册:系统信息 \nsystemId: {} \nsystemName: {} \nsystemType: {} \nsystemRegistry: {}", properties.getSystemId(), properties.getSystemName(), "RPC", properties.getSystemRegistry()); logger.info("\n应用注册:接口信息 \nsystemId: {} \ninterfaceId: {} \ninterfaceName: {} \ninterfaceVersion: {}", properties.getSystemId(), bean.getClass().getName(), apiProducerClazz.interfaceName(), apiProducerClazz.interfaceVersion()); Method[] methods = bean.getClass().getMethods(); for (Method method : methods) { ApiProducerMethod apiProducerMethod = method.getAnnotation(ApiProducerMethod.class); if (null == apiProducerMethod) { continue; } Class<?>[] parameterTypes = method.getParameterTypes(); StringBuilder parameters = new StringBuilder(); for (Class<?> clazz : parameterTypes) { parameters.append(clazz.getName()).append(","); } String parameterType = parameters.toString().substring(0,parameters.toString().lastIndexOf(",")); logger.info("\n应用注册:方法信息 \nsystemId: {} \ninterfaceId: {} \nmethodId: {} \nmethodName: {} \nparameterType: {} \nuri: {} \nhttpCommandType: {} \nauth: {}", properties.getSystemId(), bean.getClass().getName(), method.getName(), apiProducerMethod.methodName(), parameterType, apiProducerMethod.uri(), apiProducerMethod.HttpRequestType(), apiProducerMethod.auth()); } return bean; } }
|
这里实现 BeanPostProcessor 的 postProcessAfterInitialization 方法,在 Bean 实例化之后对其进行处理,判断是否类带有注解 ApiProducerClazz 以及 方法是否带有注解 ApiProducerMethod,并对其进行解析提取对应的系统、接口和方法信息。这里只做了简单的输出日志验证。
测试
新建 sdk-test 工程,引入 sdk-01 ,同时在 application.yml 中配置对应信息。
用户服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package cn.ray.gateway.sdk.interfaces;
import cn.ray.gateway.sdk.annotation.ApiProducerClazz; import cn.ray.gateway.sdk.annotation.ApiProducerMethod; import org.springframework.stereotype.Service;
@ApiProducerClazz(interfaceName = "用户服务", interfaceVersion = "1.0.0") @Service public class UserService {
@ApiProducerMethod(methodName = "探测", uri = "/wg/user/hi", HttpRequestType = "POST", auth = 1) public String hi(String str) { return "hi " + str + " by api-gateway-sdk"; }
}
|
application.yml
1 2 3 4 5 6 7 8
| server: port: 8004
api-gateway-sdk: address: http://localhost:80 systemId: api-gateway-sdk-test systemName: 网关SDK测试工程 systemRegistry: zookeeper://localhost:2181
|
启动测试
在日常使用网关服务时,我们通常也是在本地引用一个网关提供好的 SDK, 引入后简单配置就可以完成接口到网关的注册。如果再有一些其他的需求,则可以到网关的控制后台页面上进行修改。