2019年我们经历了一整年的各种迁移,其中包括了一项 RPC
框架的切换。以前我们用的 HSF RPC
框架,它是来自于阿里巴巴,经过了多年的 双11
高并发的洗礼,高性能这块儿毫无疑问没有任何的问题,而且它还同时支持 TCP
与 HTTP
的方式,唯一不太好的就是它不开源,如果出现问题定位起来确实有一些问题与风险。
所以,我们为了拥抱开源,全部采用 SpringCloud
,系统与系统之间调用是通过 FeignClient
的方式来调用的,但是由于底层的部分系统由于时间、人力、历史等原因,无法在短时间内都像我们一样能积极响应。所以就出现了 SpringCloud
与HSF服务同时存在的情况,为了大家再编码过程中都能像本地调用( TCP
, FeignClient
),所以就写了一个代理工具。
如果是上面的方式,我们还是能感受到每次都是通过 HttpClient
等方式发起一次 Http
请求,写代码时候的体验不是很好。
为了解决这个问题,那么我们的任务就是来写一个这个代理封装。
我们参考一下FeignClient的功能一个解析过程,如图:
那我们不用说写那么完善,我们的第一个目标就是实现扫描 → 代理 → 发送请求。
因为HSF的参数与标准的Http方式不太一致,所以在发起Http请求的时候,需要特殊的构造一下报文的格式
curl -d "ArgsTypes=[/"com.cyblogs..QueryConfigReq/"]&ArgsObjects=[{/"relationCode/":/"ABCD/"}]" http://127.0.0.1:8083/com.cyblogs.api.ConfigServiceV2Api:1.0.0/queryNewConfig
SpringBoot
总入口,打开 @EnableHsfClients
注解
@SpringBootApplication @EnableHsfClients(basePackages = "com.cyblogs.client.hsf") public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
这里定义好需要扫描的包,具体的类等
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import({ HsfClientsRegistrar.class }) public @interface EnableHsfClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] clients() default {}; }
利用 Spirng
的 ImportBeanDefinitionRegistrar
来进行自动注入生成Bean。
public class HsfClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registerHsfClient(importingClassMetadata, registry); } public void registerHsfClient(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableHsfClients.class.getName()); AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(HsfClient.class); final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else { basePackages = new HashSet<>(); for (Class<?> clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); } } for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage); for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@HsfClient can only be specified on an interface"); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes(HsfClient.class.getCanonicalName()); registerHsfClient(registry, annotationMetadata, attributes); } } } } protected ClassPathScanningCandidateComponentProvider getScanner() { return new ClassPathScanningCandidateComponentProvider(false) { @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { if (beanDefinition.getMetadata().isIndependent()) { if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().getInterfaceNames().length == 1 && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) { try { Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), HsfClientsRegistrar.this.classLoader); return !target.isAnnotation(); } catch (Exception ex) { log.error("Could not load target class: " + beanDefinition.getMetadata().getClassName(), ex); } } return true; } return false; } }; } protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) { Map<String, Object> attributes = importingClassMetadata .getAnnotationAttributes(EnableHsfClients.class.getCanonicalName()); Set<String> basePackages = new HashSet<>(); for (String pkg : (String[]) attributes.get("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : (String[]) attributes.get("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName())); } return basePackages; } private void registerHsfClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { String className = annotationMetadata.getClassName(); BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(HsfClientFactoryBean.class); String version = resolve((String) attributes.get("version")); String interfaceName = resolve((String) attributes.get("interfaceName")); if (interfaceName.length() == 0) { interfaceName = className; } definition.addPropertyValue("url", String.format(FORMAT, getUrl(attributes), interfaceName, version)); definition.addPropertyValue("type", className); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME); String alias = interfaceName + "HsfClient"; AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); beanDefinition.setPrimary(true); BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias }); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); } private String getUrl(Map<String, Object> attributes) { String url = resolve((String) attributes.get("url")); boolean secure = false; Object securePlaceHolder = attributes.get("secure"); if (securePlaceHolder instanceof Boolean) { secure = ((Boolean) securePlaceHolder).booleanValue(); } else { Boolean.parseBoolean(resolve((String) attributes.get("secure"))); } String protocol = secure ? "https" : "http"; if (!url.contains("://")) { url = protocol + "://" + url; } if (url.endsWith("/")) {//避免设置的url为'schema:ip:port/'格式 url = url.substring(0, url.length() - 1); } try { new URL(url); } catch (MalformedURLException e) { throw new IllegalArgumentException(url + " is malformed", e); } return url; } }
HsfClientFactoryBean
定义
@Setter @Getter public class HsfClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware { private ApplicationContext applicationContext; private Class<?> type; private String url; private RestTemplate restTemplate; @Override public void afterPropertiesSet() throws Exception { Assert.hasText(url, "url must be set"); Assert.notNull(type, "type must be set"); if (restTemplate == null) { restTemplate = new RestTemplate(); restTemplate.getMessageConverters().clear(); restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));//write application/x-www-form-urlencoded request restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter());//read and write application/json } } public Object getObject() throws Exception { Map<Method, HsfMethodHandler> methodToHandler = new LinkedHashMap<Method, HsfMethodHandler>(); for (Method method : type.getMethods()) { if (method.getDeclaringClass() == Object.class) { continue; } else if (isDefaultMethod(method)) { continue;//TODO 暂时忽略 } else { methodToHandler.put(method, new HsfMethodHandler(restTemplate, type, method, url)); } } InvocationHandler handler = new HsfInvocationHandler(methodToHandler); return Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { type }, handler); } @Override public Class<?> getObjectType() { return type; } @Override public boolean isSingleton() { return true; } private boolean isDefaultMethod(Method method) { final int SYNTHETIC = 0x00001000; return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC) && method.getDeclaringClass().isInterface(); } }
代理类的实现
public class HsfInvocationHandler implements InvocationHandler { private final Map<Method, HsfMethodHandler> handlers; public HsfInvocationHandler(Map<Method, HsfMethodHandler> handlers) { this.handlers = handlers; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("equals".equals(method.getName())) { try { Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; return equals(otherHandler); } catch (IllegalArgumentException e) { log.error(e.getMessage(), e); return false; } } else if ("hashCode".equals(method.getName())) { return hashCode(); } else if ("toString".equals(method.getName())) { return toString(); } return handlers.get(method).invoke(args); } @Override public boolean equals(Object obj) { if (obj instanceof HsfInvocationHandler) { Map<Method, HsfMethodHandler> other = ((HsfInvocationHandler) obj).handlers; return other.equals(handlers); } return false; } @Override public int hashCode() { return handlers.hashCode(); } @Override public String toString() { return handlers.toString(); } }
最后就是就是 HsfMethodHandler
的一个具体实现,包括上面所提到的 Request
参数的构造,一个 invoke
方法的调用。
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。