关于webservlet 请求异步处理,链接未关闭出现的bug
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的更多相关文章
- 接收对 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 响应时发生错误.这可能是由于服务终结点绑定 ...
- 解决WCF“接收对 http://xxx.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致"
最近在工作中新加了一个接口,本地调试的时候,直接抛出“接收对 http://xxx.svc 的 HTTP 响应时发生错误.这可能是由于服务终结点绑定未使用 HTTP 协议造成的.这还可能是由于服务器中 ...
- 请求被中止: 未能创建 SSL/TLS 安全通道,设置 TLSv1.2和TLSv1.1版本 .基础链接已经关闭,发送时发生错误
WSO2 API访问的安全要求, 只能提供TLSv1.2和TLSv1.1版本,其它SSL版本协议因为存在较高安全漏洞问题会被disable. A 改成TLSv1.1或TLSv1.2,最好使用TLSv1 ...
- 分析案例:界面提示“基础链接已经关闭:接收时发生错误”----本质为StackOverflow
问题描述: 一个业务复杂.执行时间很长的功能,经常报出“基础链接已经关闭:接收时发生错误”,很是蹊跷... 问题分析: 首先,查阅应用服务器的系统日志,发现问题发生时总是会伴随着w3wp进程崩溃的错误 ...
- SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
在打开数据库的时候,突然出现异常错误,然后我去关闭sql server 服务,然后重启服务的时候,不能重启,出现以下错误 “请求失败或服务未及时响应.有关详细信息,请参见事件日志或其它适合的错误日志” ...
- 在系统启动时,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 ...
- pos 访问超时 windows连接超时 497 天后未关闭 TIME_WAIT
问题描述: nginx连接后台tomcat程序 一直报错 nginx的error日志如下 // :: [error] #: *: A connection attempt failed because ...
- (四)整合 RocketMQ ,实现请求异步处理
整合 RocketMQ ,实现请求异步处理 1.RocketMQ简介 1.1 架构图片 1.2 角色分类 1.3 通信机制 2.实现案例 2.1 项目结构图 2.2 配置文件 2.3 生产者配置 2. ...
- 未关闭InputStream 引起的血案
下面的方法是从aws s3 读取文件对象下载到本地 public int downloadFile(HttpServletResponse httpResponse, String storePath ...
随机推荐
- Vue ElementUI 按需引入
一.element UI组件的单独使用(第一种方法): 1.cnpm install babel-plugin-component -D 2.找到.babelrc 配置文件 ...
- win10系统搜索不到某些老式打印机
问题: win10系统在设置选项里面搜索打印机,却找不到打印机. 百度了一下: 基本说的是驱动问题. 解决方法: 1.下载lansee局域网扫描软件 2.打开嗅探工具点击开始 3.搜索出来之后,会在共 ...
- python爬虫爬取京东、淘宝、苏宁上华为P20购买评论
爬虫爬取京东.淘宝.苏宁上华为P20购买评论 1.使用软件 Anaconda3 2.代码截图 三个网站代码大同小异,因此只展示一个 3.结果(部分) 京东 淘宝 苏宁 4.分析 这三个网站上的评论数据 ...
- jQuery人民币转大写,C#人命币转大写
jQuery人民币转大写 function convertCurrency(money) { //汉字的数字 var cnNums = new Array('零', '壹', '贰', '叁', '肆 ...
- 一些最常见的SNMP的OID自动翻译成zabbix数字进行表示(华为9306)
转载自:https://blog.51cto.com/davidbj/1173954 随着Zabbix 2.0版本的发布,很多企业开始用zabbix来代替之前的Nagio.Cacti等其它监控软件.至 ...
- springboot学习随笔(三):Controller参数映射
接上章内容,我们看看浏览器参数如何映射到我们的Controller层 我们新建UserController和User实 User.java package com.example.main; impo ...
- 嵌入式linux——点亮led灯(二)
刚才在jz2440板子上写了一个点亮中间led的程序,前前后后十几分钟才好.最终代码 本节内容: 1. 汇编点灯 2. C点灯 3. 参数选择点灯 4. 按键点灯 1. 汇编点灯 .text .glo ...
- azkaban使用--传入动态参数
转: azkaban的工作流中的参数可以分为如下几个类型:azkaban UI 页面输入参数, 环境变量参数, job作业文件中定义的参数,工作流的用户定义的属性文件,上游作业传递给下游的参数,工作流 ...
- MM-移动类型
链接:SAP移动类型 移动类型 备注 业务类型 SAP中事务代码 备注 101 采购订单收货.生产订单收货 收货 migo CO11N顶层处理移动类型\跨工厂收货 102 采购订单收货冲销 收货 ...
- 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: ...