废话暂且不提,直接开始:
应用场景:
Mina + Spring 提供对外服务
需求:
在整个系统没有准备好之前,系统对外拒绝服务,因为本系统涉及到一些初始化索引一些比较耗费时间的问题,所以即使spring启动了,但是数据没有准备好,依然不能提供服务。
刚开始的时候,使用 spring的 init-method,但是经历各种蛋疼以后,发现可行性太低,所以我就想,能不能监听spring的事件来做呢,结果有了如下想法:
public class InitData implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware{
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
InitDataService service = applicationContext.getBean(InitDataService.class);
List<ISearchService> searchServices = service.getSearchServices();
int serviceAmount = searchServices.size();
final ExecutorService exec = Executors.newFixedThreadPool(serviceAmount);
final CountDownLatch downLatch = new CountDownLatch(serviceAmount + 1);
final CyclicBarrier barrier = new CyclicBarrier(serviceAmount, new ServiceReadyThread(downLatch));
for (ISearchService iSearchService : searchServices) {
Thread task = new Thread(new InitLuceneIndexThread(iSearchService, barrier, downLatch));
exec.execute(task);
}
try {
downLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}//等待所有的并发访问完
exec.shutdown();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
这个是整个想法的核心,下面围绕这个核心,我来做一些解释。
首先实现了ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware二个接口,一个是监听ContextRefreshedEvent事件,一个是拿到applicationContext。
InitDataService service = applicationContext.getBean(InitDataService.class);
单独写出来的一个类,因为我的service类有很多,那么如果我把这些类都放到配置文件中,以后只要实现各自的方法和更改配置文件就可以了。
ublic class InitDataService {
private List<ISearchService> searchServices;
public List<ISearchService> getSearchServices() {
return searchServices;
}
public void setSearchServices(List<ISearchService> searchServices) {
this.searchServices = searchServices;
}
}
<bean id="initDataService" class="com.playsnail.search.service.init.InitDataService">
<property name="searchServices">
<list>
<ref bean="各种实现了ISearchService的Service"/>
</list>
</property>
</bean>
int serviceAmount = searchServices.size();
final ExecutorService exec = Executors.newFixedThreadPool(serviceAmount);
final CountDownLatch downLatch = new CountDownLatch(serviceAmount + 1);
final CyclicBarrier barrier = new CyclicBarrier(serviceAmount, new ServiceReadyThread(downLatch));
因为CyclicBarrier和CountDownLatch在定义的时候要指定需要同步的数量。所以有了 int serviceAmount = searchServices.size();
然后建立一个线程池,建立CyclicBarrier和CountDownLatch的实例,
这里需要注意ServiceReadyThread这个参数,这个是我打开整个服务的一个标志位
public class ServiceReadyThread implements Runnable{
private CountDownLatch downLatch;
public ServiceReadyThread(CountDownLatch downLatch){
this.downLatch = downLatch;
}
@Override
public void run() {
LuceneUtil.setIndexReady(true);
downLatch.countDown();
}
}
因为这里也需要downLatch.countDown(); 所以上述的CountDownLatch的参数会 +1.
for (ISearchService iSearchService : searchServices) {
Thread task = new Thread(new InitLuceneIndexThread(iSearchService, barrier, downLatch));
exec.execute(task);
}
这个目的就是把每个service加入到一个单独的线程中执行,那么这些线程的同步就是CyclicBarrier的工作了。
InitLuceneIndexThread的代码public class InitLuceneIndexThread implements Runnable{
private CyclicBarrier barrier;
private ISearchService searchService;
private CountDownLatch downLatch;
public InitLuceneIndexThread(ISearchService searchService, CyclicBarrier barrier, CountDownLatch downLatch){
this.searchService = searchService;
this.barrier = barrier;
this.downLatch = downLatch;
}
@Override
public void run() {
searchService.createIndexIfInValid();
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}finally{
downLatch.countDown(); //放在finally中执行你懂的
}
}
}
try {
downLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}//等待所有的并发访问完
上面的代码的意思就是等待所有的线程执行完,大家看到,这个是downLatch的事情。
执行完以后,没有以后了。。。。。
但是一个问题是我前面说的,这个功能是个服务提供的标识符。那怎么做的呢,
注意到ServiceReadyThread 中的 LuceneUtil.setIndexReady(true);了吗?
接下来看看这个标志符的定义
private static volatile boolean isIndexReady = false; //定义为volatile 你也懂的
get()and set()。。。。。。。
前面提过,我们的服务提供是用Mina做的,那么就在Mina的Handler里面做文章,如果isIndexReady为false
,则不让客户端建立连接。
郑重申明,该想法只是完成了代码部分,没有进行测试,让大家帮我参谋参谋有什么需要改进的地方,毕竟一个人的力量是有限的,我相信群众。Come on, Man, 大家来找茬。
相关推荐
信息系统安全集成工作流程 目 录 1 目的 4 2 适用范围 4 3 安装调试 4 3.1 准备阶段 5 3.1.1 准备工作安排 6 3.1.2 技术支持人员要求 6 3.1.3 险点分析与控制 6 3.1.4 工具及材料准备 7 3.2 施工阶段 7 3.2.1 开工 7...
读懂本文需要了解安装操作系统的一些基础知识。 首先是制作一个能启动电脑的带... 先到网上去下载一个叫“老毛桃WinPE”的工具到硬盘里,再把U盘接在电脑上,然后按下面的步骤一步步来就可以制作一个能启动的U盘了。
花型准备系统3.7T
系统调用在内核中都是必不可少的一部分,ARM 结构对系统调用的支持相比其他 架构有很多改进,其化繁为简,为开发者提供了一个便捷的方法添加一个新的系统 调用。这里涉及 ARM 架构的系统调用表 syscall.tbl, 以及 ...
.net服务准备文档
ArcGIS10.1 地图的发布和之前版本有所改变,文档对10.1发布地图进行了详细的说明,希望可以帮到你。
这是安装服务器操作系统的一个重要步骤。 驱动程序软盘通过一张随机驱动程序光盘(蓝海豚或睿捷导航软件光盘)或另一张RAID卡的驱动程序光盘制作。下面是蓝海豚或睿捷导航软件截图。建议在台式电脑上运行导航软件。...
一个能启动电脑的U盘和一个系统的光盘镜像在安装系统前,需要准备好一些东西。一个 是操作系统的镜像,另一个就是能启动的U盘。下面我们就来讲解怎么利用U盘启动安装 Ghost xp版的XP系统。注:读懂本文需要了解安装...
ERP系统实施准备ERP系统实施准备
雨林木风系统准备工具V3.6 绿色免费版(系统封装工具),这是一款非常不错的系统封装工具,很好用的。
可能是一个包含注册表数据文件的结构已损坏,也可能内存中该文件的系统映像已损坏,或者因为备份副本(或日志)不存在(或损坏)导致无法恢复该文件。 1016 由注册表引起的 I/O 操作发生了不可恢复的错误。...
用Java写的一个基于EJB框架的购物系统。
注意:1、使用ServerGuide光盘安装会清除硬盘上的分区和数据,如果还有要保留的数据请先备份或选择其他安装方式, 2、ServerGuide光盘并不包含操作系统程序,请客户自己准备一张正版Windows操作系统光盘。...
ERP前期工作Checklist:ERP实施,你准备好了吗?
这个人脸识别系统的开发需要先准备好摄像头,使用OpenCV对摄像头返回的图像进行特征提取,然后使用服务器端算法对用户进行判断,最终返回结果给客户端。这个系统实现了基本的人脸识别功能,但需要注意信息安全和隐私...
教你怎么拥有盘装系统,怎么快速德庄系统一个能启动电脑的U盘和一个系统的光盘镜像或文件 (本人目前使用比较稳定的系统GhostXP_SP3简体中文版2010_NTFS.iso) QUFGdHA6Ly8xMTI6MTEyQDYxLjE0NS42Mi45OC9HaG9zdFhQX1...
本次课程设计要求用高级语言编写和调试一个单道批处理系统的作业调度的模拟程序,了解作业调度在操作系统中的作用,以加深对作业调度算法的理解 2. 课程设计的开发语言 C语言 3. 功能描述 在批处理系统中,作业进入...
系统提示,系统的准备,报告系统用声音准备,准备问题的代码的过程 系统提示,系统的准备,报告系统用声音准备,准备问题的代码的过程
本文给大家介绍了DOS系统检修前需要做的准备工作。
创建一个新的Android产品项目 4 制作ubifs文件系统 7 android编译系统makefile(Android.mk)写法 10 Android系统移植(一)-让android系统在目标平台上运行起来 18 Android系统移植(二)-按键移植 20 Android系统移植(三...