Redissonで高性能/同時確保-Nettyの応用

背景

序文

RedissonのGithub:https://github.com/redisson/redisson

Redisson公式サイト:https://redisson.pro/

Redissonは、Javaインメモリデータグリッド(メモリ内データグリッド)上のRedisに基づいて実現されます。これは、一般的に使用されるJava分散オブジェクトのシリーズは、分散サービスの数を提供して提供するだけでなく。含む(BitSetSetMultimapSortedSetMapListQueueBlockingQueueDequeBlockingDequeSemaphoreLockAtomicLongCountDownLatchPublish / SubscribeBloom filterRemote serviceSpring cacheExecutor serviceLive Object serviceScheduler service)RedissonはRedisのが最も簡単で便利な方法を提供します。Redisson目的は、ユーザーがビジネスロジックにより狭く焦点を当てることができるようになりますように、Redisの(関心の分離)の利用者のための関心事の分離を促進することです。

以下はRedisson構造です:

Redisson底が使用ネッティーのフレームを。サポートのRedis 2.8以降、後でJava1.6 +をサポートしますか。

クライアントの初期化

createBootstrap

org.redisson.client.RedisClient#createBootstrap

private Bootstrap createBootstrap(RedisClientConfig config, Type type) {
        Bootstrap bootstrap = new Bootstrap()
                        .resolver(config.getResolverGroup())
          							//1.指定配置中的IO类型
                        .channel(config.getSocketChannelClass())
          							//2.指定配置中的线程模型
                        .group(config.getGroup());
  			//3.IO处理逻辑
        bootstrap.handler(new RedisChannelInitializer(bootstrap, config, this, channels, type));
  			//4. 指定bootstrap配置选项
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeout());
        bootstrap.option(ChannelOption.SO_KEEPALIVE, config.isKeepAlive());
        bootstrap.option(ChannelOption.TCP_NODELAY, config.isTcpNoDelay());
        config.getNettyHook().afterBoostrapInitialization(bootstrap);
        return bootstrap;
    }

あなたは上記のコードから見ることができ、ブートクラスがあるクライアントを開始Bootstrap、クライアントとサーバーの接続は、次の完了後に作成されたブートストラップクラスを開始する責任は、私たちは、クライアントが開始したプロセスを説明します。

A.まず、我々はそれに、ドライブは読み取りと書き込みのデータ接続をスレッドモデルを指定する必要があります。その後、redissonのデフォルトはIOモデルを指定しましたNioSocketChannel

II。次に、ガイドリンククラスに一連の処理を指定し、これは論理接続を処理する主なサービスの定義である、それは問題で、私たちは、後に詳細に分析し、しません理解していません

RedisChannelInitializer

org.redisson.client.handler.RedisChannelInitializer

RedisChannelInitializer

 @Override
    protected void initChannel(Channel ch) throws Exception {
      	// 开启SSL终端识别能力
        initSsl(config, ch);
        
        if (type == Type.PLAIN) {
          	//Redis正常连接处理类
            ch.pipeline().addLast(new RedisConnectionHandler(redisClient));
        } else {
          	//Redis订阅发布处理类
            ch.pipeline().addLast(new RedisPubSubConnectionHandler(redisClient));
        }
        
        ch.pipeline().addLast(
          	//链路检测狗
            connectionWatchdog,
          	//Redis协议命令编码器
            CommandEncoder.INSTANCE,
          	//Redis协议命令批量编码器
            CommandBatchEncoder.INSTANCE,
          	//Redis命令队列
            new CommandsQueue());
        
        if (pingConnectionHandler != null) {
           //心跳包连接处理类
            ch.pipeline().addLast(pingConnectionHandler);
        }
        
        if (type == Type.PLAIN) {
          	//Redis协议命令解码器
            ch.pipeline().addLast(new CommandDecoder(config.getExecutor(), config.isDecodeInExecutor()));
        } else {
          	//Redis订阅发布解码器
            ch.pipeline().addLast(new CommandPubSubDecoder(config.getExecutor(), config.isKeepPubSubOrder(), config.isDecodeInExecutor()));
        }

        config.getNettyHook().afterChannelInitialization(ch);
    }

図中、図1 Redissonリンク処理。

Redisson処理チェーン

Redisson処理チェーン

私が選んだよりChannelHandler Redisson内部のパイプライン、CommandEncoderおよびCommandDecoderソースコード解析すること。

CommandDecoder_Encoder

故障の再接続

org.redisson.client.handler.ConnectionWatchdog#再接続、再接続メカニズム

private void reconnect(final RedisConnection connection, final int attempts){
		//重试时间越来越久
    int timeout = 2 << attempts;
    if (bootstrap.config().group().isShuttingDown()) {
        return;
    }
    
    try {
        timer.newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                tryReconnect(connection, Math.min(BACKOFF_CAP, attempts + 1));
            }
        }, timeout, TimeUnit.MILLISECONDS);
    } catch (IllegalStateException e) {
        // skip
    }
}

タイマー管理でネッティー、ハッシュ時間ホイールモードの使用は、タイムホイールは、それは、古典的なアルゴリズムタイマータイマーを実装することで、時間のラウンドに翻訳しました。

声明のこのメソッドは、この次のとおりです。

 /**
     * Schedules the specified {@link TimerTask} for one-time execution after
     * the specified delay.
     *
     * @return a handle which is associated with the specified task
     *
     * @throws IllegalStateException       if this timer has been {@linkplain #stop() stopped} already
     * @throws RejectedExecutionException if the pending timeouts are too many and creating new timeout
     *                                    can cause instability in the system.
     */
    Timeout newTimeout(TimerTask task, long delay, TimeUnit unit);

この方法は、TimerTaskをオブジェクトがロジックに実行されるかを知る時間を必要とし、単位遅延時間値と時間TimeUnitでを必要とします。

Redisのprotocolコマンドエンコーダ

著者は、Redisのデータベースシステムのボトルネックは、一般的にネットワークトラフィックではなく、独自の内部ロジック上のデータベースと信じています。だから、廃棄物のテキストフローを使用してのRedisプロトコルは依然として高いパフォーマンスへのアクセスを得ることができる場合であっても。使用して、メモリ内のすべてのデータをRedisのシングルスレッドの超高QPS 10ワット/秒に達することができ、完全なCPUコアを実行する単一ノードの場合には、外部サービスを提供します。

RESPはRedisのプロトコルの短い配列です。これは、利点は非常に単純な、優れた分析性能を達成することである、直感的なテキストベースのプロトコルです。

データ転送プロトコルの構造は、搬送部の端部で均一なラインフィードと共に、最小単位の5種類をRedisの\r\n

  1. で単一の文字列+のシンボルの始まり。
  2. 複数行の文字列$シンボルの先頭には、文字列の長さが続きます。
  3. 整数値:シンボルを開始するには、整数の文字列が続きます。
  4. エラーメッセージは-記号で始まります。
  5. アレイ内の*長番号最初、アレイが続きます。

単一の文字列ハローワールド

+hello world\r\n

マルチライン文字列のハローワールド

$11\r\nhello world\r\n

複数行の文字列はもちろん、行の文字列を表現することができます。

整数 1024

:1024\r\n

間違った間違ったタイプ

-WRONGTYPE Operation against a key holding the wrong kind of value\r\n

配列 [1,2,3]

*3\r\n:1\r\n:2\r\n:3\r\n

NULLは、複数行の文字列を示しているが、長さが書き込まれるべき-1。

$-1\r\n

空の文字列は 0塗りつぶし複数行の文字列で表されます。

$0\r\n\r\n

2があることに注意してください\r\nなぜ2?2ため、\r\n空の文字列の間、間。

org.redisson.client.handler.CommandEncoder#エンコード()

private static final char ARGS_PREFIX = '*';
private static final char BYTES_PREFIX = '$';
private static final byte[] CRLF = "\r\n".getBytes();


@Override
    protected void encode(ChannelHandlerContext ctx, CommandData<?, ?> msg, ByteBuf out) throws Exception {
        try {
          	//redis命令前缀
            out.writeByte(ARGS_PREFIX);
            int len = 1 + msg.getParams().length;
            if (msg.getCommand().getSubName() != null) {
                len++;
            }
            out.writeCharSequence(Long.toString(len), CharsetUtil.US_ASCII);
            out.writeBytes(CRLF);
            
            writeArgument(out, msg.getCommand().getName().getBytes(CharsetUtil.UTF_8));
            if (msg.getCommand().getSubName() != null) {
                writeArgument(out, msg.getCommand().getSubName().getBytes(CharsetUtil.UTF_8));
            }
          	......
        } catch (Exception e) {
            msg.tryFailure(e);
            throw e;
        }
    }

private void writeArgument(ByteBuf out, ByteBuf arg) {
    out.writeByte(BYTES_PREFIX);
    out.writeCharSequence(Long.toString(arg.readableBytes()), CharsetUtil.US_ASCII);
    out.writeBytes(CRLF);
    out.writeBytes(arg, arg.readerIndex(), arg.readableBytes());
    out.writeBytes(CRLF);
}

Redisのプロトコルコマンドデコーダ

org.redisson.client.handler.CommandDecoder#readBytes

 private static final char CR = '\r';
 private static final char LF = '\n';
 private static final char ZERO = '0';

private ByteBuf readBytes(ByteBuf is) throws IOException {
    long l = readLong(is);
    if (l > Integer.MAX_VALUE) {
        throw new IllegalArgumentException(
                "Java only supports arrays up to " + Integer.MAX_VALUE + " in size");
    }
    int size = (int) l;
    if (size == -1) {
        return null;
    }
    ByteBuf buffer = is.readSlice(size);
    int cr = is.readByte();
    int lf = is.readByte();
  	//判断是否以\r\n开头
    if (cr != CR || lf != LF) {
        throw new IOException("Improper line ending: " + cr + ", " + lf);
    }
    return buffer;
}

シリアル化データ

Redissonオブジェクトクラスが読み込まれ、Redisの内部に格納されている目的を達成するために、オブジェクトのシリアライゼーションおよびデシリアライゼーションを符号化するために使用されます。Redissonはあなたの選択のために、アプリケーションをコードする以下のオブジェクトが用意されています。

コーディングクラス名 説明
org.redisson.codec.JsonJacksonCodec ジャクソンJSONエンコーディングデフォルトのエンコーディング
org.redisson.codec.AvroJacksonCodec アブロ AバイナリJSONエンコーディング
org.redisson.codec.SmileJacksonCodec スマイル別のバイナリJSONエンコーディングを
org.redisson.codec.CborJacksonCodec CBORバイナリJSONエンコーディング
org.redisson.codec.MsgPackJacksonCodec MsgPack再びバイナリJSONエンコーディング
org.redisson.codec.IonJacksonCodec アマゾンイオンアマゾンイオンは、JSONのような形式をコーディング
org.redisson.codec.KryoCodec Kryoバイナリオブジェクトシリアライゼーションエンコーディング
org.redisson.codec.SerializationCodec JDKのコード配列
org.redisson.codec.FstCodec FST 10倍JDKシリアライゼーション性能と100%互換性のある符号化
org.redisson.codec.LZ4Codec LZ4コンパクトなオブジェクトコード配列
org.redisson.codec.SnappyCodec スナッピー圧縮タイプをコードする別のオブジェクトの配列を
org.redisson.client.codec.JsonJacksonMapCodec ジャクソンは、クラスベースのマッピングを使用してエンコードされました。情報は、シリアライゼーションクラスを避けるために使用することができ、かつ使用のために解決するためにbyte[]発生した問題を。
org.redisson.client.codec.StringCodec 純粋な文字列コード(変換しません)
org.redisson.client.codec.LongCodec コーディング純粋デジタル全長(変換なし)
org.redisson.client.codec.ByteArrayCodec エンコードされたバイト配列
org.redisson.codec.CompositeCodec 互いに異なる複数のコーディングを結合するため

コーデック

コーデック

public interface Codec {

  	//返回用于HMAP Redis结构中哈希映射值的对象解码器
    Decoder<Object> getMapValueDecoder();

  	//返回用于HMAP Redis结构中哈希映射值的对象编码器
    Encoder getMapValueEncoder();

  	//返回用于HMAP Redis结构中哈希映射键的对象解码器
    Decoder<Object> getMapKeyDecoder();

  	//返回用于HMAP Redis结构中哈希映射键的对象编码器
    Encoder getMapKeyEncoder();

    //返回用于除HMAP之外的任何存储Redis结构的对象解码器
    Decoder<Object> getValueDecoder();

    //返回用于除HMAP之外的任何存储Redis结构的对象编码器
    Encoder getValueEncoder();

    //返回用于加载解码过程中使用的类的类加载器对象
    ClassLoader getClassLoader();

}

BaseCodec

org.redisson.client.codec.BaseCodec

BaseCodec

  1. 共通コーデッククラス分解プロセスを使用してオブジェクトのクラスを処理するためのHashMapコーデックのキー。

    //返回用于除HMAP之外的任何存储Redis结构的对象解码器
        Decoder<Object> getValueDecoder();
    
    //返回用于除HMAP之外的任何存储Redis结构的对象编码器
        Encoder getValueEncoder();
    

SerializationCodec

org.redisson.codec.SerializationCodec

デコーダ

SerializationCodecデコーダ

エンコーダ

SerializationCodecエンコーダ

おすすめ

転載: www.cnblogs.com/sanshengshui/p/12669011.html