【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方法拿到的接口引用,实际是一个Proxy代理类,内部持有一个ServiceInvocationProxy实例,而ServiceInvocationProxy实例持有两个属性,分别是InvokerConfig和InvokerProcessHandlerFactory的一个匿名内部类ServiceInvocationHandler实例对象,而ServiceInvocationHandler内部持有一个拦截器链,来完成具体的链式调用,具体内容如下:
image
在此,我们回顾以下,这个服务代理类的生成规则是:

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主要用来添加监控事务打点的相关信息,粗略打点数据包含以下:

  1. PigeonCall.callType:调用类型,包含SYNC,CALLBACK,ONEWAY,FUTURE四种
  2. PigeonCall.serialize:序列化方式,默认为2,HESSIAN方式
  3. PigeonCall.timeout:调用超时时间
  4. PigeonCall.QPS: 具体值为"S"+当前时间的秒数,即S1,S2,S3,……,S60,通过记录打点的数量,计算每秒的QPS
  5. PigeonCall.app: 记录调用的服务方的appname
  6. PigeonCall.server: 记录调用的服务方地址
  7. PigeonCall.requestSize:记录请求的数据大小
  8. PigeonCall.responseSize:记录响应的数据大小
  9. PigeonCall.degrade: 记录发生非失败降级(根据DegradeInfo.isFailureDegrade参数定)的接口方法完整增信息,如com.dianping.pigeon.demo.EchoService:echo(String)
  10. 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) {
   
            <
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值