前言:

  在开始入门Javaweb时,学的基本都是MVC开发模式,一个项目基本上就是model,view,controller三层。但是随着系统的服务逐渐加多,SOA模式更加适合目前项目开发。而SOA模式在Java开发过程中基本上是Dubbo和SpringCloud的天下。所以今天来看看Dubbo中的运行原理。

一、SOA模式

  首先简单介绍一下SOA模式,这对我们后面理解Dubbo很有帮助。

  SOA模式是什么?

  SQA(Service-Oriented Architecture)即面向服务架构,它将应用程序的不同功能单元(这里就理解为服务)进行了拆分。在这种架构下项目不会直接和数据库进行交互,而是通过调用不同服务的接口来访问数据库。

  模式优点在哪?

  这样最直接的好处就是解决代码冗余,如果多个项目同时都要访问数据库同一张表。比如用户表的访问。我们可以直接调用用户服务里面的接口进行开发,而不需要每个项目都去写一遍用户表的增删改查。除了这个,SOA能带给我们的好处就是能够让开发者以更迅速、更可靠、更具重用性架构整个业务系统。较之以往MVC开发模式,以SOA架构的系统能够更加从容地面对业务的急剧变化

  SOA示意图:

二、Dubbo基本组成

  聊完了SOA,现在来看看Dubbo内部架构,相信大家多多少少看过下面这幅图:

  很多人会漏看上面线的示意图,下面解释一下:

  紫色虚线:在Dubbo启动时完成的功能  蓝青色的线:都是程序运行过程中执行的功能,虚线是异步操作,实线是同步操作

  Provider:提供者,服务发布方。如果是采用SOA开发的模式,这个就是和数据库交互的接口,也就是service主要放在生产者这边

  Consumer:消费者,调用服务方。面向前端的Controller主要是在这边,可以远程调用生产者中的方法,生产者发生变化时也会实时更新消费者的调用列表。具体的看下面介绍

  Container:Dubbo容器,依赖于Spring容器。这里比较注意的就是Dubbo是依赖与Spring容器的。所以必须要和Spring配合着使用

  Registry:注册中心.当Container启动时把所有可以提供的服务列表上Registry中进行注册。作用:告诉Consumer提供了什么服务和服务方在哪里.

  Monitor:监听器

三、Dubbo运行原理

  就着上面的架构图来看看Dubbo的运行原理:

  0.Start: 启动容器,相当于在启动Dubbo的Provider,并且会创建对应的目录结构,例如代码中的共用接口名为com.learnDubbo.demo.DemoService,就会创建 /dubbo/com.learnDubbo.demo.DemoService目录,然后在创建providers目录,再在providers目录下写入自己的 URL 地址。

  1.Register:启动后会去注册中心进行注册,注册所有可以提供的服务列表。即订阅/dubbo/com.learnDubbo.demo.DemoService 目录下的所有提供者和消费者 URL 地址。

  2.Subscribe:Consumer在启动时,不仅仅会注册自身到 …/consumers/目录下,同时还会订阅…/providers目录,实时获取其上Provider的URL字符串信息。当服务消费者启动时:会在/dubbo/com.learnDubbo.demo.DemoService目录创建/consumers目录,并在/consumers目录写入自己的 URL 地址。

  3.notify:当Provider有修改后,注册中心会把消息推送给Consummer。也就是注册中心会对Provider进行观察,这里就是使用设计模式中的观察者模式。以Zookeeper注册中心为例,Dubbo中有ZookeeperRegistry中的doSubscribe方法也就是进行生产者订阅和监听。下面分析一下源码,看看订阅过程

  1. @Override
  2. protected void doSubscribe(final URL url, final NotifyListener listener) {
  3. try {
  4. if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {//根据URL得到服务接口为*,也就是所有
  5. String root = toRootPath();
  6. ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);//拿取URL下的监听器
  7. if (listeners == null) {//不存在则进行创建
  8. zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
  9. listeners = zkListeners.get(url);
  10. }
  11. ChildListener zkListener = listeners.get(listener);//得到子目录的监听器
  12. if (zkListener == null) {//无法得到子目录监听器,则会新建一个。
  13. listeners.putIfAbsent(listener, new ChildListener() {
  14. @Override
  15. public void childChanged(String parentPath, List<String> currentChilds) {
  16. for (String child : currentChilds) {
  17. child = URL.decode(child);
  18. if (!anyServices.contains(child)) {
  19. anyServices.add(child);
  20. //如果consumer的interface为*,会订阅每一个url,会触发另一个分支的逻辑
  21. //这里是用来对/dubbo下面提供者新增时的回调,相当于增量
  22. subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
  23. Constants.CHECK_KEY, String.valueOf(false)), listener);
  24. }
  25. }
  26. }
  27. });
  28. zkListener = listeners.get(listener);
  29. }
  30. zkClient.create(root, false);
  31. //添加监听器会返回子节点集合
  32. List<String> services = zkClient.addChildListener(root, zkListener);//订阅root目录下的子元素,比如:/dubbo/com.learnDubbo.demo.DemoService/providers
  33. if (services != null && !services.isEmpty()) {
  34. for (String service : services) {
  35. service = URL.decode(service);
  36. anyServices.add(service);
  37. subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
  38. Constants.CHECK_KEY, String.valueOf(false)), listener);
  39. }
  40. }
  41. } else {
  42. //这边是针对明确interface的订阅逻辑
  43. List<URL> urls = new ArrayList<URL>();
  44. //针对每种category路径进行监听
  45. for (String path : toCategoriesPath(url)) {
  46. ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
  47. if (listeners == null) {
  48. zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
  49. listeners = zkListeners.get(url);
  50. }
  51. ChildListener zkListener = listeners.get(listener);
  52. if (zkListener == null) {
  53. //封装回调逻辑
  54. listeners.putIfAbsent(listener, new ChildListener() {
  55. @Override
  56. public void childChanged(String parentPath, List<String> currentChilds) {
  57. ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
  58. }
  59. });
  60. zkListener = listeners.get(listener);
  61. }
  62. //创建节点
  63. zkClient.create(path, false);
  64. //增加回调
  65. List<String> children = zkClient.addChildListener(path, zkListener);
  66. if (children != null) {
  67. urls.addAll(toUrlsWithEmpty(url, path, children));
  68. }
  69. }
  70. //并且会对订阅的URL下的服务进行监听,并会实时的更新Consumer中的invoke列表,使得能够进行调用。这个方法不展开讲
  71. notify(url, listener, urls);
  72. }
  73. } catch (Throwable e) {
  74. throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
  75. }
  76. }

  4、invoke:根据获取到的Provider地址,真实调用Provider中功能。这里就是唯一一个同步的方法,因为消费者要得到生产者传来的数据才能进行下一步操作,但是Dubbo是一个RPC框架,RPC的核心就在于只能知道接口不能知道内部具体实现。所以在Consumer方使用了代理设计模式,创建一个Provider方类的一个代理对象,通过代理对象获取Provider中真实功能,起到保护Provider真实功能的作用。

  invoke部分源码分析

  有兴趣的可以看看invoke调用过程

  5、Monitor:Consumer和Provider每隔1分钟向Monitor发送统计信息,统计信息包含,访问次数,频率等

四、总结

  这里只是稍微对Dubbo的原理做了一下分析,想要弄懂Dubbo还需要结合源码来看。尽管很多时候我们只是一个使用者,但是能够理解内部运行原理不但能够让我们更好的使用,同时里面的编程思路,设计模式也是我们需要学习的。后面还会做更多关于Dubbo的原理解析。

  

Dubbo(一):Dubbo运行原理的更多相关文章

  1. Dubbo的优雅下线原理分析

    文/朱季谦 Dubbo如何实现优雅下线? 这个问题困扰了我一阵,既然有优雅下线这种说法,那么,是否有非优雅下线的说法呢? 这,还真有. 可以从linux进程关闭说起,其实,我们经常使用到杀进程的指令背 ...

  2. 【dubbo】服务提供者运行的三种方式

    [dubbo]服务提供者运行的三种方式 学习了:https://blog.csdn.net/yxwb1253587469/article/details/78712451 1,使用容器: 2,使用自建 ...

  3. Dubbo及注册中心原理

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天是猿灯塔“365天原创计划”第4天. 今天呢!灯塔君跟大家讲: 一.Dubbo意义 网站应用的架构变化经历了一个从所有服务分布在一台服务器上(A ...

  4. 【DUBBO】 Dubbo原理解析-Dubbo内核实现之基于SPI思想Dubbo内核实现

    转载:http://blog.csdn.net/quhongwei_zhanqiu/article/details/41577235 SPI接口定义 定义了@SPI注解 public @interfa ...

  5. go-micro开发RPC服务的方法及其运行原理

    go-micro是一个知名的golang微服务框架,最新版本是v4,这篇文章将介绍go-micro v4开发RPC服务的方法及其运作原理. 基本概念 go-micro有几个重要的概念,后边开发RPC服 ...

  6. zookeeper,dubbo,dubbo admin

    zookeeper 1. 分布式协调服务:我们的程序运行在不同的机器上,这些机器可能位于同一个机架,同一个机房又或不同的数据中心.在这样的环境中,我们要实现协调该怎么办?那么这就是分布式协调服务要干的 ...

  7. Dubbo(2)--Dubbo常用配置文件解析及核心源码阅读

    1.多版本支持 服务端 创建第二个接口实现类 package com.lf; public class HelloImpl2 implements IHello{ @Override public S ...

  8. Linux 配置 dubbo 和 dubbo的简单介绍。

    一.是么是  dubbo? 一.dubbo? 1.因为项目之间需要相互调用,达到某种预期的结果 1.1 restful? 门户网站必须要知道用户的登录状态,但是用户的登录状态在登录项目中,所以门户网站 ...

  9. iis6.0与asp.net的运行原理

    这几天上网翻阅了不少前辈们的关于iis和asp.net运行原理的博客,学的有点零零散散,花了好长时间做了一个小结(虽然文字不多,但也花了不少时间呢),鄙人不才,难免有理解不道的地方,还望前辈们不吝赐教 ...

随机推荐

  1. [USACO10FEB]给巧克力Chocolate Giving

    题意简叙: FarmerFarmerFarmer JohnJohnJohn有B头奶牛(1<=B<=25000)(1<=B<=25000)(1<=B<=25000), ...

  2. Excel催化剂开源第43波-Excel选择对象Selection在.Net开发中的使用

    Excel的二次开发有一极大的优势所在,可以结合用户的交互进行程序的运行,大量用户的交互,都是从选择对象开始,用户选择了单元格区域.图形.图表等对象,之后再进行程序代码的加工处理,生成用户所需的最终结 ...

  3. JQuery开始

    JQuery jq的选择器 等等(网页的连接:http://www.runoob.com/jquery/jquery-ref-selectors.html) 事件: hover中有俩参数(mousee ...

  4. Linux系统安装jdk——rpm版

    这里简单地阐述一下rpm.deb.tar.gz的区别. rpm格式的软件包适用于基于Red Hat发行版的系统,如Red Hat Linux.SUSE.Fedora. deb格式的软件包则是适用于基于 ...

  5. mysql添加外键失败解决方案

    mysql重启命令: [root@wshCentOS centOS7Share]# service mysqld stopRedirecting to /bin/systemctl stop  mys ...

  6. java练习---14

    abstract class A{ private String name; public A(String name) { this.name = name; } public String get ...

  7. PHP与ECMAScript_2_数据类型

    PHP ECMAScript 数据类型 基本:String.Integer.Float.Boolean 基本:String. Number. Boolean.NULL.undefined 复合:Arr ...

  8. 【JS档案揭秘】第一集 内存泄漏与垃圾回收

    程序的运行需要内存,对于一些需要持续运行很久的程序,尤其是服务器进程,如果不及时释放掉不再需要的内存,就会导致内存堆中的占用持续走高,最终可能导致程序崩溃. 不再需要使用的内存,却一直占用着空间,得不 ...

  9. Web前端三大框架_angular.js 6.0(二)

    Web前端三大框架_angular.js 6.0(一) 需要视频教程,看头像昵称处 一.Angular 6.0  1.1样式 html中引入样式:内嵌式,外链式,行内式. ng6中组件引入样式的方式也 ...

  10. unc路径

    1.什么是UNC路径?UNC路径就是类似\\softer这样的形式的网络路径.UNC为网络(主要指局域网)上资源的完整 Windows 2000 名称.格式: \\servername\sharena ...