コレクションをお勧めします!Consul Raftライブラリを使用して、履歴書を書くのに十分な完全な分散システムを構築する方法を教えてください。

一般的なCPシステムにはetcdとconsulが含まれ、一般的なシステムの反対は専用システムであるため、場合によってはそのような要求があります。

ただし、etcd埋め込みの使い勝手は非常に悪く、Windowsで実行するとさまざまな問題が発生し、プロトコルをカスタマイズできません。etcdクラスターと通信するには、etcdとクライアントで定義されたプロトコルを使用する必要があります。今回:

1.耐える

2.いかだアルゴリズムライブラリを自分で実装し、適用します

少なくともMIT6.824を作ることができるという一定の可能性がありますが、それでも産業用アプリケーションとの大きなギャップがあります

3.工業用ラフトライブラリを見つけて、それに適用します

この時点で、Raft Consensus Algorithm移動して、braft、hashicorp / raft、lni / dragonboatなどのオプションのRaftアルゴリズムライブラリをいくつか見つけます。

ただし、C ++コードは記述が難しいため、いかだは渡され、いかだ領事とドラゴンボートだけが残ります。

この記事では、領事ラフトを使用して単純なKeyValueサービスを実行します。

まず、フロントエンドで使用されるginは、put / get / inc / deleteインターフェースを提供し、3つのインターフェースはすべて、raftステートマシンを使用します。複数のノードをサポートするため、内部の非リーダーノードはリクエストをに転送する必要があります。リーダーノード。

フロントエンドコードは次のようになります。

func (this *ApiService) Start() error {
        //转发请求给leader节点
    this.router.Use(this.proxyHandler())

    this.router.POST("/get", this.Get)
    this.router.POST("/put", this.Put)
    this.router.POST("/delete", this.Delete)
    this.router.POST("/inc", this.Inc)

    address := fmt.Sprintf(":%d", this.port)
    return this.router.Run(address)
}

リクエストは非常に単純です。つまり、サービスによって提供されるコマンドまたはプリミティブをRaftステートマシンに直接接続し、Raftステートが適用されるのを待ってから、putなどの結果(future / promiseモード)を取得します。コマンド:

func (this *ApiService) Put(ctx *gin.Context) {
    req := &Request{}
    if err := ctx.ShouldBindJSON(req); err != nil {
        ctx.JSON(http.StatusBadRequest, Response{
            Error: err.Error(),
        })
        return
    }
    result, err := this.raft.ApplyCommand(raft.CommandPut, req.Key, req.Value)
    if err != nil {
        ctx.JSON(http.StatusInternalServerError, Response{
            Error: err.Error(),
        })
        return
    }
    ctx.JSON(http.StatusOK, Response{
        Value: result.Value,
    })
}

フロントエンドには、要求をリーダーノードに転送するインターセプターもあります(?これはこの名前と呼ばれる必要があります。実際、これは一種のパイプラインモードです)

func (this *ApiService) proxyHandler() gin.HandlerFunc {
    return func(context *gin.Context) {
        if this.raft.IsLeader() {
            context.Next()
        } else {
            leaderServiceAddress := this.raft.GetLeaderServiceAddress()
            if this.leaderServiceAddress != leaderServiceAddress {
                Director := func(req *http.Request) {
                    req.URL.Scheme = "http"
                    req.URL.Host = leaderServiceAddress
                }
                this.leaderProxy = &httputil.ReverseProxy{
                    Director: Director,
                }
                this.leaderServiceAddress = leaderServiceAddress
            }
            this.leaderProxy.ServeHTTP(context.Writer, context.Request)
            context.Abort()
        }
    }
}

契約の扱いは次のとおりです

func (this *FSM) Apply(log *raft.Log) interface{} {
    result := &FSMApplyResult{
        Success: false,
    }
    t, cmd, err := raftLogToCommand(log)
    if err != nil {
        result.Error = err
        return result
    }
    binary.LittleEndian.PutUint64(keyCache, uint64(cmd.Key))
    binary.LittleEndian.PutUint64(valueCache, uint64(cmd.Value))
    switch t {
    case CommandPut:
        result.Success, result.Error = this.add(keyCache, valueCache)
    case CommandDelete:
        result.Success, result.Error = this.delete(keyCache)
    case CommandGet:
        result.Value, result.Error = this.get(keyCache)
    case CommandInc:
        result.Value, result.Error = this.inc(keyCache, cmd.Value)
    }
    return result
}

Raft状態に入力されたコマンドは、実際にはシリアル化されます。Raftステートマシンは、コマンドをストレージ(メモリ、ディスク/ DBなど)に保存します。したがって、コマンドを適用するときは、最初にraft log Decodeを実行してから、処理する。

たとえば、incの処理を見てみましょう。

func (this *FSM) inc(key []byte, add int64) (int64, error) {
    var value int64 = 0
    err := this.db.Update(func(tx *bbolt.Tx) error {
        b, err := tx.CreateBucketIfNotExists(BBoltBucket)
        if err != nil {
            return err
        }
        valueBytes := b.Get(key)
        if len(valueBytes) != 8 {
            logging.Errorf("FSM.inc, key:%d, value length:%d, Reset",
                int64(binary.LittleEndian.Uint64(key)), len(valueBytes))
            valueBytes = make([]byte, 8)
        }
        value = int64(binary.LittleEndian.Uint64(valueBytes))
        value += add
        binary.LittleEndian.PutUint64(valueBytes, uint64(value))
        err = b.Put(key, valueBytes)
        return err
    })
    if err != nil {
        return -1, err
    }
    return value, err
}

この命令はもう少し複雑です。最初にデータベースで見つける必要があります。見つけた場合は、Nを追加して保存し、新しい値を返します。ラフトステートマシンはログを順番に適用するため、必要はありません。はい、inc自体はアトミックです。

これまで、単純な分散型KeyValueサービスが実装されており、CPシステムでもあります。

もちろん、これは単なるデモであり、実際のアプリケーションはこれよりもはるかに複雑です。この記事はアイデアを提供するだけです。

Etcdに縛られる必要はありませんすべての道路がローマに通じています。システムが限定された動作原理を提供するだけでよい場合は、ラフト領事またはドラゴンボートを検討してカスタムプロトコルCPサービスを作成できます。AntのSOFARaftも可能です。 。この種のものは
最後に、私は皆のための材料を学習し、いくつかのJavaアーキテクチャを用意した学習技術コンテンツが含まれています:。春、ダボ、MyBatisの、RPC、ソースコード解析、高並行性、高パフォーマンス、分散、パフォーマンスの最適化、高度なmicroservicesアーキテクチャの開発を、など、直接受け取るにはここをクリックしてください。

おすすめ

転載: blog.csdn.net/jiagouwgm/article/details/112358217