dubbo 2.6.0

Introduction

dubbo Dubbo 作为一款微服务框架,最重要的是向用户提供跨进程的 RPC 远程调用能力。如上图所示,Dubbo 的服务消费者(Consumer)通过一系列的工作将请求发送给服务提供者(Provider)。

为了实现这样一个目标,Dubbo 引入了注册中心(Registry)组件,通过注册中心,服务消费者可以感知到服务提供者的连接方式,从而将请求发送给正确的服务提供者。

在使用 Dubbo 进行服务调用时,传递的参数是接口参数,而不是序列化后的服务对象。

tio-boot 整合 jfinal-aop 依赖注入示例

添加依赖

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>

<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>tio-boot</artifactId>
  <version>${tio-boot.version}</version>
</dependency>

<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>jfinal-aop</artifactId>
  <version>1.2.7</version>
</dependency>

整合示例

编写接口

package com.litongjava.tio.duboot.demo001.service;

public interface HelloService {
  String sayHello(String name);
}

编写实现类

package com.litongjava.tio.duboot.demo001.service.impl;

import com.litongjava.jfinal.aop.annotation.AService;
import com.litongjava.tio.duboot.demo001.service.HelloService;

@AService
public class HelloServiceImpl implements HelloService {
  public String sayHello(String name) {
    return "Hello " + name;
  }
}

编写 Controller 调用接口

package com.litongjava.tio.duboot.demo001.controller;

import com.litongjava.jfinal.aop.annotation.AAutowired;
import com.litongjava.tio.duboot.demo001.service.HelloService;
import com.litongjava.tio.http.server.annotation.RequestPath;

@RequestPath("/")
public class IndexController {

  @AAutowired
  private HelloService helloService;

  @RequestPath()
  public String index() {
    return helloService.sayHello("Tong Li");
  }
}

编写启动类进行扫描,注入和启动

package com.litongjava.tio.duboot.demo001;

import com.litongjava.jfinal.aop.annotation.AComponentScan;
import com.litongjava.tio.boot.TioApplication;

@AComponentScan
public class ProviderApp {
  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    TioApplication.run(ProviderApp.class, args);
    long end = System.currentTimeMillis();
    System.out.println((end - start) + "ms");
  }
}

访问测试 http://localhost/返回如下

Hello Tong Li

注解解释

  1. @AService: 这是 JFinal AOP 的一个注解,用于标记一个类为服务类(Service),并且这个类可以被 JFinal 的 AOP 容器管理。@AService注解的作用类似于 Spring 中的@Service注解,它告诉框架这是一个服务层的组件,需要被框架管理和依赖注入。

  2. @AAutowired: 这是 JFinal AOP 中的自动注入注解,类似于 Spring 中的@Autowired。它用于自动将接口类型的依赖注入到类中。使用这个注解,可以让 JFinal AOP 容器自动将对应的实现类注入到标注了@AAutowired的属性中。

  3. @RequestPath: 这个注解来自于 Tio-boot 框架,用于定义 HTTP 请求的路径。标记在类上时,它定义了这个类处理的请求路径的前缀;标记在方法上时,它定义了具体方法处理的请求路径。如果方法上没有指定路径,那么它会使用类上的路径作为请求路径。

  4. @AComponentScan: 这个注解用于指定 JFinal AOP 需要扫描的包路径。通过这个注解,框架会自动扫描并注册指定包路径下的所有组件(如@AService标注的类),以便实现依赖注入。

注入流程

  1. 扫描服务类: @AComponentScan会扫描指定包下的所有类,找到带有@AService注解的类(如HelloServiceImpl),并将它们注册到 JFinal AOP 的容器中。

  2. 处理依赖注入: 在IndexController中,由于helloService属性被标注了@AAutowired,JFinal AOP 会查找容器中是否有HelloService接口的实现类。如果找到,就将这个实现类的实例(即HelloServiceImpl)注入到helloService属性中。

  3. 启动 Tio-boot: TioApplication.run(ProviderApp.class, args); 会启动 Tio-boot 应用。这个过程中,Tio-boot 会启动一个内嵌的 HTTP 服务器,在启动过程中,所有标记了注解的类都会被加载和初始化,确保依赖注入完成后,并加载所有的控制器(如IndexController),准备处理客户端的 HTTP 请求。

访问结果

当你访问http://localhost/时,HTTP 请求会被路由到IndexControllerindex()方法。由于helloService已经被注入为HelloServiceImpl的实例,因此helloService.sayHello("Tong Li")会调用HelloServiceImplsayHello方法,并返回"Hello Tong Li"。最终,这个字符串会作为 HTTP 响应返回给客户端。

安装 zookpper

dubbo 使用 zookpper 作为注册中心,所以需要安装 zookeeper

  1. 下载 ZooKeeper

你可以从 ZooKeeper 官方网站open in new window 下载最新版本的 ZooKeeper。

mkdir /opt/package/zookeeper -p &&cd /opt/package/zookeeper
wget https://downloads.apache.org/zookeeper/zookeeper-3.7.2/apache-zookeeper-3.7.2-bin.tar.gz
  1. 解压
tar -zxf apache-zookeeper-3.7.2-bin.tar.gz -C /opt/
  1. 进入解压后的目录
cd /opt/apache-zookeeper-3.7.2-bin
  1. 配置 ZooKeeper

conf 目录下复制 zoo_sample.cfg 并重命名为 zoo.cfg

cp conf/zoo_sample.cfg conf/zoo.cfg

编辑 zoo.cfg 配置文件:

vi conf/zoo.cfg

根据需要修改以下配置项:

tickTime = 2000
dataDir = /var/lib/zookeeper
clientPort = 2181
  1. 启动 ZooKeeper
bin/zkServer.sh start
  1. 检查 ZooKeeper 状态
bin/zkServer.sh status

如果 ZooKeeper 正常运行,你会看到类似如下输出:

Mode: standalone
  1. zookeerp 端口
8080
33125
2181
  1. 关闭 zookeeper
bin/zkServer.sh stop
  1. 开机自启 zkServer.sh 命令参数
Usage: ./bin/zkServer.sh [--config <conf-dir>] {start|start-foreground|stop|version|restart|status|print-cmd}
vi /lib/systemd/system/zookeeper.service
[Unit]
Description=zookeeper
After=network.target

[Service]
Type=simple
Restart=on-failure
RestartSec=5s
WorkingDirectory=/opt/apache-zookeeper-3.7.2-bin
Environment="JAVA_HOME=/usr/java/jdk1.8.0_211"
ExecStart=/opt/apache-zookeeper-3.7.2-bin/bin/zkServer.sh start-foreground

[Install]
WantedBy=multi-user.target

注意上面的环境变量 JAVA_HOME

systemctl start zookeeper
systemctl enable zookeeper
systemctl status zookeeper

tio-boot 整合 Dubbo 示例

创建工程

在本示例中,我们将创建三个独立的工程:

  • tio-boot-dubbo-2-6-0-demo01-api:API 模块
  • tio-boot-dubbo-2-6-0-demo01-provider:服务提供者模块
  • tio-boot-dubbo-2-6-0-demo01-consumer:服务消费者模块

tio-boot-dubbo-2-6-0-demo01-api

这个模块仅包含服务的接口定义。例如:

package com.litongjava.tio.duboot.demo001.service;

public interface HelloService {
  String sayHello(String name);
}
package com.litongjava.tio.dubbo.demo001.service;

public interface HiService {
  String sayHi(String name);
}

tio-boot-dubbo-2-6-0-demo01-provider

添加依赖

在服务提供者模块中,我们需要添加以下依赖:

  • logback-classic:日志框架
  • tio-boot
  • jfinal-aop
  • dubbo(移除与 spring-boot 相关的依赖)
  • zkclient:zookeeper 客户端
<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>tio-boot-dubbo-2-6-0-demo01-api</artifactId>
  <version>1.0.0</version>
</dependency>

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>

<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>tio-boot</artifactId>
  <version>${tio-boot.version}</version>
</dependency>

<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>jfinal-aop</artifactId>
  <version>1.2.6</version>
</dependency>

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>dubbo</artifactId>
  <version>2.6.0</version>
  <exclusions>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<dependency>
  <groupId>com.101tec</groupId>
  <artifactId>zkclient</artifactId>
  <version>0.9</version>
  <exclusions>
    <exclusion>
      <artifactId>slf4j-log4j12</artifactId>
      <groupId>org.slf4j</groupId>
    </exclusion>
  </exclusions>
</dependency>

配置文件

app.properties 中添加如下配置:

server.port=8000
app.name=service-provider
ZOOKEEPER_URL=zookeeper://192.168.3.9:2181

服务实现类

编写 HelloServiceHiService 的实现类:

package com.litongjava.tio.dubbo.demo001.service.impl;

import com.litongjava.jfinal.aop.annotation.AService;
import com.litongjava.tio.dubbo.demo001.service.HelloService;

@AService
public class HelloServiceImpl implements HelloService {
  public String sayHello(String name) {
    return "Hello " + name;
  }
}
package com.litongjava.tio.dubbo.demo001.service.impl;

import com.litongjava.tio.dubbo.demo001.service.HiService;

public class HiServiceImpl implements HiService {

  @Override
  public String sayHi(String name) {
    return "Hi " + name;
  }
}

Dubbo 配置

编写 DubboProviderConfig 类进行 Dubbo 配置:

package com.litongjava.tio.dubbo.demo001.config;

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.qos.common.Constants;
import com.alibaba.dubbo.qos.server.Server;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.jfinal.dubbo.DubboProvider;
import com.litongjava.tio.boot.constatns.TioBootConfigKeys;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.dubbo.demo001.service.HelloService;
import com.litongjava.tio.dubbo.demo001.service.HiService;
import com.litongjava.tio.dubbo.demo001.service.impl.HelloServiceImpl;
import com.litongjava.tio.dubbo.demo001.service.impl.HiServiceImpl;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.thread.TioThreadUtils;

@AConfiguration
public class DubboProviderConfig {

  @AInitialization
  public void config() {

    // 防止 QoS 端口冲突
    System.setProperty("dubbo.application.logger", "slf4j");
    System.setProperty(Constants.QOS_PORT, 2223 + "");
    System.setProperty(Constants.ACCEPT_FOREIGN_IP, "false");

    // 创建应用配置
    ApplicationConfig applicationConfig = new ApplicationConfig();
    applicationConfig.setName(EnvUtils.get(TioBootConfigKeys.APP_NAME));

    // 创建注册中心配置
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setAddress(EnvUtils.get(TioBootConfigKeys.ZOOKEEPER_URL));

    // 创建服务提供者配置
    ProviderConfig providerConfig = new ProviderConfig();

    // 初始化
    DubboProvider.init(applicationConfig, registryConfig, providerConfig);

    // 添加服务
    DubboProvider.add(HelloService.class, Aop.get(HelloServiceImpl.class));
    DubboProvider.add(HiService.class, Aop.get(HiServiceImpl.class));

    // 导出服务
    TioThreadUtils.submit(() -> {
      DubboProvider.export();
      // 启动后关闭 QoS 服务
      Server.getInstance().stop();
    });

    // 服务销毁时取消导出
    TioBootServer.me().addDestroyMethod(() -> {
      DubboProvider.unexport();
    });
  }
}

启动类

编写 ProviderApp 启动类:

package com.litongjava.tio.dubbo.demo001;

import com.litongjava.jfinal.aop.annotation.AComponentScan;
import com.litongjava.tio.boot.TioApplication;

@AComponentScan
public class ProviderApp {
  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    TioApplication.run(ProviderApp.class, args);
    long end = System.currentTimeMillis();
    System.out.println((end - start) + "ms");
  }
}

tio-boot-dubbo-2-6-0-demo01-consumer

配置文件

app.properties 中添加如下配置:

server.port=7000
app.name=service-consumer
ZOOKEEPER_URL=zookeeper://192.168.3.9:2181

Dubbo 配置

编写 DubboConsumerConfig 类进行 Dubbo 配置:

import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.qos.common.Constants;
import com.alibaba.dubbo.qos.server.Server;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.jfinal.dubbo.Dubbo;
import com.litongjava.tio.boot.constatns.TioBootConfigKeys;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.utils.environment.EnvUtils;

@AConfiguration
public class DubboConfig {

  @AInitialization
  public void config() {
    int dubboTimeout = EnvUtils.getInt("dubbo.timeout", 3000); // 设置默认超时时间为3000毫秒

    // 防止qos端口冲突
    System.setProperty(Constants.QOS_PORT, "2224");
    System.setProperty("dubbo.application.logger", "slf4j");

    // 创建和设置应用配置和注册中心配置
    ApplicationConfig applicationConfig = createApplicationConfig();
    RegistryConfig registryConfig = createRegistryConfig();

    // 初始化Dubbot
    Dubbo.initialize(applicationConfig, registryConfig, dubboTimeout);

    // 获取服务
    Dubbo.get(HelloService.class);
    Dubbo.get(HiService.class);

    // 启动后关闭 QoS 服务
    Server.getInstance().stop();

    // 在项目关闭是关闭Dubbo
    TioBootServer.me().addDestroyMethod(Dubbo::clear);
  }

  // 创建应用配置
  private ApplicationConfig createApplicationConfig() {
    ApplicationConfig applicationConfig = new ApplicationConfig();
    applicationConfig.setName(EnvUtils.get(TioBootConfigKeys.APP_NAME));
    return applicationConfig;
  }

  // 创建注册中心配置
  private RegistryConfig createRegistryConfig() {
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setAddress(EnvUtils.get(TioBootConfigKeys.ZOOKEEPER_URL));
    return registryConfig;
  }
}

控制器类

编写 IndexController 控制器类:

package com.litongjava.tio.web.hello.controller;

import com.litongjava.jfinal.dubbo.Dubbo;
import com.litongjava.tio.dubbo.demo001.service.HelloService;
import com.litongjava.tio.dubbo.demo001.service.HiService;
import com.litongjava.tio.http.server.annotation.RequestPath;

@RequestPath("/")
public class IndexController {

  @RequestPath()
  public String index() {
    HelloService helloService = Dubbo.get(HelloService.class);
    HiService hiService = Dubbo.get(HiService.class);
    return helloService.sayHello("Tong Li") + "_" + hiService.sayHi("Tong Li");
  }
}

启动类

编写 ConsumerApp 启动类:

package com.litongjava.tio.web.hello;

import com.litongjava.jfinal.aop.annotation.AComponentScan;
import com.litongjava.tio.boot.TioApplication;

@AComponentScan
public class ConsumerApp {
  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    TioApplication.run(ConsumerApp.class, args);
    long end = System.currentTimeMillis();
    System.out.println((end - start) + "ms");
  }
}

启动流程

  1. 启动服务提供者

    • ProviderApp 启动时,TioApplication.run 方法会启动 Tio 框架的内嵌服务器,并扫描带有 @AService 注解的类(如 HelloServiceImplHiServiceImpl)。
    • DubboProviderConfig 类中的 config() 方法会配置 Dubbo 的应用、注册中心和服务提供者,并通过 DubboProvider 类的 export() 方法将服务发布到注册中心。
  2. 启动服务消费者

    • ConsumerApp 启动时,TioApplication.run 会启动消费者模块,并通过 DubboConsumerConfig 类的 config() 方法配置 Dubbo 应用和注册中心。
    • Dubbo.get(HelloService.class)Dubbo.get(HiService.class) 方法会从注册中心获取服务提供者的引用,以便在消费者中调用。

服务调用流程

  1. 服务消费者通过 Dubbo.get(HelloService.class) 获取 HelloService 的远程服务引用。
  2. 当消费者调用 HelloService.sayHello("Tong Li") 时,Dubbo 框架会通过注册中心获取服务提供者的地址,并将请求发送到相应的服务提供者。
  3. 服务提供者接收到请求后,调用 HelloServiceImpl.sayHello("Tong Li") 方法,返回结果 "Hello Tong Li"
  4. 最终,消费者接收到响应,并将结果返回给调用者。

详细说明:

  1. 接口参数的传递:当客户端调用 Dubbo 服务时,它会将调用的方法名称和方法参数序列化(通常是基于协议的,如 Hessian、Protobuf 等),通过网络发送到服务端。在服务端,Dubbo 反序列化这些数据,调用对应的服务实现方法,并将结果返回给客户端。

  2. 序列化与反序列化

    • 客户端会将调用的方法名称、参数以及其他元数据序列化为二进制格式,然后通过网络发送给服务端。
    • 服务端接收到请求后,首先会对数据进行反序列化,然后根据反序列化得到的接口名称和方法名称找到对应的服务实现,最后将参数传递给该方法进行实际调用。
  3. 服务对象的传递

    • 服务对象本身并不会在调用时传递,而是通过注册表(如 Zookeeper)进行服务发现。服务提供者在启动时将自身信息(包括服务接口名称、方法等)注册到注册表,服务消费者则通过注册表查找服务提供者。
  4. 结论

  • 当你在 Dubbo 中调用远程服务时,传递的实际是接口方法的参数,而不是服务对象本身。这些参数在传输过程中被序列化,然后在服务端进行反序列化,最终调用服务实现的方法。