如何打通SpringCloud与HSF的调用?

编程

所以,我们为了拥抱开源,全部采用SpringCloud,系统与系统之间调用是通过FeignClient的方式来调用的,但是由于底层的部分系统由于时间、人力、历史等原因,无法在短时间内都像我们一样能积极响应。所以就出现了SpringCloud与HSF服务同时存在的情况,为了大家再编码过程中都能像本地调用(TCPFeignClient),所以就写了一个代理工具。

交互图

如果是上面的方式,我们还是能感受到每次都是通过HttpClient等方式发起一次Http请求,写代码时候的体验不是很好。

为了解决这个问题,那么我们的任务就是来写一个这个代理封装。

分析功能点

了解一下FeignClient

我们参考一下FeignClient的功能一个解析过程,如图:

  • 生成动态代理类
  • 解析出等的MethodHandler
  • 动态生成Request
  • Encoder
  • 拦截器处理
  • 日志处理
  • 重试机制

代理需要考虑什么?

那我们不用说写那么完善,我们的第一个目标就是实现扫描 → 代理 → 发送请求。

因为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 {};

}

利用SpirngImportBeanDefinitionRegistrar来进行自动注入生成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方法的调用。

总结

  • 其实通过HttpClient的方式去调用也不是不行,只是说如果通过参考别人的代码,做一个RPC调用底层原理的一个分析,我们是可以做到一些系统层面的封装的,而且这个jar包是可以做成plugin的方式去提供给别人用的。
  • 了解动态代理的原理,可以做到对代码项目无感知或者少感知的作用。
  • 通过该过程举一反三,其他的场景都可以复制该思路去做事情。

如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。

以上是 如何打通SpringCloud与HSF的调用? 的全部内容, 来源链接: utcz.com/z/516204.html

回到顶部