背景 2019年我们经历了一整年的各种迁移,其中包括了一项RPC
框架的切换。以前我们用的HSF RPC
框架,它是来自于阿里巴巴,经过了多年的双11
高并发的洗礼,高性能这块儿毫无疑问没有任何的问题,而且它还同时支持TCP
与HTTP
的方式,唯一不太好的就是它不开源,如果出现问题定位起来确实有一些问题与风险。
所以,我们为了拥抱开源,全部采用SpringCloud
,系统与系统之间调用是通过FeignClient
的方式来调用的,但是由于底层的部分系统由于时间、人力、历史等原因,无法在短时间内都像我们一样能积极响应。所以就出现了SpringCloud
与HSF服务同时存在的情况,为了大家再编码过程中都能像本地调用(TCP
,FeignClient
),所以就写了一个代理工具。
交互图
如果是上面的方式,我们还是能感受到每次都是通过HttpClient
等方式发起一次Http
请求,写代码时候的体验不是很好。
为了解决这个问题,那么我们的任务就是来写一个这个代理封装。
分析功能点 了解一下FeignClient 我们参考一下FeignClient的功能一个解析过程,如图:
生成动态代理类
解析出等的MethodHandler
动态生成Request
Encoder
拦截器处理
日志处理
重试机制
代理需要考虑什么?
那我们不用说写那么完善,我们的第一个目标就是实现扫描 → 代理 → 发送请求。
因为HSF的参数与标准的Http方式不太一致,所以在发起Http请求的时候,需要特殊的构造一下报文的格式
1 2 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
注解
1 2 3 4 5 6 7 8 @SpringBootApplication @EnableHsfClients(basePackages = "com.cyblogs.client.hsf") public class App { public static void main (String[] args) { SpringApplication.run(App.class, args); } }
这里定义好需要扫描的包,具体的类等
1 2 3 4 5 6 7 8 9 10 11 12 @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。
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 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) { 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 = url.substring(0 , url.length() - 1 ); } try { new URL (url); } catch (MalformedURLException e) { throw new IllegalArgumentException (url + " is malformed" , e); } return url; } }
HsfClientFactoryBean
定义
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 @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" ))); restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter ()); } } 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 ; } 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(); } }
代理类的实现
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 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
方法的调用。
总结
其实通过HttpClient的方式去调用也不是不行,只是说如果通过参考别人的代码,做一个RPC调用底层原理的一个分析,我们是可以做到一些系统层面的封装的,而且这个jar包是可以做成plugin的方式去提供给别人用的。
了解动态代理的原理,可以做到对代码项目无感知或者少感知的作用。
通过该过程举一反三,其他的场景都可以复制该思路去做事情。