网站技术架构优化-异步消息模块分离

       无意间看到ITEYE推荐《大型网站技术架构:核心原理与案例分析》,一口气看完推荐的两部分节选,

深有体会,结合鄙人所维护网站,谈下对网站技术架构优化的一些方案以及个人看法。

       首先,介绍重构前的网站架构模式:

1、同样应用系统功能采用分层,即应用层、服务层、数据层。这样分层结构,能使系统组织层次明显,以达到良好运作;

2、其次对应用层进行应用功能模块划分,并采用分布式部署(分WEB区、APP区:web区部署8个server,app区部署6个server),使用weblogic集群,提高系统可用性;

3、对页面前端的静态文件,如CSS、图片、js,采用CDN加速,提高系统响应能力;

       网站规模:截止目前,网站仍继续保持日注册量在2.5万,日登录量在55万次的访问次,网站用户数逼近2千5百万。

     以上的架构在应对如此规模的访问量上总显得力不从心,特别是在重构前APP上的server内存使用量总是逼近100%,长期处于高位。在对代码层面做优化后情况仍没有好转,最后决定对系统架构做重构,在对系统架构层面做了一番分析后,矛头直指系统的异步消息模块优化改造上。

     经分析,承担系统消息服务模块的app server即承担既是消息生产者又是消息消费者的角色,而且异步请求都是主流程中的实时性、重要性相对不是很高的,则为独立搭建消息消费者服务提供方向。

     搭建独立的消息服务app server,对保证APP端服务稳定,尽量减少关联系统异常造成的影响起到很大作用:

1、作为消息消费者,独立部署,即使消息阻塞,也能隔离影响

2、给关联系统提供接口服务,即使关联系统调用异常,也能隔离影响

3、可考虑作为后台定时任务服务器

网站的重构除了做架构消息队列分离外,前端也采用业界backboneJs前端MVC框架对页面做组织结构重构,后面有机会再另起文章写写相关内容。

        在这里将自己亲身经过的重构拿出来跟大家分享下,很多时候很难说得清楚哪种重构方案适合解决哪种类型的问题,只要是能使用较小的代价,能快速解决问题的,往这个方向设计的方案基本就差不多了。

 

下面附上当初重构遇到问题已经解决方案:

问题1:Invalid Subject

BeanCreationException: [Security:090398]Invalid Subject:jmstest

错误说明:远程调用验证不通过

原因分析:client JVM把错误的subject传递给了Remote JVM

报错代码:JmsMessengerDS jmsService = (JmsMessengerDS) context.getBean(BizContextNames.JMS_MESSENGER_DS);

解决方法:

新建类JmsJndiTemplate继承JndiTemplate并将subject缓存,在鉴权代码如下:

   Subject subject = ((JmsJndiTemplate) context.getBean("jndiJmsTemplate")).getSubject();

  try {

  Security.runAs(subject, new PrivilegedAction() {

  public Object run() {

  }

  });

 

  } catch (Exception ex) {}

 问题2:请求无法发到目标集群

 原因分析:app和message两个cluster会相互收到对方的组播包(同一个集群

中的instance才应该收到)。现在的情况是两个不同集群,提供不同服

务的集群能收到对方的组播包。app跟message在同一台主机,使用相同的IP跟端口。

所以需要将两个集群的端口换成不同。

如下是报错信息:

<2013-6-3 上午09时21分10秒 CST> <Error> <Cluster> <BEA-000110>

<Multicast socket receive error: java.io.OptionalDataException

java.io.OptionalDataException at

java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1285)

at java.io.ObjectInputStream.readObject(ObjectInputStream.java:322)

Atweblogic.cluster.MulticastManager.execute(MulticastManager.java:5

16) At

weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:224)

at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:183)> 

 

问题3:服务器依赖问题

 现象分析:原先设计逻辑在message server重启后,没有再重新lookup,造成APP server跟MESSAGE server之间存在依赖关系

解决方案:

新写JmsJndiObjectFactoryBean类替换JndiObjectFactoryBean类,当获取对象时判断jndiObject是否为空,如果为空,则再lookup一次

if (this.jndiObject == null) {

// 如果服务启动时,远程的JMS没有启动,afterPropertiesSet()调用会失败,jndiObject为null,

// 在这里补调一次

try {

afterPropertiesSet();

} catch (IllegalArgumentException e) {

logger.warn("远程服务可能还没有启动", e);

} catch (NamingException e) {

logger.warn("远程服务可能还没有启动", e);

}

}

return this.jndiObject;

 

问题4:请求还是落在本地集群

 

} 现象描述:当APP跟MESSAGE改成使用不同的端口,APP发出的请求还是落在MESSAGE上。

原因分析:可以从MESSAGE的console上分析看到,实际上APP中的消息已经发送到MESSAGE上JMS SERVER上,这是由于MESSAGE上的MessageDrivenBean获取请求解析对应的ActionID,而对ActionID的调用是通过PafaAC完成,此时需要把MESSAGE上的properties文件中关于PafaAC的T3地址配置成MESSAGE所在的服务地址即可

 

问题5:JMS重启后链接失效

现象描述:

当message server重启后,再发起交易会报发送失败错误:MessengerDS send message failure

原因分析:

QueueConnection作为单例,每个client和jms server只建立一次连接(start一次),相当socket通道打通,就一致不关闭

当jms server挂或者重启的时候,该连接则不可用了

所以在queueConnection.createQueueSession创建session时候,发现connection不可用,关闭以前的connection并设置connection为null,

下次请求时候,重新获取connection并重新start

// 发送之前进行初始化,已支持延迟加载

synchronized (this) {

if (queueConnection == null) {

try {

queueConnection = queueConnectionFactory

.createQueueConnection();

queueConnection.start();

} catch (JMSException jmsEx) {

queueConnection = null;

throw new RuntimeException(

START_CONNECTION_FAILURE);

}

}

 

}

 

 改造上线后server内容消耗情况,相比之前有明显回落(7月分上线)

 

 

猜你喜欢

转载自willwen.iteye.com/blog/1980945