mysql sock golang_golang thrift 总结一下网络上的一些坑

我们以hello world来大概分析一下golang中的thrift包,并且扒一扒网络上有关thrift的一些坑

查看源码,服务器定义如下:(详见simple_server.go文件)

type TSimpleServer struct{

quit chanstruct{}

stopped int64

processorFactory TProcessorFactory //实质是一个handler,用来相应客户端的请求

serverTransport TServerTransport //实质是一个socket

inputTransportFactory TTransportFactory //实质是传输协议的具体操作类(详细可见transport.go文件中TTransport结构体)

outputTransportFactory TTransportFactory //

inputProtocolFactory TProtocolFactory //实质是传输协议(有compact、simplejson、json、binary四种协议,默认是binary)

tputProtocolFactory TProtocolFactory //

}

在go语言中,创建一个thrift服务器有三种方法:(详见simple_server.go文件)

func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {returnNewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)

}

func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory)*TSimpleServer {returnNewTSimpleServerFactory4(NewTProcessorFactory(processor),

serverTransport,

transportFactory,

protocolFactory,

)

}

func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory)*TSimpleServer {returnNewTSimpleServerFactory6(NewTProcessorFactory(processor),

serverTransport,

inputTransportFactory,

outputTransportFactory,

inputProtocolFactory,

outputProtocolFactory,

)

}

这三个函数分别调用了工厂函数

NewTSimpleServerFactory2;

NewTSimpleServerFactory4;

NewTSimpleServerFactory6;

func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {returnNewTSimpleServerFactory6(processorFactory,

serverTransport,

NewTTransportFactory(),

NewTTransportFactory(),

NewTBinaryProtocolFactoryDefault(),

NewTBinaryProtocolFactoryDefault(),

)

}

func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory)*TSimpleServer {returnNewTSimpleServerFactory6(processorFactory,

serverTransport,

transportFactory,

transportFactory,

protocolFactory,

protocolFactory,

)

}

func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory)*TSimpleServer {return &TSimpleServer{

processorFactory: processorFactory,

serverTransport: serverTransport,

inputTransportFactory: inputTransportFactory,

outputTransportFactory: outputTransportFactory,

inputProtocolFactory: inputProtocolFactory,

outputProtocolFactory: outputProtocolFactory,

quit: make(chanstruct{}, 1),

}

}

好啦!现在假如我们需要创建一个以二进制协议传输的thrift服务器,那么可以用如下代码简单实现:

serverTransport, err := thrift.NewTServerSocket("127.0.0.1:8808")if err !=nil {

fmt.Println("Error!", err)return}

handler := &rpcService{}

processor :=rpc.NewRpcServiceProcessor(handler)

server :=thrift.NewTSimpleServer2(processor, serverTransport)

fmt.Println("thrift server in localhost")

server.Serve()

另外我在网上查看这方面资料的时候,发现大家都用的NewTSimpleServer4这个函数,然后自己又创建一遍NewTTransportFactory以及NewTBinaryProtocolFactoryDefault。

现在我们分析一下源码,发现此举实乃多此一举。这是第一坑。

接下来说说如何用golang thrift编写客户端,查看网络上的一些写法,发现根本用不了,服务器会阻塞住!还是从源码来分析:

在thrift自动生成的代码中,会生成一个关于客户端的示例。

cdec0645add3fc3c328197dda5c76203.png

81178cc93a2a3bb5048d90d76e7ec935.png

//Autogenerated by Thrift Compiler (0.9.3)//DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING

package main

import ("flag"

"fmt"

"git.apache.org/thrift.git/lib/go/thrift"

"math"

"net"

"net/url"

"os"

"strconv"

"strings"

"vic/rpc")

func Usage() {

fmt.Fprintln(os.Stderr,"Usage of", os.Args[0], "[-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:")

flag.PrintDefaults()

fmt.Fprintln(os.Stderr,"\nFunctions:")

fmt.Fprintln(os.Stderr,"Video request(string vid, string cid, string platform, string url, string clientVersion)")

fmt.Fprintln(os.Stderr)

os.Exit(0)

}

func main() {

flag.Usage=Usagevar host string

var port int

var protocol string

var urlString string

var framed bool

var useHttp bool

varparsedUrl url.URLvartrans thrift.TTransport

_=strconv.Atoi

_=math.Abs

flag.Usage=Usage

flag.StringVar(&host, "h", "localhost", "Specify host and port")

flag.IntVar(&port, "p", 9090, "Specify port")

flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)")

flag.StringVar(&urlString, "u", "", "Specify the url")

flag.BoolVar(&framed, "framed", false, "Use framed transport")

flag.BoolVar(&useHttp, "http", false, "Use http")

flag.Parse()if len(urlString) > 0{

parsedUrl, err :=url.Parse(urlString)if err !=nil {

fmt.Fprintln(os.Stderr,"Error parsing URL:", err)

flag.Usage()

}

host=parsedUrl.Host

useHttp= len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http"}else ifuseHttp {

_, err := url.Parse(fmt.Sprint("http://", host, ":", port))if err !=nil {

fmt.Fprintln(os.Stderr,"Error parsing URL:", err)

flag.Usage()

}

}

cmd := flag.Arg(0)varerr errorifuseHttp {

trans, err=thrift.NewTHttpClient(parsedUrl.String())

}else{

portStr :=fmt.Sprint(port)if strings.Contains(host, ":") {

host, portStr, err=net.SplitHostPort(host)if err !=nil {

fmt.Fprintln(os.Stderr,"error with host:", err)

os.Exit(1)

}

}

trans, err=thrift.NewTSocket(net.JoinHostPort(host, portStr))if err !=nil {

fmt.Fprintln(os.Stderr,"error resolving address:", err)

os.Exit(1)

}ifframed {

trans=thrift.NewTFramedTransport(trans)

}

}if err !=nil {

fmt.Fprintln(os.Stderr,"Error creating transport", err)

os.Exit(1)

}

defer trans.Close()varprotocolFactory thrift.TProtocolFactoryswitchprotocol {case "compact":

protocolFactory=thrift.NewTCompactProtocolFactory()break

case "simplejson":

protocolFactory=thrift.NewTSimpleJSONProtocolFactory()break

case "json":

protocolFactory=thrift.NewTJSONProtocolFactory()break

case "binary", "":

protocolFactory=thrift.NewTBinaryProtocolFactoryDefault()break

default:

fmt.Fprintln(os.Stderr,"Invalid protocol specified:", protocol)

Usage()

os.Exit(1)

}

client :=rpc.NewVideoServiceClientFactory(trans, protocolFactory)if err := trans.Open(); err !=nil {

fmt.Fprintln(os.Stderr,"Error opening socket to", host, ":", port, " ", err)

os.Exit(1)

}switchcmd {case "request":if flag.NArg()-1 != 5{

fmt.Fprintln(os.Stderr,"Request requires 5 args")

flag.Usage()

}

argvalue0 := flag.Arg(1)

value0 :=argvalue0

argvalue1 := flag.Arg(2)

value1 :=argvalue1

argvalue2 := flag.Arg(3)

value2 :=argvalue2

argvalue3 := flag.Arg(4)

value3 :=argvalue3

argvalue4 := flag.Arg(5)

value4 :=argvalue4

fmt.Print(client.Request(value0, value1, value2, value3, value4))

fmt.Print("\n")break

case "":

Usage()break

default:

fmt.Fprintln(os.Stderr,"Invalid function", cmd)

}

}

View Code

我们一部分一部分来分析分析:

flag.Usage =Usagevar host string

var port int

var protocol string

var urlString string

var framed bool

var useHttp bool

varparsedUrl url.URLvartrans thrift.TTransport

_=strconv.Atoi

_=math.Abs

flag.Usage=Usage

flag.StringVar(&host, "h", "localhost", "Specify host and port")

flag.IntVar(&port, "p", 9090, "Specify port")

flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)")

flag.StringVar(&urlString, "u", "", "Specify the url")

flag.BoolVar(&framed, "framed", false, "Use framed transport")

flag.BoolVar(&useHttp, "http", false, "Use http")

flag.Parse()

这些代码是设置了一些程序的启动命令,例如默认地址是loacalhost,我们可以根据client.exe -h xxx.xxx.xxx.xxx之类的命令来修改

我们发现这些代码都不是我们需要的,pass,继续看

if len(urlString) > 0{

parsedUrl, err :=url.Parse(urlString)if err !=nil {

fmt.Fprintln(os.Stderr,"Error parsing URL:", err)

flag.Usage()

}

host=parsedUrl.Host

useHttp= len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http"}else ifuseHttp {

_, err := url.Parse(fmt.Sprint("http://", host, ":", port))if err !=nil {

fmt.Fprintln(os.Stderr,"Error parsing URL:", err)

flag.Usage()

}

}

cmd := flag.Arg(0)varerr errorifuseHttp { trans, err = thrift.NewTHttpClient(parsedUrl.String())}else{

portStr :=fmt.Sprint(port)if strings.Contains(host, ":") {

host, portStr, err=net.SplitHostPort(host)if err !=nil {

fmt.Fprintln(os.Stderr,"error with host:", err)

os.Exit(1)

}

}

trans, err= thrift.NewTSocket(net.JoinHostPort(host, portStr))

if err !=nil {

fmt.Fprintln(os.Stderr,"error resolving address:", err)

os.Exit(1)

}ifframed {

trans=thrift.NewTFramedTransport(trans)

}

}

这部分主要作用是解析url参数,从中取得host以及port。并且用于生成一个TTransport,上面红线加粗的函数定义在源码中如下:

func NewTHttpClient(urlstr string) (TTransport, error) {returnNewTHttpClientWithOptions(urlstr, THttpClientOptions{})

}

func NewTSocket(hostPortstring) (*TSocket, error) {return NewTSocketTimeout(hostPort, 0)

}

细心的朋友们可能发现了端倪,第二个函数的返回值是一个TSocket指针,并不是TTransport,是不是有啥问题?不急,我们看看这两个结构体的定义就知道了:

type TTransport interface{

io.ReadWriteCloser

Flusher

ReadSizeProvider//Opens the transport for communication

Open() error//Returns true if the transport is open

IsOpen() bool}

原来TTransport是一个接口类型,而TSocket则实现了该接口!

目前为止,我们获得了创建客户端所需要的关键代码:

trans, err =thrift.NewTHttpClient(parsedUrl.String())

trans, err= thrift.NewTSocket(net.JoinHostPort(host, portStr))

OK,继续分析示例!

varprotocolFactory thrift.TProtocolFactoryswitchprotocol {case "compact":

protocolFactory=thrift.NewTCompactProtocolFactory()break

case "simplejson":

protocolFactory=thrift.NewTSimpleJSONProtocolFactory()break

case "json":

protocolFactory=thrift.NewTJSONProtocolFactory()break

case "binary", "":

protocolFactory=thrift.NewTBinaryProtocolFactoryDefault()break

default:

fmt.Fprintln(os.Stderr,"Invalid protocol specified:", protocol)

Usage()

os.Exit(1)

}

client := rpc.NewVideoServiceClientFactory(trans, protocolFactory)if err := trans.Open(); err !=nil {

fmt.Fprintln(os.Stderr,"Error opening socket to", host, ":", port, " ", err)

os.Exit(1)

}

switch语句是根据我们所输入的参数,选择传输协议。最后通过NewVideoServiceClientFactory函数 完成客户端的创建

最后,总结一下,假如我们要创建一个以二进制为传输协议,那么我们可以编写如下代码来完成:

transport, err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1", "8808"))if err !=nil {

fmt.Fprintln(os.Stderr,"error resolving address:", err)

os.Exit(1)

}

protocolFactory :=thrift.NewTBinaryProtocolFactoryDefault()

client :=NewRpcServiceClientFactory(transport, protocolFactory)if err := transport.Open(); err !=nil {

fmt.Fprintln(os.Stderr,"Error opening socket to 127.0.0.1:8808", " ", err)

os.Exit(1)

}

defer transport.Close()

res, _ := client.SayHi(“World”)

有疑问加站长微信联系(非本文作者)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值