做者:amchtml
引言:上一篇文章介绍了如何建立安装 TarsGo,同时也阐述了如何开始一个 TarsGo HTTP 服务。本文就要开始 TarsGo 的主力业务了:基于 TARS 自带的 RPC 协议,设计 TarsGo 服务。
本文的内容大体思路与官方 Quick Start 相同,但例子会有所不一样,同时对于一些坑也会解释得详细点。本文的代码能够在个人 GitHub repo 中找到。前端
上一篇文章中,个人 HTTP 服务器向前端返回一串 Json 字符串,其中包含了服务器时间。这一次,我设计一个服务(命名为amc.GoTarsServer.GoTarsObj
)来提供服务器时间。HTTP 服务则向这个新服务请求时间以后再返回给用户。前文提到 HTTP 服务的实例名称相对不过重要,可是供内部 RPC 调用的服务,其名称就很重要了,它是其余服务进行寻址的重要依据。c++
TARS 框架的原生 RPC 调用是使用专门设计的 tars 协议
(文件后缀名 .tars
)进行通讯的。这个协议其实也不神秘,读者能够自行尝试一下、多看一些示例,很快就能够了解了。这里我按照我本身写的协议文件来讲明吧:git
// filename: DateTime.tars module amc { struct GetTimeReq { 0 optional string timeFmt; }; struct GetTimeRsp { 0 require long utcTimestamp; // UTC UNIX timestamp 1 require long localTimestamp; 2 require string localTimeStr; }; interface DateTime { int GetTime(GetTimeReq req, out GetTimeRsp rsp); }; };
上面的协议中,其实包含了几个部分:github
struct
定义复合数据类型因此,解读上面的协议,以下:web
DateTime
下,定义了一个方法:GetTime
timeFmt
,表示以什么样的格式返回时间信息UTC
时间戳、本地时间戳和时间字符串int MethodName(MethodReq req, out MethodRsq rsp)
的模式来命名,不管是否有入参和出参,方法中的 req
和 rsp
都会存在。这种设计方式比较适合将来的扩展,若是须要添加参数或返回信息,只须要在两个 struct
中添加便可。require
属性,表示该参数是必须的;可是在之后扩展协议时,新增参数应设置为 optional
属性,保证还未升级到新版本协议的 clients 仍能正常调用。首先,咱们能够用 TarsGo 自带的工具首先生成工程模版:json
cd $GOPATH/src/github.com/TarsCloud/TarsGo/tars/tools chmod +x create_tars_server.sh ./create_tars_server.sh amc GoTarsServer GoTars
执行脚本后,在相应目录下会生成必要的源文件:segmentfault
$ cd ~/go/src/amc/GoTarsServer $ ls -l total 36 -rw-rw-r-- 1 centos centos 159 Jan 7 00:00 GoTars.tars -rw-rw-r-- 1 centos centos 303 Jan 7 00:00 gotars_imp.go -rw-rw-r-- 1 centos centos 964 Jan 7 00:00 config.conf -rw-rw-r-- 1 centos centos 422 Jan 7 00:00 main.go drwxrwxr-x 2 centos centos 4096 Jan 7 00:00 client drwxrwxr-x 2 centos centos 4096 Jan 7 00:00 debugtool -rw-rw-r-- 1 centos centos 252 Jan 7 00:00 makefile -rw-rw-r-- 1 centos centos 59 Jan 7 00:00 start.sh drwxrwxr-x 2 centos centos 4096 Jan 7 00:00 vendor
其中 GoTars.tars
文件,咱们就不须要了,用上面的 DateTime.tars
文件替换之。接着,咱们使用 TarsGo 的工具,将协议文件转换为源文件:centos
cd ~/go/src/amc/GoTarsServer tars2go DateTime.tars
执行后,tars2go
会在当前目录下,根据 .tars
文件中指定的 module
字段,生成一个新的目录。好比上面的协议文件,module
是 amc
,那么 tars2go
就生成 amc
目录。读者能够自行查看目录下的文件,若是 .tars
文件更新的话,须要再次执行 tars2go
命令刷新相应的文件——固然,我以为彻底能够调整 makefile
的逻辑来自动实现这一点。服务器
协议的实现,在 gotars_imp.go
文件中实现。下面我只列出该文件中实现的主要部分:
package main import ( "fmt" "time" "context" "github.com/TarsCloud/TarsGo/tars" amc "amc/GoTarsServer/amc" ) // GoTarsImp servant implementation type GoTarsImp struct {} // 获取log对象 var log = tars.GetLogger("logic") func (imp *GoTarsImp) GetTime(ctx context.Context, req *amc.GetTimeReq, rsp *amc.GetTimeRsp) (int32, error) { // Note 4 // get timestamp utc_time := time.Now() local_time := utc_time.Local() // convert time string var time_str string if "" == (*req).TimeFmt { log.Debug("Use default time format") time_str = local_time.Format("01/02 15:04:05 2006") } else { log.Debug(fmt.Sprintf("Got format string: %s", (*req).TimeFmt)) // ...... // ...... time_str = local_time.Format(time_str) } // construct response (*rsp).UtcTimestamp = utc_time.Unix() (*rsp).LocalTimestamp = local_time.Unix() (*rsp).LocalTimeStr = time_str return 0, nil };
针对代码里的几个注释说明以下:
tars2go
所生成的 amc
目录下的 go
文件。经过导入该包,咱们就能够 access 到咱们在前面的 .tars
文件中所定义的结构体和方法。这里实际上是写了一个基于 $GOPATH
的绝对路径来存取该包。/usr/local/app/tars/app_log/amc/GoTarsServer/
目录下生成日志文件。好比我用的 log 文件名就是:amc.GoTarsServer_logic.log
。.tars
文件中 GetTime
的实现,它做为 GoTarsImp
对象的一个方法来实现。从返回值的角度,TarsGo RPC 方法的返回值除了协议中定义的(本例中是 int
,对应于 Go 的 int32
)以外,还有一个 error
,若是须要的话,读者能够利用。细心的读者可能会发现,在上面的实现中,数据变量名和协议中定义的并不相同。是的,这就是刚转 Go 的开发者很容易遇到的坑之一:Go 语言是使用变量 / 方法 / 常量的命名方式来决定其可见性的,只有在首字母为大写的时候,该元素才能供外部访问。
笔者特地在 .tars
文件中,变量名采用了首字母小写的驼峰式命名法。读者能够看到,tars2go
会自动将变量名和方法名的首字母改成大写,以保证其可见性。请开发者注意,不然会在编译时遇到未定义错误。
如今让咱们回到 main.go
文件。其实工具自动生成的代码就差很少了,须要修改的是包导入的部分和新建app
对象语句,改成以下所示:
import ( "fmt" "os" "github.com/TarsCloud/TarsGo/tars" amc "amc/GoTarsServer/amc" ) func main() { // Get server config cfg := tars.GetServerConfig() imp := new(GoTarsImp) err := imp.Init() if err != nil { fmt.Printf("GoTarsImp init fail, err:(%s)\n", err) os.Exit(-1) } // 默认为amc.GoTars,因为使用了DateTime.tars,须要修改成amc.DateTime app := new(amc.DateTime) app.AddServantWithContext(imp, cfg.App+"."+cfg.Server+".GoTarsObj") tars.Run() }
其余不变。
这里我选择了上一篇文章中提到的 GoWebServer
来调用这个 TARS 服务。这里咱们就须要将已有的代码进行改造了。须要改造的代码是 goweb_imp.go
文件 :
package main import ( "fmt" "net/http" "github.com/TarsCloud/TarsGo/tars" amc "amc/GoTarsServer/amc" ) func HttpRootHandler(w http.ResponseWriter, r *http.Request) { comm := tars.NewCommunicator() app := new(amc.DateTime) obj := "amc.GoTarsServer.GoTarsObj" comm.SetProperty("locator", "tars.tarsregistry.QueryObj@tcp -h 192.168.211.128 -p 17890") req := amc.GetTimeReq{} rsp := amc.GetTimeRsp{} req.TimeFmt = "" comm.StringToProxy(obj, app) _, err := app.GetTime(&req, &rsp) if err != nil { // ...... 系统错误处理 } else { // ...... 从 rsp 中取出问题 } ret_str := fmt.Sprintf("{\"msg\":\"Hello, Tars-Go!\", \"time\":\"%s\"}", rsp.LocalTimeStr) w.Header().Set("Content-Type", "application/json;charset=utf-8") w.Write([]byte(ret_str)) return }
主要逻辑的说明以下:
192.168.211.128
和 17890
是 TARS 主控 tarsregistry
的地址,能够经过TarsWeb界面查看服务发布的方法在前一篇文章已经说明了。GoWebServer
只须要在原有基础上作更新操做便可。本文的 GoTarsServer
也同理。不一样的是在 协议
选项,应该选择 TARS
。
服务发布、一切正常后,参照上一篇文章,再次访问 HTTP 服务,而后咱们再查看 GoTarsServer
的 log,咱们就能够看到二者已经成功地联系起来啦。
TARS能够在考虑到易用性和高性能的同时快速构建系统并自动生成代码,帮助开发人员和企业以微服务的方式快速构建本身稳定可靠的分布式应用,从而令开发人员只关注业务逻辑,提升运营效率。多语言、敏捷研发、高可用和高效运营的特性使 TARS 成为企业级产品。
TARS微服务助您数字化转型,欢迎访问:
TARS官网:https://TarsCloud.org
TARS源码:https://github.com/TarsCloud
获取《TARS官方培训电子书》:https://wj.qq.com/s2/6570357/...
或扫码获取: