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

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

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

注意事项:

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

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

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

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

  1. java.lang.IllegalStateException: The request associated with the AsyncContext has already completed processing.
  2. at org.apache.catalina.core.AsyncContextImpl.check(AsyncContextImpl.java:)
  3. 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  对象调用

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

new Thread(work).start();

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

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

  1. public void unSubscriber() //给timeout 之后 解除事件用
  2. {
  3. PrintWriter out;
  4. try {
  5. out = response.getWriter();
  6. Gson gson = new Gson();
  7. String gsonString = gson.toJson("");
  8. out.println(gsonString);
  9. out.flush();
  10. out.close();
  11. unsubscribe("");
  12. asyncContext.complete();
  13.  
  14. } catch (IOException e) {
  15. // TODO Auto-generated catch block
  16. e.printStackTrace();
  17. }
  1. @WebListener
  2. public class AppAsyncListener implements AsyncListener {
  3. String devcmdid;
  4. Subscriber subscriber;
  5. public AppAsyncListener(){}
  6. public AppAsyncListener(Subscriber subscriber){
  7. // this.devcmdid=devcmdid;
  8. this.subscriber=subscriber;
  9. }
  10.  
  11. @Override
  12. public void onTimeout(AsyncEvent asyncEvent) throws IOException {
  13.  
  14. /* ServletResponse response = asyncEvent.getAsyncContext().getResponse();
  15. PrintWriter out = response.getWriter();
  16. out.write("SystemMessageContents.ErrorCode.MESSAGE_REQUEST_TIMEOUT""214"); //超时214
  17. out.flush();
  18. out.close();
  19. asyncEvent.getAsyncContext().complete();
  20. System.out.println("AppAsyncListener timeout");*/
  21. subscriber.unSubscriber();
  22. // unsubscribe("3856111302");
  23. }

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

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

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

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

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

  1. @Override
  2. public void onMessage(String channel, String message) { //收到消息会调用
  3. ServletResponse response = asyncContext.getResponse();
  4. PrintWriter out;
  5. try {
  6. out = response.getWriter();
  7. Gson gson = new Gson();
  8. String gsonString = gson.toJson("resever");
  9. out.println(gsonString);
  10. out.flush();
  11. out.close();
  12. unsubscribe("");
  13. asyncContext.complete();
  14.  
  15. } catch (IOException e) {

2:一个常识方面的bug

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

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

  1. public class AppAsyncListener implements AsyncListener {
  2. String devcmdid;
  3. Subscriber subscriber;
  4. public AppAsyncListener(){} //后来加上这个问题解决
  5. public AppAsyncListener(Subscriber subscriber){
  6. // this.devcmdid=devcmdid;
  7. this.subscriber=subscriber;
  8. }

  

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. sql中的等于和不等于, '=' ,'!=','<>','is null'....

    不等于:<> ,!=,~= ,^= 这四个符号据说都可以在oracle中表示不等于,但是试了之后发现<> ,!= ,^=是可以的,~=不行,需要注意的是,只有<>是 ...

  2. hanlp使用自定义词典抽取关键词

    1.在data/dictionary/custom/路径下新建文件 myDict.txt.,添加新的单词,单词,词性,词频.并删除当前文件夹下的bin文件, 2.在hanlp配置文件中的CustomD ...

  3. Android开发 ViewConfiguration 用法

    ViewConfiguration 实例获取 ViewConfiguration viewConfiguration = ViewConfiguration.get(Context); 常用对象方法 ...

  4. Android 开发 使用javax.mail发送邮件。

    简介 sun公司开源的邮件发送工具. 依赖 implementation 'com.sun.mail:android-mail:1.6.0' implementation 'com.sun.mail: ...

  5. MySQL表数据的增删改查

    1.增 INSERT INTO tb VALUES(v1,v2,v3...); INSERT INTO tb (field1,field2...) VALUES(v1,v2...); INSERT I ...

  6. 接口性能测试(Jmeter)操作总结

    以前常用SoapUI来做接口的性能测试,这次用的Jmeter,对需由客户端根据时间戳等登录参数生成随机token值和印签值来发请求的系统,非它莫属了.下面就这次测试的难点和操作注意问题展开总结. ** ...

  7. git版本冲突解决

    1. 使用git log命令查看所有的历史版本,获取某个历史版本的id,假设查到历史版本的id是139dcfaa558e3276b30b6b2e5cbbb9c00bbdca96. git log 2. ...

  8. django之缓存的用法, 文件形式与 redis的基本使用

    django的缓存的用法讲解 1. django缓存: 缓存的机制出现主要是缓解了数据库的压力而存在的 2. 动态网站中,用户的请求都会去数据库中进行相应的操作,缓存的出现是提高了网站的并发量 3. ...

  9. node图片资源捉取

    开头先简单说明一下,因为网络资源上最多的资源就是图片,所以在这里也只简单的捉取了图片资源,至于其他的文档,音乐等我是没有试过的.所以暂时还是以图片为案例!!! Step1 首先我们需要加载我们需要的资 ...

  10. mysql 增加时间字段

    alter table sign_customer add COLUMN update_time timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE ...