未验证 提交 49ee231b 编写于 作者: E Eolink 提交者: GitHub

Merge pull request #31 from Dot-Liu/master

3.1.1版本更新
......@@ -8,3 +8,5 @@
/db/
/static/
/sql/
/sql
/static
......@@ -3,15 +3,15 @@ package main
import "flag"
//ParseFlag 获取命令行参数
func ParseFlag() (port int, admin string, staticConfigFile string, isDebug bool) {
func ParseFlag() (instance string, admin string, staticConfigFile string, isDebug bool) {
adminP := flag.String("admin", "", "Please provide a valid host!")
portP := flag.Int("port", 0, "Please provide a valid listen port!")
instanceP := flag.String("instance", "", "Please provide a valid instance!")
staticConfigFileP := flag.String("config", "", "Please provide a config file")
isDebugP := flag.Bool("debug", false, "")
flag.Parse()
return *portP, *adminP, *staticConfigFileP, *isDebugP
return *instanceP, *adminP, *staticConfigFileP, *isDebugP
}
package main
import (
"github.com/eolinker/goku-api-gateway/module/graphite"
"github.com/eolinker/goku-api-gateway/module/prometheus"
)
func init() {
prometheus.Register()
graphite.Register()
}
\ No newline at end of file
......@@ -5,8 +5,6 @@ import (
"github.com/eolinker/goku-api-gateway/config"
log "github.com/eolinker/goku-api-gateway/goku-log"
console2 "github.com/eolinker/goku-api-gateway/node/console"
"github.com/eolinker/goku-api-gateway/node/gateway"
"github.com/eolinker/goku-api-gateway/node/router/httprouter"
"github.com/eolinker/goku-api-gateway/node/server"
"runtime"
)
......@@ -14,53 +12,30 @@ import (
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
port, admin, staticConfigFile, isDebug := ParseFlag()
instance, admin, staticConfigFile, isDebug := ParseFlag()
if isDebug {
log.StartDebug()
}
if port == 0{
flag.Usage()
if admin != "" && instance != ""{
console := console2.NewConsole(instance, admin)
ser := server.NewServer()
log.Fatal(ser.ServerWidthConsole(console))
return
}
if admin != "" {
// 从控制台启动,
console := console2.NewConsole(port, admin)
ser := server.NewServer(port)
ser.SetConsole(console)
log.Fatal(ser.Server())
} else if staticConfigFile != "" {
if staticConfigFile != "" {
// 从静态文件启动
c, err := config.ReadConfig(staticConfigFile)
if err != nil {
log.Panic("read config from :", staticConfigFile, "\t", err)
}
server.SetLog(c.Log)
server.SetAccessLog(c.AccessLog)
r, err := gateway.Parse(c, httprouter.Factory())
if err != nil {
log.Panic("parse config error:", err)
}
ser := server.NewServer(port)
e := ser.SetRouter(r)
if e != nil {
log.Panic("init router error:", e)
}
log.Fatal(ser.Server())
} else {
//
flag.Usage()
return
ser := server.NewServer()
log.Fatal(ser.ServerWidthConfig(c))
}
flag.Usage()
}
......@@ -185,7 +185,7 @@ CREATE TABLE "goku_gateway_api" (
"protocol" text(20),
"stripSlash" text(32),
"apiType" integer NOT NULL DEFAULT 0,
"responseDataType" text NOT NULL DEFAULT origin,
"responseDataType" text NOT NULL DEFAULT 'origin',
"linkApis" TEXT,
"staticResponse" TEXT
);
......@@ -319,22 +319,27 @@ CREATE TABLE "goku_node_group" (
DROP TABLE IF EXISTS "goku_node_info";
CREATE TABLE "goku_node_info" (
"nodeID" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"nodeIP" text(255) NOT NULL,
"updateStatus" integer(4) NOT NULL DEFAULT 0,
"createTime" text,
"updateTime" text,
"groupID" integer(11) NOT NULL DEFAULT 0,
"nodeName" text(255) NOT NULL,
"nodePort" text(255),
"nodeStatus" integer(11) NOT NULL,
"version" text(255),
"sshPort" text(255) DEFAULT 22,
"userName" text(255),
"password" text(255),
"sshAddress" text(255) DEFAULT 22,
"sshUserName" text(255),
"sshPassword" text(255),
"gatewayPath" text(255),
"key" text,
"sshKey" text,
"authMethod" integer(4) NOT NULL DEFAULT 0,
"clusterID" integer(11) NOT NULL DEFAULT 0
"clusterID" integer(11) NOT NULL DEFAULT 0,
"listenAddress" text(22) NOT NULL DEFAULT '',
"adminAddress" text(22) NOT NULL DEFAULT '',
"nodeKey" TEXT(32) NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX "nodeKey_new"
ON "goku_node_info_new" (
"nodeKey" ASC
);
-- ----------------------------
......@@ -404,6 +409,32 @@ CREATE TABLE "goku_table_update_record" (
"tableID" integer NOT NULL PRIMARY KEY AUTOINCREMENT
);
CREATE TABLE "goku_table_version" (
"tableID" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"tableName" TEXT NOT NULL,
"version" TEXT NOT NULL
);
CREATE UNIQUE INDEX "tableName"
ON "goku_table_version" (
"tableName"
);
INSERT INTO "goku_table_version" ("tableName","version") VALUES ('goku_node_info', "3.1.1");
INSERT INTO "goku_table_version" ("tableName","version") VALUES ('goku_monitor_module', "3.1.1");
CREATE TABLE "goku_monitor_module" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"config" TEXT NOT NULL,
"moduleStatus" integer NOT NULL DEFAULT 0
);
CREATE UNIQUE INDEX "moduleName"
ON "goku_monitor_module" (
"name" ASC
);
-- ----------------------------
-- Records of "sqlite_sequence"
......
......@@ -17,7 +17,7 @@
<title>GoKu Gateway | 企业微服务架构的首选解决方案,加速企业数字化转型</title>
<link href="assets/images/favicon.ico" rel="shortcut icon">
<!-- <base href="/"> -->
<link rel="stylesheet" href="styles/app-2b0238ab04.css">
<link rel="stylesheet" href="styles/app-3f7f5c6a31.css">
</head>
<!--[if lt IE 8]>
<style>html,body{overflow:hidden;height:100%}</style>
......@@ -34,6 +34,6 @@
</div>
<eo-modal></eo-modal>
</body>
<script src="scripts/app-1b47111b19.js"></script>
<script src="scripts/app-d2cabf38be.js"></script>
</html>
\ No newline at end of file
......@@ -2,7 +2,6 @@ package database
import (
"database/sql"
"fmt"
"io/ioutil"
"strings"
......@@ -57,8 +56,8 @@ func InitTable() error {
for _, sql := range sqls {
_, err = Tx.Exec(sql)
if err != nil {
fmt.Println(sql)
Tx.Rollback()
log.Error("InitTable error:",err,"\t sql:",sql)
panic(err)
return err
}
......
......@@ -2,9 +2,12 @@ package telegraph
import (
"context"
"errors"
"sync"
)
var (
ErrorContextDone = errors.New("context done")
)
//Telegraph telegraph
type Telegraph struct {
value interface{}
......@@ -40,7 +43,6 @@ func (t *Telegraph) Set(version string, value interface{}) {
func (t *Telegraph) get() (string, <-chan struct{}, interface{}) {
t.locker.RLock()
version, c, value := t.version, t.c, t.value
t.locker.RUnlock()
......@@ -48,7 +50,7 @@ func (t *Telegraph) get() (string, <-chan struct{}, interface{}) {
}
//Get get
func (t *Telegraph) Get(version string) interface{} {
func (t *Telegraph) Get(version string) (interface{},error) {
return t.GetWidthContext(context.Background(), version)
}
......@@ -61,21 +63,21 @@ func (t *Telegraph) Close() {
}
//GetWidthContext 获取上下文
func (t *Telegraph) GetWidthContext(ctx context.Context, version string) interface{} {
func (t *Telegraph) GetWidthContext(ctx context.Context, version string) (interface{} ,error){
v, c, value := t.get()
if v == "" {
// closed
return nil
return nil,nil
}
if version != v {
return value
return value,nil
}
select {
case <-c:
return t.GetWidthContext(ctx, version)
case <-ctx.Done():
return nil
return nil,ErrorContextDone
}
}
package version
//Version 版本号
const Version = "3.1.0"
const Version = "3.1.1"
package config
//GokuConfig goku根配置
type GokuConfig struct {
Version string `json:"version"`
Cluster string `json:"cluster"`
Instance string `json:"instance"`
BindAddress string `json:"bind"`
AdminAddress string `json:"admin"`
//Port int `json:"port"`
DiscoverConfig map[string]*DiscoverConfig `json:"discover,omitempty"`
Balance map[string]*BalanceConfig `json:"balance,omitempty"`
......@@ -15,6 +20,8 @@ type GokuConfig struct {
Log *LogConfig `json:"log,omitempty"`
AccessLog *AccessLogConfig `json:"access_log,omitempty"`
MonitorModules map[string]string `json:"monitor_modules"`
}
//AccessLogConfig access日志配置
......
......@@ -18,5 +18,6 @@ func ReadConfig(file string) (*GokuConfig, error) {
}
c := &GokuConfig{}
e := json.Unmarshal(data, c)
return c, e
}
......@@ -2,22 +2,18 @@ package admin
import (
"net/http"
"strconv"
"strings"
)
//GetIPPort 获取客户端IP和端口
func GetIPPort(r *http.Request) (string, int, error) {
//GetIPPort 获取客户端IP和instance
func GetInstanceAndIP(r *http.Request) (string, string, error) {
ip := r.RemoteAddr
ip = ip[:strings.Index(ip, ":")]
if realIP := strings.TrimSpace(r.Header.Get("X-Real-Ip")); realIP != "" {
ip = realIP
}
r.ParseForm()
p := r.FormValue("port")
port, err := strconv.Atoi(p)
if err != nil {
return ip, port, err
}
return ip, port, nil
}
instance := r.FormValue("instance")
return ip, instance, nil
}
\ No newline at end of file
......@@ -2,17 +2,13 @@ package admin
import (
"github.com/eolinker/goku-api-gateway/console/module/node"
entity "github.com/eolinker/goku-api-gateway/server/entity/console-entity"
)
func getCluster(ip string, port int) (string, error) {
has, node, err := node.GetNodeInfoByIPPort(ip, port)
func GetNode(key string)(*entity.Node, error){
node, err := node.GetNodeInfoByKey(key)
if err != nil {
return "", err
}
if !has {
return "", err
return nil, err
}
return node.Cluster, nil
return node, nil
}
......@@ -7,5 +7,6 @@ func router() http.Handler {
serverHandler := http.NewServeMux()
serverHandler.HandleFunc("/version/config/get", GetVersionConfig)
return serverHandler
}
package admin
import (
"net/http"
"strconv"
"encoding/json"
"github.com/eolinker/goku-api-gateway/console/controller"
"github.com/eolinker/goku-api-gateway/console/module/node"
"github.com/eolinker/goku-api-gateway/console/module/versionConfig"
"github.com/eolinker/goku-api-gateway/console/controller"
"net/http"
)
//GetVersionConfig 获取版本配置
func GetVersionConfig(httpResponse http.ResponseWriter, httpRequest *http.Request) {
httpRequest.ParseForm()
version := httpRequest.Form.Get("version")
ip, port, err := GetIPPort(httpRequest)
func GetVersionConfig(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
version := r.Form.Get("version")
ip, instance, err := GetInstanceAndIP(r)
if err != nil {
controller.WriteError(httpResponse, "700000", "cluster", err.Error(), err)
controller.WriteError(w, "700000", "cluster", err.Error(), err)
return
}
node.Refresh(ip, strconv.Itoa(port))
cluster, err := getCluster(ip, port)
if !node.Lock(instance){
controller.WriteError(w, "700007", "cluster", "invalid instance", nil)
return
}
defer node.UnLock(instance)
nodeInfo, err := GetNode(instance)
if err != nil {
controller.WriteError(httpResponse, "700001", "cluster", err.Error()+ip, err)
controller.WriteError(w, "700001", "cluster", err.Error()+ip, err)
return
}
ctx := r.Context()
result ,err:= versionConfig.GetVersionConfig(ctx,nodeInfo.Cluster, version)
if err!= nil{
// client close, connect close
return
}
result := versionConfig.GetVersionConfig(cluster, version)
result.Cluster = nodeInfo.Cluster
result.BindAddress = nodeInfo.ListenAddress
result.AdminAddress = nodeInfo.AdminAddress
result.Instance = nodeInfo.NodeKey
data,_:= json.Marshal(result)
w.Write(data)
httpResponse.Write(result)
}
......@@ -3,156 +3,11 @@ package gateway
import (
"encoding/json"
"net/http"
"strconv"
"github.com/eolinker/goku-api-gateway/console/controller"
"github.com/eolinker/goku-api-gateway/console/module/gateway"
)
//GetGatewayConfig 获取网关配置
func GetGatewayConfig(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationGatewayConfig, controller.OperationREAD)
if e != nil {
return
}
result, err := gateway.GetGatewayConfig()
if err != nil {
controller.WriteError(httpResponse,
"320000",
"gateway",
"[ERROR]The gateway config does not exist",
err)
return
}
controller.WriteResultInfo(httpResponse, "gateway", "gatewayConfig", result)
return
}
//EditGatewayBaseConfig 编辑网关基本配置
func EditGatewayBaseConfig(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationGatewayConfig, controller.OperationEDIT)
if e != nil {
return
}
successCode := httpRequest.PostFormValue("successCode")
nodeUpdatePeriod := httpRequest.PostFormValue("nodeUpdatePeriod")
monitorUpdatePeriod := httpRequest.PostFormValue("monitorUpdatePeriod")
monitorTimeout := httpRequest.PostFormValue("monitorTimeout")
nodePeriod, err := strconv.Atoi(nodeUpdatePeriod)
if err != nil {
controller.WriteError(httpResponse,
"320001",
"gateway",
"[ERROR]Illegal nodeUpdatePeriod!",
err)
return
}
monitorPeriod, err := strconv.Atoi(monitorUpdatePeriod)
if err != nil && monitorUpdatePeriod != "" {
controller.WriteError(httpResponse,
"320002",
"gateway",
"[ERROR]Illegal monitorUpdatePeriod!",
err)
return
}
if monitorUpdatePeriod == "" {
monitorPeriod = 30
}
timeout, err := strconv.Atoi(monitorTimeout)
if (err != nil && monitorTimeout != "") || (timeout < 1 && timeout > 30) {
controller.WriteError(httpResponse,
"320011",
"gateway",
"[ERROR]Illegal monitorTimeout!",
err)
return
}
if monitorTimeout == "" {
timeout = 5
}
flag, result, err := gateway.EditGatewayBaseConfig(successCode, nodePeriod, monitorPeriod, timeout)
if !flag {
controller.WriteError(httpResponse,
"320000",
"gateway",
result,
err)
return
}
controller.WriteResultInfo(httpResponse, "gateway", "", nil)
return
}
//EditGatewayAlarmConfig 编辑网关告警配置
func EditGatewayAlarmConfig(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationGatewayConfig, controller.OperationEDIT)
if e != nil {
return
}
alertStatus := httpRequest.PostFormValue("alertStatus")
apiAlertInfo := httpRequest.PostFormValue("apiAlertInfo")
sender := httpRequest.PostFormValue("sender")
senderPassword := httpRequest.PostFormValue("senderPassword")
smtpAddress := httpRequest.PostFormValue("smtpAddress")
smtpPort := httpRequest.PostFormValue("smtpPort")
smtpProtocol := httpRequest.PostFormValue("smtpProtocol")
aStatus, err := strconv.Atoi(alertStatus)
if err != nil {
controller.WriteError(httpResponse,
"320003",
"gateway",
"[ERROR]Illegal alertStatus!",
err)
return
}
port, err := strconv.Atoi(smtpPort)
if err != nil {
controller.WriteError(httpResponse,
"320005",
"gateway",
"[ERROR]Illegal smtpPort!",
err)
return
}
proto, err := strconv.Atoi(smtpProtocol)
if err != nil {
controller.WriteError(httpResponse,
"320006",
"gateway",
"[ERROR]Illegal smtpProtocol!",
err)
return
}
flag, result, err := gateway.EditGatewayAlarmConfig(apiAlertInfo, sender, senderPassword, smtpAddress, aStatus, port, proto)
if !flag {
controller.WriteError(httpResponse,
"320000",
"gateway",
result,
err)
return
}
controller.WriteResultInfo(httpResponse, "gateway", "", nil)
return
}
//GetGatewayBasicInfo 获取网关基本信息
func GetGatewayBasicInfo(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationNone, controller.OperationREAD)
......
package monitor
import (
"net/http"
"strconv"
"github.com/pkg/errors"
"github.com/eolinker/goku-api-gateway/console/controller"
"github.com/eolinker/goku-api-gateway/console/module/monitor"
)
//GetMonitorModules 获取监控模块列表
func GetMonitorModules(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationNone, controller.OperationREAD)
if e != nil {
return
}
result, err := monitor.GetMonitorModules()
if err != nil {
controller.WriteError(httpResponse,
"410000",
"monitor",
err.Error(),
err)
return
}
controller.WriteResultInfo(httpResponse, "monitorModule", "moduleList", result)
return
}
func SetMonitorModule(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationNone, controller.OperationREAD)
if e != nil {
return
}
httpRequest.ParseForm()
moduleName := httpRequest.Form.Get("moduleName")
moduleStatus := httpRequest.Form.Get("moduleStatus")
config := httpRequest.Form.Get("config")
status, err := strconv.Atoi(moduleStatus)
if err != nil && moduleStatus != "" {
errInfo := "[error]illegal moduleStatus"
controller.WriteError(httpResponse,
"410001",
"monitor",
errInfo,
errors.New(errInfo))
return
}
err = monitor.SetMonitorModule(moduleName, config, status)
if err != nil {
controller.WriteError(httpResponse,
"410000",
"monitor",
err.Error(),
err)
return
}
controller.WriteResultInfo(httpResponse, "monitorModule", "", nil)
return
}
......@@ -3,6 +3,7 @@ package node
import (
"encoding/json"
"errors"
"github.com/eolinker/goku-api-gateway/common/auto-form"
"github.com/eolinker/goku-api-gateway/console/module/cluster"
......@@ -33,30 +34,48 @@ func AddNode(httpResponse http.ResponseWriter, httpRequest *http.Request) {
}
//nodeNumber := rsa.CertConf["nodeNumber"].(int)
nodeName := httpRequest.PostFormValue("nodeName")
nodeIP := httpRequest.PostFormValue("nodeIP")
nodePort := httpRequest.PostFormValue("nodePort")
groupID := httpRequest.PostFormValue("groupID")
gatewayPath := httpRequest.PostFormValue("gatewayPath")
gID, err := strconv.Atoi(groupID)
if err != nil && groupID != "" {
controller.WriteError(httpResponse, "230015", "", "[ERROR]Illegal groupID!", err)
type NodeParam struct {
NodeName string `opt:"nodeName,require"`
ListenAddress string `opt:"listenAddress,require"`
AdminAddress string `opt:"adminAddress,require"`
GroupID int `opt:"groupID,require"`
Path string `opt:"gatewayPath"`
}
//
//nodeName := httpRequest.PostFormValue("nodeName")
//listenAddress := httpRequest.PostFormValue("listenAddress")
//adminAddress := httpRequest.PostFormValue("adminAddress")
//groupID := httpRequest.PostFormValue("groupID")
//gatewayPath := httpRequest.PostFormValue("gatewayPath")
//
//gID, err := strconv.Atoi(groupID)
//if err != nil && groupID != "" {
// controller.WriteError(httpResponse, "230015", "", "[ERROR]Illegal groupID!", err)
// return
//}
param:=new(NodeParam)
err:=auto.SetValues(httpRequest.Form,param)
if err!= nil{
controller.WriteError(httpResponse, "230015", "", "[ERROR]", err)
return
}
flag := utils.ValidateRemoteAddr(nodeIP + ":" + nodePort)
if !flag {
if !utils.ValidateRemoteAddr(param.ListenAddress) {
controller.WriteError(httpResponse,
"230006",
"node", "[ERROR]Illegal remote address!",
errors.New("Illegal remote address"))
"node", "[ERROR]Illegal listenAddress!",
errors.New("illegal listenAddress"))
return
}
if gID != 0 {
if !utils.ValidateRemoteAddr(param.AdminAddress) {
controller.WriteError(httpResponse,
"230007",
"node", "[ERROR]Illegal listenAddress!",
errors.New("illegal listenAddress"))
return
}
if param.GroupID != 0 {
// 检查分组是否存在
flag, err = node.CheckNodeGroupIsExist(gID)
flag, err := node.CheckNodeGroupIsExist(param.GroupID)
if !flag {
controller.WriteError(
......@@ -69,18 +88,7 @@ func AddNode(httpResponse http.ResponseWriter, httpRequest *http.Request) {
}
}
exits := node.CheckIsExistRemoteAddr(0, nodeIP, nodePort)
if exits {
controller.WriteError(httpResponse,
"230005",
"node",
"[ERROR]The remote address is alreadey existed!",
errors.New("The remote address is alreadey existed"))
return
}
flag, result, err := node.AddNode(clusterID, nodeName, nodeIP, nodePort, gatewayPath, gID)
flag, result, err := node.AddNode(clusterID, param.NodeName, param.ListenAddress, param.AdminAddress, param.Path, param.GroupID)
if !flag {
controller.WriteError(httpResponse,
......@@ -100,7 +108,7 @@ func AddNode(httpResponse http.ResponseWriter, httpRequest *http.Request) {
}
data, _ := json.Marshal(res)
httpResponse.Write(data)
_,_=httpResponse.Write(data)
}
//EditNode 修改节点信息
......@@ -112,18 +120,26 @@ func EditNode(httpResponse http.ResponseWriter, httpRequest *http.Request) {
}
nodeName := httpRequest.PostFormValue("nodeName")
nodeIP := httpRequest.PostFormValue("nodeIP")
nodePort := httpRequest.PostFormValue("nodePort")
listenAddress := httpRequest.PostFormValue("listenAddress")
adminAddress := httpRequest.PostFormValue("adminAddress")
groupID := httpRequest.PostFormValue("groupID")
nodeID := httpRequest.PostFormValue("nodeID")
gatewayPath := httpRequest.PostFormValue("gatewayPath")
// key := httpRequest.PostFormValue("key")
flag := utils.ValidateRemoteAddr(nodeIP + ":" + nodePort)
if !flag {
controller.WriteError(httpResponse, "230006", "node", "[ERROR]Illegal remote address!", errors.New("[ERROR]Illegal remote address"))
if !utils.ValidateRemoteAddr(listenAddress) {
controller.WriteError(httpResponse,
"230006",
"node", "[ERROR]Illegal listenAddress!",
errors.New("illegal listenAddress"))
return
}
if !utils.ValidateRemoteAddr(adminAddress) {
controller.WriteError(httpResponse,
"230007",
"node", "[ERROR]Illegal listenAddress!",
errors.New("illegal listenAddress"))
return
}
......@@ -141,7 +157,7 @@ func EditNode(httpResponse http.ResponseWriter, httpRequest *http.Request) {
if gID != 0 {
// 检查分组是否存在
flag, err = node.CheckNodeGroupIsExist(gID)
flag, err := node.CheckNodeGroupIsExist(gID)
if !flag {
controller.WriteError(
......@@ -154,15 +170,15 @@ func EditNode(httpResponse http.ResponseWriter, httpRequest *http.Request) {
}
}
exits := node.CheckIsExistRemoteAddr(id, nodeIP, nodePort)
if exits {
//exits := node.CheckIsExistRemoteAddr(id, listenAddress, adminAddress)
//if exits {
//
// controller.WriteError(httpResponse, "230005", "node", "[ERROR]The remote address is existed!", nil)
// return
//
//}
controller.WriteError(httpResponse, "230005", "node", "[ERROR]The remote address is existed!", nil)
return
}
flag, result, _ := node.EditNode(nodeName, nodeIP, nodePort, gatewayPath, id, gID)
flag, result, _ := node.EditNode(nodeName, listenAddress, adminAddress, gatewayPath, id, gID)
if !flag {
controller.WriteError(httpResponse, "330000", "node", result, nil)
......@@ -270,8 +286,8 @@ func GetNodeInfo(httpResponse http.ResponseWriter, httpRequest *http.Request) {
err)
return
}
flag, result, err := node.GetNodeInfo(id)
if !flag {
result, err := node.GetNodeInfo(id)
if err!= nil {
controller.WriteError(httpResponse,
"330000",
......@@ -286,43 +302,7 @@ func GetNodeInfo(httpResponse http.ResponseWriter, httpRequest *http.Request) {
return
}
//CheckIsExistRemoteAddr 节点IP查重
func CheckIsExistRemoteAddr(httpResponse http.ResponseWriter, httpRequest *http.Request) {
_, e := controller.CheckLogin(httpResponse, httpRequest, controller.OperationNode, controller.OperationREAD)
if e != nil {
return
}
nodeIP := httpRequest.PostFormValue("nodeIP")
nodePort := httpRequest.PostFormValue("nodePort")
flag := utils.ValidateRemoteAddr(nodeIP)
if !flag {
controller.WriteError(httpResponse,
"230006",
"node",
"[ERROR]The remote address does not exist!",
nil)
return
}
flag = node.CheckIsExistRemoteAddr(0, nodePort, nodePort)
if !flag {
controller.WriteError(httpResponse,
"330000",
"node",
"[ERROR]Remote address is existed!",
nil)
return
}
controller.WriteResultInfo(httpResponse, "node", "", nil)
return
}
//BatchEditNodeGroup 批量修改节点分组
func BatchEditNodeGroup(httpResponse http.ResponseWriter, httpRequest *http.Request) {
......
......@@ -5,10 +5,8 @@ import (
"strconv"
"github.com/eolinker/goku-api-gateway/console/controller"
"github.com/eolinker/goku-api-gateway/console/module/node"
"github.com/eolinker/goku-api-gateway/console/module/plugin"
plugin_config "github.com/eolinker/goku-api-gateway/console/module/plugin/plugin-config"
"github.com/eolinker/goku-api-gateway/utils"
)
// GetPluginList 获取插件列表
......@@ -451,37 +449,37 @@ func CheckPluginIsAvailable(httpResponse http.ResponseWriter, httpRequest *http.
nil)
return
}
flag, nodeList, err := node.GetNodeIPList()
if !flag {
controller.WriteError(httpResponse,
"210000",
"plugin",
"[ERROR]The Open node list is empty",
err)
return
}
flag, errPluginList := utils.CheckPluginIsAvailiable(pluginName, nodeList)
if !flag {
controller.WriteResultInfoWithCode(httpResponse,
"210000",
"plugin",
"errNodeList",
errPluginList)
return
}
flag, res, _ := plugin.EditPluginCheckStatus(pluginName, 1)
if !flag {
controller.WriteError(httpResponse,
"210000",
"plugin",
res,
err)
return
}
controller.WriteResultInfo(httpResponse, "plugin", "", nil)
//flag, nodeList, err := node.GetNodeIPList()
//if !flag {
// controller.WriteError(httpResponse,
// "210000",
// "plugin",
// "[ERROR]The Open node list is empty",
// err)
// return
//}
//flag, errPluginList := utils.CheckPluginIsAvailiable(pluginName, nodeList)
//if !flag {
//
// controller.WriteResultInfoWithCode(httpResponse,
// "210000",
// "plugin",
// "errNodeList",
// errPluginList)
// return
//}
//flag, res, _ := plugin.EditPluginCheckStatus(pluginName, 1)
//if !flag {
//
// controller.WriteError(httpResponse,
// "210000",
// "plugin",
// res,
// err)
// return
//}
//
//controller.WriteResultInfo(httpResponse, "plugin", "", nil)
return
}
package updater
import (
"net/http"
"github.com/eolinker/goku-api-gateway/console/controller"
"github.com/eolinker/goku-api-gateway/console/module/updater"
)
//IsTableExist 检查table是否存在
func IsTableExist(httpResponse http.ResponseWriter, httpRequest *http.Request) {
httpRequest.ParseForm()
name := httpRequest.Form.Get("name")
exist := updater.IsTableExist(name)
controller.WriteResultInfo(httpResponse, "updater", "exist", exist)
return
}
//IsColumnExist 检查列是否存在
func IsColumnExist(httpResponse http.ResponseWriter, httpRequest *http.Request) {
httpRequest.ParseForm()
name := httpRequest.Form.Get("name")
column := httpRequest.Form.Get("column")
exist := updater.IsColumnExist(name, column)
controller.WriteResultInfo(httpResponse, "updater", "exist", exist)
return
}
......@@ -56,7 +56,8 @@ func (p *PageInfo) SetPage(page, size, total int) *PageInfo {
//NewItemNum 创建新的item
func NewItemNum(num int) *PageInfo {
return &PageInfo{
ItemNum: num,
ItemNum: num,
TotalNum: num,
}
}
......
package console
import (
_ "github.com/eolinker/goku-api-gateway/console/updater/manager"
graphite "github.com/eolinker/goku-api-gateway/module/graphite/config"
prometheus "github.com/eolinker/goku-api-gateway/module/prometheus/config"
)
func moduleRegister() {
prometheus.Register()
//prometheus_pushgateway.Register()
graphite.Register()
//statsD.Register()
}
package monitor
import (
"github.com/eolinker/goku-api-gateway/ksitigarbha"
console_sqlite3 "github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3"
"github.com/pkg/errors"
)
type MonitorModule struct {
Name string `json:"moduleName"`
Config interface{} `json:"config,omitempty"`
ModuleStatus int `json:"moduleStatus"`
Desc string `json:"moduleDesc"`
Models interface{} `json:"layer"`
}
//GetMonitorModules 获取监控模块列表
func GetMonitorModules() ([]*MonitorModule, error) {
m, err := console_sqlite3.GetMonitorModules()
if err != nil {
return make([]*MonitorModule, 0), nil
}
names := ksitigarbha.GetMonitorModuleNames()
modules := make([]*MonitorModule, 0, len(names))
for _, name := range names {
model,_ := ksitigarbha.GetMonitorModuleModel(name)
mod :=&MonitorModule{
Name: name,
Config: model.GetDefaultConfig(),
ModuleStatus: 0,
Desc: model.GetDesc(),
Models: model.GetModel(),
}
v, ok := m[name]
if ok {
mod.ModuleStatus = v.ModuleStatus
c ,err := model.Decode(v.Config)
if err == nil {
mod.Config = c
}
}
modules = append(modules, mod)
}
return modules, nil
}
func SetMonitorModule(moduleName string, config string, moduleStatus int) error {
model,has := ksitigarbha.GetMonitorModuleModel(moduleName)
if !has {
return errors.New("[error]the module does not exist")
}
if moduleStatus == 1 {
_ ,err:= model.Decode(config)
if err != nil{
//errInfo := "[error]invalid config"
return err
}
}
err := console_sqlite3.SetMonitorModule(moduleName, config, moduleStatus)
if err != nil {
return err
}
return nil
}
package node
import "testing"
func Test_InstanceLocker_Lock(t *testing.T) {
l:=newInstanceLocker()
instance:="test"
if !l.Lock(instance){
t.Error("should lock true")
return
}
if l.Lock(instance){
t.Error("should lock false")
return
}
l.UnLock(instance)
if !l.Lock(instance){
t.Error("should lock true")
return
}
if l.Lock(instance){
t.Error("should lock false")
return
}
}
\ No newline at end of file
package node
import (
"time"
console_sqlite3 "github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3"
entity "github.com/eolinker/goku-api-gateway/server/entity/console-entity"
"github.com/eolinker/goku-api-gateway/utils"
)
//AddNode 新增节点信息
func AddNode(clusterID int, nodeName, nodeIP, nodePort, gatewayPath string, groupID int) (bool, map[string]interface{}, error) {
return console_sqlite3.AddNode(clusterID, nodeName, nodeIP, nodePort, gatewayPath, groupID)
func AddNode(clusterID int, nodeName, listenAddress, adminAddress, gatewayPath string, groupID int) (bool, map[string]interface{}, error) {
now := time.Now().Format("20060102150405")
nodeKey := utils.Md5(utils.GetRandomString(16) + now)
return console_sqlite3.AddNode(clusterID, nodeName, nodeKey, listenAddress, adminAddress, gatewayPath, groupID)
}
//EditNode 修改节点
func EditNode(nodeName, nodeIP, nodePort, gatewayPath string, nodeID, groupID int) (bool, string, error) {
return console_sqlite3.EditNode(nodeName, nodeIP, nodePort, gatewayPath, nodeID, groupID)
func EditNode(nodeName, listenAddress, adminAddress, gatewayPath string, nodeID, groupID int) (bool, string, error) {
return console_sqlite3.EditNode(nodeName, listenAddress, adminAddress, gatewayPath, nodeID, groupID)
}
//DeleteNode 删除节点
......@@ -22,19 +26,18 @@ func DeleteNode(nodeID int) (bool, string, error) {
}
//GetNodeInfo 获取节点信息
func GetNodeInfo(nodeID int) (bool, *entity.Node, error) {
b, node, e := console_sqlite3.GetNodeInfo(nodeID)
func GetNodeInfo(nodeID int) ( *entity.Node, error) {
node, e := console_sqlite3.GetNodeInfo(nodeID)
ResetNodeStatus(node)
return b, node, e
return node, e
}
//GetNodeInfoByIPPort 获取节点信息
func GetNodeInfoByIPPort(ip string, port int) (bool, *entity.Node, error) {
b, node, e := console_sqlite3.GetNodeByIPPort(ip, port)
//GetNodeInfoByKey 获取节点信息
func GetNodeInfoByKey(nodeKey string) ( *entity.Node, error) {
node, e := console_sqlite3.GetNodeByKey(nodeKey)
ResetNodeStatus(node)
return b, node, e
return node, e
}
// GetNodeList 获取节点列表
func GetNodeList(clusterID, groupID int, keyword string) (bool, []*entity.Node, error) {
b, nodes, e := console_sqlite3.GetNodeList(clusterID, groupID, keyword)
......@@ -42,10 +45,6 @@ func GetNodeList(clusterID, groupID int, keyword string) (bool, []*entity.Node,
return b, nodes, e
}
//CheckIsExistRemoteAddr 节点IP查重
func CheckIsExistRemoteAddr(nodeID int, nodeIP, nodePort string) bool {
return console_sqlite3.CheckIsExistRemoteAddr(nodeID, nodeIP, nodePort)
}
//BatchDeleteNode 批量删除节点
func BatchDeleteNode(nodeIDList string) (bool, string, error) {
......@@ -62,8 +61,8 @@ func BatchDeleteNode(nodeIDList string) (bool, string, error) {
func BatchEditNodeGroup(nodeIDList string, groupID int) (bool, string, error) {
return console_sqlite3.BatchEditNodeGroup(nodeIDList, groupID)
}
//GetNodeIPList 获取节点IP列表
func GetNodeIPList() (bool, []map[string]interface{}, error) {
return console_sqlite3.GetNodeIPList()
}
//
////GetNodeIPList 获取节点IP列表
//func GetNodeIPList() (bool, []map[string]interface{}, error) {
// return console_sqlite3.GetNodeIPList()
//}
package node
import (
"fmt"
"sync"
"time"
......@@ -9,13 +8,14 @@ import (
)
//EXPIRE 心跳检测过期时间
const EXPIRE = time.Second * 20
const EXPIRE = time.Second * 5
var (
manager = _StatusManager{
locker: sync.RWMutex{},
lastHeartBeat: make(map[string]time.Time),
}
instanceLocker = newInstanceLocker()
)
type _StatusManager struct {
......@@ -47,16 +47,59 @@ func (m *_StatusManager) get(id string) (time.Time, bool) {
return t, b
}
type _InstanceLocker struct {
locker sync.RWMutex
instances map[string]bool
}
func newInstanceLocker() *_InstanceLocker {
return &_InstanceLocker{
locker: sync.RWMutex{},
instances: make(map[string]bool),
}
}
func (l * _InstanceLocker)IsLock(key string)bool {
l.locker.RLock()
locked :=l.instances[key]
l.locker.RUnlock()
return locked
}
func (l * _InstanceLocker)Lock(key string)bool{
locked :=l.IsLock(key)
if locked{
return false
}
l.locker.Lock()
locked =l.instances[key]
if locked{
l.locker.Unlock()
return false
}
l.instances[key] = true
l.locker.Unlock()
return true
}
func (l * _InstanceLocker)UnLock(key string){
l.locker.Lock()
l.instances[key] = false
l.locker.Unlock()
}
//Refresh refresh
func Refresh(ip string, port string) {
id := fmt.Sprintf("%s:%d", ip, port)
manager.refresh(id)
func Refresh( instance string) {
manager.refresh(instance)
}
//IsLive 通过ip和端口获取当前节点在线状态
func IsLive(ip string, port string) bool {
id := fmt.Sprintf("%s:%d", ip, port)
t, has := manager.get(id)
func IsLive( instance string) bool {
t, has := manager.get(instance)
if !has {
return false
}
......@@ -71,10 +114,22 @@ func IsLive(ip string, port string) bool {
func ResetNodeStatus(nodes ...*entity.Node) {
for _, node := range nodes {
if IsLive(node.NodeIP, node.NodePort) {
if instanceLocker.IsLock(node.NodeKey) || IsLive(node.NodeKey) {
node.NodeStatus = 1
} else {
node.NodeStatus = 0
}
}
}
func Lock(key string)bool{
return instanceLocker.Lock(key)
}
func UnLock(key string) {
instanceLocker.UnLock(key)
Refresh(key)
}
func IsLock(key string)bool{
return instanceLocker.Lock(key)
}
\ No newline at end of file
package updater
import (
"github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3/updater"
)
//IsTableExist 检查table是否存在
func IsTableExist(name string) bool {
return updater.IsTableExist(name)
}
//IsColumnExist 检查列是否存在
func IsColumnExist(name string, column string) bool {
return updater.IsColumnExist(name, column)
}
......@@ -2,7 +2,6 @@ package versionConfig
import (
"context"
"encoding/json"
"sync"
"time"
......@@ -37,25 +36,31 @@ func init() {
func InitVersionConfig() {
load()
}
func (c *versionConfig) getConfig(cluster string, version string) []byte {
func (c *versionConfig) GetV(cluster string) *telegraph.Telegraph {
c.lock.RLock()
v, ok := c.config[cluster]
c.lock.RUnlock()
if !ok {
c.lock.Lock()
v, ok = c.config[cluster]
if !ok {
v = telegraph.NewTelegraph("", nil)
c.config[cluster] = v
}
c.lock.Unlock()
}
return v
}
func (c *versionConfig) getConfig(ctx context.Context, cluster string, version string) (*config.GokuConfig, error) {
if ok {
ctx, _ := context.WithTimeout(context.Background(), time.Second*10)
v := c.GetV(cluster)
r := v.GetWidthContext(ctx, version)
if r != nil {
return r.([]byte)
}
r, err := v.GetWidthContext(ctx, version)
if err != nil {
return nil, err
}
data, _ := json.Marshal(config.GokuConfig{
Version: version,
Cluster: cluster,
})
return data
return r.(*config.GokuConfig), err
}
//func (c *versionConfig) getVersion() string {
......@@ -65,12 +70,12 @@ func (c *versionConfig) getConfig(cluster string, version string) []byte {
//}
//GetVersionConfig 获取版本配置
func GetVersionConfig(cluster, version string) []byte {
return vc.getConfig(cluster, version)
func GetVersionConfig(ctx context.Context, cluster, version string) (*config.GokuConfig, error) {
return vc.getConfig(ctx, cluster, version)
}
func (c *versionConfig) reset(clusters []*entity.Cluster, gokuConfig *config.GokuConfig, balanceConfig map[string]map[string]*config.BalanceConfig, discoverConfig map[string]map[string]*config.DiscoverConfig) {
newConfig := make(map[string][]byte)
newConfig := make(map[string]*config.GokuConfig)
now := time.Now().Format("20060102150405")
for _, cl := range clusters {
bf := make(map[string]*config.BalanceConfig)
......@@ -81,7 +86,7 @@ func (c *versionConfig) reset(clusters []*entity.Cluster, gokuConfig *config.Gok
if v, ok := discoverConfig[cl.Name]; ok {
df = v
}
configByte, _ := json.Marshal(&config.GokuConfig{
configByte := &config.GokuConfig{
Version: now,
Cluster: cl.Name,
DiscoverConfig: df,
......@@ -93,7 +98,8 @@ func (c *versionConfig) reset(clusters []*entity.Cluster, gokuConfig *config.Gok
AnonymousStrategyID: gokuConfig.AnonymousStrategyID,
Log: gokuConfig.Log,
AccessLog: gokuConfig.AccessLog,
})
MonitorModules: gokuConfig.MonitorModules,
}
newConfig[cl.Name] = configByte
}
c.lock.Lock()
......
......@@ -3,6 +3,8 @@ package versionConfig
import (
"encoding/json"
"github.com/eolinker/goku-api-gateway/ksitigarbha"
console_sqlite3 "github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3"
dao_version_config2 "github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3/dao-version-config"
......@@ -76,6 +78,16 @@ func buildVersionConfig(v string) (string, string, string) {
if err != nil {
return "", "", ""
}
ms := make(map[string]string)
modules, _ := dao_version_config2.GetMonitorModules(1, false)
if modules != nil {
for key, config := range modules {
module ,has:= ksitigarbha.GetMonitorModuleModel(key)
if has{
ms[module.GetName()] = config
}
}
}
c := config.GokuConfig{
Version: v,
......@@ -86,6 +98,7 @@ func buildVersionConfig(v string) (string, string, string) {
AuthPlugin: authNames,
Log: logCf,
AccessLog: accessCf,
MonitorModules: ms,
}
cByte, err := json.Marshal(c)
......
......@@ -3,6 +3,10 @@ package console
import (
"net/http"
"github.com/eolinker/goku-api-gateway/console/controller/monitor"
"github.com/eolinker/goku-api-gateway/console/controller/updater"
config_log "github.com/eolinker/goku-api-gateway/console/controller/config-log"
"github.com/eolinker/goku-api-gateway/console/controller/gateway"
......@@ -148,7 +152,6 @@ func Router() {
http.HandleFunc("/node/delete", node.DeleteNode)
http.HandleFunc("/node/getInfo", node.GetNodeInfo)
http.HandleFunc("/node/getList", node.GetNodeList)
http.HandleFunc("/node/checkIsExistRemoteAddr", node.CheckIsExistRemoteAddr)
http.HandleFunc("/node/batchEditGroup", node.BatchEditNodeGroup)
http.HandleFunc("/node/batchDelete", node.BatchDeleteNode)
......@@ -195,6 +198,14 @@ func Router() {
http.HandleFunc("/version/config/delete", cluster.BatchDeleteVersionConfig)
http.HandleFunc("/version/config/publish", cluster.PublishVersion)
// 表更新
http.HandleFunc("/updater/check/table/isExist", updater.IsTableExist)
http.HandleFunc("/updater/check/column/isExist", updater.IsColumnExist)
// 监控模块
http.HandleFunc("/monitor/module/config/get", monitor.GetMonitorModules)
http.HandleFunc("/monitor/module/config/set", monitor.SetMonitorModule)
// 配置
http.Handle("/config/log/", config_log.Handle("/config/log/"))
http.HandleFunc("/", http.StripPrefix("/", http.FileServer(http.Dir("./static"))).ServeHTTP)
......
......@@ -3,17 +3,19 @@ package console
import (
"net/http"
log "github.com/eolinker/goku-api-gateway/goku-log"
"github.com/eolinker/goku-api-gateway/console/updater"
"github.com/eolinker/goku-api-gateway/common/conf"
"github.com/eolinker/goku-api-gateway/console/admin"
"github.com/eolinker/goku-api-gateway/console/module/account"
log "github.com/eolinker/goku-api-gateway/goku-log"
)
//Server 服务
func Server() {
bind, has := conf.Get("admin_bind")
moduleRegister()
updater.InitUpdater()
ec := make(chan error, 2)
if has {
go func() {
......
......@@ -362,7 +362,7 @@
if (tmp.tdObject.disabledModelKey && vm.list[tmp.itemIndex][tmp.tdObject.disabledModelKey]) {
return;
}
if ((data.radioOriginalIndex || 0).toString() === tmp.itemIndex && tmp.tdObject.isCanBeCancle) {
if ((data.radioOriginalIndex || 0).toString() === tmp.itemIndex && tmp.tdObject.isCanBecancel) {
vm.list[tmp.itemIndex][tmp.tdObject.modelKey] = !vm.list[tmp.itemIndex][tmp.tdObject.modelKey];
data.radioOriginalIndex = 0;
} else {
......@@ -506,7 +506,7 @@
return key;
}
vm.fun.watchFormLastChange = function (inputArg, callback) {
if (!vm.mainObject.setting.munalAddRow && !inputArg.item.cancleAutomaticAddRow) {
if (!vm.mainObject.setting.munalAddRow && !inputArg.item.cancelAutomaticAddRow) {
if (vm.data.isDepth) {
if (!(vm.mainObject.setting.munalHideOperateColumn && inputArg.$index === 0)) {
var tmpIndex = fun.checkIsLastItem(inputArg.$index, vm.list);
......
......@@ -9,7 +9,7 @@
list: '<',
modelKey: '@',
bindFun: '&',
cancleBindFun:'@',
cancelBindFun:'@',
disabled:'<'
}
})
......@@ -20,7 +20,7 @@
var vm = this;
vm.fun = {};
vm.fun.clickMenu = function (inputValue) {
if (!vm.cancleBindFun) {
if (!vm.cancelBindFun) {
vm.bindFun({
arg: inputValue
});
......
......@@ -59,12 +59,16 @@
@import "./directive/sort/group/index.scss";
@import "./modal/branch/gateway/index.scss";
@import "./modal/branch/common/index.scss";
@import "./modal/branch/gateway/index.scss";
@import "./ui/content/monitor/index.scss";
@import "./ui/content/login/index.scss";
@import "./ui/content/panel/index.scss";
@import "./ui/content/publish/index.scss";
@import "./ui/navbar/nav0/index.scss";
......@@ -93,20 +97,16 @@
@import "./ui/content/gpedit/overview/index.scss";
@import "./ui/content/monitor/global/index.scss";
@import "./ui/content/plugin/operate/index.scss";
@import "./ui/content/plugin/_default/index.scss";
@import "./ui/content/project/_default/index.scss";
@import "./ui/content/setting/log/index.scss";
@import "./ui/content/project/api/_default/index.scss";
@import "./ui/content/project/api/operate/index.scss";
@import "./ui/content/project/api/_default/index.scss";
@import "./ui/content/gpedit/inside/content/auth/index.scss";
@import "./ui/content/gpedit/inside/content/api/operate/index.scss";
......
......@@ -102,7 +102,7 @@
<article class="eo-modal-article">
<div>
<div class="dp_ib">
<menu-radio-common-component class="pull-left mr5" list="CONST.SERVICE_TYPE_ARR" output="ajaxResponse.serviceData" model-key="type" cancle-Bind-Fun="true" disabled="input.opr==='Edit'" ></menu-radio-common-component>
<menu-radio-common-component class="pull-left mr5" list="CONST.SERVICE_TYPE_ARR" output="ajaxResponse.serviceData" model-key="type" cancel-Bind-Fun="true" disabled="input.opr==='Edit'" ></menu-radio-common-component>
</div>
<p class="eo_form_first_item_title mt10">服务注册方式名称</p>
<div>
......@@ -242,16 +242,22 @@
<input autocomplete="off" class="eo-input " type="text" name="name" ng-model="output.nodeName" ng-class="{'eo-input-error':(ConfirmForm.name.$dirty&&ConfirmForm.name.$invalid)}"
required>
</p>
<p class="eo_form_item_title">节点IP</p>
<p class="eo_form_item_title f_row_ac">
<span>监听地址</span>
<tip-directive input="用于监听节点程序;监听的端口号用于节点做请求转发"></tip-directive>
</p>
<p class="common-p">
<input autocomplete="off" class="eo-input " type="text" name="ip" ng-model="output.nodeIP" ng-class="{'eo-input-error':(ConfirmForm.ip.$dirty&&ConfirmForm.ip.$invalid)}"
ng-pattern="/^(?:(?:1[0-9][0-9]\.)|(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5])|(?:[1-9][0-9])|(?:[0-9]))$/"
<input autocomplete="off" class="eo-input " type="text" name="ip" ng-model="output.listenAddress" ng-class="{'eo-input-error':(ConfirmForm.ip.$dirty&&ConfirmForm.ip.$invalid)}"
ng-pattern="/^(?:(?:1[0-9][0-9]\.)|(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5])|(?:[1-9][0-9])|(?:[0-9]))\:(([0-9])|([1-9][0-9]{1,3})|([1-6][0-9]{0,4}))$/"
required>
</p>
<p class="eo_form_item_title">节点端口</p>
<p class="eo_form_item_title">
<span>管理地址</span>
<tip-directive input="用于监控组件获取监控数据和对节点做健康检查等"></tip-directive>
</p>
<p class="common-p">
<input autocomplete="off" type="text" name="port" class="eo-input " ng-model="output.nodePort" ng-class="{'eo-input-error':(ConfirmForm.port.$dirty&&ConfirmForm.port.$invalid)}"
ng-pattern="/^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$/" required>
<input autocomplete="off" type="text" name="port" class="eo-input " ng-model="output.adminAddress" ng-class="{'eo-input-error':(ConfirmForm.port.$dirty&&ConfirmForm.port.$invalid)}"
ng-pattern="/^(?:(?:1[0-9][0-9]\.)|(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5])|(?:[1-9][0-9])|(?:[0-9]))\:(([0-9])|([1-9][0-9]{1,3})|([1-6][0-9]{0,4}))$/" required>
</p>
</article>
<footer class="eo-modal-footer">
......
......@@ -184,6 +184,24 @@
cancellable: true
}
});
data.api['MonitorModuleConf'] = $resource(serverUrl + 'monitor/module/config/:operate', {
}, {
Get: {
params: {
operate: 'get'
},
method: "GET",
cancellable: true,
},
Set: {
params: {
operate: 'set'
},
method: data.method,
cancellable: true,
}
});
data.api['Monitor'] = $resource(serverUrl + 'monitor/gateway/:operate', {
}, {
......
......@@ -111,9 +111,9 @@
if (callback) {
template.request = {
groupID: callback.groupID,
nodeIP: callback.nodeIP,
listenAddress: callback.listenAddress,
nodeName: callback.nodeName,
nodePort: callback.nodePort,
adminAddress: callback.adminAddress,
gatewayPath: callback.gatewayPath,
cluster: vm.ajaxRequest.cluster
}
......@@ -193,23 +193,36 @@
primaryKey: 'nodeID',
default: [{
key: '名称',
html: '{{item.nodeName}}'
html: '{{item.nodeName}}',
draggableCacheMark: 'name'
}, {
key: 'IP:Port',
html: '{{item.nodeIP}}:{{item.nodePort}}'
key: 'Node Key',
html: '{{item.nodeKey}}',
draggableCacheMark: 'key'
}, {
key: '监听地址',
html: '{{item.listenAddress}}',
draggableCacheMark: 'listenAddress'
}, {
key: '管理地址',
html: '{{item.adminAddress}}',
draggableCacheMark: 'adminAddress'
}, {
key: '状态',
html: `<span class="eo-status-warning" ng-if="item.nodeStatus=='0'">未运行</span><span class="eo-status-danger" ng-if="item.nodeStatus=='2'">异常</span><span class="eo-status-success" ng-if="item.nodeStatus=='1'">运行中</span>`
html: `<span class="eo-status-warning" ng-if="item.nodeStatus=='0'">未运行</span><span class="eo-status-danger" ng-if="item.nodeStatus=='2'">异常</span><span class="eo-status-success" ng-if="item.nodeStatus=='1'">运行中</span>`,
draggableCacheMark: 'status'
}, {
key: '分组',
html: '{{item.groupName}}'
html: '{{item.groupName}}',
draggableCacheMark: 'group'
}, {
key: '版本',
html: '{{item.version}}'
html: '{{item.version}}',
draggableCacheMark: 'version'
}, {
key: '更新时间',
html: '{{item.updateTime}}',
class: "w_180"
draggableCacheMark: 'time'
}],
operate: {
funArr: [{
......@@ -233,7 +246,19 @@
titleAuthority: 'showTitle',
unhover: true,
warning: '尚未新建任何节点',
fixFoot:true
fixFoot:true,
draggable: true,
dragCacheVar: 'NODE_LIST_DRAG_VAR',
dragCacheObj: {
name: '250px',
key: '250px',
listenAddress: '150px',
adminAddress: '150px',
status: '150px',
group: '150px',
version: '150px',
time:'180px'
}
}
}
......
......@@ -42,7 +42,7 @@
case CODE.COMMON.SUCCESS:
{
window.localStorage.setItem('LOGINCALL', tmpAjaxRequest.loginCall);
$state.go('home.monitor.global');
$state.go('home.panel');
break;
}
}
......
<div class="mlr15 container_monitor mb15">
<ul class="eo-tab-container plr15">
<li class="f_row_ac f_js lh_30" ng-repeat="moduleItem in $ctrl.ajaxResponse.list">
<span>{{moduleItem.moduleDesc}}</span>
<button type="button" class="iconfont fs20"
ng-class="{'icon-huadongkaiguan-dakai':moduleItem.moduleStatus,'icon-huadongkaiguan-guanbi':!moduleItem.moduleStatus}"
ng-click="$ctrl.fun.oprModule('on-off',moduleItem)"></button>
</li>
</ul>
<div class="eo-tab-container mt15" ng-repeat="moduleItem in $ctrl.ajaxResponse.list" ng-if="moduleItem.moduleStatus">
<p class="bbd plr15 lh_50">{{moduleItem.moduleName}}</p>
<form name="ConfirmForm">
<div class="plr15" ng-repeat="layerItem in moduleItem.layer">
<p class="ptb15 f_row_ac">
<span>{{layerItem.label}}</span>
<span class="c999 fs12 ml10">{{layerItem.descript}}</span>
</p>
<div class="f_row f_js_ac" ng-repeat="item in layerItem.items">
<input type="text" class="eo-input w_500" name="{{item.name}}" ng-required="item.required"
ng-model="moduleItem.config[item.name]" ng-pattern="item.pattern"
placeholder="{{item.placeholder}}">
</div>
</div>
<div class="f_row_ac p15" ng-if="moduleItem.config">
<button class="eo_theme_btn_success mr15" ng-click="$ctrl.fun.oprModule('save',moduleItem,ConfirmForm)">保存</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.oprModule('cancel',moduleItem)">取消</button>
</div>
</form>
</div>
<loading-common-component fun="$ctrl.fun.init(arg)"></loading-common-component>
</div>
\ No newline at end of file
(function () {
'use strict';
/**
* @name API监控设置
* @author 广州银云信息科技有限公司
*/
angular.module('eolinker')
.config(['$stateProvider', 'RouteHelpersProvider', function ($stateProvider, helper) {
$stateProvider
.state('home.monitor', {
url: '/monitor',
template: '<monitor></monitor>'
})
}])
.component('monitor', {
templateUrl: 'app/ui/content/monitor/index.html',
controller: indexController
})
indexController.$inject = ['$scope', 'GatewayResource', '$rootScope', 'CODE'];
function indexController($scope, GatewayResource, $rootScope, CODE) {
var vm = this;
vm.ajaxResponse={}
vm.fun={};
let privateFun={};
/**
* @desc 取消/关闭当前监控模块
*/
privateFun.cancelModule=(inputModuleItem)=>{
GatewayResource.MonitorModuleConf.Set({
moduleName:inputModuleItem.moduleName,
moduleStatus:0
}).$promise.then((response)=>{
switch(response.statusCode){
case CODE.COMMON.SUCCESS:{
inputModuleItem.moduleStatus=0;
break;
}
}
})
}
/**
* @desc 保存当前监控模块
*/
privateFun.saveModule=(inputModuleItem,inputOptions={})=>{
GatewayResource.MonitorModuleConf.Set({
moduleName:inputModuleItem.moduleName,
moduleStatus:1,
config:JSON.stringify(inputModuleItem.config)
}).$promise.then((response)=>{
switch(response.statusCode){
case CODE.COMMON.SUCCESS:{
if(inputOptions.isStatic){
inputModuleItem.moduleStatus=1;
}else{
$rootScope.InfoModal("保存成功","success");
}
break;
}
}
})
}
/**
* @desc 临时开启当前监控模块
*/
privateFun.tmpOpenModule=(inputModuleItem)=>{
inputModuleItem.moduleStatus=1;
}
vm.fun.oprModule=(inputOpr,inputItem,inputForm)=>{
switch(inputOpr){
case "save":{
if(inputForm.$invalid)return;
privateFun.saveModule(inputItem);
break;
}
case "cancel":{
privateFun.cancelModule(inputItem);
break;
}
case "on-off":{
if(inputItem.moduleStatus===1){
privateFun.cancelModule(inputItem);
}else if(inputItem.config){
privateFun.tmpOpenModule(inputItem);
}else{
privateFun.saveModule(inputItem,{
isStatic:true
});
}
break;
}
}
}
/**
* @desc ajax后台,获取配置
*/
privateFun.ajaxBackEnd=()=>{
let tmpPromise=GatewayResource.MonitorModuleConf.Get().$promise;
tmpPromise.then((response)=>{
vm.ajaxResponse.list=response.moduleList||[];
})
return tmpPromise;
}
vm.fun.init=privateFun.ajaxBackEnd;
}
})();
\ No newline at end of file
monitor {
.icon-huadongkaiguan-dakai {
color: #00ab6d;
}
.container_monitor {
margin-top: $HOME-NAVBAR-HEIGHT + 15px;
}
.eo-input.ng-invalid.ng-touched,.eo-input.ng-invalid.ng-dirty {
border-color: $buttonDangerBgColor;
color: $buttonDangerBgColor;
&:focus {
border-color: $buttonDangerBgColor;
color: #333;
}
}
}
\ No newline at end of file
<!-- author:广州银云信息科技有限公司 -->
<overview-product-component main-object="$ctrl.component.overviewObject"></overview-product-component>
<div class="plr30 pt25 mb65 panel-ui-div">
<div class="f_row mb25">
<div class="eo-tab-container item_panel mr25">
<div class="eo-tab-menu"><span class="item-tab">网关基本信息</span></div>
<div class="f_row_ac f_jc h_70 p20 tac">
<div class="mr80">
<p class="fs20">V{{$ctrl.ajaxResponse.monitorInfo.baseInfo.version}}</p>
<p class="c999 mt20">版本</p>
</div>
<div class="mr80">
<p class="fs20">{{$ctrl.ajaxResponse.monitorInfo.baseInfo.clusterCount||0}}</p>
<p class="c999 mt20">集群</p>
</div>
<div class="mr80">
<p class="fs20">
{{$ctrl.ajaxResponse.monitorInfo.baseInfo.nodeCount}}
</p>
<p class="c999 mt20">节点</p>
</div>
</div>
</div>
<div class="eo-tab-container item_panel">
<div class="eo-tab-menu"><span class="item-tab">API信息</span></div>
<div class="f_row_ac f_jc h_70 p20 tac">
<div class="mr80">
<p class="fs20">{{$ctrl.ajaxResponse.monitorInfo.baseInfo.projectCount}}</p>
<p class="c999 mt20">项目</p>
</div>
<div class="mr80">
<p class="fs20">{{$ctrl.ajaxResponse.monitorInfo.baseInfo.apiCount}}</p>
<p class="c999 mt20">API</p>
</div>
<div>
<p class="fs20">{{$ctrl.ajaxResponse.monitorInfo.baseInfo.strategyCount}}</p>
<p class="c999 mt20">访问策略</p>
</div>
</div>
</div>
</div>
<p class="mb25 bbd divide_panel"></p>
<div class="f_row mb25">
<div class="eo-tab-container item_panel">
<div class="eo-tab-menu"><span class="item-tab">帮助</span></div>
<div class="f_row_ac h_70 p20">
<div class="iconfont icon-bangzhu_o pr20"></div>
<div class="lh_30">
<p>访问官方网站获取最新产品资讯以及使用文档</p>
<p>
<span>[官方网站]</span>
<a class="eo_link" target="_blank" href="https://www.eolinker.com">https://www.eolinker.com</a>
</p>
<p>
<span>[帮助中心]</span>
<a class="eo_link" target="_blank"
href="https://help.eolinker.com">https://help.eolinker.com</a>
</p>
</div>
</div>
</div>
</div>
</div>
<div class="foot_panel">
<a class="eo_link pull-right ml15" href="https://www.eolinker.com" target="_blank">WWW.EOLINKER.COM</a>
<span class="pull-right">GOKU API GATEWAY V{{$ctrl.ajaxResponse.monitorInfo.baseInfo.version}}</span>
</div>
<loading-common-component fun="$ctrl.fun.init(arg)"></loading-common-component>
\ No newline at end of file
(function () {
//author:广州银云信息科技有限公司
'use strict';
angular.module('eolinker')
.config(['$stateProvider', 'RouteHelpersProvider', function ($stateProvider, helper) {
$stateProvider
.state('home.panel', {
url: '/',
template: '<panel></panel>'
})
}])
.component('panel', {
templateUrl: 'app/ui/content/panel/index.html',
controller: indexController
})
indexController.$inject = ['$scope', 'GatewayResource', '$state', 'CODE', '$rootScope', 'uibDateParser'];
function indexController($scope, GatewayResource, $state, CODE, $rootScope, uibDateParser) {
var vm = this,
privateFun = {}
vm.data = {}
vm.fun = {};
vm.ajaxRequest = {
table: {
beginTime: null,
endTime: null,
period: 0
}
}
vm.ajaxResponse = {
monitorInfo: null,
redisArr: []
};
vm.directive = {
tableTimeObject: {
show: false,
maxDate: new Date(),
maxMode: 'month',
request: {}
}
}
vm.component = {
overviewObject: {},
listDefaultCommonObject: null
}
privateFun.initTable = function () {
let tmpPromise;
tmpPromise = GatewayResource.Monitor.Info().$promise;
tmpPromise.then(function (response) {
vm.ajaxResponse.monitorInfo = response || {};
})
return tmpPromise;
}
privateFun.refresh = function () {
var tmpPromise = GatewayResource.Monitor.Refresh().$promise;
tmpPromise.then(function (response) {
switch (response.statusCode) {
case CODE.COMMON.SUCCESS: {
$rootScope.InfoModal('立即刷新成功!', 'success', function () {
$scope.$emit('$TransferStation', {
state: '$Init_LoadingCommonComponent'
});
});
break;
}
}
})
return tmpPromise;
}
privateFun.initComponent = function () {
vm.component.overviewObject = {
setting: {
title: '基本信息',
showOperate: true
}
}
}
vm.fun.init = function (arg) {
arg = arg || {
type: 'default'
}
switch (arg.type) {
default: {
return privateFun.initTable();;
}
case 'refresh': {
return privateFun.refresh();
}
}
}
vm.fun.refresh = function () {
$scope.$emit('$TransferStation', {
state: '$Init_LoadingCommonComponent',
data: {
type: 'refresh',
tips: '刷新'
}
});
}
vm.$onInit = function () {
$scope.$emit('$WindowTitleSet', {
list: ['首页']
});
privateFun.initComponent();
}
}
})();
\ No newline at end of file
panel {
.foot_panel{
@include eo-line(40px);
background-color: #f8f8f8;
border-top: 1px solid #ddd;
position: fixed;
bottom: 0;
@include eo-width(100%,20px);
left: 0;
padding-right: 20px;
}
.first-box {
padding-bottom: 0;
padding-top: 30px;
}
.basic_data_panel{
width: 100%;
}
.item_panel{
width: 50%;
overflow: hidden;
}
list-default-common-component .first-level-article {
margin-top: 0;
}
.icon-bangzhu_o {
font-size: 50px;
}
.time_panel {
.item_time_panel {
background-color: #fff;
@include eo-line(30px);
display: inline-block;
text-align: center;
padding: 0 10px;
border: 1px solid #ddd;
&:nth-child(n+2) {
border-left: none;
}
&:first-child {
border-radius: 3px 0 0 3px;
}
}
.item_time_active_panel {
background-color: $buttonSuccessBgColor;
color: #fff;
}
.disable-a {
color: #ccc;
cursor: not-allowed;
}
.item_time_panel:last-child {
margin-left: 0;
border-left: none;
border-radius: 0 3px 3px 0;
}
}
.panel-ui-div {
.table-container-div {
max-width: 1236px;
}
.eo-tab-menu .item-tab {
border-color: transparent;
cursor: default;
}
.item-p {
font-size: 14px;
color: #555;
margin: 20px 0;
}
.untop-div {
border-top: 0;
}
.default-btn {
display: inline-block;
@include eo-line(30px);
border: 1px solid #ddd;
border-radius: 3px;
background-color: #fff;
padding: 0 10px;
&:hover {
background-color: #fafafa;
}
}
}
}
\ No newline at end of file
......@@ -92,11 +92,11 @@
}
privateFun.publish = function (inputArg) {
let tmpModal = {
title: "发布配置"
title: "配置管理"
},tmpAjaxRequest={
versionID:inputArg.item.versionID
}
$rootScope.EnsureModal(tmpModal.title, null, '配置发布立即生效,确定对各节点发布配置', {
$rootScope.EnsureModal(tmpModal.title, null, '配置发布立即生效,确定对各节点配置管理', {
btnType:2,
btnMessage:"确定"
}, function (callback) {
......
......@@ -11,7 +11,7 @@
</p>
<p class="mb10 mt20 f_row_ac">
<span>记录状态</span>
<button class="iconfont open-alert-btn"
<button type="button" class="iconfont open-alert-btn"
ng-class="{'icon-huadongkaiguan-dakai':$ctrl.ajaxResponse.accessLog.enable,'icon-huadongkaiguan-guanbi':!$ctrl.ajaxResponse.accessLog.enable}"
ng-click="$ctrl.ajaxResponse.accessLog.enable=!$ctrl.ajaxResponse.accessLog.enable"></button>
</p>
......@@ -36,7 +36,7 @@
</div>
<div class="btn-group-li" ng-show="$ctrl.data.accessIsEdit" ng-if="$ctrl.service.authority.permission.default.gatewayConfig.edit">
<button class="eo_theme_btn_success pull-left" button-Set-Disable-Directive="$ctrl.fun.saveForm('access')">保存</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.cancleEdit('access')">取消</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.cancelEdit('access')">取消</button>
</div>
</form>
......@@ -55,7 +55,7 @@
</p>
<p class="mb10 mt20 f_row_ac">
<span>记录状态</span>
<button class="iconfont open-alert-btn"
<button type="button" class="iconfont open-alert-btn"
ng-class="{'icon-huadongkaiguan-dakai':$ctrl.ajaxResponse.nodeLog.enable,'icon-huadongkaiguan-guanbi':!$ctrl.ajaxResponse.nodeLog.enable}"
ng-click="$ctrl.ajaxResponse.nodeLog.enable=!$ctrl.ajaxResponse.nodeLog.enable"></button>
</p>
......@@ -82,7 +82,7 @@
<div class="btn-group-li" ng-show="$ctrl.data.nodeIsEdit"
ng-if="$ctrl.service.authority.permission.default.gatewayConfig.edit">
<button class="eo_theme_btn_success pull-left" button-Set-Disable-Directive="$ctrl.fun.saveForm('node')">保存</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.cancleEdit('node')">取消</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.cancelEdit('node')">取消</button>
</div>
</form>
......@@ -100,7 +100,7 @@
</p>
<p class="mb10 mt20 f_row_ac">
<span>记录状态</span>
<button class="iconfont open-alert-btn"
<button type="button" class="iconfont open-alert-btn"
ng-class="{'icon-huadongkaiguan-dakai':$ctrl.ajaxResponse.consoleLog.enable,'icon-huadongkaiguan-guanbi':!$ctrl.ajaxResponse.consoleLog.enable}"
ng-click="$ctrl.ajaxResponse.consoleLog.enable=!$ctrl.ajaxResponse.consoleLog.enable"></button>
</p>
......@@ -126,7 +126,7 @@
</div>
<div class="btn-group-li" ng-show="$ctrl.data.consoleIsEdit" ng-if="$ctrl.service.authority.permission.default.gatewayConfig.edit">
<button class="eo_theme_btn_success pull-left" button-Set-Disable-Directive="$ctrl.fun.saveForm('console')">保存</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.cancleEdit('console')">取消</button>
<button class="eo_theme_btn_default" type="button" ng-click="$ctrl.fun.cancelEdit('console')">取消</button>
</div>
</form>
......
......@@ -99,7 +99,7 @@
})
return tmpPromise;
}
vm.fun.cancleEdit = function (inputWhich) {
vm.fun.cancelEdit = function (inputWhich) {
vm.data.submitted = false;
vm.data[inputWhich+'IsEdit']=false;
vm.ajaxResponse[inputWhich+'Log'] = angular.copy(cache[inputWhich+'Log']);
......
......@@ -25,8 +25,7 @@
current: null,
menu: [{
name: '首页',
sref: 'home.monitor',
childSref: 'home.monitor.global',
sref: 'home.panel',
icon: 'icon-shouye_o',
power: -1,
status:'un-spreed'
......@@ -69,6 +68,11 @@
icon: 'icon-yuechi_o',
power: -1,
childSref: 'home.gpedit.default'
}, {
name: 'API监控设置',
sref: 'home.monitor',
icon: 'icon-yuechi_o',
power: -1
},
{
name: '扩展插件',
......@@ -91,7 +95,7 @@
]
}
, {
name: '发布配置',
name: '配置管理',
sref: 'home.publish',
icon: 'icon-liangliangduibi_o',
power: -1,
......
package goku311
import SQL "database/sql"
const gokuMonitorModuleSQL = `DROP TABLE IF EXISTS goku_monitor_module;
CREATE TABLE "goku_monitor_module" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT NOT NULL,
"config" TEXT NOT NULL,
"moduleStatus" integer NOT NULL DEFAULT 0
);
CREATE UNIQUE INDEX "moduleName"
ON "goku_monitor_module" (
"name" ASC
);`
func createGokuMonitorModule(db *SQL.DB) error {
_, err := db.Exec(gokuMonitorModuleSQL)
if err != nil {
return err
}
return nil
}
package goku311
import (
SQL "database/sql"
)
const gokuNodeInfoSQL = `DROP TABLE IF EXISTS goku_node_info_new;
CREATE TABLE "goku_node_info_new" (
"nodeID" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"createTime" text,
"updateTime" text,
"groupID" integer(11) NOT NULL DEFAULT 0,
"nodeName" text(255) NOT NULL,
"nodeStatus" integer(11) NOT NULL,
"version" text(255),
"sshAddress" text(255) DEFAULT 22,
"sshUserName" text(255),
"sshPassword" text(255),
"gatewayPath" text(255),
"sshKey" text,
"authMethod" integer(4) NOT NULL DEFAULT 0,
"clusterID" integer(11) NOT NULL DEFAULT 0,
"listenAddress" text(22) NOT NULL DEFAULT '',
"adminAddress" text(22) NOT NULL DEFAULT '',
"nodeKey" TEXT(32) NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX "nodeKey_new"
ON "goku_node_info_new" (
"nodeKey" ASC
);`
func createGokuNodeInfo(db *SQL.DB) error {
_, err := db.Exec(gokuNodeInfoSQL)
if err != nil {
return err
}
sql := "INSERT INTO goku_node_info_new (`nodeID`,`createTime`,`updateTime`,`groupID`,`nodeName`,`nodeStatus`,`version`,`sshAddress`,`sshUserName`,`sshPassword`,`gatewayPath`,`sshKey`,`authMethod`,`clusterID`,`listenAddress`,`adminAddress`,`nodeKey`) SELECT `nodeID`,`createTime`,`updateTime`,`groupID`,`nodeName`,`nodeStatus`,`version`,`sshPort`,`userName`,`password`,`gatewayPath`,`key`,`authMethod`,`clusterID`,`nodeIP` || ':' || `nodePort`,`nodeIP` || ':' || `nodePort`,`nodeID` || `nodeIP` || ':' || `nodePort` FROM goku_node_info;"
_, err = db.Exec(sql)
if err != nil {
return err
}
_, err = db.Exec("DROP TABLE IF EXISTS goku_node_info")
if err != nil {
return err
}
_, err = db.Exec("ALTER TABLE goku_node_info_new RENAME TO goku_node_info")
if err != nil {
return err
}
_, err = db.Exec("DROP TABLE IF EXISTS goku_node_info_new")
if err != nil {
return err
}
return nil
}
package goku311
import SQL "database/sql"
const gokuTableVersionSQL = `CREATE TABLE "goku_table_version" (
"tableID" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"tableName" TEXT NOT NULL,
"version" TEXT NOT NULL
);
CREATE UNIQUE INDEX "tableName"
ON "goku_table_version" (
"tableName"
);`
func createGokuTableVersion(db *SQL.DB) error {
_, err := db.Exec(gokuTableVersionSQL)
if err != nil {
return err
}
return nil
}
package goku311
import (
"github.com/eolinker/goku-api-gateway/common/database"
"github.com/eolinker/goku-api-gateway/console/updater"
updaterDao "github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3/updater"
)
//Version 版本号
const Version = "3.1.1"
type factory struct {
version string
}
var updaterFactory = &factory{version: Version}
func init() {
updater.Add(Version, updaterFactory)
}
func (f *factory) GetVersion() string {
return f.version
}
func (f *factory) UpdateVersion() {
return
}
func (f *factory) Exec() error {
err := Exec()
if err != nil {
return err
}
return nil
}
//Exec 执行goku_node_info
func Exec() error {
db := database.GetConnection()
existed := updaterDao.IsTableExist("goku_table_version")
if !existed {
err := createGokuTableVersion(db)
if err != nil {
return err
}
}
if version := updaterDao.GetTableVersion("goku_node_info"); version != Version {
err := createGokuNodeInfo(db)
if err != nil {
return err
}
}
if version := updaterDao.GetTableVersion("goku_monitor_module"); version != Version {
err := createGokuMonitorModule(db)
if err != nil {
return err
}
}
updaterDao.UpdateTableVersion("goku_node_info", Version)
updaterDao.UpdateTableVersion("goku_monitor_module", Version)
return nil
}
package manager
import (
// 311版本更新器
_ "github.com/eolinker/goku-api-gateway/console/updater/goku311"
)
package updater
import (
"github.com/eolinker/goku-api-gateway/server/dao/console-sqlite3/updater"
)
//Updater 更新器
type Updater interface {
GetVersion() string
UpdateVersion()
Exec() error
}
//Factory factory
type Factory interface {
Add(version string, updater Updater)
}
//Manager manager
type Manager struct {
updaters []manager
}
type manager struct {
version string
updater Updater
}
var updateManager = &Manager{updaters: make([]manager, 0, 10)}
//Add add
func Add(version string, updater Updater) {
updateManager.Add(version, updater)
}
//InitUpdater 执行版本更新操作
func InitUpdater() error {
for _, u := range updateManager.updaters {
err := u.updater.Exec()
if err != nil {
updater.SetGokuVersion(u.version)
return err
}
}
return nil
}
//Add add
func (u *Manager) Add(version string, updater Updater) {
u.updaters = append(u.updaters, manager{version: version, updater: updater})
}
package diting
import "sync"
//CacheFactory cacheFactory
type CacheFactory struct {
factory Factory
cache map[uint64]interface{}
locker sync.RWMutex
}
//NewCacheFactory new cacheFactory
func NewCacheFactory(factory Factory) *CacheFactory {
return &CacheFactory{
factory: factory,
cache: make(map[uint64]interface{}),
locker: sync.RWMutex{},
}
}
//NewCounter new counter
func (f *CacheFactory) NewCounter(opt *CounterOpts) (Counter, error) {
v, has := f.get(opt.ID)
if has {
return v.(Counter), nil
}
c, err := f.factory.NewCounter(opt)
if err != nil {
return nil, err
}
v = f.set(opt.ID, c)
return v.(Counter), nil
}
//NewHistogram new histogram
func (f *CacheFactory) NewHistogram(opt *HistogramOpts) (Histogram, error) {
v, has := f.get(opt.ID)
if has {
return v.(Histogram), nil
}
c, err := f.factory.NewHistogram(opt)
if err != nil {
return nil, err
}
v = f.set(opt.ID, c)
return v.(Histogram), nil
}
//NewGauge new gauge
func (f *CacheFactory) NewGauge(opt *GaugeOpts) (Gauge, error) {
v, has := f.get(opt.ID)
if has {
return v.(Gauge), nil
}
c, err := f.factory.NewGauge(opt)
if err != nil {
return nil, err
}
v = f.set(opt.ID, c)
return v.(Gauge), nil
}
func (f *CacheFactory) get(id uint64) (interface{}, bool) {
f.locker.RLock()
v, has := f.cache[id]
f.locker.RUnlock()
return v, has
}
func (f *CacheFactory) set(id uint64, v interface{}) interface{} {
f.locker.Lock()
vo, has := f.cache[id]
if has {
f.locker.Unlock()
return vo
}
f.cache[id] = v
f.locker.Unlock()
return v
}
package diting
//ConfigHandleFunc configHandleFunc
type ConfigHandleFunc func() interface{}
//Constructor constructor
type Constructor interface {
Namespace() string
Create(conf string) (Factory,error)
Close()
}
//Factory factory
type Factory interface {
NewCounter(opt *CounterOpts) (Counter,error)
//NewSummary(opt *SummaryOpts) (Summary,error)
NewHistogram(opt *HistogramOpts) (Histogram,error)
NewGauge(opt *GaugeOpts) (Gauge,error)
}
//Factories factories
type Factories []Factory
//NewCounter new counter
func (fs Factories) NewCounter(opt *CounterOpts) (Counters, error) {
cs := make(Counters, 0, len(fs))
for _, f := range fs {
s, err := f.NewCounter(opt)
if err != nil {
continue
}
cs = append(cs, s)
}
return cs, nil
}
//NewHistogram new histogram
func (fs Factories) NewHistogram(opt *HistogramOpts) (Histograms, error) {
hs := make(Histograms, 0, len(fs))
for _, f := range fs {
h, err := f.NewHistogram(opt)
if err != nil {
continue
}
hs = append(hs, h)
}
return hs, nil
}
//NewGauge new gauge
func (fs Factories) NewGauge(opt *GaugeOpts) (Gauges, error) {
gs := make(Gauges, 0, len(fs))
for _, f := range fs {
g, err := f.NewGauge(opt)
if err != nil {
continue
}
gs = append(gs, g)
}
return gs, nil
}
package diting
import "github.com/eolinker/goku-api-gateway/diting/internal"
var (
idCreate = internal.NewIDCreate()
)
//GetID getID
func GetID() uint64 {
return idCreate.Next()
}
package internal
import (
"sync"
"time"
)
//IDCreate idCreate
type IDCreate struct {
locker sync.Mutex
lastID uint64
}
//NewIDCreate new idCreate
func NewIDCreate() *IDCreate {
return &IDCreate{locker: sync.Mutex{}}
}
//Next next
func (c *IDCreate) Next() uint64 {
var id uint64 = 0
c.locker.Lock()
id = uint64(time.Now().UnixNano())
if id <= c.lastID {
id = c.lastID + 1
}
c.lastID = id
c.locker.Unlock()
return id
}
package diting
import (
"fmt"
"github.com/eolinker/goku-api-gateway/ksitigarbha"
)
var (
constructorMap = make(map[string]Constructor)
refresher = NewRefreshers()
)
//Register register
func Register(namespace string, constructor Constructor) {
_, has := constructorMap[namespace]
if has {
panic(fmt.Sprint("duplicate namespace of constructor by", namespace))
}
constructorMap[namespace] = constructor
}
func get(namespace string) (Constructor, bool) {
constructor, has := constructorMap[namespace]
return constructor, has
}
func construct(confs map[string]string) Factories {
factories := make(Factories,0,len(confs))
defer func() {
// close 关闭不用的模块
lives := confs
for name,constructor:= range constructorMap{
if _,has:=lives[name];!has{
constructor.Close()
}
}
}()
if confs == nil{
return factories
}
for name, conf := range confs {
namespace:=ksitigarbha.GetNameSpaceByName(name)
constructor, has := get(namespace)
if !has {
continue
}
factory, err := constructor.Create(conf)
if err != nil {
continue
}
factories = append(factories, factory)
}
return factories
}
//Refresh refresh
func Refresh(confs map[string]string) {
factories := construct(confs)
refresher.Refresh(factories)
}
package diting
//Labels labels
type Labels map[string]string
//Counter counter
type Counter interface {
Add(value float64, labels Labels)
}
//Gauge gauge
type Gauge interface {
Set(value float64, labels Labels)
}
//Observer observer
type Observer interface {
Observe(value float64, labels Labels)
}
//Histogram histogram
type Histogram interface {
Observer
}
//Summary summary
type Summary interface {
Observer
}
//Counters counters
type Counters []Counter
//Add add
func (cs Counters) Add(value float64, labels Labels) {
for _, c := range cs {
c.Add(value, labels)
}
}
//Gauges gauges
type Gauges []Gauge
//Set set
func (gs Gauges) Set(value float64, labels Labels) {
for _, g := range gs {
g.Set(value, labels)
}
}
//Histograms histograms
type Histograms []Observer
//Observe observe
func (hs Histograms) Observe(value float64, labels Labels) {
for _, h := range hs {
h.Observe(value, labels)
}
}
//Summaries summaries
type Summaries []Summary
//Observe observe
func (ss Summaries) Observe(value float64, labels Labels) {
for _, s := range ss {
s.Observe(value, labels)
}
}
package diting
import (
"time"
)
//SeparatorByte separatorByte
const SeparatorByte byte = 255
var separatorByteSlice = []byte{SeparatorByte}
//Opts opts
type Opts struct {
ID uint64
LabelNames []string
// Namespace, Subsystem, and Name are components of the fully-qualified
// name of the Metric (created by joining these components with
// "_"). Only Name is mandatory, the others merely help structuring the
// name. Note that the fully-qualified name of the metric must be a
// valid Prometheus metric name.
Namespace string
Subsystem string
Name string
// Help provides information about this metric.
//
// Metrics with the same fully-qualified name must have the same Help
// string.
Help string
// ConstLabels are used to attach fixed labels to this metric. Metrics
// with the same fully-qualified name must have the same label names in
// their ConstLabels.
//
// ConstLabels are only used rarely. In particular, do not use them to
// attach the same labels to all your metrics. Those use cases are
// better covered by target labels set by the scraping Prometheus
// server, or by one specific metric (e.g. a build_info or a
// machine_role metric). See also
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
ConstLabels Labels
}
//CounterOpts couterOpts
type CounterOpts Opts
//GaugeOpts gaugeOpts
type GaugeOpts Opts
//HistogramOpts histogramOpts
type HistogramOpts struct {
ID uint64
LabelNames []string
// Namespace, Subsystem, and Name are components of the fully-qualified
// name of the Histogram (created by joining these components with
// "_"). Only Name is mandatory, the others merely help structuring the
// name. Note that the fully-qualified name of the Histogram must be a
// valid Prometheus metric name.
Namespace string
Subsystem string
Name string
// Help provides information about this Histogram.
//
// Metrics with the same fully-qualified name must have the same Help
// string.
Help string
// ConstLabels are used to attach fixed labels to this metric. Metrics
// with the same fully-qualified name must have the same label names in
// their ConstLabels.
//
// ConstLabels are only used rarely. In particular, do not use them to
// attach the same labels to all your metrics. Those use cases are
// better covered by target labels set by the scraping Prometheus
// server, or by one specific metric (e.g. a build_info or a
// machine_role metric). See also
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
ConstLabels Labels
// buckets defines the buckets into which observations are counted. Each
// element in the slice is the upper inclusive bound of a bucket. The
// values must be sorted in strictly increasing order. There is no need
// to add a highest bucket with +Inf bound, it will be added
// implicitly. The default value is DefBuckets.
Buckets []float64
}
//SummaryOpts summaryOpts
type SummaryOpts struct {
ID uint64
LabelNames []string
// Namespace, Subsystem, and Name are components of the fully-qualified
// name of the Summary (created by joining these components with
// "_"). Only Name is mandatory, the others merely help structuring the
// name. Note that the fully-qualified name of the Summary must be a
// valid Prometheus metric name.
Namespace string
Subsystem string
Name string
// Help provides information about this Summary.
//
// Metrics with the same fully-qualified name must have the same Help
// string.
Help string
// ConstLabels are used to attach fixed labels to this metric. Metrics
// with the same fully-qualified name must have the same label names in
// their ConstLabels.
//
// Due to the way a Summary is represented in the Prometheus text format
// and how it is handled by the Prometheus server internally, “quantile”
// is an illegal label name. Construction of a Summary or SummaryVec
// will panic if this label name is used in ConstLabels.
//
// ConstLabels are only used rarely. In particular, do not use them to
// attach the same labels to all your metrics. Those use cases are
// better covered by target labels set by the scraping Prometheus
// server, or by one specific metric (e.g. a build_info or a
// machine_role metric). See also
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
ConstLabels Labels
// Objectives defines the quantile rank estimates with their respective
// absolute error. If Objectives[q] = e, then the value reported for q
// will be the φ-quantile value for some φ between q-e and q+e. The
// default value is an empty map, resulting in a summary without
// quantiles.
Objectives map[float64]float64
// MaxAge defines the duration for which an observation stays relevant
// for the summary. Must be positive. The default value is DefMaxAge.
MaxAge time.Duration
// AgeBuckets is the number of buckets used to exclude observations that
// are older than MaxAge from the summary. A higher number has a
// resource penalty, so only increase it if the higher resolution is
// really required. For very high observation rates, you might want to
// reduce the number of age buckets. With only one age bucket, you will
// effectively see a complete reset of the summary each time MaxAge has
// passed. The default value is DefAgeBuckets.
AgeBuckets uint32
// BufCap defines the default sample stream buffer size. The default
// value of DefBufCap should suffice for most uses. If there is a need
// to increase the value, a multiple of 500 is recommended (because that
// is the internal buffer size of the underlying package
// "github.com/bmizerany/perks/quantile").
BufCap uint32
}
//NewCounterOpts new counterOpts
func NewCounterOpts(namespace string, subsystem string, name string, help string, constLabels Labels, labelNames []string) *CounterOpts {
return &CounterOpts{
ID: GetID(),
LabelNames: labelNames,
Namespace: namespace,
Subsystem: subsystem,
Name: name,
Help: help,
ConstLabels: constLabels,
}
}
//NewGaugeOpts new gaugeOpts
func NewGaugeOpts(namespace string, subsystem string, name string, help string, constLabels Labels, labelNames []string) *GaugeOpts {
return &GaugeOpts{
ID: GetID(),
LabelNames: labelNames,
Namespace: namespace,
Subsystem: subsystem,
Name: name,
Help: help,
ConstLabels: constLabels,
}
}
//NewHistogramOpts new histogramOpts
func NewHistogramOpts(namespace string, subsystem string, name string, help string, constLabels Labels, labelNames []string, buckets []float64) *HistogramOpts {
return &HistogramOpts{
ID: GetID(),
LabelNames: labelNames,
Namespace: namespace,
Subsystem: subsystem,
Name: name,
Help: help,
ConstLabels: constLabels,
Buckets: buckets,
}
}
//NewSummaryOpts new summaryOpts
func NewSummaryOpts(namespace string, subsystem string, name string, help string, constLabels Labels, labelNames []string, objectives map[float64]float64, maxAge time.Duration, ageBuckets uint32, bufCap uint32) *SummaryOpts {
return &SummaryOpts{
ID: GetID(),
LabelNames: labelNames,
Namespace: namespace,
Subsystem: subsystem,
Name: name,
Help: help,
ConstLabels: constLabels,
Objectives: objectives,
MaxAge: maxAge,
AgeBuckets: ageBuckets,
BufCap: bufCap,
}
}
package diting
import "sync"
//CounterProxy counterProxy
type CounterProxy struct {
ConstLabelsProxy
opt *CounterOpts
counters Counters
locker sync.RWMutex
}
func newCounterProxy(opt *CounterOpts) *CounterProxy {
return &CounterProxy{
ConstLabelsProxy: ConstLabelsProxy(opt.ConstLabels),
opt: opt,
counters: nil,
locker: sync.RWMutex{},
}
}
//Refresh refresh
func (c *CounterProxy) Refresh(factories Factories) {
counters, _ := factories.NewCounter(c.opt)
c.locker.Lock()
c.counters = counters
c.locker.Unlock()
}
//Add add
func (c *CounterProxy) Add(value float64, labels Labels) {
c.compile(labels)
c.locker.RLock()
counters := c.counters
c.locker.RUnlock()
counters.Add(value, labels)
}
package diting
import "sync"
//GaugeProxy gaugeProxy
type GaugeProxy struct {
ConstLabelsProxy
opt *GaugeOpts
gauges Gauges
locker sync.RWMutex
}
func newGaugeProxy(opt *GaugeOpts) *GaugeProxy {
return &GaugeProxy{
ConstLabelsProxy: ConstLabelsProxy(opt.ConstLabels),
opt: opt,
gauges: nil,
locker: sync.RWMutex{},
}
}
//Refresh refresh
func (g *GaugeProxy) Refresh(factories Factories) {
gauges, _ := factories.NewGauge(g.opt)
g.locker.Lock()
g.gauges = gauges
g.locker.Unlock()
}
//Set set
func (g *GaugeProxy) Set(value float64, labels Labels) {
g.compile(labels)
g.locker.RLock()
gauges := g.gauges
g.locker.RUnlock()
gauges.Set(value, labels)
}
package diting
import "sync"
//HistogramProxy histogramProxy
type HistogramProxy struct {
ConstLabelsProxy
opt *HistogramOpts
locker sync.RWMutex
histograms Histograms
}
func newHistogramProxy(opt *HistogramOpts) *HistogramProxy {
return &HistogramProxy{
ConstLabelsProxy: ConstLabelsProxy(opt.ConstLabels),
opt: opt,
locker: sync.RWMutex{},
histograms: nil,
}
}
//Refresh refresh
func (h *HistogramProxy) Refresh(factories Factories) {
histograms, _ := factories.NewHistogram(h.opt)
h.locker.Lock()
h.histograms = histograms
h.locker.Unlock()
}
//Observe observe
func (h *HistogramProxy) Observe(value float64, labels Labels) {
h.compile(labels)
h.locker.RLock()
histograms := h.histograms
h.locker.RUnlock()
histograms.Observe(value, labels)
}
package diting
//
//type SummariesProxy struct {
// ConstLabelsProxy
// opt *SummaryOpts
// locker sync.RWMutex
// summaries Summaries
//}
//
//func newSummariesProxy(opt *SummaryOpts) *SummariesProxy {
// return &SummariesProxy{
// ConstLabelsProxy:ConstLabelsProxy(opt.ConstLabels),
// opt: opt,
// locker: sync.RWMutex{},
// summaries: nil,
// }
//}
//
//func (s *SummariesProxy) Refresh(factories Factories) {
//
// summaries, _ := factories.NewSummary(s.opt)
// s.locker.Lock()
// s.summaries = summaries
// s.locker.Unlock()
//}
//
//func (s *SummariesProxy) Observe(value float64, labels Labels) {
// s.compile(labels)
// s.locker.RLock()
// summaries :=s.summaries
// s.locker.RUnlock()
// summaries.Observe(value,labels)
//}
//
\ No newline at end of file
package diting
//Proxy proxy
type Proxy interface {
Refresh(factories Factories)
}
//ConstLabelsProxy 为了能定义 constLabel 内字段的顺序,将constLabel 改为普通Label
type ConstLabelsProxy Labels
func (p ConstLabelsProxy) compile(labels Labels) {
if p == nil || labels == nil {
return
}
for k, v := range p {
if _, has := labels[k]; !has {
labels[k] = v
}
}
}
package diting
import "sync"
//Refreshers refreshers
type Refreshers struct {
proxies []Proxy
locker sync.RWMutex
}
//NewRefreshers new refreshers
func NewRefreshers() *Refreshers {
return &Refreshers{
proxies: nil,
locker: sync.RWMutex{},
}
}
//Add add
func (r *Refreshers) Add(proxy Proxy) {
r.locker.Lock()
r.proxies = append(r.proxies, proxy)
r.locker.Unlock()
}
//Refresh refresh
func (r *Refreshers) Refresh(factories Factories) {
r.locker.RLock()
proxies := r.proxies
r.locker.RUnlock()
for _, p := range proxies {
p.Refresh(factories)
}
}
package diting
//NewCounter new counter
func NewCounter(opt *CounterOpts) Counter {
c := newCounterProxy(opt)
refresher.Add(c)
return c
}
//NewGauge new gauge
func NewGauge(opt *GaugeOpts) Gauge {
g := newGaugeProxy(opt)
refresher.Add(g)
return g
}
//NewHistogram new Histogram
func NewHistogram(opt *HistogramOpts) Histogram {
h := newHistogramProxy(opt)
refresher.Add(h)
return h
}
//func NewSummary(opt *SummaryOpts) Summary {
// s:= newSummariesProxy(opt)
// refresher.Add(s)
// return s
//}
......@@ -3,17 +3,38 @@ module github.com/eolinker/goku-api-gateway
go 1.12
require (
github.com/360EntSecGroup-Skylar/excelize v1.4.1 // indirect
github.com/cosiner/argv v0.0.1 // indirect
github.com/devopsfaith/krakend v0.0.0-20190930092458-9e6fc3784eca // indirect
github.com/eolinker/goku-plugin v0.1.3
github.com/go-delve/delve v1.3.2 // indirect
github.com/go-redis/redis v6.15.5+incompatible
github.com/go-sql-driver/mysql v1.4.1
github.com/hashicorp/consul/api v1.1.0
github.com/json-iterator/go v1.1.7
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/sirupsen/logrus v1.4.0
github.com/yuchenfw/gocrypt v0.0.0-20190627061521-ee7b5965ec93 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
google.golang.org/appengine v1.6.0 // indirect
gopkg.in/yaml.v2 v2.2.2
github.com/julienschmidt/httprouter v1.2.0
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/marpaia/graphite-golang v0.0.0-20190519024811-caf161d2c2b1
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mattn/go-runewidth v0.0.5 // indirect
github.com/mattn/go-sqlite3 v1.11.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/onsi/ginkgo v1.10.1 // indirect
github.com/onsi/gomega v1.7.0 // indirect
github.com/peterh/liner v1.1.0 // indirect
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v1.2.1
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.starlark.net v0.0.0-20191021185836-28350e608555 // indirect
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 // indirect
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect
golang.org/x/sys v0.0.0-20191024073052-e66fe6eb8e0c // indirect
google.golang.org/appengine v1.6.3 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.4
)
此差异已折叠。
package goku_labels
// constlabel
const (
Cluster = "cluster"
Instance = "instance"
)
// api labels
const (
API = "api"
Strategy = "strategy"
Status ="status"
)
// proxy
const (
Proto = "proto"
Method = "method"
Host ="host"
Path = "path"
)
\ No newline at end of file
......@@ -118,14 +118,14 @@ func (ctx *Context) RequestId() string {
//NewContext 创建Context
func NewContext(r *http.Request, requestID string, w http.ResponseWriter) *Context {
requestreader := NewRequestReader(r)
requestReader := NewRequestReader(r)
return &Context{
CookiesHandler: newCookieHandle(r.Header),
PriorityHeader: NewPriorityHeader(),
StatusHandler: NewStatusHandler(),
StoreHandler: NewStoreHandler(),
RequestOrg: requestreader,
ProxyRequest: NewRequest(requestreader),
RequestOrg: requestReader,
ProxyRequest: NewRequest(requestReader),
ProxyResponseHandler: nil,
requestID: requestID,
w: w,
......@@ -202,8 +202,8 @@ func (ctx *Context) ApiID() int {
}
//SetAPIID 设置接口ID
func (ctx *Context) SetAPIID(apiId int) {
ctx.apiID = apiId
func (ctx *Context) SetAPIID(apiID int) {
ctx.apiID = apiID
}
//Request 获取原始请求
......
package common
import (
"io/ioutil"
"net/http"
"net/url"
)
......@@ -29,15 +30,12 @@ func NewRequestReader(req *http.Request) *RequestReader {
func (r *RequestReader) ParseRequest() {
r.Header = NewHeader(r.req.Header)
body := make([]byte, r.req.ContentLength, r.req.ContentLength)
i, err := r.req.Body.Read(body)
body ,err:= ioutil.ReadAll(r.req.Body)
_ = r.req.Body.Close()
if err != nil && int64(i) == r.req.ContentLength {
r.BodyRequestHandler = NewBodyRequestHandler(r.req.Header.Get("Content-Type"), body)
} else {
if err != nil {
r.BodyRequestHandler = NewBodyRequestHandler(r.req.Header.Get("Content-Type"), nil)
} else {
r.BodyRequestHandler = NewBodyRequestHandler(r.req.Header.Get("Content-Type"), body)
}
}
......
package goku_observe
import (
"math"
"sync"
)
//type Item struct {
// Reference float64
// values int64
//}
type _Histogram struct {
size int
buckets []float64
values []int64
max float64
min float64
sum float64
count int
locker sync.Mutex
}
func (h *_Histogram) Observe(value float64) {
if value < 0 {
value = 0
}
h.locker.Lock()
h.count++
h.sum += value
if value> h.max{
h.max = value
}
if value < h.min{
h.min= value
}
l:= h.size -1
for i:=l;i>=0;i--{
if value >= h.buckets[i]{
break
}
h.values[i]++
}
h.locker.Unlock()
}
//func (h*_Histogram) Reset() {
// h.locker.Lock()
// h.values = make([]int64,len(h.values),len(h.values))
// h.sum ,h.max ,h.min,h.count = 0,0,math.MaxFloat64,0
// h.locker.Unlock()
//}
func (h *_Histogram) Collapse() (values []int64, sum ,max,min float64,count int){
h.locker.Lock()
values,sum,max,min,count = h.values,h.sum,h.max,h.min,h.count
h.locker.Unlock()
return
}
func NewHistogram(buckets []float64) *_Histogram {
max:= len(buckets) +1
h:=&_Histogram{
size: max,
buckets: make([]float64, 0, max),
values: make([]int64, max, max),
max: 0,
min: math.MaxFloat64,
sum: 0,
count: 0,
locker: sync.Mutex{},
}
h.buckets = append(h.buckets,buckets...)
h.buckets = append(h.buckets,math.MaxFloat64)
//sort.Float64s(h.buckets)
return h
}
package goku_observe
import (
"math"
"reflect"
"testing"
)
func TestHistogram_Observe(t *testing.T) {
type fields struct {
Buckets []float64
Count []int64
}
type args struct {
value float64
}
tests := []struct {
name string
fields fields
args args
want []int64
}{
{
name: "min",
fields: fields{
Buckets: []float64{0.1,0.5,1.0,math.MaxFloat64},
Count: []int64{0,0,0,0},
},
args: args{
value: 0.01,
},
want:[]int64{1,1,1,1},
},
{
name: "lavel",
fields: fields{
Buckets: []float64{0.1,0.5,1.0,math.MaxFloat64},
Count: []int64{0,0,0,0},
},
args: args{
value: 0.6,
},
want:[]int64{0,0,1,1},
},
{
name: "over",
fields:fields{
Buckets: []float64{0.1,0.5,1.0,math.MaxFloat64},
Count: []int64{0,0,0,0},
},
args: args{
value: 2,
},
want:[]int64{0,0,0,1},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &Histogram{
Buckets: tt.fields.Buckets,
Count: tt.fields.Count,
}
h.Observe(tt.args.value)
got:=h.Collapse()
if!reflect.DeepEqual(got, tt.want) {
t.Errorf("NewHistogram() = %v, want %v", got, tt.want)
}
wantOrg:= []int64{0,0,0,0}
if!reflect.DeepEqual(h.Count, wantOrg) {
t.Errorf("NewHistogram() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewHistogram(t *testing.T) {
type args struct {
buckets []float64
}
tests := []struct {
name string
args args
want *Histogram
}{
// TODO: Add test cases.
{
name: "",
args: args{
buckets: []float64{0.1,0.5,1.0},
},
want: &Histogram{
Buckets: []float64{0.1,0.5,1.0,math.MaxFloat64},
Count: []int64{0,0,0,0},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewHistogram(tt.args.buckets); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewHistogram() = %v, want %v", got, tt.want)
}
})
}
}
\ No newline at end of file
package goku_observe
type Observe interface {
Observe(value float64)
}
type Histogram interface {
Observe
Collapse() (values []int64, sum ,max,min float64,count int)
}
......@@ -12,7 +12,6 @@ func request(method string, backendDomain string, query url.Values, header http.
if backendDomain == "" {
return nil, fmt.Errorf("invaild url")
}
u, err := url.ParseRequestURI(backendDomain)
if err != nil {
......@@ -24,6 +23,7 @@ func request(method string, backendDomain string, query url.Values, header http.
return nil, err
}
queryDest:= u.Query()
if query!= nil{
for k,vs:=range query{
......
......@@ -3,10 +3,12 @@ package application
import (
"bytes"
"errors"
"github.com/eolinker/goku-api-gateway/diting"
goku_labels "github.com/eolinker/goku-api-gateway/goku-labels"
"github.com/eolinker/goku-api-gateway/node/monitor"
"io"
"net/http"
"net/url"
// "fmt"
"time"
)
......@@ -97,6 +99,17 @@ func (r *Request) Send() (*http.Response, error) {
if err != nil {
return nil, err
}
start:=time.Now()
defer func() {
delay:= time.Since(start)
labels:= make(diting.Labels)
labels[goku_labels.Proto] = req.Proto
labels[goku_labels.Host] = req.Host
labels[goku_labels.Path] = req.URL.Path
labels[goku_labels.Method] = req.Method
monitor.ProxyMonitor.Observe(float64( delay.Milliseconds()),labels)
}()
req.Header.Set("Accept-Encoding", "gzip")
req.Header = parseHeaders(r.headers)
......
package goku_template
//Template template
type Template interface {
Template() interface{}
Encode(v interface{}) (string, error)
Decode(org string) (interface{}, error)
}
package ksitigarbha
type IModule interface {
GetModel() []Model
GetDesc() string
GetName() string
GetNameSpace()string
GetDefaultConfig() interface{}
//CheckConfig(interface{}) bool
Decode(config string) (interface{},error)
Encoder(v interface{}) (string,error)
}
package ksitigarbha
//Model 模板数据
type Model struct {
Type string `json:"type"`
Label string `json:"label"`
Describe string `json:"descript"`
Items []map[string]interface{} `json:"items"`
}
package ksitigarbha
import (
"log"
)
// 这里只是在程序启动的时候会执行新增操作,所以不需要加锁
type modelManager struct {
modules map[string]IModule
names []string
namespaceNames map[string]string
}
func newModelManager() *modelManager {
return &modelManager{
modules: make(map[string]IModule),
names: make([]string,0,5),
namespaceNames: make(map[string]string),
}
}
var mManager = newModelManager()
//GetMonitorModuleNames 获取监控模块名称列表
func GetMonitorModuleNames() []string {
return mManager.getModuleNames()
}
//GetMonitorModuleModel 获取
func GetMonitorModuleModel(name string) (IModule ,bool){
return mManager.getModuleModel(name)
}
//GetNameSpaceByName 获取namespace
func GetNameSpaceByName(name string) string {
return mManager.getNameSpace(name)
}
//Register 注册
func Register(name string,f IModule) {
if f==nil {
log.Panic("register ksitigarbha nil")
}
mManager.add(name,f)
}
func (m *modelManager) add(name string,f IModule) {
_,has:=m.modules[name]
if has{
log.Panic("register ksitigarbha duplicate name")
}
m.modules[name] =f
m.names = append(m.names, f.GetName())
m.namespaceNames[name] = f.GetNameSpace()
}
func (m *modelManager) getModuleNames() []string {
return m.names
}
func (m *modelManager) getModuleModel(name string) (IModule ,bool){
v, ok := m.modules[name]
return v,ok
}
func (m *modelManager) isExisted(name string) bool {
_, ok := m.modules[name]
return ok
}
func (m *modelManager) getNameSpace(name string) string {
return m.namespaceNames[name]
}
func (m *modelManager) getModuleCount() int {
count := len(m.modules)
return count
}
package config
import (
"encoding/json"
"errors"
"regexp"
"github.com/eolinker/goku-api-gateway/ksitigarbha"
)
//ModuleNameSpace 模块空间名称
const ModuleNameSpace = "diting.graphite"
const moduleName = "Graphite"
const desc = "API监控模块对接Graphite(udp by minute)"
const addressPattern = `^[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]$`
const content = `[
{
"type": "line",
"label":"接入地址",
"descript":"(仅支持UDP)",
"items":[
{
"type":"text",
"name":"accessAddress",
"placeholder":"",
"required":true,
"pattern":"` + addressPattern + `"
}
]
}
]`
var (
mode []ksitigarbha.Model
addressMatcher *regexp.Regexp
)
func init() {
json.Unmarshal([]byte(content), &mode)
r, err := regexp.Compile(addressPattern)
if err != nil {
panic("init graphite module error:" + err.Error())
}
addressMatcher = r
}
//GraphiteModule 配置
type GraphiteModule struct {
}
//GraphiteConfig graphiteConfig
type GraphiteConfig struct {
AccessAddress string `json:"accessAddress"`
}
//GetModel getModel
func (c *GraphiteModule) GetModel() []ksitigarbha.Model {
return mode
}
//GetDesc getDesc
func (c *GraphiteModule) GetDesc() string {
return desc
}
//GetName getName
func (c *GraphiteModule) GetName() string {
return moduleName
}
//GetNameSpace getNameSpace
func (c *GraphiteModule) GetNameSpace() string {
return ModuleNameSpace
}
//GetDefaultConfig getDefauleConfig
func (c *GraphiteModule) GetDefaultConfig() interface{} {
return &GraphiteConfig{
AccessAddress: "",
}
}
//Encoder encoder
func (c *GraphiteModule) Encoder(v interface{}) (string, error) {
if v == nil {
return "", nil
}
if vm, ok := v.(*GraphiteConfig); ok {
d, _ := json.Marshal(vm)
return string(d), nil
}
return "", errors.New("illegal config")
}
//Decode decode
func Decode(config string) (*GraphiteConfig, error) {
mc := new(GraphiteConfig)
err := json.Unmarshal([]byte(config), &mc)
if err != nil {
return nil, err
}
match := addressMatcher.MatchString(mc.AccessAddress)
if !match {
return nil, errors.New("invalid accessAddress")
}
return mc, nil
}
//Decode decode
func (c *GraphiteModule) Decode(config string) (interface{}, error) {
return Decode(config)
}
//Register 模板注册
func Register() {
ksitigarbha.Register(moduleName, new(GraphiteModule))
}
package graphite
import (
"strconv"
"sync"
"time"
"github.com/eolinker/goku-api-gateway/diting"
"github.com/marpaia/graphite-golang"
)
//Count count
type Count struct {
metricKey MetricKey
metricsValuesCount *MetricsValuesCount
}
//Add add
func (c *Count) Add(value float64, labels diting.Labels) {
key := c.metricKey.Key(labels, "count")
c.metricsValuesCount.Add(key, value)
}
//Metrics metrics
func (c *Count) Metrics() []graphite.Metric {
values := c.metricsValuesCount.Collapse()
if len(values) == 0 {
return nil
}
ms := make([]graphite.Metric, 0, len(values))
t := time.Now().Unix()
for k, v := range values {
ms = append(ms, graphite.NewMetric(k, strconv.FormatInt(v, 10), t))
}
return ms
}
//NewCounter new counter
func NewCounter(metricKey MetricKey) *Count {
return &Count{
metricKey: metricKey,
metricsValuesCount: NewMetricsValuesCount(),
}
}
//MetricsValuesCount metricsValuesCount
type MetricsValuesCount struct {
values map[string]int64
locker sync.Mutex
}
//NewMetricsValuesCount new metricsValuesCount
func NewMetricsValuesCount() *MetricsValuesCount {
return &MetricsValuesCount{
values: make(map[string]int64),
locker: sync.Mutex{},
}
}
//Add add
func (m *MetricsValuesCount) Add(key string, value float64) {
m.locker.Lock()
v := int64(value)
m.values[key] += v
m.locker.Unlock()
}
//Collapse collapse
func (m *MetricsValuesCount) Collapse() map[string]int64 {
n := make(map[string]int64)
m.locker.Lock()
v := m.values
m.values = n
m.locker.Unlock()
return v
}
package graphite
import (
"strconv"
"sync"
"time"
"github.com/eolinker/goku-api-gateway/diting"
"github.com/marpaia/graphite-golang"
)
//Gauge gauge
type Gauge struct {
metricKey MetricKey
metricsValuesGauge *MetricsValuesGauge
}
//NewGauge new gauge
func NewGauge(metricKey MetricKey) *Gauge {
return &Gauge{metricKey: metricKey, metricsValuesGauge: NewMetricsValuesGauge()}
}
//Set set
func (g *Gauge) Set(value float64, labels diting.Labels) {
key := g.metricKey.Key(labels, "value")
g.metricsValuesGauge.Set(key, value)
}
//Metrics metrics
func (g *Gauge) Metrics() []graphite.Metric {
values := g.metricsValuesGauge.Collapse()
if len(values) == 0 {
return nil
}
ms := make([]graphite.Metric, 0, len(values))
t := time.Now().Unix()
for k, v := range values {
ms = append(ms, graphite.NewMetric(k, strconv.FormatFloat(v, 'f', 2, 64), t))
}
return ms
}
//MetricsValuesGauge metricsValuesGauge
type MetricsValuesGauge struct {
values map[string]float64
locker sync.Mutex
}
//NewMetricsValuesGauge new metricsValuesGauge
func NewMetricsValuesGauge() *MetricsValuesGauge {
return &MetricsValuesGauge{
values: make(map[string]float64),
locker: sync.Mutex{},
}
}
//Set set
func (m *MetricsValuesGauge) Set(key string, value float64) {
m.locker.Lock()
m.values[key] = value
m.locker.Unlock()
}
//Collapse collapse
func (m *MetricsValuesGauge) Collapse() map[string]float64 {
n := make(map[string]float64)
m.locker.Lock()
v := m.values
m.values = n
m.locker.Unlock()
return v
}
package graphite
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
"github.com/eolinker/goku-api-gateway/diting"
"github.com/eolinker/goku-api-gateway/module"
"github.com/eolinker/goku-api-gateway/module/graphite/config"
)
//Constructor constructor
type Constructor struct {
address string
graphiteProxy *Proxy
cacheFactory *diting.CacheFactory
locker sync.RWMutex
metrics []Metrics
cancelFunc context.CancelFunc
}
//Register register
func Register() {
config.Register()
g := &Constructor{
graphiteProxy: NewProxy(),
}
g.cacheFactory = diting.NewCacheFactory(g)
module.Register(config.ModuleNameSpace, true)
diting.Register(config.ModuleNameSpace, g)
}
//Close close
func (g *Constructor) Close() {
module.Close(config.ModuleNameSpace)
g.stop()
}
func (g *Constructor) addMetrics(metrics Metrics) {
g.locker.Lock()
g.metrics = append(g.metrics, metrics)
g.locker.Unlock()
}
//Send send
func (g *Constructor) Send() {
g.locker.RLock()
ms := g.metrics
g.locker.RUnlock()
for _, m := range ms {
metrics := m.Metrics()
if len(metrics) > 0 {
g.graphiteProxy.Send(metrics)
}
}
}
//Namespace nameSpace
func (g *Constructor) Namespace() string {
return config.ModuleNameSpace
}
func (g *Constructor) stop() {
g.locker.Lock()
if g.address != "" {
g.cancelFunc()
g.address = ""
g.cancelFunc = nil
}
g.locker.Unlock()
}
func (g *Constructor) start(host string, port int) {
addr := fmt.Sprintf("%s:%d", host, port)
g.locker.Lock()
defer g.locker.Unlock()
if g.address != addr {
ctx, cancel := context.WithCancel(context.Background())
go g.doLoop(ctx, host, port)
g.address = addr
g.cancelFunc = cancel
}
}
func (g *Constructor) doLoop(ctx context.Context, host string, port int) {
e := g.graphiteProxy.Connect(host, port)
if e != nil {
return
}
tick := time.NewTicker(time.Minute)
defer tick.Stop()
for {
select {
case <-tick.C:
g.Send()
case <-ctx.Done():
g.Send()
g.graphiteProxy.close()
return
}
}
}
//Create create
func (g *Constructor) Create(conf string) (diting.Factory, error) {
confV, err := config.Decode(conf)
if err != nil {
go g.stop()
return nil, err
}
par := strings.Split(confV.AccessAddress, ":")
if len(par) != 2 {
go g.stop()
return nil, errors.New("invalid AccessAddress")
}
host := par[0]
port, err := strconv.Atoi(par[1])
if err != nil {
go g.stop()
return nil, err
}
go g.start(host, port)
module.Open(config.ModuleNameSpace)
return g.cacheFactory, nil
}
//NewCounter new counter
func (g *Constructor) NewCounter(opt *diting.CounterOpts) (diting.Counter, error) {
metricKey := NewMetricKey(toLabelName(opt.Namespace, opt.Subsystem, opt.Name), opt.LabelNames)
c := NewCounter(metricKey)
g.addMetrics(c)
return c, nil
}
//NewHistogram new histogram
func (g *Constructor) NewHistogram(opt *diting.HistogramOpts) (diting.Histogram, error) {
metricKey := NewMetricKey(toLabelName(opt.Namespace, opt.Subsystem, opt.Name), opt.LabelNames)
h := NewHistogram(metricKey, opt.Buckets)
g.addMetrics(h)
return h, nil
}
//NewGauge new gauge
func (g *Constructor) NewGauge(opt *diting.GaugeOpts) (diting.Gauge, error) {
metricKey := NewMetricKey(toLabelName(opt.Namespace, opt.Subsystem, opt.Name), opt.LabelNames)
gauge := NewGauge(metricKey)
g.addMetrics(gauge)
return gauge, nil
}
func toLabelName(Namespace string, Subsystem string, Name string) string {
tmp := make([]string, 0, 3)
if Namespace != "" {
tmp = append(tmp, Namespace)
}
if Subsystem != "" {
tmp = append(tmp, Subsystem)
}
if Name != "" {
tmp = append(tmp, Name)
}
return strings.Join(tmp, "_")
}
package graphite
import (
"math"
"strconv"
"strings"
"sync"
"time"
"github.com/eolinker/goku-api-gateway/diting"
observe "github.com/eolinker/goku-api-gateway/goku-observe"
"github.com/marpaia/graphite-golang"
)
//Histogram histogram
type Histogram struct {
metricKey MetricKey
keyHistogram *_KeyHistogram
}
//NewHistogram new histogram
func NewHistogram(metricKey MetricKey, buckets []float64) *Histogram {
return &Histogram{
metricKey: metricKey,
keyHistogram: newKeyHistogram(buckets),
}
}
//Observe observe
func (h *Histogram) Observe(value float64, labels diting.Labels) {
key := h.metricKey.Key(labels, "")
h.keyHistogram.Observe(key, value)
}
//Metrics metrics
func (h *Histogram) Metrics() []graphite.Metric {
all := h.keyHistogram.Collapse()
if len(all) == 0 {
return nil
}
keySize := len(all)
size := keySize * (len(h.keyHistogram.buckets) + 5)
ms := make([]graphite.Metric, 0, size)
t := time.Now().Unix()
tmpName := make([]string, 2)
tmpBucketName := make([]string, 3)
tmpBucketName[0] = "bucket_le"
overIndex := len(h.keyHistogram.buckets)
for k, v := range all {
tmpName[0] = k
tmpName[1] = "count"
ms = append(ms, graphite.NewMetric(strings.Join(tmpName, "."), strconv.Itoa(v.count), t))
tmpName[1] = "max"
ms = append(ms, graphite.NewMetric(strings.Join(tmpName, "."), strconv.FormatFloat(v.max, 'f', 2, 64), t))
tmpName[1] = "min"
ms = append(ms, graphite.NewMetric(strings.Join(tmpName, "."), strconv.FormatFloat(v.min, 'f', 2, 64), t))
tmpName[1] = "sum"
ms = append(ms, graphite.NewMetric(strings.Join(tmpName, "."), strconv.FormatFloat(v.sum, 'f', 2, 64), t))
tmpName[1] = "bucket_le_inf"
ms = append(ms, graphite.NewMetric(strings.Join(tmpName, "."), strconv.FormatInt(v.list[overIndex], 10), t))
for i, b := range h.keyHistogram.buckets {
floor := math.Floor(b)
tmpBucketName[1] = strconv.FormatInt(int64(floor), 10)
if floor-b < 0 {
tmpBucketName[2] = strconv.FormatInt(int64(b*100-floor*100), 10)
tmpName[1] = strings.Join(tmpBucketName, "_")
} else {
tmpName[1] = strings.Join(tmpBucketName[:2], "_")
}
ms = append(ms, graphite.NewMetric(strings.Join(tmpName, "."), strconv.FormatInt(v.list[i], 10), t))
}
}
return ms
}
type _KeyHistogram struct {
buckets []float64
histograms map[string]observe.Histogram
locker sync.Mutex
}
func newKeyHistogram(buckets []float64) *_KeyHistogram {
return &_KeyHistogram{
buckets: buckets,
histograms: make(map[string]observe.Histogram),
locker: sync.Mutex{},
}
}
//HistogramValue histogramValue
type HistogramValue struct {
list []int64
max float64
min float64
count int
sum float64
}
//Collapse collapse
func (k *_KeyHistogram) Collapse() map[string]HistogramValue {
new := make(map[string]observe.Histogram)
k.locker.Lock()
histograms := k.histograms
k.histograms = new
k.locker.Unlock()
values := make(map[string]HistogramValue)
for k, hm := range histograms {
col, sum, max, min, count := hm.Collapse()
values[k] = HistogramValue{
list: col,
max: max,
min: min,
count: count,
sum: sum,
}
}
return values
}
func (k *_KeyHistogram) get(key string) observe.Histogram {
k.locker.Lock()
h, has := k.histograms[key]
if !has {
h = observe.NewHistogram(k.buckets)
k.histograms[key] = h
}
k.locker.Unlock()
return h
}
//Observe observe
func (k *_KeyHistogram) Observe(key string, value float64) {
k.get(key).Observe(value)
}
package graphite
import (
"strings"
"unicode"
"github.com/eolinker/goku-api-gateway/diting"
"github.com/marpaia/graphite-golang"
)
//Metrics Metrics
type Metrics interface {
Metrics() []graphite.Metric
}
//MetricKey MetricKey
type MetricKey interface {
Key(labels diting.Labels, valueType string) string
}
type _MetricKey struct {
name string
labelNames []string
}
//NewMetricKey new MetricKey
func NewMetricKey(name string, labelNames []string) MetricKey {
return &_MetricKey{name: name, labelNames: labelNames}
}
//Key key
func (m *_MetricKey) Key(labels diting.Labels, valueType string) string {
tmp := make([]string, 0, len(m.labelNames)+2)
tmp = append(tmp, m.name)
for _, name := range m.labelNames {
labelValue:= labels[name]
tmp = append(tmp, formatLabelValue(labelValue))
}
if valueType != "" {
tmp = append(tmp, formatLabelValue(valueType))
}
return strings.Join(tmp, ".")
}
const rep = '_'
//formatLabelValue 将label value的所有字母、数字转换成 _
func formatLabelValue(value string)string {
s:=[]rune(value)
for i, r := range s {
if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
s[i] = rep
}
}
return string(s)
}
\ No newline at end of file
package graphite
import (
"sync"
"github.com/marpaia/graphite-golang"
)
//Proxy proxy
type Proxy struct {
locker sync.RWMutex
graphite *graphite.Graphite
}
//NewProxy new proxy
func NewProxy() *Proxy {
return &Proxy{
locker: sync.RWMutex{},
graphite: nil,
}
}
//Send send
func (p *Proxy) Send(metrics []graphite.Metric) {
p.locker.RLock()
g := p.graphite
p.locker.RUnlock()
if g != nil {
g.SendMetrics(metrics)
}
}
//Connect connect
func (p *Proxy) Connect(host string, port int) error {
p.close()
newG, err := graphite.NewGraphite(host, port)
if err != nil {
return err
}
p.locker.Lock()
p.graphite = newG
p.locker.Unlock()
return nil
}
func (p *Proxy) close() (err error) {
p.locker.Lock()
if p.graphite != nil {
err = p.graphite.Disconnect()
p.graphite = nil
}
p.locker.Unlock()
return
}
func (p *Proxy) isClose() bool {
p.locker.Lock()
isClose := p.graphite == nil
p.locker.Unlock()
return isClose
}
此差异已折叠。
package prometheus
import (
"github.com/eolinker/goku-api-gateway/diting"
"github.com/prometheus/client_golang/prometheus"
)
//ReadLabels readLabels
func ReadLabels(labels diting.Labels) prometheus.Labels {
return prometheus.Labels(labels)
}
此差异已折叠。
package prometheus
import (
"github.com/eolinker/goku-api-gateway/diting"
"github.com/prometheus/client_golang/prometheus"
)
//ReadCounterOpts read CounterOpts
func ReadCounterOpts(opts *diting.CounterOpts) prometheus.CounterOpts {
return prometheus.CounterOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: opts.Name,
Help: opts.Help,
//ConstLabels: prometheus.Labels( opts.ConstLabels),
}
}
//ReadGaugeOpts read GaugeOpts
func ReadGaugeOpts(opts *diting.GaugeOpts) prometheus.GaugeOpts {
return prometheus.GaugeOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: opts.Name,
Help: opts.Help,
//ConstLabels: prometheus.Labels( opts.ConstLabels),
}
}
//ReadHistogramOpts read HistogramOpts
func ReadHistogramOpts(opts *diting.HistogramOpts) prometheus.HistogramOpts {
return prometheus.HistogramOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: opts.Name,
Help: opts.Help,
//ConstLabels: prometheus.Labels(opts.ConstLabels),
Buckets: opts.Buckets,
}
}
//ReadSummaryOpts read SummaryOpts
func ReadSummaryOpts(opts *diting.SummaryOpts) prometheus.SummaryOpts {
return prometheus.SummaryOpts{
Namespace: opts.Namespace,
Subsystem: opts.Subsystem,
Name: opts.Name,
Help: opts.Help,
//ConstLabels: prometheus.Labels(opts.ConstLabels),
Objectives: opts.Objectives,
MaxAge: opts.MaxAge,
AgeBuckets: opts.AgeBuckets,
BufCap: opts.BufCap,
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册