微服务已成为市⾯流⾏且成熟的框架。备受众多企业⻘睐。
以下是对⼀个主流的golang微服务框架go-micro的简单应⽤。

安装官⽅的定义,go-micro组件⼀般可以分为:

使⽤官⽅脚⼿架可以⽣成模板代码。不同的参数,可⽣成不同环境代码,可选参数如下:

下载脚⼿架:
go install github.com/go-micro/cli/cmd/go-micro@v1.1.1
创建微服务:
go-micro new service system
go-micro new service user
go-micro new service web
创建⼯作区
go work init system user web
⾃动⽣成好的⽬录结构如下:

go-micro框架提供了很多默认配置。有时只需要简单对配置就可以发布⼀个服务。
func main() {
// Create service
srv := micro.NewService(
micro.Name(service),
micro.Version(version),
)
srv.Init()
// Register handler
pb.RegisterSystemHandler(srv.Server(), new(handler.System))
// Run service
if err := srv.Run(); err != nil {
log.Fatal(err)
}
}
内容较多,因此单独拎出来了,可点击【go-micro全量配置】查看。
由上述配置可以看到有许多重复的配置。go-micro采⽤的是循环加载配置。即放在前⾯的配置会先执⾏,后⾯的配
置后执⾏,后执⾏的配置会覆盖先执⾏的配置。
⽐如:micro.NewService(micro.Server(server.Registry(r1)), micro.Registry(r2)),后配置的注册中⼼r2,会覆盖
服务中先配置的注册中⼼r1。
以认证功能为例

web微服务采⽤gin框架作为web路由。采⽤中间件来进⾏统⼀拦截处理。
router.Use(func(c *gin.Context) {
// ⽩名单放⾏
for _, s := range micro.Service.Options().Config.Get("auth",
"whiteList").StringSlice(make([]string, 0)) {
if c.Request.URL.Path == s {
c.Next()
return
}
}
// 通过Auth组件对token进⾏认证
token := c.GetHeader(micro.AuthHeader)
if token == "" {
token, _ = c.GetQuery("token")
}
account, err := micro.Service.Options().Auth.Inspect(token)
expireAt, _ := strconv.ParseInt(account.Metadata["expireAt"], 10, 0)
if expireAt-time.Now().Unix() < int64(micro.Service.Options().Config.Get("auth",
"refreshTime").Int(0)) {
// token要到期时,⾃动续约
// 从Config组件中获取token相关配置
expireTime := micro.Service.Options().Config.Get("auth", "expireTime").Int(0)
newAccount, err := micro.Service.Options().Auth.Generate(account.ID,
auth.WithType("user"),
auth.WithMetadata(map[string]string{
"createAt": strconv.FormatInt(time.Now().Unix(), 10),
"expireAt":
strconv.FormatInt(time.Now().Add(time.Second*time.Duration(expireTime)).Unix(), 10),
}))
if err != nil {
c.Redirect(http.StatusMovedPermanently, "/login")
c.Abort()
}
newToken, err :=
micro.Service.Options().Auth.Token(auth.WithExpiry(time.Second*time.Duration(expireTime
)), auth.WithCredentials(newAccount.ID, newAccount.Secret))
if err != nil {
c.Redirect(http.StatusMovedPermanently, "/login")
c.Abort()
}
token = newToken.AccessToken
}
c.Set(micro.AuthHeader, token)
// 认证失败,跳转到登录⻚⾯
if err != nil {
c.Redirect(http.StatusMovedPermanently, "/login")
c.Abort()
}
})
在gin路由中注册/login路由。然后在路由处理函数中,使⽤Client调⽤user微服务,完成注册
protocol buffers是⼀个消息传输协议。⽤于微服务之前的消息通信。
定义proto⽂件
option go_package = "./;user";
service User { // 服务名称
rpc Login(LoginRequest) returns (LoginResponse) {} // ⽅法名称
rpc Info(InfoRequest) returns (InfoResponse) {}
}
message LoginRequest {// ⽅法请求参数
string username = 1;
string password = 2;
}
message LoginResponse {// ⽅法响应参数
string token = 1;
}
message InfoRequest {
}
message InfoResponse {
string Id = 1;
string Username = 2;
}
根据proto⽂件⽣成go代码
执⾏Makefile⾥⾯的init和proto
init:安装⽣成代码的必要软件。
proto:根据proto⽂件地址,来⽣成代码。

.PHONY: init
init:
@go get -u google.golang.org/protobuf/proto
@go install github.com/golang/protobuf/protoc-gen-go@latest
@go install github.com/asim/go-micro/cmd/protoc-gen-micro/v4@latest
.PHONY: proto
proto:
@protoc --proto_path=. --micro_out=./proto/user --go_out=:./proto/user
proto/user/user.proto
实现接⼝具体处理逻辑
实现xx.pb.mico.go⽂件中的"{服务名称}Handler"接⼝
type UserHandler interface {
Login(context.Context, *LoginRequest, *LoginResponse) error
Info(context.Context, *InfoRequest, *InfoResponse) error
}
将服务注册到go-micro中进⾏发布
在main⽅法中,调⽤xxx.pb.micro.go⽂件中的"Register{服务名称}Handler"⽅法。“server.Server"参数为定义的
微服务,”{服务名称}Handler"参数为"{服务名称}Handler"接⼝具体实现类。
func RegisterUserHandler(s server.Server, hdlr UserHandler, opts
...server.HandlerOption) error {
type user interface {
Login(ctx context.Context, in *LoginRequest, out *LoginResponse) error
Info(ctx context.Context, in *InfoRequest, out *InfoResponse) error
}
type User struct {
user
}
h := &userHandler{hdlr}
return s.Handle(s.NewHandler(&User{h}, opts...))
}
客户端通过Client.Call对对应的微服务发起请求。
loginRsp := &user.LoginResponse{}
if err := micro.Service.Options().Client.Call(context.Background(),
micro.Service.Options().Client.NewRequest("user", "User.Login",
&user.LoginRequest{Username: username, Password: password}), loginRsp); err != nil {
ctx.HTML(422, "user/login.html", gin.H{
"message": err.Error(),
})
return
}
在下期内容中,我们会从源码角度深入分析go-micro,敬请期待吧~