分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。
分布式是一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统,其目的是利用更多的机器,处理更多的数据。
小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,又请了个配菜师,两个配菜师关系是集群。
—— 张鹏飞
分布式架构四个核心问题
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
本地过程调用
直接调用,函数体通过函数指针来指定。

远程过程调用
// Client端
1. 将调用映射为 (函数,进程 ID)。
2. 将 (函数,进程 ID) 序列化,以二进制形式打包
3. 把 2 中得到的数据包发送给 Server,这需要使用网络传输层
4. 等待服务器返回结果
5. 如果服务器调用成功,那么就将结果反序列化.
// Server端
1. 在本地维护 (函数,进程ID) 到函数指针的映射,可以用 Map map
2. 等待客户端请求
3. 得到一个请求后,将其数据包反序列化,得到 (函数,进程ID)
4. 通过在 map 中查找,得到相应的函数指针
5. 本地调用函数,得到结果
6. 将结果序列化后通过网络返回给 Client


| 节点 | 角色说明 |
|---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
指定 zookeeper 注册地址
# dubbo-admin-master-0.2.0\dubbo-admin\src\main\resources\application.properties
server.port=7001
spring.velocity.cache=false
spring.velocity.charset=UTF-8
spring.velocity.layout-url=/templates/default.vm
spring.messages.fallback-to-system-locale=false
spring.messages.basename=i18n/message
spring.root.password=root
spring.guest.password=guest
# 注册地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
在项目目录下打包 dubbo-admin
mvn clean package -Dmaven. test. skip=true


启动 zookeeper
启动 dubbo
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
浏览器访问 http://localhost:7001/,默认用户名和密码都为 root

以管理员身份运行 zkService.cmd
服务闪退问题
解决方案
编辑 zkService.cmd 文件,末尾添加 pause ,暂停服务,便于查找错误。

程序未找到 zoo.cfg

复制 conf/zoo_sample.cfg 改名为 zoo.cfg

再次以管理员身份启动 zkService.cmd

测试
打开用户端 bin/zkCli.cmd




<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.3version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>4.2.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
# application.properties
server.port=8001
# 服务应用名字
dubbo.application.name=provider-server
# 注册中心地址,即存放服务的地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 待注册的服务
dubbo.scan.base-packages=com.why.service
// service
public interface TicketService {
public String getTicket();
}
// service
// zookeeper:服务注册与发现
// 项目已启动就自动注册到注册中心
// 注意使用 dubbo 的 Service 注解
@Service
@Component
public class TicketServiceImpl implements TicketService {
@Override
public String getTicket() {
return "Train Ticket";
}
}
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>2.7.3version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>4.1.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>4.2.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
# application.properties
server.port=8002
# 消费者取服务,需要暴露自己的名字
dubbo.application.name=consumer-server
# 注册中心的地址,即取服务的地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
// service
public interface UserService {
public void buyTick();
}
// service
// 此处的 @Service 是将实现类注册到 Spring 中,注意导包路径
@Service
public class UserServiceImpl implements UserService {
// 在注册中心拿到 provider-server 的服务
// 本地可定义与 provider-server 端路径相同的接口,使用 @Reference 引用 provider-server 端注册在注册中心的服务
@Reference
private TicketService ticketService;
@Override
public void buyTick() {
String ticket = ticketService.getTicket();
System.out.println("在注册中心拿到=>" + ticket);
}
}
// service
public interface TicketService {
public String getTicket();
}
启动 zkService
启动 dubbo-admin 程序
启动 provider-server 服务



编写并启动 consumer-server 测试
@Test
void testBuyTick() {
userService.buyTick();
}

consumer-server 服务并未编写实现类实现 TicketService 接口,此为 consumer-server 在注册中心拿到的结果。即执行了如下过程:
