首页

/

归档

/

友链

/

独立黑客

/

关于


Golang和Thrift

Thrift是一款RPC协议+工具。我们团队选择了Thrift的主要原因是之前gRPC对gevent的支持不够好。目前虽然有支持,但是合并也 还没有多久。而Thrift有饿了么搞的一套,相对来说好用一些。

翻滚吧,RESTful

RESTful这些年来可谓是大红大紫,因为跨平台,human-readable等等。但是实际上我们接RESTful接口的时候,就很蛋疼了。一般 我们都这样干:

{
    "code": 200,
    "message": "success",
    "result": {
        "name": "someone like you"
    }
}
>>> name = json_dict.get("result", {}).get("name")
>>> if name:
        print(name)

所以,RESTful写一个两个还算简单,但是接多了真的是要疯。有了RPC,它会自动帮你生成native的代码,远程调用就像是调用 一个函数一样简单。不过说到底,RESTful只是一种表现形式,通过调用RESTful接口其实也是一种RPC,不过是一种蛋疼得RPC。 我们还是用Thrfit或者gRPC吧。

Thrift

Thrift有如下几个概念:

Thrift数据类型

没有unsigned的类型。论文里说原因是很多编程语言没有这玩意儿,另外据观察用的也少(其实我用的不少啊啊啊啊啊)。

Go和Thrift

Go的server类似于这样:

func rpcServer() {
    nagatoHandler := &NagatoRPCHandler{}

    transportFactory := thrift.NewTBufferedTransportFactory(BufferSize)
    protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()

    transport, err := thrift.NewTServerSocket(config.rpcAddr)

    if err != nil {
        logrus.Fatalf("failed to start rpc socket: %s", err)
    }
    processor := CustomizedTProcessor{p: nagato.NewNagatoServiceProcessor(nagatoHandler)}
    server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)

    logrus.Infof("rpc server is on %s", config.rpcAddr)
    server.Serve()
}

其中最上面的nagatoHandler就是一个 type NagatoRPCHandler struct{} 然后给他实现service里定义的方法。因为最后 RPC生成代码里的Processor其实是一个接口。实现了那些方法就好了。

更详细的例子看:https://thrift.apache.org/tutorial/go

// CustomizedTProcessor 是定制化的TProcessor,用来搞一些事情
type CustomizedTProcessor struct {
    p thrift.TProcessor
}

// Process 是为了搞事情。。。
func (c CustomizedTProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
    start := time.Now()

    // 执行
    success, err = c.p.Process(ctx, iprot, oprot)

    // 统计,然后返回
    end := time.Now()
    latency := end.Sub(start)

    var status string
    if success {
        status = "200"
    } else {
        status = "400"
    }
    /*
       endpoint暂时不好拿。可以参考生成的代码里有这么一行:
       name, _, seqId, err := iprot.ReadMessageBegin()

       但是目前我还没有看完所有的thrift代码,不敢断定是否所有的protocol实现都不会受影响。所以暂时不这么干。使用reflect
       拿出一个可以做处标识的先。

       -。-其实现在这里endpoint也标识不出啥。。。but。。。
    */
    endpoint := reflect.TypeOf(c.p).String()

    entry := logrus.WithFields(logrus.Fields{
        "request-id": "UNKNOW",
        "status":     status,
        "method":     "rpc",
        "uri":        endpoint,
        "ip":         "UNKNOW",
        "latency":    latency,
        "user-agent": "ThriftRPC",
        "time":       end.Format(time.RFC3339),
    })
    if success {
        entry.Info()
    } else {
        entry.Error(err.Error())
    }

    histogramVec.With(
        prometheus.Labels{
            "method":   "rpc",
            "endpoint": endpoint,
            "service":  "nagato",
            "status":   status,
        },
    ).Observe(latency.Seconds())

    return success, err
}

当然,目前这个实现还很粗糙。本来是可以拿到具体是哪个processor的。但是 name, _, seqId, err := iprot.ReadMessageBegin() 这一行,有点侵入到thrift的实现了。。。而且还没有读完thrift的代码,不敢乱动。。。