Java Servlets - 使用通用计时器阻止所有线程

时间:2015-07-17 15:11:57

标签: java multithreading tomcat servlets

在Tomcat 6上,我运行了一个servlet,它接受请求并将这些请求传递到外部系统。

外部系统存在限制限制 - 如果请求数超过每秒一定数量,则外部系统以Http 503响应。 没有进一步的请求可能会打到外部系统至少2秒钟,否则外部系统将重新启动其限制计时器。 最初,我检测到503 HttpResponse并执行了Thread.sleep(2000),但这是错误的,因为它不会阻止servlet使用其他线程处理其他请求 - 一旦检测到503响应,我需要阻止所有线程为至少2秒。

理想情况下,我希望阻塞的线程不要同时唤醒所有内容,而是说相隔100毫秒,以便按顺序处理请求。 我查看了Condition和ReentrantLock,但不确定这些是否合适。

2 个答案:

答案 0 :(得分:0)

只需在servlet中创建一个全局(静态)日期变量。获得503后,将此变量从null更改为本地时间。在联系外部系统之前,servlet应始终检查此变量。如果变量为null,或者超过2秒,则可以继续。否则阻塞线程(或抛出异常)。

答案 1 :(得分:0)

看起来像给我调用亚马逊服务,它可以很容易管理。

您需要一个集中管理模块来实现它,它就像一个单独的模块。

重要的是你根本不应该到达throttling limitation,如果你得到太多的请求会达到这个值,那么你应该回应你的客户端稍后检查结果(作为异步工作)。

如果请求是一项重要的业务(例如捕获付款),那么您必须使用模块实现故障转移,只需将请求数据保存到数据库中,因此如果有任何失败,您将拥有来自数据库的数据。

如果你熟悉MQ arch,那么它就是为这类东西设计的最佳解决方案,但你想拥有自己的东西,你可以接受并处理所有请求调用外部系统模块管理。

首先,您可能拥有一个带有请求信息的实体类,如

class entity{public String id,srv,blah_blah;}

第二,一个独立的模块,用于接受和处理请求,这也是请求的上下文。喜欢以下

class business{private business(){}// fan of OOP? K, go for singleton
private static final ArrayList<entity> ctx=new ArrayList<entity>();
static public void accept_request(entity e){_persist(e);ctx.add(e);}
static private void _persist(entity e){/*persist it to the db*/}
static private void _done(entity e){_remove(e);/*informing 3rd. parties if any*/}
static private void _remove(entity e){/*remove it from the db, it's done*/}
final private static int do_work(e){/*do the real business*/return 0;}//0 as success, 1, fail, 2....
}

但它还没有完成,现在你需要一种方法来调用do_work(),所以我建议一个后台线程(也是守护进程!)

所以客户端只是将请求推送到类似上下文的类,这里我们需要线程,如下面的

class business{...
static public void accept_request(entity e){_persist(e);ctx.add(e);synchronized(ctx){ctx.notify();}}
...
private static final Runnable r=new Runnable(){public void run(){try{
  while(!Thread.currentThread().interrupt()){
    if(ctx.size()==0){synchronized(ctx){if(ctx.size()==0){ctx.wait();}}}
    while(ctx.size()>0){entity e=ctx.get(0);ctx.remove(0);
     if(do_work(e)==0){_done(e);}else{ctx.add(e);/*give him another chance maybe!*/}end-else
    Thread.Sleep(100/*appreciate sleep time*/);}//end-loop

  }
}catch(Throwable wt){/*catch signals, maybe death thread*/}}};
static private Thread t;
void static public start_module(){t=new Thread(r);t.start();}
void static public stop_module(){t.interrupt();t.stop();}
...}

提示:尽量不要从容器启动过程中启动线程(调用start_module()),否则会导致内存泄漏!最佳解决方案是通过init() servlet方法调用线程将调用此模块(一次),当然通过应用程序停止(destroy()

来停止线程