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. Vue ElementUI 按需引入

    一.element UI组件的单独使用(第一种方法): 1.cnpm install babel-plugin-component -D         2.找到.babelrc 配置文件       ...

  2. win10系统搜索不到某些老式打印机

    问题: win10系统在设置选项里面搜索打印机,却找不到打印机. 百度了一下: 基本说的是驱动问题. 解决方法: 1.下载lansee局域网扫描软件 2.打开嗅探工具点击开始 3.搜索出来之后,会在共 ...

  3. python爬虫爬取京东、淘宝、苏宁上华为P20购买评论

    爬虫爬取京东.淘宝.苏宁上华为P20购买评论 1.使用软件 Anaconda3 2.代码截图 三个网站代码大同小异,因此只展示一个 3.结果(部分) 京东 淘宝 苏宁 4.分析 这三个网站上的评论数据 ...

  4. jQuery人民币转大写,C#人命币转大写

    jQuery人民币转大写 function convertCurrency(money) { //汉字的数字 var cnNums = new Array('零', '壹', '贰', '叁', '肆 ...

  5. 一些最常见的SNMP的OID自动翻译成zabbix数字进行表示(华为9306)

    转载自:https://blog.51cto.com/davidbj/1173954 随着Zabbix 2.0版本的发布,很多企业开始用zabbix来代替之前的Nagio.Cacti等其它监控软件.至 ...

  6. springboot学习随笔(三):Controller参数映射

    接上章内容,我们看看浏览器参数如何映射到我们的Controller层 我们新建UserController和User实 User.java package com.example.main; impo ...

  7. 嵌入式linux——点亮led灯(二)

    刚才在jz2440板子上写了一个点亮中间led的程序,前前后后十几分钟才好.最终代码 本节内容: 1. 汇编点灯 2. C点灯 3. 参数选择点灯 4. 按键点灯 1. 汇编点灯 .text .glo ...

  8. azkaban使用--传入动态参数

    转: azkaban的工作流中的参数可以分为如下几个类型:azkaban UI 页面输入参数, 环境变量参数, job作业文件中定义的参数,工作流的用户定义的属性文件,上游作业传递给下游的参数,工作流 ...

  9. MM-移动类型

    链接:SAP移动类型   移动类型 备注 业务类型 SAP中事务代码 备注 101 采购订单收货.生产订单收货 收货 migo CO11N顶层处理移动类型\跨工厂收货 102 采购订单收货冲销 收货 ...

  10. MySql TIMEDIFF做计算之后,后台报Illegal hour value '24' for java.sql.Time type 问题

    页面需要显示这种格式:31:01:20 但是后台Springboot会提示Illegal hour value '24' for java.sql.Time type in value '24:00: ...