go-micro 是 go 语言生态中较为成熟,全面的框架,有点类似 spring boot 框架,它支持即插即用的插件,友好的中间件支持,负载均衡,服务注册发现,订阅发布,消息事件,配置中心,而且每个插件都支持多种技术,比如服务注册可以使用 consul,mdns,etcd,kubernetes等,你可以自行选择,切换也非常容易。go-micro 默认是 gRPC 服务,你也可以切换到别的 RPC。
我这里将介绍的 go-micro 一种使用方式与官方标准文档上使用的常用方式不一样,因为我不想写 proto,它是件挺麻烦的事儿,你得对消息(message)进行转换,从 proto 消息到你自己的结构体对象,还要从结构体对象转换成 proto 消息。go-micro 提供了对 noproto 的完美支持,而且使用起来比起使用 proto 文件生成 ***.pb.go 更加友好。接下来看看具体如何实现:
此线上的全部源码都已经提供在 github 上(https://github.com/iissy/goweb)目前已经有242个星,下面都还有对应的地址,本网站(爱斯园)已经在使用这套框架,性能非常优秀,每个请求都大概在50-500毫秒以内完成,读者可以试试。
使用到的 go-micro 包"github.com/micro/go-micro/v2"
// 从文件加载配置,你也可以将配置写入 consul,或者甚至一个 gRPC 服务
"github.com/micro/go-micro/v2/config"
"github.com/micro/go-micro/v2/config/source/file"
// 服务注册发现
"github.com/micro/go-micro/v2/registry"
// 服务,调用相关
"github.com/micro/go-micro/v2/server"
"github.com/micro/go-micro/v2/client"
// consul 不是默认,需要引入插件
"github.com/micro/go-plugins/registry/consul/v2"
v2是tag,也就是版本,你也可以去掉v2,但是生产环境建议加上,可以减少兼容性问题
配置文件{
"mysql": {
"hrefs": "root:123456@tcp(192.168.111.151:3306)/hrefs?charset=utf8",
"MaxIdleConns": 5,
"MaxOpenConns": 50,
"gorp": {
"trace-on": false
}
},
"redis": {
"host": "192.168.111.151",
"port": 6379
},
"consul": [{
"host": "192.168.111.151",
"port": 8500
}],
"srv": "micro.hrefs.srv.test",
"domain": "localhost"
}
初始化配置文件const (
defaultConfigPath = "conf/config.json"
)
func init() {
loadConfig()
}
func loadConfig() {
if err := config.Load(file.NewSource(
file.WithPath(defaultConfigPath),
)); err != nil {
log.Panic(err)
}
}
consul 注册// 从配置文件获取consul地址
urls := utils.GetConsulUrls()
reg := consul.NewRegistry(func(op *registry.Options) {
op.Addrs = urls
})
服务方法type Hrefs struct{}
func (s *Hrefs) GetArticle(ctx context.Context, req string, rsp *model.Article) error {
result, err := domain.GetArticle(req)
*rsp = *result
return err
}
启动服务service := micro.NewService(
micro.Registry(reg),
micro.Name(config.Get("srv").String("micro.hrefs.srv")),
micro.WrapHandler(logWrapper),
)
server.Init()
service.Server().Init(server.Wait(nil))
micro.RegisterHandler(service.Server(), new(Hrefs))
service.Run()
封装一个客服端函数var cli client.Client
var name string
func init() {
service := micro.NewService()
service.Init()
cli = service.Client()
name = config.Get("srv").String("micro.hrefs.srv")
}
func Call(method string, req interface{}, rsp interface{}) error {
request := cli.NewRequest(name, fmt.Sprintf("Hrefs.%s", method), req, client.WithContentType("application/json"))
if err := cli.Call(context.TODO(), request, &rsp); err != nil {
fmt.Println(err)
return err
}
return nil
}
web 端控制器调用func Detail(ctx iris.Context) {
id := ctx.Params().Get("id")
result := new(model.Article)
err := cli.Call("GetArticle", id, result)
if ok := utils.WriteErrorLog(ctx.Path(), err); ok {
ctx.NotFound()
return
}
if result == nil {
ctx.NotFound()
return
}
ctx.ViewData("title", result.Title)
ctx.ViewData("body", result)
ctx.View("detail.html")
}
没有 proto 的gRPC 微服务的优势:无需维护一个 proto 文件
开发效率,使用成本更低
输入输出类型更加灵活,比如可以直接是 struct,string,bool 等类型
无需消息转换,可以直接使用 struct,string,bool 对象
适合少数人维护的项目
劣势:如果接口经常变动,而使用方不是自己就容易出错
不方便多人协作和规范化
没有统一的 proto 文件,对外部使用不友好