Skip to content

后端一站式微服务框架,提供API、web、websocket,RPC、任务调度、消息消费服务器

Notifications You must be signed in to change notification settings

micro-plat/hydra

Repository files navigation

hydra微服务容器

基于golang实现。

hydra 提供简单的、统一的、易扩展的服务容器框架。通过少量的代码集成,即可实现的丰富功能,如:集群管理、配置管理、服务注册与发现、系统监控、日志归集、链路追踪、安全访问、常用组件等。

目前已应用于50+生产项目,主要功能: 示例项目:产研任务管理系统

  • ✓ 六类服务

    支持APIWEBWEBSOCKETRPC定时任务消息消费 等服务器,可在单个应用中组合使用。

  • ✓ 跨平台

    支持windows, mac, linux以服务方式安装、运行、停止、卸载等。

  • ✓ 多种部署

    支持分布式集群部署、单机伪集群部署、单机部署。

  • ✓ 多种集群

    提供对等、分片、主从等集群模式。

  • ✓ 配置管理

    采用配置中心,中心化管理配置,本地零配置。支持zookeeper, redis, 本地文件,进程内管理配置。

  • ✓ 热更新

    配置变更后自动生效,无须手动重启服务

  • ✓ 注册与发现

    支持zookeeper, etcd, redis等作为注册中心,为远程调用提供服务管理。

  • ✓ 业务监控

    支持将metric信息(如:QPS、处理时长、响应等)定时上报到influxdb,用于系统运行状况监控大屏显示。

  • ✓ 链路跟踪

    支持Skywalking,Cat等APM工具进行分布式追踪、性能指标分析、应用和服务依赖分析。

  • ✓ 日志归集

    支持将本地日志提交到日志归集服务器( rlog),用于日志集中查询分析。

  • ✓ 访问控制

    支持白名单、黑名单访问控制,Basic Auth、API KEY、Cookie, JWT等安全验证。 支持远程认证服务, 提供加解密、验证签等服务。

  • ✓ 服务器限流

    支持服务器限流、降级、熔断等控制。

  • ✓ 灰度发布

    支持根据业务规则编写灰度脚本,将用户请求转发到不同集群。

  • ✓ 提供常用组件库

    redis,memcached,数据库,mqtt,activeMQ,rpc,uuid,分布式锁,http client,rpc client等。

hydra

一、示例

  • 1. 构建API服务

package main

import (
    "github.com/micro-plat/hydra"
    "github.com/micro-plat/hydra/hydra/servers/http"
)

func main() {

    //创建app
	app := hydra.NewApp(
            hydra.WithPlatName("test"), //平台名
            hydra.WithSystemName("apiserver"), //系统或应用名
            hydra.WithServerTypes(http.API),
    )

    //注册服务
    app.API("/hello", func(ctx hydra.IContext) interface{} {
        return "hello world"
    })

    //启动app
    app.Start()
}
  • 2. 构建RPC服务

package main

import (
    "github.com/micro-plat/hydra"
    "github.com/micro-plat/hydra/hydra/servers/rpc"
)

func main() {

    //创建app
	app := hydra.NewApp(
            hydra.WithServerTypes(rpc.RPC),
    )

    //注册服务
    app.RPC("/hello", func(ctx hydra.IContext) interface{} {
        return "hello world"
    })

    //启动app
    app.Start()
}
  • 3. 构建定时任务服务

package main

import (
    "github.com/micro-plat/hydra"   
    "github.com/micro-plat/hydra/hydra/servers/cron"
)

func main() {

	app := hydra.NewApp(
            hydra.WithServerTypes(cron.CRON),
    )
   
   //注册服务
    app.CRON("/hello",hello,"@every 5s")   
    app.Start()
}
func hello(ctx hydra.IContext) interface{} {
        return "success"
}
  • 4. 构建消息消费服务

package main

import (
    "github.com/micro-plat/hydra"
    "github.com/micro-plat/hydra/hydra/servers/mqc"
    "github.com/micro-plat/hydra/conf/vars/queue/lmq"
)

func main() {

	app := hydra.NewApp(
            hydra.WithServerTypes(mqc.MQC),
    )

    //注册服务
    app.MQC("/hello",hello,"queue-name")

    //设置消息队列服务器(本地内存MQ,支持redis,mqtt等)  
    hydra.Conf.MQC(lmq.MQ)

    app.Start()
}

func hello(ctx hydra.IContext) interface{} {
        return "success"
}

二、组合服务

package main

import (
    "github.com/micro-plat/hydra"   
     "github.com/micro-plat/hydra/hydra/servers/http"
     "github.com/micro-plat/hydra/hydra/servers/cron"
)

func main() {

	app := hydra.NewApp(
            hydra.WithServerTypes(http.API,cron.CRON),
    )

    app.API("/hello",hello)
    app.CRON("/hello",hello,"@every 5s") 
    app.Start()
}
func hello(ctx hydra.IContext) interface{} {
        return "hello world"
}
$ ./flowserver run --plat test

日志如下:

[2020/07/08 09:36:31.140432][i][29f63e41d]初始化: /test/flowserver/api-cron/1.0.0/conf
[2020/07/08 09:36:31.143027][i][29f63e41d]启动[api]服务...
[2020/07/08 09:36:31.643524][i][b65655312]启动成功(api,http://192.168.4.121:8080,1)
[2020/07/08 09:36:31.643885][i][29f63e41d]启动[cron]服务...
[2020/07/08 09:36:31.844844][i][3908a5ccc]启动成功(cron,cron://192.168.4.121,1)
[2020/07/08 09:36:32.346047][d][3908a5ccc]当前server启动为: master
[2020/07/08 09:36:36.648149][i][01751ece6]cron.request: GET /hello from 192.168.4.121
[2020/07/08 09:36:36.648244][i][01751ece6]cron.response: GET /hello 200  193.356µs
[2020/07/08 09:36:41.651858][i][00f45e17b]cron.request: GET /hello from 192.168.4.121
[2020/07/08 09:36:41.651911][i][00f45e17b]cron.response: GET /hello 200  159.694µs

服务已启动,cron server开始周期(每隔5秒)执行任务

$ curl http://192.168.4.121:8080/hello

返回内容:

hello world

三、 服务器配置

1. 设置平台、系统、集群名称

	app := hydra.NewApp(
            hydra.WithPlatName("test"),
            hydra.WithSystemName("apiserver"),
            hydra.WithServerTypes(http.API),
            hydra.WithClusterName("prod"),			
    )
    app.Start()

2. 运行时 通过cli参数指定平台、系统、集群名称

	app := hydra.NewApp(
            hydra.WithServerTypes(http.API),	
    )
    app.Start()

3. API监听端口:

	hydra.Conf.API("8090")	

4. API超时时长

	hydra.Conf.API("8090", api.WithHeaderReadTimeout(30), api.WithTimeout(30, 30)) 

5. 白名单、黑名单

    hydra.Conf.API("8080").			
	WhiteList(whitelist.NewIPList("/**", whitelist.WithIP("192.168.4.121"))).
	BlackList(blacklist.WithIP("192.168.4.120"))

6. JWT认证

    1. 配置*/member*开头人路径不验证,将jwt串串存到header中:
    hydra.Conf.API("8080").Jwt(jwt.WithExcludes("/member/**"), jwt.WithHeader())
    1. 登录成功设置jwt信息:
type UserInfo struct{
    Name string `json:"name"`
    UID string `json:"id"`
}


func login(ctx hydra.IContext) interface{} {
    userInfo := UserInfo{UID:"209867923",Name:"colin"}
	ctx.User().Auth().Response(&userInfo)
	return "success"
}
    1. 从jwt中获取用户信息:
func login(ctx hydra.IContext) interface{} {
     userInfo :=new(UserInfo)
	ctx.User().Auth().Bind(&userInfo)
	return "success"
}

7. 灰度发布

202.222. 开头的IP,转发到名称中包含gray的集群(tengo脚本):

	hydra.Conf.API("8090").Proxy(`	
                request := import("request")
                app := import("app")
                text := import("text")
                types :=import("types")
                fmt := import("fmt")

                getUpCluster := func(){
                    ip := request.getClientIP()
                    current:= app.getCurrentClusterName()
                    if text.has_prefix(ip,"202.222."){
                        return app.getClusterNameBy("gray")
                    }
                    return current
                }
                upcluster := getUpCluster()
		`)

8. 自定义输出格式

    1. 处理输出(tengo脚本):
    hydra.Conf.API("8070").Render(`
            request := import("request")
            response := import("response")
            text := import("text")
            types :=import("types")

            rc:="<response><code>{@status}</code><msg>{@content}</msg></response>"

            getContent := func(){  
                input:={status:response.getStatus(),content:response.getRaw()["id"]}

                if text.has_prefix(request.getPath(),"/tx/request"){
                    return [200,types.translate(rc,input)]
                }
                if text.has_prefix(request.getPath(),"/tx/query"){
                    return [200,types.translate(rc,input),"text/plain"]
                }
                return [200,response.getContent()]
            }

            render := getContent()
            `)
    1. 服务实现
func main() {
	app := hydra.NewApp(
		hydra.WithServerTypes(http.API),
	)

	app.API("/tx/request", request)
	app.API("/tx/query", request)
	app.API("/request", request)
	app.Start()
}
func request(ctx hydra.IContext) interface{} {
	return map[string]interface{}{
		"id": 101010,
	}
}
    1. 请求服务
~/work/bin$ curl http://localhost:8070/tx/request
<response><code>200</code><msg>101010</msg></response>

~/work/bin$ curl http://localhost:8070/request
{"id":101010}

9. 数据库配置

 hydra.Conf.Vars().DB("db", oracle.New("hydra/hydra"))

使用方法:

 func GetUserInfo(id int64)(map[string]interface{},error)
    db:=hydra.C.GetDB()
    userInfo,err:=db.Query("select t.* from user_info t where t.id=@id",map[string]interface{}{"id",id})
    return userInfo,err

10. 缓存配置

 hydra.Conf.Vars().Cache("cache", redis.New("192.168.0.109"))

使用方法:

 func GetUserInfo(id int64)(string,error)
    cache:=hydra.C.GetCache()
    userInfo,err:=cache.Get(fmt.Sprintf("user:info:%d",id))
    return userInfo,err

四、 服务注册

    1. 服务函数
func main() {
	app := hydra.NewApp(
		hydra.WithServerTypes(http.API),
	)
	app.API("/request", request)
	app.Start()
}
func request(ctx hydra.IContext) interface{} {
	return "success"
}
    1. 包含Handle服务的struct构造函数或对象
func main() {
	app := hydra.NewApp(
		hydra.WithServerTypes(http.API),
	)
    app.API("/order", NewOrderService)
    //或
    // app.API("/order", &OrderService{})
	app.Start()
}

type OrderService struct {
}
func NewOrderService()*OrderService{
    return &OrderService{}
}

func (o *OrderService) Handle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------订单处理----------"))
	return "success"
}
    1. 多个服务注册
func main() {
	app := hydra.NewApp(
		hydra.WithServerTypes(http.API),
	)
	app.API("/order/*", NewOrderService)
	app.Start()
}

type OrderService struct {
}
func NewOrderService()*OrderService{
    return &OrderService{}
}

func (o *OrderService) RequestHandle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------收单处理----------"))
	return "success"
}
func (o *OrderService) QueryHandle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------查单处理----------"))
	return "success"
}

实际注册了两个服务 /order/request,/order/query

    1. RESTful服务注册
func main() {
	app := hydra.NewApp(
		hydra.WithServerTypes(http.API),
	)
	app.API("/product", &ProductService{})
	app.Start()
}

type ProductService struct {
}



func (o *ProductService) GetHandle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------查询产品----------"))
	return "success"
}
func (o *ProductService) PostHandle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------新增产品----------"))
	return "success"
}
func (o *OrderService) PutHandle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------修改产品----------"))
	return "success"
}
func (o *OrderService) DeleteHandle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------禁用产品----------"))
	return "success"
}

实际注册了一个服务 /product

    1. 钩子函数
func main() {
	app := hydra.NewApp(
		hydra.WithServerTypes(http.API),
	)
    app.API("/order", NewOrderService)
	app.Start()
}

type OrderService struct {
}
func NewOrderService()*OrderService{
    return &OrderService{}
}
func (o *OrderService) Handling(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------预处理----------"))
	return "success"
}
func (o *OrderService) Handle(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------订单处理----------"))
	return "success"
}
func (o *OrderService) Handled(ctx hydra.IContext) interface{} {
	ctx.Log().Info("--------后处理----------"))
	return "success"
}

五、 服务响应

    1. 普通字符串
func request(ctx hydra.IContext) interface{} {
	return "success"
}

响应内容: success
Content-Type: text/plain; charset=utf-8

    1. JSON字符串
func request(ctx hydra.IContext) interface{} {
	return `{"name":"colin"}`
}

响应内容: {"name":"colin"}
Content-Type: application/json; charset=utf-8

    1. XML字符串
func request(ctx hydra.IContext) interface{} {
	return `<name>colin</name>`
}

响应内容: <name> colin</name> Content-Type: application/xml; charset=utf-8

    1. Map
func request(ctx hydra.IContext) interface{} {
	return map[string]interface{}{"name":"colin"}
}

响应内容: {"name":"colin"}
Content-Type: application/json; charset=utf-8

    1. Struct
func request(ctx hydra.IContext) interface{} {
	return &UserInfo{Name:"colin"}
}

响应内容: {"name":"colin"}
Content-Type: application/json; charset=utf-8

    1. Map,指定ContentType
func request(ctx hydra.IContext) interface{} {
    ctx.Response().ContentType(context.UTF8XML)
	return map[string]interface{}{"name":"colin"}
}

响应内容: <name/>colin <name/> Content-Type: application/xml; charset=utf-8

    1. Struct,指定ContentType
func request(ctx hydra.IContext) interface{} {
    ctx.Response().ContentType(context.UTF8XML)
	return &UserInfo{Name:"colin"}
}

响应内容: <name/>colin <name/> Content-Type: application/xml; charset=utf-8

    1. 普通错误
func request(ctx hydra.IContext) interface{} {
   return fmt.Errorf("err")
}

响应内容: err Content-Type: text/plain; charset=utf-8

    1. 带状态码的错误
func request(ctx hydra.IContext) interface{} {
   return errs.New(500,"err")
}

状态码: 500 响应内容: err Content-Type: text/plain; charset=utf-8

🐿️联系作者

image

💲捐赠 🥣

image