使用Redis协议构建网络服务

2017-12-15 19:29:49来源:CSDN作者:Loydia人点击

分享

引言

关于服务,这是一个比较抽象的概念,意在为使用者做事,达到使用者的某些需求。当然我们在此讨论的是网络服务,通常我们可以将其定义为一个运行在操作系统上的一个程序,使用者通过网络与其进行交互并能得到想要的信息。

协议

在编写网络服务程序中,其中最重要的一个环节是约定好相互通信的内容格式,也就是我们常说的网络通讯协议。协议设计的好坏很大程度上会影响系统的灵活性、可拓展性、维护性等等。关于良好的协议设计,笔者认为应该具备如下几点:1、易于实现。这里表示容易实现,但这个容易不能按照常规的意义去理解,此处容易笔者定义为简单不马虎而不失优雅。2、能够被计算机高效的 Parse。这个不用解释。3、能容易被人类读懂。这个比较关键,易于理解很重要。4、稳定性。当某些地方需要扩展内容时不应该变动已有的格式,要做到稳定处该稳定,可变处要能适应这种可变性而不影响现有的通讯。

Redis协议

Redis客户端和服务端采用TCP连接来进行数据交互,采用请求-应答的通讯模式。客户端采用发送命令及命令参数的方式,服务端处理后再把结果返回给客户端。这种有点类似我们编程中的函数调用,我们可以把Redis服务端想成一个函数,客户端想成一个调用者,这样就好理解了。例如Redis客户端发起一个GET/SET命令调用:
127.0.0.1:6379> get name"root"
请求包格式:*2/r/n$3/r/nGET/r/n$4/r/nname/r/n答复包格式:$4/r/nroot/r/n
127.0.0.1:6379> set name AdministratorOK
请求包格式:*3/r/n$3/r/nSET/r/n$4/r/nname/r/n$13/r/nAdministrator/r/n答复包格式: +OK/r/n从上可以看出Redis协议从设计上是相当简单的,而且非常易读,对于计算机来说也是很好解析,如果某个命令参数有增加,丝毫不影响整体的格式和解析。如果你对协议有兴趣,可以查找下相关资料进行研究。

编程实现

编写程序解析这种协议时,个人比较提倡的一种做法是解析成一个个Token。Token可以设计成如下结构(采用C/C++方式):
struct Token{    char* str;    int len;};
解析时Token中的指针是不分配内存的,只是指向一个RequestBuffer中的一个位置,len表示长度。例如对于上面的命令,可以解析成如下数据结构:get name --> token[0]=get token[1]=nameset name Administrator --> token[0]=set token[1]=name token[2]=Administrator这样当命令参数有变化时,Token个数会随之变化,但不会引起解析错误。程序内部应该建立一个命令映射表,这样便于维护和扩展。例如对于GET/SET命令,对应处理代码如下:
void onGetCommandHandler(...){    //keyname = token[1];    //dosomething}void onSetCommandHandler(...){    //....    //keyname = token[1]; keyvalue=token[2];    //dosomething}

应用场景

目前笔者在开发Oracle中间件,软件是一个代理程序。功能是Oracle客户端通过连接中间件来访问数据库,这对应用是完全透明的。中间件在此会进行协议解析和转发工作并完成一些功能,例如审计和拦截。由此可以看出软件的一些特点:1、中间件需要占用一个端口接受客户端的连接请求,并完成相关协议解析和收发工作。2、中间件和客户端通信使用的是Oracle 网络通讯协议(TNS),中间件需要按照约定格式解析内容。不符合协议格式的需要关闭连接。3、中间件占用的端口除了处理Oracle客户端请求外,不能接收其他客户端的请求,也就是说这个端口只能做这件事情。
做到后面会发现存在如下需求:1、需要外部去查询程序的状态。例如我要查询当前有多少客户端连入、执行了哪些SQL等2、程序需要动态改变一些配置项。例如程序内部的一些参数,开关量目前看来,这两点是没法做到的。只有程序内部知道自己的状态,外部是没法感知和操控的
思考方案1:用Oracle客户端发送特定SQL来操作程序。但存在需要解析SQL,区分SQL的过程,而且需要返回结果时,需要自己构造答复包,这个具备很大的复杂性。放弃。
思考方案2:用其他客户端协议完成,只要程序再开个端口并解析协议即可。经过思考,采用Redis协议是比较合理的解决方案。支持Redis协议的客户端的实现有很多,例如:C、C++、C#、php、java、python、go等语言。这样外部只需要用这些接口去访问程序就可以了。包括在WEB上展示一些信息也是相当的容易。例如可以解决如下问题:
1、WEB上要获取程序有哪些客户端接入方便展示方案:实现一个clientstats命令,客户端调用即可2、WEB上需要获取某个IP执行了哪些SQL方便查询方案:实现一个sqlstats [IP] 命令,客户端调用传入IP即可3、改变程序的行为,例如改变日志输出级别方案:实现一个setloglvl [level] 命令,外部调用即可4、关闭/重启程序方案:实现shutdown/restart 命令即可..........可以做的事情太多了

可以看出在此场景下,工作端口和Redis端口完全解耦,增加了Redis协议的支持将会给系统带来很大的灵活性和扩展性。总之一句话:Redis协议既简单又强大,只要合理运用可以做很多事情。

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台