文章目录
测试代码
以下面最小化代码初始pigeon服务调用者为例,分析pigeon完成服务注册的流程:
public static void main(String[] args) throws Exception {
InvokerConfig<EchoService> invokerConfig = new InvokerConfig<>(EchoService.class);
EchoService echoService = ServiceFactory.getService( invokerConfig);
System.out.println("echoService result:" + echoService.echo("echoService_input"));
}
调用ServiceFactory.getService创建流程:
- 创建一个InvokerConfig,至少传入一个接口类全限定名
- 调用ServiceFactory.getService(invokerConfig)获取具体的代理对象
- 以接口引用,直接通过代理对象调用远程方法
InvokerConfig定义
每个InvokerConfig对应一个调用服务,内部属性定义如下:
public class InvokerConfig<T> {
private static final Logger logger = LoggerLoader.getLogger(InvokerConfig.class);
/* 调用方式 */
// 同步调用,客户端线程会阻塞等待返回结果,默认设置是sync模式。
public static final String CALL_SYNC = CallMethod.SYNC.getName();
// 回调方式,客户端将请求提交给pigeon后立即返回,也不等待返回结果,它与future方式的区别是
// callback必须提供一个实现了pigeon提供的InvocationCallback接口的回调对象给pigeon,pigeon负责接收返回结果并传递回给这个回调对象
public static final String CALL_CALLBACK = CallMethod.CALLBACK.getName();
// 客户端只是将请求传递给pigeon,pigeon提交给服务端,客户端也不等待立即返回,服务端也不会返回结果给客户端,这种方式一般都是没有返回结果的接口调用。
public static final String CALL_ONEWAY = CallMethod.ONEWAY.getName();
// 客户端将请求提交给pigeon后立即返回,不等待返回结果,由pigeon负责等待返回结果,客户端可以自行决定何时何地来取返回结果
public static final String CALL_FUTURE = CallMethod.FUTURE.getName();
/* 服务协议 */
// 服务器协议,http
public static final String PROTOCOL_HTTP = Constants.PROTOCOL_HTTP;
// 服务器协议,tcp
public static final String PROTOCOL_DEFAULT = Constants.PROTOCOL_DEFAULT;
/* 序列化方式字段 */
// hessian序列化方式
public static final String SERIALIZE_HESSIAN = SerializerType.HESSIAN.getName();
// java原生序列化方式
public static final String SERIALIZE_JAVA = SerializerType.JAVA.getName();
// protostuff序列化方式
public static final String SERIALIZE_PROTO = SerializerType.PROTO.getName();
// json序列化方式
public static final String SERIALIZE_JSON = SerializerType.JSON.getName();
// FST序列化方式
public static final String SERIALIZE_FST = SerializerType.FST.getName();
// 配置中心
private ConfigManager configManager = ConfigManagerLoader.getConfigManager();
// 服务接口
private Class<T> serviceInterface;
// 服务访问url
private String url;
// 服务访问版本
private String version;
// 方法调用方式字节
private byte callMethod = CallMethod.SYNC.getCode();
// 方法调用方式字符串
private String callType = CallMethod.SYNC.getName();
// HESSIAN序列化方式眦裂
private byte serialize = SerializerType.HESSIAN.getCode();
// 调用超时时间
private int timeout = configManager.getIntValue(Constants.KEY_INVOKER_TIMEOUT, Constants.DEFAULT_INVOKER_TIMEOUT);
// 调用回调
private InvocationCallback callback;
// 泳道
private String suffix = configManager.getGroup();
// 负载均衡算法
private String loadbalance = LoadBalanceManager.DEFAULT_LOADBALANCE;
// 区域路由策略
private String regionPolicy = RegionPolicyManager.INSTANCE.DEFAULT_REGIONPOLICY;
// 是否超时重试
private boolean timeoutRetry = false;
// 集群访问策略
private String cluster = Constants.CLUSTER_FAILFAST;
// 重试次数
private int retries = 1;
// 虚拟ip
private String vip;
// 最大请求数限制
private int maxRequests = configManager.getIntValue(Constants.KEY_INVOKER_MAXREQUESTS, 0);
// 服务协议
private String protocol = Constants.PROTOCOL_DEFAULT;
// 当前服务提供的方法
private Map<String, InvokerMethodConfig> methods;
private ClassLoader classLoader;
// 服务访问密钥
private transient String secret;
// 远程appkey
private String remoteAppKey;
private Object mock;
}
服务调用方静态初始化流程
ServiceFactory静态初始化
ServiceFactory静态初始化流程类似于服务提供方,大致流程如下:
- 在首次访问ServiceFactory,会触发ServiceProxy和PublishPolicy的加载
- ServiceProxy提供作为调用方的信息注册,和服务代理获取
- PublishPolicy针对特定的providerConfig进行服务注册
- 随后会调用ProviderBootStrap.init()初始化对外提供服务的基础依赖
- ProviderProcessHandlerFactory.init()初始化所有相关拦截器
- SerializerFactory.init()初始化所有序列化方式
- ClassUtils.loadClasses(“com.dianping.pigeon”)预加载所有pigeon相关类
- 注册关闭钩子ShutdownHookListener,内部调用ServiceFactory.unpublishAllServices()进程完成相关清理工作
- RegistryManager.getInstance() 初始化注册管理器
- 加载服务器,默认包括NettyServer和JettyHttpServer,当前会注册HTTP服务器
- 启动http服务器,默认注册到4080
- 设置consoleServer,初始化注册配置
这里代码和服务提供方的初始化流程大致相同,不再展示,下面主要看服务调用方pigeon初始化流程
ServiceFactory#getService方法实现
在具体调用ServiceFactory.getService时,实际会调用serviceProxy.getProxy(invokerConfig),进一步调用AbstractServiceProxy#getProxy方法,这个方法的实现逻辑大致如下:
- 检查interface,url,protocol等参数合法性,如果url没有设置,默认使用接口全限定名,如果protocol不为空,且等于default,则修改成@DEFAULT@,同时更新url加上这个协议前缀
- 双重加锁检查invokeConfig对应的服务是否以存在,不存在先尝试启动调用方启动类InvokerBootStrap
- 先根据配置的序列方式获取相应的序列化类,再根据invokerConfig创建动态代理:ServiceInvocationProxy
- 如果配置中的负载均衡配置存在,注册服务到负载均衡管理器中
- 注册区域策略服务
- ClientManager.getInstance().registerClients(invokerConfig)注册客户端到注册中心(默认为zk)
- 缓存服务
代码实现逻辑如下:
public <T> T getProxy(InvokerConfig<T> invokerConfig) {
// 参数检查
if (invokerConfig.getServiceInterface() == null) {
throw new IllegalArgumentException("service interface is required");
}
// 如果url没有设置,默认使用接口全限定名
if (StringUtils.isBlank(invokerConfig.getUrl())) {
invokerConfig.setUrl(ServiceFactory.getServiceUrl(invokerConfig));
}
// 如果protocol不为空,且等于default,则修改成@DEFAULT@,同时更新url加上这个协议前缀
if (!StringUtils.isBlank(invokerConfig.getProtocol())
&& !invokerConfig.getProtocol().equalsIgnoreCase(Constants.PROTOCOL_DEFAULT)) {
String protocolPrefix = "@" + invokerConfig.getProtocol().toUpperCase() + "@";
if (!invokerConfig.getUrl().startsWith(protocolPrefix)) {
invokerConfig.setUrl(protocolPrefix + invokerConfig.getUrl());
}
}
// 双重检查
Object service = null;
service = services.get(invokerConfig);
if (service == null) {
synchronized (interner.intern(invokerConfig)) {
service = services.get(invokerConfig);
if (service == null) {
try {
/