dubbo-服务注册
Spring自定义标签过程
https://blog.csdn.net/zzg1229059735/article/details/82669955
spring 自定义标签是一个强大的 Hook,核心组件只有 spring-core、spring-beans。
自定义标签的制作过程
xxx.xsd文件制定了 xml 文件的规范,该文件中定义标签属性及格式:
例如定义一个简单的分布式id生成器标签:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.hexup.com/schema/distributed-id"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.hexup.com/schema/distributed-id">
<xsd:element name="distributed-id">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"></xsd:attribute>
<xsd:attribute name="bizCode" type="xsd:string"></xsd:attribute>
<xsd:attribute name="length" type="xsd:int"></xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
- 在
spring.schemes中说明xxx.xsd文件的查找路径,在spring.handlers中定义该标签的解析函数入口,该函数需要继承NamespaceHandlerSupport、BeanDefinitionParser接口。
public class DistributedIdNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 注册标签解析器
registerBeanDefinitionParser("distributed-id", new DistributedIdParser());
}
}
自定义标签解析器:
public class DistributedIdParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 解析xml内的标签
String bizCode = element.getAttribute("bizCode");
int length = Integer.valueOf(element.getAttribute("length"));
String id = element.getAttribute("id");
// 创建DistributedIdFactoryBean bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
builder.getRawBeanDefinition().setBeanClass(DistributedIdFactoryBean.class);
builder.setScope(BeanDefinition.SCOPE_SINGLETON);
builder.addPropertyValue("bizCode", bizCode);
builder.addPropertyValue("length", length);
BeanDefinition beanDefinition = builder.getBeanDefinition();
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
return beanDefinition;
}
}
分布式ID生产工厂:
public class DistributedIdFactoryBean implements InitializingBean, FactoryBean<DistributedIdComponent> {
private String bizCode;
private int length;
private DistributedIdComponent distributedIdComponent;
@Override
public DistributedIdComponent getObject() throws Exception {
return distributedIdComponent;
}
@Override
public Class<?> getObjectType() {
return DistributedIdComponent.class;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public void afterPropertiesSet() throws Exception {
distributedIdComponent = new DistributedIdComponent(bizCode, length);
}
public void setBizCode(String bizCode) {
this.bizCode = bizCode;
}
public void setLength(int length) {
this.length = length;
}
}
public class DistributedIdComponent {
private String bizCode;
private int length;
public DistributedIdComponent() {
}
public DistributedIdComponent(String bizCode, int length) {
this.bizCode = bizCode;
this.length = length;
}
public String generateId() {
System.out.println("mock generate id");
return String.valueOf(System.currentTimeMillis()).substring(0, length);
}
public String getBizCode() {
return bizCode;
}
public void setBizCode(String bizCode) {
this.bizCode = bizCode;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
Dubbo 服务解析———Service标签解析
总的来说实现 Spring 自定义标签过程:
- 编写
xsd文件,定义自定义标签配置规则; - 编写
spring.schemas文件,定义自定义标签的导入形式; - 编写
NamespaceHandlerSupport用于自定义解析逻辑; - 编写
spring.handlers文件指定自定义标签的实现类。
Dubbo 自定义标签过程:
- 编写
dubbo.xsd文件,在文件中声明标签可以配置哪些属性:
<!-- service 标签定义 -->
<xsd:element name="service" type="serviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.apache.dubbo.config.ServiceConfig"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<!-- ServiceType 类型详细定义 -->
<xsd:complexType name="serviceType">
<xsd:complexContent>
<xsd:extension base="abstractServiceType">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded"/>
</xsd:choice>
<xsd:attribute name="interface" type="xsd:token" use="required">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="java.lang.Class"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The service implementation instance bean id. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="class" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service implementation class name. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="path" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service path. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="provider" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="generic" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ Generic service. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
- 编写
spring.schemas、spring.handlers文件; - 自定义
DubboNamespaceHandler,继承自NamespaceHandlerSupport,将<dubbo:service>解析为ServiceBean对象:
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 注册 Service 标签对应的 Bean 对象
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
}
}
// 解析标签属性,生成 BeanDefinition 对象并开始实例化 ServiceBean 对象
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
String id = element.getAttribute("id");
//...省略代码
// 解析ServiceBean
if (ServiceBean.class.equals(beanClass)) {
String className = element.getAttribute("class");
if (StringUtils.isNotEmpty(className)) {
RootBeanDefinition classDefinition = new RootBeanDefinition();
classDefinition.setBeanClass(ReflectUtils.forName(className));
classDefinition.setLazyInit(false);
parseProperties(element.getChildNodes(), classDefinition);
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
}
}
//...省略代码
return beanDefinition;
}
Dubbo 服务解析———服务启动
服务注册是指将启动后的服务信息写到注册中心并暴露,这样消费方就可以订阅注册中心的服务信息。
需要在 ServiceBean 对象完成加载时,开始注册和启动服务,因此 ServiceBean 实现了 ApplicationListener 接口,具体的 OnApplicationEvent() 方法定义了服务注册和启动的详细过程。
// ServiceBean.java
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
ApplicationEventPublisherAware {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
@Override
public void export() {
super.export();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
public synchronized void export() {
// 检查配置
checkAndUpdateSubConfigs();
if (!shouldExport()) {
return;
}
// 延迟注册
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
doExport();
}
}
// 服务注册核心逻辑
private void doExportUrls() {
// 加载所有的注册中心信息
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
// 解析服务路径,比如 com.yuqiao.deeplearningdubbo.analysis.base.DemoService
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);
}
}
}
doExportUrlsFor1Protocol() 方法根据 scope 配置决定如何暴露服务(本地/远程)?
//ServiceConfig.doExportUrlsFor1Protocol()
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
//协议名,默认为dubbo
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
//下面一大段代码,都是初始化服务信息
//比如dubbo application名、接口名、方法名、端、等等。
//...省略代码
//服务机器IP
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
//服务端口
Integer port = this.findConfigedPorts(protocolConfig, name, map);
//将服务信息,转化为URL对象
//将来暴露服务,都是从URL中解析的。
//url数据示例:dubbo://172.16.184.39:20880/com.yuqiao.deeplearningdubbo.analysis.base.DemoService?anyhost=true&application=provider-app&bean.name=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&bind.ip=172.16.184.39&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&methods=sayHello,sayHello2&pid=78430&release=2.7.4.1&side=provider×tamp=1644652477685
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
//...省略代码
//scope默认为null,当scope配置为none时,才不暴露
String scope = url.getParameter(SCOPE_KEY);
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 如果没有明确配置为remote,则向本地暴露服务
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 如果配置不是local,则向远程暴露服务
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
// 生成服务调用 invoker 对象
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// 包装 invoker
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//注册服务
//注意,这里的protocol是自适应类(Protocol$Adaptive),url里面的协议参数是registry
//所以,实际上是调用了RegistryProtocol.export
//最终,1、在RegistryProtocol.export里调用了DubboProtocol.export启动服务
// 2、在RegistryProtocol.export里调用了Registry.register暴露服务
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
//...省略代码
}
//...省略代码
}
}
this.urls.add(url);
}
上述 protocol 是通过 Dubbo SPI 动态加载获取的:
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
protocol.export(xxx) 方法根据参数类型识别,实际调用的是 RegistryProtocol.export(xxx) 方法,该方法内部通过 doLocalExport() 完成服务启动、register(xxx, xxx) 完成服务注册。
//RegistryProtocol.export()
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//注册中心URL,
//当前URL中的protocol=registry
//将protocol替换成注册中心的协议,如zookeeper
URL registryUrl = getRegistryUrl(originInvoker);
// 服务提供者URL
// 与registryUrl对比,这里的protocol变成了dubbo
// 数据示例:dubbo://172.16.184.39:20880/com.yuqiao.deeplearningdubbo.analysis.base.DemoService?anyhost=true&application=provider-app&bean.name=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&bind.ip=172.16.184.39&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&methods=sayHello,sayHello2&pid=92866&release=2.7.4.1&side=provider×tamp=1644830907146
URL providerUrl = getProviderUrl(originInvoker);
// ...省略代码
//导出invoker,
//这里面再次通过Protocol$Adaptive调用Protocol的扩展点
//因为providerUrl里的protocol已经变为了dubbo,所以这里面实际上调用了DubboProtocol.export。
//在DubboProtocol.export()里,实现了服务的启动
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// ...省略代码
//注册服务,核心方法在register()里
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
register(registryUrl, registeredProviderUrl);
providerInvokerWrapper.setReg(true);
}
//...省略代码
}
// RegistryProtocol.doLocalExport()
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 是 RegistryProtocol 实例化时通过 ExtensionLoader 依赖注入的,最终调用 DubboProtocol.export(xxx)
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
// RegistryProtocol.export()
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
URL providerUrl = getProviderUrl(originInvoker);
// 这里逻辑没看懂????
// 好像是在根据配置重新构造 url
// 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 = providerUrl.getParameter(REGISTER_KEY, 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);
}
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);
});
}
// DubboProtocol.export(xxx)
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);
}
}
// ※:启动对应 URL 的服务
openServer(url);
optimizeSerialization(url);
return exporter;
}
// 启动指定 URL 的服务
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);
// 单例模式启动服务,先从 serverMap 服务列表里获取服务信息,不存在就创建服务
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(xxx)
private ExchangeServer createServer(URL 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(xxx)
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);
}
// HeaderExchanger.bind(xxx)
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
// Transporters.bind(xxx)
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);
}
// 这里通过 SPI 获取自适应扩展点对象
public static Transporter getTransporter() {
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
Transporter 是一个接口,通过 Dubbo SPI 定义自适应扩展点类型:
@SPI("netty")
public interface Transporter {
/**
* Bind a server.
*
* @param url server url
* @param handler
* @return server
* @throws RemotingException
* @see org.apache.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
*/
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException;
/**
* Connect to a server.
*
* @param url server url
* @param handler
* @return client
* @throws RemotingException
* @see org.apache.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
*/
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
可以发现 Transporter 默认获取的是 nettyTransporter 对象,所以最终调用 NettyTransporter.bind(),之后就为 NettyServer 服务端启动逻辑。
public class NettyServer extends AbstractServer implements Server {
private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
private Map<String, Channel> channels; // <ip:port, channel>
private ServerBootstrap bootstrap;
private org.jboss.netty.channel.Channel channel;
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
@Override
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
bootstrap = new ServerBootstrap(channelFactory);
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
// https://issues.jboss.org/browse/NETTY-365
// https://issues.jboss.org/browse/NETTY-379
// final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
/*int idleTimeout = getIdleTimeout();
if (idleTimeout > 10000) {
pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
}*/
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
// bind
channel = bootstrap.bind(getBindAddress());
}
@Override
protected void doClose() throws Throwable {
try {
if (channel != null) {
// unbind.
channel.close();
}
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
try {
Collection<org.apache.dubbo.remoting.Channel> channels = getChannels();
if (CollectionUtils.isNotEmpty(channels)) {
for (org.apache.dubbo.remoting.Channel channel : channels) {
try {
channel.close();
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
}
}
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
try {
if (bootstrap != null) {
// release external resource.
bootstrap.releaseExternalResources();
}
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
try {
if (channels != null) {
channels.clear();
}
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
}
}
@Override
public Collection<Channel> getChannels() {
Collection<Channel> chs = new HashSet<Channel>();
for (Channel channel : this.channels.values()) {
if (channel.isConnected()) {
chs.add(channel);
} else {
channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));
}
}
return chs;
}
@Override
public Channel getChannel(InetSocketAddress remoteAddress) {
return channels.get(NetUtils.toAddressString(remoteAddress));
}
@Override
public boolean isBound() {
return channel.isBound();
}
}
Dubbo 服务解析———服务注册
服务注册就是将服务器信息保存到注册中心里。
// RegistryProtocol.export()
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
URL providerUrl = getProviderUrl(originInvoker);
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);
// 服务注册核心方法
boolean register = providerUrl.getParameter(REGISTER_KEY, 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.register()
public void register(URL registryUrl, URL registeredProviderUrl) {
//获取注册中心对象Registry
//这里的registryFactory,是自适应扩展RegistryFactory@Adaptive
//registryUrl.protocol=zookeeper,所以获取到的是ZookeeperRegistryFactory
//最后通过ZookeeperRegistryFactory.createRegistry创建并获取注册中心对象,ZookeeperRegistry
Registry registry = registryFactory.getRegistry(registryUrl);
//调用register方法,注册服务
//Registry运用的是模板方法设计模式
//底层的抽象实现FailbackRegistry,调用各个实现类的doRegister方法。如ZookeeperRegister.doRegister.
//最终通过ZookeeperRegister.doRegister,注册服务信息,即将服务信息写到注册中心里。这样其他服务就可以从注册中心读到服务信息了。
registry.register(registeredProviderUrl);
}
registryFactory 是 RegsitryFactory 接口,并标记了 @SPI、@Adaptive 注解,说明动态获取扩展类型,这里根据 URL 信息(dubbo-provider.xml 配置文件中关于注册中心的信息)最终获取 ZookeeperRegistryFactory 注册中心工厂。
为了兼容多种注册中心,采用了模板设计模式, FailbackRegistry 提供了 register() 接口,该接口由多个注册中心 zookeeper、redis 等等分别实现注册方法。
//FailbackRegistry.register()
@Override
public void register(URL url) {
//... 省略代码
try {
// 调用子类实现的模板方法,完成注册
doRegister(url);
} catch (Exception e) {
//... 省略代码
}
}
// ZookeeperRegistry.doRegistry()
public void doRegister(URL url) {
try {
// zk 服务注册
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);
}
}
// NacosRegistry.doRegistry()
public void doRegister(URL url) {
final String serviceName = getServiceName(url);
final Instance instance = createInstance(url);
execute(namingService -> namingService.registerInstance(serviceName, instance));
}
到这里为止就完成了服务的启动和注册源码分析,以 zk 为例,服务信息的存储格式为:
- /dubbo:所有数据保存在
/dubbo目录下; - /dubbo/configurators:配置类信息;
- /dubbo/consumers:消费者信息;
- /dubbo/providers:服务提供者信息;
- /dubbo/routers:路由信息;

浙公网安备 33010602011771号