目录
在spring.handlers文件中可以看到DubboNamespaceHandler的位置
因为要看服务发布,所以直接看service标签对应的类ServiceBean。
ServiceBean 这个类,分别实现了 InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener,BeanNameAware, ApplicationEventPublisherAware
- InitializingBean 接口为 bean 提供了初始化方法的方式,它只包括 afterPropertiesSet 方法,凡是继承该接口的类,在初始化 bean 的时候会执行该方法。被重写的方法为 afterPropertiesSet
- DisposableBean 被重写的方法为 destroy ,bean 被销毁的时候,spring 容器会自动执行 destory 方法,比如释放资源
- ApplicationContextAware 实现了这个接口的 bean,当 spring 容器初始化的时候,会自动的将 ApplicationContext 注入进来
- ApplicationListener ApplicationEvent 事件监听,spring 容器启动后会发一个事件通知。被重写的方法为: onApplicationEvent ,onApplicationEvent方法传入的对象是 ContextRefreshedEvent。这个对象是当 Spring 的上下文被刷新或者加载完毕的时候触发的。因此服务就是在Spring 的上下文刷新后进行导出操作的
- BeanNameAware 获得自身初始化时,本身的 bean 的 id 属性,被重写的方法为 setBeanName
- ApplicationEventPublisherAware 这个是一个异步事件发送器。被重写的方法为 setApplicationEventPublisher ,简单来说,在 spring 里面提供了类似于消息队列的异步事件解耦功能。(典型的观察者模式的应用)
ServiceBean中服务暴露过程
服务的发布逻辑主要是通过ServiceBean中的onApplicationEvent和afterPropertiesSet实现的。
在初始化 bean 的时候会执行该方法 afterPropertiesSet ,spring 容器启动后会发一个事件通知 onApplicationEvent。
afterPropertiesSet
这里就是把 dubbo 中配置的 application 、 registry 、 service 、 protocol 等信息,加载到对应的 config实体中,便于后续的使用
onApplicationEvent
spring 容器启动之后触发,这里面做了两个事情:
- 判断服务是否已经发布过
- 如果没有发布,则调用调用 export 进行服务发布的流程,这里就是服务发布的入口。
//监听 spring上下文被刷新或者加载的时候触发
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export(); //导出、发布
}
}
export
@Override
public void export() {
super.export();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
在发布中,serviceBean重写了export 方法,实现了 一个事件的发布,并调用父类的export方法。
ServiceConfig中的export
public synchronized void export() {
checkAndUpdateSubConfigs(); //检查或更新配置
if (!shouldExport()) { //当前的服务是否需要发布 , 通过配置实现: @Service(export = false)
return;
}
if (shouldDelay()) {// 检查是否需要延时发布,通过配置 @Service(delay = 1000) 实现,单位毫秒
delayExportExecutor.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
doExport();// 如果没有配置 delay ,则直接调用 export 进行发布
}
}
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
path = interfaceName;
}
doExportUrls();
}
doExportUrls
这里做了3件事
- 记载所有配置的注册中心地址
- 遍历所有配置的协议,protocols
- 针对每种协议发布一个对应协议的服务
private void doExportUrls() {
//(N)加载注册中心,并且声称URL地址
//URL(来驱动流程的执行)->[ registry://192.168.13.106:2181/org.apache.dubbo.registry.RegsitryService/....]
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
//iterface , version ,group组成的key
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
默认为dubbo协议
String name = protocolConfig.getName(); //name =dubbo -><dubbo:protocol name="dubbo"/>
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
组装URL
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
//TODO 动态配置修改
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
发布服务
String scope = url.getParameter(SCOPE_KEY);
// don't export when none is configured
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 如果是本地发布,则直接调用exportLocal
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url); //TODO
}
// export to remote if the config is not local (export to local only when config is local)
// 发布远程服务
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
。。。。。。。。。。。。。。。。。
for (URL registryURL : registryURLs) { //registryURL: registry://ip:port...
//TODO invoker -> 代理类
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
//MetaData元数据的委托
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//wrapperInvoker : registry:///
//Protocol$Adaptive(->做适配)
// RegistryProtocol-> getExtension("registry")
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
遍历多个注册中心,进行协议的发布:调用 protocol.export(invoker)来发布这个代理,添加到 exporters 集合.
protocol.export
找到protocol 的定义处:private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
发现它是一个自适应扩展点。在方法层面上的自适应扩展,意味着它实现了对于 export 这个方法的适配。意味着这个 Protocol 是一个动态代理类,Protocol$Adaptive
会根据 url 中配置的 protocol name 来实现对应协议的适配
目前发布的 invoker(URL),实际上是一个 registry://协议,所以Protocol$Adaptive,会通过 getExtension(extName)得到一个 RegistryProtoco
RegistryProtocol.export
RegistryProtocol 是用来实现服务注册的,找到对应源码位置
//实现服务的注册和发布
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// registryUrl -> zookeeper://ip:port
URL registryUrl = getRegistryUrl(originInvoker);
// providerUrl -> dubbo:// ip:port
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.
// 订阅 override 数据。在 admin 控制台可以针对服务进行治理,比如修改权重,修改路由机制等,当注册中心有此服务的覆盖配置
注册进来时,推送消息给提供者,重新暴露服务
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
/***********************************/
//doLocalExport 本质就是去启动一个netty服务
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// 根据 invoker 中的 url 获取 Registry 实例 : zookeeperRegistry
final Registry registry = getRegistry(originInvoker);
// 获取要注册到注册中心的 URL: dubbo://ip:port
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) {// 是否配置了注册中心,如果是, 则需要注册
// 注册到注册中心的 URL
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);
}
doLocalExport
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
//bounds -chm ->computeIfAbsent if(map.get(key)==null){map.put()}
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
//orginInvoker-> InvokerDelegate(DelegateProviderMetaDataInvoker(invoker))
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
//protocol.export -> DubboProtocol.export(本质上就是 暴露一个 20880的端口)
//protocol- >Protocol$Apaptive ->QosProtocolWrapper(ProtocolListenerWrapper(ProtocolFilterWrapper(DubboProtocol(invoker))))
//将 invoker 转换为 exporter 并启动 netty 服务
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
InvokerDelegete: 是 RegistryProtocol 的一个静态内部类,该类是一个 originInvoker 的委托类,该类存储了 originInvoker,其父类 IrWrapper 还会存储 providerUrl,InvokerWrapper 会调用 originInvoker 的 invoke 方法,也会销毁 invoker。可以管理 invoker 的生命周期
根据protocol.export(invokerDelegate)可知是一个方法级别的自适应扩展点。直接定位DubboProtocol.export
DubboProtocol.export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
// 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如
//${group}/copm.gupaoedu.practice.dubbo.ISayHelloService:${version}:20880
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// 将 <key, exporter> 键值对放入缓存中
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); //openServer(url) 开启一个服务 ,暴露20880端口
optimizeSerialization(url); //优化序列化
return exporter;
}
openServer
开启一个服务,并且放入到缓存中,同一个端口上仅允许启动一个服务器实例
private void openServer(URL url) {
// 获取 host:port ,并将其作为服务器实例的 key ,用于标识当前的服务器实例
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) {// 是否在 serverMap 中缓存了
//缓存, 一个key只对应一个exchangeServer
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
//服务器已创建,则根据 url 中的配置重置服务器
server.reset(url);
}
}
}
createServer
private ExchangeServer createServer(URL 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();
//获得当前应该采用什么样的方式来发布服务, netty3, netty4, mina , grizzy,
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.
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一路找到NettyServer(url, listener)
Exchangers.bind-->getExchanger(url).bind(url, handler)-->>Transporters.bind-->getTransporter().bind-->NettyServer(url, listener)
NettyServer
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
// 获取 ip 和端口
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()));
}
doOpen
这里就是开启一个netty服务,暴露端口号
nettyServerHandler用来处理客户端传递过来的请求。
@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());
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();
}
Invoker分析
在之前的分析中,可以看到ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)中会创造一个invoker,并对其进行层层包装。
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
Invoker是Dubbo领域模型中非常重要的一个概念。
根据proxyFactory的定义可以看到这是个自适应扩展点,会生成一个动态适配器。
ProxyFactory proxyFactory =
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
ProxyFactory 它是一个spi扩展点,并且默认的扩展实现是javassit, 这个接口中有三个方法,并且都是加了@Adaptive的自适应扩展点。所以如果调用getInvoker方法,会返回一个ProxyFactory$Adaptive,
在这里通过ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(extName)获取了一个指定名称的扩展点。
在dubbo-rpc-api/resources/META-INF/com.alibaba.dubbo.rpc.ProxyFactory中,定义了javassis=JavassisProxyFactory,故调用JavassisProxyFactory的getInvoker方法。
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
Invoke本质上应该是一个代理,经过层层包装最终进行了发布。当消费者发起请求的时候,会获得这个invoker进行调用。