对系统未准备好,而拒绝服务的一个想法

废话暂且不提,直接开始:

应用场景:

       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, 大家来找茬。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值