webservlet  +redis 的消息发布订阅 ,挺好的

当请求到来,向redis server申请一个频道 ,然后等着另一端架设是B 处理完毕获得到处理信息调用redis ,使用redis 往当前申请的频道号 发送消息,在接收者C此时会收到一个事件的方式处理结果.

redis.subscribe(subscriber, channel);    //通过subscribe 的api去订阅,入参是订阅者和频道名    

注意事项:

webservlet 启动异步线程有个timeout 超时事件

AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout();
asyncContext.addListener(new AppAsyncListener(subscriber)); //通过AppAsyncListener 接收超时事件

在超时之后返回事件中可以返回超时的信息给请求端,必须要做关闭链接,异步线程结束 的操作,因为我的项目中订阅了redis消息.在此直关闭链接和关闭异步线程,还是不够的,还需要 中间取消订阅,也就是把事件给解绑了,要不异步线程实际没结束,事件还存在,

当有个redis 消息事件过来,而此时的链接已经关闭,会出现一个bug  : AsyncContext has already completed ,异步报文已经结束的bug ,实际此时,事件还是能接收到的.于是异步线程未全部退出,导致每次请求一次,多一个现在在debug内无法退出.

java.lang.IllegalStateException: The request associated with the AsyncContext has already completed processing.
at org.apache.catalina.core.AsyncContextImpl.check(AsyncContextImpl.java:)
at org.apache.catalina.core.AsyncContextImpl.getResponse(AsyncContextImpl.java:)

所以,需要在AppAsyncListener 注入subscriber 订阅者这个对象,这个对象是接收消息的事件对象,扩展了Subscriber extends JedisPubSub.只有这样才能调用unsubscriber解绑所绑定频道其实感觉就是个接触事件绑定.

请求得到之后,需要关闭频道号,关闭这次异步启用的线程,也就是让当前启动的线程退出, 如果这个异步线程未退出,那bug 简直会让你头晕......

AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(3000);
Work work = new Work(asyncContext,"3856111302");
subscriber = new Subscriber(asyncContext);
asyncContext.addListener(new AppAsyncListener(subscriber));   // 异步状态监听 类注入 redis订阅者对象subscriber,这样当异步线程出现timeout时,调用AppAsyncListener类中void onTimeout 时 可以通过subscriber  对象调用

                                                          // unSubscriber(),解除订阅订阅的频道...关闭链接, 关闭异步线程.此次请求的异步线程完美退出. 

new Thread(work).start();

上边是异步线程启动的地方

下边是timeout 后再listener 中调用 suscriber类中的解绑方法

 public void unSubscriber() //给timeout 之后 解除事件用
{
PrintWriter out;
try {
out = response.getWriter();
Gson gson = new Gson();
String gsonString = gson.toJson("");
out.println(gsonString);
out.flush();
out.close();
unsubscribe("");
asyncContext.complete(); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
@WebListener
public class AppAsyncListener implements AsyncListener {
String devcmdid;
Subscriber subscriber;
public AppAsyncListener(){}
public AppAsyncListener(Subscriber subscriber){
// this.devcmdid=devcmdid;
this.subscriber=subscriber;
} @Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException { /* ServletResponse response = asyncEvent.getAsyncContext().getResponse();
PrintWriter out = response.getWriter();
out.write("SystemMessageContents.ErrorCode.MESSAGE_REQUEST_TIMEOUT""214"); //超时214
out.flush();
out.close();
asyncEvent.getAsyncContext().complete();
System.out.println("AppAsyncListener timeout");*/
subscriber.unSubscriber();
// unsubscribe("3856111302");
}

由于异步线程启动之后加载了redis的订阅频道,在这里应该是 加载了redis 的频道事件,事件一旦加载了,这个线程是一直存在的, 它不像别的没有while的线程,运行完了线程就结束了

在此加载了事件后,线程不会出退出

RedisImpl redis = new RedisImpl();
redis.subscribe(subscriber, channel); //通过subscribe 的api去订阅,入参是订阅者和频道名

所以当收到消息后 ,必须运行  asyncContext.complete(); 对这个线程进行关闭,否则会等到 asyncContext的timeout 时间结束之后才结束.这就是我那程序里没加这句,导致每次请求完之后都要运行一次AppAsyncListener  implements AsyncListener 里边的timeout事件返回消息

@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException     // 后因结束异常,程序改为上边的方式

  @Override
public void onMessage(String channel, String message) { //收到消息会调用
ServletResponse response = asyncContext.getResponse();
PrintWriter out;
try {
out = response.getWriter();
Gson gson = new Gson();
String gsonString = gson.toJson("resever");
out.println(gsonString);
out.flush();
out.close();
unsubscribe("");
asyncContext.complete(); } catch (IOException e) {

2:一个常识方面的bug

public class Subscriber extends JedisPubSub {
private AsyncContext asyncContext;
ServletResponse response = asyncContext.getResponse();
public Subscriber(AsyncContext asyncContext){
this.asyncContext=asyncContext;
response = asyncContext.getResponse();
}
ServletResponse response = asyncContext.getResponse();    这段程序 在类下边直接去获取,在类初始化时由于还未载入asyncContext,出现asyncContext为null的bug,所以此处应该如下
public class Subscriber extends JedisPubSub {
private AsyncContext asyncContext;
ServletResponse response =null;//= asyncContext.getResponse();
public Subscriber(AsyncContext asyncContext){
this.asyncContext=asyncContext;
response = asyncContext.getResponse();
}

3:类构造方法无参未写出现初始化的错误 .init() 的bug

public class AppAsyncListener  implements AsyncListener {
String devcmdid;
Subscriber subscriber;
public AppAsyncListener(){} //后来加上这个问题解决
public AppAsyncListener(Subscriber subscriber){
// this.devcmdid=devcmdid;
this.subscriber=subscriber;
}

  

4:线程不停的循环读取redis 的值,当运行 getResource(); 时,而此时redis server未打开,出现JedisConnectionException 抛出异常,这里jedis 做的不够啊,用try catch 还抓取不到这个异常...等有空有时间做处理

关于webservlet 请求异步处理,链接未关闭出现的bug的更多相关文章

  1. 接收对 http://192.168.1.18:8001/ObtainData/Service 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。

    [2015/8/5 19:28:49]错误信息:接收对 http://192.168.1.18:8001/ObtainData/Service 的 HTTP 响应时发生错误.这可能是由于服务终结点绑定 ...

  2. 解决WCF“接收对 http://xxx.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致"

    最近在工作中新加了一个接口,本地调试的时候,直接抛出“接收对 http://xxx.svc 的 HTTP 响应时发生错误.这可能是由于服务终结点绑定未使用 HTTP 协议造成的.这还可能是由于服务器中 ...

  3. 请求被中止: 未能创建 SSL/TLS 安全通道,设置 TLSv1.2和TLSv1.1版本 .基础链接已经关闭,发送时发生错误

    WSO2 API访问的安全要求, 只能提供TLSv1.2和TLSv1.1版本,其它SSL版本协议因为存在较高安全漏洞问题会被disable. A 改成TLSv1.1或TLSv1.2,最好使用TLSv1 ...

  4. 分析案例:界面提示“基础链接已经关闭:接收时发生错误”----本质为StackOverflow

    问题描述: 一个业务复杂.执行时间很长的功能,经常报出“基础链接已经关闭:接收时发生错误”,很是蹊跷... 问题分析: 首先,查阅应用服务器的系统日志,发现问题发生时总是会伴随着w3wp进程崩溃的错误 ...

  5. SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志

    在打开数据库的时候,突然出现异常错误,然后我去关闭sql server 服务,然后重启服务的时候,不能重启,出现以下错误 “请求失败或服务未及时响应.有关详细信息,请参见事件日志或其它适合的错误日志” ...

  6. 在系统启动时,Windows Vista 中、 在 Windows 7 中,Windows Server 2008 中和在 Windows Server 2008 R2 中的 497 天后未关闭 TIME_WAIT 状态的所有 TCP/IP 端口

    在系统启动时,Windows Vista 中. 在 Windows 7 中,Windows Server 2008 中和在 Windows Server 2008 R2 中的 497 天后未关闭 TI ...

  7. pos 访问超时 windows连接超时 497 天后未关闭 TIME_WAIT

    问题描述: nginx连接后台tomcat程序 一直报错 nginx的error日志如下 // :: [error] #: *: A connection attempt failed because ...

  8. (四)整合 RocketMQ ,实现请求异步处理

    整合 RocketMQ ,实现请求异步处理 1.RocketMQ简介 1.1 架构图片 1.2 角色分类 1.3 通信机制 2.实现案例 2.1 项目结构图 2.2 配置文件 2.3 生产者配置 2. ...

  9. 未关闭InputStream 引起的血案

    下面的方法是从aws s3 读取文件对象下载到本地 public int downloadFile(HttpServletResponse httpResponse, String storePath ...

随机推荐

  1. cesium3dtiles位置改变

    cesium偏移3dtiles高度var heightOffset = 20.0; var boundingSphere = tileset.boundingSphere; var cartograp ...

  2. c里面的static inline函数

    一般来说加上static表示函数是文件作用域,有的时候单独使用inline编译器会优化,没有编译成内联函数,而是变成普通函数编译,所以必须在前面加上static,放在头文件中可以被外部文件访问.   ...

  3. NoSuchMethodError解决方法

    下面演示下如何在啥都不知道的情况下遇到该错误的解决思路: 随便找一个错误示例: Caused by: java.lang.NoSuchMethodError: org.eclipse.jdt.inte ...

  4. Java——@SupressWarnings

    J2SE 提供的最后一个批注是 @SuppressWarnings.该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默. 背景:J2SE 5.0 为 Java 语言增加了几 ...

  5. Robot Framework 自动化框架大纲

    Python + Robot Framework 环境搭建 Android SDK + Appium 环境搭建 RobotFramework - AppiumLibrary 之元素定位 RobotFr ...

  6. 删除链表的倒数第N个节点

    题目描述: 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后 ...

  7. Android 开发 Fresco框架点击小图显示全屏大图实现 ZoomableDraweeView

    目标需求 实现一张小图片,被点击后变成一个在整个屏幕上显示的大图片.类似于微信朋友圈的图片. 实现流程 1.Fresco基本初始化 2.下载并且导入ZoomableDraweeView 它是实现大图的 ...

  8. Android studio3.0打开Device File Explore(文件管理器)的方法(图文教程)

    Android studio3.0打开Device File Explore(文件管理器)的方法(图文教程) 看到网上AS3.0新增加的查看手机文件的新功能,全部都是转载的,没有几个人心细的把如何打开 ...

  9. python学习(二)--数据类型

    数据类型 1.工厂函数 type() int() float() str() list() tuple() dict() bool() set()工厂函数的理解:工厂函数看上去有点像函数,实质上他们是 ...

  10. java面试题复习(四)

    31.内部类可以引用它的外部类的私有成员吗? 可以,内部类对象可以访问创建它的外部类对象的成员 32.final关键字有哪些用法? 修饰类时该类不能被继承,修饰方法时,该方法不能被重写,修饰变量时表示 ...