最近开始研究jetty 7。已经出到稳定版了。我相信大多JETTY的爱好者已经看过了。
这里呢。对jetty 7的continuation总结一下。
为了做一个server long push的WEB应用。我选择了jetty。对于Jetty,我只能说是一个新手,在网上搜资料的时候,发现相关资料少个可怜,中文的资料都是一个抄一 个,或者就是翻译的。对于jetty我走了弯路。迫使自己看是看jetty的源文件。
大家肯定都知道jetty的 continuation是建立的NIO技术的基础上。使WEB 服务器对大量的HTTP申请做阻塞不必开启过多的线程。避免了浪费。而jetty是将封装好的servlet加入一个等待队列。之后做轮询。哪个满足条 件。就调用用户的方法去操作response。
刚开始作为一个第一次用jetty的人,我把suspend/complete和 suspend/resunme产生了一个错误的理解。我以为这个方法只是阻塞申请。于是我建立了一个方法。里面做了一个循环去判断某个值,如果存在就反 馈并complete。而最后发现jetty自带了遍历的功能。不用你去写循环。
正确的写法如下:
//判断applition中是否有msg有的话反馈,没有的话就阻塞。 protected void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { //得到applition对象 ServletContext applition = getServletConfig().getServletContext(); //获取continuation Continuation continuation = ContinuationSupport.getContinuation(request); //设定超时时间、可以不设置。默认为30秒 continuation.setTimeout(0); //阻塞 continuation.suspend(); //如果applition中有msg这个字符。开放阻塞 if(applition.getAttribute("msg").toString() != null){ continuation.complete(); response.getServletResponse().getWriter().print( applition.getAttribute("msg").toString() ); } }
其他你都不用管。服务器会做循环来调用你的serlvet。然后调用你的判断方法做处理。
要注意的一点是:
我做了一个小小的 测试。我挂起了20个长连接去判断自己需求的信息,但是呢。我第一步就给与第9个长连接做判断的对象赋值,按理论上来说,它应该立即反馈的。
错!
它不会反馈给你。因为服务器端对每个客户端挂起的长连接做了限制。代码如下:
package org.eclipse.jetty.continuation; import java.util.ArrayList; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.ServletResponseWrapper; class FauxContinuation implements ContinuationFilter.FilteredContinuation { private static final ContinuationThrowable __exception = new ContinuationThrowable(); //这里就是限制你为什么不能一台机器申请挂起多个长连接的原因。 private static final int __HANDLING = 1; private static final int __SUSPENDING = 2; private static final int __RESUMING = 3; private static final int __COMPLETING = 4; private static final int __SUSPENDED = 5; private static final int __UNSUSPENDING = 6; private static final int __COMPLETE = 7; private final ServletRequest _request; private ServletResponse _response; private int _state = 1; private boolean _initial = true; private boolean _resumed = false; private boolean _timeout = false; private boolean _responseWrapped = false; private long _timeoutMs = 30000L; private ArrayList<ContinuationListener> _listeners; FauxContinuation(ServletRequest request) { this._request = request; } public void onComplete() { if (this._listeners != null) for (ContinuationListener l : this._listeners) l.onComplete(this); } public void onTimeout() { if (this._listeners != null) for (ContinuationListener l : this._listeners) l.onTimeout(this); } public boolean isResponseWrapped() { return this._responseWrapped; } public boolean isInitial() { synchronized (this) { return this._initial; } } public boolean isResumed() { synchronized (this) { return this._resumed; } } public boolean isSuspended() { synchronized (this) { switch (this._state) { case 1: return false; case 2: case 3: case 4: case 5: return true; case 6: } return false; } } public boolean isExpired() { synchronized (this) { return this._timeout; } } public void setTimeout(long timeoutMs) { this._timeoutMs = timeoutMs; } public void suspend(ServletResponse response) { this._response = response; this._responseWrapped = response instanceof ServletResponseWrapper; suspend(); } public void suspend() { synchronized (this) { switch (this._state) { case 1: this._timeout = false; this._resumed = false; this._state = 2; return; case 2: case 3: return; case 4: case 5: case 6: throw new IllegalStateException(getStatusString()); } throw new IllegalStateException("" + this._state); } } public void resume() { synchronized (this) { switch (this._state) { case 1: this._resumed = true; return; case 2: this._resumed = true; this._state = 3; return; case 3: case 4: return; case 5: fauxResume(); this._resumed = true; this._state = 6; break; case 6: this._resumed = true; return; default: throw new IllegalStateException(getStatusString()); } } } public void complete() { synchronized (this) { switch (this._state) { case 1: throw new IllegalStateException(getStatusString()); case 2: this._state = 4; break; case 3: break; case 4: return; case 5: this._state = 4; fauxResume(); break; case 6: return; default: throw new IllegalStateException(getStatusString()); } } } public boolean enter(ServletResponse response) { this._response = response; return true; } public ServletResponse getServletResponse() { return this._response; } void handling() { synchronized (this) { this._responseWrapped = false; switch (this._state) { case 1: throw new IllegalStateException(getStatusString()); case 2: case 3: throw new IllegalStateException(getStatusString()); case 4: return; case 5: fauxResume(); case 6: this._state = 1; return; } throw new IllegalStateException("" + this._state); } } public boolean exit() { synchronized (this) { switch (this._state) { case 1: this._state = 7; onComplete(); return true; case 2: this._initial = false; this._state = 5; fauxSuspend(); if ((this._state == 5) || (this._state == 4)) { onComplete(); return true; } this._initial = false; this._state = 1; return false; case 3: this._initial = false; this._state = 1; return false; case 4: this._initial = false; this._state = 7; onComplete(); return true; case 5: case 6: } throw new IllegalStateException(getStatusString()); } } protected void expire() { synchronized (this) { this._timeout = true; } onTimeout(); synchronized (this) { switch (this._state) { case 1: return; case 2: this._timeout = true; this._state = 3; fauxResume(); return; case 3: return; case 4: return; case 5: this._timeout = true; this._state = 6; break; case 6: this._timeout = true; return; default: throw new IllegalStateException(getStatusString()); } } } private void fauxSuspend() { long expire_at = System.currentTimeMillis() + this._timeoutMs; long wait = this._timeoutMs; while ((this._timeoutMs > 0L) && (wait > 0L)) { try { super.wait(wait); } catch (InterruptedException e) { break label51: } wait = expire_at - System.currentTimeMillis(); } if ((this._timeoutMs > 0L) && (wait <= 0L)) label51: expire(); } private void fauxResume() { this._timeoutMs = 0L; super.notifyAll(); } public String toString() { return getStatusString(); } String getStatusString() { synchronized (this) { return ((this._state == 4) ? "COMPLETING" : (this._state == 6) ? "UNSUSPENDING" : (this._state == 3) ? "RESUMING" : (this._state == 5) ? "SUSPENDED" : (this._state == 2) ? "SUSPENDING" : (this._state == 1) ? "HANDLING" : new StringBuilder().append("???").append(this._state).toString()) + ((this._initial) ? ",initial" : "") + ((this._resumed) ? ",resumed" : "") + ((this._timeout) ? ",timeout" : ""); } } public void addContinuationListener(ContinuationListener listener) { if (this._listeners == null) this._listeners = new ArrayList(); this._listeners.add(listener); } public Object getAttribute(String name) { return this._request.getAttribute(name); } public void removeAttribute(String name) { this._request.removeAttribute(name); } public void setAttribute(String name, Object attribute) { this._request.setAttribute(name, attribute); } public void undispatch() { if (isSuspended()) { if (ContinuationFilter.__debug) throw new ContinuationThrowable(); throw __exception; } throw new IllegalStateException("!suspended"); } }
所以遇到这个问题的朋友不必担心。我使用多台机器测试过。服务器的servlet轮询队列是很长的。但对于单独用户来说只能接受6个 suspend.在发送长连接挂起请求。他们加入到你所处的用户等待列表。当你挂起的6个连接中有一个被释放了。你的第7个连接将被挂起。
之 前用了webbench做压测。
不管你怎么测非阻塞技术的长连接。你都会被jetty拒之门外。。。
就是因为这个做 了限制。这样的限制大大降低对服务器的压力。