Dubbo中遇到的一点坑

1.无论何种方式部署,一定要设置delay=-1

2.dubbo调用的序列化有可能不是同步的,即当你远程获取一些信息时候,如果反序列化时间较长,你读取对象信息的时候会直接报空指针异常

3.dubbo是网络连接,无论如何都应该降低调用次数,设计接口的时候最好是batchget或者是做Client部分做Proxy缓存

4.最好将Dubbo的重试机制干掉。。。快速失败,并在Client处做记录,已经遇到过因为Dubbo接口响应慢,多次重试导致直接Provider打爆

几种数据结构的使用场景

Map/HashMap/TreeMap/LinkedHashMap

Map是一种抽象,维护了一组<key,value>的集合,可以通过map.get(key)访问value

HashMap是Map的完整实现。

TreeMap同样是Map的实现,同时还是SortedMap接口的实现,使用场景包括有序访问Map中的元素其顺序由Key中的实现的Comparable接口实现有序

LinkHashMap同样实现了元素有序,其中有序是按照元素进入Map的顺序

以上数据结构没有对多线程做保护,需要额外的代码才能维护在多线程下的正确性

ConcurrentHashMap

是HashMap的多线程版本,能够在多线程下保证Map本身的正确性,同时通过减少锁范围提高了多线程访问的性能

使用场景

用作集中式缓存,或者配置文件映射

Atomic处理系列

AtomicLong,AtomicInteger……等

通过硬件cas(compare and swap)来完成在多线程下的原子操作

这类对象的特性是,因为采用硬件级别的处理,故效率极高,且每个操作的原子性都有保证。

但是cas天生的特性,在竞争非常激烈的时候会导致所有人都瘫痪

AtomicLong的弱化版本LongAdder(JDK1.8加入)

为了解决CAS的缺陷,我们将放弃一部分原子性

具体到LongAdder是放弃了sum的原子性,故而这个类不能适用于比如分发uuid,限流计数等必须对sum有原子性的操作

使用场景

Atomic系列完全可以当作任何基本类型需要用到的且是多线程环境下的需要保证线程安全的地方比如:计数场景,UUID分发场景,边界值控制场景

LongAdder:适用于对long的当前准确值没有需求的场景,比如说阿里双十一大屏的那些数据,这些场景下,对与数据的延迟是可容忍的

Redis不正确使用姿势指南

Redis当对象存储系统

先来展示一段我司日常常见代码:

……………………

Abc = redisTemplate.opsValues().get(“******”);

if(null==Abc){

abc=………………一堆逻辑

redisTemplate.opsValues().set(“******”,Abc);

}

Return Abc;

 

从逻辑上来说这段代码并没有错误

但这样最终导致的结果是把所有外部的压力都转移到了redis上来了,极有可能导致把redis打爆,从而击穿数据库导致整个系统崩溃

Redis当集群内存用

另一种情况是,redis当共享内存用:

比如我们的zuul部件,每次都把用户的请求完整的set进redis,然后当下一个同session的转发进来后去redis去get,具体表现是redis看到日志是每当一个请求进来会出发两位数到三位数的redis请求(流量极端放大),尽管是zuul部件本身做到了无状态,但其实是用了redis做了共享状态。

这样依旧是把所有压力都放在了redis上面。引发问题更糟糕的是,我们的session内容存了太大的对象redis的并发能力遭到了限制,在少量用户下就已经会引发内部系统的崩溃。

总结一下:

正确的姿势是redis类似与一个单机系统里面不同进程的buffer/channel,在考虑上网络IO的重量级后,我们应该尽可能减少进程之间的通信,在这里的语境下应该是减少集群内之间的通信,从而避免把buffer/channel本身给弄挂了。

在第一种用法下,其实应该是将Abc的状态保存在redis但是Abc本身应该要存在当前结点上,这样就改变了集群内部通过redis传递对象,转而是传递信息了,比如我用时间作为Abc的特征,那么集群内部只需要传递特征时间就能够保持集群内部操作的幂等性,从原有的可能几kb的通讯变为几bit的通讯

第二种用法下,优化的手段就要更多了,首先第一点是减少session保存的数据,将不同业务之间的session分割开来减少session的大小。接着将请求按照一致性hash原理,同一个请求只从zuul集群的同一台服务器走,将保存的信息转换成本地内存,或者将用户-》集群的映射关系存入redis从而减少redis的使用量。

如何写socket通信

为了抛弃花生壳就自己写了个动态IP发现动态IP发现

不同于应用层,在Socket需要自己封装自己的协议,要自己决定如何传输数据

因为在整个网络的层次比较中间的位置,能给予开发者很大的灵活性

比如:

我需要用socket维持一个心跳链接

我的数据封装为如下格式

type lease struct{
  lenght int
  content []byte
}

lenght代表整个传输的长度,可以用于校验传输正确性

content就是整个需要传输的心跳内容

有时候需要更多的校验可以在lease的前后添加不同的结束符或者连接符,用于处理粘包等状态

相比较于Http层协议socket比较不爽的在于会造成业务和发送的逻辑交织