Helloworld

未匹配的标注

Helloworld

内容目录

  1. 安装
  2. 运行服务
  3. 调用服务
    1. 通过命令行
    2. 通过框架
    3. 从其他语言
  4. 创建服务
  5. 存储
    1. 接口即构建基块
    2. 使用存储
      1. 通过命令行
      2. 通过框架
  6. 更新服务
  7. 配置
    1. 通过命令行
    2. 通过框架
  8. 深入阅读

安装

# MacOS
curl -fsSL https://raw.githubusercontent.com/micro/micro/master/scripts/install.sh | /bin/bash

# Linux
wget -q  https://raw.githubusercontent.com/micro/micro/master/scripts/install.sh -O - | /bin/bash

# Windows
powershell -Command "iwr -useb https://raw.githubusercontent.com/micro/micro/master/scripts/install.ps1 | iex"

运行服务

在探讨如何编写服务前, 我们先来运行一个现有的服务, 因为只需几个命令即可!

首先, 我们必须启动 micro server. 命令如下:

micro server

在与 micro server 交互之前, 我们需要使用 id 为 ‘admin’ 和密码为 ‘micro’ 的账号进行登录:

$ micro login
Enter email address: admin
Enter Password:
Successfully logged in.

如果没什么意外的话你会看到列出了上述命令启动的一些服务. 这里只是为了验证下一切是否正常, 我们来看看哪些服务正在运行:

$ micro services
api
auth
broker
config
network
proxy
registry
runtime
server
store

这些服务都是通过我们的 micro server 命令一起启动的. 看起来不错, 但这不是我们要说的重点!
All those services are ones started by our micro server. This is pretty cool, but still it’s not something we launched! Let’s start a service for which existence we can actually take credit for. If we go to github.com/micro/services, we see a bunch of services written by micro authors. One of them is the helloworld. Try our luck, shall we?

运行服务的命令是 micro run.

micro run github.com/micro/services/helloworld

如果我们使用 micro status 命令查看正在运行的服务, 应该会以下服务被列出:

NAME        VERSION    SOURCE        STATUS    BUILD    UPDATED    METADATA
helloworld    latest    helloworld    running    n/a    unknown    owner=n/a,group=n/a

我们也可以通过服务的日志来验证其是否在运行.

$ micro logs helloworld
micro@Bens-MBP-3 micro % micro logs helloworld
2020-08-11 15:18:33  file=service/service.go:192 level=info Starting [service] helloworld
2020-08-11 15:18:33  file=grpc/grpc.go:902 level=info Server [grpc] Listening on [::]:49602
2020-08-11 15:18:33  file=grpc/grpc.go:728 level=info Registry [service] Registering node: helloworld-c49ee2a3-e9d0-4411-9b9b-5fe6aea6b49d

所以我们的服务运行的还不错, 我们来尝试调用服务! 这也是服务需要做的事.

调用服务

我们有几种方式来调用在 micro server 之上运行的服务.

通过命令行

Micro 会给你的服务自动生成如 micro [service] [method] 格式的 CLI 命令, 并且默认的方法为 “Call”. 参数可以标志传递, 所以可以这样调用我们的服务:

$ micro helloworld --name=Jane
{
    "msg": "Hello Jane"
}

成功了! 如果我们想知道服务有哪些节点和终结点我们可以运行以下命令:

micro get service helloworld

通过框架

Let’s write a small client we can use to call the helloworld service. Normally you’ll make a service call inside another service so this is just a sample of a function you may write. We’ll learn how to write a full fledged service soon.

我们使用以下文件内容:

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/micro/micro/v3/service"
    proto "github.com/micro/services/helloworld/proto"
)

func main() {
    // 创建并初始化一个新的服务
    srv := service.New()

        // create the proto client for helloworld
        client := proto.NewHelloworldService("helloworld", srv.Client())

        // call an endpoint on the service
        rsp, err := client.Call(context.Background(), &proto.Request{
            Name: "John",
        })
        if err != nil {
            fmt.Println("Error calling helloworld: ", err)
            return
        }

        // 打印响应内容
        fmt.Println("Response: ", rsp.Msg)

        // let's delay the process for exiting for reasons you'll see below
        time.Sleep(time.Second * 5)
}

Save the example locally. For ease of following this guide, name the folder example-service. After doing a cd example-service && go mod init example, we are ready to run this service with micro run:

micro run .

micro runs, when successful, do not print any output. A useful command to see what is running, is micro status. At this point we should have two services running:

$ micro status
NAME                            VERSION        SOURCE                                                                    STATUS        BUILD    UPDATED        METADATA
example-service        latest        example-service                                                    starting    n/a        4s ago        owner=n/a,group=n/a
helloworld                latest        github.com/micro/services/helloworld        running        n/a        unknown        owner=n/a,group=n/a

Now, since our example-service client is also running, we should be able to see it’s logs:

$ micro logs example-service
# some go build output here
Response:  Hello John

Great! That response is coming straight from the helloworld service we started earlier!

从其他语言

In the clients repo there are Micro clients for various languages and frameworks. They are designed to connect easily to the live Micro environment or your local one, but more about environments later.

创建服务

要创建一个新的服务, 可以使用 micro new 命令. 应该会输出类似如下内容:

$ micro new helloworld
Creating service helloworld in helloworld

.
├── main.go
├── generate.go
├── plugin.go
├── handler
│   └── helloworld.go
├── subscriber
│   └── helloworld.go
├── proto/helloworld
│   └── helloworld.proto
├── Dockerfile
├── Makefile
├── README.md
├── .gitignore
└── go.mod

download protobuf for micro:

brew install protobuf
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro@master

compile the proto file helloworld.proto:

cd helloworld
protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/helloworld/helloworld.proto

通过输出可得, 在构建第一个服务前, 我们必须安装以下工具:

They are all needed to translate proto files to actual Go code. Protos exist to provide a language agnostic way to describe service endpoints, their input and output types, and to have an efficient serialization format at hand.

Currently Micro is Go focused (apart from the clients mentioned before), but this will change soon.

So once all tools are installed, being inside the service root, we can issue the following command to generate the Go code from the protos:

protoc --proto_path=.:$GOPATH/src --go_out=. --micro_out=. proto/helloworld.proto

The generated code must be committed to source control, to enable other services to import the proto when making service calls (see previous section Calling a service.

At this point, we know how to write a service, run it, and call other services too. We have everything at our fingertips, but there are still some missing pieces to write applications. One of such pieces is the store interface, which helps with persistent data storage even without a database.

存储

Amongst many other useful built-in services Micro includes a persistent storage service for storing data.

接口即构建基块

A quick side note. Micro (the server/CLI) and Go Micro (the framework) are centered around strongly defined interfaces which are pluggable and provide an abstraction for underlying distributed systems concepts. What does this mean?

Let’s take our current case of the store interface. It’s aimed to enable service writers data storage with a couple of different implementations:

  • in memory
  • file storage (default when running micro server)
  • cockroachdb

Similarly, the runtime interface, that allows you to run services in a completely runtime agnostic way has a few implementations:

  • local, which just runs actual processes - aimed at local development
  • kubernetes - for running containers in a highly available and distributed way

This is a recurring theme across Micro interfaces. Let’s take a look at the default store when running micro server.

使用存储

通过命令行1

首先, 我们来看看存储相关的基础命令.

要保存一个值, 我们可以使用 write 命令:

$ micro store write key1 value1

在 UNIX 哲学中没有输出意外着该值被轻松愉快的保存了. 那么这么读取这个值呢?

$ micro store read key1
val1

也可以换种方式来显示, 我们可以使用 --verbose-v 标志.

$ micro store read -v key1
KEY    VALUE   EXPIRY
key1   val1    None

This view is especially useful when we use the --prefix or -p flag, which lets us search for entries which key have certain prefixes.

To demonstrate that first let’s save an other value:

$ micro store write key2 val2

After this, we can list both key1 and key2 keys as they both share commond prefixes:

$ micro store read --prefix --verbose key
KEY    VALUE   EXPIRY
key1   val1    None
key2   val2    None

There is more to the store, but this knowledge already enables us to be dangerous!

通过框架1

Accessing the same data we have just manipulated from our Go Micro services could not be easier. First let’s create an entry that our service can read. This time we will specify the table for the micro store write command too, as each service has its own table in the store:

micro store write --table=example mykey "Hi there"

Let’s modify the example service we wrote previously so instead of calling a service, it reads the above value from a store.

package main

import (
    "fmt"
    "time"

    "github.com/micro/micro/v3/service"
    "github.com/micro/micro/v3/service/store"
)

func main() {
    srv := service.New(service.Name("example"))
    srv.Init()

    records, err := store.Read("mykey")
    if err != nil {
        fmt.Println("Error reading from store: ", err)
    }

    if len(records) == 0 {
        fmt.Println("No records")
    }
    for _, record := range records {
        fmt.Printf("key: %v, value: %v\n", record.Key, string(record.Value))
    }

    time.Sleep(1 * time.Hour)
}

更新服务

Now since the example service is running (can be easily verified by micro status), we should not use micro run, but rather micro update to deploy it.

We can simply issue the update command (remember to switch back to the root directory of the example service first):

micro update .

And verify both with the micro server output:

Updating service example-service version latest source /home/username/example-service
Processing update event example-service:latest in namespace default

and micro status:

$ micro status example-service
NAME            VERSION    SOURCE                            STATUS        BUILD    UPDATED        METADATA
example-service    latest    example-service.tar.gz            running    n/a    unknown    owner=n/a,group=n/a

that it was updated.

If things for some reason go haywire, we can try the time tested “turning it off and on again” solution and do:

micro kill example-service
micro run example-service

to start with a clean slate.

So once we did update the example service, we should see the following in the logs:

$ micro logs example-service
key: mykey, value: Hi there

配置

Configuration and secrets is an essential part of any production system - let’s see how the Micro config works.

命令行

The most basic example of config usage is the following:

$ micro config set key val
$ micro config get key
val

While this alone is enough for a great many use cases, for purposes of organisation, Micro also support dot notation of keys. Let’s overwrite our keys set previously:

$ micro config set key.subkey val
$ micro config get key.subkey
val

This is fairly straightforward, but what happens when we get key?

$ micro config get key
{"subkey":"val"}

As it can be seen, leaf level keys will return only the value, while node level keys return the whole subtree as a JSON document:

$ micro config set key.othersubkey val2
$ micro config get key
{"othersubkey":"val2","subkey":"val"}

通过框架2

Micro configs work very similarly when being called from Go code too:

package main

import (
    "fmt"

    "github.com/micro/micro/v3/service"
    "github.com/micro/micro/v3/service/config"
)

func main() {
    // setup the service
    srv := service.New(service.Name("example"))
    srv.Init()

    // 读取配置值
    fmt.Println("Value of key.subkey: ", config.Get("key", "subkey").String(""))
}

Assuming the folder name for this service is still example-service (to update the existing service, see updating a service):

$ micro logs example-service
Value of key.subkey:  val

深入阅读

这只是一个 Micro 快速开始和运行的简短入门指南. 随着本指南的不断更新可以随时来了解后续内容. 如果对学习 Micro 非常感兴趣, 也可以看看以下资源:


本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~