什么是 dubbo
dubbo 最新版本为 3.x,Apache Dubbo 是一款易用、高性能的 web 和 rpc 框架,同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力。 Dubbo3 替代了阿里运行多年的 HSF 框架,依托于 Dubbo 实现自己的微服务解决方案(DNS),即 Dubbo、Nacos(服务注册与管理)、Sentinel(服务降级、流量治理)。
架构的发展
水平扩展:
部署多个 tomcat服务器,client 请求时通过 loadbalance nginx
实现的特定负载均衡算法把请求发到指定服务器。这样进一步提高了系统的稳定性、可用性。但是存在数据一致性问题。
垂直扩展:
把一个单体架构划分为多个子系统,每个子系统部署在自己的 tomcat 中,多个子系统独立部署共享存储数据。这样某个子系统出现问题不会影响其它子系统的运行,便于系统维护。
RPC架构:
RPC架构是由垂直架构发展而来的,垂直架构中每个进程运行在独立的子系统中,RPC考虑的是子系统间的模块功能调用问题,Service 调用 Service 的问题。RPC 避免了服务的冗余开发,其它需要服务调用时子系统直接调用即可。RPC的核心在于如何与其它进程建立通信,需要考虑通信的方式(HTTP、TCP),采用的协议、数据序列化方式(protobuf、thrift)。
企业服务总线:
ESB,不同语言开发的系统需要调用其它语言开发的服务,这种异构系统如何进行 RPC 调用呢?因为不同进程间通过网络进行服务调用,可以在中间设计一个数据转换层(Grpc、Protobuf、Thrift IDL)将 请求参数/响应结果 进行转换,这样就实现了不同编程语言间服务的调用。
ESB 进程间通信的方式有两种,同步和异步。其中同步调用就是 RPC的调用方式,向某个服务发请请求,然后阻塞等待请求返回,适用于实时性高的系统;异步调用则可以利用 MQ消息队列,将消息放入到队列延缓处理过程。
Dubbo开发
Dubbo代码结构
代码中包含以下角色:
- provider:功能提供者;
- consumer:功能调用者(消费者);
- commons-api:通用内容;
- registry:注册中心;
软件版本
模块开发
项目整体为微服务结构,主模块 dubbo_learn 下包含 3个子模块(dubbo-01-api 用作服务 api接口定义及公共模块定义、dubbo-02-provider 用作服务提供方实现了具体的服务、dubbo-03-consumer用作消费方);
父项目依赖配置:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>3.2.0</version>
</dependency>
dubbo-01-api模块:
//定义了实体类 User、服务接口 UserService
package com.xjx.entity;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private String name;
private String password;
}
package com.xjx.service;
public interface UserService {
public boolean login(String name, String password);
}
dubbo-02-provider模块:
provider 模块引入了 api 模块的依赖,并实现了 api模块的接口,它需要结合 Spring 将服务注册为 Bean对象并将服务发布出去,这里采用 xml 方式注册 Bean。
// 服务接口的具体实现
public class UserServiceImpl implements UserService{
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login name " + name + " password " + password);
return false;
}
}
// 服务持续提供,解析配置文件并阻塞运行
public class ProviderMain {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-provider.xml");
context.start();
//阻塞启动
new CountDownLatch(1).await();
}
}
配置文件定义为 applicationContext-provider.xml
,需要定义好 rpc 的通信协议及端口、对外提供服务的名称:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- provider名称 -->
<dubbo:application name="dubbo-02-provider"/>
<!-- 定义rpc通信协议 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- bean对象注册 -->
<bean id="userService" class="com.xjx.service.UserServiceImpl"/>
<!-- 服务发布 -->
<dubbo:service interface="com.xjx.service.UserService" ref="userService"/>
</beans>
dubbo-03-consumer模块:
服务消费者方需要引入服务接口 api模块,并在配置文件中定义远端服务获取的相关配置。
public class ClientApplication {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-consumer.xml");
//服务调用
UserService service = (UserService) applicationContext.getBean("userService");
service.login("xxxx", "123456");
new CountDownLatch(1).await();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="dubbo-03-consumer"/>
<!-- 远端服务获取配置 interface:服务接口 id:待调用的服务ID url:访问路径-->
<dubbo:reference interface="com.xjx.service.UserService" id="userService" url="dubbo://192.168.220.1:20880/com.xjx.service.UserService"/>
</beans>
运行结果:
provider 启动后会打印服务的调用地址:
consumer 启动后会成功调用服务,但报错,错误原因是 provider的 qos进程和 consumer 的 qos进程端口冲突,Qos=Quality of Service
,qos 是 Dubbo的在线运维命令,可以对服务进行动态的配置,控制及查询,新版本 Dubbo 重构了 telnet,端口默认为 22222。
解决方案如下:
<!-- qos服务配置 -->
<dubbo:parameter key="qos.enable" value="true"/> <!-- 是否开启在线运维命令 -->
<dubbo:parameter key="qos.accept.foreign.ip" value="false"/> <!-- 不允许其它机器访问 -->
<dubbo:parameter key="qos.port" value="33333"/> <!-- 修改port -->
# springboot 配置
dubbo.application.qos.enable=false
dubbo.application.qos.port=33333
dubbo.application.qos.accept.foreign.ip=false
Dubbo 程序细节补充:
- provider 在配置基于 Dubbo协议时,不要显式指定协议端口,用 -1 替代。
<dubbo:protocol name="dubbo" port="-1"/>
- 入门程序浅析:
- 为什么 Provider 提供了 UserService 接口的实现,而在另一个 JVM 中的Consumer 可以调用?Consumer 中调用的到底是什么? 实际上 Consumer 没有直接调用远端的 UserServiceImpl,而是通过调用远端 UserServiceImpl 的代理对象 Proxy来实现。
- 代理的核心工作是什么? 通过代理对 consumer 屏蔽网络通信的过程(通信方式、协议、序列化)来实现数据传递。
基于 SpringBoot 使用 Dubbo
思路分析
思路:深度封装,将公共配置放到 application.yaml
中,将个性配置应用到注解进行设置。
- Provider 的配置处理
- dubbo 的公共配置,包括 dubbo应用名称、dubbo协议名和端口号;
- 服务接口实现的 Bean对象、向外提供服务的 dubbo:service 对象;
- Consumer 的配置处理
- dubbo 的公共配置,应用名、qos 应用;
- 声明需要的服务;
项目开发
- 构建多模块 SpringBoot 应用;
- 引入依赖 jar 包:
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
- 构建 Provider 模块
application.yaml
文件配置:
spring:
application:
name: DUBBO-04-BOOT-PROVIDER
dubbo:
protocol:
name: dubbo
port: -1
核心启动类上标记 @EnableDubbo
注解;
对外服务类 Service
:
@DubboService
public class UserServiceImpl implements UserService {
@Override
public boolean login(String name, String password) {
System.out.println("UserServiceImpl.login name " + name + " password " + password);
return false;
}
}
- 构建 consumer 模块
application.yaml
共有配置:
spring:
application:
name: DUBBO-05-BOOT-CONSUMER
dubbo:
application:
qos-enable: false
需要使用服务时注入配置:
@SpringBootTest
class Dubbo05BootConsumerApplicationTests {
@DubboReference(url = "dubbo://192.168.220.1:20880/com.xjx.service.UserService")
private UserService userService;
@Test
void contextLoads() {
}
@Test
void test1() {
boolean ret = userService.login("xxxx", "1234532");
System.out.println("ret= " + ret);
}
}
核心启动类上建议也加入 @EnableDubbo
。
SpringBoot 项目细节
1、@EnableDubbo 注解的作用?
@EnableDuubo
注解用于扫描 @DubboService
注解内容,并把对应的对象实例化,发布为 RPC 服务。
@EnableDubbo
默认扫描范围为启动类及其子包下的类。但是如果 @DubboService
注解修饰的类没有放到对应路径下,还希望能够扫描到,需要在核心启动类上添加注解 @DubboComponentScan(basePackages = {"xxxxxx"})
显式指定能够扫描到的路径。
除了使用 @DubboComponentScan
注解外,还可以在 yaml 文件中配置扫描 dubbo.scan.base-packages
来扫描指定路径下的服务 Bean对象,发布为 RPC服务。
2、@DubboService 注解的作用?
@DubboService
注解修饰类型,SpringBoot 会创建这个类型的对象并发布为 Dubbo服务。
@DubboService
等同于 @Component(@Service) @Bean
注解的创建对象的作用,它会创建对象并将 Bean对象注册到容器中,通过在 AbstractApplicationContext
类的 refresh()
方法打断点调试查看 SingletonObjects
对象可以验证:
Dubbo 序列化
1、常见的 Dubbo 序列化方式
Hessian
:Dubbo 协议中默认的序列化实现方案;Java Serialization
:JDK序列化方式;Dubbo 序列化
:阿里序列化方案,不成熟生成环境不建议使用;Json 序列化
:目前有两种实现方案,一种是阿里的 fastjson 库,一种是 dubbo 自己实现的简单 json;Kryo 序列化
:Java序列化方式,后续替换Hessian
,是一种非常成熟的序列化实现,已经被广泛使用;FST
:Java序列化方式,后续替换Hessian
,但是缺乏足够成熟的使用场景;ProtoBuf、Thrift IDL
等跨语言二进制序列化方案,允许在多种语言之间交换数据。
docker+zookeeper+dubbo 测试
docker 安装zookeeper 并启动的坑:https://blog.csdn.net/weixin_44808225/article/details/123329529
zookeeper 相关使用:https://juejin.cn/post/7103406988079398942
docker 安装 zookeeper:3.7.1:
# 拉取 zookeeper:3.7.1 镜像
docker pull zookeeper:3.7.1
# 启动 zookeeper 并挂载向数据/日志/配置目录
docker run -d --name zookeeper -p 2181:2181 -v /opt/docker/zookeeper/data:/data -v /opt/docker/zookeeper/conf/zoo.cfg:/conf/zoo.cfg -v /opt/docker/zookeeper/logs:/datalog --restart always zookeeper:3.7.1
标签:Dubbo,调用,服务,入门,dubbo,zookeeper,序列化
From: https://www.cnblogs.com/istitches/p/17590832.html