上一篇帖子简单介绍了go-micro的整体框架结构,这一篇主要写go-micro使用方式的例子,中间会穿插一些go-micro的源码,和调用流程图,帮大家更好的理解go-micro的底层。更详细更具体的调用流程和细节,会在以后的帖子里详细讲解。

例子的github地址: gomicrorpc   跑一遍例子,也就会明白个大概。

安装所需要的环境

go-micro服务发现默认使用的是consul,(2019年源码修改了默认使用mdns)

brew install consul
consul agent -dev

或者直接使用使用docker跑

docker run -p : -p : -p :/udp -p :/udp -p : -p : -p : -p :/udp consul

我个人更喜欢etcdv3原因我上一篇也有提到过,gomicro服务发现不支持consul集群,我之前也写过etcdv3 集群的搭建和使用帖子,有时间大家可以看一下

安装go-micro框架

go get github.com/micro/go-micro

安装protobuf和依赖 prtobuf的基础知识我这里就不讲了,如果不了解的可以看一下官方文档,就是一个跨平台,跨语言的数据序列化库,简单易学。

是go-micro用于帮助我们生成服务接口和一系列的调用代码

brew install protobuf
go get -u -v github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u -v github.com/micro/protoc-gen-micro

protobuf也可以直接从源码安装

wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz
tar zxvf protobuf-all-3.6..tar.gz
cd protobuf-3.6./
./autogen.sh
./configure
make
make install
protoc -h

 

安装micro工具包,这个安装是可选项,micro提供了一系列的工具来帮助我们更好的使用go-micro。

go get github.com/micro/micro

例子1

创建proto文件common.proto,这个文件包含了传入和返回的参数,参数包含了常用的基础类型、数组、map等。还有一个Say 服务,这个服务里有一个rpc方法。

syntax = "proto3";

package model;

message SayParam {
string msg = ;
} message Pair {
int32 key = ;
string values = ;
} message SayResponse {
string msg = ;
// 数组
repeated string values = ;
// map
map<string, Pair> header = ;
RespType type = ;
} enum RespType {
NONE = ;
ASCEND = ;
DESCEND = ;
} // 服务接口
service Say {
rpc Hello(SayParam) returns (SayResponse) {}
}

在根目录下运行,生成两个模板文件

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

一个文件是proto的go 结构文件,还有一个go-micro rpc的接口文件。

server 端:

type Say struct {}

func (s *Say) Hello(ctx context.Context, req *model.SayParam, rsp *model.SayResponse) error {
fmt.Println("received", req.Msg)
rsp.Header = make(map[string]*model.Pair)
rsp.Header["name"] = &model.Pair{Key: , Values: "abc"} rsp.Msg = "hello world"
rsp.Values = append(rsp.Values, "a", "b")
rsp.Type = model.RespType_DESCEND return nil
} func main() {
// 我这里用的etcd 做为服务发现,如果使用consul可以去掉
reg := etcdv3.NewRegistry(func(op *registry.Options){
op.Addrs = []string{
"http://192.168.3.34:2379", "http://192.168.3.18:2379", "http://192.168.3.110:2379",
}
}) // 初始化服务
service := micro.NewService(
micro.Name("lp.srv.eg1"),
micro.Registry(reg),
)
service.Init()
// 注册 Handler
model.RegisterSayHandler(service.Server(), new(Say)) // run server
if err := service.Run(); err != nil {
panic(err)
}
}

服务发现我使用的是etcdv3  替换了默认的consul

micro.NewService 初始化服务,然后返回一个Service接口的实例,newService()方法的大概流程如下,

先是给各个接口初始化默认值,再使用传入的值替换默认值,这也是go-micro可替换插件的地方。

service有一个Init()可选方法,这是一个单例方法,

func (s *service) Init(opts ...Option) {
// process options
for _, o := range opts {
o(&s.opts)
} s.once.Do(func() {
// save user action
action := s.opts.Cmd.App().Action
// set service action
s.opts.Cmd.App().Action = func(c *cli.Context) {
.........//这里就不把代码全显示出来了
.........
}
}

用于始化cmd的一些信息

service.Run()方法 调用流程

因为在初始化的时候没有指定端口,系统会自动分配一个端口号分给Server,并把这个server的信息注册到Register。

BeferStart和AfterStart也都是可以自定义的

client 端:

func main() {
// 我这里用的etcd 做为服务发现,如果使用consul可以去掉
reg := etcdv3.NewRegistry(func(op *registry.Options){
op.Addrs = []string{
"http://192.168.3.34:2379", "http://192.168.3.18:2379", "http://192.168.3.110:2379",
}
}) // 初始化服务
service := micro.NewService(
micro.Registry(reg),
)
service.Init()
sayClent := model.NewSayService("lp.srv.eg1", service.Client()) rsp, err := sayClent.Hello(context.Background(), &model.SayParam{Msg: "hello server"})
if err != nil {
panic(err)
} fmt.Println(rsp) }

上面根据proto文件的生成的两个文件中有一个是rpc的接口文件,接口文件已经帮我们把调用方法的整个流程封装好了。

只需要给出服务名称和licent就可以。然后调用Hello方法

源码:

func (c *sayService) Hello(ctx context.Context, in *SayParam, opts ...client.CallOption) (*SayResponse, error) {
req := c.c.NewRequest(c.name, "Say.Hello", in)
out := new(SayResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}

主要的流程里都在c.c.Call方法里。简单来说流程如下

就是得到节点信息address,根据address去查询 pool里是否有连接,如果有则取出来,如果没有则创建,然后进行数据传输,传输完成后把client放回到pool内。pool的大小也是可以控制的,这部分的代码读起来特别爽,具体的细节和处理流程会在以后的帖子里详细讲解

例子2

例子1,做了一个简单的服务,已经不能再简单了,只是为了能让大家熟悉一下go-micro。看完例子1后应该会有更多的想法,想使用更多的go-micro的功能,比如protobuf生成的类都在一起,如果想model和api分开怎么处理,怎么使用go-micro的双向流,怎么使用消息推送,等等。所以我就双做了一个小例子,这个例子里包含了一些东西。

这个例子我就只说一下组织结构,也没有多少代码,大家有时间看一下就ok了。

proto下的两个文件夹,一个model一个rpcapi,是把数据和api分开,api引用了model

看一下rpcapi

syntax = "proto3";

package rpcapi;
import "github.com/lpxxn/gomicrorpc/example2/proto/model/common.proto"; // 服务接口
service Say {
rpc Hello(model.SayParam) returns (model.SayResponse) {}
rpc Stream(model.SRequest) returns (stream model.SResponse) {} }

import了model里的common.proto

在生成的时候一个只要go_out另一个只要micro_out就好了

  protoc --proto_path=$GOPATH/src:. --go_out=. example2/proto/model/*.proto 

  protoc --proto_path=$GOPATH/src:. --micro_out=. example2/proto/rpcapi/*.proto 

订阅一个信息

    // Register Subscribers
if err := server.Subscribe(server.NewSubscriber(common.Topic1, subscriber.Handler)); err != nil {
panic(err)
}

当有信息发送时,所有订阅了lp.srv.eg2.topic1这个信息的服务都会收到信息

客户端发送信息

    p := micro.NewPublisher(common.Topic1, service.Client())
p.Publish(context.TODO(), &model.SayParam{Msg: lib.RandomStr(lib.Random(, ))})

如果是生产环境一定不要用go-micro默认的信息发布和订阅处理方式,micro的插件plugin里是有很多成熟的插件。

使用双向流的小功能

这个方法只是每次向客户端发送一些数据,每次只发送一部分。比如我们给客户端推送的数据很大时,一次性全都推过去,是不太正确的做法,分批推送还是比较好的。

func (s *Say) Stream(ctx context.Context, req *model.SRequest, stream rpcapi.Say_StreamStream) error {

    for i := ; i < int(req.Count); i++ {
rsp := &model.SResponse{}
for j := lib.Random(, ); j < ; j++ {
rsp.Value = append(rsp.Value, lib.RandomStr(lib.Random(, )))
}
if err := stream.Send(rsp); err != nil {
return err
}
// 模拟处理过程
time.Sleep(time.Microsecond * )
}
return nil return nil
}

希望这个小例子能让大家入门go-micro.

go微服务框架go-micro深度学习(二) 入门例子的更多相关文章

  1. go微服务框架Kratos笔记(一)入门教程

    kratos简介 Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关功能及工具 本文基于kratos v2.0.3,windows平台,其他系统平台均可借鉴参考 环境搭建 Golang开发 ...

  2. 微服务框架SpringCloud(Dalston版)学习 (一):Eureka服务注册与发现

    eureka-server eureka服务端,提供服务的注册与发现,类似于zookeeper 新建spring-boot工程,pom依赖: <dependency> <groupI ...

  3. go微服务框架go-micro深度学习-目录

    go微服务框架go-micro深度学习(一) 整体架构介绍 go微服务框架go-micro深度学习(二) 入门例子 go微服务框架go-micro深度学习(三) Registry服务的注册和发现 go ...

  4. net core 微服务框架 Viper 调用链路追踪

    1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...

  5. hello world .net core 微服务框架 Viper

    1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...

  6. Viper 微服务框架 编写一个hello world 插件-02

    1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...

  7. go微服务框架go-micro深度学习(四) rpc方法调用过程详解

    上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取server的地 ...

  8. go微服务框架go-micro深度学习 rpc方法调用过程详解

    摘要: 上一篇帖子go微服务框架go-micro深度学习(三) Registry服务的注册和发现详细解释了go-micro是如何做服务注册和发现在,服务端注册server信息,client获取serv ...

  9. go微服务框架go-micro深度学习(一) 整体架构介绍

    产品嘴里的一个小项目,从立项到开发上线,随着时间和需求的不断激增,会越来越复杂,变成一个大项目,如果前期项目架构没设计的不好,代码会越来越臃肿,难以维护,后期的每次产品迭代上线都会牵一发而动全身.项目 ...

随机推荐

  1. easyui+themeleaf 分页查询实现

    <!DOCTYPE html> <html xmlns:th="http://www.w3.org/1999/xhtml"> <head> &l ...

  2. oracle中两个时间类型的数据相减默认得到的是天数。

    想要得到月份数,需要使用months_between函数. months_between(to_date('201707','yyyyMM'),to_date('201706','yyyyMM'))  ...

  3. Django之模板基础

    Django之模板 目录 变量 过滤器 标签的使用 变量 变量的引用格式 使用双括号,两边空格不能省略. 语法格式: {{var_name}} Template和Context对象 context 字 ...

  4. window下的计划任务

    0x00前言: 这几天看了看信息安全就业的面试题,其中有一条是计划任务如何设置,好几个月前稍微接触了,但是很久没用差不多都忘了>_<,这里就稍微学习下windows的计划任务 写着写着就偏 ...

  5. PTA 5-3 解题报告

    GitHub 问题描述 解题思路 代码 问题描述 5-3 计算平均值 (50分) 现在为若干组整数分别计算平均值. 已知这些整数的绝对值都小于100,每组整数的数量不少于1个,不大于20个. 输入格式 ...

  6. ARC101E - Ribbons on Tree

    题目链接 ARC101E - Ribbons on Tree 题解 令边集\(S \subseteq E\) 设\(f(S)\)为边集S中没有边被染色的方案数 容斥一下,那么\(ans = \sum_ ...

  7. BZOJ.3351.[IOI2009]Regions(根号分治 差分)

    BZOJ 表示非常爽2333 \(Description\) 给定一棵\(n\)个点的树,每个点有一个属性\(1\leq r_i\leq R\). \(Q\)次询问,每次询问给定\(r1,r2\),求 ...

  8. Min_25筛 学习笔记

    这儿只是一个简单说明/概括/总结. 原理见这: https://www.cnblogs.com/cjyyb/p/9185093.html https://www.cnblogs.com/zhoushu ...

  9. linux修改文件为可执行文件

    修改shell为可执行文件 chmod +x test2.sh chmod 751 file   给file的属主分配读.写.执行(7)的权限,给file的所在组分配读.执行(5)的权限,给其他用户分 ...

  10. (转)我是一个java class

    前言:本文主要想讲一下Java虚拟机的故事, 可能有点偏门,不妥之处欢迎留言交流. 第一回 陌生警察 我出生在C盘下面一个很深层次的目录下, 也不知道是谁把我放到这里的. 我一直在睡觉,外边的日出日落 ...