先贴代码,看运行结果:
[code]
Class<?> cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
array[130] = array[131];

Integer x = 2;
System.out.println(x + 2);
[/code]
运行结果实际上是5,其它地方如果出现Integer类型的数值,2就会变成3。

- 阅读剩余部分 -

业务中碰到单锚点同步多项数据问题,举个例子说明以备忘之。

概念解释:
锚点同步:client用自己得到的最后一次操作编号,来获取之后所有的操作,重放以达到状态一致。

问题描述:
多从库环境下,用一个锚点通过两次DAO去同步两类业务数据,有可能会因为两个从库的同步状态不一致,导致丢数据。
比如:用100锚点去同步时,先A业务,再B业务,同步A时,拉到123,同步B时拉到125,两次取数据间A业务产生了124操作。下次client用125来同步,会丢失124操作。

后续:
演练时发现跟是否双从或是否主从无关,只有主库的情况下同样有可能出现类似问题,两次操作有时差,没法保障数据一致(不可能锁上不让操作)。

解决办法:
每次同步时,记录每个业务的最大值操作编号max(a,b,c,d,...),下次以最大编号来拉时,每个业务按自己上次记录的编号拉。

问题:
有可能会出现用125来拉时,拉到124操作。相比之前的124操作丢失,会更好。
从单个业务看,一直是连续的不会丢,如果各业务间操作不耦合,不会有影响。
如果多业务间有时续要求,需要根据业务依赖关系来确定兼容方案,会更复杂,有两种方式可以解决:
1、拉完A后拉B,如果B小于A,则以A为准,可以结束;如果B大于A,拉B后再拉A(记做A1),丢弃掉A1中比B大的部分,以B为准。如果是多从库,必拉一次小的且拉小的数据的操作从主库进行。
2、耦合的业务操作,只重放到小编号的数据。这么做很安全,缺点是及时性差,而且如果某个业务较冷的时候,滞后会非常严重。
推荐方案1。

一直工作很好的线上服务,今天忽然发现有问题,原因是取到的本机IP是错的。
ifconfig的结果如下:
[code]
eth0 Link encap:Ethernet HWaddr 12:xx:xx:xx:xx:xx
inet addr:10.xx.xx.xx Bcast:10.xx.xx.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:347107351 errors:0 dropped:0 overruns:0 frame:0
TX packets:521084913 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:73368864377 (68.3 GiB) TX bytes:73126114339 (68.1 GiB)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:407951 errors:0 dropped:0 overruns:0 frame:0
TX packets:407951 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:112925033 (107.6 MiB) TX bytes:112925033 (107.6 MiB)
[/code]
取IP的时候过滤掉了非isSiteLocalAddress的IP:
[code]
public static String getLocalIp() {
Enumeration<NetworkInterface> ifs;
try {
ifs = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
return "";
}

while (ifs.hasMoreElements()) {
NetworkInterface i = ifs.nextElement();
Enumeration<InetAddress> as = i.getInetAddresses();
while (as.hasMoreElements()) {
InetAddress a = as.nextElement();
if (!a.isSiteLocalAddress()) continue;
return a.getHostAddress();
}
}
return "";
}
[/code]
取出来的是172.x.x.x,还能ping通,ping通后,arp信息中找不到对应的mac地址。瞬间晕了。

- 阅读剩余部分 -

单纯理解Java的初始化,可以认为有两步,静态初始化的类构造器clinit和实例化的实例构造器init的初始化。
clinit发生在首次访问期静态数据或方法时,再或者是首次new的时候,在init之前。

什么时候有clinit?
《深入理解Java虚拟机》书中给出了大致的答案:
[code]
<clinit>()方法是由编译器自动收集类中的所有变更的赋值动作和静态语句块(static{}块)中的语句产生的
[/code]
这句话总结的非常到位,但下面补的这一刀很容易把人带到沟里去,大意:
[code]
如果一个类中没有静态语句块,也没有对变量的赋值操作,可以不为这个类生成<clinit>()方法。
[/code]

直接的理解是,不出现下面这两种代码,就不需要clinit方法;相反的,出现了,就需要。
[code]
//1.静态变量或静态常量
private static Xxx xxx = ....;
private static final Yyy yyy = ...;

//2.静态代码块
static{
Xxx.xxx();
Yyy.yyy();
}
[/code]

实际上并不是这样的。我下面具体描述一下。

- 阅读剩余部分 -

线上数据库崩溃,怀疑是utf8mb4编码导致的,目前已经重现,并找到具体的解决办法。

原因:
当支持utf8mb4的字段中出现emoji表情时,用like '%xx%'方式全搜索时,崩溃。
应该是MySQL的bug,怀疑是因为emoji的编码长度是4,而utf8_unicode_ci是3,MySQL的like非左匹配操作时,碰到占4字节的utf8mb4时出现问题(猜测)。

解决办法:
用instr函数取代like。不崩溃且效率高。instr('abcd','ab')值为1,instr('abcd','ca')值为0。即大于0为命中。

总结:在like左匹配的时候,用原生like靠谱。如果是两边匹配的时候,like支持复杂语法,所以效率较低。普通的%xx%类型的搜索,直接instr即可搞定。
另外:instr的大小写匹配跟like一样,默认是大小写不敏感的。

以下是完整的异常信息。

- 阅读剩余部分 -

最近申请了startssl的多个https证书,碰到一系列问题,逐个备忘一下。

1、nginx对同一个IP上多个域名证书的支持,下面的:TLS SNI support enabled。
[code]
#./nginx -V
nginx version: nginx/1.x.x
built by gcc 4.x.x 201xxxxx (Red Hat 4.x.x-x) (GCC)
TLS SNI support enabled
[/code]
如果不支持,需要重新编译openssl及nginx。

2、wget认证不过及firefox认证失败的问题。
[code]
ERROR: cannot verify www.xxxx.com's certificate, issued by `/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 1 Primary Intermediate Server CA':
Unable to locally verify the issuer's authority.
[/code]
某些浏览器或wget等工具没有内置StartCom Class 1 Primary Intermediate Server CA的证书为中级认证机构,认证链不完整,所以认证失败。
base64编码的cer,只需要把它的public的cer文件放到自己站点的public的cer下面即可。网上有人直接>>操作,导致中间没有换行,证书不认了,加个换行就对了。
加上它以后,它是被StartCom的根认证机构认证的,论证链就完整了。

3、apcache的Java的DefaultHttpClient,碰到nginx上配置多个证书时,访问其它域名的时候会出错。
[code]
javax.net.ssl.SSLException: hostname in certificate didn't match: <test.api.xxx.com> != <www.xxx.com> OR <xxx.com> OR <www.xxx.com>
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:228)
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:54)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:149)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:130)
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:641)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784)
[/code]
跟了一下代码,AbstractVerifier.verify中得到的确实是默认的www.xxx.com的证书链,第一个是当前站点,第二个是问题2中加的中级证机构的public证书。
再往下没跟动,怀疑httpclient是用ip得到的证书,也就意味着它不支持同一个IP多个域名证书。解决方案就是实现X509HostnameVerifier以取消域名的验证。
[code]
final X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {

@Override
public boolean verify(String host, SSLSession ssl) {
return true;
}

@Override
public void verify(String host, SSLSocket ssl) throws IOException {//实际上只会调用这个。
}

@Override
public void verify(String host, X509Certificate cert) throws SSLException {
}

@Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
}
};
final SSLSocketFactory ssf = new SSLSocketFactory(ctx, hostnameVerifier);
[/code]