一个不成熟的pushlet集群解决方案

最近项目中要用到pushlet,做一个基于长连接的报警提醒。

之前项目中也有类似的提醒,但是是用轮询解决的,5分钟请求一次,这次的需求提醒及时性要求比较高,通过提高轮询频率的方法显然不合适,网上基于长连接的解决方案也有不少,因为pushlet属于轻量,故选择它。

废话不多先上配置

后端jar包,页面引js

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>  
   <servlet-mapping>  
        <servlet-name>pushlet</servlet-name>  
        <url-pattern>/pushlet.srv</url-pattern>  
    </servlet-mapping>

消息推送方法
	public static void multicast(String eventKey, String fieldKey, String jsonMsg) {
		Event event = Event.createDataEvent(eventKey);
		try {
			event.setField(fieldKey, URLEncoder.encode(jsonMsg, "UTF-8"));
			Dispatcher.getInstance().multicast(event);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

页面

PL._init();
PL.joinListen('2333'); 
function onData(event) {
	result=JSON.parse(decodeURIComponent(event.get('6666'))); 
	console.log(result);
}

2333对应后端方法里的eventKey,6666对应fieldKey,result输出的就是jsonMsg

这里有几点注意的地方
如果页面是jsp的话不用调init方法,其他的话是要调的

js里面的init方法里会有这么一段

PL.pushletURL = PL._getWebRoot() + 'pushlet.srv';

	_getWebRoot: function() {
		/** Return directory of this relative to document URL. */
		if (PL.webRoot != null) {
			return PL.webRoot;
		}
		//derive the baseDir value by looking for the script tag that loaded this file
		var head = document.getElementsByTagName('head')[0];
		var nodes = head.childNodes;
		for (var i = 0; i < nodes.length; ++i) {
			var src = nodes.item(i).src;
			if (src) {
				var index = src.indexOf("ajax-pushlet-client.js");
				if (index >= 0) {
					index = src.indexOf("lib");
					PL.webRoot = src.substring(0, index);
					break;
				}
			}
		}
		return PL.webRoot;
	}
所以最后的url会是一个很纠结的url(因为存放js的路径是千奇百怪的),我们要做的就是干脆在init方法里写死,对应web.xml里的路径,即/pushlet.srv


这样的话一个后端往页面推数据的模型就完成了,pushlet还能做到前端向后端推数据(基于长连接,为啥我不用ajax交互。。。)等,业务不涉及也没做深入了解


开发环境ok的,可到了线上就不行了,因为线上部署了不同的节点

举个例子,线上部署着1,2两个节点,用户a长连接着1,此时用户b,向节点2发送了一个请求,触发了pushlet广播消息,2向所有长连接的用户推送了消息,显然此时的用户a是收不到任何消息推送的,因为用户a长连接着节点1而不是节点1和节点2。

知道问题所在接下来的就好办了,首先去寻找pushlet的集群解决方案,很可惜,网上并没有现成的。

时间缘故并没有深入了解pushlet源码,利用jms来解决

大致思路讲一下,就是在原本触发pushlet消息推送的地方改成用activemq向所有应用节点发消息,消息内容包括pushlet所要发的内容,应用对应的消费者消费时再用pushlet进行消息推送。中间加了一层mq的话,虽然时效性有所下降,但还是能满足业务需求的。

配置的话就是普通的spring activemq配置。

要注意的是用广播模式(就是订阅模式),生产者跟消费者写在一起。

下面上配置跟代码

application配置,生产者跟消费者

	<!-- 本级任务监听 -->
	<bean id="pushletTaskConnectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">
		<property name="brokerURL" value="${pushletTaskMQAddress}" />
	</bean>

	<bean id="pushletTaskQueueDestination" class="org.apache.activemq.command.ActiveMQTopic">
		<!-- 设置消息队列的名字 -->
		<constructor-arg index="0" value="pushletTaskQueue" />
	</bean>
	
	<bean id="pushletTaskMessageConvert"
		class="com.hahaha.PushletTaskMsgConventer" />

	<bean id="pushletTaskQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
		<property name="connectionFactory" ref="pushletTaskConnectionFactory" />
		<property name="defaultDestination" ref="pushletTaskQueueDestination" />
		<property name="messageConverter" ref="pushletTaskMessageConvert" />
		<property name="pubSubDomain" value="true" />
	</bean>

	
	<bean id="pushletTaskMessageListener"
		class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
		<constructor-arg>
			<bean class="com.hahaha.PushletTaskMessageListener" />
		</constructor-arg>
		<property name="defaultListenerMethod" value="onMessage" />
		<property name="messageConverter" ref="pushletTaskMessageConvert" />
	</bean>

	<bean id="pushletTaskQueueListenerContainer"  lazy-init="false" 
		class="org.springframework.jms.listener.DefaultMessageListenerContainer">
		<property name="connectionFactory" ref="pushletTaskConnectionFactory" />
		<property name="destination" ref="pushletTaskQueueDestination" />
		<property name="messageListener" ref="pushletTaskMessageListener" />
		<property name="pubSubDomain" value="true"/>
	</bean>

生产者代码的话就是发送个消息

消费者监听代码

public class PushletTaskMessageListener implements MessageListener {

	private static final Logger logger = LoggerFactory.getLogger(PushletTaskMessageListener.class);
	
	
	@Override
	public void onMessage(Message message) {
		MapMessage mapMsg = (MapMessage) message;
		try {
			String evenKey = mapMsg.getString("evenKey");
			String fieldKey = mapMsg.getString("fieldKey");
			String jsonMsg = mapMsg.getString("jsonMsg");
			PushLetUtil.multicast(evenKey, fieldKey, jsonMsg);
		} catch (JMSException e) {
			logger.error("消息推送失败:" + mapMsg.toString(), e);
		}
	}
}

写完收工~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值