1,在Dubbo使用中,客户端通过Dubbo容器来调用远程服务。在客户端调用中,通过如下代码引入远程Dubbo服务器:
<dubbo
:reference id
=
"itemManagement"
interface
=
"com.IItemManagemen" version
=
"${items.dubbo.version}" check
=
"false" async
=
"true"
></dubbo
:reference
>
引入后,在Spring中我们就可以像正常的Bean一样申明Dubbo提供的RPC对象,并调用方法获取返回值。
@Resourceprivate
IItemManagement itemManagement
IItemManagement itemManagement
2,客户端调用,简单来说涉及到Bean的注入,注入时使用ReferenceBean来完成注入。具体调用getObject完成,我们跟踪该方法继续查看。具体的注入流程如下:
private void init
(
)
{
if (initialized ) {
return ;
}
initialized = true ;
if (interfaceName == null || interfaceName .length ( ) == 0 ) {
throw new IllegalStateException ( "<dubbo:reference interface=\"\" /> interface not allow null!" ) ;
}
// 获取消费者全局配置
checkDefault ( ) ;
appendProperties (this ) ;
if (getGeneric ( ) == null && getConsumer ( ) != null ) {
setGeneric (getConsumer ( ) .getGeneric ( ) ) ;
}
if (ProtocolUtils .isGeneric (getGeneric ( ) ) ) {
interfaceClass = GenericService . class ;
} else {
try {
interfaceClass = Class .forName (interfaceName , true , Thread .currentThread ( )
.getContextClassLoader ( ) ) ;
} catch (ClassNotFoundException e ) {
throw new IllegalStateException (e .getMessage ( ) , e ) ;
}
checkInterfaceAndMethods (interfaceClass , methods ) ;
}
String resolve = System .getProperty (interfaceName ) ;
String resolveFile = null ;
if (resolve == null || resolve .length ( ) == 0 ) {
resolveFile = System .getProperty ( "dubbo.resolve.file" ) ;
if (resolveFile == null || resolveFile .length ( ) == 0 ) {
File userResolveFile = new File ( new File ( System .getProperty ( "user.home" ) ) , "dubbo-resolve.properties" ) ;
if (userResolveFile .exists ( ) ) {
resolveFile = userResolveFile .getAbsolutePath ( ) ;
}
}
if (resolveFile != null && resolveFile .length ( ) > 0 ) {
Properties properties = new Properties ( ) ;
FileInputStream fis = null ;
try {
fis = new FileInputStream ( new File (resolveFile ) ) ;
properties .load (fis ) ;
} catch (IOException e ) {
throw new IllegalStateException ( "Unload " + resolveFile + ", cause: " + e .getMessage ( ) , e ) ;
} finally {
try {
if ( null != fis ) fis .close ( ) ;
} catch (IOException e ) {
logger .warn (e .getMessage ( ) , e ) ;
}
}
resolve = properties .getProperty (interfaceName ) ;
}
}
if (resolve != null && resolve .length ( ) > 0 ) {
url = resolve ;
if (logger .isWarnEnabled ( ) ) {
if (resolveFile != null && resolveFile .length ( ) > 0 ) {
logger .warn ( "Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service." ) ;
} else {
logger .warn ( "Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service." ) ;
}
}
}
if (consumer != null ) {
if (application == null ) {
application = consumer .getApplication ( ) ;
}
if (module == null ) {
module = consumer .getModule ( ) ;
}
if (registries == null ) {
registries = consumer .getRegistries ( ) ;
}
if (monitor == null ) {
monitor = consumer .getMonitor ( ) ;
}
}
if (module != null ) {
if (registries == null ) {
registries = module .getRegistries ( ) ;
}
if (monitor == null ) {
monitor = module .getMonitor ( ) ;
}
}
if (application != null ) {
if (registries == null ) {
registries = application .getRegistries ( ) ;
}
if (monitor == null ) {
monitor = application .getMonitor ( ) ;
}
}
checkApplication ( ) ;
checkStubAndMock (interfaceClass ) ;
Map <String , String > map = new HashMap <String , String > ( ) ;
Map <Object , Object > attributes = new HashMap <Object , Object > ( ) ;
map .put (Constants .SIDE_KEY , Constants .CONSUMER_SIDE ) ;
map .put (Constants .DUBBO_VERSION_KEY , Version .getVersion ( ) ) ;
map .put (Constants .TIMESTAMP_KEY , String .valueOf ( System .currentTimeMillis ( ) ) ) ;
if (ConfigUtils .getPid ( ) > 0 ) {
map .put (Constants .PID_KEY , String .valueOf (ConfigUtils .getPid ( ) ) ) ;
}
if ( ! isGeneric ( ) ) {
String revision = Version .getVersion (interfaceClass , version ) ;
if (revision != null && revision .length ( ) > 0 ) {
map .put ( "revision" , revision ) ;
}
String [ ] methods = Wrapper .getWrapper (interfaceClass ) .getMethodNames ( ) ;
if (methods .length == 0 ) {
logger .warn ( "NO method found in service interface " + interfaceClass .getName ( ) ) ;
map .put ( "methods" , Constants .ANY_VALUE ) ;
}
else {
map .put ( "methods" , StringUtils . join ( new HashSet <String > (Arrays .asList (methods ) ) , "," ) ) ;
}
}
map .put (Constants .INTERFACE_KEY , interfaceName ) ;
appendParameters (map , application ) ;
appendParameters (map , module ) ;
appendParameters (map , consumer , Constants .DEFAULT_KEY ) ;
appendParameters (map , this ) ;
String prifix = StringUtils .getServiceKey (map ) ;
if (methods != null && methods .size ( ) > 0 ) {
for (MethodConfig method : methods ) {
appendParameters (map , method , method .getName ( ) ) ;
String retryKey = method .getName ( ) + ".retry" ;
if (map .containsKey (retryKey ) ) {
String retryValue = map .remove (retryKey ) ;
if ( "false" .equals (retryValue ) ) {
map .put (method .getName ( ) + ".retries" , "0" ) ;
}
}
appendAttributes (attributes , method , prifix + "." + method .getName ( ) ) ;
checkAndConvertImplicitConfig (method , map , attributes ) ;
}
}
//attributes通过系统context进行存储.
StaticContext .getSystemContext ( ) .putAll (attributes ) ;
ref = createProxy (map ) ;
}
if (initialized ) {
return ;
}
initialized = true ;
if (interfaceName == null || interfaceName .length ( ) == 0 ) {
throw new IllegalStateException ( "<dubbo:reference interface=\"\" /> interface not allow null!" ) ;
}
// 获取消费者全局配置
checkDefault ( ) ;
appendProperties (this ) ;
if (getGeneric ( ) == null && getConsumer ( ) != null ) {
setGeneric (getConsumer ( ) .getGeneric ( ) ) ;
}
if (ProtocolUtils .isGeneric (getGeneric ( ) ) ) {
interfaceClass = GenericService . class ;
} else {
try {
interfaceClass = Class .forName (interfaceName , true , Thread .currentThread ( )
.getContextClassLoader ( ) ) ;
} catch (ClassNotFoundException e ) {
throw new IllegalStateException (e .getMessage ( ) , e ) ;
}
checkInterfaceAndMethods (interfaceClass , methods ) ;
}
String resolve = System .getProperty (interfaceName ) ;
String resolveFile = null ;
if (resolve == null || resolve .length ( ) == 0 ) {
resolveFile = System .getProperty ( "dubbo.resolve.file" ) ;
if (resolveFile == null || resolveFile .length ( ) == 0 ) {
File userResolveFile = new File ( new File ( System .getProperty ( "user.home" ) ) , "dubbo-resolve.properties" ) ;
if (userResolveFile .exists ( ) ) {
resolveFile = userResolveFile .getAbsolutePath ( ) ;
}
}
if (resolveFile != null && resolveFile .length ( ) > 0 ) {
Properties properties = new Properties ( ) ;
FileInputStream fis = null ;
try {
fis = new FileInputStream ( new File (resolveFile ) ) ;
properties .load (fis ) ;
} catch (IOException e ) {
throw new IllegalStateException ( "Unload " + resolveFile + ", cause: " + e .getMessage ( ) , e ) ;
} finally {
try {
if ( null != fis ) fis .close ( ) ;
} catch (IOException e ) {
logger .warn (e .getMessage ( ) , e ) ;
}
}
resolve = properties .getProperty (interfaceName ) ;
}
}
if (resolve != null && resolve .length ( ) > 0 ) {
url = resolve ;
if (logger .isWarnEnabled ( ) ) {
if (resolveFile != null && resolveFile .length ( ) > 0 ) {
logger .warn ( "Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service." ) ;
} else {
logger .warn ( "Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service." ) ;
}
}
}
if (consumer != null ) {
if (application == null ) {
application = consumer .getApplication ( ) ;
}
if (module == null ) {
module = consumer .getModule ( ) ;
}
if (registries == null ) {
registries = consumer .getRegistries ( ) ;
}
if (monitor == null ) {
monitor = consumer .getMonitor ( ) ;
}
}
if (module != null ) {
if (registries == null ) {
registries = module .getRegistries ( ) ;
}
if (monitor == null ) {
monitor = module .getMonitor ( ) ;
}
}
if (application != null ) {
if (registries == null ) {
registries = application .getRegistries ( ) ;
}
if (monitor == null ) {
monitor = application .getMonitor ( ) ;
}
}
checkApplication ( ) ;
checkStubAndMock (interfaceClass ) ;
Map <String , String > map = new HashMap <String , String > ( ) ;
Map <Object , Object > attributes = new HashMap <Object , Object > ( ) ;
map .put (Constants .SIDE_KEY , Constants .CONSUMER_SIDE ) ;
map .put (Constants .DUBBO_VERSION_KEY , Version .getVersion ( ) ) ;
map .put (Constants .TIMESTAMP_KEY , String .valueOf ( System .currentTimeMillis ( ) ) ) ;
if (ConfigUtils .getPid ( ) > 0 ) {
map .put (Constants .PID_KEY , String .valueOf (ConfigUtils .getPid ( ) ) ) ;
}
if ( ! isGeneric ( ) ) {
String revision = Version .getVersion (interfaceClass , version ) ;
if (revision != null && revision .length ( ) > 0 ) {
map .put ( "revision" , revision ) ;
}
String [ ] methods = Wrapper .getWrapper (interfaceClass ) .getMethodNames ( ) ;
if (methods .length == 0 ) {
logger .warn ( "NO method found in service interface " + interfaceClass .getName ( ) ) ;
map .put ( "methods" , Constants .ANY_VALUE ) ;
}
else {
map .put ( "methods" , StringUtils . join ( new HashSet <String > (Arrays .asList (methods ) ) , "," ) ) ;
}
}
map .put (Constants .INTERFACE_KEY , interfaceName ) ;
appendParameters (map , application ) ;
appendParameters (map , module ) ;
appendParameters (map , consumer , Constants .DEFAULT_KEY ) ;
appendParameters (map , this ) ;
String prifix = StringUtils .getServiceKey (map ) ;
if (methods != null && methods .size ( ) > 0 ) {
for (MethodConfig method : methods ) {
appendParameters (map , method , method .getName ( ) ) ;
String retryKey = method .getName ( ) + ".retry" ;
if (map .containsKey (retryKey ) ) {
String retryValue = map .remove (retryKey ) ;
if ( "false" .equals (retryValue ) ) {
map .put (method .getName ( ) + ".retries" , "0" ) ;
}
}
appendAttributes (attributes , method , prifix + "." + method .getName ( ) ) ;
checkAndConvertImplicitConfig (method , map , attributes ) ;
}
}
//attributes通过系统context进行存储.
StaticContext .getSystemContext ( ) .putAll (attributes ) ;
ref = createProxy (map ) ;
}
具体的生成在createProxy方法中。
如下,我们查看对应通过注册中心配置URL。
} else { // 通过注册中心配置拼装URL
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size() == 0) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker <? >> invokers = new ArrayList <Invoker <? >>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个registry url
}
}
if (registryURL != null) { // 有 注册中心协议的URL
// 对有注册中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c && ! invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size() == 0) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker <? >> invokers = new ArrayList <Invoker <? >>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一个registry url
}
}
if (registryURL != null) { // 有 注册中心协议的URL
// 对有注册中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注册中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c && ! invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
以上可以大概看下Dubbo的一些基本生成思路,部分还不是很清楚,暂时先了解下基本实现思路吧,后续有时间可以深入了解。
3,客户端调用过程。
客户端通过Invoke来封装调用的参数,在ReferenceBean的初始化过程中我们可以看到对Invoke的初始化。
public
interface Invoker
<T
>
extends Node
{
/**
* get service interface.
*
* @return service interface.
*/
Class <T > getInterface ( ) ;
/**
* invoke.
*
* @param invocation
* @return result
* @throws RpcException
*/
Result invoke (Invocation invocation ) throws RpcException ;
/**
* get service interface.
*
* @return service interface.
*/
Class <T > getInterface ( ) ;
/**
* invoke.
*
* @param invocation
* @return result
* @throws RpcException
*/
Result invoke (Invocation invocation ) throws RpcException ;
具体的实现,可以查看MockClusterINvoke类,
public Result invoke
(Invocation invocation
) throws RpcException
{
Result result = null ;
String value = directory .getUrl ( ) .getMethodParameter (invocation .getMethodName ( ) , Constants .MOCK_KEY , Boolean . FALSE .toString ( ) ) . trim ( ) ;
if (value .length ( ) == 0 || value .equalsIgnoreCase ( "false" ) ) {
//no mock
result = this .invoker .invoke (invocation ) ;
} else if (value .startsWith ( "force" ) ) {
if (logger .isWarnEnabled ( ) ) {
logger .info ( "force-mock: " + invocation .getMethodName ( ) + " force-mock enabled , url : " + directory .getUrl ( ) ) ;
}
//force:direct mock
result = doMockInvoke (invocation , null ) ;
} else {
//fail-mock
try {
result = this .invoker .invoke (invocation ) ;
}catch (RpcException e ) {
if (e .isBiz ( ) ) {
throw e ;
} else {
if (logger .isWarnEnabled ( ) ) {
logger .info ( "fail-mock: " + invocation .getMethodName ( ) + " fail-mock enabled , url : " + directory .getUrl ( ) , e ) ;
}
result = doMockInvoke (invocation , e ) ;
}
}
}
return result ;
}
Result result = null ;
String value = directory .getUrl ( ) .getMethodParameter (invocation .getMethodName ( ) , Constants .MOCK_KEY , Boolean . FALSE .toString ( ) ) . trim ( ) ;
if (value .length ( ) == 0 || value .equalsIgnoreCase ( "false" ) ) {
//no mock
result = this .invoker .invoke (invocation ) ;
} else if (value .startsWith ( "force" ) ) {
if (logger .isWarnEnabled ( ) ) {
logger .info ( "force-mock: " + invocation .getMethodName ( ) + " force-mock enabled , url : " + directory .getUrl ( ) ) ;
}
//force:direct mock
result = doMockInvoke (invocation , null ) ;
} else {
//fail-mock
try {
result = this .invoker .invoke (invocation ) ;
}catch (RpcException e ) {
if (e .isBiz ( ) ) {
throw e ;
} else {
if (logger .isWarnEnabled ( ) ) {
logger .info ( "fail-mock: " + invocation .getMethodName ( ) + " fail-mock enabled , url : " + directory .getUrl ( ) , e ) ;
}
result = doMockInvoke (invocation , e ) ;
}
}
}
return result ;
}
此处对方法请求和返回结果进行了处理。具体的调用交由Invoke对象来实现。
public MockClusterInvoker
(Directory
<T
> directory
, Invoker
<T
> invoker
)
{
this .directory = directory ;
this .invoker = invoker ;
}
Invoke的抽象实现在AbstractInvoke类中,
public Result invoke (Invocation inv ) throws RpcException {
if (destroyed ) {
throw new RpcException ( "Rpc invoker for service " + this + " on consumer " + NetUtils .getLocalHost ( )
+ " use dubbo version " + Version .getVersion ( )
+ " is DESTROYED, can not be invoked any more!" ) ;
}
RpcInvocation invocation = (RpcInvocation ) inv ;
invocation .setInvoker (this ) ;
if (attachment != null && attachment .size ( ) > 0 ) {
invocation .addAttachmentsIfAbsent (attachment ) ;
}
Map <String , String > context = RpcContext .getContext ( ) .getAttachments ( ) ;
if (context != null ) {
invocation .addAttachmentsIfAbsent (context ) ;
}
if (getUrl ( ) .getMethodParameter (invocation .getMethodName ( ) , Constants .ASYNC_KEY , false ) ) {
invocation .setAttachment (Constants .ASYNC_KEY , Boolean . TRUE .toString ( ) ) ;
}
RpcUtils .attachInvocationIdIfAsync (getUrl ( ) , invocation ) ;
try {
return doInvoke (invocation ) ;
} catch (InvocationTargetException e ) { // biz exception
Throwable te = e .getTargetException ( ) ;
if (te == null ) {
return new RpcResult (e ) ;
} else {
if (te instanceof RpcException ) {
( (RpcException ) te ) .setCode (RpcException .BIZ_EXCEPTION ) ;
}
return new RpcResult (te ) ;
}
} catch (RpcException e ) {
if (e .isBiz ( ) ) {
return new RpcResult (e ) ;
} else {
throw e ;
}
} catch (Throwable e ) {
return new RpcResult (e ) ;
}
}
this .directory = directory ;
this .invoker = invoker ;
}
Invoke的抽象实现在AbstractInvoke类中,
public Result invoke (Invocation inv ) throws RpcException {
if (destroyed ) {
throw new RpcException ( "Rpc invoker for service " + this + " on consumer " + NetUtils .getLocalHost ( )
+ " use dubbo version " + Version .getVersion ( )
+ " is DESTROYED, can not be invoked any more!" ) ;
}
RpcInvocation invocation = (RpcInvocation ) inv ;
invocation .setInvoker (this ) ;
if (attachment != null && attachment .size ( ) > 0 ) {
invocation .addAttachmentsIfAbsent (attachment ) ;
}
Map <String , String > context = RpcContext .getContext ( ) .getAttachments ( ) ;
if (context != null ) {
invocation .addAttachmentsIfAbsent (context ) ;
}
if (getUrl ( ) .getMethodParameter (invocation .getMethodName ( ) , Constants .ASYNC_KEY , false ) ) {
invocation .setAttachment (Constants .ASYNC_KEY , Boolean . TRUE .toString ( ) ) ;
}
RpcUtils .attachInvocationIdIfAsync (getUrl ( ) , invocation ) ;
try {
return doInvoke (invocation ) ;
} catch (InvocationTargetException e ) { // biz exception
Throwable te = e .getTargetException ( ) ;
if (te == null ) {
return new RpcResult (e ) ;
} else {
if (te instanceof RpcException ) {
( (RpcException ) te ) .setCode (RpcException .BIZ_EXCEPTION ) ;
}
return new RpcResult (te ) ;
}
} catch (RpcException e ) {
if (e .isBiz ( ) ) {
return new RpcResult (e ) ;
} else {
throw e ;
}
} catch (Throwable e ) {
return new RpcResult (e ) ;
}
}
具体的doInvoke交由具体实现来完成。
我们继续查看MockClusterInvoke对构造方法,其中比较重要的另一个参数是Directory。
public
interface Directory
<T
>
extends Node
{
/**
* get service type.
*
* @return service type.
*/
Class <T > getInterface ( ) ;
/**
* list invokers.
*
* @return invokers
*/
List <Invoker <T >> list (Invocation invocation ) throws RpcException ;
}
/**
* get service type.
*
* @return service type.
*/
Class <T > getInterface ( ) ;
/**
* list invokers.
*
* @return invokers
*/
List <Invoker <T >> list (Invocation invocation ) throws RpcException ;
}
该接口可以获取待Invoker的列表,其抽象实现在AbstractDirectory。
/**
* 增加router的Directory
*
* @author chao.liuc
*/
public abstract class AbstractDirectory <T > implements Directory <T > {
// 日志输出
private static final Logger logger = LoggerFactory .getLogger (AbstractDirectory . class ) ;
private final URL url ;
private volatile boolean destroyed = false ;
private volatile URL consumerUrl ;
private volatile List <Router > routers ;
public AbstractDirectory (URL url ) {
this (url , null ) ;
}
public AbstractDirectory (URL url , List <Router > routers ) {
this (url , url , routers ) ;
}
public AbstractDirectory (URL url , URL consumerUrl , List <Router > routers ) {
if (url == null )
throw new IllegalArgumentException ( "url == null" ) ;
this .url = url ;
this .consumerUrl = consumerUrl ;
setRouters (routers ) ;
}
public List <Invoker <T >> list (Invocation invocation ) throws RpcException {
if (destroyed ) {
throw new RpcException ( "Directory already destroyed .url: " + getUrl ( ) ) ;
}
List <Invoker <T >> invokers = doList (invocation ) ;
List <Router > localRouters = this .routers ; // local reference
if (localRouters != null && localRouters .size ( ) > 0 ) {
for (Router router : localRouters ) {
try {
if (router .getUrl ( ) == null || router .getUrl ( ) .getParameter (Constants .RUNTIME_KEY , true ) ) {
invokers = router .route (invokers , getConsumerUrl ( ) , invocation ) ;
}
} catch (Throwable t ) {
logger .error ( "Failed to execute router: " + getUrl ( ) + ", cause: " + t .getMessage ( ) , t ) ;
}
}
}
return invokers ;
}
* 增加router的Directory
*
* @author chao.liuc
*/
public abstract class AbstractDirectory <T > implements Directory <T > {
// 日志输出
private static final Logger logger = LoggerFactory .getLogger (AbstractDirectory . class ) ;
private final URL url ;
private volatile boolean destroyed = false ;
private volatile URL consumerUrl ;
private volatile List <Router > routers ;
public AbstractDirectory (URL url ) {
this (url , null ) ;
}
public AbstractDirectory (URL url , List <Router > routers ) {
this (url , url , routers ) ;
}
public AbstractDirectory (URL url , URL consumerUrl , List <Router > routers ) {
if (url == null )
throw new IllegalArgumentException ( "url == null" ) ;
this .url = url ;
this .consumerUrl = consumerUrl ;
setRouters (routers ) ;
}
public List <Invoker <T >> list (Invocation invocation ) throws RpcException {
if (destroyed ) {
throw new RpcException ( "Directory already destroyed .url: " + getUrl ( ) ) ;
}
List <Invoker <T >> invokers = doList (invocation ) ;
List <Router > localRouters = this .routers ; // local reference
if (localRouters != null && localRouters .size ( ) > 0 ) {
for (Router router : localRouters ) {
try {
if (router .getUrl ( ) == null || router .getUrl ( ) .getParameter (Constants .RUNTIME_KEY , true ) ) {
invokers = router .route (invokers , getConsumerUrl ( ) , invocation ) ;
}
} catch (Throwable t ) {
logger .error ( "Failed to execute router: " + getUrl ( ) + ", cause: " + t .getMessage ( ) , t ) ;
}
}
}
return invokers ;
}
其doList方法交由具体的子类RegistryDirectory实现。
public RegistryDirectory
(Class
<T
> serviceType
, URL url
)
{
super (url ) ;
if (serviceType == null )
throw new IllegalArgumentException ( "service type is null." ) ;
if (url .getServiceKey ( ) == null || url .getServiceKey ( ) .length ( ) == 0 )
throw new IllegalArgumentException ( "registry serviceKey is null." ) ;
this .serviceType = serviceType ;
this .serviceKey = url .getServiceKey ( ) ;
this .queryMap = StringUtils .parseQueryString (url .getParameterAndDecoded (Constants .REFER_KEY ) ) ;
this .overrideDirectoryUrl = this .directoryUrl = url .setPath (url .getServiceInterface ( ) ) .clearParameters ( ) .addParameters (queryMap ) .removeParameter (Constants .MONITOR_KEY ) ;
String group = directoryUrl .getParameter ( Constants .GROUP_KEY , "" ) ;
this .multiGroup = group != null && ( "*" .equals (group ) || group .contains ( "," ) ) ;
String methods = queryMap .get (Constants .METHODS_KEY ) ;
this .serviceMethods = methods == null ? null : Constants .COMMA_SPLIT_PATTERN . split (methods ) ;
}
super (url ) ;
if (serviceType == null )
throw new IllegalArgumentException ( "service type is null." ) ;
if (url .getServiceKey ( ) == null || url .getServiceKey ( ) .length ( ) == 0 )
throw new IllegalArgumentException ( "registry serviceKey is null." ) ;
this .serviceType = serviceType ;
this .serviceKey = url .getServiceKey ( ) ;
this .queryMap = StringUtils .parseQueryString (url .getParameterAndDecoded (Constants .REFER_KEY ) ) ;
this .overrideDirectoryUrl = this .directoryUrl = url .setPath (url .getServiceInterface ( ) ) .clearParameters ( ) .addParameters (queryMap ) .removeParameter (Constants .MONITOR_KEY ) ;
String group = directoryUrl .getParameter ( Constants .GROUP_KEY , "" ) ;
this .multiGroup = group != null && ( "*" .equals (group ) || group .contains ( "," ) ) ;
String methods = queryMap .get (Constants .METHODS_KEY ) ;
this .serviceMethods = methods == null ? null : Constants .COMMA_SPLIT_PATTERN . split (methods ) ;
}
该类实现了一些客户端调用时的的Invoker对象。