go-micro 实践和部署

1,580 阅读5分钟

技术栈和环境

技术栈

go 1.16
go-micro v3
micro v3
protobuf v3
gprc
consul

环境

Mac 11.2.2
Liunx centeros 8

说明

go 1.17.1

go 1.17.1 目前(2021.9.10)go-micro会和consul冲突

go-micro micro区别

v3版本开始 micro 和 go-micro是分开来的,micro属于是公司,go-micro属于asim。

micro目标是变成云服务

go-micro是微服务

我们这里使用go-micro就可以了,micro只是帮我们快速生成框架

环境安装

安装go.16

官网(golang.google.cn/dl/)下载pkg包,直接点击安装就可以了

配置环境变量

打开配置

open ~/.bash_profile

编写配置

export GOROOT=/usr/local/go
# GOPATH 你的项目的目录
export GOPATH=/Users/Desktop/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOBIN

保存

source ~/.bash_profile

GOROOT 和 GOPATH 说明

GOROOT是golang的根目录

GOPATH是你的项目目录

在GOPATH里面一般有bin pkg src三个文件

bin是保存你二进制的目录
pkg是保存下载的第三方库
src是写代码的地方

安装protobuf

protobuf3下载地址(github.com/protocolbuf…

 

下载下来后解压压缩包,并进入目录

cd protobuf-3.7.0/

设置编译目录

./configure --prefix=/usr/local/protobuf

安装

make
make install

打开配置

open ~/.bash_profile

  编写配置

# protobuf
export PROTOBUF=/usr/local/protobuf 
export PATH=$PROTOBUF/bin:$PATH

保存

source ~/.bash_profile

测试安装结果

protoc --version

截屏2021-09-10 上午11.45.16.png

 

安装protoc-gen-go

go get github.com/micro/micro/v3/cmd/protoc-gen-micro

在你的GOPATH的bin里面看见

截屏2021-09-10 下午12.01.36.png  

安装protoc-gen-micro

go get github.com/micro/micro/v3/cmd/protoc-gen-micro

在你的GOPATH的bin里面看见 截屏2021-09-10 下午12.01.36.png

安装micro

go get github.com/micro/micro/v3

在你的GOPATH的bin里面看见

截屏2021-09-10 下午12.01.36.png

安装consul

我这里是使用docker安装

docker pull consul

运行

docker run -d -p 8500:8500 -h consul --name consul consul agent -dev -client 0.0.0.0 -ui

输入http://127.0.0.1:8500/ui 可以看管理后台

截屏2021-09-10 下午12.01.36.png

开始编写代码

启动micro

micro server

登录

micro login

// 默认账号
admin

// 默认密码
micro

创建micro服务

// testdemo是你的微服务名字
micro new testdemo

目录结构

 

截屏2021-09-10 下午12.53.17.png  

proto 文件说明

// proto 版本
syntax = "proto3";


// 包名
package testdemo;


// 包位置
option go_package = "./proto;testdemo";


// 可以理解为 函数、方法 通过GRPC 调用的方法
service Testdemo {
   rpc Call(Request) returns (Response) {}
   rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
   rpc PingPong(stream Ping) returns (stream Pong) {}
}


// 结构体、model 
// 具体规则可以看proto文档
// 命名就是这样 1,2,3顺下去就可以了,具体类型可以看proto文档
message Message {
   string say1 = 1;
   string say2 = 2;
   string say3 = 3;
}


message Request {
   string name = 1;
}


message Response {
   string msg = 1;
}


message StreamingRequest {
   int64 count = 1;
}


message StreamingResponse {
   int64 count = 1;
}


message Ping {
   int64 stroke = 1;
}


message Pong {
   int64 stroke = 1;
}

 

编写proto

因为只想写简单的demo就保存这些了

syntax = "proto3";


package testdemo;


option go_package = "./proto;testdemo";


service Testdemo {
   rpc Call(Request) returns (Response) {}
}


message Request {
   string name = 1;
}


message Response {
   string msg = 1;
}
cd ./项目


make proto

  生成三个文件

截屏2021-09-10 下午1.34.05.png

 

进入testdemo.pb.micro.go

  修改引用

原来的

import (
    fmt "fmt"
    proto "github.com/golang/protobuf/proto"
    math "math"
)


import (
    context "context"
    client "github.com/micro/go-micro/client"
    server "github.com/micro/go-micro/server"

 

修改之后的,把micro变成go-micro

import (
   fmt "fmt"
   proto "github.com/golang/protobuf/proto"
   math "math"
)


import (
   context "context"
   client "github.com/asim/go-micro/v3/client"
   server "github.com/asim/go-micro/v3/server"
)

 

进入handler

handler文件里的是protoc对应的函数名称

截屏2021-09-10 下午1.45.52.png  

修改文件

原来的

package handler


import (
   "context"


   log "github.com/micro/micro/v3/service/logger"


   testdemo "testdemo/proto"
)


type Testdemo struct{}


// Call is a single request handler called via client.Call or the generated client code
func (e *Testdemo) Call(ctx context.Context, req *testdemo.Request, rsp *testdemo.Response) error {
   log.Info("Received Testdemo.Call request")
   rsp.Msg = "Hello " + req.Name
   return nil
}


// Stream is a server side stream handler called via client.Stream or the generated client code
func (e *Testdemo) Stream(ctx context.Context, req *testdemo.StreamingRequest, stream testdemo.Testdemo_StreamStream) error {
   log.Infof("Received Testdemo.Stream request with count: %d", req.Count)


   for i := 0; i < int(req.Count); i++ {
      log.Infof("Responding: %d", i)
      if err := stream.Send(&testdemo.StreamingResponse{
         Count: int64(i),
      }); err != nil {
         return err
      }
   }


   return nil
}


// PingPong is a bidirectional stream handler called via client.Stream or the generated client code
func (e *Testdemo) PingPong(ctx context.Context, stream testdemo.Testdemo_PingPongStream) error {
   for {
      req, err := stream.Recv()
      if err != nil {
         return err
      }
      log.Infof("Got ping %v", req.Stroke)
      if err := stream.Send(&testdemo.Pong{Stroke: req.Stroke}); err != nil {
         return err
      }
   }
}

修改之后的,删除多余的函数

package handler


import (
   "context"
   testdemo "testdemo/proto"
)


type Testdemo struct{}


// Call is a single request handler called via client.Call or the generated client code
func (e *Testdemo) Call(ctx context.Context, req *testdemo.Request, rsp *testdemo.Response) error {
    fmt.Println(req, rsp)
   rsp.Msg = req.Name
   return nil
}

进入main.go

package main


import (
   "fmt"
   "github.com/asim/go-micro/plugins/registry/consul/v3"
   "github.com/asim/go-micro/v3"
   "testdemo/handler"
   pb "testdemo/proto"
)


func main() {
   // 初始化consul
   consulReg := consul.NewRegistry()


   // Create service
   // 开启名字是microdemo服务 版本是latest
   srv := micro.NewService(
      micro.Address("0.0.0.0:9001"),  // 防止随机生成 port
      micro.Name("Microdemo"), //微服务名称
      micro.Registry(consulReg),  // 添加注册 使用consul
      micro.Version("1.0.0"), // 版本
   )






   // Register handler
   pb.RegisterTestdemoHandler(srv.Server(), new(handler.Microdemo))


   // Run service
   if err := srv.Run(); err != nil {
      //logger.Fatal(err)
      fmt.Println("启动失败 =",err)
   }
}

创建web服务

  • 创建microwebdemo目录
  • 创建main.go
  • 将micor的proto项目拉过去(以后修改proto也需要重新啦)

  截屏2021-09-10 下午1.57.38.png

  进入main.go

我这里是使用gin框架的

package main


import (
   "context"
   "fmt"
   "github.com/asim/go-micro/plugins/registry/consul/v3"
   "github.com/asim/go-micro/v3"
   "github.com/gin-gonic/gin"
   pb "microwebdemo/proto"
   "net/http"
)


func main() {


   // 初始化路由
   router := gin.Default()


   // 分组
   r1 := router.Group("api/")
   {
      r1.GET("/demo",demo)
      r1.GET("/hahn",hahn)
   }


   // 启动运行
   router.Run("0.0.0.0:9002")
}


func demo(ctx *gin.Context)  {
   //指定 consul 服务发现
   consulReg := consul.NewRegistry()
   consulService := micro.NewService(
      micro.Registry(consulReg),
   )


   // 初始化客户端
   microClient := pb.NewTestdemoService("Microdemo", consulService.Client())


   // 调用远程函数
   resp, err := microClient.Call(context.TODO(), &pb.Request{Name: "zzr"})
   if err != nil {
      fmt.Println("未找到远程服务...")
      return
   }


   res := make(map[string]interface{})
   res["errno"] = 200
   res["errmsg"] = "成功"
   res["data"] = resp


   // 发送校验结果 给 浏览器
   ctx.JSON(http.StatusOK, res)
}


func hahn(ctx *gin.Context) {
   resp := make(map[string]interface{})
   resp["errno"] = 200
   resp["errmsg"] = "成功"
   resp["data"] = "data"


   ctx.JSON(http.StatusOK, resp)
}

 

错误:package xxx is not in GOROOT or GOPATH

如果出现这个

cd ./microwebdemo


go mod init
go mod tidy

 

运行项目

启动web和micro

我是使用goland idea的

直接跑就可以了

截屏2021-09-10 下午2.06.19.png

 

docker 启动consul

第一次启动

docker run -d -p 8500:8500 -h consul --name consul consul agent -dev -client 0.0.0.0 -ui

第二次启动

// 找到从前启动的服务
docker ps -a


// 启动docker xxx 镜像id或名称
docker start xxx


// 关闭docker xxx 镜像id或名称
docker stop xxx

 

测试

单纯的gin

http://127.0.0.1:9002/api/hahn

截屏2021-09-10 下午2.10.47.png  

micro

http://127.0.0.1:9002/api/demo

截屏2021-09-10 下午2.11.22.png

liunx docker-compose 部署项目

打包go文件

分别进入micro和web

打包liunx环境

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go

  修改两个main名称为microwebdemo和microdemo

 

docker 打包

分别进入micro和web

编写Dockerfile

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /xxxx/go/src/microwebdemo/ # 你的microwebdemo路径 go打包的程序
COPY microwebdemo ./
CMD ["./microwebdemo"]


FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /xxxx/go/src/microdemo/ # 你的microdemo路径go打包的程序
COPY microdemo ./
CMD ["./microdemo"]
docker build -t microwebdemo .


docker build -t microdemo .

docker images查看镜像

上传镜像

分别进入micro和web

打包镜像

docker image save microdemo > microdemo.tar


docker image save microwebdemo > microwebdemo.tar

上传microdemo.tar和microwebdemo.tar到liunx

 

liunx配置

把tar变成镜像

docker load --input microdemo.tar


docker load --input microwebdemo.tar

docker images 查看镜像

docker-compose 配置

# docker-compose 版本
version: '3.5'


services:
  # consul镜像
  consul:
    image: consul # 读取本地镜像
    container_name: consul # 命名镜像名称
    network_mode: host # 通信模式是host
    command: agent -dev -client 0.0.0.0 -ui # 开启执行命令、这里是运行consul 开发者模式


  # microdemo镜像
  microdemo:
    image: microdemo
    container_name: microdemo
    network_mode: host
    
# microwebdemo镜像
  microwebdemo:
    image: microwebdemo
    container_name: microwebdemo
    network_mode: host

  启动服务

docker-compose up

成功

 

4481631255707_.pic_hd.jpg

4471631255698_.pic.jpg

4461631255688_.pic.jpg  

docker 和 docker-compose 命令简单说明

docker

# docker镜像列表
docker images


# docker 运行的镜像
docker ps-a


# 运行docker 一般是第一次启动或者需要开多一个才跑
# xxx对应镜像名称、ID
# 如果多docker run 同一个镜像。docker ps-a会出现多个同一个镜像,而且数据也不一样
docker run xxx


# 启动docker ps-a里面的镜像 一般是在这里重启
# xxx对应镜像名称、ID
docker start xxx


# 关闭docker ps-a里面的镜像 一般是在这里关闭
# xxx对应镜像名称、ID
docker stop xxx


# 删除镜像
# xxx对应镜像名称、ID
docker rm xxx

 

docker-compose

docker-compose 可以将多个docker同时启动


// 启动docker-compose
docker-compose up


// 关闭docker-compose
docker-compose down