Recomende a coleção! Ensine como usar a biblioteca Consul Raft para construir um sistema distribuído completo, o suficiente para escrever seu currículo!

Os sistemas comuns de CP incluem etcd e consul. O oposto do comum é um sistema dedicado. Portanto, em algumas ocasiões, há essa demanda.

No entanto, a usabilidade do etcd embed é extremamente pobre. Haverá vários problemas ao executar no Windows, e o protocolo não pode ser personalizado. Você deve usar o protocolo definido pelo etcd e o cliente para se comunicar com o cluster etcd. Portanto, a escolha em desta vez:

1. suportar

2. Implemente você mesmo uma biblioteca de algoritmos de jangada e aplique-a nela

Existe uma certa possibilidade, pelo menos MIT 6.824 pode ser feito, mas ainda há uma grande lacuna com aplicações industriais

3. Encontre uma biblioteca de jangadas de nível industrial e, em seguida, aplique-a nela

Neste momento, vá para o Algoritmo de consenso de jangada para encontrar várias bibliotecas de algoritmos opcionais de jangada, como braft, hashicorp / raft, lni / dragonboat.

No entanto, o código C ++ é mais difícil de escrever, então o braft é passado. Apenas a balsa do cônsul e a dragonboat permanecem.

Este artigo usa o consul raft para fazer um serviço KeyValue simples.

Em primeiro lugar, o gin usado no front-end fornece interfaces put / get / inc / delete, e todas as três interfaces usam a máquina de estado raft. Como ele suporta vários nós, os nós não líderes internos precisam encaminhar a solicitação para o nó líder.

O código de front-end é parecido com este:

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)
}

A solicitação é muito simples, ou seja, conecte diretamente o comando ou a primitiva fornecida pelo serviço na máquina de estado da Balsa e aguarde a aplicação do estado da Balsa, para então obter o resultado (modo futuro / promessa), como o put comando:

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,
    })
}

Há também um interceptor no front-end que encaminha a solicitação para o nó líder (? Ele deve ser chamado desse nome, na verdade, é uma espécie de modo pipeline)

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()
        }
    }
}

O seguinte é o tratamento do acordo :

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
}

Os comandos de entrada para o estado da jangada são na verdade serializados. A máquina de estado da jangada salvará os comandos no armazenamento (memória ou disco / banco de dados, etc.). Portanto, ao aplicar o comando, primeiro execute a decodificação do log da jangada e, em seguida, mude para processo.

Vamos dar uma olhada no processamento de, por exemplo, 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
}

Esta instrução é um pouco mais complicada. Você precisa encontrá-la no banco de dados primeiro. Se você encontrar, adicione um N, armazene-o e retorne o novo valor. Porque a máquina de estado da jangada aplica o registro sequencialmente, então não há necessidade para travá-lo. Sim, o próprio inc é atômico.

Até o momento, foi implementado um serviço KeyValue simples distribuído, que também é um sistema CP.

Claro, isso é apenas uma demonstração, o aplicativo real é muito mais complicado do que isso, este artigo apenas fornece uma ideia.

Você não precisa se amarrar ao Etcd, todos os caminhos levam a Roma . Se o seu sistema só precisa fornecer princípios operacionais limitados, você pode considerar Consul Raft ou DragonBoat para criar um serviço de CP de protocolo personalizado. Ant's SOFARaft também Você pode fazer este tipo de coisa.
Por fim, preparei alguns materiais de aprendizagem de arquitetura Java para todos. O conteúdo de tecnologia de aprendizagem inclui: Spring, Dubbo, MyBatis, RPC, análise de código-fonte, alta simultaneidade, alto desempenho, distribuído, otimização de desempenho, microsserviços avançados Desenvolvimento de arquitetura , etc., clique aqui para receber diretamente.

Acho que você gosta

Origin blog.csdn.net/jiagouwgm/article/details/112358217
Recomendado
Clasificación