微服务已成为市面流行且成熟的框架。备受众多企业青睐。
以下是对一个主流的golang微服务框架go-micro的简单应用。
安装官方的定义,go-micro组件一般可以分为:
Auth:服务调用之间的认证组件。默认不使用认证,需要在服务处理逻辑中自行实现。
Broker:消息通知组件。消息发布方会向所有的消息订阅方推送消息。默认情况下,订阅端会发布一个broker的http请求地址,并注册到registry,发布端通过registry获取到所有订阅的服务节点,然后通过http请求推送消息。
Cache:服务的缓存组件。默认使用本地内存缓存。
Cmd:命令行工具组件。可通过命令行传参的方式来发布微服务。执行service.Init()时,会使用命令行中的参数会覆盖服务中的初始配置。
Config:配置中心组件。默认储存在本地内存中。
Client:客户端。用于发起微服务请求。默认使用官方原生的mucp协议。
Server:服务端。用于发布微服务,接收微服务请求并处理。默认使用官方原生的mucp协议。
Store:数据存储组件。默认使用本地内存存储。
Registry:注册中心。存放微服务的注册信息。默认使用mdns协议。
Runtime:服务运行时组件。可以管理其他微服务的启停状态等。
Transport:微服务通信协议。默认采用http
Profile:调试组件。可显示信息包括默认不配置。
web:页面及路由微服务。负责与外部系统交互。接收所有的请求,并分发到对应的微服务。
user:用户功能微服务。处理用户相关功能。
system:管理员功能微服务。处理管理相关功能。
etcd:服务注册中心
redis:数据存储中心
config:动态配置中心
使用官方脚手架可以生成模板代码。不同的参数,可生成不同环境代码,可选参数如下
参数 | |
---|---|
- | 生成基础代码 |
--jaeger | Generate Jaeger tracer files |
--kubernetes | Generate Kubernetes resource files |
--skaffold | Generate Skaffold files |
--tilt | Generate Tiltfile |
--health | Generate gRPC Health service used for Kubernetes liveliness and readiness probes |
--kustomize | Generate kubernetes resouce files in a kustomize structure |
--sqlc | Generate sqlc resources |
--grpc | Use gRPC as default server and client |
--buildkit | Use BuildKit features in Dockerfile |
--tern | Generate tern resouces; sql migrations templates |
--advanced | Generate advanced features in main.go server file |
--privaterepo | Amend Dockerfile to build from private repositories (add ssh-agent) |
--namespace | Default namespace for kubernetes resources, defaults to 'default' |
--postgresaddress | Default postgres address for kubernetes resources, defaults to postgres.database.svc |
--complete | Complete will set the following flags to true; jaeger, health, grpc, sqlc, tern, kustomize, tilt, advanced |
下载脚手架:
go install github.com/go-micro/cli/cmd/[email protected]
创建微服务:
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)
}
}
micro.Server(server.Server):定义微服务的服务端。
服务端用于接收并处理客户端请求。go-micro将服务端抽象成了server.Server接口。官方Server插件库提供的服务端插件包含:grpc,http,mucp。默认采用mucp。
以mucp为例。mucp是go-micro的原生协议。**server.NewServer(...server.Option)**返回了server.rpcServer插件
server.Name(string):配置服务名称。默认go.micro.server。也可以通过micro.Name(string)进行配置。
server.Id(string):服务标识。默认通过uuid.New().String()生成。
server.Version(string):服务版本。默认latest。
server.Address(string):服务发布地址。使用transport.Listen监听时,传入的地址。默认:0。
server.Advertise(string):服务发现地址。节点注册时,该地址随着节点信息一同注册到注册中心;客户端发起请求时,seletor通过服务名匹配到节点,然后通过该服务发现地址向节点发送请求。默认空,为空时使用服务发布地址的真实地址。
server.Broker(broker.Broker):事件订阅方配置。用于处理接收到的事件通知,使用broker.Subscribe订阅事件。默认broker.httpBroker。
server.Codec(contentType string, c codec.NewCodec):新增自定义消息解码器。服务端通过content-type获取消息解码器,对收到的请求进行解码,codec.ReadHeader方法解码消息头,codec.ReadBody方法解码消息体。当添加的content-type与默认的相同时,新增编码器的会覆盖默认的解码器。mucp默认的消息解码器如下:
默认解码器 | content-type |
---|---|
grpc.NewCodec | application/grpc application/grpc+json application/grpc+proto |
json.Newcodec | application/json |
jsonrpc.NewCodec | application/json-rpc |
proto.NewCodec | application/protobuf |
protorpc.NewCodec | application/proto-rpc |
raw.Newcodec | application/octet-stream |
server.Context(context.Context):上下文,用于传递值。mucp服务中,有两个地方用到context。
- 存储server.Wait(*sync.WaitGroup)设置的wait值
- 传递到RegisterCheck方法中执行
server.Tracer(trace.Tracer):服务链路追踪器,客户端发起请求时,构造一个包含trace-id和span-id的content,服务端处理请求时,会修改content中的span-id。当前版本该字段默认未用到。可通过server.WrapHandler()自主实现。
server.Transport(transport.Transport):服务端通信协议,服务端通过Transport的Listen()方法发布服务监听。默认httpTransport。
server.Metadata(map[string]string):设置元信息。元信息会在节点信息中展示,默认元信息有transport,broker,server,registry,protocol。默认的元信息无法修改。
server.Registry(registry.Registry):服务注册中心,启动服务时,通过Regsitry的Register()方法向注册中心注册节点信息。
server.RegisterTTL(time.Duration):注册生效时间,对应registry插件中的Timeout。默认90s。
server.RegisterCheck(func(context.Context) error):注册检查。启动服务时,在节点注册前进行检查,检查通过才继续注册。服务运行时,进行周期性检查,检查失败的断开注册,检查成功的重新注册。默认对任何信息都检查通过。
server.RegisterInterval(time.Duration):注册检查周期,执行注册检查的周期,为0时不进行周期检查。默认30s。
server.TLSConfig(*tls.TLSConfig):设置transport监听的tls配置。
server.WithRouter(server.Router):覆盖默认的请求路由。对客户端对请求进行处理,默认是server.router。ProcessMessage()处理事件,ServeRequest()处理微服务请求。
server.Wait(*sync.WaitGroup):服务退出时,等待请求结束。传空也能设置成功。
server.WrapHandler(server.HandlerWrapper):请求拦截器。构建请求处理链,先配置的先处理,最后处理router.ServeRequest方法
server.Subwrapper(server.SubscriberWrapper):事件拦截器。构建事件处理链,先配置的先处理,最后处理router.ProcessMessage方法。
srv := micro.NewService(
micro.Server(
server.NewServer(
server.Name(service),
server.Id(server.DefaultId),
server.Version(version),
server.Address(server.DefaultAddress),
//server.Advertise(server.DefaultAddress),
server.Broker(broker.DefaultBroker),
server.Codec("application/text", func(closer io.ReadWriteCloser) codec.Codec {
return &bytes.Codec{}
}),
server.Context(context.Background()),
server.Registry(registry.DefaultRegistry),
//server.Tracer(trace.DefaultTracer),
server.Metadata(map[string]string{"description": "web ui and route"}),
server.RegisterTTL(server.DefaultRegisterTTL),
server.Context(context.WithValue(context.Background(), "isReady", true)),
server.RegisterCheck(func(ctx context.Context) error {
if !ctx.Value("isReady").(bool) {
return fmt.Errorf("server not ready to registry")
}
return nil
}),
server.RegisterInterval(server.DefaultRegisterInterval),
server.TLSConfig(&tls.Config{Certificates: []tls.Certificate{transportCert}}),
//server.WithRouter(server.DefaultRouter),
server.Wait(nil),
server.WrapHandler(func(handlerFunc server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error { // 支持链路追踪
newCtx, s := trace.DefaultTracer.Start(ctx, "web")
s.Type = trace.SpanTypeRequestInbound
defer trace.DefaultTracer.Finish(s)
return handlerFunc(newCtx, req, rsp)
}
}),
server.WrapHandler(func(handlerFunc server.HandlerFunc) server.HandlerFunc { // 支持auth认证
return func(ctx context.Context, req server.Request, rsp interface{}) error {
token := req.Header()[authHeader]
account, err := auth.DefaultAuth.Inspect(token)
if err != nil {
return err
}
return handlerFunc(context.WithValue(ctx, "account", account), req, rsp)
}
}),
server.WrapSubscriber(func(subscriberFunc server.SubscriberFunc) server.SubscriberFunc {
return func(ctx context.Context, msg server.Message) error {
return subscriberFunc(ctx, msg)
}
}),
),
),
)
micro.Name(string):同server.Name(string)
micro.Version(string):同server.Version(string)
micro.Address(string):同server.Address(string)
**micro.Tracer(trace.Tracer):**同server.Tracer(trace.Tracer)
micro.RegisterTTL(time.Duration):同server.RegisterTTL(time.Duration)
micro.RegisterInterval(time.Duration):同server.RegisterInterval(time.Duration)
micro.Metadata(map[string]string):同server.Metadata(map[string]string)
micro.WrapHandler(...server.HandlerWrapper):同server.WrapHandler(server.HandlerWrapper)
micro.WrapSubscriber(...server.SubscriberWrapper):同server.WrapSubscriber(server.SubscriberWrapper)
srv := micro.NewService(
micro.Name(service),
micro.Version(version),
micro.RegisterTTL(10*time.Second),
micro.RegisterInterval(10*time.Second),
micro.Metadata(),
micro.WrapHandler(),
micro.WrapSubscriber(),
)
micro.Client(client.Client):定义微服务的客户端
客户端用于发送请求。go-micro将客户端抽象成了client.Client接口。官方Client插件库提供的客户端插件包含:grpc,http,mucp。默认mucp。
以mucp为例。**client.NewClient(...client.Option)**返回了client.rpcClient插件。
client.Broker(broker.Broker):事件发布方配置。用于发布事件通知,使用Broker.Publish方法发布事件。默认broker.httpBroker。
client.Codec(string, codec.NewCodec):新增自定义消息编码器。客户端通过content-type获取消息编码器,对要发出的请求使用codec.Write方法进行编码。当添加的content-type与默认的相同时,新增编码器的会覆盖默认的编码器。消息编码器和消息解码器一一对应。
client.ContentType(string):设置请求头的Content-Type。通过client.NewRequest()构造请求时,将该参数设置为请求头的Content-Type,客户端和服务端会根据该请求头,进行相应的编码和解码。默认applicaiton/json。
client.PoolSize(int):连接池大小。连接池每个地址缓存连接池大小的连接,发起请求时,先通过pool.Get从连接池中获取连接,无法获取再创建新的连接;请求结束时,通过pool.Release将连接放回连接池,连接池满或连接出错时,关闭连接,并从连接池中丢弃。
client.PoolTTL(time.Duration):连接池超时时间。连接创建时,会记录连接的创建时间,每次获取连接时,计算是否超时,超时时,关闭连接,并从连接池中丢弃。
client.Transport(transport.Transport):客户端端通信协议,客户端通过Transport的Dial方法发起请求。默认httpTransport。
client.Selector(selector.Selector):设置选择器。客户端先通过Registry.GetService,获取注册中心该服务所有的节点信息,然后通过选择器策略strategy,在注册中心获取一个服务端的节点信息,向节点发起请求。默认选择器为selector.registrySelector,该selector包含两个重要方法:
- selector.Registry(registry.Registry):设置注册中心。
- selector.SetStrategy(func ([]*registry.Service) Next):设置选择器算法。mucp提供的选择器算法有:
strategy | 描述 | 是否默认 |
---|---|---|
Random | 每次请求随机选择节点 | 是 |
RoundRobin | 首次请求随机选择节点。接下来的请求按顺序选择节点 | 否 |
自定义方法 | 实现 func(services []*registry.Service) Next |
client.Registry(registry.Registry):用于设置选择器里的注册中心。同selector.Registry
client.Wrap(client.Wrapper):客户端中间件。初始化客户端时执行的链方法。先配置的先执行,先执行其他参数的初始化,最后执行链路上的方法,返回最终的客户端。
client.WrapCall(client.CallWrapper):请求中间件。每次请求时执行的链方法。先配置的先执行。先执行链路上的方法,最后执行client.call方法发起tcp请求。
client.Backoff(client.BackoffFunc):请求备份。请求前执行,执行完成后,系统休眠方法返回的时间。
client.Retry(client.RetryFunc): 请求是否重试。请求完成后,是否重新发起请求.mucp支持以下几种方法
重试方法 | 说明 | 是否默认 |
---|---|---|
client.RetryOnError | 失败重试 | 是 |
client.RetryAlways | 总是重试 | 否 |
自定义方法 | 实现。func(ctx context.Context, req Request, retryCount int, err error) (bool, error) | 否 |
client.Retries(int):请求重试次数。默认1
client.RequestTimeout(time.Duration):请求及响应超时时间。该参数将会设置到请求头Timeout。默认5s。
client.StreamTimeout(time.Duration):流连接超时时间。该参数将会设置到请求头Timeout。默认5s。流连接,可以是双向流,也可以是单向流。
client.DialTimeout(time.Duration):设置transport中的超时时间,默认5s
client.WithRouter(Router):设置router,暂时没有用到
srv := micro.NewService(
micro.Server(
client.NewClient(
client.Broker(broker.DefaultBroker),
client.Codec("application/text", func(closer io.ReadWriteCloser) codec.Codec {
return &bytes.Codec{}
}),
client.ContentType(client.DefaultContentType),
client.PoolSize(0),
client.Selector(selector.NewSelector(selector.SetStrategy(selector.RoundRobin))),
client.Registry(registry.DefaultRegistry),
client.Wrap(func(c client.Client) client.Client { // 构造客户端
if c.Options().Registry == nil {
return nil
}
return c
}),
client.WrapCall(func(callFunc client.CallFunc) client.CallFunc {
return func(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error { // 设置请求链路追踪
newCtx, s := trace.DefaultTracer.Start(ctx, "web")
s.Type = trace.SpanTypeRequestInbound
defer trace.DefaultTracer.Finish(s)
return callFunc(newCtx, node, req, rsp, opts)
}
}),
client.Backoff(func(ctx context.Context, req client.Request, attempts int) (time.Duration, error) { // 打印请求
log.Infof("attempts %v, the req is %v", attempts, req.Body())
return 0, nil
}),
client.Retry(func(ctx context.Context, req client.Request, retryCount int, err error) (bool, error) {
return err != nil, nil
}),
client.Retries(client.DefaultRetries),
client.RequestTimeout(client.DefaultRequestTimeout),
client.StreamTimeout(client.DefaultRequestTimeout),
client.DialTimeout(transport.DefaultDialTimeout),
client.WithRouter(nil),
),
),
)
micro.Selector(selector):同client.Selector。官方Selector插件库提供的选择器插件包含:dns,label,registry,shard,static。默认使用registry。
micro.WrapClient(...client.Wrapper):同client.Wrapper(client.Wrapper)
micro.WrapCall(...client.CallWrapper):同client.WrapCall(client.CallWrapper)
srv := micro.NewService(
micro.Selector(selector.DefaultSelector),
micro.WrapClient(),
micro.WrapCall(),
)
micro.Tranport(transport.Transport):统一定义服务端和客户端中使用的transport插件。
go-micro将服务通信抽象成了transport.Transport接口。官方Transport插件库提供了众多服务通信插件。包含:grpc,http,memory,nats,quic,rabbitmq,tcp,utp。默认http。
以http为例。transport.NewTransport()返回了transport.httpTransport插件。
transport.Addr(...string):httpTransport中暂时没有用到。
transport.Codec(codec.Marshaler):httpTransport中暂时没有用到。
transport.Timeout(time.Duration):设置服务端和客户端,接收和发送消息的超时时间。零值表示不设置超时时间,默认零值。
transport.Secure(bool):是否开启tls通信
transport.TLSConfig(*tls.Config):tls证书认证配置,为空时,跳过tls校验。
srv := micro.NewService(
micro.Transport(
transport.NewHTTPTransport(
transport.Addrs(),
transport.Codec(nil),
transport.Timeout(transport.DefaultDialTimeout),
transport.Secure(true),
transport.TLSConfig(&tls.Config{Certificates: []tls.Certificate{caCert}}),
),
),
)
micro.Broker(broker.Broker):统一定义服务端和客户端中使用的broker插件。
go-micro将消息通知抽象成了broker.Broker接口。官方Broker插件库提供的插件包含:gocloud,googlepubsub,grpc,http,kafka,memory,mqtt,nats,nsq,proxy,rabbitmq,redis,segmentio,snssqs,sqs,stan,stomp。默认使用http。
以http为例。broker.NewBroker(...broker.Option)返回了broker.httpBroker插件。
broker.Addrs(...string):broker订阅地址。发布方发布消息后,可通过该地址访问对应节点,进行消息通知。
broker.Codec(codec.Marshaler):消息编码,解码器。发布消息时进行编码,接收消息时进行解码。
broker.ErrorHandler(broker.Handler):消息错误处理逻辑。在memory插件中使用,发送消息出现错误时,进行的处理罗。
broker.Registry(registry.Registry):设置注册中心,httpBroker在订阅时,会向注册中心注册一条节点订阅信息。httpBroker在发布消息时,会在注册中心找到所有订阅的节点,并向这些节点发送消息。
broker.Secure(bool):是否开启tls通信
broker.TLSConfig(*tls.Config):tls证书认证配置,为空时,跳过tls校验。
srv := micro.NewService(
micro.Broker(
broker.NewBroker(
broker.Addrs(),
broker.Codec(json.Marshaler{}),
broker.ErrorHandler(func(event broker.Event) error {
return nil
}),
broker.Registry(registry.DefaultRegistry),
broker.Secure(true),
broker.TLSConfig(nil),
),
),
)
micro.Registry(registry.Registry):统一定义服务端,客户端,broker组件中使用的registry插件。
go-micro将服务注册抽象成了registry.Registry接口。官方Registry插件库提供了众多服务注册插件。包含:cache,consul,etcd,eureka,gossip,kubernetes,mdns,memory,mutli,nacos,nats,proxy,zookeeper。默认采用mdns协议。
以etcd为例。etcd.NewRegistry(...registry.Option)返回了etcd.etcdRegistry插件。
registry.Addrs(...string):注册中心的地址。也可以通过环境变量MICRO_REGISTRY_ADDRESS,etcd默认127.0.0.1:2379。
registry.Timeout(time.Duration):注册超时时间。通过registry.Timeout(time.Duration)设置,默认5s。
registry.Secure(bool):是否启用tls认证。
registry.TLSConfig(*tls.Config):tls认证证书配置。
etcd用户名,密码:通过环境变量ETCD_USERNAME设置用户名,环境变量ETCD_PASSWORD设置密码。
srv := micro.NewService(
micro.Registry(
etcd.NewRegistry( // 设置etcd注册中心
registry.Addrs(), // etcd 地址。默认127.0.0.1:2379
registry.Timeout(10*time.Second), // 超时时间
registry.Secure(true), // 是否启用tls
registry.TLSConfig(&tls.Config{Certificates: []tls.Certificate{pair}})
), // tls设置
),
)
micro.Auth(auth.Auth):定义认证插件。auth的创建和认证,需自行实现。比如在server拦截器中,实现auth逻辑。
go-micro将认证抽象成了auth.Auth接口。官方Auth插件库提供的插件包含:jwt。默认不使用认证。
以jwt为例。jwt.NewAuth(...auth.Option)返回了jwt.jwt插件。
auth.Addrs(...string):认证地址。jwt未使用。
auth.Namespace(string):微服务命名空间,以原数据Micro-Namespace存储在context中。
auth.PublicKey(string):用于解码jwt。
auth.PrivateKey(string):用于编码jwt。
auth.Credentials(string, string):存储账号密码
auth.ClientToken(*auth.Token):存储token值
srv := micro.NewService(
micro.Auth(
jwt.NewAuth(
auth.Addrs(),
auth.Namespace("blog"),
auth.PublicKey("1234567"),
auth.PrivateKey("7654321"),
//auth.Credentials("root", "123"),
//auth.ClientToken(&auth.Token{}),
),
),
)
micro.Cache(cache.Cache):定义缓存插件。
go-micro将缓存抽象成了cache.Cache接口。官方Cache插件库提供的插件包含:redis。默认使用本地内存缓存。
以redis为例。cache.NewCache(...cache.Option)返回了redis.redisCache插件。
cache.Expiration(time.Duration):缓存失效时间。适用于本地缓存。
cache.Items(map[string]cache.Item):缓存值,适用于本地缓存。
cache.WithAddress(string):redis服务地址。默认redis://127.0.0.1:6379。
cache.WithContext(context.Context):上下文。暂时没有用到。
srv := micro.NewService(
micro.Cache(
cacheRedis.NewCache(
cache.Expiration(10*time.Second),
//cache.Items(nil),
cache.WithAddress("redis://127.0.0.1:6379"),
//cache.WithContext(context.Background()),
),
),
)
micro.Store(store.Store):定义存储插件。一个store表示一张表。
go-micro将存储抽象成了store.Store接口。官方Store插件库提供的插件包含:cockroach、consul、file、memcached、memory、mysql、nats-js、redis。默认使用memory。
以redis为例。redis.NewStore(...store.Option)返回了redis.redisCache插件。
store.Nodes(...string):redis连接地址。支持redis集群。
store.Database(string):数据库名称。redis没有用到。
store.Table(string):表名称。redis中,key值由“表名称“+”key”组成
store.WithContext(context.Context):上下文。redis没有用到。
store.WithClient(client.Client):客户端。redis没有用到。
srv := micro.NewService(
micro.Store(
redis.NewStore(
store.Nodes("127.0.0.1:6379"),
//store.Database("blog"),
store.Table("web"),
//store.WithContext(context.Background()),
//store.WithClient(nil),
),
),
)
micro.Config(config.Config):定义动态配置插件。
go-micro将动态配置抽象成了config.Config接口。默认使用的是config.config插件。官方Config插件库提供了两类插件对config.config插件进行配置:
- 配置插件的格式包含:cue、hcl、toml、xml、yaml。默认json
- 配置插件的来源方包含:configmap,consul,etcd,grpc,mucp,nacos,nats,pkger,runtimevar,url,vault。默认内存。
先通过config.Load(...source.Source),配置插件的来源及格式。然后通过config.Values中提供的方法,就可以对配置的值进行增删改等操作。
cfg := config.DefaultConfig() // 定义插件
cfg.Load(etcd.NewSource(source.WithEncoder(yaml.NewEncoder()))) // 对插件进行配置
srv := micro.NewService(
micro.Config(cfg),
)
srv.Options().Config.Set("value","parentItem","childItem") // 设置配置
srv.Options().Config.Sync()
srv.Options().Config.Get("parentItem","childItem") // 读取配置值
micro.Runtime(runtime.Runtime):定义服务运行时。
go-micro将服务运行时抽象为runtime.Runtime接口。官方自带支持两种Runtime插件:kubernetes,memory。
kubernetes:通过k8sAPI对其他微服务进行管理。
memory:通过本地进程状态来对其他微服务进行管理。
以memory为例。runtime.NewRuntime(...runtime.Option)返回了runtime.runtime组件。
runtime.WithSource(string):来源。暂未使用。
runtime.WithScheduler(runtime.Scheduler):执行计划。服务运行时插件启动后,会开启一个循环来处理事件(事件类型包含:create,delete,update)。自定义执行计划可以发布一个事件交给服务运行时来处理。
runtime.WithType(string):类型。暂时只在kubernetes中使用,以labels的方式展示。
runtime.WithImage(string):镜像。暂未使用
runtime.WithClient(client.Client):服务端,用于发起微服务请求。
type scheduler struct {
stop chan bool
}
func (s scheduler) Notify() (<-chan runtime.Event, error) {
var a = make(chan runtime.Event)
go func() {
ticker := time.NewTicker(1 * time.Minute)
for {
select {
case <-ticker.C:
a <- runtime.Event{}
case <-s.stop:
return
}
}
}()
return a, nil
}
func (s scheduler) Close() error {
s.stop <- true
return nil
}
srv := micro.NewService(
micro.Runtime(
runtime.NewRuntime(
runtime.WithSource("blog"),
runtime.WithScheduler(&scheduler{}),
//runtime.WithType("service"),
//runtime.WithImage("web:1.0"),
//runtime.WithClient(nil),
),
),
)
cmd控制程序的启动参数。go-micro,默认对组件的配置进行了参数话。可通过以下两个方法来扩展启动参数。
micro.Flags(...cli.Flag):设置启动参数。
micro.Action(func(*cli.Context) error):获取参数并处理。
srv := micro.NewService(
micro.Flags(&cli.StringFlag{
Name: "ca-cert-file",
Usage: "ca cert file path",
EnvVars: []string{"CA_CERT_FILE"},
}, &cli.StringFlag{
Name: "ca-key-file",
Usage: "ca key file path",
EnvVars: []string{"CA_KEY_FILE"},
}),
micro.Action(func(c *cli.Context) error {
caCertFile := c.String("ca-cert-file")
caKeyFile := c.String("ca-key-file")
return nil
}),
)
micro.Profile(profile.Profile):定义了调试组件。
go-micro将调试插件抽象为profile.Profile接口。官方自带支持两种Profile插件:http,pprof
http:以http的形式,提供网页地址进行访问。访问地址:/debug/pprof
pprof:以文件的形式保存在本地目录。目录路径:系统临时目录
profile.Name(string):设置调试组件的名称。
srv := micro.NewService(
micro.Profile(http.NewProfile(profile.Name("web"))),
)
micro.BeforeStart(func() error):服务启动前执行的方法。可设置多个,先设置的先执行。
micro.AfterStart(func() error):服务启动后执行的方法。可设置多个,先设置的先执行。
micro.BefortStop(func() error):服务关闭前执行的方法。可设置多个,先设置的先执行。
micro.AfterStop(func() error):服务关闭后执行的方法。可设置多个,先设置的先执行。
micro.HandleSignal(bool):是否支持通过系统信号来关闭服务。为true时,当收到SIGTERM、SIGINT、SIGQUIT、SIGKILL信号时,关闭服务。
srv := micro.NewService(
micro.BeforeStart(func() error {
log.Info("before start 1")
return nil
}),
micro.AfterStart(func() error {
log.Info("after start 1")
return nil
}),
micro.BeforeStart(func() error {
log.Info("before stop 1")
return nil
}),
micro.BeforeStart(func() error {
log.Info("after stop 1")
return nil
}),
micro.HandleSignal(true),
)
由上述配置可以看到有许多重复的配置。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文件地址,来生成代码。
生成的文件 | 文件说明 |
---|---|
xxx.pb.go | 定义服务及方法的结构体 |
xxx.pb.micro.go | 定义服务的处理逻辑。 |
.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
}