简体   繁体   English

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

[英]Java Servlets - block all threads using common timer

On Tomcat 6, I have a servlet running which accepts requests and passes these onto an external system. 在Tomcat 6上,我正在运行一个servlet,该servlet接受请求并将这些请求传递到外部系统。


There is a throttling limitation on the external system - if the number of requests exceed a certain number per second, then the external system responds with a Http 503. No further requests may hit the external system for at least 2 seconds or else the external system will restart its throttling timer. 外部系统上有一个节流限制-如果请求的数量超过每秒一定数量,则外部系统将以Http 503进行响应。至少2秒钟内没有其他请求可以到达外部系统,否则外部系统将重新启动其节流计时器。 Initially, I detected the 503 HttpResponse and did a Thread.sleep(2000) but that is wrong as it doesn't prevent the servlet servicing other requests using other threads - once a 503 response is detected, I need to block all threads for at least the 2 seconds. 最初,我检测到503 HttpResponse并执行Thread.sleep(2000),但这是错误的,因为它不能阻止servlet使用其他线程处理其他请求-一旦检测到503响应,我就需要在至少2秒。


Ideally, I would prefer the blocked threads not to wake up all at the same time but say a 100ms apart so that requests would be handled in order. 理想情况下,我希望阻塞的线程不要同时唤醒所有线程,而是说间隔100毫秒,以便按顺序处理请求。 I've looked at the Condition and ReentrantLock but unsure if these are appropriate. 我查看了Condition和ReentrantLock,但不确定是否合适。

Just create a global (static) date variable in the servlet. 只需在Servlet中创建一个全局(静态)日期变量。 When you get a 503, change this variable from null to the local time. 当您收到503时,将此变量从null更改为本地时间。 The servlet should always check this variable before contacting the external system. Servlet在联系外部系统之前应始终检查此变量。 If the variable is null, or more than 2 seconds have passed, then you can proceed. 如果变量为null,或者已超过2秒,则可以继续。 Otherwise block the thread (or throw an exception). 否则,阻塞线程(或引发异常)。

Looks like calling Amazon services to me, and it can be managed so easy. 看起来像是在给我打电话给Amazon服务,而且可以轻松地对其进行管理。

You need a central and managed module for doing it, and it comes like a single module. 您需要一个集中的托管模块来执行此操作,它就像一个模块一样。

The important thing is you should not reach the throttling limitation at all, and if you get too much requests which would reach this value, so you should respond to your client check the result later(as async work). 重要的是,您根本不应达到throttling limitation ,并且如果收到过多的请求将达到此值,那么您应该稍后响应客户端检查结果(作为异步工作)。

If the request is kinda important business(such as capturing a payment), so you have to implement a failover with the module too, simply by persisting the request data into the database, so if there is any fail, you will have the data from the database. 如果请求是一项很重要的业务(例如收取款项),那么您也必须使用该模块来实现故障转移,只需将请求数据持久保存到数据库中即可,因此,如果有任何失败,您将获得来自数据库。

If you are familiar with MQ arch, so it would be the best solution where they are designed for this kind of stuffs, but you like to have your own, you may accept and process all requests to call teh external system by the module manage. 如果您熟悉MQ arch,那么这是为此类材料设计的最佳解决方案,但是您希望拥有自己的MQ,您可以接受并处理模块管理调用所有调用外部系统的请求。

first you may have a entity class which carries the request info like 首先,您可能有一个实体类,其中包含请求信息,例如

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

Second, a stand-alone module for accepting and processing the requests, which would be the context for the requests too. 其次,一个用于接受和处理请求的独立模块,这也是请求的上下文。 like following 喜欢以下

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....
}

But it's not completed yet, now you need a way to call the do_work() guy, so I suggest a background thread(would be daemon too!) 但这还没有完成,现在您需要一种方法来调用do_work()家伙,所以我建议使用后台线程(也将是守护进程!)

So clients just push the requests to this context-like class, and here we need the thread, like following 因此客户只需将请求推送到类似上下文的类,在这里我们需要线程,如下所示

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();}
...}

Tip: try not start the thread(calling start_module() ) out of container initiation process, or you will have memory leak! 提示:尝试不要在容器初始化过程之外启动线程(调用start_module() ),否则会发生内存泄漏! best solution would call the thread by init() method of servlet(s) would call this module(once), and of course halting the the thread by application halt ( destroy() ) 最好的解决方案是通过servlet的init()方法调用线程,然后才调用此模块,并且当然要通过应用程序暂停( destroy() )暂停线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM