pushlet一个很坑爹的地方。。。
来源:互联网 发布:矩阵微分方程 编辑:程序博客网 时间:2024/05/21 09:16
上回书说道pushlet的集群解决方案,但是节点是全部起的,没有什么问题。
这礼拜线上节点先起了两个,之后起得节点全pushlet全失效了,觉得很不可思议,又再次探究了一下。
HTTP ERROR 503Problem accessing /pushlet.srv. Reason: javax.servlet.ServletException: Failed to initialize Pushlet framework java.lang.NullPointerException
这是当时爆的一个异常信息,是前端请求/pushlet.srv时的异常。
/pushlet.srv就像配置servlet一样是配置在web.xml里面的,于是就去找pushlet的处理类
/** <a href="http://www.cpupk.com/decompiler">Eclipse Class Decompiler</a> plugin, Copyright (c) 2017 Chen Chao. **//* */ package nl.justobjects.pushlet.servlet;/* *//* */ import java.io.IOException;/* */ import java.io.InputStreamReader;/* */ import java.util.Enumeration;/* */ import javax.servlet.ServletContext;/* */ import javax.servlet.ServletException;/* */ import javax.servlet.http.HttpServlet;/* */ import javax.servlet.http.HttpServletRequest;/* */ import javax.servlet.http.HttpServletResponse;/* */ import nl.justobjects.pushlet.Version;/* */ import nl.justobjects.pushlet.core.Command;/* */ import nl.justobjects.pushlet.core.Config;/* */ import nl.justobjects.pushlet.core.Controller;/* */ import nl.justobjects.pushlet.core.Dispatcher;/* */ import nl.justobjects.pushlet.core.Event;/* */ import nl.justobjects.pushlet.core.EventParser;/* */ import nl.justobjects.pushlet.core.EventSourceManager;/* */ import nl.justobjects.pushlet.core.Protocol;/* */ import nl.justobjects.pushlet.core.Session;/* */ import nl.justobjects.pushlet.core.SessionManager;/* */ import nl.justobjects.pushlet.util.Log;/* */ import nl.justobjects.pushlet.util.Servlets;/* *//* */ public class Pushlet extends HttpServlet/* */ implements Protocol/* */ {/* */ public void init()/* */ throws ServletException/* */ {/* */ try/* */ {/* 31 */ String webInfPath = getServletContext().getRealPath("/") + "/WEB-INF";/* 32 */ Config.load(webInfPath);/* *//* 34 */ Log.init();/* *//* 37 */ Log.info("init() Pushlet Webapp - version=" + Version.SOFTWARE_VERSION + " built=" + Version.BUILD_DATE);/* *//* 40 */ SessionManager.getInstance().start();/* *//* 43 */ Dispatcher.getInstance().start();/* *//* 46 */ if (Config.getBoolProperty("sources.activate"))/* 47 */ EventSourceManager.start(webInfPath);/* */ else/* 49 */ Log.info("Not starting local event sources");/* */ }/* */ catch (Throwable t) {/* 52 */ throw new ServletException("Failed to initialize Pushlet framework " + t, t);/* */ }/* */ }/* *//* */ public void destroy() {/* 57 */ Log.info("destroy(): Exit Pushlet webapp");/* *//* 59 */ if (Config.getBoolProperty("sources.activate"))/* */ {/* 61 */ EventSourceManager.stop();/* */ }/* */ elseLog.info("No local event sources to stop");/* *//* 67 */ Dispatcher.getInstance().stop();/* *//* 70 */ SessionManager.getInstance().stop();/* */ }/* *//* */ public void doGet(HttpServletRequest request, HttpServletResponse response)/* */ throws ServletException, IOException/* */ {/* 77 */ Event event = null;/* */ Enumeration e;/* */ try/* */ {/* 81 */ String eventType = Servlets.getParameter(request, "p_event");/* *//* 84 */ if (eventType == null) {/* 85 */ Log.warn("Pushlet.doGet(): bad request, no event specified");/* 86 */ response.sendError(400, "No eventType specified");/* 87 */ return;/* */ }/* *//* 91 */ event = new Event(eventType);/* 92 */ for (e = request.getParameterNames(); e.hasMoreElements();) {/* 93 */ String nextAttribute = (String) e.nextElement();/* 94 */ event.setField(nextAttribute, request.getParameter(nextAttribute));/* */ }/* *//* */ }/* */ catch (Throwable t)/* */ {/* 100 */ Log.warn("Pushlet: Error creating event in doGet(): ", t);/* 101 */ response.setStatus(400);/* 102 */ return;/* */ }/* *//* 106 */ doRequest(event, request, response);/* */ }/* *//* */ public void doPost(HttpServletRequest request, HttpServletResponse response)/* */ throws ServletException, IOException/* */ {/* 114 */ Event event = null;/* */ try/* */ {/* 117 */ event = EventParser.parse(new InputStreamReader(request.getInputStream()));/* *//* 120 */ if (event.getEventType() == null) {/* 121 */ Log.warn("Pushlet.doPost(): bad request, no event specified");/* 122 */ response.sendError(400, "No eventType specified");/* 123 */ return;/* */ }/* *//* */ }/* */ catch (Throwable t)/* */ {/* 129 */ Log.warn("Pushlet: Error creating event in doPost(): ", t);/* 130 */ response.setStatus(400);/* 131 */ return;/* */ }/* *//* 135 */ doRequest(event, request, response);/* */ }/* *//* */ protected void doRequest(Event anEvent, HttpServletRequest request, HttpServletResponse response)/* */ {/* 144 */ String eventType = anEvent.getEventType();/* */ try/* */ {/* 149 */ Session session = null;/* 150 */ if (eventType.startsWith("join"))/* */ {/* 152 */ session = SessionManager.getInstance().createSession(anEvent);/* *//* 154 */ String userAgent = request.getHeader("User-Agent");/* 155 */ if (userAgent != null)/* 156 */ userAgent = userAgent.toLowerCase();/* */ else {/* 158 */ userAgent = "unknown";/* */ }/* 160 */ session.setUserAgent(userAgent);/* */ }/* */ else/* */ {/* 166 */ String id = anEvent.getField("p_id");/* *//* 169 */ if (id == null) {/* 170 */ response.sendError(400, "No id specified");/* 171 */ Log.warn("Pushlet: bad request, no id specified event=" + eventType);/* 172 */ return;/* */ }/* *//* 176 */ session = SessionManager.getInstance().getSession(id);/* *//* 179 */ if (session == null) {/* 180 */ response.sendError(400, "Invalid or expired id: " + id);/* 181 */ Log.warn("Pushlet: bad request, no session found id=" + id + " event=" + eventType);/* 182 */ return;/* */ }/* *//* */ }/* *//* 190 */ Command command = Command.create(session, anEvent, request, response);/* 191 */ session.getController().doCommand(command);/* */ }/* */ catch (Throwable t) {/* 194 */ Log.warn("Pushlet: Exception in doRequest() event=" + eventType, t);/* 195 */ t.printStackTrace();/* 196 */ response.setStatus(500);/* */ }/* */ }/* */ }
反编译的代码,凑活着看吧。
那个异常是在pushlet的init方法里面爆的,很显然上面一个地方爆空指针了。
一个最直接的疑点就是为啥会报异常。
再仔细想想,我web.xml中的配置
<servlet> <servlet-name>pushlet</servlet-name> <servlet-class>nl.justobjects.pushlet.servlet.Pushlet</servlet-class> <load-on-startup>3</load-on-startup> </servlet>
答案显然只有一个,初始化失败了。
也就是说第一次调用init方法的时候就报错了
去看线上日志也没有我想要的答案,但至少确定,是在初始化的时候发生的异常。
通过断点调试(class文件调试的话需要插件)我们发现,后面请求中爆的空指针异常来自
SessionManager.getInstance().start();getInstance返回了一个null
让我们来看看这个getInstance方法究竟是个啥
public static SessionManager getInstance() { return instance; }一开始我看到这里的时候我是惊呆了的。。。我还以为是一个复杂的静态工厂方法
既然getInstance只是返回一个实例,那真正的初始化是在哪儿的呢
我们来到SessionManager的静态块
try/* */ {/* 33 */ instance = (SessionManager) Config.getClass("sessionmanager.class", "nl.justobjects.pushlet.core.SessionManager").newInstance();/* 34 */ Log.info("SessionManager created className=" + instance.getClass());/* */ } catch (Throwable t) {/* 36 */ Log.fatal("Cannot instantiate SessionManager from config", t);/* */ }/* */ }
原来实例化发生在静态块里面。
很明确,应该是执行静态块的时候发什么了什么异常,导致session无法正常实例化,因为静态块只在类加载的时候执行一次,我们的instance就永远只能是null了
那究竟又是发生么什么导致静态块执行失败了呢?
我们到Config类里继续看
public static Class getClass(String aClassNameProp, String aDefault)/* */ throws PushletException/* */ {/* 50 */ String clazz = (aDefault == null)? getProperty(aClassNameProp): getProperty(aClassNameProp, aDefault);/* */ try/* */ {/* 53 */ return Class.forName(clazz);/* */ }/* */ catch (ClassNotFoundException t) {/* 56 */ throw new PushletException("Cannot find class for " + aClassNameProp + "=" + clazz, t);/* */ }/* */ }public static String getProperty(String aName, String aDefault) {/* 88 */ return properties.getProperty(aName, aDefault);/* */ }/* */public static String getProperty(String aName) {/* 92 */ String value = properties.getProperty(aName);/* 93 */ if (value == null) {/* 94 */ throw new IllegalArgumentException("Unknown property: " + aName);/* */ }/* 96 */ return value;/* */ }
public static void load(String aDirPath)/* */ {/* */ try/* */ {/* 67 */ Log.info("Config: loading pushlet.properties from classpath");/* 68 */ properties = Sys.loadPropertiesResource("pushlet.properties");/* */ }/* */ catch (Throwable t) {/* 71 */ String filePath = aDirPath + File.separator + "pushlet.properties";/* 72 */ Log.info("Config: cannot load pushlet.properties from classpath, will try from " + filePath);/* */ try/* */ {/* 75 */ properties = Sys.loadPropertiesFile(filePath);/* */ } catch (Throwable t2) {/* 77 */ Log.fatal("Config: cannot load properties file from " + filePath, t);/* *//* 80 */ return;/* */ }/* */ }/* *//* 84 */ Log.info("Config: loaded values=" + properties);/* */ }我们发现getClass方法进去会调用一个getProperty方法,我们遇到了一个可能会爆空指针的点
properties.getProperty(aName, aDefault)如果properties为空的话就会报错,
而这个properties的赋值是在load方法里面的,只要保证load方法在getProperty方法之前被调用就不会爆空指针了。
那load方法又是什么时候被调用的呢,让我们回到pushlet类的init方法。
先调load在初始化session,没毛病啊,这流程下来没有任何问题啊!
怎么可能执行session静态块的时候load方法还没执行啊!
一开始我也想不通,后来看了点基础知识。。web容器的初始化过程
图片网上copy的。。。
spring容器的初始化在servlet初始化之前。也就是说这个加载顺序完完全全具有发生调用session静态块在load之前,即在load之前调用到了session的条件
那么问题来了,到底是啥调用了session呢。。。
然后一想就想到了。。。没错,就是那个mq的消费者。
spring容器初始化完成,mq监听容器也完成了初始化,mq消费者开始消费消息。
有人可能会问了,应用刚起来,哪儿来的消息啊。。关键是为了做到集群,我确实用到了mq订阅模式。
因为第二次发布的时候,是先起了两个节点,中间可能有人发了mq,到后面节点启动的时候,每个节点在spring容器一初始化好就开始了消费。。。。
后面节点启动时会较pushlet初始化前开始消费,这一点都认同的吧。。
那么问题来了,我消费者里写了啥呢。。。说来惭愧,就是利用pushlet发送消息的方法,也就是说,确确实实在pushlet初始化前调用了session。。。。
问题知道出在哪儿解决就简单了。我的做法是在调用pushlet发消息的工具类里写个静态块,初始化手动pushlet。。。。
- pushlet一个很坑爹的地方。。。
- 一个不成熟的pushlet集群解决方案
- Pushlet的Ajax-pushlet-client.js分析
- Pushlet的Ajax-pushlet-client.js分析
- 挺好的一个地方
- 一个安静的地方
- 挺好的一个地方
- pushlet的简单使用
- Pushlet的工作原理
- pushlet的一般流程
- Pushlet
- pushlet
- pushlet
- 一个js的好地方
- Twistd一个诡异的地方
- filenet 一个问答的地方
- 一个介绍驱动的地方
- 一个没人认识的地方
- Android Activity之间的传值示例
- MyBatis模糊查询的三种方式
- docker常用命令
- Java + Scanner + next
- [App] NTP + Keepalived
- pushlet一个很坑爹的地方。。。
- 数据流图概述
- SpringMVC整合swagger
- 一些基础知识
- 一条简单的SQL的加锁实现分析
- Jmeter接口压力测试(先登录再测接口)
- java8笔记_1
- NOIP 模拟题 简单题 随便做 题解与总结
- iOS中关键字copy与mutableCopy的详解,看我你就都懂了