基本描述
新闻推荐系统能够支持用户登录,注册,检索新闻,并根据登录用户的浏览记录进行新闻推荐。支持360浏览器,Chrome浏览器,IE浏览器,安卓手机浏览器,苹果手机浏览器的访问。部署在云服务器上,以守护进程的方式运行。
主要分为5个模块:
l 爬虫模块
使用python的Scrapy框架爬取新浪新闻网页存储到MySQL数据库中,爬取的过程中使用结巴分词对标题进行关键词过滤,便于后期的推荐和检索。
l 后台服务器模块
使用EPOLL边缘触发,线程池,多线程异步日志。使用简单的HTML,支持get和post。
l 数据库模块
使用MySQL数据库管理新闻信息,用户身份信息,用户访问记录。
l 搜索模块
使用MySQL5.7新增的全文索引,并且通过c++多线程调用python程序,对检索结果使用BM25算法进行相关度排序。
l 推荐模块
根据用户的访问记录,使用结巴分词的关键词抽取模块对关键词进行排序,得到5个权重最高的关键词,在数据库中检索,实现推荐。
系统框图
遇到的主要问题:
1、 后台服务器的搭建,这是最主要的问题。
目前是采用的方案是主线程接受连接,对数据进行读,读完之后将请求的任务分给线程池中的线程去做,线程池中的线程处理完之后,让主线程去写。这种方案对主线程的负担比较大,不适合处理高并发的请求。改进方案是采用经典的One Thread Per Loop方案,主线程只负责连接的建立,然后将这个连接以RoundRobin的方式分摊给线程池的线程,此后这个线程就全权接管这个连接。
一开始我就使用的性能比较高的EPOLL加边缘触发的方式。但是由于对EPOLL和非阻塞读写的理解没有到位,程序出现了很多bug。印象最深的是我采用条件变量搭建的线程池,主线程接受连接之后让线程池中的线程来处理,但是我只注册了EPOLLIN事件,所以导致在对方断开连接的时候也会触发一个可读事件,当线程池中的线程去对他进行读的时候就会出错。还有我一开始对EAGAIN或者EWOULDBLOCK这个错误没有正确的认识,其实忽略它就可以了。
还有accept必须非阻塞,在ET模式下必须用while循环去接受accept连接。
2、 程序的调试问题
网络编程的程序错误一般为运行期的错误。并且我们的系统有一部分是调用的python程序,程序运行到python代码时如果有错,能给我们的出错信息很少。这给调试带来了很大的麻烦,必须让服务器运行起来,让用户去访问,看看哪里出错了。由于是在云服务器上搭建的http服务器,终端退出之后需要http服务器还能运行,所以需要设置守护进程,并且启用日志系统记录系统的关键信息。总的来说,调试的方式有:
全局变量errno
Assert断言
终端输出
打日志
程序崩溃查看core dump文件
使用gdb调试
3、 c++多线程调用python的过程中出现的GIL极大地影响程序的性能
一开始只能搜索一次,搜索完之后搜索第二次就一直卡住。后来多方求证,终于找到问题的所在,就是在多线程环境下,python程序执行需要有GIL锁,用完之后必须释放,否则会造成死锁的情况。
4、建立全文索引之后极大地影响数据库的插入性能,导致爬虫效率低下。(一开始我还以为是我的ip被网站封了,直到我用慢查询日志抓出每条插入语句都是慢查询。)删掉索引又是不行的,因为我们需要索引来检索,来保证爬虫的时候存入数据库中的新闻是没有重复的。目前准备采用的方案是爬虫程序存储到另一个数据库中,这个数据库是没有索引的,服务器访问的数据库是有索引的,爬虫的时候先检索服务器访问的数据库,如果数据库中没有记录,就存到没有索引的那个数据库中。一段时间之后删除服务器访问的那个数据库上的索引,将另一个数据库中的数据导入之后再建立索引。基本的思路如图: