文章目录
1. 目录结构
-
bin
编译好的二进制文件 -
cmd
:
各微服务的main函数和相关初始化函数 -
docker
容器相关,包括docker-compose.yml和dockerFile -
internal
:内部代码(不需要对外)app
:各微服务私有的代码cache
:redis相关的代码conf
:配置和变量相关的models
:数据库相关pkg
:各微服务公用的方法等代码
-
pkg
:公共代码,外部可能用到的 -
sql
:sql文件 -
MakeFile
-
README.md
2. 添加自己的微服务
已获取k8s的node节点列表为例,写一个k8s的微服务接入crow-han中
2.1 配置文件和必要的函数
2.1.1 配置文件
- 在
cmd
目录下创建k8s
目录
以后放置微服务k8s的main函数、初始化相关代码、配置文件等
- 该目录下创建
etc
目录放置配置文件 etc
目录下创建kube.conf
文件,将k8s服务器master上 ~/.kube/config文件内容拷贝进去。
2.1.2 连接k8s
在internal/conf
目录下创建 connect_k8s.go
文件
以下这段是链接k8s的代码。这段代码可以作为微服务
k8s
的私有代码放在internal/app/k8s
中,也可以最为脚手架公用配置放在internal/conf
中。考虑到我之后将按照crow-han的风格,在微服务k8s的main函数中初始化k8s链接,为避免以后出现循环调用的可能,这里选择了后者。
ackage conf
import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
var K8sClientSet *kubernetes.Clientset
func ConnectK8s() (clientSet *kubernetes.Clientset, err error) {
configPath := "etc/kube.conf"
config, err := clientcmd.BuildConfigFromFlags("", configPath)
if err != nil {
return nil, err
}
clientSet, err = kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
return clientSet, nil
}
2.2 k8s服务相关代码
在internal/app
下添加k8s
目录(放置该微服务处理逻辑的一些代码),在下边创建 nodes.go
文件(放置node操作相关的代码,我们这里写一个获取nodeList作为示例)。
package service
import (
"context"
"crow-han/internal/conf"
coreV1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func GetNodeList() (nodeList *coreV1.NodeList, err error) {
nodeList, err = conf.K8sClientSet.CoreV1().Nodes().List(context.TODO(), metaV1.ListOptions{})
if err != nil {
return nodeList, err
}
return nodeList, nil
}
2.3 proto
2.3.1 创建proto文件
- 在
proto
目录下创建k8s
目录,下边创建k8s.proto
文件
syntax = "proto3";
package k8s;
option go_package = "./proto/k8s;k8s";
service K8s {
rpc GetNodeList(GetNodeListRequest) returns (GetNodeListResponse) {}
}
message GetNodeListRequest {
}
message GetNodeListResponse {
string NodeList = 1;
}
GetNodeListResponse
这个是微服务返回给gateway的返回值,GetNodeList() 返回是*coreV1.NodeList
,通常我们可以转换成proto的格式。但是因为这个node返回值比较复杂,所以这里我返回一个字串(json)然后在gateway再解析回来。
2.3.2 编译proto
- 编译proto
protoc --proto_path=. --micro_out=. --go_out=:. proto/k8s/k8s.proto
- 生成文件
此时proto/k8s
目录下生成k8s.pb.go
k8s.pb.micro.go
两个文件
2.4 handler
在internal/app/k8s
目录下创建 /handler
目录,下边创建 nodes.go
文件
package handler
import (
"context"
"crow-han/internal/app/k8s/service"
pb "crow-han/proto/k8s"
"encoding/json"
"github.com/toolkits/pkg/logger"
)
type Nodes struct{}
func (e *Nodes) GetNodeList(ctx context.Context, req *pb.GetNodeListRequest, rsp *pb.GetNodeListResponse) error {
logger.Infof("Received K8s.GetNodeList request: %v", req)
nodeList, err := service.GetNodeList()
if err != nil {
logger.Error(err)
return err
}
output, err := json.Marshal(&nodeList)
if err != nil {
logger.Error(err)
}
rsp.NodeList = string(output)
return nil
}
2.5 k8s的main函数
在cmd目录下创建k8s
目录,目录下创建main.go
文件,内容如下
package k8s
import (
"crow-han/internal/app/k8s/handler"
"crow-han/internal/cache"
"crow-han/internal/conf"
pb "crow-han/proto/k8s"
"fmt"
"github.com/go-micro/plugins/v4/registry/consul"
"github.com/kelseyhightower/envconfig"
"go-micro.dev/v4/registry"
"go-micro.dev/v4"
"go-micro.dev/v4/logger"
)
func init() {
//初始化变量
err := envconfig.Process("crow", &conf.MyEnvs)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", &conf.MyEnvs)
//初始化日志
conf.LoggerInit()
logger.Info("logger init")
//mysql初始化
var tx conf.MyConnect
tx.ConnectMysql()
//初始化redis
//redis初始化
conf.RedisConnect()
cache.CheckRides()
//初始化k8s
conf.K8sClientSet, err = conf.ConnectK8s()
if err != nil {
fmt.Println(err)
}
}
var (
service = "k8s"
version = "latest"
)
func main() {
// Create service
srv := micro.NewService()
consulRegis := consul.NewRegistry(func(options *registry.Options) {
options.Addrs = []string{
conf.MyEnvs.ConsulAddr,
}
})
srv.Init(
micro.Name(service),
micro.Version(version),
micro.Registry(consulRegis),
)
// Register handler
if err := pb.RegisterK8SHandler(srv.Server(), new(handler.Nodes)); err != nil {
logger.Fatal(err)
}
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}
3. gateway调用
3.1 调用微服务
在internal/app/gate-way/service
目录下创建k8s_nodes.go
文件,内容如下:
package service
import (
"context"
k8sProto "crow-han/proto/k8s"
"encoding/json"
"github.com/gin-gonic/gin"
"github.com/toolkits/pkg/logger"
coreV1 "k8s.io/api/core/v1"
)
func GetNodeList(c *gin.Context) {
//调用k8s
userSrv := k8sProto.NewK8SService("k8s", srv.Client())
respSr, err := userSrv.GetNodeList(context.Background(), &k8sProto.GetNodeListRequest{})
logger.Infof("%+v", respSr)
if err != nil {
logger.Error(err)
SetErr(c, 500, err, err.Error())
return
}
//将结果转换为原结构体
var resp *coreV1.NodeList
json.Unmarshal([]byte(respSr.NodeList), &resp)
SetOK(c, resp)
}
3.2 router
在internal/app/gate-way/service/router.go
文件中添加一条路由规则
func ServerRouter() {
……
groupV1 := r.Group("/api/v1")
{
……
groupV1.GET("/k8s/node/list", user(), GetNodeList) //添加这条路由
}
说明:需要user验证的加上验证方法 user(),不想验证可以不加。
4. 添加文档 swagger
- 在
internal/app/gate-way/service/k8s_nodes.go
文件的GetNodeList()
函数前添加如下注释
// GetNodeList 查看node列表
// @Summary 查看node列表
// @Description 查看node列表
// @Tags Node
// @Success 200 {object} response.Response{data=v1.NodeList} "{"requestId": "string","code": 200,"msg": "ok","data": [...]}"
// @Failure 500 {object} response.Response{msg=string} "{"requestId": "string","code": 500,"msg": "string","status": "error","data": null}"
// @Router /api/v1/k8s/node/list [get]
// @Security Bearer
func GetNodeList(c *gin.Context) {
……
}
详细方法见另一篇博文《GO语言gin框架实战-03-swagger和接口文档》
- 编译swagger
cd internal/app/gate-way/service
swag init --parseDependency --parseInternal --parseDepth 2 -g .\router.go
5. 调试
5.1 启动服务
-
按上一章 《crow-han(基于go-micro框架的微服务脚手架)-01-快速启动》方法配置数据库并启动gate-way、auth、user。
-
goland配置k8s服务并启动
-
consul上查看
我们新写的微服务注册上来了
5.2 swagger接口测试
- 获取token
默认用户名密码:admin/liubei@161
输出结果
- Bearer验证
点开锁标记,输入Bearer xxxxxxxxxxx
(xxxx 是刚才获取的token值)
- 验证我们之前写的接口
输出如下: