我们在调用Dubbo服务的时候,一般只需要将Consumer端的dubbo:reference
指定成服务端中dubbo:service
暴露的服务,就可以找到服务端,完成调用,也就是说,Dubbo只需要服务接口信息就可以找到服务提供者。 其实除了服务提供者以外,Dubbo也有服务分组和版本的概念,在客户端去寻找“匹配”的服务端的时候,需要服务接口,版本号,组别这三个信息都匹配,才算是一个有效的服务端:
public static boolean isMatch(URL consumerUrl, URL providerUrl) { String consumerInterface = consumerUrl.getServiceInterface(); String providerInterface = providerUrl.getServiceInterface(); if (!(Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface))) return false; if (!isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY), consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) { return false; } if (!providerUrl.getParameter(Constants.ENABLED_KEY, true) && !Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) { return false; } String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY); String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY); String consumerClassifier = consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE); String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY); String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY); String providerClassifier = providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE); return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup)) && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion)) && (consumerClassifier == null || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier)); }
如果没有配置组别和版本号,默认值为空。服务端和消费端都没有配,只有服务接口,其他两个信息都为空,也是可以“找到”对方的,那服务名和版本号可以如何使用呢?下面我们来看一下具体的场景:
当一个接口有多种实现时,可以用 group 区分。
服务
<dubbo:service group="feedback" interface="com.xxx.IndexService" /> <dubbo:service group="member" interface="com.xxx.IndexService" />
引用
<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" /> <dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />
任意组
<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
老版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
老版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
如果不需要区分版本,可以按照以下的方式配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
按组合并返回结果,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。
搜索所有分组
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" />
合并指定分组
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" />
指定方法合并结果,其它未指定的方法,将只调用一个 Group
<dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger="true" /> </dubbo:service>
某个方法不合并结果,其它都合并结果
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true"> <dubbo:method name="getMenuItems" merger="false" /> </dubbo:service>
指定合并策略,缺省根据返回值类型自动匹配,如果同一类型有两个合并器时,需指定合并器的名称
<dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger="mymerge" /> </dubbo:service>
指定合并方法,将调用返回结果的指定方法进行合并,合并方法的参数类型必须是返回结果类型本身
<dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger=".addAll" /> </dubbo:service>
如果配置了merge,Dubbo会分别调用多个组别的服务提供者,然后把结果聚合,返回给消费端,具体的实现在MergeableClusterInvoker.java
里:
public Result invoke(final Invocation invocation) throws RpcException { List<Invoker<T>> invokers = directory.list(invocation); String merger = getUrl().getMethodParameter(invocation.getMethodName(), Constants.MERGER_KEY); if (ConfigUtils.isEmpty(merger)) { // If a method doesn't have a merger, only invoke one Group for (final Invoker<T> invoker : invokers) { if (invoker.isAvailable()) { return invoker.invoke(invocation); } } return invokers.iterator().next().invoke(invocation); } Class<?> returnType; try { returnType = getInterface().getMethod( invocation.getMethodName(), invocation.getParameterTypes()).getReturnType(); } catch (NoSuchMethodException e) { returnType = null; } Map<String, Future<Result>> results = new HashMap<String, Future<Result>>(); for (final Invoker<T> invoker : invokers) { Future<Result> future = executor.submit(new Callable<Result>() { @Override public Result call() throws Exception { return invoker.invoke(new RpcInvocation(invocation, invoker)); } }); results.put(invoker.getUrl().getServiceKey(), future); } Object result = null; List<Result> resultList = new ArrayList<Result>(results.size()); int timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); for (Map.Entry<String, Future<Result>> entry : results.entrySet()) { Future<Result> future = entry.getValue(); try { Result r = future.get(timeout, TimeUnit.MILLISECONDS); if (r.hasException()) { log.error("Invoke " + getGroupDescFromServiceKey(entry.getKey()) + " failed: " + r.getException().getMessage(), r.getException()); } else { resultList.add(r); } } catch (Exception e) { throw new RpcException("Failed to invoke service " + entry.getKey() + ": " + e.getMessage(), e); } } if (resultList.isEmpty()) { return new RpcResult((Object) null); } else if (resultList.size() == 1) { return resultList.iterator().next(); } if (returnType == void.class) { return new RpcResult((Object) null); }
如果配置了merger,会依次调用,结果都放在results里面,其中value都是future类型,等调用完成之后,再遍历results,通过future.get拿到真正的结果,到此为止,所有调用的结果都放在resultList里面了,接下来要做的是把结果进行聚合:
Merger resultMerger; if (ConfigUtils.isDefault(merger)) { resultMerger = MergerFactory.getMerger(returnType); } else { resultMerger = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(merger); } if (resultMerger != null) { List<Object> rets = new ArrayList<Object>(resultList.size()); for (Result r : resultList) { rets.add(r.getValue()); } result = resultMerger.merge( rets.toArray((Object[]) Array.newInstance(returnType, 0))); } else { throw new RpcException("There is no merger to merge result.") } return new RpcResult(result);
这里会根据返回值的类型,获取到对应的resultMerger,除了Dubbo默认实现的类型外,也可以自己指定merger类型并且添加相应的扩展,通过实现merge
方法类进行结果聚合。