先说前言,因为平常工作比较忙,社区上的featrue也需要追踪源码,导致我实在没有精力像其他作者那样频繁的更新博客了。所以这一次,对已经落幕的天池中间件,以一介失败者的身份做一下总结
提交记录:
github地址: github.com/complone/ro…
yuque: www.yuque.com/docs/share/… 《RocketMQ冷热存储-leveldb方案调研》
processon(第一版方案构想): www.processon.com/view/link/6…
leveldb的设计论文: www.igvita.com/2012/02/06/…
leveldb源码注释版:github.com/complone/le…
看到赛题的第一反应是要同时支持顺序写以及随机读
顺序写想到了LevelDb的写入流程(流程图的下面部分)
1.用条件变量加重入锁,控制消息每一次 写入缓冲区->刷写磁盘 保证顺序写
2.采用三级缓存 leveldb的slice (byteBuffer的深拷贝)+ ConrruentSkipListHashMap (并发的跳表保证随机读) + filechannel.force(当缓冲区写入的byteBuffer写满64kb的时候,刷写到磁盘)
public final class Slice {
private final byte[] data;
private final int offset;
private final int length;
private int hash;
public Slice(){
this.data = new byte[0];
this.offset = 0;
this.length = 0;
}
public Slice(String s){
requireNonNull(s, "String is null");
this.data = s.getBytes();
this.offset = 0;
this.length = s.length();
}
public Slice(byte[] data)
{
requireNonNull(data, "array is null");
this.data = data;
this.offset = 0;
this.length = data.length;
}
public Slice(int length)
{
this.data = new byte[length];
this.offset = 0;
this.length = length;
}
public Slice(byte[] data, int offset, int length)
{
requireNonNull(data, "array is null");
this.data = data;
this.offset = offset;
this.length = length;
}
/**
* 返回数据长度,以byte计算
* */
public int length()
{
return this.length;
}
/**
* 返回数据的头指针
* */
public byte[] getData()
{
return this.data;
}
/**
* 判断数据是否为空
* */
public boolean empty(){
return this.length == 0;
}
/**
* 返回数据的偏移量
*/
public int getOffset()
{
return this.offset;
}
/**
* 返回指定index的byte
* @throws IndexOutOfBoundsException
*/
public byte getByte(int index)
{
checkPositionIndexes(index, index + DataUnit.BYTE_UNIT, this.length);
index += this.offset;
return this.data[index];
}
/**
*
* Java中所有的byte类型都是signed类型。只能表达(-128,127),signed byte可以表达(0,255)。
* 通过将byte声明为short或者int类型。然后与0xFF取&。即可将signed byte转为unsigned byte。
* 0xff 表示为二进制就是 1111 1111。在signed byte类型中,代表-1;但在short或者int类型中则代表255.
* 当把byte类型的-1赋值到short或者int类型时,虽然值仍然代表-1,但却由1111 1111变成1111 1111 1111 1111.
* 再将其与0xff进行掩码:
* -1: 11111111 1111111
* 0xFF: 00000000 1111111
* 255: 00000000 1111111
* 所以这样,-1就转换成255.
*
* @throws IndexOutOfBoundsException
*/
public short getUnsignedByte(int index)
{
return (short) (getByte(index) & 0xFF);
}
/**
* 返回16位的short,@param index 索引位置
*
* 知识点:
* Leveldb对于数字的存储是little-endian的,在把int32或者int64转换为char*的函数中,
* 是按照先低位再高位的顺序存放的,也就是little-endian的。所以,这里先要把高位左移8位,
* 再与低位做或运算。
*
* @throws IndexOutOfBoundsException
*/
public short getShort(int index)
{
checkPositionIndexes(index, index + DataUnit.SHORT_UNIT, this.length);
index += this.offset;
return (short) (this.data[index] & 0xFF | this.data[index + 1] << 8);
}
/**
* 返回32位的int,@param index 索引位置
*
* @throws IndexOutOfBoundsException
*/
public int getInt(int index)
{
checkPositionIndexes(index, index + DataUnit.INT_UNIT, this.length);
index += offset;
return (this.data[index] & 0xff) |
(this.data[index + 1] & 0xff) << 8 |
(this.data[index + 2] & 0xff) << 16 |
(this.data[index + 3] & 0xff) << 24;
}
/**
* 返回64位的long,@param index 索引位置
*
* @throws IndexOutOfBoundsException
*/
public long getLong(int index)
{
checkPositionIndexes(index, index + DataUnit.LONG_UNIT, this.length);
index += offset;
return ((long) this.data[index] & 0xff) |
((long) this.data[index + 1] & 0xff) << 8 |
((long) this.data[index + 2] & 0xff) << 16 |
((long) this.data[index + 3] & 0xff) << 24 |
((long) this.data[index + 4] & 0xff) << 32 |
((long) this.data[index + 5] & 0xff) << 40 |
((long) this.data[index + 6] & 0xff) << 48 |
((long) this.data[index + 7] & 0xff) << 56;
}
/**
* 把当前数据复制到目标Slice中
*/
public void getBytes(int index, Slice dst, int dstIndex, int length)
{
getBytes(index, dst.data, dstIndex, length);
}
Continue..QwQ