lucene 数据库
无需修改任何代码即可将现有应用程序或库公开为Web服务的能力是最吸引人的概念。 使用Baratine,这是一个用于构建松耦合微服务平台的开源框架,可以通过两个步骤来完成:
- 然后实施服务部分(SOA)
- 实现一个用于通信的客户端库。
使用这种方法,Baratine可以将现有的库或应用程序转换为独立的Web服务。 Baratine服务将与现有库通信,并且Baratine客户将服务于来自外部世界的请求。
在本文中,我们将探讨一个使用Baratine将Apache Lucene(一种流行的开源Java搜索库)公开为高性能Web服务的示例。
Apache Foundation将Lucene描述为:“一个完全用Java编写的高性能,功能齐全的文本搜索引擎库。 这项技术几乎适用于所有需要全文搜索的应用程序,特别是跨平台。”
我们的示例将作为使现有应用程序或库作为功能微服务在线使用的蓝图。 它将Lucene库转换为具有以下特征的高效搜索微服务:
- 充当异步消息传递服务。
- 公开用于Lucene的公共WebSocket API。
- 部署到独立的受管服务器。
运行示例
如果您想在阅读本文时运行该示例,可以从Github下载该示例并使用以下步骤启动它:
- 安装lucene-plugin :
- 执行./baratine-run.sh
- 打开最新的浏览器并导航到http:// localhost:8085
项目内容和概述
该示例包括用于Lucene的Java服务,以及用于浏览器或node.jsJavaScript客户端。 该项目还使用Angularjs框架连接后端服务。 这意味着仅使用Baratine和前端框架,就可以轻松创建高性能的微服务。 我们要做的是使用Baratine Services发布Lucene HTTP / WebSocket API。
这是通过两个主要任务完成的:
任务1.发布HTTP / WebSocket客户端API。
任务2.将Lucene的同步库桥接到异步Baratine服务。
大多数工作在以下Javascript和Java文件中完成:
服务:
- 公共API服务 :负责将方法请求代理到Lucene库。
- Lucene Reader服务和writer服务 :负责读写搜索索引,在Lucene的同步/阻塞API和Baratine的异步/消息之间建立桥梁。
客户:
- Baratine Lucene客户端 :为我们的示例编码,公开了用于浏览器结果的Lucene客户端方法功能。
- Baratine Javascript客户端:与Baratine一起分发的协议库,该库使用HTTP或WebSockets和Baratine的JAMP协议与服务进行通信。
让我们看看如何实现这两个主要任务:
任务1-常规服务:LuceneFacade
LuceneFacade是已发布的HTTP / WebSocket的API,可以完成第一个任务。 它的实现是LuceneFacadeImpl。 已发布的API是异步的,使用Baratine的Result作为结果的回调持有者。 该实现是单线程且无阻塞的,从而消除了同步需求。
任务2A- 常规服务:LuceneReaderImpl
LuceneReaderImpl实现了搜索查询服务,该服务在Lucene的同步库与Baratine的异步API之间架起了桥梁。 由于Lucene的搜索是多线程且阻塞的,因此LuceneReaderImpl使用Baratine的多员工支持(@Workers)。 多员工支持类似于数据库连接池。 LuceneReader的用户可以看到异步服务,而无需知道它是作为多线程服务实现的。
任务2B- 常规服务:LuceneWriterImpl
LuceneWriterImpl实现索引更新服务。 这是一个单线程服务,可以批量向Lucene写入请求,以提高效率。 随着负载的增加,LuceneWriter变得更加高效,因为写入批处理大小会自动增加,如下所示。
LuceneWriterImpl实现不仅说明将方法调用封装为自己的自包含服务有多么简单,而且还防止对这些方法的调用阻止其他客户端请求。 Lucene不提供异步API,但是由于Baratine服务是异步的,因此我们允许在连续调用这些方法时进行处理。 新架构的高级概述可以如下图所示:
HTTP / WebSocket客户端API
客户端API是服务的Java接口,在这种情况下为LuceneFacade.java接口。 使用URL语法,每个服务都有其自己的地址。 服务上的方法的调用方式类似于普通的进程内方法调用。 Baratine的Javascript库管理细节,为协议提供方法接口,该接口在HTTP或WebSockets上使用JSON方法调用。
Lucene API方法:
void indexFile (字符串集合,字符串路径,Result <Boolean>结果)引发LuceneException;
void indexText (String collection,String id,String text,Result <Boolean>结果)引发LuceneException;
void indexMap (String collection,String id,Map <String,Object>地图,Result <Boolean>结果)引发LuceneException;
无效搜索 (字符串集合,字符串查询,整数限制,Result <List <LuceneEntry >>结果)抛出LuceneException;
void delete (字符串集合,字符串id,Result <Boolean>结果)将引发LuceneException;
void clear (字符串集合,Result <Void>结果)引发LuceneException;
客户可以直接调用这些方法。
我们知道某个服务位于URL,那么我们如何创建一个客户端来与该服务对话?
如何做客户
要创建客户,我们必须执行以下操作:
- 将服务网址传递给Baratine:
client = new Jamp.BaratineClient(url);
- 通过致电ServiceRef查找我们的服务
- 保存从查找返回的代理
- 代理上的调用方法
寿命长的客户端(例如Java应用服务器或Node.js服务)可以共享单个线程安全连接,因为该协议使用消息是异步的。 快速请求(例如搜索内存缓存)可以在较早的缓慢请求之前无序完成,例如,调用MySQL数据库的Lucene搜索。 使用单个连接甚至可以提高效率,因为可以批量处理多个调用,从而提高TCP性能。
JavaScript客户端
对于Lucene示例,API包括搜索文本文档并将新文档添加到搜索引擎的方法。
Baratine将接口实现为类,从而使我们可以直接在我们选择公开的任何接口上调用方法。 调用的结果是一个回调,它将返回搜索结果或通知索引已完成。 由于Baratine是异步的,因此不会阻止对search或indexText的调用。
Baratine支持创建从客户端到呼叫服务的Websocket连接。 由于websocket旨在将本地桌面响应能力引入Web服务,因此这允许通过单个TCP连接进行全双工通信。
LuceneJavaScript客户端遵循Java API。 以下是从lucene.js文件中提取的内容,以显示Baratine调用如何转换为Javascript。
首先,使用服务器的HTTP URL(即“ http:// localhost:8085 / s / lucene”)上的新Jamp.BaratineClient创建新连接。 通常,客户端将是长期存在的,用于许多请求。
搜索
{
this.client.query(“/service”,
“search”, [coll, query, limit], onResult);
}
搜索非常简单,将请求直接代理到后端。
指数
{
this.client.send(“/service”,
“indexText”, [coll, extId, text]);
}
索引文本被实现为一种非阻塞方法。
通过Baratine公开服务:创建一个由Baratine管理的Bean。
Java客户端
也可以从Java客户端(例如使用Lucene的Web应用程序)调用该服务。 与Javascript客户端一样,Java客户端通常是一个长期存在的连接。 由于客户端本身是线程安全的,因此可以从多线程应用程序中有效地使用它。
异步Java客户端
@Inject
@Lookup("public://lucene/service")
LuceneFacade _lucene;
当Java应用程序使用Baratine服务(例如Lucene服务)时,通常将使用对服务API的同步版本的同步阻塞调用。 Baratine客户端代理充当从同步客户端到异步Baratine服务的桥梁。 Lucene门面的同步版本如下所示:
LuceneFacadeSync
public interface LuceneFacadeSync extends LuceneFacade
{
List<LuceneEntry> search (String collection, String query, int limit);
}
客户端调用看起来像普通的Java方法调用,如下面的代码所示。 由于ServiceClient是线程安全的,并且可以用于多个Baratine服务,因此可以将其用作单例。 实际上,由于Baratine的内部批处理和消息传递,使用跨线程共享的单个客户端会更高效。
同步Java客户端
ServiceClient client = ServiceClient.newClient(url).build();
LuceneFacadeSync lucene;
lucene = client.lookup(“remote:///service”).as(LuceneFacadeSync.class);
List<LuceneEntry> result = lucene.search(collection, query, limit);
如您所见,由于Baratine的灵活体系结构允许高效的API协议设计,因此客户端创建非常简单。
Lucene服务器实施
Lucene服务器有两个任务:发布客户端API,并充当Lucene的多线程阻塞实现与Baratine的异步体系结构的桥梁。 该示例使用三个Baratine服务来实现该服务:
- 客户端API门面服务
- 读者服务搜索
- 用于搜索索引更新的Writer服务
对于Lucene服务器,由于读写行为不同,因此读取和写入被分为两个服务,因此对服务进行了定制以有效地支持这些差异。
写操作受益于单个写程序线程,因为它可以将多个写操作批量处理到一个提交中,因此提高了高负载下的效率。
Lucene的读取可以受益于多个读取器线程。 Lucene搜索可能会阻塞数据库查询,从而占用线程。 使用多个线程,一个单独的线程可以处理新的搜索。 请注意,仅由于Lucene可能使用缓慢的阻塞服务,才需要多个线程。 如果Lucene是基于内存的,或者本身是异步的,则由于CPU缓存,单个读取器线程会更高效。
Lucene服务的实现包含在五个主要文件中,我们将对其进行简要描述。
LuceneFacadeImpl.java
客户端API由LuceneFacade Baratine服务实现。 它主要将请求分派给Reader和Writer服务。 在下一篇文章中,当我们使用多种服务对Lucene服务器进行分区时,外观将发挥更大的作用。 对于此示例,将客户端API的重点放在简单性上很有用,以确保服务器能够使客户端更容易工作。
LuceneIndexBean.java
因为Lucene库是作为一个单例实例编写的,所以为读者和作家提供的服务共享同一个LuceneIndexBean。 该设计基于Lucene自己的设计。 我们正在使用Baratine与现有架构配合使用,而不是试图强迫Lucene遵循Baratine。 如果我们从头开始构建Baratine服务,而不是改编现有的库,我们可能会选择其他体系结构。 初始化时,共享的LuceneIndexBean单例将注入到读取器和写入器服务中。
由于我们仍在使用底层的Lucene库,因此我们可以简单地从LuceneFacade接口中实现方法,并为自己的特定索引添加任何辅助方法(即,转义特殊字符,使用其他数据存储,限制提交的大小等)。
LuceneWriterImpl.java
Writer服务从外观获取请求并更新Lucene的索引。 它具有单个工作线程,该线程为收件箱中的所有请求向Lucene写更新。 当其收件箱为空时,它将调用Lucene的commit方法以完成写入。 在高负载下,收件箱将有更多请求,这将导致更大的批次,从而提高性能。
LuceneReaderImpl.java
由于Lucene的搜索在等待慢速数据库时可能会阻塞,并且因为Lucene是多线程的,因此阅读器实现使用@Worker批注为该服务请求多个线程。 通过使用@Workers(20)注释此服务,我们提供了一个线程池执行程序,该执行程序可以连续地将线程分派到读取服务。 由于仅在需要时才分派线程,因此多个工作线程的成本很低。 多名工作人员允许我们的Lucene服务一次处理多个请求。
通常,此多工作人员功能仅应用于具有外部阻塞依赖性的网关服务,例如数据库连接或REST调用。 为Baratine设计的服务应使用单个工作程序,因为它们应被设计为异步,非阻塞服务。
巴拉丁建筑
我们所做的是将Lucene API实施为一组Baratine Services。 在Baratine中,每个服务都使用其自己的唯一URL,并根据单线程,单所有者/单作者合同运行。 (Lucene阅读器中使用的多工桥服务是一个例外;它用于将外部库带入Baratine。)对特定服务URL的请求被排队到该服务的收件箱中 ,以确保处理请求时的顺序。
核心Baratine构建块如下所示:
参照上图,我们现在可以将Baratine服务总结为:
- Baratine服务位于唯一的URL
- Baratine服务具有一个单独的线程,负责处理服务的数据
- 请求被排队到服务的收件箱中并分批处理
在Baratine中,我们无需向通话添加同步。 因为每个服务仅由一个线程回答,所以另一个线程不可能破坏正在更新的数据。 这使我们的类成为POJO对象。
性能和与Apache Solr的比较
正如某些读者可能已经注意到的那样,我们的示例类似于Apache Solr,后者为Lucene库提供了服务器。 Solr是一个很好的比较,因为它是一个熟悉的示例,可以直接进行比较。
我们以具有多个客户端的Apache Solr为基准。 在仅读取的测试中,Baratine更好,或在Solr的性能的20%以内。 在混合读写基准中,Baratine被证明比Solr快3倍。
按照以下规格进行测试:
英特尔(R)酷睿(TM)2 Quad CPU
Q6700 @ 2.66 GHz
内存4 G速度:667 MHz
硬盘:ST3250410AS
Java版本“ 1.8.0_51” OS Linux deb-0 3.16.0-4-amd64#1 SMP Debian 3.16.7-ckt11-1 + deb8u2(2015-07-17)x86_64 GNU / Linux
基准图如下:
结果表明,在并排比较中,Baratine搜索(读取)的性能优于Apache Solr。
读/写请求的混合负载显示竞争性数字。
摘要
在本示例中,我们可以使用任何库或应用程序完成对Lucene的操作。 通过将Baratine服务作为外观包装到库中,我们可以将任何库(例如java.util)转换为异步服务。
React宣言反映了Baratine包括的许多原则。 响应式应用程序是弹性的,响应式的,弹性的和消息驱动的。 这是物联网的需求,其中成千上万的设备连接到同一应用程序。 这些原则是开发人员梦co以求的,但在实践中很难实现。 Baratine独特的POJO级别抽象定义了可解决此难题的数据和线程封装级别。 这样,Baratine是React平台的SOA实现,使开发人员能够以他们习惯的面向对象的方式进行编程。 我们相信,大多数新的Web应用程序将在需要与当前系统集成的同时纳入这些原则。 因此,正如我们在本文中所显示的,Baratine非常适合构建两者。
当您向应用程序添加功能时,Baratine的价值将增加。 例如,如果我们决定对正在执行的查询进行实时分析,则可以在Baratine节点上部署一个简单的POJO类收集统计信息并中继此信息。 它可以实时地从BFS(Baratine文件系统)的最新更新中实时提供此信息,也可以将这些结果预先计算并批处理到外部以供使用。 无论哪种方式,Baratine的统一而灵活的体系结构都允许系统专门针对当前任务进行设计,而不会限制未来的潜力。 由于服务始终修饰着Web应用程序的质量,因此概念证明可以在短短几分钟内从白板API变为部署状态。
Baratine当前处于Beta(0.10)中,并计划于2016年第一季度发布可投入生产的版本。在此示例中,我们仅涉及Baratine支持Lucene微服务的功能。
请继续关注第二部分,我们将在Baratine中处理分片和扩展Lucene微服务!
lucene 数据库