先放出总结:
(1)Dubbo服务暴露是将持有的服务实例通过代理转换成Invoker(一个真实的服务对象实例,可以是本地jvm实现、远程实现或是集群实现),然后把Invoker通过具体的协议(比如Dubbo)转换成Exporter。
(2)配置信息优先级覆盖策略:-D传递给JVM参数优先级最高,代码或者XML配置优先级次高,配置文件如dubbo.properties优先级最低。
(3)服务端和客户端配置覆盖策略:如果只有服务端指定配置,这些配置也会在客户端生效。如果客户端也配置了相应属性,那么服务端配置会被覆盖。
(4)服务暴露的入口在ServiceConfig#doExport,无论XML还是注解,都会转换成ServiceBean,ServiceBean是ServiceConfig的子类。
(5)Dubbo支持同一个服务暴露多个协议,如Dubbo协议和REST协议,每个协议注册元数据都会写入注册中心。
每一个标签都会被解析成一个BeanDefinition,然后注册成Bean。
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
}
Dubbo会将每个需要暴露的服务解析为ServiceBean对象。
ServiceBean实现了ApplicationListener接口,对ContextRefreshedEvent进行监听。当Spring容器启动发布ContextRefreshedEvent,ServiceBean实例会触发onApplicationEvent方法,然后调用export方法。
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
ServiceBean是ServiceConfig的子类,ServiceBean#export会先去调用父类的export方法,然后发布ServiceBeanExportedEvent。
public void export() {
super.export();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
ServiceConfig#export会调用checkAndUpdateSubConfigs方法检查参数配置,再执行doExport导出服务。
public synchronized void export() {
checkAndUpdateSubConfigs();
if (!shouldExport()) {
return;
}
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
doExport();
}
}
ServiceConfig#checkAndUpdateSubConfigs
(1)使用在全局配置上明确定义的默认配置。
(2)设置注解配置的优先级。
(3)检查ProviderConfig是否创建,如果没有,实例化一个。
(4)检查ProtocolConfig是否创建,如果没有,实例化一个。
(5)检查ApplicationConfig是否创建,如果没有,实例化一个。
public void checkAndUpdateSubConfigs() {
// Use default configs defined explicitly on global configs
completeCompoundConfigs();
// Config Center should always being started first.
startConfigCenter();
checkDefault();
checkProtocol();
checkApplication();
// if protocol is not injvm checkRegistry
if (!isOnlyInJvm()) {
checkRegistry();
}
this.refresh();
checkMetadataReport();
}
ServiceConfig没有的配置会使用ProviderConfig、ModuleConfig和ApplicationConfig的配置。
private void completeCompoundConfigs() {
if (provider != null) {
if (application == null) {
setApplication(provider.getApplication());
}
if (module == null) {
setModule(provider.getModule());
}
if (registries == null) {
setRegistries(provider.getRegistries());
}
if (monitor == null) {
setMonitor(provider.getMonitor());
}
if (protocols == null) {
setProtocols(provider.getProtocols());
}
if (configCenter == null) {
setConfigCenter(provider.getConfigCenter());
}
}
if (module != null) {
if (registries == null) {
setRegistries(module.getRegistries());
}
if (monitor == null) {
setMonitor(module.getMonitor());
}
}
if (application != null) {
if (registries == null) {
setRegistries(application.getRegistries());
}
if (monitor == null) {
setMonitor(application.getMonitor());
}
}
}
判断是否存在配置中心,如果存在配置中心,设置读取配置参数的优先级。
void startConfigCenter() {
if (configCenter == null) {
ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);
}
if (this.configCenter != null) {
// TODO there may have duplicate refresh
this.configCenter.refresh();
prepareEnvironment();
}
ConfigManager.getInstance().refreshAll();
}
AbstractConfig#refresh调用Environment#getConfiguration获取配置文件的优先级:JVM配置>操作系统配置>配置中心应用配置>配置中心全局配置>配置文件配置
如果Environment的configCenterFirst为true,配置文件优先级是:
JVM配置>操作系统配置>配置中心应用配置>配置中心全局配置>注解配置>配置文件配置
如果Environment的configCenterFirst为false,配置文件优先级是:
JVM配置>操作系统配置>注解配置>配置中心应用配置>配置中心全局配置>配置文件配置
循环遍历配置类(如ApplicationConfig)的所有方法,找出set方法,根据set后面的名称到各种配置方式中查找优先级最高的值,最后通过反射的方式给配置类的属性赋值。
public void refresh() {
try {
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
Configuration config = new ConfigConfigurationAdapter(this);
if (Environment.getInstance().isConfigCenterFirst()) {
// The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
compositeConfiguration.addConfiguration(4, config);
} else {
// The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
compositeConfiguration.addConfiguration(2, config);
}
// loop methods, get override value and set the new value back to method
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
try {
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
// isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} catch (NoSuchMethodException e) {
logger.info("Failed to override the property " + method.getName() + " in " +
this.getClass().getSimpleName() +
", please make sure every property has getter/setter method provided.");
}
}
}
} catch (Exception e) {
logger.error("Failed to override ", e);
}
}
Environment#getConfiguration依次将JVM配置(SystemConfig)、系统配置(EnvironmentConfig)、应用配置(AppExternalConfig)、全局配置(ExternalConfig)和配置文件配置(PropertiesConfig)添加到List集合里。
public CompositeConfiguration getConfiguration(String prefix, String id) {
CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
// Config center has the highest priority
compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
return compositeConfiguration;
}
CompositeConfiguration#getInternalProperty
根据key到各个配置中查找,如果不为空,就返回。
public Object getInternalProperty(String key) {
Configuration firstMatchingConfiguration = null;
for (Configuration config : configList) {
try {
if (config.containsKey(key)) {
firstMatchingConfiguration = config;
break;
}
} catch (Exception e) {
logger.error("Error when trying to get value for key " + key + " from " + config + ", will continue to try the next one.");
}
}
if (firstMatchingConfiguration != null) {
return firstMatchingConfiguration.getProperty(key);
} else {
return null;
}
}
判断是否符合条件的set方法:以set开头,方法名称不是set的public方法,且方法只能有一个基本类型的参数。
public class MethodUtils {
public static boolean isSetter(Method method) {
return method.getName().startsWith("set")
&& !"set".equals(method.getName())
&& Modifier.isPublic(method.getModifiers())
&& method.getParameterCount() == 1
&& ClassUtils.isPrimitive(method.getParameterTypes()[0]);
}
}
判断Class是否为原始类型。
public static boolean isPrimitive(Class<?> type) {
return type.isPrimitive()
|| type == String.class
|| type == Character.class
|| type == Boolean.class
|| type == Byte.class
|| type == Short.class
|| type == Integer.class
|| type == Long.class
|| type == Float.class
|| type == Double.class
|| type == Object.class;
}
ConfigManager#refreshAll最终调用AbstractConfig#refresh去更新application、monitor、module、protocol、registry、provider和consumer的配置。
public void refreshAll() {
// refresh all configs here,
getApplication().ifPresent(ApplicationConfig::refresh);
getMonitor().ifPresent(MonitorConfig::refresh);
getModule().ifPresent(ModuleConfig::refresh);
getProtocols().values().forEach(ProtocolConfig::refresh);
getRegistries().values().forEach(RegistryConfig::refresh);
getProviders().values().forEach(ProviderConfig::refresh);
getConsumers().values().forEach(ConsumerConfig::refresh);
}
ServiceConfig#doExport用于服务暴露
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
}
if (exported) {
return;
}
exported = true;
if (StringUtils.isEmpty(path)) {
path = interfaceName;
}
doExportUrls();
}
ServiceConfig#doExportUrls 将RegistryConfig转换成URL,再根据不同的protocol协议暴露服务。
注册的URL格式如下:
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-provider-demo
&dubbo=2.0.2&pid=4400&qos.enable=false®istry=zookeeper&release=2.7.3×tamp=1636259747478
loadRegistries:获取注册中心实例。
doExportUrlsFor1Protocol:循环不同协议暴露服务。
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
ApplicationModel.initProviderModel(pathKey, providerModel);
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
在本地暴露的URL:
injvm://127.0.0.1/gdut.DemoService?anyhost=true&application=dubbo-provider-demo
&bean.name=ServiceBean:gdut.DemoService:default&bind.ip=192.168.1.103&bind.port=20881&deprecated=false
&dubbo=2.0.2&dynamic=true&generic=false&interface=gdut.DemoService&methods=sayHello,sayHelloAsync&pid=21424
&qos.enable=false®ister=true&release=2.7.3&revision=default&side=provider×tamp=1636260108865&version=default
ServiceConfig#doExportUrlsFor1Protocol会调用exportLocal方法显示指定injvm协议进行暴露,将服务保存在内存中,同一个jvm的消费方可以直接调用,避免跨进程通信。
private void exportLocal(URL url) {
URL local = URLBuilder.from(url)
.setProtocol(LOCAL_PROTOCOL)
.setHost(LOCALHOST_VALUE)
.setPort(0)
.build();
Exporter<?> exporter = protocol.export(
PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}
以下是ServiceConfig#doExportUrlsFor1Protocol最关键的代码:
(1)通过动态代理生成一个Invoker。Dubbo有两种代理实现。
a.JavassistProxyFactory:创建wrapper子类,在子类实现invokeMethod方法,方法体内会为每个ref方法都做方法名和方法参数的校验,如果匹配直接调用即可。b.JdkProxyFactory:通过反射获取真实对象的方法。
ProxyFactory的SPI默认实现是JavassistProxyFactory。
调用Protocol#export生成一个Exporter对象。Protocol的默认SPI实现是DubboProtocol,这里的protocol是生成的Protocol$Adaptive代理类。这个代理类有三个包装类:ProtocolFilterWrapper,ProtocolListenerWraper和QosProtocolWrapper。最终会去调用RegistryProtocol#export(这里的URL协议是registry)。
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
// For providers, this is used to enable custom proxy to generate invoker
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
RegistryProtocol#export
调用doLocalExport暴露provider服务。调用register方法向注册中心注册服务。
registryUrl:
zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-provider-demo&dubbo=2.0.2&export=dubbo://192.168.1.103:20881/gdut.DemoService?anyhost=true&application=dubbo-provider-demo&bean.name=ServiceBean:gdut.DemoService:default&bind.ip=192.168.1.103&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=gdut.DemoService&methods=sayHello,sayHelloAsync&pid=20348&qos.enable=false®ister=true&release=2.7.3&revision=default&side=provider×tamp=1636295108119&version=default&pid=20348&qos.enable=false&release=2.7.3×tamp=1636295108116
providerUrl:
dubbo://192.168.1.103:20881/gdut.DemoService?anyhost=true&application=dubbo-provider-demo&bean.name=ServiceBean:gdut.DemoService:default&bind.ip=192.168.1.103&bind.port=20881&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=gdut.DemoService&methods=sayHello,sayHelloAsync&pid=20348&qos.enable=false®ister=true&release=2.7.3&revision=default&side=provider×tamp=1636295108119&version=default
overrideSubscribeUrl :
provider://192.168.1.103:20881/gdut.DemoService?anyhost=true&application=dubbo-provider-demo&bean.name=ServiceBean:gdut.DemoService:default&bind.ip=192.168.1.103&bind.port=20881&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=gdut.DemoService&methods=sayHello,sayHelloAsync&pid=20348&qos.enable=false®ister=true&release=2.7.3&revision=default&side=provider×tamp=1636295108119&version=default
(1)doLocalExport:调用具体的协议如Dubbo进行服务暴露,创建NettyServer监听端口和保存服务实例。
(2)getRegistry:创建注册中心对象,与注册中心创建TCP连接。
(3)register:注册服务元数据到注册中心。
(4)subscribe:订阅configurators节点,监听服务动态属性变更事件。
(5)DestroyableExporter:服务销毁如关闭端口、销毁注册服务信息等。
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
URL providerUrl = getProviderUrl(originInvoker);
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
// the same service. Because the subscribed is cached key with the name of the service, it causes the
// subscription information to cover.
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
//export invoker
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
final Registry registry = getRegistry(originInvoker);
final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
registryUrl, registeredProviderUrl);
//to judge if we need to delay publish
boolean register = registeredProviderUrl.getParameter("register", true);
if (register) {
register(registryUrl, registeredProviderUrl);
providerInvokerWrapper.setReg(true);
}
// Deprecated! Subscribe to override rules in 2.6.x or before.
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<>(exporter);
}
RegistryProtocol#doLocalExport通过providerUrl获取一个Invoker,然后最终调用DubboProtocol#export(因为这里的URL是dubbo协议的)。调用真正的protocol前,也会先去调用包装类:ProtocolFilterWrapper,ProtocolListenerWraper和QosProtocolWrapper
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
ProtocolListenerWrapper#export
DubboProtocl生成的Exporter经过ListenerExporterWrapper包装会添加监听器ExporterListener,
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
}
ProtocolFilterWrapper#export
触发buildInvokerChain进行拦截器链构造。
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
DubboProtocol#export
创建一个NettyServer用于在客户端和服务端传输数据。
Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);
//export an stub service for dispatching event
Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
if (isStubSupportEvent && !isCallbackservice) {
String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
if (logger.isWarnEnabled()) {
logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
"], has set stubproxy support event ,but no stub methods founded."));
}
} else {
stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
}
}
openServer(url);
optimizeSerialization(url);
return exporter;
}
调用DubbotProtocol#createServer创建ExchangeServer。
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client can export a service which's only for server to invoke
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
ExchangeServer server = serverMap.get(key);
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
if (server == null) {
serverMap.put(key, createServer(url));
}
}
} else {
// server supports reset, use together with override
server.reset(url);
}
}
}
DubboProtocol#createServer获取的url如下:
dubbo://192.168.1.103:20881/gdut.DemoService?anyhost=true&application=dubbo-provider-demo
&bean.name=ServiceBean:gdut.DemoService:default&bind.ip=192.168.1.103&bind.port=20881
&channel.readonly.sent=true&codec=dubbo&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&heartbeat=60000
&interface=gdut.DemoService&methods=sayHello,sayHelloAsync&pid=15800&qos.enable=false®ister=true&release=2.7.3
&revision=default&side=provider×tamp=1636275140059&version
判断url是否有server参数,表示创建server的类型,如果没有,默认值是netty。
private ExchangeServer createServer(URL url) {
url = URLBuilder.from(url)
// send readonly event when server closes, it's enabled by default
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
// enable heartbeat by default
.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
.addParameter(CODEC_KEY, DubboCodec.NAME)
.build();
String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
}
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
str = url.getParameter(CLIENT_KEY);
if (str != null && str.length() > 0) {
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return server;
}
Exchangers#bind
如果没有指定Exchanger的类型,通过getExhanger方法获得一个默认HeaderExchanger。
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
return getExchanger(url).bind(url, handler);
}
public static Exchanger getExchanger(URL url) {
String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
return getExchanger(type);
}
public static Exchanger getExchanger(String type) {
return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
}
HeaderExchanger#bind
public class HeaderExchanger implements Exchanger {
public static final String NAME = "header";
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
}
Transporters#bind
创建一个Transporter$Adaptive代理类,调用NettyTransporter#bind创建NettyServer。
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handlers == null || handlers.length == 0) {
throw new IllegalArgumentException("handlers == null");
}
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else {
handler = new ChannelHandlerDispatcher(handlers);
}
return getTransporter().bind(url, handler);
}
public static Transporter getTransporter() {
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
调用父类AbstractServer构造函数。
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
// the handler will be warped: MultiMessageHandler->HeartbeatHandler->handler
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
获取开启NettyServer需要绑定的IP和端口。调用NettySever#doOpen创建一个NettyServer。
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
bindIp = ANYHOST_VALUE;
}
bindAddress = new InetSocketAddress(bindIp, bindPort);
this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
try {
doOpen();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
}
} catch (Throwable t) {
throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
+ " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
}
//fixme replace this with better method
DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
}
NettyServer#doOpen
初始化开启一个NettyServer。
boss线程组的线程数默认是1;worker线程组的线程数是处理器数+1。
int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32);
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
new DefaultThreadFactory("NettyServerWorker", true));
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
RegistryProtocol#register
这里获得的registry是ZookeeperRegistry。ZookeeperRegistry是FailbackRegistry的子类,会调用FailbackRegistry的注册方法。
public void register(URL registryUrl, URL registeredProviderUrl) {
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registeredProviderUrl);
}
FailbackRegistry#register
FailbackRegistry是AbstractRegistry的子类,super.register调用的是父类的注册方法,把这个url放入一个集合中。
FailbackRegistry的doRegister方法是一个模板抽象方法,没有具体的实现。这里调用的还是实现类ZookeeperRegistry的实现。
public void register(URL url) {
super.register(url);
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// Sending a registration request to the server side
doRegister(url);
} catch (Exception e) {
//省略
}
}
ZookeeperRegistry#doRegister 是调用Zookeeper客户端去创建节点,也就是注册服务。
public void doRegister(URL url) {
try {
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}