文章目录
服务代理对象调用原理
对于以下示例代码:
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方法拿到的接口引用,实际是一个Proxy代理类,内部持有一个ServiceInvocationProxy实例,而ServiceInvocationProxy实例持有两个属性,分别是InvokerConfig和InvokerProcessHandlerFactory的一个匿名内部类ServiceInvocationHandler实例对象,而ServiceInvocationHandler内部持有一个拦截器链,来完成具体的链式调用,具体内容如下:
在此,我们回顾以下,这个服务代理类的生成规则是:
Proxy.newProxyInstance(
ClassUtils.getCurrentClassLoader(invokerConfig.getClassLoader()),
new Class[]{
invokerConfig.getServiceInterface()},
new ServiceInvocationProxy(invokerConfig, InvokerProcessHandlerFactory.selectInvocationHandler(invokerConfig))
)
结合动态代理的原理,在执行代码echoService.echo("echoService_input")
时,实际执行的是ServiceInvocationProxy#invoke方法。
InvocationProxy#invoke方法的具体实现如下:
// 调用代理接口方法时,实际会调用下面方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
// 如果方法定义于Object类中,直接调用
if (method.getDeclaringClass() == Object.class) {
return method.invoke(handler, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return handler.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return handler.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return handler.equals(args[0]);
}
// 交由拦截器链进行处理,处理完后解析结果返回
return extractResult(handler.handle(new DefaultInvokerContext(invokerConfig, methodName, parameterTypes, args)),
method.getReturnType());
}
在这里,执行handler.handle(new DefaultInvokerContext(invokerConfig, methodName, parameterTypes, args))
会执行InvokerProcessHandlerFactory中创建的匿名内部类的方法:
public InvocationResponse handle(InvocationContext invocationContext) throws Throwable {
// 调用filter f(n),内部持有f(n+1)的引用,会在执行f(n)的中途执行f(n+1)
InvocationResponse resp = filter.invoke(next, invocationContext);
return resp;
}
在这一步,开始了拦截器链的链式调用,下面开始分析pigeon自带拦截器链中的每一个拦截器用途。而在此之前,先看看在每个拦截器传递的调用上下文InvocationContext。
InvocationContext 调用上下文讲解
在每个拦截器调用invoke方法时,会传入两个参数,一个是下一个拦截器的引用,另一个则是调用上下文InvocationContext。 在InvocationContext的抽象实现类AbstractInvocationContext中,定义了以下通用属性:
public abstract class AbstractInvocationContext<M extends MonitorData> implements InvocationContext<M> {
// 调用请求封装
protected InvocationRequest request;
// 调用相应封装
protected InvocationResponse response;
// 链式调用上下文信息,用于在多层RPC调用中,传递调用的上下文信息
private Map<String, Serializable> contextValues;
// 调用时间节点
private List<TimePoint> timeline = new ArrayList<TimePoint>();
// 调用上下文监控数据
protected M monitorData;
}
其中TimePoint是定义在InvocationContext接口的一个内部类,包含枚举TimePhase和long型变量time,用于记录整个调用请求每个阶段的进行时间点,具体TimePoint和TimePhase定义如下:
public static class TimePoint {
TimePhase phase;
long time;
}
enum TimePhase {
S/** start **/
, R/** receive **/
, T/** thread pool **/
, D/** degrade **/
, Q/** request **/
, P/** response **/
, O/** monitor **/
, C/** context **/
, G/** gateway **/
, A/** authenticate **/
, U/** business **/
, M/** method **/
, F/** future **/
, B/** back **/
, E
/** end **/
}
而在调用链上下文传递中,实际传递的是DefaultInvokerContext,DefaultInvokerContext继承自AbstractInvocationContext抽象类,具备上述介绍的所有InvocationContext特性,除此之外,还实现了InvokerContext,拓展了作为服务调用方的一些调用上下文信息,具体InvokerContext接口定义如下:
public interface InvokerContext<M extends InvokerMonitorData> extends InvocationContext<M> {
// 获取调用配置信息
InvokerConfig<?> getInvokerConfig();
// 获取调用方法名
String getMethodName();
// 获取调用方法参数类型
Class<?>[] getParameterTypes();
// 获取实际调用方法的传餐
Object[] getArguments();
// 获取服务器客户端连接,如NettyClient
Client getClient();
// 设置服务器客户端连接,如NettyClient
void setClient(Client client);
// 获取降级信息
DegradeInfo getDegradeInfo();
}
在DefaultInvokerContext,定义了作为调用方的相关上下文属性,具体包括:
public class DefaultInvokerContext extends AbstractInvocationContext implements InvokerContext {
// 调用配置
private InvokerConfig<?> invokerConfig;
// 调用的方法名
private String methodName;
// 调用的参数类型列表
private Class<?>[] parameterTypes;
// 调用的实际参数列表
private Object[] arguments;
// 客户端连接,如NettyClient,建立了和服务调用方的连接,完成远程调用请求
private Client client;
// 降级信息
final private DegradeInfo degradeInfo = new DegradeInfo();
// 构造函数,在ServiceInvocationProxy#invoke调用实际方法时,会调用本构造函数生成一个调用上下文在拦截器链内传递调用
public DefaultInvokerContext(InvokerConfig<?> invokerConfig, String methodName, Class<?>[] parameterTypes,
Object[] arguments) {
super(null);
this.invokerConfig = invokerConfig;
this.methodName = methodName;
this.parameterTypes = parameterTypes;
this.arguments = arguments;
getTimeline().add(new TimePoint(TimePhase.S, System.currentTimeMillis()));
}
}
pigeon自带拦截器解析
RemoteCallMonitorInvokeFilter
RemoteCallMonitorInvokeFilter主要用来添加监控事务打点的相关信息,粗略打点数据包含以下:
- PigeonCall.callType:调用类型,包含SYNC,CALLBACK,ONEWAY,FUTURE四种
- PigeonCall.serialize:序列化方式,默认为2,HESSIAN方式
- PigeonCall.timeout:调用超时时间
- PigeonCall.QPS: 具体值为"S"+当前时间的秒数,即S1,S2,S3,……,S60,通过记录打点的数量,计算每秒的QPS
- PigeonCall.app: 记录调用的服务方的appname
- PigeonCall.server: 记录调用的服务方地址
- PigeonCall.requestSize:记录请求的数据大小
- PigeonCall.responseSize:记录响应的数据大小
- PigeonCall.degrade: 记录发生非失败降级(根据DegradeInfo.isFailureDegrade参数定)的接口方法完整增信息,如com.dianping.pigeon.demo.EchoService:echo(String)
- PigeonCall.failureDegrade:记录发生失败降级(根据DegradeInfo.isFailureDegrade参数定)的接口方法完整增信息,如com.dianping.pigeon.demo.EchoService:echo(String)
具体实现如下:
public class RemoteCallMonitorInvokeFilter extends InvocationInvokeFilter {
private static final Logger logger = LoggerLoader.getLogger(RemoteCallMonitorInvokeFilter.class);
private final Monitor monitor = MonitorLoader.getMonitor();
public RemoteCallMonitorInvokeFilter() {
}
@Override
public InvocationResponse invoke(ServiceInvocationHandler handler, InvokerContext invocationContext)
throws Throwable {
invocationContext.getTimeline().add(new TimePoint(TimePhase.O));
MonitorTransaction transaction = null;
InvocationRequest request = invocationContext.getRequest();
String targetApp = null;
String callInterface = null;
InvokerConfig<?> invokerConfig = invocationContext.getInvokerConfig();
byte callMethodCode = invokerConfig.getCallMethod(invocationContext.getMethodName()); // 先尝试获取方法级别配置,如果不存在,再返回默认的CallMethod.SYNC.getCode()
CallMethod callMethod = CallMethod.getCallMethod(callMethodCode); // 获取方法调用类型枚举,默认为SYNC
if (monitor != null) {
// 添加监控打点信息
try {
callInterface = InvocationUtils.getRemoteCallFullName(invokerConfig.getUrl(),
invocationContext.getMethodName(), invocationContext.getParameterTypes());// 获取详细调用接口名,如com.dianping.pigeon.demo.EchoService:echo(String)
transaction = monitor.createTransaction("PigeonCall", callInterface, invocationContext);
if (transaction != null) {
monitor.setCurrentCallTransaction(transaction);
transaction.setStatusOk();
transaction.logEvent("PigeonCall.callType",
invokerConfig.getCallType(invocationContext.getMethodName()), "");
transaction.logEvent("PigeonCall.serialize", invokerConfig.getSerialize() + "", "");
transaction.logEvent("PigeonCall.timeout",
invokerConfig.getTimeout(invocationContext.getMethodName()) + "", "");
transaction.logEvent("PigeonCall.QPS", "S" + Calendar.getInstance().get(Calendar.SECOND), "");
}
} catch (Throwable e) {
monitor.logMonitorError(e);
}
}
boolean error = false;
try {
// 调用下一个拦截器
InvocationResponse response = handler.handle(invocationContext);
if (transaction != null) {
if (CallMethod.SYNC == callMethod) {
// 添加降级相关的打点信息,进支持SYNC方式监控
DegradationManager.INSTANCE.monitorDegrade(invocationContext, transaction);
}
Client client = invocationContext.getClient();
if (client != null) {
// 调用的app打点
targetApp = RegistryManager.getInstance().getReferencedAppFromCache(client.getAddress());
transaction.logEvent("PigeonCall.app", targetApp, "");
String parameters = "";
if (request != null && Constants.LOG_PARAMETERS) {
// 参数打点
parameters = InvocationUtils.toJsonString(request.getParameters(), 1000, 50);
}
// 服务地址打点
transaction.logEvent("PigeonCall.server", client.getAddress(), parameters);
}
request = invocationContext.getRequest();
if (request != null) {
// 请求体大小打点
String reqSize = SizeMonitor.getInstance().getLogSize(request.getSize());
if (reqSize != null) {
monitor.logEvent("PigeonCall.requestSize", reqSize, "" + request.getSize());
}
}
if (response != null && response.getSize() > 0) {
// 响应大小打点
String respSize = SizeMonitor.getInstance().getLogSize(response.getSize());
if (respSize != null) {
monitor.logEvent("PigeonCall.responseSize", respSize, "" + response.getSize());
}
invocationContext.getTimeline().add(new TimePoint(TimePhase.R, response.getCreateMillisTime()));
invocationContext.getTimeline().add(new TimePoint(TimePhase.R));
}
}
return response;
} catch (Throwable e) {
<