网络编程
位置:首页>> 网络编程>> Python编程>> 基于Python编写一个简单的服务注册发现服务器

基于Python编写一个简单的服务注册发现服务器

作者:真的不能告诉你我的名字  发布时间:2022-06-11 20:23:31 

标签:Python,服务器

我们都知道有很多的非常著名的注册服务器,例如: ConsulZooKeeperetcd,甚至借助于redis完成服务注册发现。但是本篇文章我们将使用python socket写一个非常简单的服务注册发现服务器。

本篇文章所依赖的环境为:

基于Python编写一个简单的服务注册发现服务器

案例展示

项目地址:

gitee: gitee.com/pdudo/golearn/blob/master/python/registerCenter/registerSer.py

该服务注册服务器,没有使用第三方库,可以复制到本地,使用python registerSer.py启动即可,启动成功后,会输出“注册服务器已经启动...”的信息。

该案例使用的协议是TCP协议,为了防止粘包,客户端在发送消息的时候,使用CRLF进行区分,服务器是按照读取到CRLF进行分割的,服务数据,均采用json格式,共支持注册服务、查询服务、销毁服务。

对应的json为:

注册服务:

{"type":"register","key":"redis","values":"123456@127.0.0.1:6379","timeout":"30"}

注册和更新服务都是用的上述报文,其中type 是告诉服务器需要做什么,register代表注册服务,keyvalues是注册服务的名称和注册服务的值,最后的timeout是存活时间,单位是秒,若超过这个时间没有更新服务,则自动销毁该服务。

如果客户端发送的信息没有携带time,则注册存活时间为30s

查询服务:

{"type":"query","key":"redis"}

{"type":"query","key":"all"}

查询服务使用的typequery,查询的key若是all则是查询全部,其他则是查询值为key的服务信息。

销毁服务:

{"type":"destruction","key":"redis"}

销毁服务的typedestructionkey为需要销毁服务的名称。

目前还暂不支持登录验证。

案例测试

由于该注册服务报文采用的是使用CRLF进行分割,所以可以直接使用telnet进行模拟注册,为了方便,我们还是使用python来写这个测试,代码如下:

基于Python编写一个简单的服务注册发现服务器

上述代码,我们需要自己指定\r\n进行分割,我们指定了4个注册,以及等待5s再进行查询,最后再销毁了一个服务。

运行结果如下:

基于Python编写一个简单的服务注册发现服务器

可以看到上述结果,第一个输出是我们注册的消息,而后我们等待了5秒,目的是为了等redis服务因为超时被自动销毁,而后再进行查询,就没有redis2的注册信息了,接着我们销毁了mysql1的服务,整个测试完毕。

如何来写一个注册发现服务器

我们想要写一个注册服务器,我们至少需要知道以下几个知识点:

  • 服务注册发现核心是啥?

  • 应用层数据报文应该如何定义。

  • 如何从tcp数据流中按条获取记录。

服务器发现核心是啥

python中,我们可以通过定义一个字典,来存储所有注册信息,例如:

{
       "redis": {
       "value": "123456@127.0.0.1:6379",
       "timeout":30,
       "lastUpdateTime": time.time()
       },
       "redis1": {....}
}

我们只需要维护好这个字典,就可以做到最基本的服务注册和服务发现了。

报文如何定义

该服务,我们使用的是json来定义的数据报文,按照type区分,分别为:

  • register: 注册或更新服务。

  • query: 查询服务。

  • destruction销毁服务。

json中,以keyvalue来确定服务名称和服务值,还有一个timeout来定义超时销毁时间。

例如:

{"type":"register","key":"redis1","values":"123456@127.0.0.1:6379","timeout":"30"}
{"type":"query","key":"all"}
{"type":"destruction","key":"mysql1"}

如何从tcp流中获取记录

如何从tcp中读取需要的记录呢?

这个点其实核心是如何解决tcp粘包,我们都知道,tcp socket也被称之为数据流,就是因为一旦三次握手之后,数据就像水龙头一样源源不断流动,我们不知道哪儿是头,哪儿是尾巴。

所以需要指定一个结束符,告知程序,读到哪儿,一条记录就读完了,这里我们每条信息都发送一个\r\n来作为结束符。

编写注册发现服务器

如何从tcp流中读取数据

要解决粘包,有2种方法,第一种是定义特殊字段分隔符,例如: \r\n当读到\r\n的时候,就判断一条消息结束了。

例如,如图:

基于Python编写一个简单的服务注册发现服务器

我们发送了一条记录,hello world如何判断结束了呢? 在最后面加一个\r\n,当程序读到的时候,就判断这条消息接收完了,就可以了。

还有一种方法是,提前计算发送的字节数,然后记录到行首,每次读取的时候,先读取行首的个数,而后更具个数再读取数据,例如:

基于Python编写一个简单的服务注册发现服务器

我们提前计算hello world字符串占用多少个字节,而后再在行首分n个字节来存储个数,例如我们使用2个字节来存储,存储数据为10。

我们这篇服务发现使用的是第一种方法。

那要如何判断\r\n呢?可以参考如下代码:

data = b""
while True:
   data += client.recv(1)
   if data.endswith(b"\r\n"):
       break
data = data[:len(data)-2]

上述代码中,我们先定义了一个空的byte,变量名为data,而后每次读取1个字节,再判断是否以\r\n结尾,如果是以\r\n结尾的话,就跳出循环,跳出循环后,我们需要删除掉\r\n所以会有data = data[:len(data)-2]这个语句,这样的话,我们就成功拿到了记录。

数据包如何区分类型

上个段落,我们已经拿到了一条数据包,那么我们这个服务有3种操作,分别是 查询、注册、销毁,如何客户端发上来的请求呢? 其实如之前所述,我们可以通过type来定义,操作流程为:

  • 解析客户端发上来的json信息。

  • 判断其type类型。

整合为语句的话,可以理解为这样:

基于Python编写一个简单的服务注册发现服务器

上述代码,我们先使用json.loads序列化为json格式,而后根据type信息区分是注册服务、销毁服务还是其查询服务。这里注意的是,若json字符串都没有type或者key类型,则证明该数据包有问题,直接拒绝掉即可。如果不是上述三种服务类型的话,那也证明包有问题,直接拒绝即可。

服务注册

服务注册,需要获取客户端传上来的keyvalues信息,若没有的话,则证明该包有问题,而后判断其timeout是否携带,若没有携带的话,则定义为默认值30s,而后将该值入早已定义好的字典类型中即可,代码如下:

基于Python编写一个简单的服务注册发现服务器

这里,需要注意的是,我们再更新服务信息的时候,会将lastUpdateTime更新到目前的时间戳,方便后续查询服务的时候做惰性删除。

服务销毁

服务销毁,核心点是删除字典中的记录即可,为了避免抛错,还是要检查客户端传上来的json字符串是否携带key,虽然在前面检查过,这个再检查一下,还是有必要的。

销毁服务,就直接删除字典的该记录就可以,如果传入的值,在该字典中没有记录的话,默认为销毁完毕。

代码如下:

基于Python编写一个简单的服务注册发现服务器

服务查询

服务查询,客户端发上来的还是以key为主,若key的值为all的话,则查询所有服务,若不为all的话,则查询单个服务注册的信息,这里在查询的时候,是将服务超时销毁一起做了的,采用的是惰性删除,当查询到该值的是,它会比对超时时间,若已经超时了,则直接删除该对象,代码如下:

基于Python编写一个简单的服务注册发现服务器

上述查询到key后,会做一次超时检测,若超时了,则删除该记录的值,若没有超出时间,再返回信息。

来源:https://juejin.cn/post/7224147971833823287

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com