在Tomcat 6上,我运行了一个servlet,它接受请求并将这些请求传递到外部系统。
外部系统存在限制限制 - 如果请求数超过每秒一定数量,则外部系统以Http 503响应。 没有进一步的请求可能会打到外部系统至少2秒钟,否则外部系统将重新启动其限制计时器。 最初,我检测到503 HttpResponse并执行了Thread.sleep(2000),但这是错误的,因为它不会阻止servlet使用其他线程处理其他请求 - 一旦检测到503响应,我需要阻止所有线程为至少2秒。
理想情况下,我希望阻塞的线程不要同时唤醒所有内容,而是说相隔100毫秒,以便按顺序处理请求。 我查看了Condition和ReentrantLock,但不确定这些是否合适。
答案 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()
)