ALPHA: Lile当前正处于Alpha版本,可能还会存在一些变化。目前,我正在收集反馈意见,并将尽快敲定Lile,以避免发生变化。
Lile是可以帮助您快速创建基于gRPC通讯,或者可以通过gateway创建REST通讯的发布订阅服务的一个生成器和工具集。
Lile主要是用于过创建基本结构,测试示例,Dockerfile,Makefile等基础骨架。
Lile也是一个简单的服务生成器,扩展了基本的gRPC服务器,包括诸如指标(如Prometheus),跟踪(如Zipkin)和发布订阅(如Google PubSub等可插拔选项。
Lile在的Slack上的Gopher Slack 交流渠道#lile
安装Lile很容易,使用go get
便可以安装Lile的命令行工具来生成新的服务和所需的库。
$ go get -u github.com/lileio/lile/...
您还需要安装Google的Protocol Buffers。
通过执行lile run
和一个短文件路径生成一个新的服务。
Lilek可以自动根据username/service
生成一个完整的路径到$GOPATH
下的github.com
中。
$ lile new lileio/users
首先,你需要确保您在您安装Lile之前已经安装了Go。
安装Lile很容易,使用go get
便可以安装Lile的命令行工具来生成新的服务和所需的库。
$ go get github.com/lileio/lile/...
您还需要安装Google的[Protocol Buffers]Protocol Buffers。
在MacOS你可以使用brew install protobuf
来安装。
Lile使用生成器来快速生成新的Lile服务。
Lile遵循Go关于$GOPATH的约定(参见如何写Go),并且自动解析您的新服务的名称,以在正确的位置创建服务。
如果您的Github用户名是lileio,并且您想创建一个新的服务为了发布消息到Slack,您可以使用如下命令:
lile new lileio/slack
这将创建一个项目到$GOPATH/src/github.com/lileio/slack
Lile服务主要使用gRPC,因此使用protocol buffers作为接口定义语言(IDL),用于描述有效负载消息的服务接口和结构。 如果需要,可以使用其他替代品。
我强烈建议您先阅读Google API设计文档,以获得有关RPC方法和消息的一般命名的好建议,以及如果需要,可以将其转换为REST/JSON。
您可以在Lile中发现一个简单的例子account_service
service AccountService {
rpc List (ListAccountsRequest) returns (ListAccountsResponse) {}
rpc GetById (GetByIdRequest) returns (Account) {}
rpc GetByEmail (GetByEmailRequest) returns (Account) {}
rpc AuthenticateByEmail (AuthenticateByEmailRequest) returns (Account) {}
rpc GeneratePasswordToken (GeneratePasswordTokenRequest) returns (GeneratePasswordTokenResponse) {}
rpc ResetPassword (ResetPasswordRequest) returns (Account) {}
rpc ConfirmAccount (ConfirmAccountRequest) returns (Account) {}
rpc Create (CreateAccountRequest) returns (Account) {}
rpc Update (UpdateAccountRequest) returns (Account) {}
rpc Delete (DeleteAccountRequest) returns (google.protobuf.Empty) {}
}
默认情况下,Lile将创建一个RPC方法和一个简单的请求和响应消息。
syntax = "proto3";
option go_package = "github.com/lileio/slack";
package slack;
message Request {
string id = 1;
}
message Response {
string id = 1;
}
service Slack {
rpc Read (Request) returns (Response) {}
}
我们来修改一下使它能够提供真正的服务,并添加自己的方法。
我们来创建一个Announce
方法向Slack发布消息。
我们假设Slack团队和身份验证已经由服务配置来处理,所以我们服务的用户只需要提供一个房间和他们的消息。 该服务将发送特殊的空响应,因为我们只需要知道是否发生错误,也不需要知道其他任何内容。
现在我们的proto
文件看起来像这样:
syntax = "proto3";
option go_package = "github.com/lileio/slack";
import "google/protobuf/empty.proto";
package slack;
message AnnounceRequest {
string channel = 1;
string msg = 2;
}
service Slack {
rpc Announce (AnnounceRequest) returns (google.protobuf.Empty) {}
}
现在我们运行protoc
工具我们的文件,以及Lile生成器插件。
protoc -I . slack.proto --lile-server_out=. --go_out=plugins=grpc:$GOPATH/src
Lile提供了一个Makefile
,每个项目都有一个已经配置的proto
构建步骤。 所以我们可以运行它。
make proto
我们可以看到,Lile将在server
目录中为我们创建两个文件。
$ make proto
protoc -I . slack.proto --lile-server_out=. --go_out=plugins=grpc:$GOPATH/src
2017/07/12 15:44:01 [Creating] server/announce.go
2017/07/12 15:44:01 [Creating test] server/announce_test.go
我们来看看Lile为我们创建的announce.go
文件。
package server
import (
"errors"
"github.com/golang/protobuf/ptypes/empty"
"github.com/lileio/slack"
context "golang.org/x/net/context"
)
func (s SlackServer) Announce(ctx context.Context, r *slack.AnnounceRequest) (*empty.Empty, error) {
return nil, errors.New("not yet implemented")
}
接下来我们实现这个生成的方法,让我们从测试开始吧!
当您使用Lile生成RPC方法时,也会创建一个对应的测试文件。例如,给定我们的announce.go
文件,Lile将在同一目录中创建announce_test.go
看起来如下:
package server
import (
"testing"
"github.com/lileio/slack"
"github.com/stretchr/testify/assert"
context "golang.org/x/net/context"
)
func TestAnnounce(t *testing.T) {
ctx := context.Background()
req := &slack.AnnounceRequest{}
res, err := cli.Announce(ctx, req)
assert.Nil(t, err)
assert.NotNil(t, res)
}
您现在可以使用Makefile
运行测试,并运行make test
命令
$ make test
=== RUN TestAnnounce
--- FAIL: TestAnnounce (0.00s)
Error Trace: announce_test.go:16
Error: Expected nil, but got: &status.statusError{Code:2, Message:"not yet implemented", Details:[]*any.Any(nil)}
Error Trace: announce_test.go:17
Error: Expected value not to be nil.
FAIL
coverage: 100.0% of statements
FAIL github.com/lileio/slack/server 0.011s
make: *** [test] Error 2
我们的测试失败了,因为我们还没有实现我们的方法,在我们的方法中返回一个“未实现”的错误。
让我们在announce.go
中实现Announce
方法,这里是一个使用nlopes
的slack library的例子。
package server
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/golang/protobuf/ptypes/empty"
"github.com/lileio/slack"
sl "github.com/nlopes/slack"
context "golang.org/x/net/context"
)
var api = sl.New(os.Getenv("SLACK_TOKEN"))
func (s SlackServer) Announce(ctx context.Context, r *slack.AnnounceRequest) (*empty.Empty, error) {
_, _, err := api.PostMessage(r.Channel, r.Msg, sl.PostMessageParameters{})
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
return &empty.Empty{}, nil
}
我们再次修改我们的测试用力,然后再次运行我们的测试
package server
import (
"testing"
"github.com/lileio/slack"
"github.com/stretchr/testify/assert"
context "golang.org/x/net/context"
)
func TestAnnounce(t *testing.T) {
ctx := context.Background()
req := &slack.AnnounceRequest{
Channel: "@alex",
Msg: "hellooo",
}
res, err := cli.Announce(ctx, req)
assert.Nil(t, err)
assert.NotNil(t, res)
}
现在如果我使用我的Slack令牌作为环境变量运行测试,我应该看到通过测试!
$ alex@slack: SLACK_TOKEN=zbxkkausdkasugdk make test
go test -v ./... -cover
? github.com/lileio/slack [no test files]
=== RUN TestAnnounce
--- PASS: TestAnnounce (0.32s)
PASS
coverage: 75.0% of statements
ok github.com/lileio/slack/server 0.331s coverage: 75.0% of statements
? github.com/lileio/slack/slack [no test files]
? github.com/lileio/slack/slack/cmd [no test files]
? github.com/lileio/slack/subscribers [no test files]
生成您的服务时,Lile生成一个命令行应用程序。 您可以使用自己的命令行扩展应用程序或使用内置的命令行来运行服务。
运行没有任何参数的命令行应用程序将打印生成的帮助。
例如go run orders/main.go
运行serve
将运行RPC服务。
运行subscribe
订阅者将会收到你的订阅发布事件。
运行up
将同时运行RPC服务器和发布订阅的订阅者。
要添加您自己的命令行,您可以使用cobra,它是Lile的内置的命令行生成器。
$ cd orders
$ cobra add import
您现在可以编辑生成的文件,以创建您的命令行,cobra
会自动将命令行的名称添加到帮助中。
默认情况下,Lile将Prometheus的采集指标暴露在:9000/metrics
。
如果您的服务正在运行,您可以使用cURL来预览Prometheus指标。
$ curl :9000/metrics
你应该看到如下的一些输出:
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
...
...
Lile的Prometheus指标实现使用go-grpc-promesheus的拦截器将其内置到自身的gPRC中,提供如以下的指标:
grpc_server_started_total
grpc_server_msg_received_total
grpc_server_msg_sent_total
grpc_server_handling_seconds_bucket
有关使用Prometheus的更多信息,收集和绘制这些指标,请参阅Prometheus入门
有关gRPC Prometheus查询的示例,请参阅查询示例。
虽然大多数服务将主要通过RPC进行通信,但Lile提供了一个pubsub来执行Publish & Subscribe通信,这在开发异步的请求时特别有用。
You can either manually publish events or use the gRPC middleware to automatically publish an event when a RPC method is called.
发布者与订阅者是松耦合的,甚至不知道它们的存在。大多数Lile已经内置了事件钩子,但是您也可以很容易的添加事件到您的服务中。您可以手动发布事件或使用gRPC中间件在调用RPC方法时自动发布事件。
Lile的发布订阅是基于每个用户存在“至少一次”消息传递。换句话说,给定一个发布事件account_created
的account_service
(发布者),如果email_service
(subscriber)和fraud_detection_service
(subscriber)的多个实例正在运行,则每个email_service
和fraud_detection_service
都只有一个实例收到一条消息。
如果配置了发布订阅(通过环境自动或手动进行),那么只需要一个简单的“Publish”调用。
以下是“订单”服务的“Get”方法示例:
func (s OrdersServer) Get(ctx context.Context, r *orders.GetRequest) (*orders.GetResponse, error) {
o, err := getOrder(r.Id)
if err != nil {
return nil, err
}
res := &orders.GetResponse{
Id: o.Id,
Name: o.Name,
}
pubsub.Publish(ctx, "orders_service.Get", res)
return res, nil
}
Publish
需要一个context.Context
用于跟踪和指标,一个主题名称和一个proto.Msg
,它是可以序列化到protobuf的任何对象。
当使用了Lile的gPRC中间件,在调用gRPC方法时,可以自动发布事件。
在我们的main.go
中,我们可以添加发布订阅拦截器,将我们的gRPC方法映射到我们的发布订阅主题。
拦截器将自动发布gRPC响应到该主题。
lile.AddPubSubInterceptor(map[string]string{
"Create": "account_service.created",
"Update": "account_service.updated",
})
默认情况下,Lile将在项目中生成一个具有一些订阅事件基本设置的subscribers.go
文件。
lile.Subscriber
借口用于订阅符合Setup
事件规范任何主题的事件。
type OrdersServiceSubscriber struct{}
func (s *OrdersServiceSubscriber) Setup(c *pubsub.Client) {
c.On("shipments.updated", s.ShipmentUpdate, 30*time.Second, true)
}
func (s *OrdersServiceSubscriber) ShipmentUpdate(sh *shipments.Shipment) {
// do something with sh
}
Handler interface是用于监听符合发布订阅的任何内容的功能。
Protobuf消息自动解码。
Lile已经建立了跟踪,将opentracing 兼容的跟踪器设置为GlobalTracer
,默认情况下,Lile报告所有gRPC方法和发布/订阅操作。
要通过HTTP将所有跟踪事件发送到Zipkin,请将环境变量ZIPKIN_SERVICE_HOST
设置为Zipkin服务的DNS名称。 如果服务已经运行命名为“zipkin”,Kubernetes将自动将ZIPKIN_SERVICE_HOST
暴露给容器。
如果你想使用追踪而又不想维护Zipkin,Stackdriver提供了一个zipkin-collector
映像,它将侦听Zipkin跟踪,转换并发送到Stackdriver。更多请参阅Google Cloud Tracing