dubbo 2.6.0
Introduction
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.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
注解解释
@AService: 这是 JFinal AOP 的一个注解,用于标记一个类为服务类(Service),并且这个类可以被 JFinal 的 AOP 容器管理。
@AService
注解的作用类似于 Spring 中的@Service
注解,它告诉框架这是一个服务层的组件,需要被框架管理和依赖注入。@AAutowired: 这是 JFinal AOP 中的自动注入注解,类似于 Spring 中的
@Autowired
。它用于自动将接口类型的依赖注入到类中。使用这个注解,可以让 JFinal AOP 容器自动将对应的实现类注入到标注了@AAutowired
的属性中。@RequestPath: 这个注解来自于 Tio-boot 框架,用于定义 HTTP 请求的路径。标记在类上时,它定义了这个类处理的请求路径的前缀;标记在方法上时,它定义了具体方法处理的请求路径。如果方法上没有指定路径,那么它会使用类上的路径作为请求路径。
@AComponentScan: 这个注解用于指定 JFinal AOP 需要扫描的包路径。通过这个注解,框架会自动扫描并注册指定包路径下的所有组件(如
@AService
标注的类),以便实现依赖注入。
注入流程
扫描服务类:
@AComponentScan
会扫描指定包下的所有类,找到带有@AService
注解的类(如HelloServiceImpl
),并将它们注册到 JFinal AOP 的容器中。处理依赖注入: 在
IndexController
中,由于helloService
属性被标注了@AAutowired
,JFinal AOP 会查找容器中是否有HelloService
接口的实现类。如果找到,就将这个实现类的实例(即HelloServiceImpl
)注入到helloService
属性中。启动 Tio-boot:
TioApplication.run(ProviderApp.class, args);
会启动 Tio-boot 应用。这个过程中,Tio-boot 会启动一个内嵌的 HTTP 服务器,在启动过程中,所有标记了注解的类都会被加载和初始化,确保依赖注入完成后,并加载所有的控制器(如IndexController
),准备处理客户端的 HTTP 请求。
访问结果
当你访问http://localhost/
时,HTTP 请求会被路由到IndexController
的index()
方法。由于helloService
已经被注入为HelloServiceImpl
的实例,因此helloService.sayHello("Tong Li")
会调用HelloServiceImpl
的sayHello
方法,并返回"Hello Tong Li"
。最终,这个字符串会作为 HTTP 响应返回给客户端。
安装 zookpper
dubbo 使用 zookpper 作为注册中心,所以需要安装 zookeeper
- 下载 ZooKeeper
你可以从 ZooKeeper 官方网站 下载最新版本的 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
- 解压
tar -zxf apache-zookeeper-3.7.2-bin.tar.gz -C /opt/
- 进入解压后的目录
cd /opt/apache-zookeeper-3.7.2-bin
- 配置 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
- 启动 ZooKeeper
bin/zkServer.sh start
- 检查 ZooKeeper 状态
bin/zkServer.sh status
如果 ZooKeeper 正常运行,你会看到类似如下输出:
Mode: standalone
- zookeerp 端口
8080
33125
2181
- 关闭 zookeeper
bin/zkServer.sh stop
- 开机自启 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
服务实现类
编写 HelloService
和 HiService
的实现类:
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 {
@Initialization
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 {
@Initialization
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.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");
}
}
启动流程
启动服务提供者:
ProviderApp
启动时,TioApplication.run
方法会启动 Tio 框架的内嵌服务器,并扫描带有@AService
注解的类(如HelloServiceImpl
和HiServiceImpl
)。DubboProviderConfig
类中的config()
方法会配置 Dubbo 的应用、注册中心和服务提供者,并通过DubboProvider
类的export()
方法将服务发布到注册中心。
启动服务消费者:
ConsumerApp
启动时,TioApplication.run
会启动消费者模块,并通过DubboConsumerConfig
类的config()
方法配置 Dubbo 应用和注册中心。Dubbo.get(HelloService.class)
和Dubbo.get(HiService.class)
方法会从注册中心获取服务提供者的引用,以便在消费者中调用。
服务调用流程
- 服务消费者通过
Dubbo.get(HelloService.class)
获取HelloService
的远程服务引用。 - 当消费者调用
HelloService.sayHello("Tong Li")
时,Dubbo 框架会通过注册中心获取服务提供者的地址,并将请求发送到相应的服务提供者。 - 服务提供者接收到请求后,调用
HelloServiceImpl.sayHello("Tong Li")
方法,返回结果"Hello Tong Li"
。 - 最终,消费者接收到响应,并将结果返回给调用者。
详细说明:
接口参数的传递:当客户端调用 Dubbo 服务时,它会将调用的方法名称和方法参数序列化(通常是基于协议的,如 Hessian、Protobuf 等),通过网络发送到服务端。在服务端,Dubbo 反序列化这些数据,调用对应的服务实现方法,并将结果返回给客户端。
序列化与反序列化:
- 客户端会将调用的方法名称、参数以及其他元数据序列化为二进制格式,然后通过网络发送给服务端。
- 服务端接收到请求后,首先会对数据进行反序列化,然后根据反序列化得到的接口名称和方法名称找到对应的服务实现,最后将参数传递给该方法进行实际调用。
服务对象的传递:
- 服务对象本身并不会在调用时传递,而是通过注册表(如 Zookeeper)进行服务发现。服务提供者在启动时将自身信息(包括服务接口名称、方法等)注册到注册表,服务消费者则通过注册表查找服务提供者。
结论:
- 当你在 Dubbo 中调用远程服务时,传递的实际是接口方法的参数,而不是服务对象本身。这些参数在传输过程中被序列化,然后在服务端进行反序列化,最终调用服务实现的方法。