0%

IM - API处理层

长连网关通过调用RPC接口,将上行消息直接透传给API处理层。

在API处理层,首先会根据 Header 中声明的接口信息,通过反射调用相应的Controller中的 method,长连网关层就类似 SpringMVCDispatcherServlet,而API层的处理就类似 @RequestMapping所做的事情。

首先,定义注解,用来标识一个方法。

1
2
3
4
5
6
7
8
9
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DispatcherURL {
//URL地址
String value();

//接口标识
String tag();
}

再定义一个对象,用来表示一个url - tag - bean - method的关系。

1
2
3
4
5
6
7
@Data
public class DispatcherMapping {
private String url;
private String tag;
private Object bean;
private String method;
}

基于SpringBoot来实现,容器初始化完成将会初始化映射关系。

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
import com.google.common.collect.Maps;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.util.Arrays;
import java.util.Map;

/**
* 用于初始化映射关系
*/
@Component
public class Initializer implements ApplicationListener<ContextRefreshedEvent> {
public static Map<String, DispatcherMapping> urlDispatcherMappingMap = Maps.newHashMap();

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context != null) {
String[] beanNames = context.getBeanDefinitionNames();
Arrays.stream(beanNames).forEach(beanName -> {
Object bean = context.getBean(beanName);
ReflectionUtils.doWithMethods(bean.getClass(), method -> {
if (method.isAnnotationPresent(DispatcherURL.class)) {
DispatcherMapping dispatcherMapping = new DispatcherMapping();
DispatcherURL dispatcherURL = method.getAnnotation(DispatcherURL.class);
dispatcherMapping.setUrl(dispatcherURL.value());
dispatcherMapping.setTag(dispatcherURL.tag());
dispatcherMapping.setBean(bean);
dispatcherMapping.setMethod(method.getName());
urlDispatcherMappingMap.put(dispatcherMapping.getTag(), dispatcherMapping);
}
});
});
}
}
}

声明一个方法大概如下:

1
2
3
4
5
6
7
8
@Controller
public class TestController {
@DispatcherURL(value = "/say", tag = "(1,0)")
public String say(Map<Integer, Object> header, Map<Integer, Object> body) {
//handler header & body
return "Hello world";
}
}

RPC服务端如下:

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
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.Map;

/**
* rpc服务端
*/
@Service
public class RpcService {
//长连网关调用
public Object invoke(Map<Integer, Object> headerMap, Map<Integer, Object> bodyMap) {
Object result = null;
try {
String tag = getTagFromHeader(headerMap);
DispatcherMapping dispatcherMapping = Initializer.urlDispatcherMappingMap.get(tag);
if (dispatcherMapping == null) {
//ignore...
return null;
}
Class bean = dispatcherMapping.getBean().getClass();
Method method = bean.getDeclaredMethod(dispatcherMapping.getMethod(), new Class[]{Map.class, Map.class});
result = method.invoke(bean, headerMap, bodyMap);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

private String getTagFromHeader(Map<Integer, Object> headerMap) {
//假设tag的key为1和2
return "(" + headerMap.get(1) + "," + headerMap.get(2) + ")";
}
}

最终,客户端只需要在headerMap中传入tag,在bodyMap中传入业务参数,通过简单的RPC透传调用,即可实现网关通用性,一旦有新的接口上线,只需要修改API处理层即可。