基于Python编写一个简单的服务注册发现服务器
作者:真的不能告诉你我的名字 发布时间:2022-06-11 20:23:31
我们都知道有很多的非常著名的注册服务器,例如: Consul
、ZooKeeper
、etcd
,甚至借助于redis
完成服务注册发现。但是本篇文章我们将使用python socket
写一个非常简单的服务注册发现服务器。
本篇文章所依赖的环境为:
案例展示
项目地址:
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
代表注册服务,key
和values
是注册服务的名称和注册服务的值,最后的timeout
是存活时间,单位是秒,若超过这个时间没有更新服务,则自动销毁该服务。
如果客户端发送的信息没有携带time
,则注册存活时间为30s
。
查询服务:
{"type":"query","key":"redis"}
{"type":"query","key":"all"}
查询服务使用的type
是query
,查询的key
若是all
则是查询全部,其他则是查询值为key
的服务信息。
销毁服务:
{"type":"destruction","key":"redis"}
销毁服务的type
是destruction
,key
为需要销毁服务的名称。
目前还暂不支持登录验证。
案例测试
由于该注册服务报文采用的是使用CRLF
进行分割,所以可以直接使用telnet
进行模拟注册,为了方便,我们还是使用python
来写这个测试,代码如下:
上述代码,我们需要自己指定\r\n
进行分割,我们指定了4个注册,以及等待5s
再进行查询,最后再销毁了一个服务。
运行结果如下:
可以看到上述结果,第一个输出是我们注册的消息,而后我们等待了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
中,以key
和value
来确定服务名称和服务值,还有一个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
的时候,就判断一条消息结束了。
例如,如图:
我们发送了一条记录,hello world
如何判断结束了呢? 在最后面加一个\r\n
,当程序读到的时候,就判断这条消息接收完了,就可以了。
还有一种方法是,提前计算发送的字节数,然后记录到行首,每次读取的时候,先读取行首的个数,而后更具个数再读取数据,例如:
我们提前计算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
类型。
整合为语句的话,可以理解为这样:
上述代码,我们先使用json.loads
序列化为json
格式,而后根据type
信息区分是注册服务、销毁服务还是其查询服务。这里注意的是,若json
字符串都没有type
或者key
类型,则证明该数据包有问题,直接拒绝掉即可。如果不是上述三种服务类型的话,那也证明包有问题,直接拒绝即可。
服务注册
服务注册,需要获取客户端传上来的key
和values
信息,若没有的话,则证明该包有问题,而后判断其timeout
是否携带,若没有携带的话,则定义为默认值30s
,而后将该值入早已定义好的字典类型中即可,代码如下:
这里,需要注意的是,我们再更新服务信息的时候,会将lastUpdateTime
更新到目前的时间戳,方便后续查询服务的时候做惰性删除。
服务销毁
服务销毁,核心点是删除字典中的记录即可,为了避免抛错,还是要检查客户端传上来的json
字符串是否携带key
,虽然在前面检查过,这个再检查一下,还是有必要的。
销毁服务,就直接删除字典的该记录就可以,如果传入的值,在该字典中没有记录的话,默认为销毁完毕。
代码如下:
服务查询
服务查询,客户端发上来的还是以key
为主,若key
的值为all
的话,则查询所有服务,若不为all
的话,则查询单个服务注册的信息,这里在查询的时候,是将服务超时销毁一起做了的,采用的是惰性删除,当查询到该值的是,它会比对超时时间,若已经超时了,则直接删除该对象,代码如下:
上述查询到key
后,会做一次超时检测,若超时了,则删除该记录的值,若没有超出时间,再返回信息。
来源:https://juejin.cn/post/7224147971833823287
猜你喜欢
- 纯粹的截取字符串其实比较简单,用一个Left就搞定,但一个是全英文标题,一个是全中文标题,或中文混合排在一起,长短不一就很明显了,要考虑到中
- uniapp页面跳转的几种方式一、uni.navigateTo定义:保留当前页面,跳转到应用内的某个页面,使用uni.navigateBac
- 新云4.0模版标签是全新改的了,加了前缀。如果你怀旧,请查看新云CMS3.1常用模板标签。下面的标签说明,后台就有,为了方便查看转到这里。{
- 本文实例讲述了php验证session无效的解决方法。分享给大家供大家参考。具体方法如下:一、问题今天在配置 apache+php环境时折腾
- 运维平台导入数据这一功能实在是太重要了,我敢说在没有建自己的cmdb平台前,大多数公司管理服务器信息肯定是表格,用表格最麻烦的就是有点更新就
- 这个目前还是有个别无法显示,翻了下msdn貌似没看到更好的解决方案,暂时放弃继续研究,有晓得完全解决的朋友不妨回复说一声。 先附bat创建畸
- 本文实例讲述了PHP编程文件处理类SplFileObject和SplFileInfo用法。分享给大家供大家参考,具体如下:php对于大文件的
- 本文实例讲述了从ThinkPHP3.2.3过渡到ThinkPHP5.0学习笔记。分享给大家供大家参考,具体如下:用tp3.2.3做了不少项目
- 对于更完整的代码可以参考,这个是支持数据库的版本。经过测试Asp+Ajax仿google搜索提示效果 数据库版google搜索提示.rar
- 本文实例讲述了PHP闭包定义与使用。分享给大家供大家参考,具体如下:<?phpfunction getClosure($i){ &nb
- 一日,遇到一个问题,求上一个月的今天。 最开始我们使用 strtotime(”-1 month”) 函数求值,发现有一个问题,月长度不一样的
- 1、yield,将函数变为 generator (生成器)例如:斐波那契数列def fib(num): a, b, c = 1,
- 不同数据库之间若不能导入导出,那么将是一件可怕的事情,所幸的是一般情况下通过不同的方法和途径,都可以实现,方法有多种,本人提供其中的一个,提
- 本文实例讲述了Go语言通过Luhn算法验证信用卡卡号是否有效的方法。分享给大家供大家参考。具体实现方法如下:package mainimpo
- Hihi, 大家好~ 最近有不少人都提及了网页上该如何选择字体的问题。问题虽然小,但是却是前端开发中的基本,因为目前的网页,还是以文字信息
- 安装依赖1)下载安装opencv-2.4.9,并将cv2.pyd拷贝到python安装目录的site-package下2)pip insta
- 测试方法首先使用implode, serialize, json_encode, msgpack_pack创建四个文本文件,用于测试。创建代
- MySQL安装文件已被广泛应用但是也在不断的更新,这里介绍MySQL安装文件设置使用,帮助大家安装更新MySQL安装文件系统。Fedora5
- 如果使用注释的方法得当的话,为你的CSS文件添加注释可以在开发过程中给予你和其他人很大的帮助。最常见的是为CSS样式规则添加提示信息,不过使
- 通过 CSS transform (firefox文档, safari文档)属性. 无所不能的css也开始玩起3D效果了。在配合 CSS t