这篇博文是我决心深度学习Dubbo框架时记录的笔记, 主题是Dubbo的拓展点, 下面的几个部分相对来说比较零散, 貌似是不和主题挂钩的 , 并且是一些很冷门的知识点 , 但是它们确实是深入学习Dubbo的前置知识

知识储备一: Dubbo的架构图

细化一下上图的各个组成部分:

  • 服务提供者

    • 提供服务接口的实现类
    • 注册服务 (远程注册, 本地注册)
    • 对外暴露服务
  • 注册中心
    • 保存 服务名称&服务地址 的映射关系
    • 当服务地址发生变动时, 主动通知消费者
  • 服务消费者
    • 启动时从注册中心拉取服务提供者的地址, 缓存在本地
    • 根据负载均衡策略选出一个服务进行远程调用 (Dubbo会将下面的信息封装成对象通过网络发送给服务提供者)
      • 参数1: 接口名
      • 参数2: 方法名
      • 参数3: 参数列表类型
      • 参数4: 参数值列表
  • 监控中心
    • 统计RPC过程的细节数据, 如: 服务调用次数, 调用时间

知识储备二: Dubbo中的核心概念

URL

Dubbo自己有个封装类叫URL如下: URL: 全称 Uniform Resources Loactor 统一资源定位符, 它是不可变的, 也是线程安全的

  • URL的作用

其实, Dubbo它作为一款RPC通信框架, 主体功能就是负责在服务集群中各个点之间进行数据的传递, 打个例子比如: 服务消费者调用服务的提供者,这个过程中的通信是Dubbo框架实现的, 通信的格式就好比自定义协议一样, Dubbo将服务提供者和服务消费者两种之间进行数据传递 需要的协议信息/ 端口号信息/ 请求那个接口 / 参数信息 / 账号 / 密码信息. 等一系列的信息进行封装,于是上图中的 URL 诞生了

  • 对URL的理解

对URL最直观的理解: URL是dobbo 对一系列数据的封装, 方便代码的编写, 参数的传递

很多人也将URL称为Dubbo的消息总线, 说URL贯穿于Dubbo的上下文, 我感觉到这个结论也许是这样得出的, 就是说 Dubbo作为一款RPC框架, 首要的任务就是 RPC 远程过程调用, 怎么样找到提供服务的机器呢? 无论是发起socket 还是借助Thrift或者Netty这种框架实现也罢, 前提是得知道提供服务的机器在哪里, 它的哪些接口对外暴露服务 , 没错! 这些信息都被Dubbo封装在了URL中

  • URL常见的组成

    • protobuf - 协议信息, 如 zk / Dubbo / http / Thrift
    • host/port - 目标主机端口信息
    • path - 接口的名称
    • parameters - 参数键值对信息
  • 典型的Dubbo URL格式

# 描述 Dubbo 协议的服务
Dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000 # 描述 zookeeper 注册中心
zookeeper://127.0.0.1:2181/org.apache.Dubbo.registry.RegistryService?application=demo-consumer&Dubbo=2.0.2&interface=org.apache.Dubbo.registry.RegistryService&pid=1214&qos.port=33333&timestamp=1545721981946 # 描述消费者 服务
consumer://30.5.120.217/org.apache.Dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&Dubbo=2.0.2&interface=org.apache.Dubbo.demo.DemoService&methods=sayHello&pid=1209&qos.port=33333&side=consumer&timestamp=1545721827784 # for this case, url protocol = null, url host = 192.168.1.3, port = 20880, url path = null
192.168.1.3:20880 # for this case, url protocol = file, url host = null, url path = home/user1/router.js
file:///home/user1/router.js?type=script ... 更多参照URL源码

Invoker

invoker 直译调用者

  • 在服务提供方: invoker 对象被构造出来去调用提供服务的函数
  • 在服务的消费方: invoker用于调用 执行远程过程调用的类

Invocation

指代程序中的调用对象, 包含了 接口名 / 方法名 / 参数类型列表 / 参数值列表 等

知识储备三: Java SPI (Service Provider Interface )

怎么理解SPI机制呢?

如果说SPI是java提供的一种拓展机制, 其实是不明确的, 结合java本身的语言特性来说, SPI直观的看就是 基于接口的编程 + 策略模式 + 配置文件 组合实现的动态加载机制, 用大白话解释就是说, 一个框架的设计为了后期的拓展性, 肯定先会在顶层设计接口, 然后再为这些接口提供一些默认的实现类, 未了良好的拓展性, 如果想让, 如果想实现允许当前框架 识别 / 加载 / 使用 第三方提供的jar包时 , 就可以使用SPI实现接口的动态加载, 只要遵循SPI的规范, java就能将我们自己的类也加载进JVM供我们使用

说起来总归是模糊的, 看下面的小Demo自然就懂了

// 接口
public interface Person {
String getName();
}
// 实现类一:
public class Student implements Person {
@Override
public String getName() {
return "Student";
}
}
// 实现类二:
public class Teacher implements Person {
@Override
public String getName() {
return "Teacher";
}
}

resources/META-INF/services/ 目录下面添加配置文件, 文件名称为 Person接口的全限定名, 内容如下

com.changwu.javaspi.api.Student
com.changwu.javaspi.api.Teacher

测试程序:

public class Test {
public static void main(String[] args) {
// 加载接口中的实现类
ServiceLoader<Person> load = ServiceLoader.load(Person.class);
Iterator<Person> iterator = load.iterator();
while (iterator.hasNext()){
Person next = iterator.next();
System.out.println(next.getName());
}
}
}

测试结果控制台输出如下:

Student
Teacher

Dubbo SPI

Dubbo自己也封装了一套SPI机制, 并将此作为它的扩展点,如果我们有更好的想法, 可以使用Dubbo这个特性加将我们自己的类注入给Dubbo, 它用法和JDK原生的SPI相似, 不同点在哪里呢? Dubbo的更强大, 比如相对于JDK的SPI , 它支持根据名称获取出指定的拓展类

一个小demo

  • 接口如下 , 注意点 Dubbo的SPI需要在接口上标注注解 @SPI
@SPI
public interface PersonInterface {
String getName();
}
  • 两个实现类
public class Student implements PersonInterface {
@Override
public String getName() {
return "Student";
}
} public class Teacher implements PersonInterface {
@Override
public String getName() {
return "Teacher";
}
}
  • 配置文件

  • 测试类 可以根据名称明确指出使用哪一个实现类
public class Test {
public static void main(String[] args) {
// todo 第一点: Dubbo 的SPI算作是他的一个可扩展的机制
ExtensionLoader<PersonInterface> extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class);
PersonInterface personInterface = extensionLoader.getExtension("student");
System.out.println(personInterface.getName());
}
}

Dubbo IOC

Spring 的IOC肯定是鼎鼎大名的, 很直接的能想到Spring的 @Autowired 注解, 或者的配置文件版本的 <bean>标签中可以帮我们自动维护bean之间的相互的依赖的关系

Dubbo 也实现了自己的IOC

比如下面的代码这样: Human.java 中依赖了 PersonInterface 类型的对象, 打眼看上去, 这个对象肯定是借助我们提供的setter方法完成的注入

public class Human implements PersonInterface {

    private PersonInterface personInterface;

    public void setpersonInterface(PersonInterface personInterface) {
this.personInterface = personInterface;
} @Override
public String getColor(URL url) {
System.out.println("i am Human ");
return "i am Human + " + personInterface.getColor(url);
}
}

那么问题来了, 假如我们在配置文件中添加了多个PersonInterface接口的实现类, 那Dubbo是如何得知需要注入哪一个的呢? 答案就在入参位置的URL中, 也就是我在 知识储备二中提到的概念URL

可以看下面这段测试代码, 怎么读下面的这段代码呢?

单独看 (PersonInterface) extensionLoader.getExtension("human"); 其实就是前面所说的 Dubbo的SPI机制, 但是在这个基础上多出来的逻辑是啥呢? 是我们构建了一个URL, 那为什么加进去一个URL? 因为上面的示例代码说了, human依赖了一个 PersonInterface 类型的变量, Dubbo就是根据这个URL变量, 进而得知自己到底该该注入哪一个变量Personinterface实例的 (因为我提供了两个 一个是Student , 另一个是Teacher)

此外, 他需要的是map , 我们给它的也是一个hashmap , 特性就是HashMap的key是不重复的, 用大白话说, 它的底层肯定是 key=value 唯一绑定, 并且key也不会出现重复的情况

public class Test {
public static void main(String[] args) {
// todo 源码的入口, 进入 getExtensionLoader()
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class);
HashMap<String, String> map = new HashMap<>();
map.put("human", "student");
URL url = new URL("", "",1,map);
// todo 继续跟进这个方法
PersonInterface personInterface = (PersonInterface) extensionLoader.getExtension("human");
System.out.println(personInterface.getName(url));
}
}

**那说了这么多, 到底注入的是哪一个对象呢? 从map.put("human", "student"); 也能很清楚的看出来, 不就是Student吗? 是的, 确实是它, 但是还少了点东西, 就是Personinterface怎么编写呢? 如下: **

// @SPI("stu") 可以给注解添加参数, 参数表示 PersonInterface 的默认实现类
@SPI
public interface PersonInterface {
// todo 下面的注解很重要, 啥意思呢? 可以点进这个注解, 我有一些翻译
// 验证AOP, 依然注入的信息从 url中获取出来
@Adaptive("human")
String getName(URL url);
}

看上面的代码, 除了@SPI注解, 还有一个注解就是@Adaptive注解, 这个注解的value部分决定了Dubbo到底需要注入哪一个 ExtensionObject

因为Dubbo在启动的过程中会去读取/META-INF/services/ Dubbo-SPI配置文件, 并将每行数据读取维护在一个map中, key就是我们自定义的名字, 值就是左边的全类名

看下面我们传递进去的是 human , 表示告诉Dubbo, 让Dubbo拿着human去查找, 很显然Dubbo把我们前面传递给它的student 找出来, 有了Student 进一步再从上下文中所有的 ExtensionObject中(包含了我们在配置文件中添加进去的Personinterface的两个实现) 找到具体的注入对象

Dubbo AOP

还是说, AOP是面向切面编程的思想, Spring自己实现了一套, Dubbo 也实现了一套

验证Dubbo的AOP实现类如下:

  • Dubbo的AOP增强实现和静态代理的编码方式相似, 比如我们就增强 PersonInterface中的方法, 所以我们继承PersonInterface, 提供构造方法入, 留给Dubbo通过反射完成指定目标对象的注入, 并在注入进来的目标对象的目标方法前后植入增强的逻辑
public class PersonWrapper implements PersonInterface {

    // todo 验证Dubbo的自动注入
private PersonInterface personInterface;
// todo 根据构造方法进行注入
public PersonWrapper(PersonInterface in){ // 假设传递进来的就是具体的实现类
this.personInterface=in;
} // todo 当我们将 personWrapper 配置进 Dubbo的 spi中时, 通过Dubbo的Spi获取personInterface执行时,下面的方法就会被执行
@Override
public String getName() {
System.out.println("before... ");
String color = personInterface.getName();
System.out.println("after... ");
return "getName";
}
}
  • 那如何让Dubbo知道包含我们增强逻辑的 PersonWrapper对象呢? 还是老样子, 通过配置文件完成, 如下:

  • 测试如下:
 public static void main(String[] args) {
// todo 第一点: Dubbo 的SPI算作是他的一个可扩展的机制
ExtensionLoader<PersonInterface> extensionLoader = ExtensionLoader.getExtensionLoader(PersonInterface.class);
PersonInterface personInterface = extensionLoader.getExtension("student");
System.out.println(personInterface.getName());
}

结果如下:

before...
after...
getName

**如过存在多个AOP增强类, 比如从上到下出现的顺序是 w1 w2 ... 那么增强的逻辑添加顺序是 before2 before1 **

结语:

​下一篇博文就是探究Dubbo的这些拓展点的底层实现细节了 , 还是挺带劲的...

最后打一个小广告: 我是bloger 赐我白日梦, 本科大三在读, 热衷java研发, 期望有一份Java相关实习岗位的工作, 可以全职实习半年左右, 最理想城市是北京, 求大佬的内推哇

探究Dubbo的拓展机制: 上的更多相关文章

  1. 探究Dubbo的拓展机制: 下

    承接上篇, 本篇博文的主题就是认认真真捋一捋, 看一下 Dubbo是如何实现他的IOC / AOP / 以及Dubbo SPI这个拓展点的 总览: 本篇的话总体上分成两部分进行展开 第一点就是 Dub ...

  2. jdk和dubbo的SPI机制

    前言:开闭原则一直是软件开发领域中所追求的,开闭原则中的"开"是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的,“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代 ...

  3. Dubbo剖析-SPI机制

    文章要点: 1.什么是SPi 2.Dubbo为什么要实现自己的SPi 3.Dubbo的IOC和AOP 4.Dubbo的Adaptive机制 5.Dubbo动态编译机制 6.Dubbo与Spring的融 ...

  4. Dubbo的SPI机制与JDK机制的不同及原理分析

    从今天开始,将会逐步介绍关于DUbbo的有关知识.首先先简单介绍一下DUbbo的整体概述. 概述 Dubbo是SOA(面向服务架构)服务治理方案的核心框架.用于分布式调用,其重点在于分布式的治理. 简 ...

  5. 面试常问的dubbo的spi机制到底是什么?

    前言 dubbo是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力.作为spring cloud alibaba体系中重要的一部分,随着spring cloud alibaba在 ...

  6. Wcf实现IServiceBehavior拓展机制

    IServiceBehavior接口 描述:提供一种在整个服务内修改或插入自定义拓展机制: 命名空间:  System.ServiceModel.Description程序集:  System.Ser ...

  7. dubbo的spi机制

    SPI SPI是一种扩展机制,在java中SPI机制被广泛应用,比如Spring中的SpringServletContainerInitializer 使得容器启动的时候SpringServletCo ...

  8. 聊聊Dubbo - Dubbo可扩展机制实战

    1. Dubbo的扩展机制 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架.今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性. 如同罗马不是一天建成的,任何系统都一定是从 ...

  9. dubbo重连机制会不会造成错误

    dubbo在调用服务不成功时,默认会重试2次. Dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以 dubbo的重试机器也能一定程度的保证服务的质量. 但是如果不合理的配置重试 ...

随机推荐

  1. sublime 插件安装packagecontrol

    https://packagecontrol.io/installation 第一步: Installation Simple The simplest method of installation ...

  2. 2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树

    边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<st ...

  3. poj 2442 Sequence (Priority Queue)

    2442 -- Sequence 真郁闷,明明方法是对的,为什么我的代码老是那么的慢._(:з」∠)_ 这题要想考虑两列的情况,然后逐列拓展. 代码如下: #include <cstdio> ...

  4. C#的类

    一.String类 1.Length 字符的长度 string x = Console.ReadLine();int i = x.Length;// Length 是获取字符串的长度(从1开始数)Co ...

  5. $_GET $_POST $_REQUEST

    <form action="__APP__/View/editArticle?id=5" method="GET"> <form>表单提 ...

  6. P1071 01字符串的交叉安排

    题目描述 你有 \(n(1 \le n \le 10^6)\) 个字符'0' 和 \(m(1 \le m \le 10^6)\) 个字符'1'.你需要使用这些字符拼接成一个01字符串,使得满足如下两个 ...

  7. 用nodejs创建服务器显示html页面

    nodejs版本:v10.14.2 1.首先准备一个简单的html页面 <!DOCTYPE html> <html lang="en"> <head& ...

  8. linux I/O 内存分配和映射

    I/O 内存区必须在使用前分配. 分配内存区的接口是( 在 <linux/ioport.h> 定义): struct resource *request_mem_region(unsign ...

  9. vue中处理时间格式化的问题

    vue main.js中修改Date原型链,插入(百度) Date.prototype.format = function(fmt) { var o = { "M+" : this ...

  10. 2018-2-13-win10-uwp-图标制作器

    title author date CreateTime categories win10 uwp 图标制作器 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17 ...