go微服务框架go-micro深度学习(二) 入门例子

go微服务框架go-micro深度学习(二) 入门例子

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

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

安装所需要的环境

    go-micro服务发现默认使用的是consul

brew install consul
consul agent -dev

   或者直接使用使用docker跑

docker run -p 8300:8300 -p 8301:8301 -p 8301:8301/udp -p 8302:8302/udp -p 8302:8302 -p 8400:8400 -p 8500:8500 -p 53:53/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.1.tar.gz
cd protobuf-3.6.1/
./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 = 1;
}

message Pair {
    int32 key = 1;
    string values = 2;
}

message SayResponse {
    string msg = 1;
    // 数组
    repeated string values = 2;
    // map
    map<string, Pair> header = 3;
    RespType type = 4;
}

enum RespType {
    NONE = 0;
    ASCEND = 1;
    DESCEND = 2;
}

// 服务接口
service Say {
    rpc Hello(SayParam) returns (SayResponse) {}
}
复制代码

 

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

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

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

go微服务框架go-micro深度学习(二) 入门例子

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: 1, 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微服务框架go-micro深度学习(二) 入门例子

 

先是给各个接口初始化默认值,再使用传入的值替换默认值,这也是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()方法 调用流程

go微服务框架go-micro深度学习(二) 入门例子

   因为在初始化的时候没有指定端口,系统会自动分配一个端口号分给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方法里。简单来说流程如下

go微服务框架go-micro深度学习(二) 入门例子

 

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

 例子2

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

    go微服务框架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(3, 10))})

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

   使用双向流的小功能

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

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

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

    return nil
}
复制代码

 

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101413.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • lock.trylock_java中trycatch的用法

    lock.trylock_java中trycatch的用法概述tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。代码@Test publicvoidtestTryLock(){ Locklock=newReentrantLock(); newThrea

  • 各位学弟学妹,别再看教材了,时间复杂度看这篇就好了[通俗易懂]

    各位学弟学妹,别再看教材了,时间复杂度看这篇就好了[通俗易懂]时间复杂度是学习算法的基石,今天我们来聊聊为什么要引入时间复杂度,什么是时间复杂度以及如何去算一个算法的时间复杂度一、刻画算法的运行时间某日,慧能叫来了一尘打算给他补习补习一下基础知识,只见克写了一段非常简单的代码一尘看老师有点生气,开始虚心请教了为了方便讨论,这里我们把每一条语句的执行时间都看做是一样的,记为一个时间单元①蓝色框的两条语句,花费两个时间单元②黑色框的一条语句,花费n+1个时间单元③红色框的两条语句,花费2*n个时间单元这不是.

  • 盘点分布式文件存储系统

    盘点分布式文件存储系统在项目的数据存储中,结构化数据通常采用关系型数据库,非结构化数据(文件)的存储就有很多种方式,服务器本地存储、Nas挂载、ftp等等,今天就来盘点一下,分布式文件存储系统。

  • jdk提供的线程池_创建线程的三种方法

    jdk提供的线程池_创建线程的三种方法JDK1.8创建线程池有哪几种方式?newFixedThreadPool定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程测试代码:publicclassTestThreadPool{ //定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化…

  • 2017noip提高组初赛_noip2021初赛答案

    2017noip提高组初赛_noip2021初赛答案一:装饰器实现AOP///<summary>///装饰器模式实现静态代理///AOP在方法前后增加自定义的方法///</summary>publicclassDecoratorAOP{publicstaticvoidShow(){Useruser=newUser(){Name=”看看看看”,Password=”1231231231

  • vue-router路由懒加载_前端懒加载原理

    vue-router路由懒加载_前端懒加载原理什么是路由懒加载官方的解释:当打包构建应用时,JavaScript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号