数据不可变 与 方法副作用

一、问题

之前的文章提到公司要求项目组做所谓的网络标准化,在 Spring MVC 的 Controller 层与 Service 层之间拆分成两个项目来部署,中间通过 Hessian 远程调用来通信。

这样可就有大问题了:

Controller 调用 Service 的方法时,传递一个 POJO 作为参数,Service 里修改了这个参数的某个属性,最终返还 Controller,Controller 再根据这个被修改的属性继续处理;

Service 调用的深层方法通过 Spring MVC 通过的 org.springframework.web.context.request.RequestContextHolder 获取了 HttpSession 对象,然后设置了一些状态值。

在同一 JVM 里面,上面的都没有问题;但按标准化的要求,部署在两个 JVM 上时,Service 层对 Controller 层传入的 POJO 所做的修改在 Controller 层就不可见了。

这些问题是实现这个标准化的最大困难,几乎所有代码都要检查是否存在上面的情况。

二、如果上天再给一次机会。。。

上面的问题可以说是“参数的可变性”和“方法副作用”导致的,“参数” 是个特殊的东西,它穿过两个方法之间调用时形成的边界。如果“参数”的属性在被调用的方法里被修改了,可以说这个方法是有副作用的。

如果上天再给一次机会:把所有方法都实现为无副作用,参数封装为不可变的对象,方法的所有效果都通过返回值来体现。这样每一个方法都可以变成一个远程服务。

之前在微博上看到一个说他的架构宣言是:Passing Message Everywhere。有点感触。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

不恰当使用线程池处理 MQ 消息引起的故障

现状

业务部门反应网站访问特别慢,负责运维监控的同事说MQ消息队列积压了,中间件的说应用服务器内存占用很高,GC 一直回收不了内存,GC 线程占了近 100% 的 CPU,其他的基本上都在等待,数据库很正常,完全没压力。没啥办法,线程、堆 dump 出来后,重启吧,然后应用又正常了。

分析

这种故障之前其实也碰到过了,分析了当时 dump 出来的堆后发现,处理 MQ 消息的线程池的队列长度达百万级别,占用了超过 1.3G 内存,这些内存都是没法回收的。

程序的实现目前是这样的:关联系统把消息推送到 MQ 上,我们再从 MQ 上拉消息下来处理;每种类型的消息都有一个线程负责从 MQ 上拉消息,拉下来后封装成线程池的任务提交给相应的线程池去执行。代码可以简化为:

package net.coderbee.mq.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MQListener {
     public ExecutorService executor = Executors.newFixedThreadPool(8);

     public void onMessage(final Object message) {
          executor.execute(new Runnable() {
               @Override
               public void run() {
                    // 耗时且复杂的消息处理逻辑
                    complicateHanlde(message);
               }
          });
     }

     private void complicateHanlde(Object message) {
     }
}

继续阅读

踩坑之 双机热备切换

这个问题还是去年在上家公司时碰到的,当时领导要我优化一个后台系统,它的核心逻辑就是接收客户端上传文件,把解析出的信息入库,再把文件上传到一个 FTP 服务器。

搞完之后总得部署到生产环境,因为这是个接手系统,只有源码,也不知道部署结构,只告诉我有两个结点,部署上去之后验证下没问题就行了。

我当时是准备一个一个结点部署的,在 serverA 上更新完成,验证后发现没有问题,然后准备更新 serverB 时,却发现突然又不行了,当时好像是请求没收到还是咋,具体不记得了,只有一个人在那里,折腾很久,最后联系运维人员,人家把双机热备的切换机制给停了才行。

事后分析认为是双机热备切换导致的,主机更新后一开始正常是因为还没有切换到备机上,而主机上的服务很快就起来了,请求仍然是到了主机上,所以一开始验证是可以的,但后来被切换到备机上去了,备机上跑的是旧的程序,请求不会到主机上,在主机上看是怎么也不会知道原因的,还以为程序有问题,当时也不会抓包分析,人都慌了,哈哈。

现在的 web 系统为了达到高可用,都会进行双机热备,就是两台服务器 serverA 和 serverB 上的部署是完全一样的,它们都配置了同一个虚拟 IP,但只有一台机器对外提供服务。如果这台提供服务的机器出故障了,双机热备程序就会自动把请求切换到另一台服务器上,从而保证服务是可用的。但这个切换是有延迟的,取决于双机热备的实现机制和配置。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

故障之 CDN 使用问题

故障问题

最近公司的一个系统用上了 CDN 服务,用户反应快多了,那个系统主要是静态资源展示和一些可公开的数据查询服务。另一个项目组的系统A看了也要上,不过这个A的后台代码是放在我所在项目组M里,是同一个应用,都在同一个域名 main.com 下。网络组把 main.com 的 DNS 解析到 CDN 服务器上去之后,访问也是快了,但是用户反应数据乱了,而且有些服务没法用了,后台统计特定地区用户的访问也不准了,因为所有请求基本上都是从几个 IP 里过来的。

问题原因

稍微对 CDN 有所了解的人看完问题描述之后都会发现问题所在:由于只有一个域名 main.com ,且被解析到 CDN 服务器上,CDN 不仅分发静态资源,还变成代理服务器了,所有动态请求、数据都要经过 CDN 服务器。

很明显,这是错误使用 CDN 带来的问题。这带来的问题不仅是上面描述的问题,更大的业务安全问题:当所有业务数据都经过 CDN 服务器时,CDN 厂商就有了那些业务数据,这相当于把银行卡和密码交给一个中间人,要存钱、取钱的时候就让这个中间人去 ATM/银行 处理,再给自己反馈结果。有这么值得信赖的中间人吗??

继续阅读

我眼中的 性能劣化、优化的曲线

性能劣化

性能劣化的图形

随着代码写得越来越烂,程序运行时 数据库操作更多、IO 阻塞等待跟过、不必要的对象创建、GC 回收更频繁,线程的上下文切换也更多,开销越来越多,所有因素综合起来,程序运行更慢,响应延迟加大。

当到达临界点的时候,压垮骆驼的最后一根稻草出现了,系统直接崩溃。

坏东西、副作用是会累积的。

继续阅读

2014-03 杂记

首先要说的是:一转眼,这个博客就启动一年了,今天续费,还是14.99$一年的,人民币93.27¥,BudgetVM 现在支持支付宝付款,更方便了,有意者请点链接,这是广告。

今年的动作太大了,换城市、换工作、旅游,都值得记录下。

换城市

在广州的时候,总感觉空气不好,天空太灰霾,所以想换个沿海的空气好点的。一般也就是珠海或深圳了,珠海的IT企业不如深圳多,城市也小,所以只有选择深圳了。深圳当然也有灰霾,但对于我等屌丝,算无路可逃了吧。

继续阅读

我的学习工具

今天跟一个同学聊天,发现很多很好用的工具他都还不知道,我周围的绝大多数同事也不知道。虽然我用的工具不多,用得也不是很深入,但对我的帮助已经非常大,所以写的文字介绍。

浏览器

首先要说的是浏览器,因为上网大多数都是在浏览器里进行的。我用的是Firefox,因为有很多插件可用。Chrome也不错,只是习惯了火狐。

火狐的一个很重的功能是账号同步功能,可以同步的最重要的两个内容:已安装插件、书签。也就是在办公室的火狐上安装了一个插件或者收藏了一个网址,回家后也有了,不用再搞一遍。换了一台机或重装了系统,登录火狐账号后,一切又回来了。这也是云的作用吧!

浏览器插件

现在用的浏览器插件主要有:

  • AutoProxy:翻墙,你懂的!
  • pocket:以前好像叫“read it later”。可以把网页的内容收藏起来、归档,以后想看的时候再看。排版不错,对有些网站还会去掉一些网页周边的广告,只保留内容。
  • 印象笔记插件:可以把一个网页、图片、网址减藏到印象笔记里。在浏览器里用google搜索时,还可以搜笔记里的内容。
  • DownThemAll:下载,断点续传、分块下载。
  • 花瓣插件:微博上很多有价值的文章是图片形式的长微博,保存到花瓣方便查找。
  • 微博极简:过滤微博的,很多信息比如广告是不想看到的。

继续阅读

很佩服那些搞安全的人

今晚看了下博客的4xx访问日志,发现了这么几条有趣的记录:
scriptboy-scan

里面的路径:/testcopy-moved.txt, /testcopy.txt, /abctest/testcopy.txt, /\x22 + cnzz_protocol + \x22s22.cnzz.com/z_stat.php%3Fid%3D1000033072,这些路径都不是我的博客里的常规地址,怎么突然有人会去扫描这些路径呢?以前发现的一般都是扫描phpAdmin的。

再想想,记起来了,前面三个路径是我在前一篇博客里介绍我写的Dropbox Go SDK里的例子里有提到,而cnzz那个估计是人家发现我的站点使用cnzz的统计服务。

哈哈,真心佩服搞安全的人的耐心与细心!虽然前三个路径是Dropbox里的虚拟路径。

之前写博客搭建笔记和博客维护脚本时,贴了一些脚本,但没有把没有把所有细节贴出来,本来是想贴的,觉得太麻烦,也算是侥幸了。服务器的具体配置、细节还真不能乱透露,特别要小心无意中泄漏了。

最后,如果哪个大神已经拿下或准备拿下这个VPS时,我只能说:此地真无银三百两!


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

Nginx与Apache 之间的 HTTP 400错误

魅影危机

有同事今天碰到的一个问题:用户访问公网的Nginx服务器,Nginx再把用户请求转发到内网的Tomcat服务器的80端口,现在内网Tomcat服务器的应用想拆分,不同应用用不同的Tomcat来运行,这样就在内网配置了一台Apache(不方便改Nginx的转发规则,那个Nginx是别人弄的),根据应用的url转发到对应的Tomcat。出现的问题是:从公网访问Nginx出现HTTP 400错误,从Apache访问日志可以看到,请求是过来了,但没转发给后端的Tomcat,Apache直接返回400;如果Nginx直接转发到Tomcat也是可以的,从内网通过Apache访问Tomcat也是可以的。

所以问题应该是Nginx到Apache的请求有问题。这个同事知道我以前配置过Nginx,所以找我要Nginx转发到Tomcat的配置。

在本地测试机上,Nginx的转发配置都没问题,从 Nginx --> Tomcat 或者 Nginx --> Apache --> Tomcat 都没问题。

没辙,我只好回自己工位去google。
继续阅读