Volatile Vs Static in JAVA

http://malalanayake.wordpress.com/2013/09/12/volatile-vs-static-in-java/

volatile能保证原子性和可见性.对static field的内存模型认识不足导致有些概念模糊了.内存结构见图:

101 个 MySQL 的调优贴士

http://www.oschina.net/translate/101-tips-to-mysql-tuning-and-optimization?print

看数据库相关的文章看到”双缓存”“问题.数据库服务把OS cache打开会导致无意义的”预读取”(操作系统层面的随机或者顺序读取文件)和”后写入”(批处理),而且会导致”双缓冲”和昂贵的内存拷贝.”双缓冲”浪费了系统内存,并且浪费了额外的资源处理时间(读:从磁盘到os cache到DBMS cache;写:从DBMS cache 到OS cache 到磁盘).

搜索双缓存相关的东东,把这篇文章搜索出来了.有几点可以说说(数据库管理层面的I DONT CARE!).

74.为了 避免在更新前SELECT,使用INSERT ON DUPLICATE KEY或者INSERT IGNORE ,不要用UPDATE去实现。 (这深有体会,以前处于二逼阶段写的存储过程由17行变成了7行,简单依赖啊,减少了查询,减少了并发时的异常处理)
81.使用DROP TABLE,CREATE TABLE DELETE FROM从表中删除所有数据。 (为毛不用truncate?)
95. 为了更快的进行导入,在导入时临时禁用外键约束。 
96. 为了更快的进行导入,在导入时临时禁用唯一性检测。

zookeeper 分布式锁的实现

http://bohr.me/zookeeper-dev-ops/

给自己写的zookeeper相关的分析添加一个链接地址.

Spring Data Repositories - A Deep Dive

https://github.com/olivergierke/repositories-deepdive

http://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

最近在看jpa,spring对jpa的支持确实比较犀利.Querydsl结合APT可以写出类型安全,易读的查询语句.不知道性能如何?

@Test
public void testQuerydsl() throws Exception {
	QTask $ = QTask.task;
	BooleanExpression titleStartWith = $.title.startsWith("Study");
	BooleanExpression descEndWith = $.description.endsWith("org/");
	
	Iterable<Task> result = taskDao.findAll(titleStartWith.and(descEndWith));
	for (Task task : result) {
		System.out.println(task);
	}
} 上面这段代码最后生成的sql如下:

select
    id,raw_add_time,raw_update_time,description,title,user_id 
from
    t_task  
where
    (title like ? escape '!') 
    and (description like ? escape '!')	

ROCA Resource-oriented Client Architecture

A collection of simple recommendations for decent Web application frontends. 可以参考下,

加盐密码哈希:如何正确使用

http://blog.jobbole.com/61872/

The Enemy Knows The System.看了这篇文章,感觉我们做的东西太少了,对于密码的重视程度还不够,简单总结下:

  1. 永远不要告诉用户到底是用户名错了,还是密码错了
  2. 不要使用短盐值和盐值重复(明白了某开源项目为什么用户表里有个salt字段)
  3. 盐值应该使用基于加密的伪随机数生成器来生成,java里面用java.security.SecureRandom
  4. 使用慢哈希函数,java里面用PBKDF2WithHmacSHA1(需要权衡,太慢了会把登录做成DDOS攻击)
  5. 不要使用过时的hash算法,比如MD5或SHA1
  6. 密码重置时令牌应该要有有效期,并且重置时需要生成新的盐值.
  7. 不要过于频繁地强制你的用户修改密码,最多6个月1次(呵呵,bops)

这些用上基本上就够了.附带赠送一个能够破解任何8位及以下长度MD5值的彩虹表

Java中的CopyOnWrite容器

http://coolshell.cn/articles/11175.html

以前只是把CopyOnWrite用于拦截器这些场景,看来用于黑/白名单也挺适合的.特别是CopyOnWriteMap,很适合读写比例很大的场景.

不过原文中提到的内存占用问题,我不赞同此问题,map本身的数据结构是Entry,这个Entry会上百M?Arrays.copyOfjava.util.HashMap#HashMap(java.util.Map<? extends K,? extends V>)都不是深拷贝.

5 Techniques to Improve Your Server Logging

http://www.takipiblog.com/2014/02/27/5-techniques-to-improve-your-server-logging/

文章提到5个处理服务器端日志的细节.值得分享下:

  1. Hello my (Thread) name is

    给线程取一个合理的名字.在开发云消息中间件时,考虑到便于跟踪业务执行轨迹,统一用msgid来作为线程的名字.

    现在想起来还是比较土,当时不知道有MDC这个概念(2012年看到这个概念还很兴奋).现在我写的系统中,基本上都引入了MDC这个东东,方便自己定位问题.

  2. Distributed identifiers

    每个业务请求需要一个唯一的业务标识.这个必须有,写了ID就是来干这事的.我很赞同作者的观点, every inbound operation going into your system should have a unique ID which will travel with it until it’s completed. ,我们的gid改造还没有结束,哈哈.

  3. Dont text + drive. Don’t log + loop

    在循环里面处理日志要小心,作者举的例子是加了一个打印日志次数的限制.在while循环里处理东东都要小心,最近给某项目分析定时任务没有执行的问题,发现循环中插入数据库,结果悲剧了,死循环一直插,数据库表示好伤啊.

  4. Uncaught handlers

    给线程加上未捕获的异常处理器.

    在和peigen讨论摘要日志时,提到了要加这个东东,结果真的给忘了.哎,真的老了么?忘事太容易了.真得注意下防御性编程了.openapi使用摘要日志api有问题,导致空指针异常,我在代码中加了一行error日志由于api使用不当导致线程本地变量DL被清空,请仔细检查您的代码.本次操作会重新生成DL对象.但是还有个洞没有塞住.万一使用SDL时,没有在final block中调用SDL.end(),最终会导致OOM.在Thread.setDefaultUncaughtExceptionHandler时,来清除SDL,也算是一个不错的方式.

    摘要日志API的设计过程中主要想着能给大家提供方便使用的api.现在易用性已经达到了,但是感觉用得不当太危险了.特别是SDL,ThreadLocal中有个ArrayDeque,万一没有调用SDL.end(),迟早会有OOM.

    线程变量真可怕!!!

  5. Catch external calls

    外部调用都要catch异常并记录日志.最好也别把异常吞了,重新抛出一个程序内部定义的运行时异常吧.分布式应用外部调用都很多,建议还是统一来处理日志和异常转换,AOP派上用场了.

评论更精彩,有童鞋建议外部调用记录处理时长,用csv来记录日志便于后期处理.摘要日志中有记录时长的,我们用的json来记录日志,更方便处理,只是业务线程需要承担序列化的开销.

The Logging Olympics – A Race Between Today’s Top 5 Java Logging Frameworks

http://www.takipiblog.com/2013/12/18/the-logging-olympics-a-race-between-todays-top-5-logging-frameworks/

比较了几种日志框架,以前在写日志优化时,注意到了log4j2异常牛掰,特别是采用了disruptor的异步日志,在多线程下的表现非常夸张.不过我们已经被绑架到logback了,只是看看而已.

First-class functions in Java 8

http://www.youtube.com/watch?v=Rd-sqHjmfB0

这动画做的挺有意思,只是Java8还离我们比较远啊.

HTTP/1.1 Upgrade header

http://en.wikipedia.org/wiki/HTTP/1.1_Upgrade_header

通过http upgrade header来实现协议转换,比如把http协议转换为websocket协议.wildfly走得更远,8080端口支持HTTP (Servlet, JAX-RS, JAX-WS), Web Sockets, HTTP Upgraded Remoting (EJB Invocation, Remote JNDI).这篇文章讲述了一些细节.

When should I not kill -9 a process?

http://unix.stackexchange.com/questions/8916/when-should-i-not-kill-9-a-process

Generally, you should use kill -15 before kill -9 to give the target process a chance to clean up after itself.

Java里快如闪电的线程间通讯

http://www.infoq.com/cn/articles/High-Performance-Java-Inter-Thread-Communications

多线程中,锁是一个很大的性能开销.如果采用无锁实现,会发现原来世界可以更美好.

elasticsearch中文学习文档

https://github.com/medcl/elasticsearch-rtf

http://tanjianna.diandian.com/post/2013-07-27/elasticsearch-study

elasticsearch中文发行版,针对中文集成了相关插件,并带有Demo,方便新手学习,或者在生产环境中直接使用

elasticsearch中文学习文档

http://webappchecklist.com/ Web开发者必备:Web应用检查清单

Cache coherence

http://en.wikipedia.org/wiki/Cache_coherence

In a shared memory multiprocessor system with a separate cache memory for each processor, it is possible to have many copies of any one instruction operand: one copy in the main memory and one in each cache memory. When one copy of an operand is changed, the other copies of the operand must be changed also.

Cache coherence is the discipline that ensures that changes in the values of shared operands are propagated throughout the system in a timely fashion.

高性能、高流量Java Web站点打造的22条建议

http://www.csdn.net/article/2013-12-20/2817861-22-recommendations-for-building-effective-high-traffic-web-application

  • 通过使用类似Lucene的索引器做表的索引,使用一个允许在结果集上做基于其他字段的查询.

    对于复杂的查询,在数据库中直接做是很影响性能的,通过使用搜索引擎,能减轻数据库的压力.

  • 考虑使用Oracle或者MySQL分片

    数据量大时,做分片能获得不错的性能提升.

  • 不要使用session stickiness

    会话粘滞会带来一系列的问题.我们的分布式session方案中,默认要求LB启用会话粘滞,这样做的目的是能让本地缓存生效.当需要failover时,才去后端memcached中取数据.能同时兼顾性能和高可用.

  • 终止反向代理商的SSL

    在反向代理或者LB上卸载ssl,能够减轻web应用服务器的压力.

  • 拥抱一切“reactor”

序列化框架选型

分布式应用中,序列化很关键,选择一个合适的序列化框架能给分布式应用带来性能红利,还能减少不必要的麻烦.本文仅仅从我遇到的一些实际问题来说明序列化的选型.更深入部分也可以参考java序列化

序列化框架的性能可以参考jvm-serializers,Kryo的性能还是很牛叉的.

除了性能,还要考虑兼容性/序列化后的大小.如果仅仅考虑性能,我们选择Kryo就足够了.但是Kryo有些用着不爽的地方,比如不是线程安全的/兼容性.

1.兼容性

字段增删不兼容,这个问题有时候很麻烦.比如用memache做缓存,把对象序列化后存入memcache,如果出现字段增删的情况,必须在服务重启的时候把缓存清空,不然就会导致灰常严重的BUG.但是如果应用服务器有多台,这个问题还是避免不了.总会有个时间窗口会出现不同服务器上的同一个应用有不同的类版本,仍然可能会出现灰常严重的BUG.

现在的Kryo提供了兼容性的支持,使用CompatibleFieldSerializer.class,在kryo.writeClassAndObject写入的信息如下:

class name|field lenght|field1 name|field2 name|field1 value| filed2 value

读入kryo.readClassAndObject时,会先读入field names.然后匹配当前反序列化类的field和顺序,构造结果.

子类和父类中有同名的字段时,kryo反序列化会丢失字段值,出现问题的原因和hessian出问题一样.

给kryo提交了一个improvement,在初始化类型信息时,去掉父类中重复名称的field.

2.线程安全

非线程安全也很好处理,每次都new对象出来,当然这样不是最佳的使用方式.通过线程变量来解决会比较合理,保证了性能还能提供很方便使用的工具类.

3.如何生成对象

对于没有无参构造器的类来说,生成新对象是个问题,可以使用java内部的机制来new一个对象。 可以参考下KryoReflectionFactorySupport的实现方式

4.性能

下面测试了java下的各种序列化实现方式的性能

0 Serialisation    write=4,206ns read=16,945ns total=21,151ns
 	1 Serialisation    write=3,626ns read=18,205ns total=21,831ns
0 MemoryByteBuffer write=270ns read=324ns total=594ns
 	1 MemoryByteBuffer write=270ns read=330ns total=600ns
 	0 MemoryByteBufferWithNativeOrder  write=357ns read=360ns total=717ns
 	1 MemoryByteBufferWithNativeOrder  write=323ns read=359ns total=682ns
 	0 DirectByteBuffer write=236ns read=325ns total=561ns
 	1 DirectByteBuffer write=231ns read=301ns total=532ns
 	0 DirectByteBufferWithNativeOrder  write=261ns read=310ns total=571ns
 	1 DirectByteBufferWithNativeOrder  write=243ns read=290ns total=533ns
 	0 UnsafeMemory write=28ns read=82ns total=110ns
 	1 UnsafeMemory write=24ns read=75ns total=99ns
 	0 kryo write=373ns read=348ns total=721ns
 	1 kryo write=390ns read=386ns total=776ns
 	0 kryoWithCompatibleFields write=1,037ns read=1,625ns total=2,662ns
 	1 kryoWithCompatibleFields write=1,038ns read=1,657ns total=2,695ns
 	0 kryoWithCompatibleFieldsAndDuplicateFieldAccept  write=1,077ns read=1,560ns total=2,637ns
 	1 kryoWithCompatibleFieldsAndDuplicateFieldAccept  write=1,064ns read=1,583ns total=2,647ns
 	0 kryoWithUnsafe   write=164ns read=204ns total=368ns
 	1 kryoWithUnsafe   write=168ns read=210ns total=378ns
 	0 fastjson write=1,942ns read=5,834ns total=7,776ns
 	1 fastjson write=1,873ns read=5,879ns total=7,752ns

每种序列化执行1000000次,并且有预热. 各组数据相对比较,可以得出一些结论:

  • 直接调用unsafe,最快,但是最麻烦
  • java自带的序列化很慢,最好不要用
  • kryo2.22提供的unsafe支持,性能非常卓越
  • kryo兼容性序列化器,开销挺大.写需要写入字段名,读的时候还需要做匹配撮合,读比写慢
  • fastjson也挺快的,兼容性\跨语言互操性俱佳.

序列化后的字节大小可以参考jvm-serializers