micro、M3O微服务系列(三)


这篇主打micro的技术

概述

Micro 是一个云原生开发平台。它由服务器、命令行界面和服务框架组成,使您能够构建、运行、管理和使用微服务。被认为很像一种语言规范,并且会随着时间的推移而发展。

安装

环境是go+linux
go的内容可以参考我的https://blog.csdn.net/weixin_45843419/article/details/124243479
最好提前装了protocol-buffers系列的产品,比如protoc protoc-gen-go protoc-gen-grpc-go

go get github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
(install也行)

或者
在这里插入图片描述

主机上(本地安装)

go方式

注意是micro不是go-micro,go-micro现在只在Asim的个人账户下

go get github.com/micro/micro/v3
或者
go install github.com/micro/micro/v3@latest

docker

dockre pull micro/micro

binary

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

或者
在这里插入图片描述

kubernetes

使用helm方式

helm repo add micro https://micro.github.io/helm
helm install micro micro/micro

卸载
helm uninstall micro
helm repo remove micro

服务器

微服务器是云及其他领域的分布式系统运行时。它作为一组服务、命令行和服务框架提供分布式系统开发的构建块。在每个组件彼此独立运行但作为一个系统协同工作的意义上,服务器很像一个分布式操作系统。这种组合使我们甚至可以为平台使用微服务架构模式。

特征

服务器提供以下功能作为服务开发的内置原语。
Authentication
Configuration
PubSub Messaging
Event Streaming
Service Discovery
Service Networking
Key-Value Storage
HTTP API Gateway
gRPC Identity Proxy
Web Dashboard

用法

要启动服务器,只需运行

[root@master1 ~]# micro server
2022-07-16 11:56:30  file=server/server.go:88 level=info Starting server
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering registry
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering broker
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering network
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering runtime
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering config
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering store
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering events
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering auth
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering proxy
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering api
2022-07-16 11:56:30  file=server/server.go:116 level=info Registering web
2022-07-16 11:56:30  file=server/server.go:206 level=info Starting server runtime

启动整个系统和服务,包括:8080 上的 http api 和 :8081 上的 grpc 代理

帮助

[root@master1 ~]# micro --help
NAME:
   micro - A framework for cloud native development

   Use `micro [command] --help` to see command specific help.

USAGE:
   micro [global options] command [command options] [arguments...]

VERSION:
   v3.11.0

COMMANDS:
   auth      Manage authentication, accounts and rules
   call      Call a service e.g micro call greeter Say.Hello '{"name": "John"}'
   cli       Run the interactive CLI
   config    Manage configuration values
   env       Get/set micro cli environment
   gen       Generate a micro related dependencies e.g protobuf
   get       Get resources from micro
   health    Get the service health
   init      Generate a profile for micro plugins
   kill      Kill a service: micro kill [source]
   login     Interactive login flow.
   logout    Logout.
   logs      Get logs for a service e.g. micro logs helloworld
   network   Manage the micro service network
   new       Create a service template
   run       Run a service: micro run [source]
   server    Run the micro server
   service   Run a micro service
   services  List services in the registry
   stats     Query the stats of specified service(s), e.g micro stats srv1 srv2 srv3
   status    Get the status of services
   store     Commands for accessing the store
   stream    Create a service stream e.g. micro stream foo Bar.Baz '{"key": "value"}'
   update    Update a service: micro update [source]
   user      Print the current logged in user
   web       Run the web dashboard
   help, h   Shows a list of commands or help for one command

GLOBAL OPTIONS:
   -c value                          Set the config file: Defaults to ~/.micro/config.json [$MICRO_CONFIG_FILE]
   --env value, -e value             Set the environment to operate in [$MICRO_ENV]
   --profile value                   Set the micro server profile: e.g. local or kubernetes [$MICRO_PROFILE]
   --namespace value                 Namespace the service is operating in (default: "micro") [$MICRO_NAMESPACE]
   --auth_address value              Comma-separated list of auth addresses [$MICRO_AUTH_ADDRESS]
   --auth_id value                   Account ID used for client authentication [$MICRO_AUTH_ID]
   --auth_secret value               Account secret used for client authentication [$MICRO_AUTH_SECRET]
   --auth_public_key value           Public key for JWT auth (base64 encoded PEM) [$MICRO_AUTH_PUBLIC_KEY]
   --auth_private_key value          Private key for JWT auth (base64 encoded PEM) [$MICRO_AUTH_PRIVATE_KEY]
   --registry_address value          Comma-separated list of registry addresses [$MICRO_REGISTRY_ADDRESS]
   --registry_tls_ca value           Certificate authority for TLS with registry [$MICRO_REGISTRY_TLS_CA]
   --registry_tls_cert value         Client cert for TLS with registry [$MICRO_REGISTRY_TLS_CERT]
   --registry_tls_key value          Client key for TLS with registry [$MICRO_REGISTRY_TLS_KEY]
   --broker_address value            Comma-separated list of broker addresses [$MICRO_BROKER_ADDRESS]
   --events_tls_ca value             Certificate authority for TLS with events [$MICRO_EVENTS_TLS_CA]
   --events_tls_cert value           Client cert for TLS with events [$MICRO_EVENTS_TLS_CERT]
   --events_tls_key value            Client key for TLS with events [$MICRO_EVENTS_TLS_KEY]
   --broker_tls_ca value             Certificate authority for TLS with broker [$MICRO_BROKER_TLS_CA]
   --broker_tls_cert value           Client cert for TLS with broker [$MICRO_BROKER_TLS_CERT]
   --broker_tls_key value            Client key for TLS with broker [$MICRO_BROKER_TLS_KEY]
   --store_address value             Comma-separated list of store addresses [$MICRO_STORE_ADDRESS]
   --proxy_address value             Proxy requests via the HTTP address specified [$MICRO_PROXY]
   --report_usage                    Report usage statistics (default: true) [$MICRO_REPORT_USAGE]
   --service_name value              Name of the micro service [$MICRO_SERVICE_NAME]
   --service_version value           Version of the micro service [$MICRO_SERVICE_VERSION]
   --service_address value           Address to run the service on [$MICRO_SERVICE_ADDRESS]
   --config_secret_key value         Key to use when encoding/decoding secret config values. Will be generated and saved to file if not provided. [$MICRO_CONFIG_SECRET_KEY]
   --tracing_reporter_address value  The host:port of the opentracing agent e.g. localhost:6831 [$MICRO_TRACING_REPORTER_ADDRESS]
   --help, -h                        show help (default: false)
   --version, -v                     print the version (default: false)

基础命令

运行 helloworld 并检查其状态

micro env	# should point to local
micro run github.com/micro/services/helloworld # run helloworld
micro status 	# wait for status running
micro services	# should display helloworld

调用服务并验证输出

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

删除服务

micro kill helloworld

命令行

命令行界面是与微服务器交互的主要方式。它是一个简单的二进制文件,可以使用简单的命令或交互式提示进行交互。CLI 将所有命令作为对 Micro 服务器的 RPC 调用进行代理。在许多内置命令中,它将执行格式化和额外的语法工作。(运行某个服务之后,也会产生该服务专属命令)

内置命令

内置命令是用于与服务器交互或更改用户配置的系统或配置级别命令。在大多数情况下,这是为了方便用户而使用的语法糖。这是众所周知的命令的子集。

signup
login
run
update
kill
services
logs
status
env
user

signup

注册是一个尝试查询“注册”以注册新帐户的命令,这是特定于环境的,需要运行注册服务。默认情况下,这在本地是不存在的,建议用户使用 admin/micro 凭据来管理系统。然后,你可以选择在 micro/proto 中运行符合 proto 的自己的注册服务或使用micro auth create account

对于那些想为他人运行自己的微服务器并可能许可软件收取费用的人来说,注册被视为一种命令。

login

登录对用户进行身份验证并将凭据本地存储在 .micro/tokens 文件中。这会调用微身份验证服务来根据系统中存储的现有帐户对用户进行身份验证。登录要求在提示符下输入用户名和密码。

[root@master1 .micro]# pwd
/root/.micro
[root@master1 .micro]# ls
config.json  config_secret_key  id_rsa  id_rsa.pub  server  tokens

动态命令

当向 Micro CLI发出命令时micro command,如果该命令不是内置命令,Micro 将尝试动态解析该命令并调用正在运行的服务(其实就是调用某个服务)。使用micro registry命令,虽然注册表是默认在本地 Micro 设置上运行的核心服务,但该registry命令不是内置的。(是服务,但是该服务对应的micro的命令不是内置命令)

使用–help标志,我们可以获得有关可用子命令和标志的信息

[root@master1 .micro]# micro services
api
auth
broker
config
events
network
proxy
registry
runtime
store
web

[root@master1 .micro]# micro registry --help
NAME:
        micro registry

VERSION:
        latest

USAGE:
        micro registry [command]

COMMANDS:
        deregister
        getService
        listServices
        register
        watch

列出的命令是registry服务的端点(命令)
要查看子命令的标志(命令的参数)

[root@master1 .micro]# micro registry getService --help
NAME:
        micro registry getService

USAGE:
        micro registry getService [flags]

FLAGS:
        --service string
        --options_ttl int64
        --options_domain string

有兴趣的可以查看下registry服务的源码,看下它的proto文件的定义

message Options {
	int64 ttl = 1;
	string domain = 2;
}

message GetRequest {
	string service = 1;
	Options options = 2;
}

正如上面的定义告诉我们的那样,请求GetService的字段service位于顶层,字段ttl和domain位于options结构中。动态 CLI 将带下划线的标志名(即。options_domain)映射到请求字段,因此以下请求 JSON:

{
    "service": "serviceName",
    "options": {
        "domain": "domainExample"
    }
}

等效于以下标志:

micro registry getService --service=serviceName --options_domain=domainExample

User Config

命令行使用 ~/.micro 中的本地用户配置存储来存储任何形式的状态,例如已保存的环境、令牌等。除非另有说明,否则它将始终尝试从此处读取用户的配置。目前我们将所有配置存储在一个文件中config.json,并将任何身份验证令牌存储在一个tokens文件中。

config.json:

{"env":"local","local":{"current-user":"admin"},"micro":{"auth":{"local":{"expiry":"","refresh-token":"","token":""},"platform":{"expiry":"","refresh-token":"","token":""}}}}

env

Micro 在构建时考虑了联合和多环境模型。我们的开发通常通过本地、暂存和生产进行映射,因此 Micro 采用这种前瞻性的观点,并建立了环境的概念,这些环境是完全隔离的微环境,可以通过 CLI 与之交互。

查看env

[root@master1 .micro]# micro env
* local      127.0.0.1:8081    Local running Micro Server
  platform   proxy.m3o.com     Cloud hosted Micro Platform

有两个内置环境(local默认)和m3o特定产品;平台。这些存在是为了方便和开发速度。可以创建其他环境micro env add [name] [host:port]。环境地址指向默认为**:8081** 的微代理。(grpc)

添加env

该命令micro env --help提供了使用摘要。下面是一个如何添加环境的示例。

[root@master1 .micro]# micro env --help
NAME:
   micro env - Get/set micro cli environment

USAGE:
   micro env command [command options] [arguments...]

COMMANDS:
   get      Get the currently selected environment
   set      Set the environment to use for subsequent commands e.g. micro env set dev
   add      Add a new environment e.g. micro env add foo 127.0.0.1:8081
   del      Delete an environment from your list e.g. micro env del foo
   help, h  Shows a list of commands or help for one command

OPTIONS:
   --help, -h  show help (default: false)

[root@master1 .micro]# micro env add foobar example.com
[root@master1 .micro]# micro env
  foobar     example.com
* local      127.0.0.1:8081    Local running Micro Server
  platform   proxy.m3o.com     Cloud hosted Micro Platform

设置env

[root@master1 .micro]# micro env set foobar
[root@master1 .micro]# micro env
* foobar     example.com
  local      127.0.0.1:8081    Local running Micro Server
  platform   proxy.m3o.com     Cloud hosted Micro Platform

登录到环境

每个环境实际上都是一个独立的部署,具有自己的身份验证、存储等。因此每个环境都需要注册和登录。此时我们必须在example使用micro login. 如果您没有环境凭据,则必须询问管理员。(登录paltform也是这样)

service

Micro 构建为利用微服务架构模式的分布式操作系统。

Micro Server 提供的服务列表。每个服务都被认为是平台和分布式系统开发的构建块原语。每个的 proto 接口都可以在micro/proto/auth:https://github.com/micro/micro/blob/master/proto/auth/auth.proto中找到 ,Go 库、客户端和服务器实现可以在micro/service/auth:https://github.com/micro/micro/tree/master/service/auth中找到。

micro的所有源码:https://github.com/micro/micro

micro/proto/auth/auth.proto


syntax = "proto3";

package auth;

option go_package = "github.com/micro/micro/v3/proto/auth;auth";

service Auth {
	rpc Generate(GenerateRequest) returns (GenerateResponse) {};
	rpc Inspect(InspectRequest) returns (InspectResponse) {};		
	rpc Token(TokenRequest) returns (TokenResponse) {};
}

service Accounts {
	rpc List(ListAccountsRequest) returns (ListAccountsResponse) {};
	rpc Delete(DeleteAccountRequest) returns (DeleteAccountResponse) {};
	rpc ChangeSecret(ChangeSecretRequest) returns (ChangeSecretResponse) {};
}

service Rules {
	rpc Create(CreateRequest) returns (CreateResponse) {};
	rpc Delete(DeleteRequest) returns (DeleteResponse) {};
	rpc List(ListRequest) returns (ListResponse) {};
}

message ListAccountsRequest {
	Options options = 1;
}

message ListAccountsResponse {
	repeated Account accounts = 1;
}

message DeleteAccountRequest {
	string id = 1;
	Options options = 2;
}

message DeleteAccountResponse {}

message Token {
	string access_token = 1;
	string refresh_token = 2;
	int64 created = 3;
	int64 expiry = 4;
}

message Account {
	string id = 1;
	string type = 2;
	map<string, string> metadata = 4;
	repeated string scopes = 5;
	string issuer = 6;
	string secret = 7;
	string name = 8;
}

message Resource{
	string name = 1;
	string type = 2;
	string endpoint = 3;
}

message GenerateRequest {
	string id = 1;
	map<string, string> metadata = 3;
	repeated string scopes = 4;
	string secret = 5;
	string type = 6;
	string provider = 7;
	Options options = 8;
	string name = 9;
}

message GenerateResponse {
	Account account = 1;
}

message GrantRequest {
	string scope = 1;
	Resource resource = 2;
	Options options = 3;
}

message GrantResponse {}

message RevokeRequest {
	string scope = 1;
	Resource resource = 2;
	Options options = 3;
}

message RevokeResponse {}

message InspectRequest {
	string token = 1;
	Options options = 2;
}

message InspectResponse {
	Account account = 1;
}

message TokenRequest {
	string id = 1;
	string secret = 2;
	string refresh_token = 3;
	int64 token_expiry = 4;
	Options options = 5;
}

message TokenResponse {
	Token token = 1;
}

enum Access {
	UNKNOWN = 0;
	GRANTED = 1;
	DENIED = 2;
}

message Rule {
	string id = 1;
	string scope = 2;
	Resource resource = 3;
	Access access = 4;
	int32 priority = 5;
}

message Options {
	string namespace = 1;
}

message CreateRequest {
	Rule rule = 1;
	Options options = 2;
}

message CreateResponse {}

message DeleteRequest {
	string id = 1;
	Options options = 2;
}

message DeleteResponse {}

message ListRequest {
	Options options = 2;
}

message ListResponse {
	repeated Rule rules = 1;
}

message ChangeSecretRequest{
	string id = 1;
	string old_secret = 2;
	string new_secret = 3;
	Options options = 4;
}

message ChangeSecretResponse{}

API

API 服务是一个 http API 网关,它充当公共入口点并将 http/json 转换为 RPC。(本来是向只接受的rpc,但是如今的通信中http还是大多数,所以弄个API网关支持http,相信将来会走向定义API的标准协议的protocol buffer和grpc的通信的时代)

概念

微 API 是所有外部访问服务的公共入口点,供前端、移动设备等使用。API 接受 http/json 请求并使用基于路径的路由来解析后端服务。它将请求转换为 gRPC 并适当地转发。这里的想法是专注于后端的微服务,并将所有内容组合在一起作为前端的单个 API。(用gin+vue的开发模式来理解前后端的api即可)

用法

在默认local 环境中,API 地址是127.0.0.1:8080. 每个运行的服务都可以通过这个 API 调用。(8080是http)

[root@master1 services]# curl http://127.0.0.1:8080
{"version": "v3.11.0"}

调用注册表这个服务,并且使用注册表的列出功能:

[root@master1 services]# curl http://127.0.0.1:8080/registry/listServices
{"id":"registry.Registry.ListServices","code":401,"detail":"an account is required","status":"Unauthorized"}

格式是

curl http://127.0.0.1:8080/[servicename]/[endpointName]

参数可以作为查询参数传递

[root@master1 services]# curl http://127.0.0.1:8080/helloworld/call?name=yy
{"message":"Hello yy"}

或 JSON 正文:

[root@master1 services]# curl -XPOST --header "Content-Type: application/json" -d '{"name":"yy"}' http://127.0.0.1:8080/helloworld/call
{"message":"Hello yy"}

(注意有时候服务刚开起来,curl没那么快就访问到,需要等一会)

要在调用 API 时指定命名空间,Micro-Namespace可以使用标头(http Head):

curl -H "Micro-Namespace: foobar" http://127.0.0.1:8080/helloworld/call?name=yy

要调用非公共服务或endpoint,Authorization可以使用标头:

MICRO_API_TOKEN=`micro user token`
curl -H "Authorization: Bearer $MICRO_API_TOKEN" http://127.0.0.1:8080/helloworld/call?name=yy

认证

auth 服务提供身份验证和授权。

概述

auth 服务存储帐户和访问规则。它为 Micro 运行时中的所有身份验证和授权提供了单一的事实来源。每个服务和用户都需要一个帐户才能操作。当运行时启动服务时,会为其生成一个帐户。微负载规则定期运行的核心服务和服务,并根据每个请求管理对其资源的访问。

用法

对于 CLI 命令帮助,请使用micro auth --helpauth 子命令帮助,例如micro auth create --help.

登录

要登录到服务器,只需执行以下操作

$ micro login
Enter username: admin
Enter password: 
Successfully logged in.

密码是micro
默认为本地micro server.

规则

规则确定用户可以访问的资源。默认规则如下:

[root@master1 services]# micro auth list rules
ID              Scope                   Access          Resource                Priority
default         <public>                GRANTED         *:*:*                   0

该规则,输出default中的所有服务都可调用

如果我们想防止未经授权的用户调用我们的服务,我们可以创建以下规则

# This command creates a rule that enables only logged in users to call the micro server
micro auth create rule  --access=granted --scope='*' --resource="*:*:*" onlyloggedin
# Create the rule which allows us to login
micro auth create rule --access=granted --resource="service:auth:*" auth-public

service类型的服务,选定auth服务,其下的全部命令

并删除默认的。在这里,作用域与我们之前在执行 a 时看到*的作用域明显不同:micro auth list rules

[root@master1 services]# micro auth list rules
ID                      Scope                   Access          Resource                Priority
auth-public             <public>                GRANTED         service:auth:*          0
onlyloggedin            *                       GRANTED         *:*:*                   0
default                 <public>                GRANTED         *:*:*                   0

删除默认规则:

# This command deletes the 'default' rule - the rule which enables anyone to call the 'micro server'.
$ micro auth delete rule default
Rule deleted

再次尝试 curl 我们的服务:

[root@master1 services]# curl 127.0.0.1:8080/helloworld/call?name=Alice
{"id":"helloworld","code":401,"detail":"Unauthorized call made to helloworld:Helloworld.Call","status":"Unauthorized"}

onlyloggedin规则生效了。我们仍然可以使用令牌调用服务,即验证授权:

$ token=$(micro user token)
# Locally:
[root@master1 services]# curl --header "Authorization: Bearer $token" 127.0.0.1:8080/helloworld/call?name=yy
{"message":"Hello yy"}

--header验证等的信息一般都是写在请求头

(请注意,令牌的生命周期有限,因此$ token=$(micro user token)必须不时重新发布该行,或者必须内联使用该命令。)

账户

Auth 服务支持帐户的概念。用于访问micro server的默认帐户是管理员帐户。

[root@master1 services]# micro auth list accounts
ID              Name            Scopes          Metadata
admin           admin           admin           created=1657689442

我们可以为队友和同事创建帐户micro auth create account

[root@master1 services]# micro auth create account --scopes=admin yy
Account created: {"id":"yy","type":"user","issuer":"micro","metadata":{"created":"1657958447"},"scopes":["admin"],"secret":"cbbe8671-2988-409e-a438-cc15f7799e3c","name":"yy"}

新创建的帐户可以micro login通过使用id 和密码cbbe8671-2988-409e-a438-cc15f7799e3c来使用。

Broker

代理是异步发布订阅消息的消息代理。

概述

代理为 pubsub 消息传递提供了一个简单的抽象。它专注于即发即弃异步通信的简单语义。这里的目标是为异步通知提供一种模式,其中发生了一些更新或事件,但不需要持久性。客户端和服务器建立在一侧发布和另一侧订阅的能力。代理不提供消息排序保证。

虽然服务通常按名称调用,但消息传递侧重于可以有多个发布者和订阅者的主题。代理在服务的客户端/服务器中进行抽象,其中包括消息编码/解码,因此您不必花费所有时间进行编组。

客户

客户端包含Publish获取原始消息、对其进行编码并在给定主题上发布到代理的方法。它从客户端上下文中获取元数据,并将它们作为包含内容类型的消息的标头包含在内,以便订阅方知道如何处理它。

服务器

服务器支持一种Subscribe方法,该方法允许您像处理请求一样注册处理程序。通过这种方式,我们可以镜像处理程序的行为并在从代理消费时反序列化消息。在此模型中,服务器处理与代理的连接、订阅、消费和执行您的订阅者功能。

用法

Publisher:

bytes, err := json.Marshal(&Healthcheck{
	Healthy: true,
	Service: "foo",
})
if err != nil {
	return err
}

return broker.Publish("health", &broker.Message{Body: bytes})

Subscriber:

handler := func(msg *broker.Message) error {
	var hc Healthcheck
	if err := json.Unmarshal(msg.Body, &hc); err != nil {
		return err
	}
	
	if hc.Healthy {
		logger.Infof("Service %v is healthy", hc.Service)
	} else {
		logger.Infof("Service %v is not healthy", hc.Service)
	}

	return nil
}

sub, err := broker.Subscribe("health", handler)
if err != nil {
	return err
}

config(配置)

config 服务为服务提供动态配置
Config 可以单独存储和加载到应用程序本身,用于配置业务逻辑、api 键等。我们将这些作为键值对进行读写,也支持 JSON 值的嵌套。config接口还支持通过在写入值时将密钥定义为选项来存储secret。

用法

假设我们有一个服务调用helloworld,我们想从中读取配置数据。首先,我们必须使用 cli 插入所述数据。配置数据可以用点符号组织在不同的“路径”下。将属于服务的所有配置数据保存在与服务名称匹配的顶级路径段下是一个很好的约定:

[root@master1 services]# micro config set helloworld.somekey hello
[root@master1 services]# micro config get helloworld.somekey
hello
[root@master1 services]# micro config get helloworld
{"somekey":"hello"}

也可以保存另一个键并使用点符号一次性读取所有值:

[root@master1 services]# micro config set helloworld.someotherkey 'Hi there!'
[root@master1 services]# micro config get helloworld
{"somekey":"hello","someotherkey":"Hi there!"}

可以看出,config(默认情况下)将配置数据存储为 JSON。我们可以保存任何类型:

[root@master1 services]# micro config set helloworld.someboolkey true
[root@master1 services]# micro config get helloworld
{"someboolkey":true,"somekey":"hello","someotherkey":"Hi there!"}

探索点符号的优点:

[root@master1 services]# micro config set helloworld.keywithsubs.subkey1 'So easy!'
[root@master1 services]# micro config get helloworld
{"keywithsubs":{"subkey1":"So easy!"},"someboolkey":true,"somekey":"hello","someotherkey":"Hi there!"}
[root@master1 services]# micro config del helloworld.someotherkey
[root@master1 services]# micro config get helloworld
{"keywithsubs":{"subkey1":"So easy!"},"someboolkey":true,"somekey":"hello"}

不仅可以删除叶级别键,还可以删除顶级键:

[root@master1 services]# micro config del helloworld.keywithsubs
[root@master1 services]# micro config get helloworld
{"someboolkey":true,"somekey":"hello"}
secret

config在很多知识领域,往往都会伴随这一个secret,简单来看就是一个明文,一个加密,secret更安全,防止泄露

[root@master1 services]# micro config set --secret helloworld.hushkey "Very secret stuff"
[root@master1 services]# micro config get helloworld.hushkey
[secret]
[root@master1 services]# micro config get --secret helloworld.hushkey
Very secret stuff
[root@master1 services]# micro config get helloworld
{"hushkey":"[secret]","someboolkey":true,"somekey":"hello"}
[root@master1 services]# micro config get --secret helloworld
{"hushkey":"Very secret stuff","someboolkey":true,"somekey":"hello"}

甚至 bool 或数字值也可以保存为秘密,[secret]除非解​​密,否则它们将显示为字符串常量:

[root@master1 services]# micro config set --secret helloworld.hush_number_key 42
[root@master1 services]# micro config get helloworld
{"hush_number_key":"[secret]","hushkey":"[secret]","someboolkey":true,"somekey":"hello"}
[root@master1 services]# micro config get --secret helloworld
{"hush_number_key":42,"hushkey":"Very secret stuff","someboolkey":true,"somekey":"hello"}

服务框架

设置配置值和从服务访问一样简单

package main

import (
	"fmt"
	"time"

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

type keyConfig struct {
	Subkey  string `json:"subkey"`
	Subkey1 int    `json:"subkey1"`
}

type conf struct {
	Key keyConfig `json:"key"`
}

func main() {
	go func() {
		for {
			time.Sleep(time.Second)
			val, err := config.Get("key.subkey")
			fmt.Println("Value of key.subkey: ", val.String(""), err)

			val, err = config.Get("key", config.Secret(true))
			if err != nil {
				fmt.Println(err)
			}
			c := conf{}
			err = val.Scan(&c.Key)
			fmt.Println("Value of key.subkey1: ", c.Key.Subkey1, err)
		}
	}()

	// run the service
	service.Run()
}

上述服务将每秒打印key.subkey和的值。key.subkey通过传入config.Secret(true)选项,我们告诉 config 为我们解密secret值,类似于–secretCLI 标志。

config 接口不仅指定Get Set和Delete访问值,还指定接口中的一些便利功能Value。

值得注意的是,String Int等方法将尽最大努力尝试强制类型,即。如果保存的值是一个字符串,Int将尝试解析它。但是,这不适用于在后台使用的Scan方法,json.Unmarshal我们都知道在遇到类型不匹配时会失败。

Get在所有情况下,都应该返回一个非 nil Value,所以即使Get出现错误,Value.Int()其他操作也不应该恐慌。

高级概念

合并配置值

当使用 CLI 保存一个有效 JSON 映射的字符串时,它会被扩展以保存为正确的映射结构,而不是字符串,即

[root@master1 test]# micro config set helloworld '{"a":"val1","b":"val2"}'
[root@master1 test]# micro config get helloworld
{"a":"val1","b":"val2","hush_number_key":"[secret]","hushkey":"[secret]","someboolkey":true,"somekey":"hello"}
[root@master1 test]# micro config get helloworld.a
val1
type conf struct {
	A string `json:"a"`
	B string `json:"b"`
}

c1 := conf{"val1", "val2"}
config.Set("key", c1)

v, _ := config.Get("key")
c2 := &conf{}
v.Scan(c2)
// c1 and c2 should be equal

或者用下面的例子

$ micro config del helloworld
$ micro config set helloworld '{"a":1}'
$ micro config get helloworld
{"a":1}
$ micro config set helloworld '{"b":2}'
$ micro config get helloworld
{"a":1,"b":2}

micro server的加密密钥

默认情况下,如果未指定,micro server则会生成加密密钥并将其保存到 ~/.micro/config_secret_key。这适用于本地零依赖使用,但不适用于生产。
要为微服务器指定密钥,必须指定环境变量MICRO_CONFIG_SECRET_KEY或标志键config_secret_key

错误

errors 包为大多数常见的 HTTP 状态代码提供错误类型,例如 BadRequest、InternalServerError 等。建议在向 RPC 处理程序返回错误时使用这些错误之一。如果返回任何其他类型的错误,则将其视为 InternalServerError。

Micro API 会检测这些错误类型,并将使用它们来确定响应状态代码。例如,如果您的处理程序返回 errors.BadRequest,API 将返回 400 状态代码。如果没有返回错误,API 将返回默认的 200 状态码。

处理重试时也会使用错误代码。如果您的服务返回 500 (InternalServerError) 或 408 (Timeout),则客户端将重试请求。其他状态码被视为客户端错误,不会重试。

用法

import (
	"github.com/micro/micro/v3/service/errors"
)

func (u *Users) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error {
	if len(req.Id) == 0 {
		return errors.BadRequest("users.Read.MissingID", "Missing ID")
	}

	...
}

活动

事件服务是用于事件流和持久存储事件的服务。

概述

事件流与 pubsub 消息传递的不同之处在于它提供了一个有序的事件流,可以从过去的任何给定点消费或重放。如果你有使用 Kafka 的经验,那么你就会知道它基本上是一个分布式日志,它允许你从不同的偏移量读取文件并将其流式传输。

事件服务和接口为写入和读取事件以及从任何给定偏移量消费提供事件流抽象。它还支持适当的确认和错误处理。

事件与代理的不同之处还在于它提供了一个固定的事件类型,你可以在其中填写详细信息并自己处理消息正文的解码。事件可能具有较大的有效负载,因此不想不必要地解码,你可能只想移交给存储系统的位置。

功能

events 包有两个部分:Stream 和 Store。Stream 用于发布和消费给定主题的消息。例如,在聊天应用程序中,一个用户会发布消息,而另一个用户会订阅。如果您以后需要检索消息,您可以使用 Subscribe 函数并传递 Offset 选项重播它们,或者使用 Read 函数列出它们。

func Publish(topic string, msg interface{}, opts …PublishOption) error
Publish 函数有两个必需的参数:主题和消息。主题是您将事件发布到的频道,对于聊天应用程序,这将是聊天 ID。消息是任何结构,例如发送到聊天的消息。当订阅者收到事件时,他们将能够解组此对象。Publish 有两个受支持的选项,WithMetadata 用于传递键/值对,WithTimestamp 用于覆盖事件的默认时间戳。

func Consume(topic string, opts ...ConsumeOption) (<-chan Event, error)
Consume 函数用于消费事件。对于聊天应用程序,客户端会将聊天 ID 作为主题传递,发布到流的任何事件都将发送到事件通道。Event 有一个 Unmarshal 函数,可用于访问消息负载,如下所示:

for {
	evChan, err := events.Consume(chatID)
	if err != nil {
		logger.Error("Error subscribing to topic %v: %v", chatID, err)
		return err
	}
	for {
		ev, ok := <- evChan
		if !ok {
			break
		}
		var msg Message
		if err :=ev.Unmarshal(&msg); err != nil {
			logger.Errorf("Error unmarshaling event %v: %v", ev.ID, err)
			return err
		}
		logger.Infof("Received message: %v", msg.Subject)
	}
}

网络

网络是用于请求代理的服务到服务网络

概述

网络为服务网络抽象提供服务,包括代理、身份验证、租户隔离,并利用现有的服务发现和路由系统。这里的目标不是提供服务网格,而是提供更高级别的路由控制平面,可以基于现有系统管理访问。网络要求每个服务都指向它,为路由做出明确的选择。

在封面之下 cilium、envoy 和其他服务网格工具可用于提供高弹性的网格。

登记处

注册表是服务目录和端点资源管理器

概述

服务注册中心为所有服务及其 API 提供单一事实来源。启动时的所有服务都将在注册服务中注册它们的名称、版本、地址和端点。然后,它们会定期重新注册到“心跳”,否则会根据预定义的 90 秒 TTL 过期。

注册中心的目标是允许用户在正在运行的系统中探索 API 和服务。

最简单的访问形式是以下列出服务的命令。

micro services

用法

获取服务端点返回有关服务的信息,包括端点的响应参数参数:

$ micro registry getService --service=helloworld
{
	"services": [
		{
			"name": "helloworld",
			"version": "latest",
			"metadata": {
				"domain": "micro"
			},
			"endpoints": [
				{
					"name": "Helloworld.Call",
					"request": {
						"name": "CallRequest",
						"type": "CallRequest",
						"values": [
							{
								"name": "name",
								"type": "string",
								"values": []
							}
						]
					},
					"response": {
						"name": "CallResponse",
						"type": "CallResponse",
						"values": [
							{
								"name": "message",
								"type": "string",
								"values": []
							}
						]
					},
					"metadata": {}
				},
				{
					"name": "Helloworld.Stream",
					"request": {
						"name": "Context",
						"type": "Context",
						"values": []
					},
					"response": {
						"name": "Stream",
						"type": "Stream",
						"values": []
					},
					"metadata": {
						"stream": "true"
					}
				}
			],
			"nodes": [
				{
					"id": "helloworld-3a0d02be-f98e-4d9d-a8fa-24e942580848",
					"address": "192.168.43.193:34321",
					"port": "0",
					"metadata": {
						"broker": "service",
						"protocol": "grpc",
						"registry": "service",
						"server": "grpc",
						"transport": "grpc"
					}
				}
			],
			"options": {
				"ttl": "0",
				"domain": ""
			}
		}
	]
}

运行

概述

运行时服务负责运行、更新和删除二进制文件或容器(取决于平台 - 例如本地二进制文件、k8s 上的 pod 等)及其日志。

运行服务

该micro run命令告诉运行时运行服务。以下都是有效的例子:

micro run github.com/micro/services/helloworld
micro run .  # deploy local folder to your local micro server
micro run ../path/to/folder # deploy local folder to your local micro server
micro run helloworld # deploy latest version, translates to micro run github.com/micro/services/helloworld or your custom base url
micro run helloworld@9342934e6180 # deploy certain version
micro run helloworld@branchname  # deploy certain branch
micro run --name helloworld .

指定服务名称

服务名称派生自应用程序的目录名称。如果您想覆盖它,请指定–name标志。

micro run --name helloworld github.com/myorg/helloworld/server

运行本地文件夹

如果第一个参数是现有的本地文件夹,即

micro run ./foobar

然后 CLI 将该文件夹上传到运行时,运行时运行该文件夹。

运行一个 git 源

如果micro run第一个参数指向一个 git 存储库(无论是在 GitHub、GitLab、Bitbucket 还是任何其他提供者上),那么地址会被发送到运行时,运行时会下载代码并运行它。

使用参考

@引用是传递给运行标志之后的第一个参数的一部分。它可以是分支名称(没有引用意味着版本latest等于 git 术语中的 master)或提交哈希。

传入分支名称后,将运行代码的最新提交。

列出运行时对象

该micro status命令列出了运行时中运行的所有内容:

$ micro status
NAME		VERSION	SOURCE					STATUS	BUILD	UPDATED		METADATA
helloworld	latest	github.com/micro/services/helloworld	running	n/a	20h43m45s ago	owner=admin, group=micro

如果有错误,则输出包括错误。像micro kill,micro logs之类的命令,micro update接受返回的名称micro status作为第一个参数(而不是可能不同的服务名称)。

更新服务

该micro update命令使运行时拉取分支中的最新提交并重新启动服务。

对于本地代码,它不需要运行时名称(由 返回micro status),而是本地路径。对于提交哈希部署,它只是重新启动服务。

例子:micro update helloworld, micro update helloworld@branch, micro update helloworld@commit, micro update ./helloworld

删除服务

该micro kill命令从运行时中删除运行时对象。它接受由 . 返回的名称micro status。

例子:micro kill helloworld

日志

该micro logs命令显示运行时对象的日志。它接受由 . 返回的名称micro status。

该-f标志使命令流连续记录。

例子:micro logs helloworld, micro logs -f helloworld

store

支持键排序的键值存储可用于构建复杂的应用程序。由于其功能集非常有限,键值存储通常可以轻松可靠地扩展,通常与添加的节点数量呈线性关系。

这种可扩展性是以编写业务逻辑时的不便和精神开销为代价的。对于线性可扩展性很重要的用例,这种权衡是首选。

按ID查询

按 ID 读取是键值存储的典型工作。存储数据以启用此 ID 就像在任何其他数据库中一样:

# entries designed for querying "users by id"
KEY         VALUE
id1         {"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}
id2         {"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
id3         {"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}
id4         {"id":"id4", "name":"Betty","class":"thirdGrade"    "avgScore": 94}
import "github.com/micro/micro/v3/service/store"

records, err := store.Read("id1")
if err != nil {
	fmt.Println("Error reading from store: ", err)
}
fmt.Println(records[0].Value)
// Will output {"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}

给定这个数据结构,我们可以做两个查询:
1.读取给定的密钥(获取“id1”,获取“id2”)
2.如果键是有序的,我们可以在一个键之后要求 X 个条目(在“id2”之后得到 3 个条目)

在有序集合中查找值可能是我们可以询问数据库的最简单的任务。上述数据结构的问题在于,询问“按“id2”之后的顺序查找密钥并不是很有用。要启用其他类型的查询,必须使用不同的键保存数据。

考虑到查询,我们可以将数据复制到另一个以我们想要执行的查询命名的表中:

按字段值相等查询

# entries designed for querying "users by class"
KEY             VALUE
firstGrade/id1  {"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}
secondGrade/id2 {"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
secondGrade/id3 {"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}
thirdGrade/id4  {"id":"id4", "name":"Betty","class":"thirdGrade"    "avgScore": 94}
import "github.com/micro/micro/v3/service/store"

records, err := store.Read("", store.Prefix("secondGrade"))
if err != nil {
	fmt.Println("Error reading from store: ", err)
}
fmt.Println(records[0].Value)
// Will output
// secondGrade/id2 {"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
// secondGrade/id3 {"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}

由于钥匙是订购的,因此返回非常简单,让我们说“所有二年级学生”。键值排序的键值存储支持类似于“键开头/键有前缀”的查询。对于二年级学生,列出所有以“键开头secondGrade”的记录将返回所有二年级学生。

这个查询基本上是 a field equals to,因为我们基本上做了 a field class == secondGrade。但是,如果我们适当地对数据建模,我们也可以利用键的有序特性来进行值比较查询,即field avgScores is less than 90等:field AvgScores is between 90 and 95

查询字段值范围

# entries designed for querying "users by avgScore"
KEY         VALUE
089/id3     {"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}
092/id2     {"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
094/id4     {"id":"id4", "name":"Betty","class":"thirdGrade"    "avgScore": 94}
098/id1     {"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}

值得记住的是,键是字符串,并且它们是按字典顺序排列的。出于这个原因,在处理编号值时,我们必须确保它们被适当地预先设置为相同的长度。

表用法

微服务只能访问一个 Store 表。这意味着所有键都位于同一个命名空间中并且可能发生冲突。一个非常有用的模式是按照预期的查询模式来分隔条目,即上面的“users by id”和users by class records:

KEY         VALUE
# entries designed for querying "users by id"
usersById/id1         		{"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}
usersById/id2         		{"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
usersById/id3         		{"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}
usersById/id4         		{"id":"id4", "name":"Betty","class":"thirdGrade"    "avgScore": 94}
# entries designed for querying "users by class"
usersByClass/firstGrade/id1  {"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}
usersByClass/secondGrade/id2 {"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
usersByClass/secondGrade/id3 {"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}
usersByClass/thirdGrade/id4  {"id":"id4", "name":"Betty","class":"thirdGrade"    "avgScore": 94}

这样各个go示例就变成了:

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

const idPrefix = "usersById/"

records, err := store.Read(idPrefix + "id1")
if err != nil {
	fmt.Println("Error reading from store: ", err)
}
fmt.Println(records[0].Value)
// Will output {"id":"id1", "name":"Jane", "class":"firstGrade",   "avgScore": 98}
import "github.com/micro/micro/v3/service/store"

const classPrefix = "usersByClass/"

records, err := store.Read("", store.Prefix(classPrefix + "secondGrade"))
if err != nil {
	fmt.Println("Error reading from store: ", err)
}
fmt.Println(records[0].Value)
// Will output
// secondGrade/id2 {"id":"id2", "name":"Alice","class":"secondGrade",  "avgScore": 92}
// secondGrade/id3 {"id":"id3", "name":"Joe",  "class":"secondGrade"   "avgScore": 89}

元数据

元数据/标头可以通过 RPC 调用中的上下文传递。上下文/元数据包允许服务在上下文中获取和设置元数据。Micro API 会将请求标头添加到上下文中,例如,如果在对“localhost:8080/users/List”的 API 调用中设置了“Foobar”标头,则用户服务可以访问此值,如下所示:

import (
	"context"
	"github.com/micro/micro/v3/service/context/metadata"
)

...

func (u *Users) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
	val, ok := metadata.Get(ctx, "Foobar")
	if !ok {
		return fmt.Errorf("Missing Foobar header")
	}

	fmt.Println("Foobar header was set to: %v", val)
	return nil
}
同样,客户端可以使用 metadata.Set 函数在上下文中设置元数据,如下所示:

func (u *Users) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
	newCtx := metadata.Set(ctx, "Foobar", "mycustomval")
	fRsp, err := u.foosrv.Call(newCtx, &foosrv.Request{})
	...
}

网络仪表板

在 localhost:8082 的 Web 浏览器中查看和查询服务。Web 仪表板是系统顶部的一个简单层,用于可视化服务及其端点。此外,它还生成动态表单以便于查询。

插件

Micro 是一种基于 Go 接口类型的可插拔架构。插件可以交换底层基础设施。

概述

Micro 是可插拔的,这意味着每个模块的实现都可以根据需求进行替换。插件应用于微服务器而不是直接应用于服务,这样做是为了让底层基础设施可以更改,而您的服务所需的代码更改为零。

可插入接口的一个示例是商店。本地 micro 将使用文件存储来持久化数据,这很棒,因为它需要零依赖,并且仍然提供重启之间的持久性。在测试套件中运行 micro 时,可以将其交换到更适合的内存缓存,因为它提供了运行之间的一致性。在生产中,可以根据需要将其替换为独立的基础架构,例如 cockroachdb 或 etcd。

让我们举一个例子,我们的服务想要从商店加载数据。我们的服务将调用store.Read(userPrefix + userID)加载值,在幕后这将执行一个 RPC 到存储服务,这将反过来调用为服务器配置store.Read的当前DefaultStore实现。

简介

配置文件用于一次配置多个插件。Micro 附带了一些开箱即用的配置文件,例如“local”、“kubernetes”和“test”。这些配置文件可以在 中找到profile/profile.go。您可以使用 env var 将 micro 配置为使用这些配置文件之一MICRO_PROFILE,例如:MICRO_PROFILE=test micro server. 使用的默认配置文件是“本地”。

编写个人资料

配置文件应在配置文件目录中创建为包。让我们通过创建profile/staging/staging.go. 下面的示例显示了如何覆盖Local配置文件的默认存储以使用内存实现:

// Package staging configures micro for a staging environment
package staging

import (
	"github.com/urfave/cli/v2"

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

func init() {
	profile.Register("staging", staging)
}

var staging = &profile.Profile{
	Name: "staging",
	Setup: func(ctx *cli.Context) error {
		profile.Local.Setup(ctx)
		store.DefaultStore = memory.NewStore()
		return nil
	},
}
pushd profile/staging
go mod init github.com/micro/micro/profile/staging
go mod tidy
popd

使用自定义配置文件
您可以使用几个命令加载自定义配置文件,首先将替换添加到您的 go mod,表明它应该在配置文件目录中查找您的自定义配置文件:

go mod edit -replace github.com/micro/micro/profile/staging/v3=./profile/staging
go mod tidy

第二个命令创建一个 profile.go 文件来导入你的配置文件。导入您的配置文件时,将调用 staging.go 中定义的 init() 函数,注册您的配置文件。

micro init --profile=staging --output=profile.go

现在您可以使用此配置文件启动服务器:

MICRO_PROFILE=staging go run . server
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿白,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值