技术栈和环境
技术栈
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
安装protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
在你的GOPATH的bin里面看见
安装protoc-gen-micro
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
在你的GOPATH的bin里面看见
安装micro
go get github.com/micro/micro/v3
在你的GOPATH的bin里面看见
安装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 可以看管理后台
开始编写代码
启动micro
micro server
登录
micro login
// 默认账号
admin
// 默认密码
micro
创建micro服务
// testdemo是你的微服务名字
micro new testdemo
目录结构
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
生成三个文件
进入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对应的函数名称
修改文件
原来的
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也需要重新啦)
进入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的
直接跑就可以了
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
micro
http://127.0.0.1:9002/api/demo
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
成功
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