共计 5456 个字符,预计需要花费 14 分钟才能阅读完成。
作者:amc
引言:上一篇文章介绍了如何创立装置 TarsGo,同时也论述了如何开始一个 TarsGo HTTP 服务。本文就要开始 TarsGo 的主力业务了:基于 TARS 自带的 RPC 协定,设计 TarsGo 服务。
本文的内容大抵思路与官网 Quick Start 雷同,但例子会有所不同,同时对于一些坑也会解释得具体点。本文的代码能够在我的 GitHub repo 中找到。
设计指标
上一篇文章中,我的 HTTP 服务器向前端返回一串 Json 字符串,其中蕴含了服务器工夫。这一次,我设计一个服务(命名为amc.GoTarsServer.GoTarsObj
)来提供服务器工夫。HTTP 服务则向这个新服务申请工夫之后再返回给用户。前文提到 HTTP 服务的实例名称绝对不太重要,然而供外部 RPC 调用的服务,其名称就很重要了,它是其余服务进行寻址的重要依据。
设计协议
TARS 框架的原生 RPC 调用是应用专门设计的 tars 协定
(文件后缀名 .tars
)进行通信的。这个协定其实也不神秘,读者能够自行尝试一下、多看一些示例,很快就能够理解了。这里我依照我本人写的协定文件来阐明吧:
// 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);
};
};
协定解析
下面的协定中,其实蕴含了几个局部:
- 文件名:是协定的一部分。协定文件名其实也就是这个协定包的名称,转换工具会将其转为同名的源文件。
- 模块名:参考 C++ 的命名空间,开发者能够自在利用这个模块名。我集体喜爱维持和 App 同名。
- 接口和办法:接口(interface)内能够定义多个 RPC 办法
- 复合数据类型:应用
struct
定义复合数据类型
所以,解读下面的协定,如下:
- 在接口
DateTime
下,定义了一个办法:GetTime
- GetTime 办法蕴含两个参数,别离是两个构造体。
- 入参中蕴含了变量名
timeFmt
,示意以什么样的格局返回工夫信息 - 出参蕴含了
UTC
工夫戳、本地工夫戳和工夫字符串
集体倡议
- 在创立协定的时候,我喜爱以
int MethodName(MethodReq req, out MethodRsq rsp)
的模式来命名,不管是否有入参和出参,办法中的req
和rsp
都会存在。这种设计形式比拟适宜将来的扩大,如果须要增加参数或返回信息,只须要在两个struct
中增加即可。 - 第一次创立协定的时候,如果入参都是必要的,那么倡议均设置为
require
属性,示意该参数是必须的;然而在当前扩大协定时,新增参数应设置为optional
属性,保障还未降级到新版本协定的 clients 仍能失常调用。
服务端代码设计
创立源码模板
首先,咱们能够用 TarsGo 自带的工具首先生成工程模版:
cd $GOPATH/src/github.com/TarsCloud/TarsGo/tars/tools
chmod +x create_tars_server.sh
./create_tars_server.sh amc GoTarsServer GoTars
执行脚本后,在相应目录下会生成必要的源文件:
$ 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 的工具,将协定文件转换为源文件:
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
的绝对路径来存取该包。 - 定义了该 servant 的对象,供 server 调用——这个后文讲到 server 时会再提到。
- 应用 TARS 自带的服务器本地日志模块。该模块须要传入一个文件名参数,模块会依据该文件名,在
/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
会主动将变量名和办法名的首字母改为大写,以保障其可见性。请开发者留神,否则会在编译时遇到未定义谬误。
server 代码调用
当初让咱们回到 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
}
次要逻辑的阐明如下:
- 抉择路由,这里读者能够参照官网 Quick Start 文档的阐明来解释。这里笔者就只采纳正式用法。其中
192.168.211.128
和17890
是 TARS 主控tarsregistry
的地址,能够通过 TarsWeb 界面查看 - 筹备用于承载参数和返回值的构造体
- 这两行就是理论的 RPC 调用
公布服务
服务公布的办法在前一篇文章曾经阐明了。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/…
或扫码获取:
原文公布于:https://cloud.tencent.com/developer/article/1382458