pushlet一个很坑爹的地方。。。

上回书说道pushlet的集群解决方案,但是节点是全部起的,没有什么问题。

这礼拜线上节点先起了两个,之后起得节点全pushlet全失效了,觉得很不可思议,又再次探究了一下。

HTTP ERROR 503

Problem 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();
			/*     */ }
		/*     */ else
			Log.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> 


pushlet的初始化已改发生在应用初始化的时候,并且init方法只会被调用一遍,为什么只会被调用一遍的init方法中会持续报错呢?

答案显然只有一个,初始化失败了。

也就是说第一次调用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。。。。



出现这个错误的原因是在导入seaborn包时,无法从typing模块中导入名为'Protocol'的对象。 解决这个问题的方法有以下几种: 1. 检查你的Python版本是否符合seaborn包的要求,如果不符合,尝试更新Python版本。 2. 检查你的环境中是否安装了typing_extensions包,如果没有安装,可以使用以下命令安装:pip install typing_extensions。 3. 如果你使用的是Python 3.8版本以下的版本,你可以尝试使用typing_extensions包来代替typing模块来解决该问题。 4. 检查你的代码是否正确导入了seaborn包,并且没有其他导入错误。 5. 如果以上方法都无法解决问题,可以尝试在你的代码中使用其他的可替代包或者更新seaborn包的版本来解决该问题。 总结: 出现ImportError: cannot import name 'Protocol' from 'typing'错误的原因可能是由于Python版本不兼容、缺少typing_extensions包或者导入错误等原因造成的。可以根据具体情况尝试上述方法来解决该问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [ImportError: cannot import name ‘Literal‘ from ‘typing‘ (D:\Anaconda\envs\tensorflow\lib\typing....](https://blog.youkuaiyun.com/yuhaix/article/details/124528628)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值