Dubbo最核心功能——服务暴露的配置、使用及原理(1)

简介: Dubbo最核心功能——服务暴露的配置、使用及原理

前言

今天开始,将正式的进入Dubbo 核心功能的掌握与学习,Dubbo的最核心功能是什么?自然是其RPC功能,而该功能其实分为三个方面:

1.服务暴露

2.服务引用

3.服务调用


三者分别专注于服务提供者、服务消费者、服务调用过程。我们今天就先关注服务暴露,我们至少有这么几个疑问待解决:


服务暴露有几种,分别是怎么实现的

我们如何进行并配置服务暴露

Dubbo和Spirng框架是怎么结合的



一、服务暴露的分类

看过我们之前的文章 超简单,手把手教你搭建Dubbo工程(内附源码) 的读者,应该有所了解了。即Dubbo 实际上有两种服务暴露的类型,即远程暴露和本地暴露


1.远程暴露

远程暴露是指将服务发布到远程注册中心,供消费者调用。Dubbo支持多种远程通信协议,例如dubbo、rmi、hessian、http等。远程暴露的实现原理大致如下:

服务提供者在启动时,将服务实现类通过协议进行网络传输,发布到注册中心上。

服务消费者在启动时,从注册中心上获取到服务提供者的地址,然后通过协议进行网络通信,调用服务提供者的方法。

15b61f113d1f4fcea8abb44c2f73ee08.png

2.本地暴露

本地暴露是指将服务发布到本地JVM上,供同一JVM中的其他服务调用。Dubbo的本地通信使用了JVM内部的IPC或者共享内存等方式,从而避免了网络传输的开销。本地暴露的实现原理大致如下:

服务提供者在启动时,将服务实现类通过代理方式,注入到本地JVM的缓存中。

服务消费者在启动时,直接从本地JVM缓存中获取到服务提供者的代理对象,然后调用服务提供者的方法。

1197a3dd0904491a8985ec841f860e73.png

PS:需要注意的是,本地暴露仅限于同一JVM内的服务调用,如果需要跨进程或跨机器进行通信,仍需要使用远程通信方式


二、本地暴露

有的人会问,Dubbo提供本地调用的服务是什么意思?因为大部分人都是把Dubbo当作RPC框架使用的,为什么其还会提供本地暴露这种用法?

应该说:Dubbo的本地暴露实际上是比较少用到的,但对于一些需要高性能、低延迟、本地调用的应用场景,使用本地暴露可以提高服务的性能和可用性。从实际项目上来讲,笔者接触过一些项目,其功能全部实现了对外暴露,也就是说有很多功能,既允许外部远程调用,也能被本地其他方法调用。这个时候,本地的调用如果还走RPC的路子,不仅带来延时,还无谓地浪费了带宽,支持本地调用就很有必要了


1. 配置

在Dubbo 2.2 0版本开始,Dubbo默认在本地以JVM的方式暴露服务,当然,对于老版本而言,还需要进行一些配置。这样的话,在第一步需要在整体配置文件里开启


#开启服务暴露
dubbo.provider.export= true

其次就是在单个接口上,我们需要配置引用的源scope = local

<bean id="demoServiceTarget" class="org.apache.dubbo.samples.local.impl.DemoServiceImpl"/>
<!-- 服务提供者指定scope -->
<dubbo:service interface="org.apache.dubbo.samples.local.api.DemoService" ref="demoServiceTarget" scope="local"/>
<dubbo:reference id="demoService" interface="org.apache.dubbo.samples.local.api.DemoService"/>


2. 实现原理

我们来看进行服务暴露(或者说导出)的基础方法 ServiceConfig 下私有方法 exportUrl

private void exportUrl(URL url, List<URL> registryURLs) {
    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)) {
           // 远程导出省略.....
        }
    }
    // 不难看出,`scope` 字段采取的判断逻辑非常宽容,试想一下,如果填入 scope = "abc" 会发生什么?
    this.urls.add(url);
}

通过上述方法,我们知道了除非指定某接口就是远程暴露,否则都会进行本地暴露。那具体本地暴露是怎么回事呢?我们继续来看

private Protocol protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
String LOCAL_PROTOCOL = "injvm";
String LOCALHOST_VALUE = "127.0.0.1";
private void exportLocal(URL url) {
    URL local = URLBuilder.from(url)
        .setProtocol(LOCAL_PROTOCOL)
        .setHost(LOCALHOST_VALUE)
        .setPort(0)
        .build();
    local = local.setScopeModel(getScopeModel())
        .setServiceModel(providerModel);
    doExportUrl(local, false);
    logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}
private void doExportUrl(URL url, boolean withMetaData) {
    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    if (withMetaData) {
        invoker = new DelegateProviderMetaDataInvoker(invoker, this);
    }
    Exporter<?> exporter = protocolSPI.export(invoker);
    exporters.add(exporter);
}

不难发现,本地暴露并没有采用取巧的方式去做特殊处理,而是老老实实构建了个URL,并且像模像样的进行导出,只是这个URL的设置了协议类型为injvm,主机更是直接127.0.0.1。当然最重要的是 protocolSPI.export(invoker) 这个代码,这里的 protocolSPI 是利用的Dubbo-SPI的自适应机制,所以导致最终执行导出的实现类为 InjvmProtocol (关于Dubbo-SPI的内容可见 一篇文章让你精通Dubbo的SPI机制),我们来看 InjvmProtocol 的 export 方法

// AbstractProtocol.class
protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<>();
// InjvmProtocol.class 继承自 AbstractProtocol
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
// InjvmExporter.class
public 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 afterUnExport() {
        exporterMap.remove(key);
    }
}   

可以看到这里其实使用了一个 Map 结构存储了这样的服务暴露的结果,因为这个Map 结构存储在抽象类中,也就是说不管是本地暴露还是哪种RPC协议的暴露,实际都会存储在该位置,并没有什么不同。


目录
相关文章
|
2月前
|
XML Dubbo Java
【Dubbo3高级特性】「框架与服务」服务的异步调用实践以及开发模式
【Dubbo3高级特性】「框架与服务」服务的异步调用实践以及开发模式
99 0
|
2月前
|
负载均衡 监控 Dubbo
秒懂Dubbo接口(原理篇)
【4月更文挑战第25天】秒懂Dubbo接口(原理篇)
59 3
秒懂Dubbo接口(原理篇)
|
27天前
|
Dubbo 前端开发 Java
Dubbo3 服务原生支持 http 访问,兼具高性能与易用性
本文展示了 Dubbo3 triple 协议是如何简化从协议规范与实现上简化开发测试、入口流量接入成本的,同时提供高性能通信、面向接口的易用性编码。
16222 7
|
25天前
|
负载均衡 Dubbo Java
哈啰面试:说说Dubbo运行原理?
哈啰面试:说说Dubbo运行原理?
22 0
哈啰面试:说说Dubbo运行原理?
|
18天前
|
XML 监控 Dubbo
Dubbo怎么配置监控中心
**摘要:** 本文介绍了如何配置Dubbo的简单监控中心。首先,通过添加`&lt;dubbo:monitor protocol=&quot;registry&quot; /&gt;`到配置文件启用监控。接着,修改`dubbo.properties`设置Zookeeper地址。启动监控中心,服务提供者和消费者需添加`monitorEnabled=&quot;true&quot;`以开启监控功能。配置完成后,监控中心的Web界面能展示服务状态和性能指标,助力开发者和运维人员实时监控服务健康。
|
2月前
|
负载均衡 算法
Dubbo-负载均衡原理解析(1),一个本科渣渣是怎么逆袭从咸鱼到Offer收割机的
Dubbo-负载均衡原理解析(1),一个本科渣渣是怎么逆袭从咸鱼到Offer收割机的
|
2月前
|
XML Dubbo Java
【Dubbo3高级特性】「框架与服务」 Nacos作为注册中心-服务分组及服务分组聚合实现
【Dubbo3高级特性】「框架与服务」 Nacos作为注册中心-服务分组及服务分组聚合实现
126 0
|
2月前
|
Cloud Native Dubbo 应用服务中间件
【Dubbo3高级特性】「微服务云原生架构」带你从零基础认识搭建公司内部服务用户中心体系(实战指南-序章)
【Dubbo3高级特性】「微服务云原生架构」带你从零基础认识搭建公司内部服务用户中心体系(实战指南-序章)
133 0
|
2月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
2月前
|
Dubbo Java 应用服务中间件
阿里巴巴资深架构师深度解析微服务架构设计之SpringCloud+Dubbo
软件架构是一个包含各种组织的系统组织,这些组件包括Web服务器,应用服务器,数据库,存储,通讯层),它们彼此或和环境存在关系。系统架构的目标是解决利益相关者的关注点。