这段时间一直在写一个小模块,功能上来说,就是根据客服和用户之间的对话记录进行在线记录(原谅我还不敢用在线学习这么专业的术语,因为目前确实没有用专业的学习算法),为用户的新问题自动推荐答案。目前在线调测的差不多了,不知道上线试用会是怎么样。借此休息的机会,想对项目做个总结,由于还是自己单干,也希望有经验的伙伴能不吝赐教。
1. 权限最小化选择
其实权限最小化对各方都有积极的意义:对于整个系统来说,权限最小化可以降低系统出错的风险,而对于个人来说,最小的权限也就意味着可以担当最低的责任,目前这个生产的系统已经连续运行了600多天了,很不容易啊。由于个人的检索模块用的是Lucy,并且依赖于Clownfish,而这两个库都比较小众,各大发行版基本都没收录,所以就只能下载代码本地编译,然后将头文件和链接/运行库放到项目本地,修改Makefile编译参数
-I./include/ApacheLucy -I/home/taozj/root/share/clownfish -L/home/taozj/root/lib/ -llucy -lcfish
这样,我的服务如果不是要绑定到1024一下的特权端口,这个服务完全就可以普通用户运行了。
2. 积累代码,统一风格
开发的过程是要积累的,这不但包含只是经验的积累,还有自己编写调试好的模块也是。自己之前使用的代码都放到 st_utils 里面了,这次链表、红黑树、数据库连接池等模块都直接拿来使用了。当然,可能觉得项目集成更好的方式是git submodule,但是这不算是个功能特定的模块,所以此处还是不太合适。
而外就是个人代码的风格需要一致、统一。就像C函数,有些返回非0表示成功,返回0表示失败;有些返回0表示成功,返回-1表示失败,如果自己的风格不统一的话,很可能写写之后自己都把自己搞晕了。所以模仿Libmicrohttpd的方式,用自己定义的放回类型
typedef enum RET_TYPE {
RET_YES = 1,
RET_NO = 0,
} RET_T;
这样,自己约定RET_YES正常返回,RET_NO异常返回;而当返回值不表示函数调用的成败,而是具体的计算结果的时候,再用int类型来作为返回类型。
3. 多看代码,善于借鉴
现在深深地觉得作为程序员,读代码是十分重要的。因为很多需求和实现要么是自己凭空臆想出来的,要么是网上搜索出来的,而实际上,很多论坛以及博客的技术方案和实现代码,要么只是浅显涉及到教你怎么用,要么就是实现的低质粗糙,有些作者自己还是一知半解的,甚至还会将你误入歧途。这种情况,如果身边没有大牛给你当百科全书查阅,就自己去看声望较高的开源软件的代码吧,因为那些代码是实实在在运行的,经受大家监督并且久经考验了的。
比如自己的这个小模块中:(1)数据库连接池设计中,连接的请求操作借鉴了Memcached采用pipefd的阻塞读取,来实现阻塞请求返回;(2)在内存中记录对话内容,由于对话长度是未知的,直接设计固定长度的数组空间利用率会很低,采用strdup保存指针的方式(目前是这样的)容易内存碎片和泄露,那么我觉得Libevent的evbuffer及其evbuffer_chain的设计将是一个很好的范本;(3)这里用的Libmicrohttpd,其本身集成了线程池模型,如果后续用Libmicrohttpd重构的话,线程池可以借鉴Memcached的。
4. 模块化,接口化设计
这个是构架师的工作内容了,我是控制不了的。但是这次使用看来,用HTTP协议+JSON数据封装的通信,比传统的Socket开发要方便不少,因为HTTP作为应用层的协议,这样就不用考虑传输细节和数据封装,只需要对取得的有效数据进行处理就可以了。不过另一方面说,对于模块和模块之间的通信,而不是开放出去的API,直接用长连接的方式通信可能吞吐量会更大,所以也还需要具体情况具体分析。视野越宽,选的路才会更好。不排除会有更高效、更便捷的通信协议适用于相对应的场景。
5. 善用工具,积累经验
5.1 工具类
- SlickEdit
如果你不属于Vim+gdb这类的Geeker,想找一个省心的C/C++ IDE工具,那么强烈推荐SlickEdit。其几乎支持所有的平台,支持在线调试跟踪,如果gdb不是很熟练的话,用这家伙绝对是最省心的; - Makefile
Linux的开发者几乎逃脱不了这个东西,上面的SlickEdit说是支持自动Makefile的,当然也有很多工具辅助生成Makefile。目前我开发时候都是手写Makefile,一方面项目比较小,自己添加调教参数方便,二来不用目录下生成很多乱七八糟的文件,当然如果你想自己的软件能发布,自动适应目标环境,可以参见 利用autotools自动生成项目的Makefile 。其实Makefile不算是很复杂,如果不了解的话建议看看Coolshell陈皓的 跟我一起写Makefile ,入门很实用。而且一旦自己有了一个Makefile模板,后面新的工程基本修改几个参数就可以直接套用了。 - Advanced REST client/ curl
HTTP协议的调试,用这两个工具可以快速的构造请求,显示服务端返回信息。Advanced REST client是Chrome的一个程序,跨平台的,而且图形化的操作更加好使。 - Python
最后写个Python,是当自己需要批量导入已有数据的时候,Python的urllib和json库可以方便的进行批操作。 - valgrind
算是个神器,建议写完程序上线之前先用这家伙检查一下,可以检测使用未初始化变量、内存泄露等各种不服,如果想服务程序长期稳定运行,不得不考虑这些因素的。
5.2 经验教训
- MySQL字符串MD5+索引
数据库建立索引,其内部可以通过BTREE/HASH的方式,大大加快数据库的检索速度。参见之前的数据库设计,字符串模式直接采用了TEXT的数据类型,而虽然TEXT可以建立索引,但是必须指定前缀长度。这里采用了一个简单的方式,就是添加一个TEXT对应的MD5值字段,然后将TEXT的MD5值放到这个字段中,然后对这个字段建立索引。虽然,相关的文献建议,不要使用MD5/SHA1这类函数作为哈希函数,因为这些是设计的强加密函数,目的是最大程度的消除冲突性,使用之会带来存储和性能的损耗,但是这样使用就可以支持查询MD5不用考虑冲突情况了。
建议的方式:使用CRC32,或者自设计合理的HASH函数,查询的时候将MD5和原本TEXT域都加入到查询条件中即可。关于MySQL数据类型和索引,将会在后面的文章中做一次学习和整理。
注:事后发现,我的字符串字段还是使用VARCHAR(M)要比TEXT合适一些。 - 项目参数配置文件化
通过命令行参数+getopt也可以为程序设置参数,但是一旦参数多了,或者参数值比较复杂,用起来就会觉得不方便,此时还是用配置文件的方式比较稳妥,而且参数固化到配置文件后也方便部署。还不清楚开源软件的配置有没有什么解析库,但是既然这里用了json-c库,就把配置文件直接设计成json格式的了。 - MySQL的FULLTEXT全文索引
CHAR/VARCHAR/TEXT可以建立FULLTEXT索引,可以用关键字做搜索引擎式的检索,能返回匹配纪录以及score,难道我又画蛇添足了???不过InnoDB在5.6版本才引入此特性,而且对中文这类需要分词的语言支持不行,基本都查不到~~~国人,请自强! - 程序运行
程序本地控制台运行没有问题,也没啥内存泄漏,到服务器上面后台运行后,大概几个小时就挂掉了,后面用strace跟踪后,发现是“killed by SIGHUP”,这时候想到ss客户端运行的时候用nohup运行的,果真加了这个前缀后,程序跑的HI HI的。
程序运行有时候需要动态传递给程序一些信息,这时候想到的办法是:寻找一个自定义信号(比如SIGUSR1)及其相应函数,然后把要传递的信息写入固定的文件中,再向运行的程序手动发送该信号,从信号的响应函数中加载这些信息。
当然,说不上有啥干货,很多大神可能觉得还很幼稚。只是自己写出项目过程中的心得感受,望各位大神指正补充。
本文完!
原文 http://taozj.org/2016/05/答案推荐模块项目小结/