redisソース解読まとめ(redisコンシステンシハッシュ実装)

6536 ワード

最近仕事の中でずっとredisでキャッシュの机能の実现を行って、redisのソースコードは1万数行だけありますが、しかし确かに研究する価値があって、以下の个人の少しの研究と见方(もともと図で表すつもりで、本当に1种の良い絵のツールが探し出せないで说明して、だから文字で说明しました)、各位と共に努力することができることを望みます.
一、1.JedisShardInfoリストリストリストjedisShardInfoListを構築します.JedisShardInfoにはサーバIP、ポートなどの情報が含まれています.ソースコードは次のとおりです.
public JedisShardInfo(String host, int port, String name) {
this(host, port, 2000, name);
}

2.GenericObjectPoolConfig poolConfigを構築するこの中には主にキャッシュプールの構成:最大接続数、最大待ち時間などの構成情報を設定します.
3.1と2に基づいてShardedJeddisPoolオブジェクトを構築し、主なソースコードは以下の通りである.
public ShardedJedisPool(final GenericObjectPoolConfig poolConfig,
List<JedisShardInfo> shards) {
this(poolConfig, shards, Hashing.MURMUR_HASH);
}

このオブジェクトを構築する過程で、同時にShardedJeddisFactoryオブジェクトを構築しました.このオブジェクトは非常に重要で、後で詳しく説明します.このオブジェクトのソースコードを構築するには、次のようにします.
public ShardedJedisPool(final GenericObjectPoolConfig poolConfig,
List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern));
}

親コンストラクション関数を呼び出します.
public Pool(final GenericObjectPoolConfig poolConfig,
PooledObjectFactory<T> factory) {
initPool(poolConfig, factory);
}

初期化プールinitPool()メソッドを呼び出します.次のコードはキーソースのみを残します.
public void initPool(final GenericObjectPoolConfig poolConfig,
PooledObjectFactory<T> factory) {
this.internalPool = new GenericObjectPool<T>(factory, poolConfig);
}

上のコードは、以前に述べた非常に重要なShardedJedisFactoryオブジェクトをGenericObjectPoolクラスにロードします.
以上のすべての情報は後の重要な操作のために、各種のデータ構造の操作のように、敷き詰められています.
二、「一」に基づいて以下の操作を行い、ShardedJedisPoolを構築した.
1.ShardedJeddisPoolを呼び出すgetResourceメソッドShardedJeddisに戻ってgetまたはset操作を行う
public T getResource() {
return internalPool.borrowObject();
}

ここでinternalPoolオブジェクトは、1の最後のGenericObjectPoolというクラスのオブジェクトインスタンスであり、最終的にはこのようなcreateメソッドに呼び出されます.
private PooledObject<T> create() throws Exception {
final PooledObject<T> p;
try {
p = factory.makeObject();
} catch (Exception e) {
createCount.decrementAndGet();
throw e;
}
return p;
}

ShardedJedisFactoryこのオブジェクトが重要だと言ったのはここだfactorymakeObject()操作中のfactoryは
ShardedJedisFactoryというクラスは、以前にロードされていましたが、上に説明があります.makeObjectという方法もこのクラスで最も重要な方法です
@Override
public PooledObject<ShardedJedis> makeObject() throws Exception {
ShardedJedis jedis = new ShardedJedis(shards, algo, keyTagPattern);
return new DefaultPooledObject<ShardedJedis>(jedis);
}

makeObjectこのメソッド(1)の最初のステップは、データ構造に対する様々な操作を含むgetResource()メソッドを呼び出して返されるオブジェクトを構築することです.
コンストラクション関数ShardedJeddis(shards,algo,keyTagPattern)を使用して親クラスのコンストラクションメソッドを呼び出し、initializeメソッドを呼び出します.この初期化メソッドは、redisでコンシステンシハッシュを使用する
場所は、循環shards(ここでshardsは上のjedisShardInfoListという変数であり、例えばlistのsizeは4であり、4台のサーバに相当する)であり、shardsごとに160個設定されている.
ノード、n台のサーバーはn*160のノードがあって、ノードのkey値はHashingを通じて計算して、これらのノードは実はすでに均一に1つの閉じた
ループが当たったので、サーバが多ければ利用率が増加します.Nodesの設定が完了すると、n個のshardsと対応するjedisがMAPに設定されます.
public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
this.algo = algo;
this.tagPattern = tagPattern;
initialize(shards);
}

private void initialize(List<S> shards) {
nodes = new TreeMap<Long, S>();

for (int i = 0; i != shards.size(); ++i) {
final S shardInfo = shards.get(i);
if (shardInfo.getName() == null)
for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n),
shardInfo);
}
else
for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
nodes.put(
this.algo.hash(shardInfo.getName() + "*"
+ shardInfo.getWeight() + n), shardInfo);
}
resources.put(shardInfo, shardInfo.createResource());
}
}

上の初期化動作が完了すると,このように後からキャッシュを行う場合やキャッシュから値をとる場合には,まずkey呼び出しHashingクラスで同じアルゴリズムで一致性ハッシュ値を算出し,計算する.
ハッシュ値の後、TreeMapのtailMapメソッドに従って上のnodesからこのハッシュ値以上のSortedMapを取得し、次に最初のkeyおよび対応するshardInfoを取得し、
最終的にresourcesからjedisが取得されます.
public R getShard(String key) {
return resources.get(getShardInfo(key));
}

public S getShardInfo(byte[] key) {
SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
if (tail.isEmpty()) {
return nodes.get(nodes.firstKey());
}
return tail.get(tail.firstKey());
}
public String set(String key, String value) {
Jedis j = getShard(key);
return j.set(key, value);
}

(2)第2ステップはDefaultPooledObjectオブジェクトを構築し,このオブジェクトを介してShardedJeddisを返し,このオブジェクトが第1ステップで行うことである.
2.上で取得したShardedJeddisによりデータのキャッシュまたは取得が行われ、一例を挙げて簡単に説明すると、setメソッドが呼び出され、まず上述したコンシステンシハッシュアルゴリズムに基づいてjeddisが取得される
JedisにはクライアントClientが定義されています.
Jedisのsetメソッドを呼び出し、まずクライアントの接続を完了し、コマンドを送信し、データのキャッシュを行います.
protected Connection sendCommand(final Command cmd, final byte[]... args) {
connect();
Protocol.sendCommand(outputStream, cmd, args);
pipelinedCommands++;
return this;
}

public void connect() {
if (!isConnected()) {
try {
socket = new Socket();
// ->@wjw_add
socket.setReuseAddress(true);
socket.setKeepAlive(true); // Will monitor the TCP connection is
// valid
socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
// ensure timely delivery of data
socket.setSoLinger(true, 0); // Control calls close () method,
// the underlying socket is closed
// immediately
// <-@wjw_add

socket.connect(new InetSocketAddress(host, port), timeout);
socket.setSoTimeout(timeout);
outputStream = new RedisOutputStream(socket.getOutputStream());
inputStream = new RedisInputStream(socket.getInputStream());
} catch (IOException ex) {
throw new JedisConnectionException(ex);
}
}
}

public static void sendCommand(final RedisOutputStream os,
final Command command, final byte[]... args) {
sendCommand(os, command.raw, args);
}

private static void sendCommand(final RedisOutputStream os,
final byte[] command, final byte[]... args) {
try {
os.write(ASTERISK_BYTE);
os.writeIntCrLf(args.length + 1);
os.write(DOLLAR_BYTE);
os.writeIntCrLf(command.length);
os.write(command);
os.writeCrLf();

for (final byte[] arg : args) {
os.write(DOLLAR_BYTE);
os.writeIntCrLf(arg.length);
os.write(arg);
os.writeCrLf();
}
} catch (IOException e) {
throw new JedisConnectionException(e);
}
}

三、総括:以上は私がredisソースコードに対する簡単な理解であり、後続の深い学習と使用が必要であり、レンガを撮ることを歓迎します.