书接上回dubbo整合spring后会往Spring里面添加ServiceBean.class的beanDefinition。
当ServiceBean被Spring实例化后,会回调其Bean的生命周期方法onApplicationEvent()
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 当前服务没有被导出并且没有卸载,才导出服务
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
// 服务导出(服务注册)
export();
}
}
1、加载配置
这么多配置优先级怎么确定?
2、启动tomcat、nettry 开放远程服务端口
3、将本地的服务链接写入注册中心供,服务调用放发现服务
4、监听配置中心的变更。例如修改了timeout、心跳检测时间、限流等等
ServiceConfig#checkAndUpdateSubConfigs() 该方法会检查并更新最新Dubbo服务的配置
public synchronized void export() {
checkAndUpdateSubConfigs();
// 检查服务是否需要导出
if (!shouldExport()) {
return;
}
// 检查是否需要延迟发布
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
// 导出服务
doExport();
}
}
1、将公共的配置赋值给ServiceBean(ServiceConfiig)的属性
2、启动配置中心并更新本地配置
3、将多个地方获取的配置通过this.refresh();刷新到ServiceBean中
public void checkAndUpdateSubConfigs() {
// Use default configs defined explicitly on global configs
// ServiceConfig中的某些属性如果是空的,那么就从ProviderConfig、ModuleConfig、ApplicationConfig中获取
// 补全ServiceConfig中的属性
completeCompoundConfigs();
// Config Center should always being started first.
// 从配置中心获取配置,包括应用配置和全局配置
// 把获取到的配置放入到Environment中的externalConfigurationMap和appExternalConfigurationMap中
// 并刷新所有的XxConfig的属性(除开ServiceConfig),刷新的意思就是将配置中心的配置覆盖调用XxConfig中的属性
startConfigCenter();
checkDefault();
checkProtocol();
checkApplication();
// if protocol is not injvm checkRegistry
// 如果protocol不是只有injvm协议,表示服务调用不是只在本机jvm里面调用,那就需要用到注册中心
if (!isOnlyInJvm()) {
checkRegistry();
}
// 刷新ServiceConfig
this.refresh();
// 如果配了metadataReportConfig,那么就刷新配置
checkMetadataReport();
if (StringUtils.isEmpty(interfaceName)) {
throw new IllegalStateException(" interface not allow null!");
}
// 当前服务对应的实现类是一个GenericService,表示没有特定的接口
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
// 加载接口
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
// 刷新MethodConfig,并判断MethodConfig中对应的方法在接口中是否存在
checkInterfaceAndMethods(interfaceClass, methods);
// 实现类是不是该接口类型
checkRef();
generic = Boolean.FALSE.toString();
}
// local和stub一样,不建议使用了
if (local != null) {
// 如果本地存根为true,则存根类为interfaceName + "Local"
if (Boolean.TRUE.toString().equals(local)) {
local = interfaceName + "Local";
}
// 加载本地存根类
Class<?> localClass;
try {
localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
// 本地存根
if (stub != null) {
// 如果本地存根为true,则存根类为interfaceName + "Stub"
if (Boolean.TRUE.toString().equals(stub)) {
stub = interfaceName + "Stub";
}
Class<?> stubClass;
try {
stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
}
}
// 检查local和stub
checkStubAndLocal(interfaceClass);
// 检查mock
checkMock(interfaceClass);
}
private void completeCompoundConfigs() {
// 如果配置了provider,那么则从provider中获取信息赋值其他属性,在这些属性为空的情况下
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());
}
}
// 如果配置了module,那么则从module中获取信息赋值其他属性,在这些属性为空的情况下
if (module != null) {
if (registries == null) {
setRegistries(module.getRegistries());
}
if (monitor == null) {
setMonitor(module.getMonitor());
}
}
// 如果配置了application,那么则从application中获取信息赋值其他属性,在这些属性为空的情况下
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);
}
// 如果配置了ConfigCenter
if (this.configCenter != null) {
// 从其他位置获取配置中心的相关属性信息,比如配置中心地址
// TODO there may have duplicate refresh
this.configCenter.refresh();
// 属性更新后,从远程配置中心获取数据(应用配置,全局配置)
prepareEnvironment();
}
// 从配置中心取到配置数据后,刷新所有的XxConfig中的属性,除开ServiceConfig
ConfigManager.getInstance().refreshAll();
}
// 刷新XxConfig
// 一个XxConfig对象的属性可能是有值的,也可能是没有值的,这时需要从其他位置获取属性值,来进行属性的覆盖
// 覆盖的优先级,从大到小为系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
// 以ServiceConfig为例,ServiceConfig中包括很多属性,比如timeout
// 但是在定义一个Service时,如果在注解上没有配置timeout,那么就会其他地方获取timeout的配置
// 比如可以从系统变量->配置中心应用配置->配置中心全局配置->注解或xml中定义->dubbo.properties文件
// refresh是刷新,将当前ServiceConfig上的set方法所对应的属性更新为优先级最高的值
public void refresh() {
try {
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
// 表示XxConfig对象本身- AbstractConfig
Configuration config = new ConfigConfigurationAdapter(this); // ServiceConfig
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(); //ServiceBean
for (Method method : methods) {
// 是不是setXX()方法
if (MethodUtils.isSetter(method)) {
// 获取xx配置项的value
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));
}
// 是不是setParameters()方法
} else if (isParametersSetter(method)) {
// 获取parameter配置项的value
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
if (StringUtils.isNotEmpty(value)) {
Map<String, String> map = invokeGetParameters(getClass(), this);
map = map == null ? new HashMap<>() : map;
map.putAll(convert(StringUtils.parseParameters(value), ""));
invokeSetParameters(getClass(), this, map);
}
}
}
} catch (Exception e) {
logger.error("Failed to override ", e);
}
}
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId())
_/**
本质就是通过linkedList维护了各个配置的顺序。
public CompositeConfiguration getConfiguration(String prefix, String id) {
CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
// Config center has the highest priority
// JVM环境变量
compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
// 操作系统环境变量
compositeConfiguration.addConfiguration(this.getEnvironmentConfig(prefix, id));
// 配置中心APP配置
compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
// 配置中心Global配置
compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
// dubbo.properties中的配置
compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
return compositeConfiguration;
}
最后获取配置会调用到 CompositeConfiguration#getInternalProperty
遍历前面按顺序排好优先级的list。
@Override
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;
}
}
注册中心的url会以registry开头。
url参数中registry=zookeeper,可以猜测后面肯定用到了Dubbo-Spi机制有个Zooeeper的注册类
/**
*
* Load the registry and conversion it to {@link URL}, the priority order is: system property > dubbo registry config
*
* @param provider whether it is the provider side
* @return
*/
protected List<URL> loadRegistries(boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
if (CollectionUtils.isNotEmpty(registries)) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
// 如果注册中心没有配地址,则地址为0.0.0.0
if (StringUtils.isEmpty(address)) {
address = ANYHOST_VALUE;
}
// 如果注册中心的地址不是"N/A"
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
// 把application中的参数放入map中,注意,map中的key是没有prefix的
appendParameters(map, application);
// 把config中的参数放入map中,注意,map中的key是没有prefix的
// config是RegistryConfig,表示注册中心
appendParameters(map, config);
// 此处path值固定为RegistryService.class.getName(),因为现在是在加载注册中心
map.put(PATH_KEY, RegistryService.class.getName());
// 把dubbo的版本信息和pid放入map中
appendRuntimeParameters(map);
// 如果map中如果没有protocol,那么默认为dubbo
if (!map.containsKey(PROTOCOL_KEY)) {
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
}
// 构造注册中心url,地址+参数
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = URLBuilder.from(url)
.addParameter(REGISTRY_KEY, url.getProtocol())
.setProtocol(REGISTRY_PROTOCOL)
.build();
// 到此为止,url的内容大概为:
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936®istry=zookeeper×tamp=1584886077813
// 该url表示:使用registry协议调用org.apache.dubbo.registry.RegistryService服务
// 参数为application=dubbo-demo-annotation-provider&dubbo=2.0.2&pid=269936®istry=zookeeper×tamp=1584886077813
// 这里是服务提供者和服务消费者区别的逻辑
// 如果是服务提供者,获取register的值,如果为false,表示该服务不注册到注册中心
// 如果是服务消费者,获取subscribe的值,如果为false,表示该引入的服务不订阅注册中心中的数据
if ((provider && url.getParameter(REGISTER_KEY, true))
|| (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
Dubbo中的服务都是以URL的方式来表示
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
// protocolConfig表示某个协议,registryURLs表示所有的注册中心
// 如果配置的某个协议,没有配置name,那么默认为dubbo
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
// 这个map表示服务url的参数
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
appendRuntimeParameters(map);
// 监控中心参数
appendParameters(map, metrics);
// 应用相关参数
appendParameters(map, application);
// 模块相关参数
appendParameters(map, module);
// remove 'default.' prefix for configs from ProviderConfig
// appendParameters(map, provider, Constants.DEFAULT_KEY);
// 提供者相关参数
appendParameters(map, provider);
// 协议相关参数
appendParameters(map, protocolConfig);
// 服务本身相关参数
appendParameters(map, this);
...............
}
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
...............
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
// 如果是injvm,则不需要进行注册中心注册
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
// 该服务是否是动态,对应zookeeper上表示是否是临时节点,对应dubbo中的功能就是静态服务
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
// 拿到监控中心地址
URL monitorUrl = loadMonitor(registryURL);
// 当前服务连接哪个监控中心
if (monitorUrl != null) {
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
// 服务的register参数,如果为true,则表示要注册到注册中心
if (logger.isInfoEnabled()) {
if (url.getParameter(REGISTER_KEY, true)) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
} else {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
}
// For providers, this is used to enable custom proxy to generate invoker
// 服务使用的动态代理机制,如果为空则使用javassit
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
// 生成一个当前服务接口的代理对象
// 使用代理生成一个Invoker,Invoker表示服务提供者的代理,可以使用Invoker的invoke方法执行服务
// 对应的url为 registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&export=http%3A%2F%2F192.168.40.17%3A80%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D80%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D19472%26release%3D%26side%3Dprovider%26timestamp%3D1585207994860&pid=19472®istry=zookeeper×tamp=1585207994828
// 这个Invoker中包括了服务的实现者、服务接口类、服务的注册地址(针对当前服务的,参数export指定了当前服务)
// 此invoker表示一个可执行的服务,调用invoker的invoke()方法即可执行服务,同时此invoker也可用来导出
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// invoker.invoke(Invocation)
// DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
// 1. 先使用RegistryProtocol进行服务注册
// 2. 注册完了之后,使用DubboProtocol进行导出
// 到此为止做了哪些事情? ServiceBean.export()-->刷新ServiceBean的参数-->得到注册中心URL和协议URL-->遍历每个协议URL-->组成服务URL-->生成可执行服务Invoker-->导出服务
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
...............
}
核心方法就是Exporter> exporter = protocol.export(wrapperInvoker);
wrapperInvoker.getUrl() 返回的Url,getProtocol()方法返回的就是url的协议:registry
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// 导出服务
// registry:// ---> RegistryProtocol
// zookeeper:// ---> ZookeeperRegistry
// dubbo:// ---> DubboProtocol
// registry://xxx?xx=xx®istry=zookeeper ---> zookeeper://xxx?xx=xx 表示注册中心
URL registryUrl = getRegistryUrl(originInvoker); // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-provider-application&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.40.17%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddubbo-demo-provider-application%26bean.name%3DServiceBean%3Aorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.40.17%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26logger%3Dlog4j%26methods%3DsayHello%26pid%3D27656%26release%3D2.7.0%26side%3Dprovider%26timeout%3D3000%26timestamp%3D1590735956489&logger=log4j&pid=27656&release=2.7.0×tamp=1590735956479
// 得到服务提供者url,表示服务提供者
URL providerUrl = getProviderUrl(originInvoker); // dubbo://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-provider-application&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&logger=log4j&methods=sayHello&pid=27656&release=2.7.0&side=provider&timeout=3000×tamp=1590735956489
// 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.
// overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
// 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
// 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
// OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
// 在这个方法里会利用providerConfigurationListener和serviceConfigurationListener去重写providerUrl
// providerConfigurationListener表示应用级别的动态配置监听器,providerConfigurationListener是RegistyProtocol的一个属性
// serviceConfigurationListener表示服务级别的动态配置监听器,serviceConfigurationListener是在每暴露一个服务时就会生成一个
// 这两个监听器都是新版本中的监听器
// 新版本监听的zk路径是:
// 服务: /dubbo/config/dubbo/org.apache.dubbo.demo.DemoService.configurators节点的内容
// 应用: /dubbo/config/dubbo/dubbo-demo-provider-application.configurators节点的内容
// 注意,要喝配置中心的路径区分开来,配置中心的路径是:
// 应用:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService/dubbo.properties节点的内容
// 全局:/dubbo/config/dubbo/dubbo.properties节点的内容
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// export invoker
// 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
........
}
@SuppressWarnings("unchecked")
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);
// protocol属性的值是哪来的,是在SPI中注入进来的,是一个代理类
// 这里实际利用的就是DubboProtocol或HttpProtocol去export NettyServer
// 为什么需要ExporterChangeableWrapper?方便注销已经被导出的服务
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
invokerDelegate.getUrl().getProtocol() 已经是dubbo
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
// 构造一个Exporter进行服务导出
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 {
// 服务的stub方法
stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
}
}
// 开启NettyServer
openServer(url); //请求--->invocation--->服务key--->exporterMap.get(key)--->exporter--->invoker--->invoker.invoke(invocation)-->执行服务
// 特殊的一些序列化机制,比如kryo提供了注册机制来注册类,提高序列化和反序列化的速度
optimizeSerialization(url);
return exporter;
}
private void openServer(URL url) {
// find server.
String key = url.getAddress(); // 获得ip地址和port, 192.168.40.17:20880
// NettyClient, NettyServer
//client can export a service which's only for server to invoke
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
// 缓存Server对象
ExchangeServer server = serverMap.get(key);
// DCL,Double Check Lock
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
if (server == null) {
// 创建Server,并进行缓存
serverMap.put(key, createServer(url));
}
}
} else {
// server supports reset, use together with override
// 服务重新导出时,就会走这里
server.reset(url);
}
}
}
/**
* Init and start netty server
*
* @throws Throwable
*/
@Override
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());
// 这里就会拿到DubboCodec,接收到数据之后就会进行解码
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();
}
1、会将服务的url简化一下。因为很多配置项不需要暴露出去。只是在本地启动服务的时候需要
2、将简化好的服务写入注册中心既可
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
........
// url to registry
// 得到注册中心-ZookeeperRegistry
final Registry registry = getRegistry(originInvoker);
// 得到存入到注册中心去的providerUrl,会对服务提供者url中的参数进行简化
final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
// 将当前服务提供者Invoker,以及该服务对应的注册中心地址,以及简化后的服务url存入ProviderConsumerRegTable
ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
registryUrl, registeredProviderUrl);
//to judge if we need to delay publish
//是否需要注册到注册中心
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 注册服务,把简化后的服务提供者url注册到registryUrl中去
register(registryUrl, registeredProviderUrl);
providerInvokerWrapper.setReg(true);
}
..........
}
public void register(URL registryUrl, URL registeredProviderUrl) {
Registry registry = registryFactory.getRegistry(registryUrl);
// 调用ZookeeperRegistry的register方法
registry.register(registeredProviderUrl);
}
@Override
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);
}
}
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
..................
// overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
// 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider://,增加参数category=configurators&check=false
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
// 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
// OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
// 针对老版本的动态配置,需要把overrideSubscribeListener绑定到overrideSubscribeUrl上去进行监听
// 兼容老版本的配置修改,利用overrideSubscribeListener去监听旧版本的动态配置变化
// 监听overrideSubscribeUrl provider://192.168.40.17:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.demo.DemoService&bind.ip=192.168.40.17&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=416332&release=&side=provider×tamp=1585318241955
// 那么新版本的providerConfigurationListener和serviceConfigurationListener是在什么时候进行订阅的呢?在这两个类构造的时候
// Deprecated! Subscribe to override rules in 2.6.x or before.
// 老版本监听的zk路径是:/dubbo/org.apache.dubbo.demo.DemoService/configurators/override://0.0.0.0/org.apache.dubbo.demo.DemoService?category=configurators&compatible_config=true&dynamic=false&enabled=true&timeout=6000
// 监听的是路径的内容,不是节点的内容
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
..................
}
如果配置变更了会重新的导出,但是不会重新的启动nettry服务。端口和ip是允许修改的
public <T> void reExport(final Invoker<T> originInvoker, URL newInvokerUrl) {
// 根据newInvokerUrl进行导出
// update local exporter
ExporterChangeableWrapper exporter = doChangeLocalExport(originInvoker, newInvokerUrl);
// 获取准确的ProviderUrl
// update registry
URL registryUrl = getRegistryUrl(originInvoker);
// 对于一个服务提供者url,在注册到注册中心时,会先进行简化
final URL registeredProviderUrl = getRegisteredProviderUrl(newInvokerUrl, registryUrl);
//decide if we need to re-publish
// 根据getServiceKey获取ProviderInvokerWrapper
ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.getProviderWrapper(registeredProviderUrl, originInvoker);
// 生成一个新的ProviderInvokerWrapper
ProviderInvokerWrapper<T> newProviderInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
/**
* Only if the new url going to Registry is different with the previous one should we do unregister and register.
* 如果新的服务提供者url简化后的url和这个服务之前的服务提供者url简化后的url不相等,则需要把新的简化后的服务提供者url注册到注册中心去
*/
if (providerInvokerWrapper.isReg() && !registeredProviderUrl.equals(providerInvokerWrapper.getProviderUrl())) {
unregister(registryUrl, providerInvokerWrapper.getProviderUrl());
register(registryUrl, registeredProviderUrl);
newProviderInvokerWrapper.setReg(true);
}
exporter.setRegisterUrl(registeredProviderUrl);
}