最近在学习了下Google的Guava包,发现这真是一个好东西啊。。由于平时也会写一些基于多线程的东西,所以特意了解了下这个Service框架。这里Guava包里的Service接口用于封装一个服务对象的运行状态、包括start和stop等方法。例如web服务器,RPC服务器、计时器等可以实现这个接口。对此类服务的状态管理并不轻松、需要对服务的开启/关闭进行妥善管理、特别是在多线程环境下尤为复杂。Guava包提供了一些基础类帮助你管理复杂的状态转换逻辑和同步细节。

概述

一个服务正常生命周期有:

  • Service.State.NEW
  • Service.State.STARTING
  • Service.State.RUNNING
  • Service.State.STOPPING
  • Service.State.TERMINATED

    服务一旦被停止就无法再重新启动了。如果服务在starting、running、stopping状态出现问题、会进入Service.State.FAILED.状态。调用 startAsync()方法可以异步开启一个服务,同时返回this对象形成方法调用链。注意:只有在当前服务的状态是NEW时才能调用startAsync()方法,因此最好在应用中有一个统一的地方初始化相关服务。停止一个服务也是类似的、使用异步方法stopAsync() 。但是不像startAsync(),多次调用这个方法是安全的。这是为了方便处理关闭服务时候的锁竞争问题。

Service也提供了一些方法用于等待服务状态转换的完成:通过 addListener()方法异步添加监听器。此方法允许你添加一个 Service.Listener 、它会在每次服务状态转换的时候被调用。注意:最好在服务启动之前添加Listener(这时的状态是NEW)、否则之前已发生的状态转换事件是无法在新添加的Listener上被重新触发的。

同步使用awaitRunning()。这个方法不能被打断、不强制捕获异常、一旦服务启动就会返回。如果服务没有成功启动,会抛出IllegalStateException异常。同样的, awaitTerminated() 方法会等待服务达到终止状态(TERMINATED 或者 FAILED)。两个方法都有重载方法允许传入超时时间。

Service 接口本身实现起来会比较复杂、且容易碰到一些捉摸不透的问题。因此我们不推荐直接实现这个接口。而是请继承Guava包里已经封装好的基础抽象类。每个基础类支持一种特定的线程模型。

Service接口的实现

AbstractIdleService

AbstractIdleService在我们服务处于running状态时,不会做执行任何动作,我们仅仅只有在startup和shutdown的时候才执行一些动作,所以我们在实现这个方法时,只是简单的实现startUp() 和 shutDown() 这两个方法即可,在startUp方法中做一些比如初始化,注册等操作,在shutDown中做一些清理操作等。举个例子,也就是官网的例子:

  1. protected void startUp() {
  2. servlets.add(new GcStatsServlet());
  3. }
  4. protected void shutDown() {}

我们在startUp()方法的时候,实例化了一个GcStatsServlet,当我们在运行的时候,会有现成的线程处理这个Servlet,所以在服务运行时就不需要做什么额外动作了。这个比较简单,就不举例子了,应该用的情况应该不会很多吧?。。。。

AbstractExecutionThreadService

AbstractExecutionThreadService在单个线程中执行startup, running, and shutdown,我们必须实现run()方法,同事在方法中要能响应停止服务的请求,比如在一个循环中:

  1. import com.google.common.util.concurrent.AbstractExecutionThreadService;
  2. import com.google.common.util.concurrent.MoreExecutors;
  3. import com.google.common.util.concurrent.Uninterruptibles;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * User: hupeng
  7. * Date: 14-12-22
  8. * Time: 下午10:17
  9. */
  10. public class AbstractExecutionThreadServiceTest extends AbstractExecutionThreadService {
  11. private volatile boolean running = true; //声明一个状态
  12. @Override
  13. protected void startUp() {
  14. ////在这里我们可以做一些初始化操作
  15. }
  16. @Override
  17. public void run() {
  18. while (running) {
  19. // do our work
  20. try {
  21. Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
  22. System.out.println("do our work.....");
  23. } catch (Exception e) {
  24. //处理异常,这里如果抛出异常,会使服务状态变为failed同时导致任务终止。
  25. }
  26. }
  27. }
  28. @Override
  29. protected void triggerShutdown() {
  30. running = false; //这里我们改变状态值,run方法中就能够得到响应。=
  31. //可以做一些清理操作,也可以移到shutDown()方法中执行
  32. }
  33. @Override
  34. protected void shutDown() throws Exception {
  35. //可以关闭资源,关闭连接。。。
  36. }
  37. public static void main(String[] args) throws InterruptedException {
  38. AbstractExecutionThreadServiceTest service = new AbstractExecutionThreadServiceTest();
  39. service.addListener(new Listener() {
  40. @Override
  41. public void starting() {
  42. System.out.println("服务开始启动.....");
  43. }
  44. @Override
  45. public void running() {
  46. System.out.println("服务开始运行");;
  47. }
  48. @Override
  49. public void stopping(State from) {
  50. System.out.println("服务关闭中");
  51. }
  52. @Override
  53. public void terminated(State from) {
  54. System.out.println("服务终止");
  55. }
  56. @Override
  57. public void failed(State from, Throwable failure) {
  58. System.out.println("失败,cause:" + failure.getCause());
  59. }
  60. }, MoreExecutors.directExecutor());
  61. service.startAsync().awaitRunning();
  62. System.out.println("服务状态为:" + service.state());
  63. Thread.sleep(10 * 1000);
  64. service.stopAsync().awaitTerminated();
  65. System.out.println("服务状态为:" + service.state());
  66. }
  67. }

triggerShutdown() 方法会在执行方法stopAsync调用,startUp方法会在执行startAsync方法时调用,这个类的实现都是委托给AbstractService这个方法实现的。。具体代码可以自己看一下

AbstractScheduledService

AbstractScheduledService类用于在运行时处理一些周期性的任务。子类可以实现 runOneIteration()方法定义一个周期执行的任务,以及相应的startUp()和shutDown()方法。为了能够描述执行周期,你需要实现scheduler()方法。通常情况下,你可以使用AbstractScheduledService.Scheduler类提供的两种调度器:newFixedRateSchedule(initialDelay, delay, TimeUnit) 和newFixedDelaySchedule(initialDelay, delay, TimeUnit),类似于JDK并发包中ScheduledExecutorService类提供的两种调度方式。如要自定义schedules则可以使用 CustomScheduler类来辅助实现。一个实现类看起来应该是这样的

  1. import com.google.common.util.concurrent.AbstractScheduledService;
  2. import com.google.common.util.concurrent.MoreExecutors;
  3. import java.util.concurrent.TimeUnit;
  4. /**
  5. * User: hupeng
  6. * Date: 14-12-22
  7. * Time: 下午7:43
  8. */
  9. public class AbstractScheduledServiceTest extends AbstractScheduledService {
  10. @Override
  11. protected void startUp() throws Exception {
  12. }
  13. @Override
  14. protected void shutDown() throws Exception {
  15. }
  16. @Override
  17. protected void runOneIteration() throws Exception {
  18. // //处理异常,这里如果抛出异常,会使服务状态变为failed同时导致任务终止
  19. try {
  20. System.out.println("do work....");
  21. } catch (Exception e) {
  22. //处理异常
  23. }
  24. }
  25. @Override
  26. protected Scheduler scheduler() {
  27. return Scheduler.newFixedDelaySchedule(1, 5, TimeUnit.SECONDS);
  28. }
  29. public static void main(String[] args) throws InterruptedException {
  30. AbstractScheduledServiceTest service = new AbstractScheduledServiceTest();
  31. service.addListener(new Listener() {
  32. @Override
  33. public void starting() {
  34. System.out.println("服务开始启动.....");
  35. }
  36. @Override
  37. public void running() {
  38. System.out.println("服务开始运行");
  39. ;
  40. }
  41. @Override
  42. public void stopping(State from) {
  43. System.out.println("服务关闭中");
  44. }
  45. @Override
  46. public void terminated(State from) {
  47. System.out.println("服务终止");
  48. }
  49. @Override
  50. public void failed(State from, Throwable failure) {
  51. System.out.println("失败,cause:" + failure.getCause());
  52. }
  53. }, MoreExecutors.directExecutor());
  54. service.startAsync().awaitRunning();
  55. System.out.println("服务状态为:" + service.state());
  56. Thread.sleep(10 * 1000);
  57. service.stopAsync().awaitTerminated();
  58. System.out.println("服务状态为:" + service.state());
  59. }
  60. }

当然这个Listener的注册只是为了测试观察。AbstractScheduledServic默认使用Executors.newSingleThreadScheduledExecutor来执行的

  1. /**
  2. * Returns the {@link ScheduledExecutorService} that will be used to execute the {@link #startUp},
  3. * {@link #runOneIteration} and {@link #shutDown} methods. If this method is overridden the
  4. * executor will not be {@linkplain ScheduledExecutorService#shutdown shutdown} when this
  5. * service {@linkplain Service.State#TERMINATED terminates} or
  6. * {@linkplain Service.State#TERMINATED fails}. Subclasses may override this method to supply a
  7. * custom {@link ScheduledExecutorService} instance. This method is guaranteed to only be called
  8. * once.
  9. *
  10. * <p>By default this returns a new {@link ScheduledExecutorService} with a single thread thread
  11. * pool that sets the name of the thread to the {@linkplain #serviceName() service name}.
  12. * Also, the pool will be {@linkplain ScheduledExecutorService#shutdown() shut down} when the
  13. * service {@linkplain Service.State#TERMINATED terminates} or
  14. * {@linkplain Service.State#TERMINATED fails}.
  15. */
  16. protected ScheduledExecutorService executor() {
  17. final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
  18. new ThreadFactory() {
  19. @Override public Thread newThread(Runnable runnable) {
  20. return MoreExecutors.newThread(serviceName(), runnable);
  21. }
  22. });
  23. // Add a listener to shutdown the executor after the service is stopped. This ensures that the
  24. // JVM shutdown will not be prevented from exiting after this service has stopped or failed.
  25. // Technically this listener is added after start() was called so it is a little gross, but it
  26. // is called within doStart() so we know that the service cannot terminate or fail concurrently
  27. // with adding this listener so it is impossible to miss an event that we are interested in.
  28. addListener(new Listener() {
  29. @Override public void terminated(State from) {
  30. executor.shutdown();
  31. }
  32. @Override public void failed(State from, Throwable failure) {
  33. executor.shutdown();
  34. }
  35. }, directExecutor());
  36. return executor;
  37. }

你可以参照这个实现override这个方法,得到你想要的ScheduledExecutorService。

AbstractService

如需要自定义的线程管理、可以通过扩展 AbstractService类来实现。一般情况下、使用上面的几个实现类就已经满足需求了,但如果在服务执行过程中有一些特定的线程处理需求、则建议继承AbstractService类。

继承AbstractService方法必须实现两个方法.

doStart(): 首次调用startAsync()时会同时调用doStart(),doStart()内部需要处理所有的初始化工作、如果启动成功则调用notifyStarted()方法;启动失败则调用notifyFailed()

doStop(): 首次调用stopAsync()会同时调用doStop(),doStop()要做的事情就是停止服务,如果停止成功则调用 notifyStopped()方法;停止失败则调用 notifyFailed()方法。

doStart和doStop方法的实现需要考虑下性能,尽可能的低延迟。如果初始化的开销较大,如读文件,打开网络连接,或者其他任何可能引起阻塞的操作,建议移到另外一个单独的线程去处理。

使用ServiceManager

除了对Service接口提供基础的实现类,Guava还提供了 ServiceManager类使得涉及到多个Service集合的操作更加容易。通过实例化ServiceManager类来创建一个Service集合,你可以通过以下方法来管理它们:

startAsync() : 将启动所有被管理的服务。如果当前服务的状态都是NEW的话、那么你只能调用该方法一次、这跟 Service#startAsync()是一样的。

stopAsync() :将停止所有被管理的服务。

addListener :会添加一个ServiceManager.Listener,在服务状态转换中会调用该Listener

awaitHealthy() :会等待所有的服务达到Running状态

awaitStopped():会等待所有服务达到终止状态

检测类的方法有:

isHealthy() :如果所有的服务处于Running状态、会返回True

servicesByState():以状态为索引返回当前所有服务的快照

startupTimes() :返回一个Map对象,记录被管理的服务启动的耗时、以毫秒为单位,同时Map默认按启动时间排序。

我们建议整个服务的生命周期都能通过ServiceManager来管理,不过即使状态转换是通过其他机制触发的、也不影响ServiceManager方法的正确执行。例如:当一个服务不是通过startAsync()、而是其他机制启动时,listeners 仍然可以被正常调用、awaitHealthy()也能够正常工作。ServiceManager 唯一强制的要求是当其被创建时所有的服务必须处于New状态。

参考:http://ifeve.com/google-guava-serviceexplained/

Goolge-Guava Concurrent中的Service的更多相关文章

  1. Android中的Service小结

    简介 Service适合执行不需要和用户交互,而且长期运行的任务.即使程序被切换回后台,服务仍然可以正常运行.Service并不自动开启线程,默认运行在主线程中. Service中需要重载的函数 on ...

  2. 在Java filter中调用service层方法

    在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: public class WeiXinFilter implements Filter ...

  3. Android 中的 Service 全面总结

    1.Service的种类   按运行地点分类: 类别 区别  优点 缺点   应用 本地服务(Local) 该服务依附在主进程上,  服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另 ...

  4. [转]servlet中的service, doGet, doPost方法的区别和联系

    原文地址:http://m.blog.csdn.net/blog/ghyg525/22928567 大家都知道在javax.servlet.Servlet接口中只有init, service, des ...

  5. Android 中的 Service 全面总结(转载)

    转载地址:http://www.cnblogs.com/newcj/archive/2011/05/30/2061370.html 感谢作者 Android 中的 Service 全面总结 1.Ser ...

  6. 如何在Java Filter 中注入 Service

    在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: public class WeiXinFilter implements Filter ...

  7. Android 中的 Service 全面总结 (转)

    原文地址:http://www.cnblogs.com/newcj/archive/2011/05/30/2061370.html 1.Service的种类   按运行地点分类: 类别 区别  优点 ...

  8. Cloud Foundry中 JasperReports service集成

    Cloud Foundry作为业界第一个开源的PaaS解决方案,正越来越多的被业界接受和认可.随着PaaS的发展,Cloud Foundry顺应潮流,充分发挥开源项目的特点,到目前为止,已经支持了大批 ...

  9. Neutron中的Service类

    Service是OpenStack中非常重要的一个概念,各个服务的组件都以Service类的方式来进行交互. Neutron中的Service类继承自rpc中的Service,总体的继承关系为 neu ...

随机推荐

  1. 404 Note Found -选题报告

    目录 NABCD分析引用 N(Need,需求): A(Approach,做法): B(Benefit,好处): C(Competitors,竞争): D(Delivery,交付): 初期 中期 个人贡 ...

  2. HDU 1121 Complete the Sequence 差分

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1121 Complete the Sequence Time Limit: 3000/1000 MS ...

  3. HDU 5656 CA Loves GCD 01背包+gcd

    题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5656 bc:http://bestcoder.hdu.edu.cn/contests/con ...

  4. 使用TestNG 和 CSV文件进行数据驱动

    package testNGPractice; import java.io.BufferedReader; import java.io.FileInputStream; import java.i ...

  5. JAVA字节流(读写文件)

    InputStream此抽象类是表示字节输入流的所有类的超类.需要定义 InputStream 的子类的应用程序必须始终提供返回下一个输入字节的方法. int available()返回此输入流方法的 ...

  6. SSH框架配置

    --------------------------------applicationContext.xml-------------------------------- <?xml vers ...

  7. windows查看端口占用指令

    1.Windows平台 在windows命令行窗口下执行: 1.查看所有的端口占用情况 C:\>netstat -ano 协议    本地地址                     外部地址  ...

  8. 框架整合小小总结【SSH】注解式

    Spring 注解式注册 bean: 大致分为以下几步: 开启 context 空间支持 开启自动扫描功能,指定扫描包路径 使用注解配置 bean (使用@Component 注解) 给 bean 注 ...

  9. UVA12546_LCM Pair Sum

    题目的意思是求 [西伽马(p+q)]其中lcm(p,q)=n. 又见数论呀. 其实这个题目很简单,考虑清楚了可以很简单的方法飘过. 我一开始是这样来考虑的. 对于每一个单独的质因子,如果为p,它的次数 ...

  10. week1day01 认识python 变量 数据类型 条件if语句

    1.什么是python? Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年.像Pe ...