SONA 是一个由比心语音技术团队开发,用于快速搭建语音房产品的全端解决方案,包含了房间管理、实时音视频、房间IM、长连接网关等能力,支撑了比心聊天室、直播、游戏房等业务。
前言
Sona 平台是一个搭建语音房产品的全端解决方案,包含了房间管理、实时音视频、房间IM、长连接网关等能力。其中最基础核心的就是长连接网关。
池化技术是把一些能够复用的东西放到池中,提前保存大量的资源,避免重复创建、销毁的开销,从而极大提高性能。在请求量比较大的时候能明显优化应用性能,降低系统频繁建连的资源开销。池化技术的应用非常广泛,我们工作中常见的比如数据库连接池、线程池、http连接池等。
一、对象池
还有一种可能在工作中不是经常使用的池化技术 - 对象池 ,它的核心思想就是空间换时间。
我们知道 Java 中频繁地创建和销毁对象的开销是很大的,通常情况下对象是分配在堆上的,因为堆是线程共享的,所以同一时间可能会有很多线程申请空间分配,在这种情况下要加锁处理,这样一来就会造成分配效率下降。虽然可以使用 TLAB 来分配(TLAB是每个线程独有的,它可以避免这种开销,直接分配内存),但还是存在着一些效率问题。
我们可以使用预先创建好的对象来减少频繁创建对象的性能开销,同时还可以对对象进行统一的管理,降低了对象的使用成本。这样不仅避免频繁地创建和销毁所带来的性能损耗,提高并发处理能力,而且能够降低 JVM GC 的压力。
像 Disruptor,它之所以高性能其实有一部分原因就是,RingBuffer 数组元素在初始化时一次性全部创建,提升缓存命中率,对象循环利用,避免频繁GC。
二、Netty Recycler
Recycler 是 Netty 提供的自定义实现的轻量级对象回收站,借助 Recycler 可以完成对象的获取和回收。
在 SONA网关中,每次业务请求最终都会被封装成 ChannelEventTask 丢到线程池里执行。为了尽可能地提升网关的性能,我就使用了 Recycler 来做池化。在压测过程中,内存占用以及CPU 消耗都有比较明显的改善。
使用方式其实非常简单,下面这个是 Netty 官方给出的样例:
通过 RECYCLER.get() 来获取对象,handle.recycle(this) 来回收对象 。
public class MyObject {
private static final Recycler<MyObject> RECYCLER = new Recycler<MyObject>() {
protected MyObject newObject(Recycler.Handle<MyObject> handle) {
return new MyObject(handle);
}
}
public static MyObject newInstance(int a, String b) {
MyObject obj = RECYCLER.get();
obj.myFieldA = a;
obj.myFieldB = b;
return obj;
}
private final Recycler.Handle<MyObject> handle;
private int myFieldA;
private String myFieldB;
private MyObject(Handle<MyObject> handle) {
this.handle = handle;
}
public boolean recycle() {
myFieldA = 0;
myFieldB = null;
return handle.recycle(this);
}
}
MyObject obj = MyObject.newInstance(42, "foo");
...
obj.recycle();
Recycler 原理
Recycler 里面一共包含四个核心组件:Stack、WeakOrderQueue、Link、DefaultHandle。
整个 Recycler 的内部结构中各个组件的关系,可以通过下面这幅图进行描述

Stack
private static final class Stack<T> {
final Recycler<T> parent; // 所属的 Recycler
final WeakReference<Thread> threadRef; // 所属线程的弱引用
final AtomicInteger availableSharedCapacity; // 线程回收对象时,其他线程能保存的被回收对象的最大个数
final int maxDelayedQueues; // WeakOrderQueue最大个数
private final int maxCapacity; // 对象池的最大大小,默认最大为 4095
private final int ratioMask; // 控制对象的回收比率,默认只回收 1/8 的对象
private DefaultHandle<?>[] elements; // 存储缓存数据的数组
private int size; // 缓存的 DefaultHandle 对象个数
private int handleRecycleCount;
// WeakOrderQueue 链表的三个重要节点
private WeakOrderQueue cursor, prev;
private volatile WeakOrderQueue head;
// 省略其他代码
}
Stack 对象是从 Recycle 内部的 FastThreadLocal 对象中获得,因此每个线程拥有属于自己的 Stack 对象,创造了无锁的环境,并通过 weakOrderQueue 与其他线程建立沟通的桥梁。
每个 Stack 会维护一个 WeakOrderQueue 的链表,每个 WeakOrderQueue 节点会保存非当前线程的其他线程所释放的对象。例如图中 ThreadA 表示当前线程,WeakOrderQueue 的链表存储着 ThreadB、ThreadC 等其他线程释放的对象。
WeakOrderQueue
WeakOrderQueue 用于存储其他线程回收到当前线程所分配的对象,并且在合适的时机,Stack 会从异线程的 WeakOrderQueue 中收割对象。如上图所示,ThreadB 回收到 ThreadA 所分配的内存时,就会被放到 ThreadA 的 WeakOrderQueue 当中。
/**
* 「WorkOrderQueue」 内部链表是由「Head」和「ta

最低0.47元/天 解锁文章
698





