最近在学习了下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中做一些清理操作等。举个例子,也就是官网的例子:

protected void startUp() {
servlets.add(new GcStatsServlet());
}
protected void shutDown() {}

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

AbstractExecutionThreadService

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

import com.google.common.util.concurrent.AbstractExecutionThreadService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Uninterruptibles; import java.util.concurrent.TimeUnit; /**
* User: hupeng
* Date: 14-12-22
* Time: 下午10:17
*/
public class AbstractExecutionThreadServiceTest extends AbstractExecutionThreadService {
private volatile boolean running = true; //声明一个状态 @Override
protected void startUp() {
////在这里我们可以做一些初始化操作
} @Override
public void run() {
while (running) {
// do our work
try {
Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS);
System.out.println("do our work.....");
} catch (Exception e) {
//处理异常,这里如果抛出异常,会使服务状态变为failed同时导致任务终止。
}
}
} @Override
protected void triggerShutdown() {
running = false; //这里我们改变状态值,run方法中就能够得到响应。=
//可以做一些清理操作,也可以移到shutDown()方法中执行
} @Override
protected void shutDown() throws Exception {
//可以关闭资源,关闭连接。。。
} public static void main(String[] args) throws InterruptedException {
AbstractExecutionThreadServiceTest service = new AbstractExecutionThreadServiceTest(); service.addListener(new Listener() {
@Override
public void starting() {
System.out.println("服务开始启动.....");
} @Override
public void running() {
System.out.println("服务开始运行");;
} @Override
public void stopping(State from) {
System.out.println("服务关闭中");
} @Override
public void terminated(State from) {
System.out.println("服务终止");
} @Override
public void failed(State from, Throwable failure) {
System.out.println("失败,cause:" + failure.getCause());
}
}, MoreExecutors.directExecutor()); service.startAsync().awaitRunning();
System.out.println("服务状态为:" + service.state()); Thread.sleep(10 * 1000); service.stopAsync().awaitTerminated(); System.out.println("服务状态为:" + service.state());
} }

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类来辅助实现。一个实现类看起来应该是这样的

import com.google.common.util.concurrent.AbstractScheduledService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.TimeUnit; /**
* User: hupeng
* Date: 14-12-22
* Time: 下午7:43
*/
public class AbstractScheduledServiceTest extends AbstractScheduledService { @Override
protected void startUp() throws Exception { } @Override
protected void shutDown() throws Exception { } @Override
protected void runOneIteration() throws Exception {
// //处理异常,这里如果抛出异常,会使服务状态变为failed同时导致任务终止
try {
System.out.println("do work....");
} catch (Exception e) {
//处理异常
}
} @Override
protected Scheduler scheduler() {
return Scheduler.newFixedDelaySchedule(1, 5, TimeUnit.SECONDS);
} public static void main(String[] args) throws InterruptedException {
AbstractScheduledServiceTest service = new AbstractScheduledServiceTest(); service.addListener(new Listener() {
@Override
public void starting() {
System.out.println("服务开始启动.....");
} @Override
public void running() {
System.out.println("服务开始运行");
;
} @Override
public void stopping(State from) {
System.out.println("服务关闭中");
} @Override
public void terminated(State from) {
System.out.println("服务终止");
} @Override
public void failed(State from, Throwable failure) {
System.out.println("失败,cause:" + failure.getCause());
}
}, MoreExecutors.directExecutor()); service.startAsync().awaitRunning();
System.out.println("服务状态为:" + service.state()); Thread.sleep(10 * 1000); service.stopAsync().awaitTerminated(); System.out.println("服务状态为:" + service.state());
}
}

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

/**
* Returns the {@link ScheduledExecutorService} that will be used to execute the {@link #startUp},
* {@link #runOneIteration} and {@link #shutDown} methods. If this method is overridden the
* executor will not be {@linkplain ScheduledExecutorService#shutdown shutdown} when this
* service {@linkplain Service.State#TERMINATED terminates} or
* {@linkplain Service.State#TERMINATED fails}. Subclasses may override this method to supply a
* custom {@link ScheduledExecutorService} instance. This method is guaranteed to only be called
* once.
*
* <p>By default this returns a new {@link ScheduledExecutorService} with a single thread thread
* pool that sets the name of the thread to the {@linkplain #serviceName() service name}.
* Also, the pool will be {@linkplain ScheduledExecutorService#shutdown() shut down} when the
* service {@linkplain Service.State#TERMINATED terminates} or
* {@linkplain Service.State#TERMINATED fails}.
*/
protected ScheduledExecutorService executor() {
final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
new ThreadFactory() {
@Override public Thread newThread(Runnable runnable) {
return MoreExecutors.newThread(serviceName(), runnable);
}
});
// Add a listener to shutdown the executor after the service is stopped. This ensures that the
// JVM shutdown will not be prevented from exiting after this service has stopped or failed.
// Technically this listener is added after start() was called so it is a little gross, but it
// is called within doStart() so we know that the service cannot terminate or fail concurrently
// with adding this listener so it is impossible to miss an event that we are interested in.
addListener(new Listener() {
@Override public void terminated(State from) {
executor.shutdown();
}
@Override public void failed(State from, Throwable failure) {
executor.shutdown();
}
}, directExecutor());
return executor;
}

你可以参照这个实现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. lintcode-408-二进制求和

    408-二进制求和 给定两个二进制字符串,返回他们的和(用二进制表示). 样例 a = 11 b = 1 返回 100 标签 二进制 字符串处理 脸书 思路 先相加,在处理进位,为了方便操作,将选字符 ...

  2. springMVC 流程

    springMVC流程控制 SpringMVC流程 web.xml 中配置 org.springframework.web.servlet.DispatcherServlet 这一步其实和spring ...

  3. Beta 阶段项目计划

    Beta 阶段项目计划 NewTeam 目标 实现用户数量的目标. 在多个平台发布 完成稳定运行.界面优雅的客户端 充分测试,避免发布后出现bug影响用户使用 及时更新开发文档 合理安排时间,避免和其 ...

  4. [学习]WireShark 的过滤功能

    1. 打开 wireShark 过滤显示 协议 比如显示arp协议 过滤栏输入arp即可 支持的协议类型 TCP UDP HTTP FTP ICMP SMTP等等 2. 过滤ip地址 ip.addr ...

  5. 关于jquery中on绑定click事件在苹果手机失效的问题(巨坑啊)

    用一个div当做了一个按钮来使用. <div class="button"> <div class=" next_button button_left ...

  6. web三大组件的加载顺序

    Web三大组件:过滤器组件  监听器组件  Servlet组件 过滤器的顶级接口:javax.servlet.Filter 监听器的顶级接口:javax.servlet.ServletContextL ...

  7. android Eclipse there no select

    点mainactivity类 右键  run as 进行 配置 就可运行

  8. 最大流Dinic算法模板(pascal)

    program rrr(input,output); const inf=; type pointer=^nodetype; nodetype=record t,c:longint; next,rev ...

  9. React安装React Devtools调试工具

    在运行一个React项目的时候浏览器控制台会提醒你去安装react devtools调试工具. Download the React DevTools for a better development ...

  10. CPP 替代 PIL 图片处理(缩略图生成)

    python中使用PIL(Pyhton Image Library)进行图片处理,好处就是编写简单方便,但是不能很好利用机器多核的特点,于是在项目中决定使用cpp来实现图片处理. 项目中的图片处理主要 ...