文章目录
测试代码
以下面最小化代码初始pigeon服务提供者为例,分析pigeon完成服务注册的流程:
public static void main(String[] args) throws Exception {
// 初始化ProviderConfig,服务接口是EchoService,具体服务提供者是EchoServiceDefaultImpl
ProviderConfig<EchoService> providerConfig = new ProviderConfig<>(EchoService.class,
new EchoServiceDefaultImpl());
// 注册服务
ServiceFactory.addService(providerConfig);
System.in.read();
}
ProviderConfig定义
每个接口服务都以一个ProviderConfig类作为元数据,里面定义的相关属性如下:
// T为提供服务的抽象接口类型
public class ProviderConfig<T> {
// 提供服务的抽象接口类型
private Class<?> serviceInterface;
// 访问服务的url
private String url;
// 服务版本,调用时会附到url上区分不同版本调用
private String version;
// 具体服务示例类
private T service;
// 服务器配置
private ServerConfig serverConfig = new ServerConfig();
// 是否已发布
private boolean published = false;
// 调用超时
private boolean cancelTimeout = Constants.DEFAULT_TIMEOUT_CANCEL;
// 配置中心
private ConfigManager configManager = ConfigManagerLoader.getConfigManager();
// 如果useSharedPool为false,pigeon就会为每个方法设置独立的线程池执行请求。
private boolean useSharedPool = configManager.getBooleanValue(Constants.KEY_SERVICE_SHARED,
Constants.DEFAULT_SERVICE_SHARED);
// 保存当前服务接口的相关调用方法
private Map<String, ProviderMethodConfig> methods;
// 单独设置某个方法的最大并发数,如果并发超过设置的最大并发数,服务端会抛出com.dianping.pigeon.remoting.common.exception.RejectedException异常,客户端也会收到这个异常。
private int actives = 0;
// 是否支持新协议,如thrift
private boolean supported;
// 独立线程池配置,通过poolConfig --> threadPool找到对应的线程池实例
private PoolConfig poolConfig;
}
ServerConfig配置
如果说一个ProviderConfig映射一个服务调用接口,一个ServerConfig则对应一个服务器进程,具体服务器可能为基于netty的RPC服务器或jetty的http服务器。
ServerConfig相关配置如下:
public class ServerConfig {
// 配置中心,默认为本地文件,可以拓展为zk,mysql或其他的存储中心
private static ConfigManager configManager = ConfigManagerLoader.getConfigManager();
// 默认非http服务器的监听端口
public static final int DEFAULT_PORT = getDefaultPort();
// 默认http服务器的监听端口
public static final int DEFAULT_HTTP_PORT = 4080;
// 实际非http服务器的监听端口
private int port = configManager.getIntValue("pigeon.server.defaultport", DEFAULT_PORT);
// 实际http服务器的监听端口
private int httpPort = configManager.getIntValue("pigeon.httpserver.defaultport", DEFAULT_HTTP_PORT);
// 是否允许在端口冲突时,自动选择新端口
private boolean autoSelectPort = true;
private boolean enableTest = configManager
.getBooleanValue(Constants.KEY_TEST_ENABLE, Constants.DEFAULT_TEST_ENABLE);
// 处理服务请求的线程池核心数
private int corePoolSize = Constants.PROVIDER_POOL_CORE_SIZE;
// 处理服务请求的线程池最大数
private int maxPoolSize = Constants.PROVIDER_POOL_MAX_SIZE;
// 处理服务请求的线程池最大队列容量
private int workQueueSize = Constants.PROVIDER_POOL_QUEUE_SIZE;
//
private String suffix = configManager.getGroup();
// 服务器访问协议,default=tcp或http
private String protocol = Constants.PROTOCOL_DEFAULT;
// 当前环境,区分不同环境调用,如dev,prod等。
private String env;
// 当前服务ip
private String ip;
// 最终监听端口,在port的基础上,可能因为实际监听冲突改变
private int actualPort = port;
}
服务提供方静态初始化流程
ServiceFactory静态初始化
在示例中,通过一行代码:ServiceFactory.addService(providerConfig)
来完成服务注册,在具体调用方法前,会触发ServiceFactory的静态初始化,具体初始化工作包括三部分:
- 初始化serviceProxy,提供作为调用方的信息注册,和服务代理获取
- 初始化publishPolicy,针对特定的providerConfig进行服务注册
- 调用ProviderBootStrap.init()初始化调用ProviderBootStrap
ProviderBootStrap初始化流程
ProviderBootStrap是服务提供者的核心启动类,内部包括以下初始化流程:
- ProviderProcessHandlerFactory.init()初始化所有相关拦截器
- SerializerFactory.init()初始化所有序列化方式
- ClassUtils.loadClasses(“com.dianping.pigeon”)预加载所有pigeon相关类
- 注册关闭钩子ShutdownHookListener,内部调用ServiceFactory.unpublishAllServices()进程完成相关清理工作
- RegistryManager.getInstance() 初始化注册管理器
- 加载服务器,默认包括NettyServer和JettyHttpServer,当前会注册HTTP服务器
- 启动http服务器,默认注册到4080
- 设置consoleServer,初始化注册配置
在初始化前,通过双重检查判断是否已经初始化,具体实现源码如下所示:
public static void init() {
if (!isInitialized) {
synchronized (ProviderBootStrap.class) {
if (!isInitialized) {
// 初始化所有相关拦截器
ProviderProcessHandlerFactory.init();
// 初始化所有序列化方式
SerializerFactory.init();
// 加载所有pigeon相关类
ClassUtils.loadClasses("com.dianping.pigeon");
// 注册关闭钩子,进程清理工作
Thread shutdownHook = new Thread(new ShutdownHookListener());
shutdownHook.setDaemon(true);
shutdownHook.setPriority(Thread.MAX_PRIORITY);
Runtime.getRuntime().addShutdownHook(shutdownHook);
ServerConfig config = new ServerConfig();
// 设置配置协议为http
config.setProtocol(Constants.PROTOCOL_HTTP);
//确保注册管理器已被初始化
RegistryManager.getInstance();
// 加载服务器,包括NettyServer和JettyHttpServer
List<Server> servers = ExtensionLoader.getExtensionList(Server.class);
for (Server server : servers) {
if (!server.isStarted()) {
// config的协议类型是http,因而只初始化HttpServer
if (server.support(config)) {
server.start(config);
registerConsoleServer(config);
initRegistryConfig(config);
httpServer = server;
serversMap.put(server.getProtocol() + server.getPort(), server);
logger.warn("pigeon " + server + "[version:" + VersionUtils.VERSION + "] has been started");
}
}
}
isInitialized = true;
}
}
}
}
注册服务调用请求的处理拦截器
在pigeon中,发送和接受处理请求实际上都通过一系列的拦截器链实现,结合拦截器链,我们可以很方便地通过定义一个拦截器,添加到拦截器链中来拓展我们自己的逻辑,具体注册的拦截器如下所示,具体每个拦截器的实现原理在服务被调用部分文档解析:
public static void init() {
/*
业务逻辑拦截器开始
*/
// 调用链追踪拦截器,记录分布式链路追踪上下文
registerBizProcessFilter(new TraceFilter());
if (Constants.MONITOR_ENABLE) {
// 调用监控拦截器,添加调用上下文信息打点
registerBizProcessFilter(new MonitorProcessFilter());
}
// 记录当前处理流程,处理自定义拦截器逻辑,在拦截器回调时调用ProviderProcessInt