【Linux网络编程】分布式Json-RPC框架 - 项目设计
目录
理解项目功能
模块划分
服务端
Network
Protocol
Dispatcher
RpcRoute
Publish-Subscribe
Registry-Discovery
Server
客户端
Requestor
RpcCaller
Publish-Subscribe
Registry-Discovery
Client
框架设计
抽象层
具象层
业务层
整体设计框架
理解项目功能
本质上来讲,我们要实现的RPC(远端调用)思想上并不复杂,甚至可以说是简单,其实就是客户端想要完成某个任务的处理,但是这个处理的过程并不自已来完成,而是,将请求发送到服务器上,让服务器来帮其完成处理过程,并返回结果,客户端拿到结果后返回。
然而上图的模型中,是一种多对一或一对一的关系,一旦服务端掉线,则客户端无法进行远端调用,且其服务端的负载也会较高,因此在RPC实现中,我们不仅要实现其基本功能,还要再进一步,实现分布式架构的RPC。对应关系变成多对多,即使有一个服务端挂了,还可以请求其他的分布式架构:简单理解就是由多个节点组成的一个系统,这些节点通常指的是服务器,将不同的业务或者同一个业务拆分分布在不同的节点上,通过协同工作解决高并发的问题,提高系统扩展性和可用性。也就是由多个服务器组成一个系统,由不同的服务器完成同一个业务或不同的业务。其实现思想也并不复杂,也就是在原来的模型基础上,增加一个注册中心,基于注册中心不同的服务提供服务器向注册中心进行服务注册,相当于告诉注册中心自己能够提供什么服务,而客户端在进行远端调用前,先通过注册中心进行服务发现,找到能够提供服务的服务器,然后发起调用。
服务提供者启动之后,先告诉注册中心,自己能提供什么服务。注册中心中就会记录每个服务器能够提供什么服务。当客户端启动之后,就会先进行服务发现,也就是通过注册中心获得能够提供服务的服务端信息。客户端就能够根据获取到的服务端信息进行请求了,从而获取服务。这样设计的话,客户端和注册中心不就成了多对一的关系了吗?此时是可以在某一个服务端上扩展出注册中心的功能,作为备用注册中心。
RPC内部一般会实现一个发布/订阅的功能。它允许服务(发布者)将消息异步地发送给多个对该消息感兴趣的其它服务(订阅者),而无需知道这些订阅者的具体身份或数量。实际上,发布订阅就是服务端可以对消息进行转发。一个客户端想给另一个客户端发送消息,这个客户端可以先将消息发送给服务端,再由服务端对消息进行转发,服务端是会转发给多个客户端的,也就是对消息进行了广播。为了让服务端的广播更加灵活,就引入了一个主题,就是对消息进行划分种类。客户端可以进行创建主题、删除主题、订阅主题、取消订阅主题、主题消息发布等操作。当一个客户端给服务器发送消息时,需要表明将这条消息发布到哪一个主题,服务端就会根据这条消息的主题,给所有订阅了这个主题的客户端推送消息。
注册中心服务器既可以进行服务注册、服务发现,也可以进行消息中转。
所以,我们的项目主要有三个功能:
- RPC远程调用服务
- 服务注册与发现
- 发布与订阅
基于以上功能的合并,我们可以得到一个实现所有功能的结构图:
模块划分
服务端
服务端的功能需求:
- 基于网络通信接收客户端的请求,提供rpc服务
- 基于网络通信接收客户端的请求,提供服务注册与发现,上线&下线通知
- 基于网络通信接收客户端的请求,提供主题操作(创建/删除/订阅/取消),消息发布
在服务端的模块划分中,基于以上理解的功能,可以划分出这么几个模块:
- Network:网络通信模块。网络通信模块是基于muduo库搭建一个高性能服务器。
- Protocol:应用层通信协议模块。有了应用层协议才能解决网络通信过程中的粘包问题。
- Dispatcher:消息分发处理模块 根据消息的类型,将消息分发给不同的模块处理。这个模块的核心是一张表,根据消息类型,将其交给对应的回调函数。会向外提供一个回调注册的接口。
- RpcRouter:远端调用路由功能模块。根据请求中调用的方法来进行路由。
- Publish-Subscribe:发布订阅功能模块。
- Registry-Discovery:服务注册/发现/上线/下线功能模块。
- Server:基于以上模块整合而出的服务端模块。
4、5、6就是上面所说的三个业务功能模块。
Network
该模块为网络通信模块,实现底层的网络通信功能,这个模块本质上也是一个比较复杂庞大的模块,因此鉴于项目的庞大,该模块我们将使用陈硕大佬的Muduo库来进行搭建。
Protocol
之所以要有协议,就是为了解决TCP网络通信过程中的粘包问题。解决粘包有三种方式:特殊字符间隔,定长,LV格式。特殊字符间隔会有一个问题,当消息中也有特殊字符时,需要进行转义,这是比较麻烦的。定长也是不好的,因为我们的消息是有长有短的。所以,我们采用LV格式。
Length:该字段固定4字节长度,用于表示后续的本条消息数据长度。
MType:该字段为Value中的固定字段,固定4字节长度,用于表示该条消息的类型。
- Rpc调用请求/响应类型消息
- 发布/订阅/取消订阅/消息推送类型消息
- 主题创建/删除类型消息
- 服务注册/发现/上线/下线类型消息
IDLength:为消息中的固定字段,该字段固定4字节长度,用于描述后续ID字段的实际长度。
MID:在每条消息中都会有一个固定字段为ID字段,用于唯一标识消息,ID字段长度不固定。
Body:消息主题正文数据字段,为请求或响应的实际内容字段。
对于ID:根据这个ID,就可以判断出一条应答是那一条请求的应答。也可标识一条消息的唯一性。这里一般使用一个字符串,并且字符串由几部分组成,不会使用一个整数。因为这样才能更好地标识唯一性。而使用了字符串就是非定长的,所以需要有LDLength。
Dispatcher
模块存在的意义:区分消息类型,根据不同的类型,调用不同的业务处理函数进行消息处理。
当服务器接收到数据时,就会将数据交给muduo库的数据处理函数进行处理,muduo的数据处理函数onMessageCallback内部会有Protocol模块的onMessage函数,用于将数据进行协议处理,处理完成之后,就会调用Protocol的处理函数onMessageCallback,对数据进行处理,在onMessageCallback中,就会调用Dispatcher的onMessage函数,找到业务处理函数进行调用。
RpcRoute
模块存在的意义:提供RPC请求的处理回调函数,内部所要实现的功能,分辨出客户端请求的服务进行处理得到结果进行响应。
在RPC请求中,请求正文部分一定要包含的两个信息:
- 请求方法名称
- 请求要处理的参数信息
序列化方式有很多种,鉴于当前我们是Json-Rpc,因此这个序列化过程我们就初步使用Json序列化来进行,所定义格式如下:
//RPC-request
{"method" : "Add","parameters" : {"num1" : 11,"num2" : 22}
}//RPC-response
{"rcode" : OK,"result": 33
} {"rcode" : ERROR_INVALID_PARAMETERS
}
当服务端接收到请求,Dispatcher模块识别到是RPC请求时,会将Request中的parameters传递给RPC相对应的回调函数,但是此时这个请求内的数据不一定是合法的,也就是说请求的这个方法,和他传递过来的参数不一定匹配,所以在RpcRoute模块中,调用具体的方法处理函数之前,需要对参数进行检查。这也就要求服务端在进行服务注册时,每一个服务都需要有一个服务描述。服务描述中包含4个内容:方法名称、参数字段及格式描述、参数校验接口、业务回调函数,也就是真正处理请求的函数。
- 方法名称:Add
- 参数字段及格式描述:第一个参数为num1,类型为int,第二个参数为num2,类型为int,返回值类型为int
- 参数校验接口:验证请求中的参数是否合法的函数
- 业务回调函数:真正处理请求的函数
所以服务端在进行服务注册时,需要告诉服务端上面的4个信息。
基于以上理解,在实现该模块时,应该有以下设计:
- 该模块必须具备一个RPC路由管理,其中包含对于每个服务的参数校验功能。
- 该模块必须具备一个方法名称和方法业务回调的映射。
- 该模块必须向外提供RPC请求的业务处理函数。这里说的向外,实际上就是给Dispatcher模块提供,方便Dispatcher直接调用。
Publish-Subscribe
模块存在的意义:针对发布订阅请求进行处理,提供一个回调函数设置给Dispatcher模块。
发布订阅所包含的请求操作:
- 主题的创建
- 主题的删除
- 主题的订阅
- 主题的取消订阅
- 主题消息的发布
既然涉及到网络通信,那就先将通信消息的正文格式定义出来:
//Topic-request
{"key" : "music", //主题名称// 主题操作类型"optype" : TOPIC_CRAETE/TOPIC_REMOVE/TOPIC_SUBSCRIBE/TOPIC_CANCEL/TOPIC_PUBLISH,//TOPIC_PUBLISH请求才会包含有message字段"message" : "Hello World"
}//Topic-response
{"rcode" : OK,
} {"rcode" : ERROR_INVALID_PARAMETERS,
}
基于以上理解,在实现该模块时,应该有以下设计:
- 该模块必须具备一个主题管理,且主题中需要保存订阅了该主题的客户端连接。因为主题收到一条消息,需要将这条消息推送给订阅了该主题的所有客户端。
- 该模块必须具备一个订阅者管理,且每个订阅者描述中都必须保存自己所订阅的主题名称,目的是为了当一个订阅客户端断开连接时,能够找到订阅信息的关联关系,进行删除。因为将消息发布给订阅用户时,只会发送给所有在线的订阅用户。
- 该模块必须向外提供主题创建/销毁,主题订阅/取消订阅,消息发布处理的业务处理函数。这里说的向外,实际上就是给Dispatcher模块提供,方便Dispatcher直接调用。
Registry-Discovery
模块存在的意义:就是针对服务注册、服务发现、服务上线/下线的处理。
服务注册/发现类型请求中的详细划分:
- 服务注册:服务provider告诉中转中心,自己能提供哪些服务
- 服务发现:服务caller询问中转中心,谁能提供指定服务
- 服务上线:在一个provider上线了指定服务后,通知发现过该服务的客户端有个provider可以提供该服务
- 服务下线:在一个provider断开连接,通知发现过该服务的caller,谁下线了哪个服务
既然涉及到网络通信,那就先将通信消息的正文格式定义出来:
// RD--request
{//SERVICE_REGISTRY-Rpc-provider进⾏服务注册//SERVICE_DISCOVERY - Rpc-caller进⾏服务发现//SERVICE_ONLINE/SERVICE_OFFLINE 在provider下线后对caller进⾏服务上下线通知"optype" : SERVICE_REGISTRY/SERVICE_DISCOVERY/SERVICE_ONLINE/SERVICE_OFFLINE,"method" : "Add",//服务注册/上线/下线有host字段,发现则⽆host字段"host" : {"ip" : "127.0.0.1","port" : 9090}
}//Registry/Online/Offline-response
{"rcode" : OK,
}
//error-response
{"rcode" : ERROR_INVALID_PARAMETERS,
}
//Discovery-response
{"method" : "Add","host" : [{"ip" : "127.0.0.1","port" : 9090},{"ip" : "127.0.0.2","port" : 8080} // 表示能提供这个服务的服务器信息]
}
基于以上理解,在实现该模块时,应该有以下设计:
1. 必须具备一个服务发现者的管理:
a.方法与发现者:当一个客户端进行服务发现的时候,进行记录谁发现过该服务,当有一个新的提供者上线的时候,可以通知该发现者
b.连接与发现者:当一个发现者断开连接了,删除关联关系,往后就不需要通知了
2. 必须具备一个服务提供者的管理:
a.连接与提供者:当一个提供者断开连接的时候,能够通知该提供者提供的服务对应的发现者,该主机的该服务下线了
b.方法与提供者:能够知道谁的哪些方法下线了,然后通知发现过该方法的客户端
3. 必须向Dispatcher模块提供一个服务注册/发现的业务处理回调函数
这样,当一个rpc-provider登记了服务,则将其管理起来,当rpc-caller进行服务发现时,则将保存的对应服务所对应的主机信息,响应给rpc-caller。而,当中途一个rpc-provider上线登记服务时,则可以给进行了对应服务发现的rpc-caller进行服务上线通知,通知rpc-caller当前多了一个对应服务的rpc-provider。同时,当一个rpc-provider下线时,则可以找到进行了该服务发现的rpc-caller进行服务的下线通知。
Server
当以上的所有功能模块都完成后,我们就可以将所有功能整合到一起来实现服务端程序了。
- RpcServer:rpc功能模块与网络通信部分结合。
- RegistryServer:服务发现注册功能模块与网络通信部分结合
- TopicServer:发布订阅功能模块与网络通信部分结合。
客户端
在客户端的模块划分中,基于以上理解的功能,可以划分出这么几个模块:
- Protocol:应用层通信协议模块
- Network:网络通信模块
- Dispatcher:消息分发处理模块
- Requestor:请求管理模块。这个模块主要是判断服务端的应答是哪一个请求的应答
- RpcCaller:远端调用功能模块。给用户提供RPc调用的接口
- Publish-Subscribe:发布订阅功能模块。给用户提供主题操作、消息操作的接口
- Registry-Discovery:服务注册/发现/上线/下线功能模块。给用户提供操作接口,并且能对收到的服务上限、下线进行处理
- Client:基于以上模块整合而出的客户端模块
前3个模块与服务端没有任何区别,这里就不过多赘述了。
Requestor
模块存在的意义:针对客户端的每一条请求进行管理,以便于对请求对应的响应做出合适的操作。
首先,对于客户端来说,不同的地方在于,更多时候客户端是请求方,是主动发起请求服务的一方,而在多线程的网络通信中,多线程下,针对多个请求进行响应可能会存在时序的问题,这种情况下,则我们无法保证一个线程发送一个请求后,接下来接收到的响应就是针对自己这条请求的响应,这种情况是非常危险的一种情况。
其次,类似于Muduo库这种异步lO网络通信库,通常lO操作都是异步操作,即Muduo库对于每一个链接都会有一个在应用层的接收缓冲区和发送缓冲区,当调用send时,底层并不是调用原生的send将数据通过网络发送出去,而是将数据放入了这个发送缓冲区,等到IO写事件触发后,才会将发送缓冲区的数据发送出去。IO读取数据完成后调用处理回调进行数据处理,因此也无法直接在发送请求后去等待该条请求的响应。
针对以上问题,我们则创建出当前的请求管理模块来解决,它的思想也非常简单,就是给每一个请求都设定一个请求ID,服务端进行响应的时候标识响应针对的是哪个请求(也就是响应信息中会包含请求ID),因此客户端这边我们不管收到哪条请求的响应,将数据存储入一则hash_map中,以请求ID作为映射,并向外提供获取指定请求ID响应的阻塞接口,这样只要在发送请求的时候知道自己的请求ID,那么就能获取到自己想要的响应,而不会出现异常。
针对这个思想,我们再进一步,可以将每个请求进一步封装描述,添加入异步的future控制,或者设置回调函数的方式,在不仅可以阻塞获取响应,也可以实现异步获取响应以及回调处理响应。
给用户提供3种不同的请求方式:
- 同步:发送请求之后,阻塞等待响应。
- 异步:发送请求之后,返回一个future对象,在想要获取结果时,就可以通过这个future对象获取结果了。
- 回调:让用户设置回调函数,这样用户就不需要等待了,客户端在接收到响应之后,会自动进行处理。
客户端在发送请求时都是通过Requestor模块发送请求的,不会直接发送,这样才能将所有的请求管理起来。
RpcCaller
模块存在的意义:向用户提供进行rpc调用的模块。
Rpc服务调用模块,这个模块相对简单,只需要向外提供几个rpc调用的接口,内部实现向服务端发送请求,等待获取结果即可,稍微麻烦一些的是Rpc调用我们需要提供多种不同方式的调用:
- 同步调用:发起调用后,等收到响应结果后返回
- 异步调用:发起调用后立即返回,在想获取结果的时候进行获取
- 回调调用:发起调用的同时设置结果的处理回调,收到响应后自动对结果进行回调处理
这个模块向外提供3种不同类型的接口,用户直接调用相应的接口来进行RPC调用。但是注意,请求都是在Requestor模块进行发送的,响应也是Requestor模块先接收到。Requestor模块接收到请求后,如果是同步或异步,就直接设置到call的参数response或future中,如果是回调,就直接调用cal中传过来的callback。
Publish-Subscribe
模块存在意义:向用户提供发布订阅所需的接口,针对推送过来的消息进行处理。
一个客户端发布了某一个主题的消息后,是将请求交给Requestor模块发送的,请求交给服务端之后,服务端会给所有订阅了这个主题的客户端推送这条消息。主题创建/删除/订阅/取消订阅,都是客户端的主动请求。但是消息发布到服务器后,对于发布消息的客户端是一个主动请求,但是对于订阅了该主题的客户端是一个被动请求。所以,对于每一个客户端,在订阅一个主题时,都必须设置一个回调函数,用于接收到这个主题的消息时进行调用,因为这是来自服务端的消息发布请求,不是一个响应。因为一个客户端可能会订阅多个主题,所以客户端也需要有Dispatcher模块,根据接收到的消息的主题进行分发。
客户端接收到消息后,Dispatcher模块会判断消息是消息发布请求,还是响应,若是消息发布请求就将请求交给onPublish接口,这个接口内部就会调用客户端在订阅主题时注册进来的回调函数;若是响应,就交给Requestor模块。在订阅主题处,需要传入一个callback,就是收到一个消息发布请求时的处理函数。
Registry-Discovery
我们之前说过,为了避免客户端与服务器的多对一或一对一关系,引入了一个注册中心。服务端要提供服务,先要向注册中心进行服务注册,客户端要请求服务需要先向注册中心进行服务发现,然后才能向服务端请求服务。RPC的服务端肯定是没办法与注册中心的服务端进行连接的,所以在服务器上是需要有一个注册中心的客户端的。因为一个客户端没办法连接多个服务端,所以在客户的机器上,除了要有一个RPC的客户端,也要有一个注册中心的客户端。
现在在讲的实际上就是RegisteryClient、DiscoverClient。
作为服务操作的客户端:
- 向外提供服务注册的功能接口:连接注册中心,发送服务注册请求,等待响应判断是否注册成功
- 向外提供服务发现的功能接口:连接注册中心,发送服务发现请求,等待响应获取到提供服务的主机信息
也就是要实现的客户端既能用于provider进行服务注册,也能用于caller进行服务发现。
进行服务发现的时候,注册中心只能将当前注册了该服务的主机地址进行返回,如果后续又有其他的主机注册了该服务,之前进行了服务发现的主机是得不到这个主机信息的,因此还得有服务上线/下线通知。
服务通知的回调是设置给Dispatcher模块的,当接收到服务上线/下线的通知时,就调用这个函数,
因为这是注册中心主动推送给客户端的消息。对于服务发现的应答则会交给Requestor模块。同时,我们也给用户一个回调函数,当服务下线时让用户可以有一些灵活的处理。
Client
TcpClient接收到消息之后,就会调用onMessageCallback,这个函数内部又会调用LVProtocol的onMessage函数,得到一条完整的消息,并反序列化,再调用Dispatcher的onMessage函数,分发给不同的模块,其实就是根据消息的类别,调用不同的回调函数。接收到的消息如果是之前请求的应答,直接交给Requestor模块进行处理;如果是消息发布请求或者上线/下线的通知,则调用先前设置好的回调函数进行处理。
框架设计
在当前项目的实现中,我们将整个项目的实现划分为三层来进行实现:
- 抽象层:将底层的网络通信以及应用层通信协议以及请求响应进行抽象,使项目更具扩展性和灵活性。在上层使用时,都是基于抽象,也就是通过父类指针指向子类对象去实现功能未来如果底层发现变化,如网络通信不使用muduo库,此时只需要对抽象层进行改动即可,不影响上层。
- 具象层:针对抽象的功能进行具体的实现。对抽象层中抽象出来的基类进行派生,派生出实现一个用于具体功能的子类,如对于抽象出的网络通信部分,派生出子类,然后基于muduo库实现基本的功能。
- 业务层:基于抽象的框架在上层实现项目所需功能。在网络通信之上实现具体的业务,如支持RPC服务、发布/订阅服务、服务注册/发现服务等。
抽象层
在我们的项目实现中,网络通信部分采用了第三方库Muduo库,以及通信协议使用了LV格式的通信协议解决粘包问题,数据正文中采用了Json格式进行序列化和反序列化,而这几方面我们都可能会存在继续优化的可能,甚至在序列化方面不一定非要采用Json,因此在设计项目框架的时候,我们对于底层通信部分相关功能先进行抽象,形成一层抽象层,而上层业务部分根据抽象层来完成功能,这样的好处是在具体的底层功能实现部分,我们可以实现插拔式的模块化替换,以此来提高项目的灵活性和扩展性。具体需要抽象那些内容呢?
请求/响应部分。请求/响应部分中一定要有的内容是消息类型、消息ID、序列化与反序列化功能、检测消息是否为合法消息的检测功能。基于这些功能,就抽象出了一个基类BaseMessage,这个基类中就提供了消息类型的设置与获取、消息ID的设置与获取、消息序列化与反序列化接口、以及消息检测接口。在序列化中,要使用其他的方案进行序列化,只需要在派生类中进行修改即可。而在应用的位置,是不需要修改的。
对接收缓冲区的抽象。为了能够读取到一条完整的数据,就需要有一个接收缓冲区。peeklnt32是从缓冲区中读取4字节的数据,但是不取出。readlnt32是从缓冲区取出4字节的数据。
对于应用层协议的抽象。onMessage就是从缓冲区取出一条消息,进行反序列化,然后将消息返回。serialize就是将消息序列化。canProcessed是判断一下缓冲区中是否有一条完整的消息。
对于连接的抽象。send是发送消息的接口,只需要传入一个BaseMessage,内部会进行序列化,然后再发送。shutdown是关闭连接。connected是判断连接是否正常。不需要提供recv接口,因为现在都是基于事件触发的。
有了以上的抽象,就可以基于上面的抽象去实现BaseClient和BaseServer了。
高并发服务器都是一个多Reactor多线程的模型。主Reactor只负责对监听套接字进行事件监控,当有新链接到来时,就调用onAccept,创建一个新链接,并将这个新连接交给从属Reactor。当从属Reactor中有可读事件触发了,会将数据读取到接收缓冲区BaseBuffer中,然后调用canProcessed判断BaseBuffer中的数据是否是一条完整的消息,若至少一条完整的消息,则会调用onMessage,进行消息处理,首先会进行反序列化,分析是什么类型的消息,然后交给Dispatcher模块进行处理。当然,在连接刚建立和连接关闭时,分别会调用onConnected和onClose这两个回调函数,让用户能够更加灵活地设置。向外提供3个设置回调函数的接口。
一个客户端只能与服务器建立一次连接,所以只需要有一个Reactor。当客户端与服务器建立好连接之后,就将这个连接加入到Reactor中。
具象层
具象层就是针对抽象的具体实现。
而具体的实现也比较简单,从抽象类派生出具体功能的派生类,然后在内部实现各个接口功能即可。
- 基于Muduo库实现网络通信部分抽象
- 基于LV通信协议实现Protocol部分抽象
不过这一层中比较特殊的是,我们需要针对不同的请求,从BaseMessage中派生出不同的请求和响应类型,以便于在针对指定消息处理时,能够更加轻松的获取或设置请求及响应中的各项数据元素。
业务层
业务层就是基于底层的通信框架,针对项目中具体的业务功能的实现了,比如Rpc请求的处理,发布订阅请求的处理以及服务注册与发现的处理等等。
RPC
服务端:RpcRoute模块会给Dispatcher模块提供一个onRpcRequest函数,当一条消息是RPC请求时,就会调用这个函数。
客户端:Requestor模块会给Dispatcher模块提供一个OnRocResponse函数,当一条消息是RPC响应时,就会调用这个函数。当然,Requestor模块也会向服务端发送RPC请求。
发布订阅
客户端给用户提供了5个操作函数。当用户调用这5个接口时,内部就会通过Requestor模块发送请求。到达服务端之后,Dispatcher模块就会判断这个消息是一个发布订阅的消息,就会调用PSManager设置的回调函数onTopicRequest,这个函数内部就会判断具体是关于主题的什么操作,然后进行处理。如果是消息发送的消息,是需要转发给其他客户端的,所以客户端还要有一个onPublic函数,用于对接收到的转发消息进行处理。所以,PSManger还需要向Dispatcher提供一个onPublic的回调接口。客户端还会向Dispatcher提供一个onTopicResponse的回调函数,用于对接收到的响应进行处理。
服务注册与发现
客户端会有一个RDManager,服务注册于发现的管理者,其中包含一个服务发现者和一个服务提供者。服务提供者向外提供一个serviceRegistry接口,用于向服务端发送一个服务注册的消息。通过Requestor模块发送出去后,服务端的Dispatcher模块就会调用onRegistry进行服务注册。服务发现者会向用户提供一个serviceDiscovery接口,通过Requestor发送请求,就会调用onDiscovery接口进行服务发现,然后进行响应。通过onRDResponse返回给客户端。如果有某个服务提供者下线了,或者有新的服务提供者上线了,都需要告诉发现过这个服务的客户端。所以,Discoverer需要提供一个onRDNotify。
整体设计框架
由具象层实现具体的网络通信、协议解析、消息字段。抽象层将这些接口抽象出来,并提供给业务层使用。业务层于抽象层之间,是通过Dispatcher进行关联的。