小团队大开发:iogo微服务集群框架

KeyWord: iogo,golang,grpc,protobuf,etcd,zookeeper,microservice,distributed lock,service discovery,cluster,load balancing,k8s,docker,redis,mamcache,5G物联网,微服务框架,分布式集群,分布式锁,分布式文件系统,分布式数据库,高速缓存集群,服务发现,负载均衡,容器编排,一致性哈希

目录

一、iogo 简介

1、开发iogo的初衷

iogo微服务集群框架是grpc、http远程服务方法调用的易用性封装,集成了微服务集群开发中服务发现和负载均衡的核心部分,iogo本着“小团队大开发”的理念,减少开发人员在底层通信方面的开发工作,将精力集中放在应用逻辑的开发上,并且可以轻而易举的进行微服务集群的动态伸缩操作。借助命令行工具iogoc,可以快速创建微服务集群的项目工程模板。

“小团队大开发”是iogo的初衷,对于大型应用场景的超大规模集群来说未免是大话了,但对于中小型的应用场景来说未免不可,尤其更加方便于小型的开发团队,特别特别那个只有一个人做后端的团队。

2、iogo功能简介

  • 跨平台

适用于windows和linux平台的集群开发

  • 便捷的通讯协议定义

您只需定义一对golang结构体:

type LoginRequest struct {
	
}

type LoginReply struct {
	
}

或一对ProtoBuf消息体:

message LoginRequest {
}

message LoginReply {
}

无需定义服务方法,即可实现HTTP或者GRPC的方法调用。iogo自动配对Request和Reply关键字的前缀作为服务方法的函数名称。

  • 插件化设计

iogo使用了插件化的设计方法,您可以自由选择http或者grpc方式来进行通讯,自由选择JSON或者ProtoBuf格式来序列化通讯数据,自由选择ETCD或者Zookeeper作为服务发现的平台,当然您还可以根据插件接口标准自定义插件。

  • 适用互联网或物联网开发

iogo适用于常规的互联网应用开发,也适用于5G物联网开发。

  • 适用消息或流体通讯

iogo全面支持GRPC一元、客户服务流的四种模式,适用于互联网和物联网的消息及流体(文件及视频等)通讯。

  • 其他功能(动态权重负载均衡、异步日志)

iogo集成了动态权重负载均衡,您可以根据实时的CPU、内存、网络IO流量、磁盘IO流量、磁盘空间等综合计算权重值,报告给iogo,iogo负载均衡算法会实时更新权重,合适分配服务响应资源。另外iogo集成了异步日志模块。

二、iogo 框架

1、iogo框架

注:Zookeeper尚未实现

2、典型微服务集群框架

你可以根据集群特点,选择适合自己的微服务访问模式,比如:互联网端使用HTTP+JSON或ProtoBuf模式,内部局域网端集群互相访问则使用GRPC+ProtoBuf模式。

三、iogo 使用说明

以下配置均由iogoc命令行工具自动生成,您仅需要修改集群名字、IP地址、通讯秘钥等参数即可。

注:

1、为便于docker容器编排的动态设置要求,iogo提供了config单元,CfgReader首先读取命令行参数,如果没有则读取ini文件,再没有则选择相应函数传入参数的默认值,详细参阅iogo核心源码文件config.go。

2、iogo插件配置详细方法详细参阅工程模板下的 \comm\iogo\grpc\iogo.go 和 \comm\iogo\http\iogo.go 源码文件。

3、iogo插件实现配置化的设置方式,根据NewOption选项来创建插件

1、ETCD插件选项设置

func getNewEtcdOption(endpoints, flag string) etcd.NewOption {
	if flag == "mutway" {
		caFile := iogo.CfgReader.GetSdCaFileName("")
		certFile := iogo.CfgReader.GetSdCertFileName("")
		keyFile := iogo.CfgReader.GetSdKeyFileName("")
		certSvrName := iogo.CfgReader.GetCertServerName("")
		return etcd.WithCertFile(endpoints, certFile, keyFile, caFile, certSvrName)
	} else {
		return etcd.WithEndpoints(endpoints)
	}
}

ETCD插件提供两项设置:TLS(单向或双向)和非TLS

2、创建JSON和ProtoBuf插件

func getCodec(flag string) plugin.Codec {
	if flag == "json" {
		return json.NewCodec()
	} else {
		return pb.NewCodec()
	}
}

3、创建负载均衡插件

hash.NewLoadBalan(200)

参数:replicas,哈希环复制因子,节点少的时候数值越大平衡性更好。

4、HTTP插件选项设置

// Select IP based on subnet mask
const defSubnetMask = "192.168.1.2/22"

func getNewNetOption(addr, flag string) (svr http.NewOption, cli http.NewOption) {
	if flag == "mutway" {
		caFile := iogo.CfgReader.GetCaFileName("../keys/mutway/ca.crt")
		svrCertFile := iogo.CfgReader.GetServerCertFileName("../keys/mutway/server.crt")
		svrKeyFile := iogo.CfgReader.GetServerKeyFileName("../keys/mutway/server.key")
		cliCertFile := iogo.CfgReader.GetClientCertFileName("../keys/mutway/client.crt")
		cliKeyFile := iogo.CfgReader.GetClientKeyFileName("../keys/mutway/client.key")
		certSvrName := iogo.CfgReader.GetCertServerName("")
		cli := http.WithCertFilesCli(cliCertFile, cliKeyFile, caFile, certSvrName)
		svr := http.WithCertFilesSvr(addr, svrCertFile, svrKeyFile, caFile)
		return svr, cli
	} else if flag == "oneway" {
		caFile := ""
		svrCertFile := iogo.CfgReader.GetServerCertFileName("../keys/oneway/server.crt")
		svrKeyFile := iogo.CfgReader.GetServerKeyFileName("../keys/oneway/server.key")
		cliCertFile := svrCertFile
		cliKeyFile := ""
		certSvrName := ""
		cli := http.WithCertFilesCli(cliCertFile, cliKeyFile, caFile, certSvrName)
		svr := http.WithCertFilesSvr(addr, svrCertFile, svrKeyFile, caFile)
		return svr, cli
	} else {
		return http.WithAddr(addr), http.WithDefault()
	}
}

addr: iogo可以指定服务地址(IP+Port),也可以仅指定端口和子网掩码,iogo自动获取适合的网卡地址来监听

5、GRPC插件选项设置

// Select IP based on subnet mask
const defSubnetMask = "192.168.1.2/22"

func getNewNetOption(addr, flag string) (svr grpc.NewOption, cli grpc.NewOption) {
	if flag == "mutway" {
		caFile := iogo.CfgReader.GetCaFileName("../keys/mutway/ca.crt")
		svrCertFile := iogo.CfgReader.GetServerCertFileName("../keys/mutway/server.crt")
		svrKeyFile := iogo.CfgReader.GetServerKeyFileName("../keys/mutway/server.key")
		cliCertFile := iogo.CfgReader.GetClientCertFileName("../keys/mutway/client.crt")
		cliKeyFile := iogo.CfgReader.GetClientKeyFileName("../keys/mutway/client.key")
		certSvrName := iogo.CfgReader.GetCertServerName("")
		cli := grpc.WithCertFilesCli(cliCertFile, cliKeyFile, caFile, certSvrName)
		svr := grpc.WithCertFilesSvr(addr, svrCertFile, svrKeyFile, caFile)
		return svr, cli
	} else if flag == "oneway" {
		caFile := ""
		svrCertFile := iogo.CfgReader.GetServerCertFileName("../keys/oneway/server.crt")
		svrKeyFile := iogo.CfgReader.GetServerKeyFileName("../keys/oneway/server.key")
		cliCertFile := svrCertFile
		cliKeyFile := ""
		certSvrName := ""
		cli := grpc.WithCertFilesCli(cliCertFile, cliKeyFile, caFile, certSvrName)
		svr := grpc.WithCertFilesSvr(addr, svrCertFile, svrKeyFile, caFile)
		return svr, cli
	} else {
		return grpc.WithAddr(addr), grpc.WithDefault()
	}
}

注:GRPC插件设置基本和HTTP一致,无论HTTP或GRPC都支持单向或双向加密认证通讯或者非加密通讯。

6、iogo插件配置

        pluginCli := iogo.NewPluginClient()
	pluginCli.Cdc = getCodec("pb")
	pluginCli.Lb = hash.NewLoadBalan(200)
	pluginCli.Sd = etcd.NewClient(newOptEtcd)
	pluginCli.Net = grpc.NewClient(newOptNetCli)

	pluginSvr := iogo.NewPluginServer()
	if servicePort != "" {
		pluginSvr.Cdc = pluginCli.Cdc
		pluginSvr.Net = grpc.NewServer(newOptNetSvr)
		pluginSvr.Sd = etcd.NewServer(newOptEtcd, 20, 25)
	}

	iogo.SetPlugin(pluginSvr, pluginCli)

注:服务端和客户端可单独设置不同的网络模式(HTTP和GRPC),便于实现可区别的外部服务访问和内部服务访问。

7、iogo启动

func RunIogo(servicePort string) {
	sdEndpoints := iogo.CfgReader.GetSdEndpoints(defSdEndpoints)
	clusterName := iogo.CfgReader.GetClusterName(defClusterName)
	serviceAddr := iogo.CfgReader.GetServiceAddr("", servicePort, defSubnetMask)

	newOptEtcd := getNewEtcdOption(sdEndpoints, "")                // or mutway stl, or oneway  stl, or "" not saft
	newOptNetSvr, newOptNetCli := getNewNetOption(serviceAddr, "") // or mutway stl, or oneway  stl, or "" not saft

	pluginCli := iogo.NewPluginClient()
	pluginCli.Cdc = getCodec("pb") // json
	pluginCli.Lb = hash.NewLoadBalan(200)
	pluginCli.Sd = etcd.NewClient(newOptEtcd)
	pluginCli.Net = grpc.NewClient(newOptNetCli)

	pluginSvr := iogo.NewPluginServer()
	if servicePort != "" {
		pluginSvr.Cdc = pluginCli.Cdc
		pluginSvr.Net = grpc.NewServer(newOptNetSvr)
		pluginSvr.Sd = etcd.NewServer(newOptEtcd, 20, 25)
	}

	iogo.SetPlugin(pluginSvr, pluginCli)
	iogo.Run(clusterName, serviceAddr)
}

四、iogoc 命令行工具说明

1、集群项目目录,默认在GoPath/src目录下

clustername
├── comm
│   └── iogo
│       ├── grpc
│       │   └── iogo.go
│       ├── http
│       │   └── iogo.go
│       └── iogo.go
├── proto
│   ├── src
│   │    ├── servicename1
│   │    │   └── servicename1.proto
│   │    └── servicename2
│   │        └── servicename1.proto
│   ├── servicename1
│   │   ├── servicename1.ig.go
│   │   └── servicename1.pb.go
│   └── servicename2
│       ├── servicename2.ig.go
│       └── servicename2.pb.go
│
├── svcservicename1
│   ├── main.go
│   └── server
│       ├── interceptor.go
│       └── method.go
└── svcservicename2
    ├── main.go
    └── server
        ├── interceptor.go
        └── method.go

\comm: 公共目录

\comm\iogo\grpc\iogo.go: iogo的GRPC设置

\comm\iogo\http\iogo.go: iogo的HTTP设置

\comm\iogo\iogo.go: iogo的模式选择(GRPC或者HTTP):

package iogo


// 修改此处可选择HTTP或者GRPC模式
// 执行通讯协议转换命令时,iogoc命令行工具会自动根据输出的模式来修改
import "testcluster/comm/iogo/grpc"

func RunIogo(servicePort string) {
	iogo.RunIogo(servicePort)
}

func StopIogo() {
	iogo.StopIogo()
}

\proto\src:ProtoBuf通讯协议源码目录,一个服务进程一个目录

\proto\src\servicename\servicename.proto:ProtoBuf通讯协议源码文件

\proto\servicename\servicename.pb.go:ProtoBuf生成的通讯协议源码go文件

\proto\servicename\servicename.ig.go:iogoc生成的通讯协议源码go文件

\svcservicename1\:  服务进程servicename1目录,iogoc自动加前缀svc

\svcservicename1\server\interceptor.go: 服务方法拦截器go文件

\svcservicename1\server\method.go: 服务方法实现go文件:

package server

import (
	"testcluster/proto/servicename1"
)

type demoServer struct {
}

func init() {
	proto.RegisterDemoServer(&demoServer{})
}

func (self *demoServer) Hellow(ctx proto.Context, request *proto.HellowRequest) (*proto.HellowReply, error) {
	reply := &proto.HellowReply{
		Answer: "Welcome to here",
	}
	return reply, nil
}

\svcservicename1\main.go: 服务进程main.go文件:

package main

import (
	"testcluster/comm/iogo"
	_ "testcluster/svcservicename1/server"
)

const defServicePort = "28666"

func main() {

	iogo.RunIogo(defServicePort)
}

2、选择GoPath(golang源码工作空间)

iogoc需要设置工作空间目录,如果您已经设置GoPath,那么它会自动获取并显示索引号供您输入,或者您也可以直接输入go工作空间的目录全名:

3、创建集群模板或进入集群目录

选择工作空间以后,工作空间目录下的src目录作为集群的根目录,您可以选择集群索引号进入集群目录,或者创建新的集群(创建以后自动进入该集群目录,iogoc自动创建一个svcdemo的服务供参考,目录名均为小写):

mkc NewClusterName

4、创建服务模板

进入集群目录以后,您可以输入mks命令创建服务进程项目目录:

mks NewServiceName

5、编译go源通讯协议(仅适用于HTTP模式)

如果您不打算使用ProtoBuf,那么您可以直接在目录 newclustername\proto\newservicename 下创建通讯协议文件:

newservicename.go(定义Request和Reply对应的一对结构体)

package proto

type LoginRequest struct {
	
}

type LoginReply struct {
	
}

然后输入命令:

igc

iogoc会在目录 newclustername\proto\newservicename 下创建通讯协议接口文件:newservicename.ig.go

注:iogoc会把proto目录下所有服务进程目录下的xxx.go文件生成对应的通讯协议文件xxx.ig.go

newclustername
└── proto
    └── newservicename
        ├── newservicename.go
        └── newservicename.ig.go

6、编译ProtoBuf源通讯协议

如果您打算使用ProtoBuf来序列化数据,你可以使用命令 pbc 来生成HTTP模式的协议文件:

pbc

也可以使用 pbc -g 命令来生成GRPC模式的协议文件

pbc -g

注:

1、iogoc会调用protoc程序把 proto/src 目录下所有 xxx.proto 文件生成xxx.pb.go文件

2、同时iogoc会生成对应的通讯协议文件xxx.ig.go

newclustername
└── proto
    ├── src
    │    └── newservicename
    │        └── newservicename.proto
    └── newservicename
        ├── newservicename.ig.go
        └── newservicename.pb.go

7、iogoc其他命令:

返回上一层目录:

up

退出iogoc:

exit

五、iogo Demo 说明

iogo Demo实现了客户端登录会话服务集群并获取Token,然后通过Token访问应用逻辑服务集群的功能。

六、下载安装

1、golang开发包、liteide、etcd(win)、protobuf、grpc源码、iogo源码等下载包(内附安装方法):

2、iogo源码下载