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. Flask即插视图与tornado比较

    由于公司使用了Tornado框架和Flask框架,之前一直使用的都是Flask框架,已经对url下面紧跟着视图的写法很固执.刚开始接触Tornado框架,对于其url和视图分开的写法思想上无法转变.今 ...

  2. vue2.0插件--loading

    loading效果很常见,常见到我们任何一个项目中,都可以见到他的身影.今天就以loading作为切入口,唠叨一下vuejs的插件的写法. 看vuejs官方文档关于插件的说明,关于使用插件和写插件,V ...

  3. leetCode66:加一

    /** * @param {number[]} digits * @return {number[]} */ var plusOne = function(digits) { if(digits[di ...

  4. Kafka(2)--kafka基本原理之消息的分发与接收

    关于 Topic 和 Partition Topic 在 kafka 中,topic 是一个存储消息的逻辑概念,可以认为是一个消息集合.每条消息发送到 kafka 集群的消息都有一个类别.物理上来说, ...

  5. django开发网站 让局域网中的电脑访问你的主机

    1. 关闭主机电脑上的防火墙(不用关闭,加一个端口号就行) 2.在你的settings.py文件中,找到ALLOWED_HOSTS=[ ],在中括号中加入你在局域网中的IP.如我在局域网中的IP为19 ...

  6. Python11

    os模块 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录:相当于shell下cd os ...

  7. Delphi调用C#编写的WebService 注意事项

                            返回的字段值区分大小写,c#和Delphi的字段要一致

  8. Redis的Errorlog或者启动日志(错误日志)的配置

    Errorlog或者是运行日志是任何一个软件的运行中异常诊断必看的文件之一,折腾Redis的过程中以为有默认的错误日志(或启动日志),不过一直没有发现类似的日志文件,在看了默认的配置文件之后,发现Re ...

  9. ArcGIS 批量修改数据名称-arcgis案例实习教程

    ArcGIS 批量修改数据名称-arcgis案例实习教程 联系方式:谢老师,135-4855-4328,xiexiaokui#qq.com 功能:批量修改数据/文件名称 使用方法: 输入:文件夹(或者 ...

  10. eos dapp开发

    https://blog.csdn.net/u010665359/article/details/82906497