Skip to content

Instantly share code, notes, and snippets.

@findstr
Created October 26, 2020 09:52
Show Gist options
  • Save findstr/d2ad45724fb684a80806d4b34bab1205 to your computer and use it in GitHub Desktop.
Save findstr/d2ad45724fb684a80806d4b34bab1205 to your computer and use it in GitHub Desktop.

#分布式系统架构

现在很多公司都是使用dubbo或者类似dubbo的rpc调用。说说我对dubbo的理解 dubbo 存活检测感觉分为下面三个层面 服务端与注册中心的链接状态 通常注册中心是zookeeper,服务端注册临时节点,客户端注册这个节点的watch事件,一但服务端失联, 客户端将把该服务从自己可用服务列表中移除。(一个服务通常有多个提供者,只是把失联的提供者移除)。 zookeeper是通过心跳发现服务提供者失联的,心跳实际上就是以固定的频率(比如每秒)发送检测的数据包;

客户端与注册中心的链接状态 客户端与zookeeper失联,会暂时使用自己缓存的服务提供者列表。如果每个提供者多次调不通,把它移除。

客户端与服务单的链接状态 服务端提供类似于echo的方法,客户定时调用。部分返回正常,认为服务处于亚健康状态,如果超过阀值,会被降级 从服务提供者列表移除。被移除的方法可能会在超过一定时间后,拿回来重试,可以恢复成正常服务,也可能继续降级。

##概要设计 0. Master重启期间不允许,新增worker

  1. 服务器分为Master和Worker.
  2. Master为中心节点每次启动都会有一个惟一的instanceid来代表当前实例.
  3. Worker为工作节点,每次启动后,由Master分配一个惟一instanceid来代表当前实例.
  4. 所有类型服务器个数,在开服前确定
  5. 整体存活的服务器个数不会超过开服前设定的服务器个数
  6. 服务器之间逻辑层无链接(底层使用TCP仅仅是为了数据传输可靠性)
  7. 服务器存活仅依靠心跳判断
  8. Master会提供telnet/http接口来显示服务器的状态
  9. 已经处理down状态下的实例,在有新实例替换后,不可以再次复活(需要运维保证)

###服务状态 up ready run down

###Worker服务类型 gate xn role xn citywar xn auth x1 chat x1 scene x1 league x1

###服务发现机制

  1. Master作为第一个进程启动
  2. Worker启动后会循环向Master进行up_r来申请运行
  3. Master等待所有Worker都启动完毕(收到所有Worker的up_r或run_r,如果有多个down状态下的实例,则会统计所有非down的个数和)
  4. 向当前所有启动的实例,同步一次当前实例列表
  5. 开始分配hashid, instanceid, 然后返回成功
  6. Worker拿到返回的注册数据后,开始执行初始化逻辑, 初始化完成之后,会再次循环向Master发送run_r来申请工作.
  7. MASTER在收到run_r之后检查是否所有实例都已经处理run状态
  8. Worker收到成功之后,开始真正注册处理逻辑

###Master存活检测

  1. Worker每10s会向Master发送心跳, Master会向Worker返回一个当前MID.
  2. Worker会校验当前WID与上次拿到的WID不同,说明Master被重启,丢失集群信息.主动上报自己信息.

###Worker存活检测

  1. Master每10s会向所有Worker发送心跳.
  2. 如果心跳超时,则将其标记为DOWN. 但并不会集群广播(因为已经失联了,其他集群也超时了,广播不广播用处不大,并且有可能仅仅和worker失联).
  3. 如果在此服务没有被踢掉(被新来的register_r挤掉)之前,如果心跳恢复,则状态恢复到ALIVE

###服务器类型

Master 	x1  <-> Auth, Role, Scene, Chat
Auth	x1	<-> Gate, Master
Gate	xN  <-> Role, Scene, Chat
Role	xN	<-> Gate, Chat, Citywar
CityWar xN  <-> Gate, Chat, Role
Chat	x1	<-> CityWar, Role, Gate
League  x1  <-> Gate, Role, Chat
Scene	x1	<-> Gate

###启动顺序

首先启动Master

Master启动后,会自动加载所有uid和cityid,

根据预定义RoleServer的个数预处理为M个slot为每个来注册的RoleServer分配一个slot中所包含的uid 根据预定义CitywarServer的个数预处理为M个slot为每个注册的CitywarServer分配一个slot所包含的cityid

服务注册与发现

每个服务采用register_r向Master注册服务,当Master服务收到register_r时 Master根据相应类型向此服务颁发其应该负责的ID. 然后会通过welcome_r来通知所有已经注册过的服务器,'欢迎'已注册过的服务器.

每个服务器收到服务器之后.根据需要,通过query_r向MasterServer拉到其责任ID,然后通过hello_r来进行握手进行互联.

###客户端登陆流程

认证

  1. Client->AuthServer 发送**认证协议(比如挑战式认证)**进行认证
  2. AuthServer首先确认当前Account是否存在,如果不存在则直接创建,如果存在则验证登陆信息是否正确
  3. 如果正确,则查看是否有正在登陆玩家,如果有先踢下线,然后挑选出负载最低的GateServer
  4. 然后通过RPC向GateServer索取登陆token, 然后将此uid, token, GateServer的监听端口一并返回给客户端
  5. 客户端拿着uid, token向GateServer进行登陆

ps. AuthServer会定时向所有GateServer进行轮询, 检查当前负载数量

登陆

TODO:

###协议定义 error_code [ 0x00 --> OK 0x01 --> Fitting ] stabilize_r 0x0001 { instance { .type:string 1 .status:string 2 .listen:string 3 .slotid:integer 4 .instanceid:integer 5 } .instances:instance[] 1 } stabilize_a 0x0002 {

}
up_r 0x0003 {
	.type:string 1
	.listen:string 2
	.slotid:integer 3
	.instanceid:integer 4
}
up_a 0x0004 {
	.result:integer 1
	.status:string 2
	.slotid:integer 3
	.instanceid:integer 4
}
run_r 0x0005 {
	.type:string 1
	.listen:string 2
	.slotid:integer 3
	.instanceid:integer 4
}
run_a 0x0006 {
	.result:integer 1
	.status:string 2
}
heartbeat_r 0x0007 {
	.type:string 1
	.instanceid:integer 2
}
heartbeat_a 0x0008 {
	.type:string 1
	.instanceid:integer 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment