作为SpringCloudAlibaba微服务架构实战派上下册和RocketMQ消息中间件实战派上下册的作者,我来给大家分析一下Nacos的源码。
Nacos支持采用gRPC作为通信渠道,并且针对Nacos集群之间的通信以及客户端和Nacos服务端之间的通信,同时启动了两个相互隔离的gRPC通信渠道。
注册Handler
Nacos构建了一个RequestHandlerRegistry(Handler注册中心),并完成当前JVM中所有RequestHandler接口实现类的注册,并存储在registryHandlers中(一个HashMap的本地缓存),其中key为requestType(gRPC请求类型)。
@Service
public class RequestHandlerRegistry implements ApplicationListener<ContextRefreshedEvent> {
Map<String, RequestHandler> registryHandlers = new HashMap<>();
/**
* 根据请求类型获取请求处理器。
*
* @param requestType 请求类型的字符串,查看RequestTypeConstants子常量类的定义。
* @return 返回对应请求类型的请求处理器。
*/
public RequestHandler getByRequestType(String requestType) {
return registryHandlers.get(requestType);
}
/**
* 当Spring上下文被刷新时监听事件,用于初始化请求处理器的注册。
* 遍历所有RequestHandler类型的Bean,注册它们到处理器映射中。
* 如果某个处理器的方法上标注了TpsControl注解,并且Tps控制开启,则注册该方法的Tps控制点。
*
* @param event Spring上下文刷新事件。
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 获取所有RequestHandler类型的Bean
Map<String, RequestHandler> beansOfType = event.getApplicationContext().getBeansOfType(RequestHandler.class);
Collection<RequestHandler> values = beansOfType.values();
for (RequestHandler requestHandler : values) {
Class<?> clazz = requestHandler.getClass();
boolean skip = false;
// 检查处理器是否直接继承自RequestHandler,跳过非直接继承者
while (!clazz.getSuperclass().equals(RequestHandler.class)) {
if (clazz.getSuperclass().equals(Object.class)) {
skip = true;
break;
}
clazz = clazz.getSuperclass();
}
if (skip) {
continue;
}
try {
// 尝试获取handle方法,并检查是否启用了Tps控制
Method method = clazz.getMethod("handle", Request.class, RequestMeta.class);
if (method.isAnnotationPresent(TpsControl.class) && TpsControlConfig.isTpsControlEnabled()) {
// 如果启用了Tps控制,则注册该方法的Tps控制点
TpsControl tpsControl = method.getAnnotation(TpsControl.class);
String pointName = tpsControl.pointName();
ControlManagerCenter.getInstance().getTpsControlManager().registerTpsPoint(pointName);
}
} catch (Exception e) {
// 忽略异常,通常为方法不存在或Tps控制相关异常
}
// 获取并注册处理器的实际请求类型
Class tClass = (Class) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
registryHandlers.putIfAbsent(tClass.getSimpleName(), requestHandler);
}
}
}
Nacos为不同类型的 gRPC请求,定义了对应的 Handler,并且在处理的过程中用gRPC请求类型去做路由处理。
Nacos目前支持如下类型的RPC Request。
路由Handler
当Nacos的gRPC通信渠道收到客户端的RPC请求之后,就会执行路由Handler的操作。
//从Handler注册中心获取对应请求类型的Handler
RequestHandler requestHandler = requestHandlerRegistry.getByRequestType(type);
利用Handler处理RPC请求。
try {
Connection connection = connectionManager.getConnection(GrpcServerConstants.CONTEXT_KEY_CONN_ID.get());
RequestMeta requestMeta = new RequestMeta();
requestMeta.setClientIp(connection.getMetaInfo().getClientIp());
requestMeta.setConnectionId(GrpcServerConstants.CONTEXT_KEY_CONN_ID.get());
requestMeta.setClientVersion(connection.getMetaInfo().getVersion());
requestMeta.setLabels(connection.getMetaInfo().getLabels());
requestMeta.setAbilityTable(connection.getAbilityTable());
connectionManager.refreshActiveTime(requestMeta.getConnectionId());
//利用Handler处理RPC请求。
Response response = requestHandler.handleRequest(request, requestMeta);
Payload payloadResponse = GrpcUtils.convert(response);
traceIfNecessary(payloadResponse, false);
if (response.getErrorCode() == NacosException.OVER_THRESHOLD) {
RpcScheduledExecutor.CONTROL_SCHEDULER.schedule(() -> {
traceIfNecessary(payloadResponse, false);
responseObserver.onNext(payloadResponse);
responseObserver.onCompleted();
}, 1000L, TimeUnit.MILLISECONDS);
} else {
traceIfNecessary(payloadResponse, false);
responseObserver.onNext(payloadResponse);
responseObserver.onCompleted();
}
MetricsMonitor.recordGrpcRequestEvent(type, response.isSuccess(),
response.getErrorCode(), null, request.getModule(), System.nanoTime() - startTime);
} catch (Throwable e) {
Loggers.REMOTE_DIGEST
.error("[{}] Fail to handle request from connection [{}] ,error message :{}", "grpc", connectionId,
e);
Payload payloadResponse = GrpcUtils.convert(ErrorResponse.build(e));
traceIfNecessary(payloadResponse, false);
responseObserver.onNext(payloadResponse);
responseObserver.onCompleted();
MetricsMonitor.recordGrpcRequestEvent(type, false,
ResponseCode.FAIL.getCode(), e.getClass().getSimpleName(), request.getModule(), System.nanoTime() - startTime);
}