前面一篇dubbo源码分析中,我们对ClusterInvoker和LoadBalance进行了分析,可以知道ClusterInvoker在一批Invoker中选择了一个Invoker来进行调用,而这里的Invoker是通过RegistryDirectory得到的,而RegistryDirectory返回的Invoker实现为:InvokerDelegete(RegistryDirectory的内部类),改类维护了对应的provider url, 同时也包含了一个ListenerInvokerWrapper, InvokerDelegete执行invoke方法时,调用的就是ListenerInvokerWrapper的invoke方法(参考dubbo源码分析-consumer端3-Invoker创建流程)。ListenerInvokerWrapper依然不是真正的调用者,它主要是监听了invoker的创建与销毁事件,它维护的invoker为经过ProtocolFilterWrapper转换过的Invoker,该Invoker在执行前需要先经过filter链的处理,转换代码:
-
- private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
-
- Invoker<T> last = invoker;
-
- List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
- if (filters.size() > 0) {
-
- for (int i = filters.size() - 1; i >= 0; i --) {
- final Filter filter = filters.get(i);
- final Invoker<T> next = last;
- last = new Invoker<T>() {
-
- public Class<T> getInterface() {
- return invoker.getInterface();
- }
-
- public URL getUrl() {
- return invoker.getUrl();
- }
-
- public boolean isAvailable() {
- return invoker.isAvailable();
- }
-
- public Result invoke(Invocation invocation) throws RpcException {
- return filter.invoke(next, invocation);
- }
-
- public void destroy() {
- invoker.destroy();
- }
-
- @Override
- public String toString() {
- return invoker.toString();
- }
- };
- }
- }
- return last;
- }
我们先来看看Filter是如何加载的,首先通过ExtensionLoader加载所有Filter类型的SPI,并从中过滤出Activate注解中group含consumer的Filter,同时,如果Activate注解中配置了value,则需要对应的url中也配置了相应的值,否则将会被排除掉。
- @Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000)
- public class GenericImplFilter implements Filter {
- ...
- }
过滤出合适的Filter以后,还需要对Filter进行排序,使其能够按照正确的顺序执行,其排序的比较器(ActivateComparator)根据Activate注解信息进行处理:
- public @interface Activate {
-
-
-
-
-
- String[] group() default {};
-
-
-
-
-
-
-
-
-
- String[] value() default {};
-
-
-
-
- String[] before() default {};
-
-
-
-
- String[] after() default {};
-
-
-
-
- int order() default 0;
- }
对于consumer来说,主要的Filter包括:ConsumerContextFilter, MonitorFilter,FutureFilter,ActiveLimitFilter、GenericImplFilter、ValidationFilter。
ConsumerContextFilter:设置consumer调用的上下文,如本地地址,要调用的provider的地址,invoker信息,invocation信息等。RpcContext通过ThreadLocal实现,因此你可以在业务代码中直接通过RpcContext获取上下文信息(在调用对应方法之后才能获取)。需要注意的是RpcContext中的attachments中的内容在后面的远程调用中被传到provider,不建议业务使用,可以考虑traceId之类的数据传递,由于每次调用完成后都会进行清理,因此需要传递的数据每次调用都要重新设置。
- @Activate(group = Constants.CONSUMER, order = -10000)
- public class ConsumerContextFilter implements Filter {
-
- public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
- RpcContext.getContext()
- .setInvoker(invoker)
- .setInvocation(invocation)
- .setLocalAddress(NetUtils.getLocalHost(), 0)
- .setRemoteAddress(invoker.getUrl().getHost(),
- invoker.getUrl().getPort());
- if (invocation instanceof RpcInvocation) {
- ((RpcInvocation)invocation).setInvoker(invoker);
- }
- try {
- return invoker.invoke(invocation);
- } finally {
-
- RpcContext.getContext().clearAttachments();
- }
- }
-
- }
MonitorFilter: 收集consumer或provider的每一次调用信息,将信息保存在内存中进行合并,并定时的讲信息上报到monitor服务。注意monitor需要用户主动的配置才会生效。收集的信息包括:调用耗时、调用结果(成功/失败)、并发调用数
- <dubbo:monitor protocol="registry">
- <!-- 2分钟上报一次 -->
- <dubbo:parameter key="interval" value="120000" />
- </dubbo:monitor>
FutureFilter: 执行事件通知逻辑,包括调用前(oninvoke)、同步调用后/异步调用完成后(onreturn/onthrow),他们配置的方式一样:beanId.methodName,以下是官方的demo:
- <bean id ="demoCallback" class = "com.alibaba.dubbo.callback.implicit.NofifyImpl" />
- <dubbo:reference id="demoService" interface="com.alibaba.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
- <dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
- </dubbo:reference>
ActiveLimitFilter: 用于控制每个consumer调用指定方法的最大并发数,当配置了actives时生效。
-
- @Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)
- public class ActiveLimitFilter implements Filter {
-
- public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
- URL url = invoker.getUrl();
- String methodName = invocation.getMethodName();
-
- int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
-
- RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
- if (max > 0) {
-
- long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
- long start = System.currentTimeMillis();
- long remain = timeout;
- int active = count.getActive();
- if (active >= max) {
-
-
-
-
-
- synchronized (count) {
- while ((active = count.getActive()) >= max) {
- try {
- count.wait(remain);
- } catch (InterruptedException e) {
- }
- long elapsed = System.currentTimeMillis() - start;
- remain = timeout - elapsed;
- if (remain <= 0) {
- throw new RpcException("Waiting concurrent invoke timeout in client-side for service: "
- + invoker.getInterface().getName() + ", method: "
- + invocation.getMethodName() + ", elapsed: " + elapsed
- + ", timeout: " + timeout + ". concurrent invokes: " + active
- + ". max concurrent invoke limit: " + max);
- }
- }
- }
- }
- }
- try {
- long begin = System.currentTimeMillis();
-
- RpcStatus.beginCount(url, methodName);
- try {
- Result result = invoker.invoke(invocation);
- RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
- return result;
- } catch (RuntimeException t) {
- RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
- throw t;
- }
- } finally {
- if(max>0){
-
- synchronized (count) {
- count.notify();
- }
- }
- }
- }
-
- }
注意上面的代码,为了RpcStatus.endCount记录成功失败的状态,endCount并没有在finally中执行,也就是在某些特殊的异常下可能会导致并发计数一直上升而不下降,到达阈值后就无法再进行对应的调用了,虽然这种情况很少,但还是觉得这个写法并不是很好。如果你需要扩展的Filter在ActiveLimitFilter之后执行,一定不要抛出Error或者Throwable级的异常!!
GenericImplFilter: 对泛化调用的支持,当consumer没有的api接口时使用,具体用法参考 官方文档 ; 注意返回调用无法调用回声测试,这个不知道是dubbo的一个bug还是故意这样设置的,如果需要用泛化调用的方式进行回声测试,可以联系我;
ValidationFilter: 用于对调用参数的验证,通过注解设置规则,可参考hibernate-validator。
consumer端常用的Filter就讲到这里,如果要自行扩展Filter,请注意@Activate中group的设置及优先级的设置。