Tags golang thrift microservice
2016-02-28 23:31:14

这是我对微服务向往已久的第一次实践虽然屡屡碰壁工期紧张,但最后还是按照正确的时间完成了任务。在这里记录一下使用Thrift的感受。整个工程希望能够有清晰的文档,清晰的通讯逻辑,干净整洁的代码。其中还有很多团队的约定。
注意以下代码都只谈技术不谈业务。
我们看中了Thrift的性能。
struct UserInfo{
    1:required string ID (go.tag = 'json:"user_id" bson:"user_id"'),
    2:required string Name,
    4:optional i32 XXXXX,
}
优点
缺点
ObjectId类型,由于此造成时间浪费在结构体之间的数据互相转换。
复用结构体开发任务还是比较辛苦的。service UserService {
    // Check if the service is health
    string Ping(),
	// CreateUser: create a new user
	string CreateUser(1:string traceID, 2:string userID, 3:string password) throws (1:woerr.WoError we),
}
优点
缺点
thrift --gen go:package_prefix="github.com/wothing/thrift/" -out . user.thrift
最后生成的结果是一个包,并且里面的代码很长很难看。可以把类似上面的命令写到Makefile备用。
package main
import (
	"flag"
	"fmt"
	"git.apache.org/thrift.git/lib/go/thrift"
	"github.com/wothing/log"
	"github.com/wothing/thrift/user"
)
func main() {
	log.SetOutputLevel(log.Linfo)
	port := flag.String("p", "3001", "listening port")
	flag.Parse()
	listenAddr := fmt.Sprintf(":%s", *port)
    // Init dependance modules
    // ....................................................
	transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
	protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
	serverTransport, err := thrift.NewTServerSocket(listenAddr)
	if err != nil {
		log.Fatalf("error on creating server socket : %s", err.Error())
		return
	}
	handler := &UserServiceImpl{}
	processor := user.NewUserServiceProcessor(handler)
	server := thrift.NewTSimpleServer4(processor, serverTransport, transportFactory, protocolFactory)
	log.Infof("User Service servering in %s", listenAddr)
	if err = server.Serve(); err != nil {
		log.Errorf("User Service startup error: %s", err.Error())
	}
}
然后接下来就针对结构体UserServiceImpl进行Interface的实现就可以了。最快速最准确的方法是去找自动生成的代码复制过来,在类型中不要忘记包的引用就可以。因为他们处于不同的包的位置。
可以看到Thrift Server 初始化是依赖自动生成的包来完成服务器的初始化的,更底层的依赖于Thrift的golang源代码。如此的实现方式耦合性的确有点强。
由于们某些原因往后写的时候发现微服务呈现网状模式,客户端调用不只是Gateway所以客户端的开发独立一个包出来。
通用服务连接
func prepareConn(svcName string) (*thrift.TBinaryProtocolFactory, *thrift.TTransport, error) {
    // Sevice discoary -> address
    // ...........................
    
	transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
	protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
	transport, err := thrift.NewTSocket(address.String())
	if err != nil {
		log.Errorf("new socket to '%s' service fail: %s", svcName, err)
		return nil, nil, errors.New(woerr.ErrMicroService)
	}
	useTransport := transportFactory.GetTransport(transport)
	registerConnection(svcName, useTransport)
	if err := useTransport.Open(); err != nil {
		log.Errorf("connect to '%s' service fail: %s", svcName, err)
		return nil, nil, errors.New(woerr.ErrMicroService)
	}
	return protocolFactory, &useTransport, nil
}
具体服务的连接
func CheckUserConn(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	svcName := userSvcName
	if userService != nil {
		if _, err := userService.Ping(); err == nil {
			next(rw, r)
			return
		}
		Services[svcName].Close()
	}
	protocolFactory, useTransport, err := prepareConn(svcName)
	if err != nil {
		misc.RespondString(rw, fmt.Sprintf(`{"code":"%s", "message":"connect to %s service error"}`, woerr.ErrMicroService, svcName))
		return
	}
	userService = user.NewUserServiceClientFactory(*useTransport, protocolFactory)
	log.Infof("connected to '%s' service", svcName)
	next(rw, r)
}
此代码为negroni中间件,缺陷在于,到路由的时候都要对服务健康进行检查。
微服务的调用
func XXXX(rw http.ResponseWriter, r *http.Request) {
	// Generate TracerID
	// Check data format
	userID, err := userService.CreateUser(traceID, userID, password)
	if err != nil {
		// handle error
	}
	// HTTP response
}
Thrift 只是提供了通讯方案,其他都需要自己解决。