设为首页 收藏本站
查看: 1242|回复: 0

[经验分享] 整合 GWT 与 Jetty Continuations

[复制链接]

尚未签到

发表于 2017-2-26 10:55:03 | 显示全部楼层 |阅读模式
虽然GWT充满了争议,但是对于Java开发者而言,能够使用自己最熟悉的语言来进行Ajax开发,Google推出的GWT的确具有相当大的诱惑力,而且作为一个快速发展的开源框架,相信以后它的用户群应该会越来越多。
Jetty Continuations 很好的解决了服务器更新客户端的问题,服务器不用再为每一个等待响应的客户端单独建立一个线程,借助Continuations和新IO,可以在有限的线程内支持更多用户。
更多内容参见http://docs.codehaus.org/display/JETTY/Continuations
最近在做一个浏览器上的在线聊天项目,用来做为在线客服支持,打算使用GWT编写界面,但是GWT所提供RPC机制并不直接支持Jetty Continuations。为了支持比当前线程数更多的用户,Jetty中的Continuation.suspend()会抛出一个特殊的运行时异常:RetryRequest。这个异常将传播到servlet以外,然后通过过滤器传回,再由SelectChannelConnector捕获,将请求放入处于等待状态的Continuation队列中,此时HTTP连接并不关闭,而当前的线程却可以被放回线程池,供别的请求使用。但是使用GWT时,GWT捕获了所有的Throwable,这样就会导致Continuations机制失败。而且GWT提供的RemoteServiceServlet类中很多方法都被定义为final或static,于是你无法通过继承这个类来重写相关方法,让它放过RetryRequest。
但是因为GWT是开源项目,于是Jetty组织改写RemoteServiceServlet,提供了OpenRemoteServiceServlet,将Continuations所敏感的方法去掉final和static,然后继承OpenRemoteServiceServlet,提供了它们自己的AsyncRemoteServiceServlet,这样我们GWT服务器端的RPC接口的实现类直接继承AsyncRemoteServiceServlet,无须其它更改,就可以使用Jetty Continuations的API了。
AsyncRemoteServiceServlet14.java中放过RetryRequest的相关代码:

/**
* Throws the Jetty RetryRequest if found.
*
* @param caught the exception
*/
protected void throwIfRetyRequest(Throwable caught) {
if (caught instanceof UnexpectedException) {
caught = caught.getCause();
}
if (caught instanceof RuntimeException && JETTY_RETRY_REQUEST_EXCEPTION.equals(caught.getClass().getName())) {
throw (RuntimeException) caught;
}
}

更多内容参见http://blogs.webtide.com/gregw/2006/12/07/1165517549286.html
这里提供一个完整的小例子给大家,也作为我这段时间学习的总结 DSC0000.gif
下载最新的
GWT http://code.google.com/webtoolkit/download.html
Jetty http://docs.codehaus.org/display/JETTY/Downloading+and+Installing#download
然后在http://jira.codehaus.org/browse/JETTY-399上面找到OpenRemoteServiceServlet14.java和AsyncRemoteServiceServlet14.java,这是OpenRemoteServiceServlet和AsyncRemoteServiceServlet的新版本,支持GWT1.4版本。
首先使用GWT的命令行工具创建eclipse下的GWT项目,然后导入到eclipse中,再导入jetty-util-6.1.3.jar。
RPC接口定义:

public interface TestService extends RemoteService {
public String getNews();
}


GWT的入口类:

public class GCEntry implements EntryPoint {
public void onModuleLoad() {
final TestServiceAsync testService = (TestServiceAsync)GWT.create(TestService.class);
ServiceDefTarget target = (ServiceDefTarget)testService;
target.setServiceEntryPoint(GWT.getModuleBaseURL() + "test");
final TextArea printArea = new TextArea();
printArea.setVisibleLines(10);
printArea.setCharacterWidth(30);
testService.getNews(new AsyncCallback() {
public void onFailure(Throwable caught) {}
public void onSuccess(Object result) {
printArea.setText(printArea.getText() + result);
testService.getNews(this);
}
});
DockPanel dp = new DockPanel();
dp.add(printArea, DockPanel.CENTER);
RootPanel.get().add(dp);
}
}

界面将显示一个文本框,反复调用testService.getNews(),将异步返回的结果输出到文本框中。
服务器端RPC接口的实现:

public class TestServiceImpl extends AsyncRemoteServiceServlet14 implements
TestService {
private NewsCreator newsCreator;
public void init() {
newsCreator = new NewsCreator();
}
public String getNews() {
return newsCreator.getNews(getThreadLocalRequest());
}
}

注意这里继承的是AsyncRemoteServiceServlet14。通过一个辅助类NewsCreator来生成新的时间:

public class NewsCreator implements Runnable {
private Set<Continuation> readers;
public NewsCreator() {
readers = new HashSet<Continuation>();
new Thread(this).start();
}
public void run() {
while (true) {
synchronized(this) {
for (Continuation continuation : readers) {
continuation.setObject(new Date().toString() + "\r\n");
continuation.resume();
}
readers.clear();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public String getNews(HttpServletRequest request) {
Continuation continuation = ContinuationSupport.getContinuation(request, null);
synchronized(this) {
readers.add(continuation);
}
continuation.suspend(10000);
String news = continuation.getObject() == null ? "No Time" : (String)continuation.getObject();
continuation.setObject(null);
return news;
}
}

getNews()为每个请求获取一个Continuation实例,然后将这个实例保存起来,阻塞10秒,同时NewsCreator每隔两秒发布一次时间,发布时将当前时间附在Continuation上,恢复被阻塞的Continuation。Continuation在被恢复或超时之后相对应的请求都会被重新执行,当再次执行到Continuation.suspend()时,这个方法会马上返回,然后继续执行suspend()后面的代码,返回上一次发布的时间或是"No Time",当然这里发布的间隔比阻塞的时间小,不会出现"No Time"。
完整代码见附件

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-347368-1-1.html 上篇帖子: maven项目中jetty插件的部署 下篇帖子: 手动建立jetty datasource
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表