Dubbo
What
[阿里巴巴]开源的一款高性能优秀的服务框架(jar包),通过高性能RPC实现服务治理(整合了一些常见协议主要rpc),可以和Spring框架无缝集成。
他提供了三大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
dubbo:阿里
dubbox:当当(因阿里停更一段时间而产生)
Why
架构从单一进入集群时代并由SOA向下过度后,遇到了一系列问题:
1、服务器越来越多,服务URL配置管理变得复杂困难,F5单点压力也越来越大。
2、服务间依赖错综复杂,甚至分不清哪个应用要在那个应用之前启动,即便架构师也不能准确完整的描述。
3、服务的调用越来越大,服务的容量问题就暴漏出来了,这个服务需要多少机器支撑?什么时候该加服务器?
Dubbo的出现就是为了解决以上问题,他的核心服务治理图示如下:

应用解耦、有利于工作分工
有利于热点应用集中部署
技术原理:
技术架构:

Provider:暴漏服务的服务方提供
Consumer:调用远程服务的服务消费方
Registry:服务注册于发现的注册中心(外部提供zookeeper、redis)
Monitor:统计服务的调用次数和调用时间的监控中心
Container:服务运行容器
Dubbo架构简单,实现细节复杂
整体设计

流程描述:
启动容器-》服务提供者将对外的服务发布到注册中心-》消费者启动时去注册中心订阅自己需要的服务,根据软负载访问服务地址
异常处理:
1、服务提供这地址如发生变更,注册中心根据消费者提供的订阅信息,主动基于长连接将变更数据给消费者
2、消费者根据请求地址+软负载如果第一地址调用失败,再选另一台调用。
3、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo服务治理
| 特性 | 描述 |
|---|---|
| 透明远程调用 | 就像调用本地方法一样调用远程方法;只需简单配置,没有任何API侵入; |
| 负载均衡机制 | Client端LB,可在内网替代F5等硬件负载均衡器; |
| 容错重试机制 | 服务Mock数据,重试次数、超时机制等; |
| 自动注册发现 | 注册中心基于接口名查询服务提 供者的IP地址,并且能够平滑添加或删除服务提供者; |
| 性能日志监控 | Monitor统计服务的调用次调和调用时间的监控中心; |
| 服务治理中心 | 路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等手动配置。 |
| 自动治理中心 | 无,比如:熔断限流机制、自动权重调整等; |
Dubbox扩展特性
支持REST风格远程调用(HTTP + JSON/XML);
支持基于Kryo和FST的Java高效序列化实现;
支持基于Jackson的JSON序列化;
支持基于嵌入式Tomcat的HTTP remoting体系;
升级Spring至3.x;
升级ZooKeeper客户端;
支持完全基于Java代码的Dubbo配置;
标品对比
| 描述 | Dubbo | DubboX | Eureka | Nacos |
|---|---|---|---|---|
| who | 阿里 | 当当 | Netflix | 阿里 |
| 一致性协议 | CP | CP | AP | CP+AP |
| 健康检查 | Keep Alive | Keep Alive | Client Beat | TCP/HTTP/MYSQL/Client Bea |
| 负载均衡策略 | - | - | Ribbon | 权重/ metadata/Selector |
| 雪崩保护 | 无 | 无 | 有 | 有 |
| 自动注销实例 | 支持 | 支持 | 支持 | 支持 |
| 访问协议 | TCP | TCP | HTTP | HTTP/DNS |
| 监听支持 | 支持 | 支持 | 支持 | 支持 |
| 多数据中心 | 不支持 | 不支持 | 支持 | 支持 |
| 跨注册中心同步 | 不支持 | 不支持 | 支持 | 不支持 |
| SpringCloud集成 | 支持 | 支持 | 支持 | 支持 |
| k8s集成 | 不支持 | 不支持 | 支持 | 不支持 |
RPC框架功能比较:
| 功能 | Hessian | Montan | rpcx | gRPC | Thrift | Dubbo | Dubbox | Spring Cloud |
|---|---|---|---|---|---|---|---|---|
| 开发语言 | 跨语言 | Java | Go | 跨语言 | 跨语言 | Java | Java | Java |
| 分布式(服务治理) | × | √ | √ | × | × | √ | √ | √ |
| 多序列化框架支持 | hessian | √(支持Hessian2、Json,可扩展) | √ | × 只支持protobuf) | ×(thrift格式) | √ | √ | √ |
| 多种注册中心 | × | √ | √ | × | × | √ | √ | √ |
| 管理中心 | × | √ | √ | × | × | √ | √ | √ |
| 跨编程语言 | √ | ×(支持php client和C server) | × | √ | √ | × | × | × |
| 支持REST | × | × | × | × | × | × | √ | √ |
| 关注度 | 低 | 中 | 低 | 中 | 中 | 中 | 高 | 中 |
| 上手难度 | 低 | 低 | 中 | 中 | 中 | 低 | 低 | 中 |
| 运维成本 | 低 | 中 | 中 | 中 | 低 | 中 | 中 | 中 |
| 开源机构 | Caucho | Apache | Apache | Alibaba | Dangdang | Apache |
实际场景中的选择
- Spring Cloud : Spring全家桶,用起来很舒服,只有你想不到,没有它做不到。可惜因为发布的比较晚,国内还没出现比较成功的案例,大部分都是试水,不过毕竟有Spring作背书,还是比较看好。
- Dubbox: 相对于Dubbo支持了REST,估计是很多公司选择Dubbox的一个重要原因之一,但如果使用Dubbo的RPC调用方式,服务间仍然会存在API强依赖,各有利弊,懂的取舍吧。
- Thrift: 如果你比较高冷,完全可以基于Thrift自己搞一套抽象的自定义框架吧。
- Montan: 可能因为出来的比较晚,目前除了新浪微博16年初发布的,
- Hessian: 如果是初创公司或系统数量还没有超过5个,推荐选择这个,毕竟在开发速度、运维成本、上手难度等都是比较轻量、简单的,即使在以后迁移至SOA,也是无缝迁移。
- rpcx/gRPC: 在服务没有出现严重性能的问题下,或技术栈没有变更的情况下,可能一直不会引入,即使引入也只是小部分模块优化使用。
HOW
1、api:edu-dubbo-api
```java
class:User
public class User implements Serializable {
private Long id;
private String name;
private Date birthday;
private String sex;
private Integer age;
…
}
interface:IUserService
public interface IUserService {
void save(User user);
}
```
2、provider:edu-dubbo-provider
pom.xml
<dependency>
<groupId>org.dh</groupId>
<artifactId>edu-dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
class:interface->impl
import com.alibaba.dubbo.config.annotation.Service;
import org.dh.entiy.User;
import org.dh.service.IUserService;
@Service
public class UserServiceImpl implements IUserService {
@Override
public void save(User user) {
System.out.println(user);
}
}
application.properties
server.port=8081
dubbo.application.name=edu-dubbo-provider
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.port=20880
dubbo.protocol.name=dubbo
dubbo.monitor.protocol=registry
3、edu-dubbo-consumer
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<dependency>
<groupId>org.dh</groupId>
<artifactId>edu-dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
controller:UserController
import com.alibaba.dubbo.config.annotation.Reference;
import org.dh.entiy.User;
import org.dh.service.IUserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
@RequestMapping("user")
public class UserController {
@Reference
private IUserService userService;
@GetMapping("add")
public boolean add(){
User user = new User();
user.setId(1L);
user.setName("Dcrain");
user.setAge(25);
user.setBirthday(new Date(System.currentTimeMillis()));
user.setSex("男");
userService.save(user);
return true;
}
}
application.properties
server.port=8082
dubbo.application.name=edu-dubbo-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.consumer.timeout=3000
4、启动服务
1)启动zookeeper
2)启动provider
3)启动consumer
4)访问:http://localhost:8082/user/add


Dubbo遇到的坑
使用过Dubbo如果没有碰到坑证明没有深交Dubbo
dubbo如何解决循环依赖的问题?
在分布式项目中,A调用B, B再调用A,或者A调B,B调用C,C再调用A,形成一个环路时,就会出现循环依赖的问题
当启动A服务时,需要B服务暴露的接口,找不到就会抛异常,B服务启动时,需要同样需要依赖A服务暴露的接口,也会抛异常。
所以我们经常在reference中配置check=false,这样在服务启动时就不会检查实例是否已经实例化。
还可以从架构层面进行解决:
1:职责划分,把职责划分更加清晰,只允许A调B,或者B调A,不允许出现循环
2:可以使用中间间代替dubbo调用,A调用B,B异步推送数据到A
为什么重写equals时一定要重写hashcode?
一个类只重写了equals方法,不重写hashcode,那么对象equals为true(比较内容),但是hashcode为false(因为不同对象,地址不同)
那么对于hash散列表结构的容器集合,就会出现问题。
新建一个类,重写equals的同时一定要重写hashcode方法,否则使用散列表数据结构的容器时就会出现问题,在重写equals和hashcode后,可以保证equals比较为true,hashcode比较一定为true,但是hashcode相同的两个对象,equals不一定相同。
本文详细介绍了Dubbo作为一款高性能的服务框架,其核心功能包括远程方法调用、负载均衡及服务自动注册与发现等。同时对比了Dubbo与其他服务框架的特点,并提供了基于Spring Boot的Dubbo集成示例。
436

被折叠的 条评论
为什么被折叠?



