化茧成蝶:Go在FreeWheel服务化中的实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

快速开始

Wheels命令行简介

当编译安装好Wheels后,你就可以在命令行运行wheels -h命令获取基本使用帮助。

        $ wheels -h
        The output should look like:

        Usage:
          wheels [command]

        Available Commands:
          console     Runs your Wheels app in a REPL console
          generate    A collection of generators
          new         Creates a new Wheels application
          server      Starts the Wheels application server

        Use "wheels [command] --help" for more information about a command.

接下来可以使用子命令加上-h参数获取子命令的使用细节,如:

        $ wheels generate -h
        It will output something like:

        A collection of generators

        Usage:
          wheels generate [command]

        Aliases:
          generate,  g

        Available Commands:
          proto       Generates new proto
          service     Generates new service

        Use "wheels generate [command] --help" for more information about
        a command.

让我们进一步挖掘:

        $ wheels generate service -h

我们在这里省略了输出。通过内置的帮助系统,实际上使用者可以很容易地获取所有命令的使用方法。

创建新项目

现在,让我们来创建一个叫mysrv的新项目,我们将在这个项目中开发我们的服务。

        $ wheels new mysrv

执行该命令后,一个叫做mysrv的新目录将自动被创建。

        $ tree mysrv
        mysrv
       ├—— app
        |   └—— services
       ├—— config
        |   ├—— config.go
        |   ├—— config.yml
        |   └—— dev
        |       └—— config.yml
       ├—— main.go
       └—— proto

创建服务描述

接下来,我们将生成一个使用Protobuf语言描述的服务定义文件。假设我们定义的是一个叫hello的服务

        $ wheels generate proto hello

执行完该命令后,一个叫hello.proto的文件被自动生成在了项目的proto文件夹下。

        ├—— app
        |   └—— services
        ├—— config
        |   ├—— config.go
        |   ├—— config.yml
        |   └—— dev
        |       └—— config.yml
        ├—— main.go
        └—— proto
            └—— hello.proto

我们依靠自动生成的hello.proto模板并稍加编辑得到以下内容:

        syntax = "proto3";

        package proto;

        import "google/api/annotations.proto";

        service HelloService {
          rpc SayHello (HelloRequest) returns (HelloReply) {
              option (google.api.http) = { get: "/v1/hello/{name}" };
          }
        }

        message HelloRequest {
          string name = 1;
        }

        message HelloReply {
          string msg = 1;
        }

利用服务描述生成实现代码

现在我们有了服务的基本定义,可以利用它来生成实际的实现代码骨架。

        $ wheels generate service hello --proto hello
        Some new files will be generated:
        ├—— app
        |   └—— services
        |       ├—— hello.go
        ├—— config
        |   ├—— config.go
        |   ├—— config.yml
        |   └—— dev
        |       └—— config.yml
        ├—— main.go
        └—— proto
            ├—— hello.pb.go
            ├—— hello.pb.gw.go
            └—— hello.proto

我们注意到自动生成的app/services/hello.go文件,可以在其中实现我们的具体业务逻辑。

        package services

        import (
            proto "mysrv/proto"
            "golang.org/x/net/context"
        )

        func (s *Server) SayHello(ctx context.Context,  req *proto.
        HelloRequest) (*proto.HelloReply,  error) {
          // TODO: implement your logic here
        }
        Let's add some code in this function

        package services
        import (
            proto "[your_directory]/proto"
            "golang.org/x/net/context"
        )

        func (s *Server) SayHello(ctx context.Context,  req *proto.
        HelloRequest) (*proto.HelloReply,  error) {
            return &proto.HelloReply{Msg: "Hello,  " + req.Name},  nil
        }

运行服务

现在服务已经准备好运行了,让我们来试试:

        $ go run main.go

好的,服务已经启动,并且监听在默认的8080端口上,可以在这个端口上同时服务HTTP和gRPC接口。

接下来,我们可以利用Wheels框架来自动生成gRPC客户端,通过gRPC协议访问服务。

        $ wheels generate client hello --proto hello

当然它也是支持HTTP协议的:

        $ curl http://localhost:8080/v1/hello/wheels
        {"msg":"Hello,  wheels"}

好了,我们现在已经快速实现了第一个非常简单的服务。现在可以再来实现一个稍微复杂点的服务。

添加另一个服务

现在我们再来添加一个服务,这个服务可以创建和查询用户。

        $ wheels generate proto user

proto/user.proto

        syntax = "proto3";

        package proto;

        import "google/api/annotations.proto";

        service UserService {
          rpc GetUser (GetUserRequest) returns (User) {
              option (google.api.http) = { get: "/v1/users/{id}" };
          }

          rpc CreateUser (CreateUserRequest) returns (User) {
            option (google.api.http) = {
                  post: "/v1/users"
                  body: "user"
            };
          }
        }

        message User {
          int64 id = 1;
          string name = 2;
        }

        message GetUserRequest {
          int64 id = 1;
        }

        message CreateUserRequest {
          User user = 1;
        }

生成服务骨架

        $ wheels generate service user --proto user

编辑自动生成的app/services/user.go来实现这个服务。

app/services/user.go

        package services

        import (
            "golang.org/x/net/context"
            "mysrv/proto"
        )

        func (s *Server) GetUser(ctx context.Context,  req *proto.
        GetUserRequest) (*proto.User,  error) {
            return &proto.User{Id: req.Id},  nil
        }

        func (s *Server) CreateUser(ctx context.Context,  req *proto.
        CreateUserRequest) (*proto.User,  error) {
            return &proto.User{Name: req.User.Name},  nil
        }

运行服务

我们可以通过gRPC和HTTP访问这个服务,如使用HTTP:

        $ curl http://localhost:8080/v1/users/2
        {"id":"2"}

        $ curl -d "{ \"name\": \"wheels\" }" http://localhost:8080/v1/
        users
        {"name":"wheels"}

        # Wrong URL
        $ curl http://localhost:8080/v1/user/2
        Not Found

生成Swagger文档

我们还可以利用Wheels的命令行工具,从Protobuf服务描述文件生成Swagger文档,用于服务的API参考文档或者发布给客户的文档。

        $ wheels generate swagger --proto hello

proto/hello.swagger.json

        {
          "swagger": "2.0",
          "info": {
            "title": "proto/hello.proto",
            "version": "version not set"
          },
          "schemes": [
            "http",
            "https"
          ],
          "consumes": [
            "application/json"
          ],
          "produces": [
            "application/json"
          ],
          "paths": {
            "/v1/hello/{name}": {
              "get": {
                "operationId": "SayHello",
                "responses": {
                  "200": {
                    "description": "",
                    "schema": {
                      "$ref": "#/definitions/protoHelloReply"
                    }
                  }
                },
                "parameters": [
                  {
                    "name": "name",
                    "in": "path",
                    "required": true,
                    "type": "string"

                  }
                ],
                "tags": [
                    "HelloService"
                ]
              }
            }
          },
          "definitions": {
            "protoHelloReply": {
              "type": "object",
              "properties": {
                "msg": {
                    "type": "string"
                }
              }
            },
            "protoHelloRequest": {
              "type": "object",
              "properties": {
                "name": {
                    "type": "string"
                }
              }
            }
          }
        }