深入浅出jackrabbit之十四 分布式文档提取

/**
*author:ahuaxuan
*2009-09-24
*/

前言:
本来针对jackrabbit这一系列的文章其实都是有顺序的,比如先讲索引的创建,然后讲索引的查询,等等,但是无奈总是有些横生的枝节,这些横生的枝节又让ahuaxuan有了一些新的想法。所以只能将这篇文章写到后面来了。

切入正题,今天这篇文章其实是对前面文本提取的一个补充。前面讲到文本提取的逻辑,逻辑有点小复杂,但是复杂的逻辑并不是这篇文章的关键,今天的话题是进程模型和线程模型,也许童鞋们会感到非常的奇怪,一个jackrabbit居然扯到了线程模型进程模型,还真能扯啊,但是你别说,ahuaxuan确实有这样的想法,并将会实施该想法。

按照ahuaxuan写文章的习惯,一般都是先描述问题,然后再描述解决方案以及方案中涉及的知识点,此次亦不例外。

问题:
我们知道,提取文本的目的就是为了将各种文档中(pdf, xls, word, ppt等等等等)中的文本数据抽取出来,然后再将其索引到lucene文件中去。而且我们还知道,jackrabbit中提取文件可以选择异步的方式,也就是基于生产消费模型的后台deamon线程的处理方式。但是这样做其实有比较大的隐患。

这不是危言耸听,因为jackrabbit要处理的文档是由用户上传上来的,基于以往的经验,经常有各种各样的文档在解析的时候会导致outofmemory之类的错误,带来的后果就是jackrabbit挂掉,那么就停止服务了,也许你会讲,不要紧,我们有集群。等等,难道我们不能阻止这种事情发生吗,经常性的crash甚至会降低运维对我们的印象。

我们设置还遇到这种错误,在解析一个pdf的时候,解析线程遇到了io上的问题,导致一直阻塞在那里。由于我们无法保证我们所使用的解析器是没有bug的,那么我们就必须站在使用者的角度去解决这个问题。

最后问题就集中在如何使文档提取这个功能不影响到jackrabbit的主功能。答案是分布式,废话,肯定是分布式,将文本提取的功能挪出去,但是用什么程序来做呢?

要回答这个问题那么就得明白文本提取的需求特性。
1. 文本提取不能影响到程序的主体功能
2. 文本提取如果有一个任务出现问题不能影响到其他任务的执行
3. 如果有一个任务出现问题,能够将该任务占用的资源释放。

显然第一条的解决方案是分布式,关键是第二,三条。
2. 文本提取如果有一个任务出现问题不能影响到其他任务的执行
3. 如果有一个任务出现问题,能够将该任务占用的资源释放。
试想,如果我们用线程模型,
第一, 那么当一个java线程执行的时候是否会影响到其他java线程的执行呢,貌似不会,但是如果这个java线程在提取一个问题文本时outofmemory,那么这个时候其他线程能正常工作吗?
第二, 退一步说,这个线程由于io之类的阻塞住了(我们设置碰到过解析某个文档出现死循环),那么我们能让这个线程释放它占用的资源吗,显然这类阻塞无法中断,而且我们也不能随意kill掉一个java线程。
由此看来java的线程模型用在我们这个场景下并不合适。

线程模型不行,那么我们再来看看进程模型,
还是第二点,一个任务出现问题会不会影响其他任务呢,由于我们为每个提取任务单独分配一个进程,所以即使这个进程出现了问题,也不会影响到其他进程,而且我们可以写shell脚本监控这些python进程,一旦他们的内存消耗超过200m或者某个阈值,我们可以直接kill等等,这样也不会因为某个问题文档导致系统出现严重问题。

第三点,出问题的任务能否被终结以回收系统资源呢,这个也容易,直接kill进程就行了。

从这两个基本需求来看,显然进程模型比线程模型更适合这个场景。


在确定方向之后,那么接下来需要做的事情就是解决方案。一般来说,分布式调用我会首选http,那么在这里,我也首先考虑http调用,http+多进程,以我的认知来看,lighttpd+fastcgi+python是比较合适我们的需求的。不了解的童鞋可以看看这篇文章:http://ahuaxuan.iteye.com/blog/267429

这样我们就可以使用进程模型来工作,那么下一步要考虑的是python如何提取常见文档。Python调用c是非常方便的,而且python的sdk中有很多这样的用法,而且c或者c++解析文档的速度更高,一般来说内存消耗也少点,那么我们可以考虑使用c或者c++的文档解析组件。网上一搜一大把,不一一罗列了。

而lighttpd+fastcgi+python的方案网上到处都有,比如lighttpd+fastcgi+django(然后让django提供http接口,供jackrabbit调用)等等,到处都是,也不一一罗列。

值得一提的是,文档提取显然是cpu密集型应用,而非io密集型,文件传输问题我们可以通过nfs等方案解决。

更进步考虑,这个方案的负载均衡和容错,其实也很简单,因为lighttpd可以不部署在执行提取的机器上,这样一来lighttpd就只负责路由请求。就像这样一样


fastcgi.server = (
"/mysite.fcgi" => (
"main" => (
"host" => "127.0.0.1",
"port" => 3033,
"check-local" => "disable",
)
),
)
Lighttpd可以充当负载均衡的角色,因为刚才讲到该应用是cpu密集型,网络请求也不是很多,所以lighttpd的压力并不是很高。
容错的问题可以使用两套这样的服务,进行动态的切换。通过心跳发现某个服务没戏了,那么就切换到另外的服务接口上去。

通过这样一整套方案,比如说之前讲到的key-value DBMS, 和这里讲到的进程模型处理文本提取,这样jackrabbit的在搜索这块的可扩展性才能叫有所提高。至少我认为将来的jackrabbit实现应该预留类似的接口,供开发者选择。

总结,进程模型有时候更合适我们的应用,这取决于应用的场景。
上一篇:后摩尔定律时代预测科技越来越难 但计算设备还会更强大


下一篇:安卓学习笔记-RecyclerView使用Glide加载网络图片瀑布流失效的情况以及item间距设置