Dubbo之服务暴露
前言
本文 Dubbo 使用版本2.7.5
Dubbo 通过使用dubbo:service
配置或@service
在解析完配置后进行服务暴露,供服务消费者消费。
Dubbo 的服务暴露有两种:
- 远程暴露
- 本地暴露
可以通过scope
显式指定暴露方式:
- none 不暴露
- remote 远程暴露
- local 本地暴露
服务暴露流程
下面是一个服务暴露的流程图:
ProxyFactory 是动态代理,用来创建 Invoker 对象,实现代理使用JavassistProxyFactory
和JdkProxyFactory
。
Invoker 是一个服务对象实例,Dubbo 框架的实体域。它可以是一个本地的实现,一个远程的实现或一个集群的实现,可以向它发起 Invoker 调用。
Protocol 是服务域,负责 Invoker 的生命周期管理,是 Invoker 暴露和引用的主要功能入口,对应该类的export
和refer
方法。
Exporter 是根据不同协议暴露 Invoker 进行封装的类,它会根据不同的协议头进行识别(比如:registry://
和dubbo://
),调用对应XXXProtocol
的export()
方法。
从上图中可以看到,Dubbo 中服务暴露分为两个大步骤:第一步通过代理将服务实例转换成 Invoker,这就是通过我们常用的反射实现。第二步将 Invoker 根据具体的协议转换成 Exporter,这是就是我们要分析的核心。从这里可以看到 Dubbo 服务对象都是围绕 Invoker 进行工作。
远程暴露
服务远程暴露从字面上理解,就是将服务跨网络进行远程通信,并非同一 JVM 中的服务进行调用。
服务最后都是转换成org.apache.dubbo.config.spring.ServiceBean
,它的UML类图:
ServiceBean
继承自ServiceConfig
,服务在ServiceConfig#doExportUrls
根据不同协议进行暴露。
通过获取所有注册中心实例(registryURLs)后,进行依次暴露,暴露操作在doExportUrlsFor1Protocol
中。
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
Map<String, String> map = new HashMap<String, String>();
// 配置信息存入 map
.....
// 获取服务URL
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
.....
String scope = url.getParameter(SCOPE_KEY);
// 如果 scope 配置为 none,则服务不进行暴露
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 本地暴露
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 远程暴露
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
// 判断是否有注册中心
if (CollectionUtils.isNotEmpty(registryURLs)) {
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
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
// 追加监控上报地址,在拦截器上报数据
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
// 日志打印
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
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
// 将服务对象转换成 Invoker
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 暴露服务,向注册中心注册服务,进入对应的 RegistryProtocol
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else { // 没有注册中心时
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 直接暴露服务
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
/**
* 存储Dubbo服务的元数据,元数据可以存储在远端配置中心和本地,默认是存储在本地
* @since 2.7.0
* ServiceData Store
*/
WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
if (metadataService != null) {
metadataService.publishServiceDefinition(url);
}
}
}
this.urls.add(url);
}
上面是代码片段为暴露服务的核心,可以看到 scope 由三个值控制是否暴露和远程或本地暴露,默认远程和本地都暴露。
在远程调用中,分为使用注册中心暴露和直接暴露(默认dubbo协议),它们之间的区别在url上:
- 无注册中心:dubbo://192.168.3.19:20880/xxxx
- 有注册中心:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=provider&dubbo=2.0.2&export=dubbo://192.168.3.19:20880/xxxx
无注册中心的直接暴露服务。
有注册中心的先创建注册中心,再得到 export 的服务地址,然后暴露服务,当服务暴露成功后把服务元数据注册到注册中心。
代码中protocol#export
会根据服务 url 的请求头进行区分不同XXXProtocol#export
的逻辑,比如。
目前 Dubbo 中有以下几种:
本地暴露
同一个应用中,可能既要提供服务远程暴露给其他应用引用,也要给自身提供引用。如果只提供远程暴露的话,当自身应用需要引用自身的服务时,需要通过远程通信访问,那么这大大浪费网络资源。这是就需要用 injvm 协议暴露,就是我们所说的本地暴露,无需跨网络远程通信,可以更好的节省资源。
通过上面代码中,我们知道本地暴露调用的是ServiceConfig#exportLocal
方法。
本地暴露会指定 injvm 协议,并且 host 指定为本地127.0.0.1
和端口号为0。
protocol.export 调用 InjvmProtocol#export
实现:
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
export 中返回了 InjvmExporter 实例化对象。
class InjvmExporter<T> extends AbstractExporter<T> {
private final String key;
private final Map<String, Exporter<?>> exporterMap;
InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
super(invoker);
this.key = key;
this.exporterMap = exporterMap;
exporterMap.put(key, this);
}
@Override
public void unexport() {
super.unexport();
exporterMap.remove(key);
}
}
本地暴露就比较简单,将 Invoker 直接保存在 InjvmExporter 的 exporterMap 中。
最后
本篇对 Dubbo 的服务暴露流程进行了分析,核心点就是开篇图中的得到 Invoker 后转化到 Export。其中更多详细的地方,由于展开后篇幅太大,不能一一写到,会在今后相关的 Dubbo 文章再进行讲解。
个人博客: https://ytao.top
关注公众号 【ytao】,更多原创好文
Dubbo之服务暴露的更多相关文章
- dubbo服务暴露过程
所谓服务暴露最终做的事情:绑定网络端口,开启serversokect服务以接收外部请求 服务暴露时序图 本地暴露 远程暴露 整体总结 dubbo服务提供者暴露服务的主过程:首先 ServiceConf ...
- Dubbo服务暴露分析
Dubbo的服务暴露是一个重要的特性,了解其机制很重要.之前有很多人写了有关的源代码分析,在本文中不再重新分析.官方文档中的一篇写的就很好,本文主要是有关内容进行补充与总结. 传送门:服务导出 为什么 ...
- dubbo服务暴露原理-远程暴露
1.与本地暴露相比,远程暴露也大同小异 我们已经到了第三个关键词Procotol我们来看看他的继承体系图 按照经典图的路线,我们下一个关键词应该就是Server了,从方法名openServer(url ...
- dubbo服务暴露原理
1.发布流程 暴露本地服务 暴露远程服务 启动netty 连接zookeeper 到zookeeper注册 监听zookeeper 2.官方文档 3.看输出日志,就会发现在暴露本地服务之前,有一句很重 ...
- 7.1 服务暴露前的准备-ServiceBean的装配
dubbo的服务暴露以第一章 第一个dubbo项目中的dubbo-demo-provider来讲述. 列出dubbo-demo-provider的xml配置: <?xml version=&qu ...
- Dubbo之服务注册
在上一篇文章Dubbo之服务暴露分析中介绍了当远程暴露时,如果有注册中心,需要在服务暴露后再将服务注册到注册中心.该篇将介绍该功能的有关步骤. 注册的起点 在RegistryProtocol.expo ...
- Dubbo之服务消费原理
前言 上篇文章<Dubbo之服务暴露>分析 Dubbo 服务是如何暴露的,本文接着分析 Dubbo 服务的消费流程.主要从以下几个方面进行分析:注册中心的暴露:通过注册中心进行服务消费通知 ...
- Dubbo 服务暴露注册流程
Dubbo的应用会在启动时完成服务注册或订阅(不论是生产者,还是消费者)如下图所示. 图中小方块Protocol, Cluster, Proxy, Service, Container, Regist ...
- dubbo服务暴露
想熟悉dubbo源码,首先要知道dubbo extensionLoader,而dubbo的这种扩展机制,是根据java spi衍生而来. 这是基础,但是我放在后面说明. 一:dubbo demo pr ...
随机推荐
- Jenkins 2 如何使用 PowerShell 以及自定 build fail (指定 exit code)
Jenkins 除了用來做為 CI(持續性整合) 工具外,也可以與其他 plugin 配合達成其他目的(e.g.IIS restart.檔案壓縮備份-),今天就來看看可以怎麼與 PowerShell ...
- 关于(Building tool)的认识以及当下流行的Building tool有哪些?
1.Building tool是什么? (Building tool)构建工具是一种工具,它负责构建流程的所有内容,并自动化与构建项目相关的所有内容.它致力于以下任务: 生成源代码(如果在软件项目中使 ...
- Scrapy初体验(一) 环境部署
系统选择centOs 7,Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中. 其最初是为了 页面抓取 (更确切来说, ...
- 【51nod1462】树据结构
Source and Judge 51nod1462 Analysis 请先思考后再展开 dffxtz师兄出的题 做法一:暴力树剖+分块,时间复杂度为 $O(nlognsqrt n)$ 做法二:利用矩 ...
- AndroidImageSlider
最核心的类是SliderLayout,他继承自相对布局,包含了可以左右滑动的SliderView,以及页面指示器PagerIndicator.这两部分都可以自定义. AndroidImageSlide ...
- selenium+requests进行cookies保存读取操作
看这篇文章之前大家可以先看下我的上一篇文章:cookies详解 本篇我们就针对上一篇来说一下cookies的基本应用 使用selenium模拟登陆百度 from selenium import web ...
- XML转换
找到两个不错的c#的关于XML转string和将string格式化XML输出 感谢以下两位的分享: [string格式化XML输出]http://blog.csdn.net/a497785609/ar ...
- C:数组习题
与字符串处理有关的函数: 头文件:<stdio.h> gets().puts() 头文件:<string.h> (1).字符串长度测量函数 :strlen(字符数组名) ...
- IDEA打包web项目为war,通过本地Tomcat启动war
1.打包 ①idea的打包很简单,网上教程也很多,简单说下:project struct-->artifact-->+-->Web Application:Archive--> ...
- 聊一聊关于MySQL的count(*)
0.背景 自从大家对于MySQL数据库的稳定性有了更高的追求后,经常有小伙伴有这样的疑问,对于count(*)这样的操作,有没有正确的姿势,或者有没有可以优化的地方? 但答案比较残酷,如果已经使用了正 ...