Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
462442e21a | ||
|
|
90fcddfb9f | ||
|
|
8de791079c | ||
|
|
13b89d5971 | ||
|
|
8b97638624 | ||
|
|
79ea9e795e | ||
|
|
38e06e7b03 | ||
|
|
f25de8d5c9 | ||
|
|
af74500810 | ||
|
|
189295ffcf | ||
|
|
da09889eca | ||
|
|
9ffa910044 | ||
|
|
a6d711c2a0 | ||
|
|
6bedc97c95 | ||
|
|
4bdd1eda76 | ||
|
|
7fd9766565 | ||
|
|
72983d8d86 | ||
|
|
ec6494fa9c | ||
|
|
a4a6e95099 | ||
|
|
2e11c99b7a | ||
|
|
014f433191 | ||
|
|
e6ac085025 | ||
|
|
6f60be6a00 | ||
|
|
dceb082a83 | ||
|
|
9084794448 | ||
|
|
065de8d208 | ||
|
|
e5f9316e33 | ||
|
|
bb5fa38613 | ||
|
|
ccb97b1c79 | ||
|
|
853e4fd0f0 | ||
|
|
d3169eaea5 | ||
|
|
68b93bf6b4 | ||
|
|
1279f0d394 | ||
|
|
24fbd740b5 | ||
|
|
5772fb2309 | ||
|
|
bf2b889c16 | ||
|
|
9372bc90dd | ||
|
|
5b46c80431 | ||
|
|
1bdb988425 | ||
|
|
a544a77669 | ||
|
|
c61108faa8 | ||
|
|
30ac3118e2 | ||
|
|
f0e8dd1baa | ||
|
|
04a327ce9a | ||
|
|
2ac26f6aa4 | ||
|
|
d9aac44ea3 | ||
|
|
160a1f1466 | ||
|
|
38e2c151ec | ||
|
|
9d54c17695 | ||
|
|
e31d68c1e1 | ||
|
|
7ae9180bf9 | ||
|
|
424f3ae29d | ||
|
|
ca0571a21b | ||
|
|
c9bd9fd460 | ||
|
|
8d4ec6822c | ||
|
|
061253b4c3 | ||
|
|
f6dfd6acec | ||
|
|
a35aa2f520 | ||
|
|
ea84c41be3 | ||
|
|
0f0776fc1a | ||
|
|
6aacf49764 | ||
|
|
18a01b9b43 | ||
|
|
32a3e08332 | ||
|
|
2397695a2d | ||
|
|
0ceebd9902 | ||
|
|
7e62c72b79 | ||
|
|
b3dedbdc31 | ||
|
|
fa967b5450 | ||
|
|
b619eb4efe |
@@ -5,6 +5,7 @@ function build() {
|
|||||||
NAME="edge-node"
|
NAME="edge-node"
|
||||||
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
|
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
|
||||||
DIST=$ROOT/"../dist/${NAME}"
|
DIST=$ROOT/"../dist/${NAME}"
|
||||||
|
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
||||||
OS=${1}
|
OS=${1}
|
||||||
ARCH=${2}
|
ARCH=${2}
|
||||||
TAG=${3}
|
TAG=${3}
|
||||||
@@ -53,7 +54,6 @@ function build() {
|
|||||||
|
|
||||||
echo "building ..."
|
echo "building ..."
|
||||||
|
|
||||||
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
|
||||||
CC_PATH=""
|
CC_PATH=""
|
||||||
CXX_PATH=""
|
CXX_PATH=""
|
||||||
if [[ `uname -a` == *"Darwin"* && "${OS}" == "linux" ]]; then
|
if [[ `uname -a` == *"Darwin"* && "${OS}" == "linux" ]]; then
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -88,6 +89,34 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
app.On("goman", func() {
|
||||||
|
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||||
|
reply, err := sock.Send(&gosock.Command{Code: "goman"})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
} else {
|
||||||
|
instancesJSON, err := json.MarshalIndent(reply.Params, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Println(string(instancesJSON))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
app.On("conns", func() {
|
||||||
|
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||||
|
reply, err := sock.Send(&gosock.Command{Code: "conns"})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
} else {
|
||||||
|
resultJSON, err := json.MarshalIndent(reply.Params, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Println(string(resultJSON))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
app.Run(func() {
|
app.Run(func() {
|
||||||
node := nodes.NewNode()
|
node := nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
|
|||||||
23
go.mod
23
go.mod
@@ -5,7 +5,6 @@ go 1.15
|
|||||||
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
replace github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
|
||||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||||
github.com/andybalholm/brotli v1.0.3
|
github.com/andybalholm/brotli v1.0.3
|
||||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670
|
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670
|
||||||
@@ -13,28 +12,24 @@ require (
|
|||||||
github.com/chai2010/webp v1.1.0 // indirect
|
github.com/chai2010/webp v1.1.0 // indirect
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/dop251/goja v0.0.0-20210804101310-32956a348b49
|
github.com/dop251/goja v0.0.0-20210804101310-32956a348b49
|
||||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
github.com/golang/protobuf v1.5.2
|
|
||||||
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24
|
github.com/iwind/TeaGo v0.0.0-20211026123858-7de7a21cad24
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
||||||
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
|
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3
|
||||||
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8
|
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
|
||||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.9
|
github.com/mattn/go-sqlite3 v1.14.9
|
||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/mssola/user_agent v0.5.3
|
||||||
github.com/mssola/user_agent v0.5.2
|
|
||||||
github.com/pires/go-proxyproto v0.6.1
|
github.com/pires/go-proxyproto v0.6.1
|
||||||
github.com/shirou/gopsutil v3.21.5+incompatible
|
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||||
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
github.com/tklauser/go-sysconf v0.3.6 // indirect
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
|
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22
|
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d
|
||||||
golang.org/x/text v0.3.6
|
golang.org/x/text v0.3.7
|
||||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect
|
golang.org/x/tools v0.1.3 // indirect
|
||||||
google.golang.org/grpc v1.38.0
|
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||||
|
google.golang.org/grpc v1.43.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||||
)
|
)
|
||||||
|
|||||||
44
go.sum
44
go.sum
@@ -1,4 +1,5 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
@@ -9,6 +10,7 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
|
|||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM=
|
github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM=
|
||||||
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670 h1:FQPKKjDhzG0T4ew6dm6MGrXb4PRAi8ZmTuYuxcF62BM=
|
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670 h1:FQPKKjDhzG0T4ew6dm6MGrXb4PRAi8ZmTuYuxcF62BM=
|
||||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670/go.mod h1:iRWAFbKXMMkVQyxZ1PfGlkBr1TjATx1zy2MRprV7A3Q=
|
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670/go.mod h1:iRWAFbKXMMkVQyxZ1PfGlkBr1TjATx1zy2MRprV7A3Q=
|
||||||
@@ -22,6 +24,11 @@ github.com/chai2010/webp v1.1.0/go.mod h1:LP12PG5IFmLGHUU26tBiCBKnghxx3toZFwDjOY
|
|||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -37,10 +44,14 @@ github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||||
@@ -63,6 +74,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
@@ -75,6 +87,7 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060 h1:qdLtK4PDXxk2vMKkTWl5Fl9xqYuRCukzWAgJbLHdfOo=
|
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060 h1:qdLtK4PDXxk2vMKkTWl5Fl9xqYuRCukzWAgJbLHdfOo=
|
||||||
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
github.com/iwind/TeaGo v0.0.0-20210628135026-38575a4ab060/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||||
@@ -114,6 +127,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mssola/user_agent v0.5.2 h1:CZkTUahjL1+OcZ5zv3kZr8QiJ8jy2H08vZIEkBeRbxo=
|
github.com/mssola/user_agent v0.5.2 h1:CZkTUahjL1+OcZ5zv3kZr8QiJ8jy2H08vZIEkBeRbxo=
|
||||||
github.com/mssola/user_agent v0.5.2/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
github.com/mssola/user_agent v0.5.2/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||||
|
github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578=
|
||||||
|
github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -129,8 +144,11 @@ github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9G
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
|
github.com/shirou/gopsutil v3.21.5+incompatible h1:OloQyEerMi7JUrXiNzy8wQ5XN+baemxSl12QgIzt0jc=
|
||||||
github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
github.com/shirou/gopsutil v3.21.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
|
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||||
|
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -138,20 +156,25 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
|
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
|
||||||
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||||
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||||
|
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
@@ -163,19 +186,25 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9 h1:kmreh1vGI63l2FxOAYS3Yv6ATsi7lSTuwNSVbGfJV9I=
|
||||||
|
golang.org/x/net v0.0.0-20211215060638-4ddde0e984e9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -199,6 +228,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d h1:1oIt9o40TWWI9FUaveVpUvBe13FNqBNVXy3ue2fcfkw=
|
||||||
|
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
@@ -206,6 +237,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@@ -225,17 +258,25 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
|
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
|
||||||
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||||
|
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
|
||||||
|
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
|
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
|
||||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
|
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||||
|
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
|
||||||
|
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -248,6 +289,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -257,6 +300,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
|||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -187,6 +188,11 @@ func (this *AppCmd) runStart() {
|
|||||||
_ = os.Setenv("EdgeBackground", "on")
|
_ = os.Setenv("EdgeBackground", "on")
|
||||||
|
|
||||||
cmd := exec.Command(os.Args[0])
|
cmd := exec.Command(os.Args[0])
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Foreground: false,
|
||||||
|
Setsid: true,
|
||||||
|
}
|
||||||
|
|
||||||
err := cmd.Start()
|
err := cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(this.product+" start failed:", err.Error())
|
fmt.Println(this.product+" start failed:", err.Error())
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type Item struct {
|
|||||||
Type ItemType `json:"type"`
|
Type ItemType `json:"type"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
ExpiredAt int64 `json:"expiredAt"`
|
ExpiredAt int64 `json:"expiredAt"`
|
||||||
|
StaleAt int64 `json:"staleAt"`
|
||||||
HeaderSize int64 `json:"headerSize"`
|
HeaderSize int64 `json:"headerSize"`
|
||||||
BodySize int64 `json:"bodySize"`
|
BodySize int64 `json:"bodySize"`
|
||||||
MetaSize int64 `json:"metaSize"`
|
MetaSize int64 `json:"metaSize"`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package caches
|
package caches
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -59,15 +60,15 @@ func TestItems_Memory2(t *testing.T) {
|
|||||||
runtime.ReadMemStats(stat)
|
runtime.ReadMemStats(stat)
|
||||||
var memory1 = stat.HeapInuse
|
var memory1 = stat.HeapInuse
|
||||||
|
|
||||||
var items = map[int32]map[string]bool{}
|
var items = map[int32]map[string]zero.Zero{}
|
||||||
for i := 0; i < 10_000_000; i++ {
|
for i := 0; i < 10_000_000; i++ {
|
||||||
var week = int32((time.Now().Unix() - int64(86400*rands.Int(0, 300))) / (86400 * 7))
|
var week = int32((time.Now().Unix() - int64(86400*rands.Int(0, 300))) / (86400 * 7))
|
||||||
m, ok := items[week]
|
m, ok := items[week]
|
||||||
if !ok {
|
if !ok {
|
||||||
m = map[string]bool{}
|
m = map[string]zero.Zero{}
|
||||||
items[week] = m
|
items[week] = m
|
||||||
}
|
}
|
||||||
m[types.String(int64(i)*1_000_000)] = true
|
m[types.String(int64(i)*1_000_000)] = zero.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.ReadMemStats(stat)
|
runtime.ReadMemStats(stat)
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ package caches
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -45,6 +47,7 @@ type FileList struct {
|
|||||||
hitsTableName string
|
hitsTableName string
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool
|
||||||
|
isReady bool
|
||||||
|
|
||||||
memoryCache *ttlcache.Cache
|
memoryCache *ttlcache.Cache
|
||||||
}
|
}
|
||||||
@@ -67,7 +70,7 @@ func (this *FileList) Init() error {
|
|||||||
remotelogs.Println("CACHE", "create cache dir '"+this.dir+"'")
|
remotelogs.Println("CACHE", "create cache dir '"+this.dir+"'")
|
||||||
}
|
}
|
||||||
|
|
||||||
this.itemsTableName = "cacheItems_v2"
|
this.itemsTableName = "cacheItems_v3"
|
||||||
this.hitsTableName = "hits"
|
this.hitsTableName = "hits"
|
||||||
|
|
||||||
var dir = this.dir
|
var dir = this.dir
|
||||||
@@ -75,21 +78,16 @@ func (this *FileList) Init() error {
|
|||||||
// 防止sqlite提示authority错误
|
// 防止sqlite提示authority错误
|
||||||
dir = ""
|
dir = ""
|
||||||
}
|
}
|
||||||
db, err := sql.Open("sqlite3", "file:"+dir+"/index.db?cache=shared&mode=rwc&_journal_mode=WAL")
|
var dbPath = dir + "/index.db"
|
||||||
|
remotelogs.Println("CACHE", "loading database '"+dbPath+"'")
|
||||||
|
db, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=shared&mode=rwc&_journal_mode=WAL")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.New("open database failed: " + err.Error())
|
||||||
}
|
}
|
||||||
db.SetMaxOpenConns(1)
|
|
||||||
this.db = db
|
|
||||||
|
|
||||||
// 清除旧表
|
db.SetMaxOpenConns(1)
|
||||||
this.oldTables = []string{
|
|
||||||
"cacheItems",
|
this.db = db
|
||||||
}
|
|
||||||
err = this.removeOldTables()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Warn("CACHE", "clean old tables failed: "+err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO 耗时过长,暂时不整理数据库
|
// TODO 耗时过长,暂时不整理数据库
|
||||||
/**_, err = db.Exec("VACUUM")
|
/**_, err = db.Exec("VACUUM")
|
||||||
@@ -100,7 +98,18 @@ func (this *FileList) Init() error {
|
|||||||
// 创建
|
// 创建
|
||||||
err = this.initTables(db, 1)
|
err = this.initTables(db, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.New("init tables failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除旧表
|
||||||
|
// 这个一定要在initTables()之后,因为老的数据需要转移
|
||||||
|
this.oldTables = []string{
|
||||||
|
"cacheItems",
|
||||||
|
"cacheItems_v2",
|
||||||
|
}
|
||||||
|
err = this.removeOldTables()
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Warn("CACHE", "clean old tables failed: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取总数量
|
// 读取总数量
|
||||||
@@ -121,7 +130,7 @@ func (this *FileList) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
this.insertStmt, err = this.db.Prepare(`INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "host", "serverId", "createdAt") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
this.insertStmt, err = this.db.Prepare(`INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -141,7 +150,7 @@ func (this *FileList) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
this.purgeStmt, err = this.db.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" WHERE expiredAt<=? LIMIT ?`)
|
this.purgeStmt, err = this.db.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" WHERE staleAt<=? LIMIT ?`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -168,6 +177,8 @@ func (this *FileList) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isReady = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,11 +188,15 @@ func (this *FileList) Reset() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) Add(hash string, item *Item) error {
|
func (this *FileList) Add(hash string, item *Item) error {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.Host, item.ServerId, utils.UnixTime())
|
if item.StaleAt == 0 {
|
||||||
|
item.StaleAt = item.ExpiredAt
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -200,7 +215,7 @@ func (this *FileList) Add(hash string, item *Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) Exist(hash string) (bool, error) {
|
func (this *FileList) Exist(hash string) (bool, error) {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +245,7 @@ func (this *FileList) Exist(hash string) (bool, error) {
|
|||||||
|
|
||||||
// CleanPrefix 清理某个前缀的缓存数据
|
// CleanPrefix 清理某个前缀的缓存数据
|
||||||
func (this *FileList) CleanPrefix(prefix string) error {
|
func (this *FileList) CleanPrefix(prefix string) error {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,8 +258,9 @@ func (this *FileList) CleanPrefix(prefix string) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
var count = int64(10000)
|
var count = int64(10000)
|
||||||
|
var staleLife = 600 // TODO 需要可以设置
|
||||||
for {
|
for {
|
||||||
result, err := this.db.Exec(`UPDATE "`+this.itemsTableName+`" SET expiredAt=0 WHERE id IN (SELECT id FROM "`+this.itemsTableName+`" WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)=1 LIMIT `+strconv.FormatInt(count, 10)+`)`, utils.UnixTime(), prefix)
|
result, err := this.db.Exec(`UPDATE "`+this.itemsTableName+`" SET expiredAt=0,staleAt=? WHERE id IN (SELECT id FROM "`+this.itemsTableName+`" WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)=1 LIMIT `+types.String(count)+`)`, utils.UnixTime()+int64(staleLife), utils.UnixTime(), prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -259,7 +275,7 @@ func (this *FileList) CleanPrefix(prefix string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) Remove(hash string) error {
|
func (this *FileList) Remove(hash string) error {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +319,7 @@ func (this *FileList) Remove(hash string) error {
|
|||||||
// count 每次遍历的最大数量,控制此数字可以保证每次清理的时候不用花太多时间
|
// count 每次遍历的最大数量,控制此数字可以保证每次清理的时候不用花太多时间
|
||||||
// callback 每次发现过期key的调用
|
// callback 每次发现过期key的调用
|
||||||
func (this *FileList) Purge(count int, callback func(hash string) error) (int, error) {
|
func (this *FileList) Purge(count int, callback func(hash string) error) (int, error) {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,7 +363,7 @@ func (this *FileList) Purge(count int, callback func(hash string) error) (int, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) PurgeLFU(count int, callback func(hash string) error) error {
|
func (this *FileList) PurgeLFU(count int, callback func(hash string) error) error {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,7 +406,7 @@ func (this *FileList) PurgeLFU(count int, callback func(hash string) error) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) CleanAll() error {
|
func (this *FileList) CleanAll() error {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +421,7 @@ func (this *FileList) CleanAll() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) Stat(check func(hash string) bool) (*Stat, error) {
|
func (this *FileList) Stat(check func(hash string) bool) (*Stat, error) {
|
||||||
if this.isClosed {
|
if !this.isReady {
|
||||||
return &Stat{}, nil
|
return &Stat{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,6 +464,7 @@ func (this *FileList) OnRemove(f func(item *Item)) {
|
|||||||
|
|
||||||
func (this *FileList) Close() error {
|
func (this *FileList) Close() error {
|
||||||
this.isClosed = true
|
this.isClosed = true
|
||||||
|
this.isReady = false
|
||||||
|
|
||||||
this.memoryCache.Destroy()
|
this.memoryCache.Destroy()
|
||||||
|
|
||||||
@@ -472,7 +489,16 @@ func (this *FileList) Close() error {
|
|||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
func (this *FileList) initTables(db *sql.DB, times int) error {
|
func (this *FileList) initTables(db *sql.DB, times int) error {
|
||||||
|
// 检查是否存在
|
||||||
|
_, err := db.Exec(`SELECT id FROM "` + this.itemsTableName + `" LIMIT 1`)
|
||||||
|
var notFound = false
|
||||||
|
if err != nil {
|
||||||
|
notFound = true
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// expiredAt - 过期时间,用来判断有无过期
|
||||||
|
// staleAt - 陈旧最大时间,用来清理缓存
|
||||||
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemsTableName + `" (
|
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemsTableName + `" (
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
"hash" varchar(32),
|
"hash" varchar(32),
|
||||||
@@ -481,6 +507,7 @@ func (this *FileList) initTables(db *sql.DB, times int) error {
|
|||||||
"bodySize" integer DEFAULT 0,
|
"bodySize" integer DEFAULT 0,
|
||||||
"metaSize" integer DEFAULT 0,
|
"metaSize" integer DEFAULT 0,
|
||||||
"expiredAt" integer DEFAULT 0,
|
"expiredAt" integer DEFAULT 0,
|
||||||
|
"staleAt" integer DEFAULT 0,
|
||||||
"createdAt" integer DEFAULT 0,
|
"createdAt" integer DEFAULT 0,
|
||||||
"host" varchar(128),
|
"host" varchar(128),
|
||||||
"serverId" integer
|
"serverId" integer
|
||||||
@@ -496,6 +523,11 @@ ON "` + this.itemsTableName + `" (
|
|||||||
"expiredAt" ASC
|
"expiredAt" ASC
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "staleAt"
|
||||||
|
ON "` + this.itemsTableName + `" (
|
||||||
|
"staleAt" ASC
|
||||||
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "hash"
|
CREATE UNIQUE INDEX IF NOT EXISTS "hash"
|
||||||
ON "` + this.itemsTableName + `" (
|
ON "` + this.itemsTableName + `" (
|
||||||
"hash" ASC
|
"hash" ASC
|
||||||
@@ -506,6 +538,7 @@ ON "` + this.itemsTableName + `" (
|
|||||||
"serverId" ASC
|
"serverId" ASC
|
||||||
);
|
);
|
||||||
`)
|
`)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 尝试删除重建
|
// 尝试删除重建
|
||||||
if times < 3 {
|
if times < 3 {
|
||||||
@@ -520,6 +553,19 @@ ON "` + this.itemsTableName + `" (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果数据为空,从老数据中加载数据
|
||||||
|
if notFound {
|
||||||
|
// v2 => v3
|
||||||
|
remotelogs.Println("CACHE", "transferring old data from v2 to v3 ...")
|
||||||
|
result, err := db.Exec(`INSERT INTO "` + this.itemsTableName + `" ("id", "hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "createdAt", "host", "serverId", "staleAt") SELECT "id", "hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "createdAt", "host", "serverId", "expiredAt"+600 FROM cacheItems_v2`)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Println("CACHE", "transfer old data from v2 to v3 failed: "+err.Error())
|
||||||
|
} else {
|
||||||
|
count, _ := result.RowsAffected()
|
||||||
|
remotelogs.Println("CACHE", "transfer old data from v2 to v3 finished, "+types.String(count)+" rows transferred")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.hitsTableName + `" (
|
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.hitsTableName + `" (
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -568,11 +614,11 @@ func (this *FileList) removeOldTables() error {
|
|||||||
}
|
}
|
||||||
if lists.ContainsString(this.oldTables, name) {
|
if lists.ContainsString(this.oldTables, name) {
|
||||||
// 异步执行
|
// 异步执行
|
||||||
go func() {
|
goman.New(func() {
|
||||||
remotelogs.Println("CACHE", "remove old table '"+name+"' ...")
|
remotelogs.Println("CACHE", "remove old table '"+name+"' ...")
|
||||||
_, _ = this.db.Exec(`DROP TABLE "` + name + `"`)
|
_, _ = this.db.Exec(`DROP TABLE "` + name + `"`)
|
||||||
remotelogs.Println("CACHE", "remove old table '"+name+"' done")
|
remotelogs.Println("CACHE", "remove old table '"+name+"' done")
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package caches
|
package caches
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -127,7 +128,7 @@ func TestFileList_Exist_Many_DB(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
for i := 0; i < threads; i++ {
|
for i := 0; i < threads; i++ {
|
||||||
go func() {
|
goman.New(func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -143,7 +144,7 @@ func TestFileList_Exist_Many_DB(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
t.Log("left:", count)
|
t.Log("left:", count)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package caches
|
package caches
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -15,7 +16,7 @@ type MemoryList struct {
|
|||||||
|
|
||||||
itemMaps map[string]map[string]*Item // prefix => { hash => item }
|
itemMaps map[string]map[string]*Item // prefix => { hash => item }
|
||||||
|
|
||||||
weekItemMaps map[int32]map[string]bool // week => { hash => true }
|
weekItemMaps map[int32]map[string]zero.Zero // week => { hash => Zero }
|
||||||
minWeek int32
|
minWeek int32
|
||||||
|
|
||||||
prefixes []string
|
prefixes []string
|
||||||
@@ -29,7 +30,7 @@ type MemoryList struct {
|
|||||||
func NewMemoryList() ListInterface {
|
func NewMemoryList() ListInterface {
|
||||||
return &MemoryList{
|
return &MemoryList{
|
||||||
itemMaps: map[string]map[string]*Item{},
|
itemMaps: map[string]map[string]*Item{},
|
||||||
weekItemMaps: map[int32]map[string]bool{},
|
weekItemMaps: map[int32]map[string]zero.Zero{},
|
||||||
minWeek: currentWeek(),
|
minWeek: currentWeek(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,7 @@ func (this *MemoryList) Reset() error {
|
|||||||
for key := range this.itemMaps {
|
for key := range this.itemMaps {
|
||||||
this.itemMaps[key] = map[string]*Item{}
|
this.itemMaps[key] = map[string]*Item{}
|
||||||
}
|
}
|
||||||
this.weekItemMaps = map[int32]map[string]bool{}
|
this.weekItemMaps = map[int32]map[string]zero.Zero{}
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|
||||||
atomic.StoreInt64(&this.count, 0)
|
atomic.StoreInt64(&this.count, 0)
|
||||||
@@ -103,9 +104,9 @@ func (this *MemoryList) Add(hash string, item *Item) error {
|
|||||||
// week map
|
// week map
|
||||||
wm, ok := this.weekItemMaps[item.Week]
|
wm, ok := this.weekItemMaps[item.Week]
|
||||||
if ok {
|
if ok {
|
||||||
wm[hash] = true
|
wm[hash] = zero.New()
|
||||||
} else {
|
} else {
|
||||||
this.weekItemMaps[item.Week] = map[string]bool{hash: true}
|
this.weekItemMaps[item.Week] = map[string]zero.Zero{hash: zero.New()}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
@@ -381,9 +382,9 @@ func (this *MemoryList) IncreaseHit(hash string) error {
|
|||||||
}
|
}
|
||||||
wm, ok = this.weekItemMaps[week]
|
wm, ok = this.weekItemMaps[week]
|
||||||
if ok {
|
if ok {
|
||||||
wm[hash] = true
|
wm[hash] = zero.New()
|
||||||
} else {
|
} else {
|
||||||
this.weekItemMaps[week] = map[string]bool{hash: true}
|
this.weekItemMaps[week] = map[string]zero.Zero{hash: zero.New()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package caches
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -12,6 +14,13 @@ import (
|
|||||||
|
|
||||||
var SharedManager = NewManager()
|
var SharedManager = NewManager()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
events.On(events.EventQuit, func() {
|
||||||
|
logs.Println("CACHE", "quiting cache manager")
|
||||||
|
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Manager 缓存策略管理器
|
// Manager 缓存策略管理器
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
// 全局配置
|
// 全局配置
|
||||||
@@ -25,10 +34,12 @@ type Manager struct {
|
|||||||
|
|
||||||
// NewManager 获取管理器对象
|
// NewManager 获取管理器对象
|
||||||
func NewManager() *Manager {
|
func NewManager() *Manager {
|
||||||
return &Manager{
|
var m = &Manager{
|
||||||
policyMap: map[int64]*serverconfigs.HTTPCachePolicy{},
|
policyMap: map[int64]*serverconfigs.HTTPCachePolicy{},
|
||||||
storageMap: map[int64]StorageInterface{},
|
storageMap: map[int64]StorageInterface{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePolicies 重新设置策略
|
// UpdatePolicies 重新设置策略
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -55,7 +57,7 @@ type FileStorage struct {
|
|||||||
totalSize int64
|
totalSize int64
|
||||||
|
|
||||||
list ListInterface
|
list ListInterface
|
||||||
writingKeyMap map[string]bool // key => bool
|
writingKeyMap map[string]zero.Zero // key => bool
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
purgeTicker *utils.Ticker
|
purgeTicker *utils.Ticker
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ type FileStorage struct {
|
|||||||
func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage {
|
func NewFileStorage(policy *serverconfigs.HTTPCachePolicy) *FileStorage {
|
||||||
return &FileStorage{
|
return &FileStorage{
|
||||||
policy: policy,
|
policy: policy,
|
||||||
writingKeyMap: map[string]bool{},
|
writingKeyMap: map[string]zero.Zero{},
|
||||||
hotMap: map[string]*HotItem{},
|
hotMap: map[string]*HotItem{},
|
||||||
lastHotSize: -1,
|
lastHotSize: -1,
|
||||||
}
|
}
|
||||||
@@ -202,14 +204,19 @@ func (this *FileStorage) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileStorage) OpenReader(key string) (Reader, error) {
|
func (this *FileStorage) OpenReader(key string, useStale bool) (Reader, error) {
|
||||||
return this.openReader(key, true)
|
return this.openReader(key, true, useStale)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error) {
|
func (this *FileStorage) openReader(key string, allowMemory bool, useStale bool) (Reader, error) {
|
||||||
|
// 使用陈旧缓存的时候,我们认为是短暂的,只需要从文件里检查即可
|
||||||
|
if useStale {
|
||||||
|
allowMemory = false
|
||||||
|
}
|
||||||
|
|
||||||
// 先尝试内存缓存
|
// 先尝试内存缓存
|
||||||
if allowMemory && this.memoryStorage != nil {
|
if allowMemory && this.memoryStorage != nil {
|
||||||
reader, err := this.memoryStorage.OpenReader(key)
|
reader, err := this.memoryStorage.OpenReader(key, useStale)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return reader, err
|
return reader, err
|
||||||
}
|
}
|
||||||
@@ -217,6 +224,17 @@ func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error
|
|||||||
|
|
||||||
hash, path := this.keyPath(key)
|
hash, path := this.keyPath(key)
|
||||||
|
|
||||||
|
// 检查文件记录是否已过期
|
||||||
|
if !useStale {
|
||||||
|
exists, err := this.list.Exist(hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO 尝试使用mmap加快读取速度
|
// TODO 尝试使用mmap加快读取速度
|
||||||
var isOk = false
|
var isOk = false
|
||||||
fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
|
fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
|
||||||
@@ -233,15 +251,6 @@ func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 检查文件记录是否已过期
|
|
||||||
exists, err := this.list.Exist(hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := NewFileReader(fp)
|
reader := NewFileReader(fp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -313,7 +322,7 @@ func (this *FileStorage) OpenWriter(key string, expiredAt int64, status int) (Wr
|
|||||||
return nil, ErrFileIsWriting
|
return nil, ErrFileIsWriting
|
||||||
}
|
}
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
this.writingKeyMap[key] = true
|
this.writingKeyMap[key] = zero.New()
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
defer func() {
|
defer func() {
|
||||||
if !isWriting {
|
if !isWriting {
|
||||||
@@ -562,12 +571,12 @@ func (this *FileStorage) CleanAll() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 重新遍历待删除
|
// 重新遍历待删除
|
||||||
go func() {
|
goman.New(func() {
|
||||||
err = this.cleanDeletedDirs(dir)
|
err = this.cleanDeletedDirs(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Warn("CACHE", "delete '*-deleted' dirs failed: "+err.Error())
|
remotelogs.Warn("CACHE", "delete '*-deleted' dirs failed: "+err.Error())
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -672,12 +681,12 @@ func (this *FileStorage) initList() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用异步防止阻塞主线程
|
// 使用异步防止阻塞主线程
|
||||||
/**go func() {
|
/**goman.New(func() {
|
||||||
dir := this.dir()
|
dir := this.dir()
|
||||||
|
|
||||||
// 清除tmp
|
// 清除tmp
|
||||||
// TODO 需要一个更加高效的实现
|
// TODO 需要一个更加高效的实现
|
||||||
}()**/
|
})**/
|
||||||
|
|
||||||
// 启动定时清理任务
|
// 启动定时清理任务
|
||||||
var autoPurgeInterval = this.policy.PersistenceAutoPurgeInterval
|
var autoPurgeInterval = this.policy.PersistenceAutoPurgeInterval
|
||||||
@@ -695,26 +704,26 @@ func (this *FileStorage) initList() error {
|
|||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.purgeTicker.Next() {
|
for this.purgeTicker.Next() {
|
||||||
trackers.Run("FILE_CACHE_STORAGE_PURGE_LOOP", func() {
|
trackers.Run("FILE_CACHE_STORAGE_PURGE_LOOP", func() {
|
||||||
this.purgeLoop()
|
this.purgeLoop()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
// 热点处理任务
|
// 热点处理任务
|
||||||
this.hotTicker = utils.NewTicker(1 * time.Minute)
|
this.hotTicker = utils.NewTicker(1 * time.Minute)
|
||||||
if Tea.IsTesting() {
|
if Tea.IsTesting() {
|
||||||
this.hotTicker = utils.NewTicker(10 * time.Second)
|
this.hotTicker = utils.NewTicker(10 * time.Second)
|
||||||
}
|
}
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.hotTicker.Next() {
|
for this.hotTicker.Next() {
|
||||||
trackers.Run("FILE_CACHE_STORAGE_HOT_LOOP", func() {
|
trackers.Run("FILE_CACHE_STORAGE_HOT_LOOP", func() {
|
||||||
this.hotLoop()
|
this.hotLoop()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -794,9 +803,9 @@ func (this *FileStorage) decodeFile(path string) (*Item, error) {
|
|||||||
|
|
||||||
// URL
|
// URL
|
||||||
if urlSize > 0 {
|
if urlSize > 0 {
|
||||||
data := utils.BytePool1024.Get()
|
data := utils.BytePool1k.Get()
|
||||||
result, ok, err := this.readN(fp, data, int(urlSize))
|
result, ok, err := this.readN(fp, data, int(urlSize))
|
||||||
utils.BytePool1024.Put(data)
|
utils.BytePool1k.Put(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -921,7 +930,7 @@ func (this *FileStorage) hotLoop() {
|
|||||||
this.hotMap = map[string]*HotItem{}
|
this.hotMap = map[string]*HotItem{}
|
||||||
this.hotMapLocker.Unlock()
|
this.hotMapLocker.Unlock()
|
||||||
|
|
||||||
// 取Top10
|
// 取Top10写入内存
|
||||||
if len(result) > 0 {
|
if len(result) > 0 {
|
||||||
sort.Slice(result, func(i, j int) bool {
|
sort.Slice(result, func(i, j int) bool {
|
||||||
return result[i].Hits > result[j].Hits
|
return result[i].Hits > result[j].Hits
|
||||||
@@ -933,9 +942,10 @@ func (this *FileStorage) hotLoop() {
|
|||||||
size = len(result) / 10
|
size = len(result) / 10
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf = make([]byte, 32*1024)
|
var buf = utils.BytePool16k.Get()
|
||||||
|
defer utils.BytePool16k.Put(buf)
|
||||||
for _, item := range result[:size] {
|
for _, item := range result[:size] {
|
||||||
reader, err := this.openReader(item.Key, false)
|
reader, err := this.openReader(item.Key, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ type StorageInterface interface {
|
|||||||
Init() error
|
Init() error
|
||||||
|
|
||||||
// OpenReader 读取缓存
|
// OpenReader 读取缓存
|
||||||
OpenReader(key string) (Reader, error)
|
OpenReader(key string, useStale bool) (reader Reader, err error)
|
||||||
|
|
||||||
// OpenWriter 打开缓存写入器等待写入
|
// OpenWriter 打开缓存写入器等待写入
|
||||||
OpenWriter(key string, expiredAt int64, status int) (Writer, error)
|
OpenWriter(key string, expiredAt int64, status int) (Writer, error)
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package caches
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -43,7 +45,7 @@ type MemoryStorage struct {
|
|||||||
purgeTicker *utils.Ticker
|
purgeTicker *utils.Ticker
|
||||||
|
|
||||||
totalSize int64
|
totalSize int64
|
||||||
writingKeyMap map[string]bool // key => bool
|
writingKeyMap map[string]zero.Zero // key => bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemoryStorage(policy *serverconfigs.HTTPCachePolicy, parentStorage StorageInterface) *MemoryStorage {
|
func NewMemoryStorage(policy *serverconfigs.HTTPCachePolicy, parentStorage StorageInterface) *MemoryStorage {
|
||||||
@@ -62,7 +64,7 @@ func NewMemoryStorage(policy *serverconfigs.HTTPCachePolicy, parentStorage Stora
|
|||||||
locker: &sync.RWMutex{},
|
locker: &sync.RWMutex{},
|
||||||
valuesMap: map[uint64]*MemoryItem{},
|
valuesMap: map[uint64]*MemoryItem{},
|
||||||
dirtyChan: dirtyChan,
|
dirtyChan: dirtyChan,
|
||||||
writingKeyMap: map[string]bool{},
|
writingKeyMap: map[string]zero.Zero{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,26 +86,26 @@ func (this *MemoryStorage) Init() error {
|
|||||||
|
|
||||||
// 启动定时清理任务
|
// 启动定时清理任务
|
||||||
this.purgeTicker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second)
|
this.purgeTicker = utils.NewTicker(time.Duration(autoPurgeInterval) * time.Second)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.purgeTicker.Next() {
|
for this.purgeTicker.Next() {
|
||||||
var tr = trackers.Begin("MEMORY_CACHE_STORAGE_PURGE_LOOP")
|
var tr = trackers.Begin("MEMORY_CACHE_STORAGE_PURGE_LOOP")
|
||||||
this.purgeLoop()
|
this.purgeLoop()
|
||||||
tr.End()
|
tr.End()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
// 启动定时Flush memory to disk任务
|
// 启动定时Flush memory to disk任务
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for hash := range this.dirtyChan {
|
for hash := range this.dirtyChan {
|
||||||
this.flushItem(hash)
|
this.flushItem(hash)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenReader 读取缓存
|
// OpenReader 读取缓存
|
||||||
func (this *MemoryStorage) OpenReader(key string) (Reader, error) {
|
func (this *MemoryStorage) OpenReader(key string, useStale bool) (Reader, error) {
|
||||||
hash := this.hash(key)
|
hash := this.hash(key)
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
@@ -113,7 +115,7 @@ func (this *MemoryStorage) OpenReader(key string) (Reader, error) {
|
|||||||
return nil, ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.ExpiredAt > utils.UnixTime() {
|
if useStale || (item.ExpiredAt > utils.UnixTime()) {
|
||||||
reader := NewMemoryReader(item)
|
reader := NewMemoryReader(item)
|
||||||
err := reader.Init()
|
err := reader.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,7 +159,7 @@ func (this *MemoryStorage) openWriter(key string, expiredAt int64, status int, i
|
|||||||
if ok {
|
if ok {
|
||||||
return nil, ErrFileIsWriting
|
return nil, ErrFileIsWriting
|
||||||
}
|
}
|
||||||
this.writingKeyMap[key] = true
|
this.writingKeyMap[key] = zero.New()
|
||||||
defer func() {
|
defer func() {
|
||||||
if !isWriting {
|
if !isWriting {
|
||||||
delete(this.writingKeyMap, key)
|
delete(this.writingKeyMap, key)
|
||||||
@@ -254,7 +256,7 @@ func (this *MemoryStorage) Stop() {
|
|||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
|
|
||||||
this.valuesMap = map[uint64]*MemoryItem{}
|
this.valuesMap = map[uint64]*MemoryItem{}
|
||||||
this.writingKeyMap = map[string]bool{}
|
this.writingKeyMap = map[string]zero.Zero{}
|
||||||
_ = this.list.Reset()
|
_ = this.list.Reset()
|
||||||
if this.purgeTicker != nil {
|
if this.purgeTicker != nil {
|
||||||
this.purgeTicker.Stop()
|
this.purgeTicker.Stop()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.3.6"
|
Version = "0.3.7"
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ var (
|
|||||||
InTrafficBytes = uint64(0)
|
InTrafficBytes = uint64(0)
|
||||||
OutTrafficBytes = uint64(0)
|
OutTrafficBytes = uint64(0)
|
||||||
|
|
||||||
NodeId int64 = 0
|
NodeId int64 = 0
|
||||||
|
NodeIdString = ""
|
||||||
)
|
)
|
||||||
|
|||||||
12
internal/goman/instance.go
Normal file
12
internal/goman/instance.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package goman
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Instance struct {
|
||||||
|
Id uint64
|
||||||
|
CreatedTime time.Time
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
}
|
||||||
81
internal/goman/lib.go
Normal file
81
internal/goman/lib.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package goman
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var locker = &sync.Mutex{}
|
||||||
|
var instanceMap = map[uint64]*Instance{} // id => *Instance
|
||||||
|
var instanceId = uint64(0)
|
||||||
|
|
||||||
|
// New 新创建goroutine
|
||||||
|
func New(f func()) {
|
||||||
|
_, file, line, _ := runtime.Caller(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
locker.Lock()
|
||||||
|
instanceId++
|
||||||
|
|
||||||
|
var instance = &Instance{
|
||||||
|
Id: instanceId,
|
||||||
|
CreatedTime: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.File = file
|
||||||
|
instance.Line = line
|
||||||
|
|
||||||
|
instanceMap[instanceId] = instance
|
||||||
|
locker.Unlock()
|
||||||
|
|
||||||
|
// run function
|
||||||
|
f()
|
||||||
|
|
||||||
|
locker.Lock()
|
||||||
|
delete(instanceMap, instanceId)
|
||||||
|
locker.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithArgs 创建带有参数的goroutine
|
||||||
|
func NewWithArgs(f func(args ...interface{}), args ...interface{}) {
|
||||||
|
_, file, line, _ := runtime.Caller(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
locker.Lock()
|
||||||
|
instanceId++
|
||||||
|
|
||||||
|
var instance = &Instance{
|
||||||
|
Id: instanceId,
|
||||||
|
CreatedTime: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.File = file
|
||||||
|
instance.Line = line
|
||||||
|
|
||||||
|
instanceMap[instanceId] = instance
|
||||||
|
locker.Unlock()
|
||||||
|
|
||||||
|
// run function
|
||||||
|
f(args...)
|
||||||
|
|
||||||
|
locker.Lock()
|
||||||
|
delete(instanceMap, instanceId)
|
||||||
|
locker.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 列出所有正在运行goroutine
|
||||||
|
func List() []*Instance {
|
||||||
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
|
|
||||||
|
var result = []*Instance{}
|
||||||
|
for _, instance := range instanceMap {
|
||||||
|
result = append(result, instance)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
28
internal/goman/lib_test.go
Normal file
28
internal/goman/lib_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package goman
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
New(func() {
|
||||||
|
t.Log("Hello")
|
||||||
|
|
||||||
|
t.Log(List())
|
||||||
|
})
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
t.Log(List())
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewWithArgs(t *testing.T) {
|
||||||
|
NewWithArgs(func(args ...interface{}) {
|
||||||
|
t.Log(args[0], args[1])
|
||||||
|
}, 1, 2)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
130
internal/iplibrary/ip2Region.go
Normal file
130
internal/iplibrary/ip2Region.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// 源码改自:https://github.com/lionsoul2014/ip2region/blob/master/binding/golang/ip2region/ip2Region.go
|
||||||
|
|
||||||
|
package iplibrary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IndexBlockLength = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
type IP2Region struct {
|
||||||
|
headerSip []int64
|
||||||
|
headerPtr []int64
|
||||||
|
headerLen int64
|
||||||
|
|
||||||
|
// super block index info
|
||||||
|
firstIndexPtr int64
|
||||||
|
lastIndexPtr int64
|
||||||
|
totalBlocks int64
|
||||||
|
|
||||||
|
dbData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type IpInfo struct {
|
||||||
|
CityId int64
|
||||||
|
Country string
|
||||||
|
Region string
|
||||||
|
Province string
|
||||||
|
City string
|
||||||
|
ISP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ip IpInfo) String() string {
|
||||||
|
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIpInfo(cityId int64, line []byte) *IpInfo {
|
||||||
|
lineSlice := strings.Split(string(line), "|")
|
||||||
|
ipInfo := &IpInfo{}
|
||||||
|
length := len(lineSlice)
|
||||||
|
ipInfo.CityId = cityId
|
||||||
|
if length < 5 {
|
||||||
|
for i := 0; i <= 5-length; i++ {
|
||||||
|
lineSlice = append(lineSlice, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipInfo.Country = lineSlice[0]
|
||||||
|
ipInfo.Region = lineSlice[1]
|
||||||
|
ipInfo.Province = lineSlice[2]
|
||||||
|
ipInfo.City = lineSlice[3]
|
||||||
|
ipInfo.ISP = lineSlice[4]
|
||||||
|
return ipInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIP2Region(path string) (*IP2Region, error) {
|
||||||
|
var region = &IP2Region{}
|
||||||
|
region.dbData, err = ioutil.ReadFile(path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
region.firstIndexPtr = region.ipLongAtOffset(0)
|
||||||
|
region.lastIndexPtr = region.ipLongAtOffset(4)
|
||||||
|
region.totalBlocks = (region.lastIndexPtr-region.firstIndexPtr)/IndexBlockLength + 1
|
||||||
|
return region, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IP2Region) MemorySearch(ipStr string) (ipInfo *IpInfo, err error) {
|
||||||
|
ip, err := ip2long(ipStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := this.totalBlocks
|
||||||
|
var dataPtr, l int64
|
||||||
|
for l <= h {
|
||||||
|
m := (l + h) >> 1
|
||||||
|
p := this.firstIndexPtr + m*IndexBlockLength
|
||||||
|
sip := this.ipLongAtOffset(p)
|
||||||
|
if ip < sip {
|
||||||
|
h = m - 1
|
||||||
|
} else {
|
||||||
|
eip := this.ipLongAtOffset(p + 4)
|
||||||
|
if ip > eip {
|
||||||
|
l = m + 1
|
||||||
|
} else {
|
||||||
|
dataPtr = this.ipLongAtOffset(p + 8)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dataPtr == 0 {
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLen := (dataPtr >> 24) & 0xFF
|
||||||
|
dataPtr = dataPtr & 0x00FFFFFF
|
||||||
|
return getIpInfo(this.ipLongAtOffset(dataPtr), this.dbData[(dataPtr)+4:dataPtr+dataLen]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IP2Region) ipLongAtOffset(offset int64) int64 {
|
||||||
|
return int64(this.dbData[offset]) |
|
||||||
|
int64(this.dbData[offset+1])<<8 |
|
||||||
|
int64(this.dbData[offset+2])<<16 |
|
||||||
|
int64(this.dbData[offset+3])<<24
|
||||||
|
}
|
||||||
|
|
||||||
|
func ip2long(IpStr string) (int64, error) {
|
||||||
|
bits := strings.Split(IpStr, ".")
|
||||||
|
if len(bits) != 4 {
|
||||||
|
return 0, errors.New("ip format error")
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum int64
|
||||||
|
for i, n := range bits {
|
||||||
|
bit, _ := strconv.ParseInt(n, 10, 64)
|
||||||
|
sum += bit << uint(24-8*i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum, nil
|
||||||
|
}
|
||||||
@@ -29,11 +29,9 @@ func NewIPList() *IPList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expireList := expires.NewList()
|
expireList := expires.NewList()
|
||||||
go func() {
|
expireList.OnGC(func(itemId int64) {
|
||||||
expireList.StartGC(func(itemId int64) {
|
list.Delete(itemId)
|
||||||
list.Delete(itemId)
|
})
|
||||||
})
|
|
||||||
}()
|
|
||||||
list.expireList = expireList
|
list.expireList = expireList
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -281,6 +283,22 @@ func TestGC(t *testing.T) {
|
|||||||
logs.PrintAsJSON(list.sortedItems, t)
|
logs.PrintAsJSON(list.sortedItems, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTooManyLists(t *testing.T) {
|
||||||
|
debug.SetMaxThreads(20)
|
||||||
|
|
||||||
|
var lists = []*IPList{}
|
||||||
|
var locker = &sync.Mutex{}
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
locker.Lock()
|
||||||
|
lists = append(lists, NewIPList())
|
||||||
|
locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
t.Log(runtime.NumGoroutine())
|
||||||
|
t.Log(len(lists), "lists")
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkIPList_Contains(b *testing.B) {
|
func BenchmarkIPList_Contains(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/lionsoul2014/ip2region/binding/golang/ip2region"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IP2RegionLibrary struct {
|
type IP2RegionLibrary struct {
|
||||||
db *ip2region.Ip2Region
|
db *IP2Region
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IP2RegionLibrary) Load(dbPath string) error {
|
func (this *IP2RegionLibrary) Load(dbPath string) error {
|
||||||
db, err := ip2region.New(dbPath)
|
db, err := NewIP2Region(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -49,6 +48,10 @@ func (this *IP2RegionLibrary) Lookup(ip string) (*Result, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
if info.Country == "0" {
|
if info.Country == "0" {
|
||||||
info.Country = ""
|
info.Country = ""
|
||||||
}
|
}
|
||||||
@@ -76,7 +79,5 @@ func (this *IP2RegionLibrary) Lookup(ip string) (*Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IP2RegionLibrary) Close() {
|
func (this *IP2RegionLibrary) Close() {
|
||||||
if this.db != nil {
|
|
||||||
this.db.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,25 +3,83 @@ package iplibrary
|
|||||||
import (
|
import (
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/logs"
|
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestIP2RegionLibrary_Lookup_MemoryUsage(t *testing.T) {
|
||||||
|
var mem = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(mem)
|
||||||
|
|
||||||
|
library := &IP2RegionLibrary{}
|
||||||
|
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mem2 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(mem2)
|
||||||
|
t.Log((mem2.HeapInuse-mem.HeapInuse)/1024/1024, "MB")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIP2RegionLibrary_Lookup_Single(t *testing.T) {
|
||||||
|
library := &IP2RegionLibrary{}
|
||||||
|
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range []string{"8.8.9.9"} {
|
||||||
|
result, err := library.Lookup(ip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("IP:", ip, "result:", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Lookup(t *testing.T) {
|
func TestIP2RegionLibrary_Lookup(t *testing.T) {
|
||||||
library := &IP2RegionLibrary{}
|
library := &IP2RegionLibrary{}
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
result, err := library.Lookup("114.240.223.47")
|
|
||||||
|
for _, ip := range []string{"", "a", "1.1.1", "192.168.1.100", "114.240.223.47", "8.8.9.9", "::1"} {
|
||||||
|
result, err := library.Lookup(ip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("IP:", ip, "result:", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIP2RegionLibrary_Lookup_Concurrent(t *testing.T) {
|
||||||
|
library := &IP2RegionLibrary{}
|
||||||
|
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
logs.PrintAsJSON(result, t)
|
|
||||||
|
var count = 4000
|
||||||
|
var wg = sync.WaitGroup{}
|
||||||
|
wg.Add(count)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
_, _ = library.Lookup(strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Done()
|
||||||
|
t.Log("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Memory(t *testing.T) {
|
func TestIP2RegionLibrary_Memory(t *testing.T) {
|
||||||
@@ -43,13 +101,13 @@ func TestIP2RegionLibrary_Memory(t *testing.T) {
|
|||||||
func BenchmarkIP2RegionLibrary_Lookup(b *testing.B) {
|
func BenchmarkIP2RegionLibrary_Lookup(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
library := &IP2RegionLibrary{}
|
var library = &IP2RegionLibrary{}
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = library.Lookup(strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)))
|
_, _ = library.Lookup("8.8.8.8")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -21,7 +22,9 @@ var SharedCountryManager = NewCountryManager()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
go SharedCountryManager.Start()
|
goman.New(func() {
|
||||||
|
SharedCountryManager.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ func (this *CountryManager) Start() {
|
|||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
})
|
})
|
||||||
for range ticker.C {
|
for ticker.Next() {
|
||||||
err := this.loop()
|
err := this.loop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ErrorObject("COUNTRY_MANAGER", err)
|
remotelogs.ErrorObject("COUNTRY_MANAGER", err)
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package iplibrary
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -17,7 +19,9 @@ var IPListUpdateNotify = make(chan bool, 1)
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
go SharedIPListManager.Start()
|
goman.New(func() {
|
||||||
|
SharedIPListManager.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +165,7 @@ func (this *IPListManager) FindList(listId int64) *IPList {
|
|||||||
|
|
||||||
func (this *IPListManager) processItems(items []*pb.IPItem, shouldExecute bool) {
|
func (this *IPListManager) processItems(items []*pb.IPItem, shouldExecute bool) {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
var changedLists = map[*IPList]bool{}
|
var changedLists = map[*IPList]zero.Zero{}
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
var list *IPList
|
var list *IPList
|
||||||
// TODO 实现节点专有List
|
// TODO 实现节点专有List
|
||||||
@@ -187,7 +191,7 @@ func (this *IPListManager) processItems(items []*pb.IPItem, shouldExecute bool)
|
|||||||
this.listMap[item.ListId] = list
|
this.listMap[item.ListId] = list
|
||||||
}
|
}
|
||||||
|
|
||||||
changedLists[list] = true
|
changedLists[list] = zero.New()
|
||||||
|
|
||||||
if item.IsDeleted {
|
if item.IsDeleted {
|
||||||
list.Delete(item.Id)
|
list.Delete(item.Id)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -25,7 +26,9 @@ var SharedProvinceManager = NewProvinceManager()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
go SharedProvinceManager.Start()
|
goman.New(func() {
|
||||||
|
SharedProvinceManager.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +67,7 @@ func (this *ProvinceManager) Start() {
|
|||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
})
|
})
|
||||||
for range ticker.C {
|
for ticker.Next() {
|
||||||
err := this.loop()
|
err := this.loop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ErrorObject("PROVINCE_MANAGER", err)
|
remotelogs.ErrorObject("PROVINCE_MANAGER", err)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
@@ -34,14 +35,14 @@ func NewUpdater() *Updater {
|
|||||||
func (this *Updater) Start() {
|
func (this *Updater) Start() {
|
||||||
// 这里不需要太频繁检查更新,因为通常不需要更新IP库
|
// 这里不需要太频繁检查更新,因为通常不需要更新IP库
|
||||||
ticker := time.NewTicker(1 * time.Hour)
|
ticker := time.NewTicker(1 * time.Hour)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
err := this.loop()
|
err := this.loop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ErrorObject("IP_LIBRARY", err)
|
remotelogs.ErrorObject("IP_LIBRARY", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单次任务
|
// 单次任务
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"os"
|
"os"
|
||||||
@@ -51,8 +53,8 @@ type Task struct {
|
|||||||
selectTopStmt *sql.Stmt
|
selectTopStmt *sql.Stmt
|
||||||
sumStmt *sql.Stmt
|
sumStmt *sql.Stmt
|
||||||
|
|
||||||
serverIdMap map[int64]bool // 所有的服务Ids
|
serverIdMap map[int64]zero.Zero // 所有的服务Ids
|
||||||
timeMap map[string]bool // time => bool
|
timeMap map[string]zero.Zero // time => bool
|
||||||
serverIdMapLocker sync.Mutex
|
serverIdMapLocker sync.Mutex
|
||||||
|
|
||||||
statsMap map[string]*Stat
|
statsMap map[string]*Stat
|
||||||
@@ -64,8 +66,8 @@ type Task struct {
|
|||||||
func NewTask(item *serverconfigs.MetricItemConfig) *Task {
|
func NewTask(item *serverconfigs.MetricItemConfig) *Task {
|
||||||
return &Task{
|
return &Task{
|
||||||
item: item,
|
item: item,
|
||||||
serverIdMap: map[int64]bool{},
|
serverIdMap: map[int64]zero.Zero{},
|
||||||
timeMap: map[string]bool{},
|
timeMap: map[string]zero.Zero{},
|
||||||
statsMap: map[string]*Stat{},
|
statsMap: map[string]*Stat{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +165,7 @@ ON "` + this.statTableName + `" (
|
|||||||
func (this *Task) Start() error {
|
func (this *Task) Start() error {
|
||||||
// 读取数据
|
// 读取数据
|
||||||
this.statsTicker = utils.NewTicker(1 * time.Minute)
|
this.statsTicker = utils.NewTicker(1 * time.Minute)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.statsTicker.Next() {
|
for this.statsTicker.Next() {
|
||||||
var tr = trackers.Begin("[METRIC]DUMP_STATS_TO_LOCAL_DATABASE")
|
var tr = trackers.Begin("[METRIC]DUMP_STATS_TO_LOCAL_DATABASE")
|
||||||
|
|
||||||
@@ -181,11 +183,11 @@ func (this *Task) Start() error {
|
|||||||
|
|
||||||
tr.End()
|
tr.End()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
// 清理
|
// 清理
|
||||||
this.cleanTicker = utils.NewTicker(24 * time.Hour)
|
this.cleanTicker = utils.NewTicker(24 * time.Hour)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.cleanTicker.Next() {
|
for this.cleanTicker.Next() {
|
||||||
var tr = trackers.Begin("[METRIC]CLEAN_EXPIRED")
|
var tr = trackers.Begin("[METRIC]CLEAN_EXPIRED")
|
||||||
err := this.CleanExpired()
|
err := this.CleanExpired()
|
||||||
@@ -194,11 +196,11 @@ func (this *Task) Start() error {
|
|||||||
remotelogs.Error("METRIC", "clean expired stats failed: "+err.Error())
|
remotelogs.Error("METRIC", "clean expired stats failed: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
// 上传
|
// 上传
|
||||||
this.uploadTicker = utils.NewTicker(this.item.UploadDuration())
|
this.uploadTicker = utils.NewTicker(this.item.UploadDuration())
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.uploadTicker.Next() {
|
for this.uploadTicker.Next() {
|
||||||
var tr = trackers.Begin("[METRIC]UPLOAD_STATS")
|
var tr = trackers.Begin("[METRIC]UPLOAD_STATS")
|
||||||
err := this.Upload(1 * time.Second)
|
err := this.Upload(1 * time.Second)
|
||||||
@@ -207,7 +209,7 @@ func (this *Task) Start() error {
|
|||||||
remotelogs.Error("METRIC", "upload stats failed: "+err.Error())
|
remotelogs.Error("METRIC", "upload stats failed: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -293,8 +295,8 @@ func (this *Task) InsertStat(stat *Stat) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.serverIdMapLocker.Lock()
|
this.serverIdMapLocker.Lock()
|
||||||
this.serverIdMap[stat.ServerId] = true
|
this.serverIdMap[stat.ServerId] = zero.New()
|
||||||
this.timeMap[stat.Time] = true
|
this.timeMap[stat.Time] = zero.New()
|
||||||
this.serverIdMapLocker.Unlock()
|
this.serverIdMapLocker.Unlock()
|
||||||
|
|
||||||
keyData, err := json.Marshal(stat.Keys)
|
keyData, err := json.Marshal(stat.Keys)
|
||||||
@@ -346,14 +348,14 @@ func (this *Task) Upload(pauseDuration time.Duration) error {
|
|||||||
for serverId := range this.serverIdMap {
|
for serverId := range this.serverIdMap {
|
||||||
serverIds = append(serverIds, serverId)
|
serverIds = append(serverIds, serverId)
|
||||||
}
|
}
|
||||||
this.serverIdMap = map[int64]bool{} // 清空数据
|
this.serverIdMap = map[int64]zero.Zero{} // 清空数据
|
||||||
|
|
||||||
// 时间
|
// 时间
|
||||||
var times = []string{}
|
var times = []string{}
|
||||||
for t := range this.timeMap {
|
for t := range this.timeMap {
|
||||||
times = append(times, t)
|
times = append(times, t)
|
||||||
}
|
}
|
||||||
this.timeMap = map[string]bool{} // 清空数据
|
this.timeMap = map[string]zero.Zero{} // 清空数据
|
||||||
|
|
||||||
this.serverIdMapLocker.Unlock()
|
this.serverIdMapLocker.Unlock()
|
||||||
|
|
||||||
@@ -470,7 +472,7 @@ func (this *Task) loadServerIdMap() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
this.serverIdMapLocker.Lock()
|
this.serverIdMapLocker.Lock()
|
||||||
this.serverIdMap[serverId] = true
|
this.serverIdMap[serverId] = zero.New()
|
||||||
this.serverIdMapLocker.Unlock()
|
this.serverIdMapLocker.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -491,7 +493,7 @@ func (this *Task) loadServerIdMap() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
this.serverIdMapLocker.Lock()
|
this.serverIdMapLocker.Lock()
|
||||||
this.timeMap[timeString] = true
|
this.timeMap[timeString] = zero.New()
|
||||||
this.serverIdMapLocker.Unlock()
|
this.serverIdMapLocker.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -16,7 +17,9 @@ var SharedValueQueue = NewValueQueue()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
go SharedValueQueue.Start()
|
goman.New(func() {
|
||||||
|
SharedValueQueue.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -30,6 +31,9 @@ import (
|
|||||||
|
|
||||||
type APIStream struct {
|
type APIStream struct {
|
||||||
stream pb.NodeService_NodeStreamClient
|
stream pb.NodeService_NodeStreamClient
|
||||||
|
|
||||||
|
isQuiting bool
|
||||||
|
cancelFunc context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPIStream() *APIStream {
|
func NewAPIStream() *APIStream {
|
||||||
@@ -37,12 +41,14 @@ func NewAPIStream() *APIStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *APIStream) Start() {
|
func (this *APIStream) Start() {
|
||||||
isQuiting := false
|
|
||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
isQuiting = true
|
this.isQuiting = true
|
||||||
|
if this.cancelFunc != nil {
|
||||||
|
this.cancelFunc()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
for {
|
for {
|
||||||
if isQuiting {
|
if this.isQuiting {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := this.loop()
|
err := this.loop()
|
||||||
@@ -60,19 +66,17 @@ func (this *APIStream) loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
isQuiting := false
|
|
||||||
ctx, cancelFunc := context.WithCancel(rpcClient.Context())
|
|
||||||
nodeStream, err := rpcClient.NodeRPC().NodeStream(ctx)
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
isQuiting = true
|
|
||||||
|
|
||||||
remotelogs.Println("API_STREAM", "quiting")
|
ctx, cancelFunc := context.WithCancel(rpcClient.Context())
|
||||||
if nodeStream != nil {
|
this.cancelFunc = cancelFunc
|
||||||
cancelFunc()
|
|
||||||
}
|
defer func() {
|
||||||
})
|
cancelFunc()
|
||||||
|
}()
|
||||||
|
|
||||||
|
nodeStream, err := rpcClient.NodeRPC().NodeStream(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isQuiting {
|
if this.isQuiting {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
@@ -80,14 +84,14 @@ func (this *APIStream) loop() error {
|
|||||||
this.stream = nodeStream
|
this.stream = nodeStream
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if isQuiting {
|
if this.isQuiting {
|
||||||
remotelogs.Println("API_STREAM", "quit")
|
remotelogs.Println("API_STREAM", "quit")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
message, err := nodeStream.Recv()
|
message, err := nodeStream.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isQuiting {
|
if this.isQuiting {
|
||||||
remotelogs.Println("API_STREAM", "quit")
|
remotelogs.Println("API_STREAM", "quit")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -139,15 +143,11 @@ func (this *APIStream) handleConnectedAPINode(message *pb.NodeStreamMessage) err
|
|||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
_, err = rpc.SharedRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = rpcClient.NodeRPC().UpdateNodeConnectedAPINodes(rpcClient.Context(), &pb.UpdateNodeConnectedAPINodesRequest{ApiNodeIds: []int64{msg.APINodeId}})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
remotelogs.Println("API_STREAM", "connected to api node '"+strconv.FormatInt(msg.APINodeId, 10)+"'")
|
remotelogs.Println("API_STREAM", "connected to api node '"+strconv.FormatInt(msg.APINodeId, 10)+"'")
|
||||||
|
|
||||||
// 重新读取配置
|
// 重新读取配置
|
||||||
@@ -239,7 +239,7 @@ func (this *APIStream) handleReadCache(message *pb.NodeStreamMessage) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := storage.OpenReader(msg.Key)
|
reader, err := storage.OpenReader(msg.Key, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == caches.ErrNotFound {
|
if err == caches.ErrNotFound {
|
||||||
this.replyFail(message.RequestId, "key not found")
|
this.replyFail(message.RequestId, "key not found")
|
||||||
@@ -607,7 +607,7 @@ func (this *APIStream) handleChangeAPINode(message *pb.NodeStreamMessage) error
|
|||||||
|
|
||||||
this.replyOk(message.RequestId, "")
|
this.replyOk(message.RequestId, "")
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
// 延后生效,防止变更前的API无法读取到状态
|
// 延后生效,防止变更前的API无法读取到状态
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ func (this *APIStream) handleChangeAPINode(message *pb.NodeStreamMessage) error
|
|||||||
|
|
||||||
remotelogs.Println("API_STREAM", "change rpc endpoint to '"+
|
remotelogs.Println("API_STREAM", "change rpc endpoint to '"+
|
||||||
messageData.Addr+"' successfully")
|
messageData.Addr+"' successfully")
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,60 +5,50 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/ratelimit"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 发送监控流量
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventStart, func() {
|
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
|
||||||
go func() {
|
|
||||||
for range ticker.C {
|
|
||||||
// 加入到数据队列中
|
|
||||||
if teaconst.InTrafficBytes > 0 {
|
|
||||||
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemTrafficIn, maps.Map{
|
|
||||||
"total": teaconst.InTrafficBytes,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if teaconst.OutTrafficBytes > 0 {
|
|
||||||
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemTrafficOut, maps.Map{
|
|
||||||
"total": teaconst.OutTrafficBytes,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置数据
|
|
||||||
atomic.StoreUint64(&teaconst.InTrafficBytes, 0)
|
|
||||||
atomic.StoreUint64(&teaconst.OutTrafficBytes, 0)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientConn 客户端连接
|
// ClientConn 客户端连接
|
||||||
type ClientConn struct {
|
type ClientConn struct {
|
||||||
rawConn net.Conn
|
once sync.Once
|
||||||
isClosed bool
|
globalLimiter *ratelimit.Counter
|
||||||
|
|
||||||
|
isTLS bool
|
||||||
|
hasRead bool
|
||||||
|
|
||||||
|
BaseClientConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientConn(conn net.Conn, quickClose bool) net.Conn {
|
func NewClientConn(conn net.Conn, isTLS bool, quickClose bool, globalLimiter *ratelimit.Counter) net.Conn {
|
||||||
if quickClose {
|
if quickClose {
|
||||||
|
// TCP
|
||||||
tcpConn, ok := conn.(*net.TCPConn)
|
tcpConn, ok := conn.(*net.TCPConn)
|
||||||
if ok {
|
if ok {
|
||||||
// TODO 可以设置此值
|
// TODO 可以在配置中设置此值
|
||||||
_ = tcpConn.SetLinger(3)
|
_ = tcpConn.SetLinger(nodeconfigs.DefaultTCPLinger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ClientConn{rawConn: conn}
|
return &ClientConn{BaseClientConn: BaseClientConn{rawConn: conn}, isTLS: isTLS, globalLimiter: globalLimiter}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientConn) Read(b []byte) (n int, err error) {
|
func (this *ClientConn) Read(b []byte) (n int, err error) {
|
||||||
|
if this.isTLS {
|
||||||
|
if !this.hasRead {
|
||||||
|
_ = this.rawConn.SetReadDeadline(time.Now().Add(5 * time.Second)) // TODO 握手超时时间可以设置
|
||||||
|
this.hasRead = true
|
||||||
|
defer func() {
|
||||||
|
_ = this.rawConn.SetReadDeadline(time.Time{})
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
n, err = this.rawConn.Read(b)
|
n, err = this.rawConn.Read(b)
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n))
|
atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n))
|
||||||
}
|
}
|
||||||
@@ -75,6 +65,17 @@ func (this *ClientConn) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
func (this *ClientConn) Close() error {
|
func (this *ClientConn) Close() error {
|
||||||
this.isClosed = true
|
this.isClosed = true
|
||||||
|
|
||||||
|
// 全局并发数限制
|
||||||
|
this.once.Do(func() {
|
||||||
|
if this.globalLimiter != nil {
|
||||||
|
this.globalLimiter.Release()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 单个服务并发数限制
|
||||||
|
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
||||||
|
|
||||||
return this.rawConn.Close()
|
return this.rawConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +98,3 @@ func (this *ClientConn) SetReadDeadline(t time.Time) error {
|
|||||||
func (this *ClientConn) SetWriteDeadline(t time.Time) error {
|
func (this *ClientConn) SetWriteDeadline(t time.Time) error {
|
||||||
return this.rawConn.SetWriteDeadline(t)
|
return this.rawConn.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientConn) IsClosed() bool {
|
|
||||||
return this.isClosed
|
|
||||||
}
|
|
||||||
|
|||||||
38
internal/nodes/client_conn_base.go
Normal file
38
internal/nodes/client_conn_base.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
type BaseClientConn struct {
|
||||||
|
rawConn net.Conn
|
||||||
|
|
||||||
|
isBound bool
|
||||||
|
serverId int64
|
||||||
|
remoteAddr string
|
||||||
|
|
||||||
|
isClosed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *BaseClientConn) IsClosed() bool {
|
||||||
|
return this.isClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBound 是否已绑定服务
|
||||||
|
func (this *BaseClientConn) IsBound() bool {
|
||||||
|
return this.isBound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind 绑定服务
|
||||||
|
func (this *BaseClientConn) Bind(serverId int64, remoteAddr string, maxConnsPerServer int, maxConnsPerIP int) bool {
|
||||||
|
if this.isBound {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
this.isBound = true
|
||||||
|
this.serverId = serverId
|
||||||
|
this.remoteAddr = remoteAddr
|
||||||
|
|
||||||
|
// 检查是否可以连接
|
||||||
|
return sharedClientConnLimiter.Add(this.rawConn.RemoteAddr().String(), serverId, remoteAddr, maxConnsPerServer, maxConnsPerIP)
|
||||||
|
}
|
||||||
|
|
||||||
14
internal/nodes/client_conn_interface.go
Normal file
14
internal/nodes/client_conn_interface.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
type ClientConnInterface interface {
|
||||||
|
// IsClosed 是否已关闭
|
||||||
|
IsClosed() bool
|
||||||
|
|
||||||
|
// IsBound 是否已绑定服务
|
||||||
|
IsBound() bool
|
||||||
|
|
||||||
|
// Bind 绑定服务
|
||||||
|
Bind(serverId int64, remoteAddr string, maxConnsPerServer int, maxConnsPerIP int) bool
|
||||||
|
}
|
||||||
130
internal/nodes/client_conn_limiter.go
Normal file
130
internal/nodes/client_conn_limiter.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sharedClientConnLimiter = NewClientConnLimiter()
|
||||||
|
|
||||||
|
// ClientConnRemoteAddr 客户端地址定义
|
||||||
|
type ClientConnRemoteAddr struct {
|
||||||
|
remoteAddr string
|
||||||
|
serverId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientConnLimiter 客户端连接数限制
|
||||||
|
type ClientConnLimiter struct {
|
||||||
|
remoteAddrMap map[string]*ClientConnRemoteAddr // raw remote addr => remoteAddr
|
||||||
|
ipConns map[string]map[string]zero.Zero // remoteAddr => { raw remote addr => Zero }
|
||||||
|
serverConns map[int64]map[string]zero.Zero // serverId => { remoteAddr => Zero }
|
||||||
|
|
||||||
|
locker sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientConnLimiter() *ClientConnLimiter {
|
||||||
|
return &ClientConnLimiter{
|
||||||
|
remoteAddrMap: map[string]*ClientConnRemoteAddr{},
|
||||||
|
ipConns: map[string]map[string]zero.Zero{},
|
||||||
|
serverConns: map[int64]map[string]zero.Zero{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 添加新连接
|
||||||
|
// 返回值为true的时候表示允许添加;否则表示不允许添加
|
||||||
|
func (this *ClientConnLimiter) Add(rawRemoteAddr string, serverId int64, remoteAddr string, maxConnsPerServer int, maxConnsPerIP int) bool {
|
||||||
|
if (maxConnsPerServer <= 0 && maxConnsPerIP <= 0) || len(remoteAddr) == 0 || serverId <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
// 检查服务连接数
|
||||||
|
var serverMap = this.serverConns[serverId]
|
||||||
|
if maxConnsPerServer > 0 {
|
||||||
|
if serverMap == nil {
|
||||||
|
serverMap = map[string]zero.Zero{}
|
||||||
|
this.serverConns[serverId] = serverMap
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxConnsPerServer <= len(serverMap) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查IP连接数
|
||||||
|
var ipMap = this.ipConns[remoteAddr]
|
||||||
|
if maxConnsPerIP > 0 {
|
||||||
|
if ipMap == nil {
|
||||||
|
ipMap = map[string]zero.Zero{}
|
||||||
|
this.ipConns[remoteAddr] = ipMap
|
||||||
|
}
|
||||||
|
if maxConnsPerIP > 0 && maxConnsPerIP <= len(ipMap) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.remoteAddrMap[rawRemoteAddr] = &ClientConnRemoteAddr{
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
serverId: serverId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxConnsPerServer > 0 {
|
||||||
|
serverMap[rawRemoteAddr] = zero.New()
|
||||||
|
}
|
||||||
|
if maxConnsPerIP > 0 {
|
||||||
|
ipMap[rawRemoteAddr] = zero.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 删除连接
|
||||||
|
func (this *ClientConnLimiter) Remove(rawRemoteAddr string) {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
addr, ok := this.remoteAddrMap[rawRemoteAddr]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(this.remoteAddrMap, rawRemoteAddr)
|
||||||
|
delete(this.ipConns[addr.remoteAddr], rawRemoteAddr)
|
||||||
|
delete(this.serverConns[addr.serverId], rawRemoteAddr)
|
||||||
|
|
||||||
|
if len(this.ipConns[addr.remoteAddr]) == 0 {
|
||||||
|
delete(this.ipConns, addr.remoteAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(this.serverConns[addr.serverId]) == 0 {
|
||||||
|
delete(this.serverConns, addr.serverId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conns 获取连接信息
|
||||||
|
// 用于调试
|
||||||
|
func (this *ClientConnLimiter) Conns() (ipConns map[string][]string, serverConns map[int64][]string) {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
ipConns = map[string][]string{} // ip => [addr1, addr2, ...]
|
||||||
|
serverConns = map[int64][]string{} // serverId => [addr1, addr2, ...]
|
||||||
|
|
||||||
|
for ip, m := range this.ipConns {
|
||||||
|
for addr := range m {
|
||||||
|
ipConns[ip] = append(ipConns[ip], addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for serverId, m := range this.serverConns {
|
||||||
|
for addr := range m {
|
||||||
|
serverConns[serverId] = append(serverConns[serverId], addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
38
internal/nodes/client_conn_limiter_test.go
Normal file
38
internal/nodes/client_conn_limiter_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClientConnLimiter_Add(t *testing.T) {
|
||||||
|
var limiter = NewClientConnLimiter()
|
||||||
|
{
|
||||||
|
b := limiter.Add("127.0.0.1:1234", 1, "192.168.1.100", 10, 5)
|
||||||
|
t.Log(b)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b := limiter.Add("127.0.0.1:1235", 1, "192.168.1.100", 10, 5)
|
||||||
|
t.Log(b)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b := limiter.Add("127.0.0.1:1236", 1, "192.168.1.100", 10, 5)
|
||||||
|
t.Log(b)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b := limiter.Add("127.0.0.1:1237", 1, "192.168.1.101", 10, 5)
|
||||||
|
t.Log(b)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
b := limiter.Add("127.0.0.1:1238", 1, "192.168.1.100", 5, 5)
|
||||||
|
t.Log(b)
|
||||||
|
}
|
||||||
|
limiter.Remove("127.0.0.1:1238")
|
||||||
|
limiter.Remove("127.0.0.1:1239")
|
||||||
|
limiter.Remove("127.0.0.1:1237")
|
||||||
|
logs.PrintAsJSON(limiter.remoteAddrMap, t)
|
||||||
|
logs.PrintAsJSON(limiter.ipConns, t)
|
||||||
|
logs.PrintAsJSON(limiter.serverConns, t)
|
||||||
|
}
|
||||||
40
internal/nodes/client_conn_traffic.go
Normal file
40
internal/nodes/client_conn_traffic.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 发送监控流量
|
||||||
|
func init() {
|
||||||
|
events.On(events.EventStart, func() {
|
||||||
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
|
goman.New(func() {
|
||||||
|
for range ticker.C {
|
||||||
|
// 加入到数据队列中
|
||||||
|
if teaconst.InTrafficBytes > 0 {
|
||||||
|
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemTrafficIn, maps.Map{
|
||||||
|
"total": teaconst.InTrafficBytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if teaconst.OutTrafficBytes > 0 {
|
||||||
|
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemTrafficOut, maps.Map{
|
||||||
|
"total": teaconst.OutTrafficBytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置数据
|
||||||
|
atomic.StoreUint64(&teaconst.InTrafficBytes, 0)
|
||||||
|
atomic.StoreUint64(&teaconst.OutTrafficBytes, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -11,12 +11,10 @@ func isClientConnClosed(conn net.Conn) bool {
|
|||||||
if conn == nil {
|
if conn == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
clientConn, ok := conn.(*ClientConn)
|
clientConn, ok := conn.(ClientConnInterface)
|
||||||
if ok {
|
if ok {
|
||||||
return clientConn.IsClosed()
|
return clientConn.IsClosed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 解决tls.Conn无法获取底层连接对象的问题
|
return true
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,30 +3,49 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/ratelimit"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var sharedConnectionsLimiter = ratelimit.NewCounter(nodeconfigs.DefaultTCPMaxConnections)
|
||||||
|
|
||||||
// ClientListener 客户端网络监听
|
// ClientListener 客户端网络监听
|
||||||
type ClientListener struct {
|
type ClientListener struct {
|
||||||
rawListener net.Listener
|
rawListener net.Listener
|
||||||
|
isTLS bool
|
||||||
quickClose bool
|
quickClose bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientListener(listener net.Listener, quickClose bool) net.Listener {
|
func NewClientListener1(listener net.Listener, quickClose bool) *ClientListener {
|
||||||
return &ClientListener{
|
return &ClientListener{
|
||||||
rawListener: listener,
|
rawListener: listener,
|
||||||
quickClose: quickClose,
|
quickClose: quickClose,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ClientListener) SetIsTLS(isTLS bool) {
|
||||||
|
this.isTLS = isTLS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientListener) IsTLS() bool {
|
||||||
|
return this.isTLS
|
||||||
|
}
|
||||||
|
|
||||||
func (this *ClientListener) Accept() (net.Conn, error) {
|
func (this *ClientListener) Accept() (net.Conn, error) {
|
||||||
|
// 限制并发连接数
|
||||||
|
var limiter = sharedConnectionsLimiter
|
||||||
|
limiter.Ack()
|
||||||
|
|
||||||
conn, err := this.rawListener.Accept()
|
conn, err := this.rawListener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
limiter.Release()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是否在WAF名单中
|
// 是否在WAF名单中
|
||||||
ip, _, err := net.SplitHostPort(conn.RemoteAddr().String())
|
ip, _, err := net.SplitHostPort(conn.RemoteAddr().String())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -38,11 +57,12 @@ func (this *ClientListener) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
limiter.Release()
|
||||||
return this.Accept()
|
return this.Accept()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewClientConn(conn, this.quickClose), nil
|
return NewClientConn(conn, this.isTLS, this.quickClose, limiter), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientListener) Close() error {
|
func (this *ClientListener) Close() error {
|
||||||
|
|||||||
57
internal/nodes/client_tls_conn.go
Normal file
57
internal/nodes/client_tls_conn.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientTLSConn TLS连接封装
|
||||||
|
type ClientTLSConn struct {
|
||||||
|
BaseClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientTLSConn(conn *tls.Conn) net.Conn {
|
||||||
|
return &ClientTLSConn{BaseClientConn{rawConn: conn}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) Read(b []byte) (n int, err error) {
|
||||||
|
n, err = this.rawConn.Read(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) Write(b []byte) (n int, err error) {
|
||||||
|
n, err = this.rawConn.Write(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) Close() error {
|
||||||
|
this.isClosed = true
|
||||||
|
|
||||||
|
// 单个服务并发数限制
|
||||||
|
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
||||||
|
|
||||||
|
return this.rawConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) LocalAddr() net.Addr {
|
||||||
|
return this.rawConn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) RemoteAddr() net.Addr {
|
||||||
|
return this.rawConn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) SetDeadline(t time.Time) error {
|
||||||
|
return this.rawConn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return this.rawConn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ClientTLSConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return this.rawConn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"strings"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,7 +27,9 @@ func NewHTTPAccessLogQueue() *HTTPAccessLogQueue {
|
|||||||
queue := &HTTPAccessLogQueue{
|
queue := &HTTPAccessLogQueue{
|
||||||
queue: make(chan *pb.HTTPAccessLog, maxSize),
|
queue: make(chan *pb.HTTPAccessLog, maxSize),
|
||||||
}
|
}
|
||||||
go queue.Start()
|
goman.New(func() {
|
||||||
|
queue.Start()
|
||||||
|
})
|
||||||
|
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
@@ -55,24 +58,11 @@ func (this *HTTPAccessLogQueue) Push(accessLog *pb.HTTPAccessLog) {
|
|||||||
func (this *HTTPAccessLogQueue) loop() error {
|
func (this *HTTPAccessLogQueue) loop() error {
|
||||||
var accessLogs = []*pb.HTTPAccessLog{}
|
var accessLogs = []*pb.HTTPAccessLog{}
|
||||||
var count = 0
|
var count = 0
|
||||||
var timestamp int64
|
|
||||||
var requestId = 1_000_000
|
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case accessLog := <-this.queue:
|
case accessLog := <-this.queue:
|
||||||
var unixTime = utils.UnixTime()
|
|
||||||
if unixTime > timestamp {
|
|
||||||
requestId = 1_000_000
|
|
||||||
timestamp = unixTime
|
|
||||||
} else {
|
|
||||||
requestId++
|
|
||||||
}
|
|
||||||
|
|
||||||
// timestamp + requestId + nodeId
|
|
||||||
accessLog.RequestId = strconv.FormatInt(unixTime, 10) + strconv.Itoa(requestId) + strconv.FormatInt(accessLog.NodeId, 10)
|
|
||||||
|
|
||||||
accessLogs = append(accessLogs, accessLog)
|
accessLogs = append(accessLogs, accessLog)
|
||||||
count++
|
count++
|
||||||
|
|
||||||
@@ -100,8 +90,55 @@ Loop:
|
|||||||
|
|
||||||
_, err := this.rpcClient.HTTPAccessLogRPC().CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
_, err := this.rpcClient.HTTPAccessLogRPC().CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 是否包含了invalid UTF-8
|
||||||
|
if strings.Contains(err.Error(), "string field contains invalid UTF-8") {
|
||||||
|
for _, accessLog := range accessLogs {
|
||||||
|
this.toValidUTF8(accessLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新提交
|
||||||
|
_, err = this.rpcClient.HTTPAccessLogRPC().CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPAccessLogQueue) toValidUTF8(accessLog *pb.HTTPAccessLog) {
|
||||||
|
accessLog.RemoteUser = this.toValidUTF8string(accessLog.RemoteUser)
|
||||||
|
accessLog.RequestURI = this.toValidUTF8string(accessLog.RequestURI)
|
||||||
|
accessLog.RequestPath = this.toValidUTF8string(accessLog.RequestPath)
|
||||||
|
accessLog.RequestFilename = this.toValidUTF8string(accessLog.RequestFilename)
|
||||||
|
accessLog.RequestBody = bytes.ToValidUTF8(accessLog.RequestBody, []byte{})
|
||||||
|
|
||||||
|
for _, v := range accessLog.SentHeader {
|
||||||
|
for index, s := range v.Values {
|
||||||
|
v.Values[index] = this.toValidUTF8string(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accessLog.Referer = this.toValidUTF8string(accessLog.Referer)
|
||||||
|
accessLog.UserAgent = this.toValidUTF8string(accessLog.UserAgent)
|
||||||
|
accessLog.Request = this.toValidUTF8string(accessLog.Request)
|
||||||
|
accessLog.ContentType = this.toValidUTF8string(accessLog.ContentType)
|
||||||
|
|
||||||
|
for k, c := range accessLog.Cookie {
|
||||||
|
accessLog.Cookie[k] = this.toValidUTF8string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessLog.Args = this.toValidUTF8string(accessLog.Args)
|
||||||
|
accessLog.QueryString = this.toValidUTF8string(accessLog.QueryString)
|
||||||
|
|
||||||
|
for _, v := range accessLog.Header {
|
||||||
|
for index, s := range v.Values {
|
||||||
|
v.Values[index] = this.toValidUTF8string(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPAccessLogQueue) toValidUTF8string(v string) string {
|
||||||
|
return strings.ToValidUTF8(v, "")
|
||||||
|
}
|
||||||
|
|||||||
133
internal/nodes/http_access_log_queue_test.go
Normal file
133
internal/nodes/http_access_log_queue_test.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPAccessLogQueue_Push(t *testing.T) {
|
||||||
|
// 发送到API
|
||||||
|
client, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestId = 1_000_000
|
||||||
|
|
||||||
|
var utf8Bytes = []byte{}
|
||||||
|
for i := 0; i < 254; i++ {
|
||||||
|
utf8Bytes = append(utf8Bytes, uint8(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
//bytes = []byte("真不错")
|
||||||
|
|
||||||
|
var accessLog = &pb.HTTPAccessLog{
|
||||||
|
ServerId: 23,
|
||||||
|
RequestId: strconv.FormatInt(time.Now().Unix(), 10) + strconv.Itoa(requestId) + strconv.FormatInt(1, 10),
|
||||||
|
NodeId: 48,
|
||||||
|
Host: "www.hello.com",
|
||||||
|
RequestURI: string(utf8Bytes),
|
||||||
|
RequestPath: string(utf8Bytes),
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
Cookie: map[string]string{"test": string(utf8Bytes)},
|
||||||
|
|
||||||
|
Header: map[string]*pb.Strings{
|
||||||
|
"test": {Values: []string{string(utf8Bytes)}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
new(HTTPAccessLogQueue).toValidUTF8(accessLog)
|
||||||
|
|
||||||
|
// logs.PrintAsJSON(accessLog)
|
||||||
|
|
||||||
|
//t.Log(strings.ToValidUTF8(string(utf8Bytes), ""))
|
||||||
|
_, err = client.HTTPAccessLogRPC().CreateHTTPAccessLogs(client.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: []*pb.HTTPAccessLog{
|
||||||
|
accessLog,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
// 这里只是为了重现错误
|
||||||
|
t.Logf("%#v, %s", err, err.Error())
|
||||||
|
|
||||||
|
statusErr, ok := status.FromError(err)
|
||||||
|
if ok {
|
||||||
|
t.Logf("%#v", statusErr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPAccessLogQueue_Push2(t *testing.T) {
|
||||||
|
var utf8Bytes = []byte{}
|
||||||
|
for i := 0; i < 254; i++ {
|
||||||
|
utf8Bytes = append(utf8Bytes, uint8(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessLog = &pb.HTTPAccessLog{
|
||||||
|
ServerId: 23,
|
||||||
|
RequestId: strconv.FormatInt(time.Now().Unix(), 10) + strconv.Itoa(1) + strconv.FormatInt(1, 10),
|
||||||
|
NodeId: 48,
|
||||||
|
Host: "www.hello.com",
|
||||||
|
RequestURI: string(utf8Bytes),
|
||||||
|
RequestPath: string(utf8Bytes),
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
var v = reflect.Indirect(reflect.ValueOf(accessLog))
|
||||||
|
var countFields = v.NumField()
|
||||||
|
for i := 0; i < countFields; i++ {
|
||||||
|
var field = v.Field(i)
|
||||||
|
if field.Kind() == reflect.String {
|
||||||
|
field.SetString(strings.ToValidUTF8(field.String(), ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = client.HTTPAccessLogRPC().CreateHTTPAccessLogs(client.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: []*pb.HTTPAccessLog{
|
||||||
|
accessLog,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkHTTPAccessLogQueue_ToValidUTF8(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
var utf8Bytes = []byte{}
|
||||||
|
for i := 0; i < 254; i++ {
|
||||||
|
utf8Bytes = append(utf8Bytes, uint8(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = bytes.ToValidUTF8(utf8Bytes, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkHTTPAccessLogQueue_ToValidUTF8String(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
var utf8Bytes = []byte{}
|
||||||
|
for i := 0; i < 254; i++ {
|
||||||
|
utf8Bytes = append(utf8Bytes, uint8(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = string(utf8Bytes)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = strings.ToValidUTF8(s, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"net"
|
"net"
|
||||||
@@ -33,7 +34,9 @@ func NewHTTPClientPool() *HTTPClientPool {
|
|||||||
clientsMap: map[string]*HTTPClient{},
|
clientsMap: map[string]*HTTPClient{},
|
||||||
}
|
}
|
||||||
|
|
||||||
go pool.cleanClients()
|
goman.New(func() {
|
||||||
|
pool.cleanClients()
|
||||||
|
})
|
||||||
|
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
@@ -171,7 +174,7 @@ func (this *HTTPClientPool) Client(req *HTTPRequest, origin *serverconfigs.Origi
|
|||||||
MaxConnsPerHost: maxConnections,
|
MaxConnsPerHost: maxConnections,
|
||||||
IdleConnTimeout: idleTimeout,
|
IdleConnTimeout: idleTimeout,
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
TLSHandshakeTimeout: 0, // 不限
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
Proxy: nil,
|
Proxy: nil,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -10,8 +11,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -25,17 +28,13 @@ import (
|
|||||||
// 环境变量
|
// 环境变量
|
||||||
var HOSTNAME, _ = os.Hostname()
|
var HOSTNAME, _ = os.Hostname()
|
||||||
|
|
||||||
// byte pool
|
|
||||||
var bytePool256b = utils.NewBytePool(20480, 256)
|
|
||||||
var bytePool1k = utils.NewBytePool(20480, 1024)
|
|
||||||
var bytePool32k = utils.NewBytePool(20480, 32*1024)
|
|
||||||
var bytePool128k = utils.NewBytePool(20480, 128*1024)
|
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
var errWritingToClient = errors.New("writing to client error")
|
var errWritingToClient = errors.New("writing to client error")
|
||||||
|
|
||||||
// HTTPRequest HTTP请求
|
// HTTPRequest HTTP请求
|
||||||
type HTTPRequest struct {
|
type HTTPRequest struct {
|
||||||
|
requestId string
|
||||||
|
|
||||||
// 外部参数
|
// 外部参数
|
||||||
RawReq *http.Request
|
RawReq *http.Request
|
||||||
RawWriter http.ResponseWriter
|
RawWriter http.ResponseWriter
|
||||||
@@ -64,11 +63,14 @@ type HTTPRequest struct {
|
|||||||
rewriteRule *serverconfigs.HTTPRewriteRule // 匹配到的重写规则
|
rewriteRule *serverconfigs.HTTPRewriteRule // 匹配到的重写规则
|
||||||
rewriteReplace string // 重写规则的目标
|
rewriteReplace string // 重写规则的目标
|
||||||
rewriteIsExternalURL bool // 重写目标是否为外部URL
|
rewriteIsExternalURL bool // 重写目标是否为外部URL
|
||||||
cacheRef *serverconfigs.HTTPCacheRef // 缓存设置
|
|
||||||
cacheKey string // 缓存使用的Key
|
cacheRef *serverconfigs.HTTPCacheRef // 缓存设置
|
||||||
isCached bool // 是否已经被缓存
|
cacheKey string // 缓存使用的Key
|
||||||
isAttack bool // 是否是攻击请求
|
isCached bool // 是否已经被缓存
|
||||||
bodyData []byte // 读取的Body内容
|
cacheCanTryStale bool // 是否可以尝试使用Stale缓存
|
||||||
|
|
||||||
|
isAttack bool // 是否是攻击请求
|
||||||
|
requestBodyData []byte // 读取的Body内容
|
||||||
|
|
||||||
// WAF相关
|
// WAF相关
|
||||||
firewallPolicyId int64
|
firewallPolicyId int64
|
||||||
@@ -107,12 +109,15 @@ func (this *HTTPRequest) init() {
|
|||||||
this.varMapping = map[string]string{
|
this.varMapping = map[string]string{
|
||||||
// 缓存相关初始化
|
// 缓存相关初始化
|
||||||
"cache.status": "BYPASS",
|
"cache.status": "BYPASS",
|
||||||
|
"cache.age": "0",
|
||||||
|
"cache.key": "",
|
||||||
"cache.policy.name": "",
|
"cache.policy.name": "",
|
||||||
"cache.policy.id": "0",
|
"cache.policy.id": "0",
|
||||||
"cache.policy.type": "",
|
"cache.policy.type": "",
|
||||||
}
|
}
|
||||||
this.logAttrs = map[string]string{}
|
this.logAttrs = map[string]string{}
|
||||||
this.requestFromTime = time.Now()
|
this.requestFromTime = time.Now()
|
||||||
|
this.requestId = httpRequestNextId()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do 执行请求
|
// Do 执行请求
|
||||||
@@ -129,7 +134,7 @@ func (this *HTTPRequest) Do() {
|
|||||||
// Web配置
|
// Web配置
|
||||||
err := this.configureWeb(this.Server.Web, true, 0)
|
err := this.configureWeb(this.Server.Web, true, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
this.doEnd()
|
this.doEnd()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -200,6 +205,28 @@ func (this *HTTPRequest) Do() {
|
|||||||
|
|
||||||
// 开始调用
|
// 开始调用
|
||||||
func (this *HTTPRequest) doBegin() {
|
func (this *HTTPRequest) doBegin() {
|
||||||
|
// 处理request limit
|
||||||
|
if this.web.RequestLimit != nil &&
|
||||||
|
this.web.RequestLimit.IsOn {
|
||||||
|
if this.doRequestLimit() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理requestBody
|
||||||
|
if this.RawReq.ContentLength > 0 &&
|
||||||
|
this.web.AccessLogRef != nil &&
|
||||||
|
this.web.AccessLogRef.IsOn &&
|
||||||
|
this.web.AccessLogRef.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
|
||||||
|
var err error
|
||||||
|
this.requestBodyData, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize))
|
||||||
|
if err != nil {
|
||||||
|
this.write50x(err, http.StatusBadGateway, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body))
|
||||||
|
}
|
||||||
|
|
||||||
// 处理健康检查
|
// 处理健康检查
|
||||||
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
||||||
if len(healthCheckKey) > 0 {
|
if len(healthCheckKey) > 0 {
|
||||||
@@ -208,11 +235,6 @@ func (this *HTTPRequest) doBegin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计
|
|
||||||
if this.web.StatRef != nil && this.web.StatRef.IsOn {
|
|
||||||
this.doStat()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 跳转
|
// 跳转
|
||||||
if len(this.web.HostRedirects) > 0 {
|
if len(this.web.HostRedirects) > 0 {
|
||||||
if this.doHostRedirect() {
|
if this.doHostRedirect() {
|
||||||
@@ -228,7 +250,7 @@ func (this *HTTPRequest) doBegin() {
|
|||||||
|
|
||||||
// 缓存
|
// 缓存
|
||||||
if this.web.Cache != nil && this.web.Cache.IsOn {
|
if this.web.Cache != nil && this.web.Cache.IsOn {
|
||||||
if this.doCacheRead() {
|
if this.doCacheRead(false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,6 +316,12 @@ func (this *HTTPRequest) doEnd() {
|
|||||||
if metrics.SharedManager.HasHTTPMetrics() {
|
if metrics.SharedManager.HasHTTPMetrics() {
|
||||||
this.doMetricsResponse()
|
this.doMetricsResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统计
|
||||||
|
if this.web.StatRef != nil && this.web.StatRef.IsOn {
|
||||||
|
// 放到最后执行
|
||||||
|
this.doStat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawURI 原始的请求URI
|
// RawURI 原始的请求URI
|
||||||
@@ -419,6 +447,11 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
|||||||
this.web.Auth = web.Auth
|
this.web.Auth = web.Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// request limit
|
||||||
|
if web.RequestLimit != nil && (web.RequestLimit.IsPrior || isTop) {
|
||||||
|
this.web.RequestLimit = web.RequestLimit
|
||||||
|
}
|
||||||
|
|
||||||
// 重写规则
|
// 重写规则
|
||||||
if len(web.RewriteRefs) > 0 {
|
if len(web.RewriteRefs) > 0 {
|
||||||
for index, ref := range web.RewriteRefs {
|
for index, ref := range web.RewriteRefs {
|
||||||
@@ -487,6 +520,11 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if varMapping, isMatched := location.Match(rawPath, this.Format); isMatched {
|
if varMapping, isMatched := location.Match(rawPath, this.Format); isMatched {
|
||||||
|
// 检查专属域名
|
||||||
|
if len(location.Domains) > 0 && !configutils.MatchDomains(location.Domains, this.Host) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if len(varMapping) > 0 {
|
if len(varMapping) > 0 {
|
||||||
this.addVarMapping(varMapping)
|
this.addVarMapping(varMapping)
|
||||||
}
|
}
|
||||||
@@ -556,6 +594,8 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
return strconv.Itoa(this.requestRemotePort())
|
return strconv.Itoa(this.requestRemotePort())
|
||||||
case "remoteUser":
|
case "remoteUser":
|
||||||
return this.requestRemoteUser()
|
return this.requestRemoteUser()
|
||||||
|
case "requestId":
|
||||||
|
return this.requestId
|
||||||
case "requestURI", "requestUri":
|
case "requestURI", "requestUri":
|
||||||
return this.rawURI
|
return this.rawURI
|
||||||
case "requestURL":
|
case "requestURL":
|
||||||
@@ -627,6 +667,11 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
return this.requestString()
|
return this.requestString()
|
||||||
case "cookies":
|
case "cookies":
|
||||||
return this.requestCookiesString()
|
return this.requestCookiesString()
|
||||||
|
case "isArgs":
|
||||||
|
if strings.Contains(this.uri, "?") {
|
||||||
|
return "?"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
case "args", "queryString":
|
case "args", "queryString":
|
||||||
return this.requestQueryString()
|
return this.requestQueryString()
|
||||||
case "headers":
|
case "headers":
|
||||||
@@ -923,7 +968,7 @@ func (this *HTTPRequest) requestRemotePort() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 情趣的URI中的参数部分
|
// 获取的URI中的参数部分
|
||||||
func (this *HTTPRequest) requestQueryString() string {
|
func (this *HTTPRequest) requestQueryString() string {
|
||||||
uri, err := url.ParseRequestURI(this.uri)
|
uri, err := url.ParseRequestURI(this.uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1097,44 +1142,53 @@ func (this *HTTPRequest) processRequestHeaders(reqHeader http.Header) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add
|
|
||||||
for _, header := range this.web.RequestHeaderPolicy.AddHeaders {
|
|
||||||
if !header.IsOn {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
oldValues, _ := this.RawReq.Header[header.Name]
|
|
||||||
newHeaderValue := header.Value // 因为我们不能修改header,所以在这里使用新变量
|
|
||||||
if header.HasVariables() {
|
|
||||||
newHeaderValue = this.Format(header.Value)
|
|
||||||
}
|
|
||||||
oldValues = append(oldValues, newHeaderValue)
|
|
||||||
reqHeader[header.Name] = oldValues
|
|
||||||
|
|
||||||
// 支持修改Host
|
|
||||||
if header.Name == "Host" && len(header.Value) > 0 {
|
|
||||||
this.RawReq.Host = newHeaderValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set
|
// Set
|
||||||
for _, header := range this.web.RequestHeaderPolicy.SetHeaders {
|
for _, header := range this.web.RequestHeaderPolicy.SetHeaders {
|
||||||
if !header.IsOn {
|
if !header.IsOn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newHeaderValue := header.Value // 因为我们不能修改header,所以在这里使用新变量
|
|
||||||
if header.HasVariables() {
|
// 是否已删除
|
||||||
newHeaderValue = this.Format(header.Value)
|
if this.web.ResponseHeaderPolicy.ContainsDeletedHeader(header.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求方法
|
||||||
|
if len(header.Methods) > 0 && !lists.ContainsString(header.Methods, this.RawReq.Method) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 域名
|
||||||
|
if len(header.Domains) > 0 && !configutils.MatchDomains(header.Domains, this.Host) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerValue = header.Value
|
||||||
|
if header.ShouldReplace {
|
||||||
|
if len(headerValue) == 0 {
|
||||||
|
headerValue = reqHeader.Get(header.Name) // 原有值
|
||||||
|
} else if header.HasVariables() {
|
||||||
|
headerValue = this.Format(header.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range header.ReplaceValues {
|
||||||
|
headerValue = v.Replace(headerValue)
|
||||||
|
}
|
||||||
|
} else if header.HasVariables() {
|
||||||
|
headerValue = this.Format(header.Value)
|
||||||
}
|
}
|
||||||
reqHeader[header.Name] = []string{newHeaderValue}
|
|
||||||
|
|
||||||
// 支持修改Host
|
// 支持修改Host
|
||||||
if header.Name == "Host" && len(header.Value) > 0 {
|
if header.Name == "Host" && len(header.Value) > 0 {
|
||||||
this.RawReq.Host = newHeaderValue
|
this.RawReq.Host = headerValue
|
||||||
|
} else {
|
||||||
|
if header.ShouldAppend {
|
||||||
|
reqHeader[header.Name] = append(reqHeader[header.Name], headerValue)
|
||||||
|
} else {
|
||||||
|
reqHeader[header.Name] = []string{headerValue}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace
|
|
||||||
// TODO 需要实现
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1159,7 +1213,6 @@ func (this *HTTPRequest) processResponseHeaders(statusCode int) {
|
|||||||
|
|
||||||
// 删除/添加/替换Header
|
// 删除/添加/替换Header
|
||||||
// TODO 实现AddTrailers
|
// TODO 实现AddTrailers
|
||||||
// TODO 实现ReplaceHeaders
|
|
||||||
if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
|
if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
|
||||||
// 删除某些Header
|
// 删除某些Header
|
||||||
for name := range responseHeader {
|
for name := range responseHeader {
|
||||||
@@ -1168,44 +1221,58 @@ func (this *HTTPRequest) processResponseHeaders(statusCode int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add
|
|
||||||
for _, header := range this.web.ResponseHeaderPolicy.AddHeaders {
|
|
||||||
if !header.IsOn {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if header.Match(statusCode) {
|
|
||||||
if this.web.ResponseHeaderPolicy.ContainsDeletedHeader(header.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
oldValues, _ := responseHeader[header.Name]
|
|
||||||
if header.HasVariables() {
|
|
||||||
oldValues = append(oldValues, this.Format(header.Value))
|
|
||||||
} else {
|
|
||||||
oldValues = append(oldValues, header.Value)
|
|
||||||
}
|
|
||||||
responseHeader[header.Name] = oldValues
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set
|
// Set
|
||||||
for _, header := range this.web.ResponseHeaderPolicy.SetHeaders {
|
for _, header := range this.web.ResponseHeaderPolicy.SetHeaders {
|
||||||
if !header.IsOn {
|
if !header.IsOn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if header.Match(statusCode) {
|
|
||||||
if this.web.ResponseHeaderPolicy.ContainsDeletedHeader(header.Name) {
|
// 是否已删除
|
||||||
continue
|
if this.web.ResponseHeaderPolicy.ContainsDeletedHeader(header.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态码
|
||||||
|
if header.Status != nil && !header.Status.Match(statusCode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求方法
|
||||||
|
if len(header.Methods) > 0 && !lists.ContainsString(header.Methods, this.RawReq.Method) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 域名
|
||||||
|
if len(header.Domains) > 0 && !configutils.MatchDomains(header.Domains, this.Host) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否为跳转
|
||||||
|
if header.DisableRedirect && httpStatusIsRedirect(statusCode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerValue = header.Value
|
||||||
|
if header.ShouldReplace {
|
||||||
|
if len(headerValue) == 0 {
|
||||||
|
headerValue = responseHeader.Get(header.Name) // 原有值
|
||||||
|
} else if header.HasVariables() {
|
||||||
|
headerValue = this.Format(header.Value)
|
||||||
}
|
}
|
||||||
if header.HasVariables() {
|
|
||||||
responseHeader[header.Name] = []string{this.Format(header.Value)}
|
for _, v := range header.ReplaceValues {
|
||||||
} else {
|
headerValue = v.Replace(headerValue)
|
||||||
responseHeader[header.Name] = []string{header.Value}
|
|
||||||
}
|
}
|
||||||
|
} else if header.HasVariables() {
|
||||||
|
headerValue = this.Format(header.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.ShouldAppend {
|
||||||
|
responseHeader[header.Name] = append(responseHeader[header.Name], headerValue)
|
||||||
|
} else {
|
||||||
|
responseHeader[header.Name] = []string{headerValue}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace
|
|
||||||
// TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HSTS
|
// HSTS
|
||||||
@@ -1230,19 +1297,16 @@ func (this *HTTPRequest) addError(err error) {
|
|||||||
|
|
||||||
// 计算合适的buffer size
|
// 计算合适的buffer size
|
||||||
func (this *HTTPRequest) bytePool(contentLength int64) *utils.BytePool {
|
func (this *HTTPRequest) bytePool(contentLength int64) *utils.BytePool {
|
||||||
if contentLength <= 0 {
|
if contentLength < 8192 { // 8K
|
||||||
return bytePool1k
|
return utils.BytePool1k
|
||||||
}
|
|
||||||
if contentLength < 1024 { // 1K
|
|
||||||
return bytePool256b
|
|
||||||
}
|
}
|
||||||
if contentLength < 32768 { // 32K
|
if contentLength < 32768 { // 32K
|
||||||
return bytePool1k
|
return utils.BytePool4k
|
||||||
}
|
}
|
||||||
if contentLength < 1048576 { // 1M
|
if contentLength < 131072 { // 128K
|
||||||
return bytePool32k
|
return utils.BytePool16k
|
||||||
}
|
}
|
||||||
return bytePool128k
|
return utils.BytePool32k
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否可以忽略错误
|
// 检查是否可以忽略错误
|
||||||
@@ -1279,3 +1343,34 @@ func (this *HTTPRequest) canIgnore(err error) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭当前连接
|
||||||
|
func (this *HTTPRequest) closeConn() {
|
||||||
|
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := requestConn.(net.Conn)
|
||||||
|
if ok {
|
||||||
|
_ = conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查连接是否已关闭
|
||||||
|
func (this *HTTPRequest) isConnClosed() bool {
|
||||||
|
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ok := requestConn.(net.Conn)
|
||||||
|
if ok {
|
||||||
|
return isClientConnClosed(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
|||||||
return writer.StatusCode(), nil
|
return writer.StatusCode(), nil
|
||||||
}, this.Format)
|
}, this.Format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b {
|
if b {
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -15,7 +17,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 读取缓存
|
// 读取缓存
|
||||||
func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
||||||
|
this.cacheCanTryStale = false
|
||||||
|
|
||||||
cachePolicy := this.Server.HTTPCachePolicy
|
cachePolicy := this.Server.HTTPCachePolicy
|
||||||
if cachePolicy == nil || !cachePolicy.IsOn {
|
if cachePolicy == nil || !cachePolicy.IsOn {
|
||||||
return
|
return
|
||||||
@@ -80,6 +84,12 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 校验请求
|
||||||
|
if !this.cacheRef.MatchRequest(this.RawReq) {
|
||||||
|
this.cacheRef = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 相关变量
|
// 相关变量
|
||||||
this.varMapping["cache.policy.name"] = cachePolicy.Name
|
this.varMapping["cache.policy.name"] = cachePolicy.Name
|
||||||
this.varMapping["cache.policy.id"] = strconv.FormatInt(cachePolicy.Id, 10)
|
this.varMapping["cache.policy.id"] = strconv.FormatInt(cachePolicy.Id, 10)
|
||||||
@@ -103,6 +113,7 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.cacheKey = key
|
this.cacheKey = key
|
||||||
|
this.varMapping["cache.key"] = key
|
||||||
|
|
||||||
// 读取缓存
|
// 读取缓存
|
||||||
storage := caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
storage := caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
||||||
@@ -113,12 +124,14 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
|
|
||||||
// 判断是否在Purge
|
// 判断是否在Purge
|
||||||
if this.web.Cache.PurgeIsOn && strings.ToUpper(this.RawReq.Method) == "PURGE" && this.RawReq.Header.Get("X-Edge-Purge-Key") == this.web.Cache.PurgeKey {
|
if this.web.Cache.PurgeIsOn && strings.ToUpper(this.RawReq.Method) == "PURGE" && this.RawReq.Header.Get("X-Edge-Purge-Key") == this.web.Cache.PurgeKey {
|
||||||
|
this.varMapping["cache.status"] = "PURGE"
|
||||||
|
|
||||||
err := storage.Delete(key)
|
err := storage.Delete(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_CACHE", "purge failed: "+err.Error())
|
remotelogs.Error("HTTP_REQUEST_CACHE", "purge failed: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, rpcServerService := range rpcClient.ServerRPCList() {
|
for _, rpcServerService := range rpcClient.ServerRPCList() {
|
||||||
@@ -132,16 +145,11 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytePool32k.Get()
|
|
||||||
defer func() {
|
|
||||||
bytePool32k.Put(buf)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var reader caches.Reader
|
var reader caches.Reader
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -150,16 +158,20 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
this.web.WebP.IsOn &&
|
this.web.WebP.IsOn &&
|
||||||
this.web.WebP.MatchRequest(filepath.Ext(this.requestPath()), this.Format) &&
|
this.web.WebP.MatchRequest(filepath.Ext(this.requestPath()), this.Format) &&
|
||||||
this.web.WebP.MatchAccept(this.requestHeader("Accept")) {
|
this.web.WebP.MatchAccept(this.requestHeader("Accept")) {
|
||||||
reader, _ = storage.OpenReader(key + webpSuffix)
|
reader, _ = storage.OpenReader(key+webpSuffix, useStale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查正常的文件
|
// 检查正常的文件
|
||||||
if reader == nil {
|
if reader == nil {
|
||||||
reader, err = storage.OpenReader(key)
|
reader, err = storage.OpenReader(key, useStale)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == caches.ErrNotFound {
|
if err == caches.ErrNotFound {
|
||||||
// cache相关变量
|
// cache相关变量
|
||||||
this.varMapping["cache.status"] = "MISS"
|
this.varMapping["cache.status"] = "MISS"
|
||||||
|
|
||||||
|
if !useStale && this.web.Cache.Stale != nil && this.web.Cache.Stale.IsOn {
|
||||||
|
this.cacheCanTryStale = true
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,11 +185,23 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
_ = reader.Close()
|
_ = reader.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
this.varMapping["cache.status"] = "HIT"
|
if useStale {
|
||||||
this.logAttrs["cache.status"] = "HIT"
|
this.varMapping["cache.status"] = "STALE"
|
||||||
|
this.logAttrs["cache.status"] = "STALE"
|
||||||
|
} else {
|
||||||
|
this.varMapping["cache.status"] = "HIT"
|
||||||
|
this.logAttrs["cache.status"] = "HIT"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备Buffer
|
||||||
|
var pool = this.bytePool(reader.BodySize())
|
||||||
|
var buf = pool.Get()
|
||||||
|
defer func() {
|
||||||
|
pool.Put(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
// 读取Header
|
// 读取Header
|
||||||
headerBuf := []byte{}
|
var headerBuf = []byte{}
|
||||||
err = reader.ReadHeader(buf, func(n int) (goNext bool, err error) {
|
err = reader.ReadHeader(buf, func(n int) (goNext bool, err error) {
|
||||||
headerBuf = append(headerBuf, buf[:n]...)
|
headerBuf = append(headerBuf, buf[:n]...)
|
||||||
for {
|
for {
|
||||||
@@ -204,8 +228,19 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置cache.age变量
|
||||||
|
var age = strconv.FormatInt(reader.ExpiresAt()-utils.UnixTime(), 10)
|
||||||
|
this.varMapping["cache.age"] = age
|
||||||
|
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.writer.Header().Set("X-Cache", "HIT, "+refType+", "+reader.TypeName())
|
if useStale {
|
||||||
|
this.writer.Header().Set("X-Cache", "STALE, "+refType+", "+reader.TypeName())
|
||||||
|
} else {
|
||||||
|
this.writer.Header().Set("X-Cache", "HIT, "+refType+", "+reader.TypeName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if this.web.Cache.AddAgeHeader {
|
||||||
|
this.writer.Header().Set("Age", age)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ETag
|
// ETag
|
||||||
@@ -223,7 +258,7 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
// 这里强制设置Last-Modified,如果先前源站设置了Last-Modified,将会被覆盖,避免因为源站的Last-Modified导致源站返回304 Not Modified
|
// 这里强制设置Last-Modified,如果先前源站设置了Last-Modified,将会被覆盖,避免因为源站的Last-Modified导致源站返回304 Not Modified
|
||||||
var modifiedTime = ""
|
var modifiedTime = ""
|
||||||
if lastModifiedAt > 0 {
|
if lastModifiedAt > 0 {
|
||||||
modifiedTime = time.Unix(lastModifiedAt, 0).Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
modifiedTime = time.Unix(utils.GMTUnixTime(lastModifiedAt), 0).Format("Mon, 02 Jan 2006 15:04:05") + " GMT"
|
||||||
respHeader.Set("Last-Modified", modifiedTime)
|
respHeader.Set("Last-Modified", modifiedTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +285,7 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(reader.Status())
|
this.processResponseHeaders(reader.Status())
|
||||||
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
|
|
||||||
// 输出Body
|
// 输出Body
|
||||||
if this.RawReq.Method == http.MethodHead {
|
if this.RawReq.Method == http.MethodHead {
|
||||||
@@ -327,6 +363,8 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
this.varMapping["cache.status"] = "MISS"
|
||||||
|
|
||||||
if err == caches.ErrInvalidRange {
|
if err == caches.ErrInvalidRange {
|
||||||
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
this.processResponseHeaders(http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
@@ -387,6 +425,8 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
|
|
||||||
_, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n")
|
_, err = this.writer.WriteString("\r\n--" + boundary + "--\r\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
this.varMapping["cache.status"] = "MISS"
|
||||||
|
|
||||||
// 不提示写入客户端错误
|
// 不提示写入客户端错误
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -402,6 +442,8 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
this.varMapping["cache.status"] = "MISS"
|
||||||
|
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_CACHE", "read from cache failed: "+err.Error())
|
remotelogs.Warn("HTTP_REQUEST_CACHE", "read from cache failed: "+err.Error())
|
||||||
}
|
}
|
||||||
@@ -417,3 +459,19 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置Expires Header
|
||||||
|
func (this *HTTPRequest) addExpiresHeader(expiresAt int64) {
|
||||||
|
if this.cacheRef.ExpiresTime != nil && this.cacheRef.ExpiresTime.IsPrior && this.cacheRef.ExpiresTime.IsOn {
|
||||||
|
if this.cacheRef.ExpiresTime.Overwrite || len(this.writer.Header().Get("Expires")) == 0 {
|
||||||
|
if this.cacheRef.ExpiresTime.AutoCalculate {
|
||||||
|
this.writer.Header().Set("Expires", time.Unix(utils.GMTUnixTime(expiresAt), 0).Format("Mon, 2 Jan 2006 15:04:05")+" GMT")
|
||||||
|
} else if this.cacheRef.ExpiresTime.Duration != nil {
|
||||||
|
var duration = this.cacheRef.ExpiresTime.Duration.Duration()
|
||||||
|
if duration > 0 {
|
||||||
|
this.writer.Header().Set("Expires", utils.GMTTime(time.Now().Add(duration)).Format("Mon, 2 Jan 2006 15:04:05")+" GMT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@@ -11,22 +12,42 @@ func (this *HTTPRequest) write404() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(http.StatusNotFound)
|
this.processResponseHeaders(http.StatusNotFound)
|
||||||
|
|
||||||
msg := "404 page not found: '" + this.RawURI() + "'"
|
|
||||||
|
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, _ = this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte("404 page not found: '" + this.requestFullURL() + "'" + " (Request Id: " + this.requestId + ")"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) write50x(err error, statusCode int) {
|
func (this *HTTPRequest) writeCode(code int) {
|
||||||
|
if this.doPage(code) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processResponseHeaders(code)
|
||||||
|
this.writer.WriteHeader(code)
|
||||||
|
_, _ = this.writer.Write([]byte(types.String(code) + " " + http.StatusText(code) + ": '" + this.requestFullURL() + "'" + " (Request Id: " + this.requestId + ")"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.addError(err)
|
this.addError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 尝试从缓存中恢复
|
||||||
|
if canTryStale &&
|
||||||
|
this.cacheCanTryStale &&
|
||||||
|
this.web.Cache.Stale != nil &&
|
||||||
|
this.web.Cache.Stale.IsOn &&
|
||||||
|
(len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, statusCode)) {
|
||||||
|
ok := this.doCacheRead(true)
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示自定义页面
|
||||||
if this.doPage(statusCode) {
|
if this.doPage(statusCode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.processResponseHeaders(statusCode)
|
this.processResponseHeaders(statusCode)
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
_, _ = this.writer.Write([]byte(types.String(statusCode) + " " + http.StatusText(statusCode)))
|
_, _ = this.writer.Write([]byte(types.String(statusCode) + " " + http.StatusText(statusCode) + " (Request Id: " + this.requestId + ")"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
|
client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,13 +159,13 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
resp, stderr, err := client.Call(fcgiReq)
|
resp, stderr, err := client.Call(fcgiReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(stderr) > 0 {
|
if len(stderr) > 0 {
|
||||||
err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
|
err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func (this *HTTPRequest) doHealthCheck(key string) (stop bool) {
|
|||||||
|
|
||||||
this.RawReq.Header.Del(serverconfigs.HealthCheckHeaderName)
|
this.RawReq.Header.Del(serverconfigs.HealthCheckHeaderName)
|
||||||
|
|
||||||
data, err := nodeutils.DecryptData(sharedNodeConfig.NodeId, sharedNodeConfig.Secret, key)
|
data, err := nodeutils.Base64DecodeMap(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_HEALTH_CHECK", "decode key failed: "+err.Error())
|
remotelogs.Error("HTTP_REQUEST_HEALTH_CHECK", "decode key failed: "+err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -27,9 +27,17 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
if u.KeepRequestURI {
|
if u.KeepRequestURI {
|
||||||
afterURL += this.RawReq.URL.RequestURI()
|
afterURL += this.RawReq.URL.RequestURI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 前后是否一致
|
||||||
|
if fullURL == afterURL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
|
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
|
this.processResponseHeaders(u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -60,17 +68,31 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 前后是否一致
|
||||||
|
if fullURL == afterURL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
|
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
|
this.processResponseHeaders(u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} else { // 精准匹配
|
} else { // 精准匹配
|
||||||
if fullURL == u.RealBeforeURL() {
|
if fullURL == u.RealBeforeURL() {
|
||||||
|
// 前后是否一致
|
||||||
|
if fullURL == u.AfterURL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
|
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, u.AfterURL, http.StatusTemporaryRedirect)
|
http.Redirect(this.RawWriter, this.RawReq, u.AfterURL, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
|
this.processResponseHeaders(u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, u.AfterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, u.AfterURL, u.Status)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
32
internal/nodes/http_request_limit.go
Normal file
32
internal/nodes/http_request_limit.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func (this *HTTPRequest) doRequestLimit() (shouldStop bool) {
|
||||||
|
// 检查请求Body尺寸
|
||||||
|
// TODO 处理分片提交的内容
|
||||||
|
if this.web.RequestLimit.MaxBodyBytes() > 0 &&
|
||||||
|
this.RawReq.ContentLength > this.web.RequestLimit.MaxBodyBytes() {
|
||||||
|
this.writeCode(http.StatusRequestEntityTooLarge)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置连接相关参数
|
||||||
|
if this.web.RequestLimit.MaxConns > 0 || this.web.RequestLimit.MaxConnsPerIP > 0 {
|
||||||
|
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn != nil {
|
||||||
|
clientConn, ok := requestConn.(ClientConnInterface)
|
||||||
|
if ok && !clientConn.IsBound() {
|
||||||
|
if !clientConn.Bind(this.Server.Id, this.requestRemoteAddr(true), this.web.RequestLimit.MaxConns, this.web.RequestLimit.MaxConnsPerIP) {
|
||||||
|
this.writeCode(http.StatusTooManyRequests)
|
||||||
|
this.closeConn()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -7,6 +7,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AccessLogMaxRequestBodySize 访问日志存储的请求内容最大尺寸 TODO 此值应该可以在访问日志页设置
|
||||||
|
AccessLogMaxRequestBodySize = 2 * 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
// 日志
|
// 日志
|
||||||
func (this *HTTPRequest) log() {
|
func (this *HTTPRequest) log() {
|
||||||
if this.disableLog {
|
if this.disableLog {
|
||||||
@@ -32,6 +37,11 @@ func (this *HTTPRequest) log() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否记录499
|
||||||
|
if !ref.EnableClientClosed && this.writer.StatusCode() == 499 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
addr := this.RawReq.RemoteAddr
|
addr := this.RawReq.RemoteAddr
|
||||||
index := strings.LastIndex(addr, ":")
|
index := strings.LastIndex(addr, ":")
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
@@ -81,7 +91,7 @@ func (this *HTTPRequest) log() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
accessLog := &pb.HTTPAccessLog{
|
accessLog := &pb.HTTPAccessLog{
|
||||||
RequestId: "",
|
RequestId: this.requestId,
|
||||||
NodeId: sharedNodeConfig.Id,
|
NodeId: sharedNodeConfig.Id,
|
||||||
ServerId: this.Server.Id,
|
ServerId: this.Server.Id,
|
||||||
RemoteAddr: this.requestRemoteAddr(true),
|
RemoteAddr: this.requestRemoteAddr(true),
|
||||||
@@ -135,6 +145,15 @@ func (this *HTTPRequest) log() {
|
|||||||
accessLog.OriginAddress = this.originAddr
|
accessLog.OriginAddress = this.originAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 请求Body
|
||||||
|
if ref.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
|
||||||
|
accessLog.RequestBody = this.requestBodyData
|
||||||
|
|
||||||
|
if len(accessLog.RequestBody) > AccessLogMaxRequestBodySize {
|
||||||
|
accessLog.RequestBody = accessLog.RequestBody[:AccessLogMaxRequestBodySize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO 记录匹配的 locationId和rewriteId
|
// TODO 记录匹配的 locationId和rewriteId
|
||||||
|
|
||||||
sharedHTTPAccessLogQueue.Push(accessLog)
|
sharedHTTPAccessLogQueue.Push(accessLog)
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
this.writer.Prepare(stat.Size(), status)
|
this.writer.Prepare(stat.Size(), status)
|
||||||
this.writer.WriteHeader(status)
|
this.writer.WriteHeader(status)
|
||||||
}
|
}
|
||||||
buf := bytePool1k.Get()
|
buf := utils.BytePool1k.Get()
|
||||||
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
||||||
return []byte(this.Format(string(p)))
|
return []byte(this.Format(string(p)))
|
||||||
})
|
})
|
||||||
bytePool1k.Put(buf)
|
utils.BytePool1k.Put(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_PAGE", "write to client failed: "+err.Error())
|
remotelogs.Warn("HTTP_REQUEST_PAGE", "write to client failed: "+err.Error())
|
||||||
@@ -88,6 +88,12 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
} else if page.BodyType == shared.BodyTypeHTML {
|
} else if page.BodyType == shared.BodyTypeHTML {
|
||||||
|
// 这里需要实现设置Status,因为在Format()中可以获取${status}等变量
|
||||||
|
if page.NewStatus > 0 {
|
||||||
|
this.writer.statusCode = page.NewStatus
|
||||||
|
} else {
|
||||||
|
this.writer.statusCode = status
|
||||||
|
}
|
||||||
var content = this.Format(page.Body)
|
var content = this.Format(page.Body)
|
||||||
|
|
||||||
// 修改状态码
|
// 修改状态码
|
||||||
|
|||||||
@@ -15,5 +15,5 @@ func (this *HTTPRequest) doPlanExpires() {
|
|||||||
this.processResponseHeaders(statusCode)
|
this.processResponseHeaders(statusCode)
|
||||||
|
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
_, _ = this.writer.WriteString(serverconfigs.DefaultPlanExpireNoticePageBody)
|
_, _ = this.writer.WriteString(this.Format(serverconfigs.DefaultPlanExpireNoticePageBody))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ func (this *HTTPRequest) doRedirectToHTTPS(redirectToHTTPSConfig *serverconfigs.
|
|||||||
}
|
}
|
||||||
|
|
||||||
newURL := "https://" + host + this.RawReq.RequestURI
|
newURL := "https://" + host + this.RawReq.RequestURI
|
||||||
|
this.processResponseHeaders(statusCode)
|
||||||
http.Redirect(this.writer, this.RawReq, newURL, statusCode)
|
http.Redirect(this.writer, this.RawReq, newURL, statusCode)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
requestCall.CallResponseCallbacks(this.writer)
|
requestCall.CallResponseCallbacks(this.writer)
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
err := errors.New(this.requestFullURL() + ": no available origin sites for reverse proxy")
|
err := errors.New(this.requestFullURL() + ": no available origin sites for reverse proxy")
|
||||||
remotelogs.ServerError(this.Server.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
remotelogs.ServerError(this.Server.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.origin = origin // 设置全局变量是为了日志等处理
|
this.origin = origin // 设置全局变量是为了日志等处理
|
||||||
@@ -61,7 +61,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
if origin.Addr == nil {
|
if origin.Addr == nil {
|
||||||
err := errors.New(this.requestFullURL() + ": origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
err := errors.New(this.requestFullURL() + ": origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
||||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
||||||
@@ -117,7 +117,15 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
this.RawReq.URL.Host = this.RawReq.Host
|
this.RawReq.URL.Host = this.RawReq.Host
|
||||||
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
||||||
this.RawReq.Host = originAddr
|
// 源站主机名
|
||||||
|
var hostname = originAddr
|
||||||
|
if origin.Addr.Protocol.IsHTTPFamily() {
|
||||||
|
hostname = strings.TrimSuffix(hostname, ":80")
|
||||||
|
} else if origin.Addr.Protocol.IsHTTPSFamily() {
|
||||||
|
hostname = strings.TrimSuffix(hostname, ":443")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.RawReq.Host = hostname
|
||||||
this.RawReq.URL.Host = this.RawReq.Host
|
this.RawReq.URL.Host = this.RawReq.Host
|
||||||
} else {
|
} else {
|
||||||
this.RawReq.URL.Host = this.Host
|
this.RawReq.URL.Host = this.Host
|
||||||
@@ -148,7 +156,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol)
|
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,18 +175,18 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, true)
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
|
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
|
||||||
} else if httpErr.Err != context.Canceled {
|
} else if httpErr.Err != context.Canceled {
|
||||||
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
if httpErr.Timeout() {
|
if httpErr.Timeout() {
|
||||||
this.write50x(err, http.StatusGatewayTimeout)
|
this.write50x(err, http.StatusGatewayTimeout, true)
|
||||||
} else if httpErr.Temporary() {
|
} else if httpErr.Temporary() {
|
||||||
this.write50x(err, http.StatusServiceUnavailable)
|
this.write50x(err, http.StatusServiceUnavailable, true)
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, true)
|
||||||
}
|
}
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
|
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
|
||||||
} else {
|
} else {
|
||||||
@@ -186,6 +194,12 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
isClientError := false
|
isClientError := false
|
||||||
if ok {
|
if ok {
|
||||||
if httpErr.Err == context.Canceled {
|
if httpErr.Err == context.Canceled {
|
||||||
|
// 如果是服务器端主动关闭,则无需提示
|
||||||
|
if this.isConnClosed() {
|
||||||
|
this.disableLog = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
isClientError = true
|
isClientError = true
|
||||||
this.addError(errors.New(httpErr.Op + " " + httpErr.URL + ": client closed the connection"))
|
this.addError(errors.New(httpErr.Op + " " + httpErr.URL + ": client closed the connection"))
|
||||||
this.writer.WriteHeader(499) // 仿照nginx
|
this.writer.WriteHeader(499) // 仿照nginx
|
||||||
@@ -193,7 +207,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isClientError {
|
if !isClientError {
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resp != nil && resp.Body != nil {
|
if resp != nil && resp.Body != nil {
|
||||||
@@ -218,8 +232,6 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 清除源站错误次数
|
|
||||||
|
|
||||||
// 特殊页面
|
// 特殊页面
|
||||||
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
@@ -294,7 +306,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 是否成功结束
|
// 是否成功结束
|
||||||
if err == nil && closeErr == nil {
|
if (err == nil || err == io.EOF) && (closeErr == nil || closeErr == io.EOF) {
|
||||||
this.writer.SetOk()
|
this.writer.SetOk()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ func (this *HTTPRequest) doRewrite() (shouldShop bool) {
|
|||||||
// 跳转
|
// 跳转
|
||||||
if this.rewriteRule.Mode == serverconfigs.HTTPRewriteModeRedirect {
|
if this.rewriteRule.Mode == serverconfigs.HTTPRewriteModeRedirect {
|
||||||
if this.rewriteRule.RedirectStatus > 0 {
|
if this.rewriteRule.RedirectStatus > 0 {
|
||||||
|
this.processResponseHeaders(this.rewriteRule.RedirectStatus)
|
||||||
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, this.rewriteRule.RedirectStatus)
|
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, this.rewriteRule.RedirectStatus)
|
||||||
} else {
|
} else {
|
||||||
|
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, http.StatusTemporaryRedirect)
|
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
@@ -17,23 +18,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 文本mime-type列表
|
// 文本mime-type列表
|
||||||
var textMimeMap = map[string]bool{
|
var textMimeMap = map[string]zero.Zero{
|
||||||
"application/atom+xml": true,
|
"application/atom+xml": {},
|
||||||
"application/javascript": true,
|
"application/javascript": {},
|
||||||
"application/x-javascript": true,
|
"application/x-javascript": {},
|
||||||
"application/json": true,
|
"application/json": {},
|
||||||
"application/rss+xml": true,
|
"application/rss+xml": {},
|
||||||
"application/x-web-app-manifest+json": true,
|
"application/x-web-app-manifest+json": {},
|
||||||
"application/xhtml+xml": true,
|
"application/xhtml+xml": {},
|
||||||
"application/xml": true,
|
"application/xml": {},
|
||||||
"image/svg+xml": true,
|
"image/svg+xml": {},
|
||||||
"text/css": true,
|
"text/css": {},
|
||||||
"text/plain": true,
|
"text/plain": {},
|
||||||
"text/javascript": true,
|
"text/javascript": {},
|
||||||
"text/xml": true,
|
"text/xml": {},
|
||||||
"text/html": true,
|
"text/html": {},
|
||||||
"text/xhtml": true,
|
"text/xhtml": {},
|
||||||
"text/sgml": true,
|
"text/sgml": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用本地静态资源
|
// 调用本地静态资源
|
||||||
@@ -109,7 +110,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, true)
|
||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, true)
|
||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -283,7 +284,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
|
|
||||||
reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
|
reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ func (this *HTTPRequest) doShutdown() {
|
|||||||
this.processResponseHeaders(http.StatusOK)
|
this.processResponseHeaders(http.StatusOK)
|
||||||
this.writer.WriteHeader(http.StatusOK)
|
this.writer.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
buf := bytePool1k.Get()
|
buf := utils.BytePool1k.Get()
|
||||||
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
||||||
return []byte(this.Format(string(p)))
|
return []byte(this.Format(string(p)))
|
||||||
})
|
})
|
||||||
bytePool1k.Put(buf)
|
utils.BytePool1k.Put(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_SHUTDOWN", "write to client failed: "+err.Error())
|
remotelogs.Warn("HTTP_REQUEST_SHUTDOWN", "write to client failed: "+err.Error())
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import "github.com/TeaOSLab/EdgeNode/internal/stats"
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
|
)
|
||||||
|
|
||||||
// 统计
|
// 统计
|
||||||
func (this *HTTPRequest) doStat() {
|
func (this *HTTPRequest) doStat() {
|
||||||
@@ -9,6 +11,6 @@ func (this *HTTPRequest) doStat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 内置的统计
|
// 内置的统计
|
||||||
stats.SharedHTTPRequestStatManager.AddRemoteAddr(this.Server.Id, this.requestRemoteAddr(true))
|
stats.SharedHTTPRequestStatManager.AddRemoteAddr(this.Server.Id, this.requestRemoteAddr(true), this.writer.SentBodyBytes(), this.isAttack)
|
||||||
stats.SharedHTTPRequestStatManager.AddUserAgent(this.Server.Id, this.requestHeader("User-Agent"))
|
stats.SharedHTTPRequestStatManager.AddUserAgent(this.Server.Id, this.requestHeader("User-Agent"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/iwind/TeaGo/assert"
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,3 +34,17 @@ func TestHTTPRequest_RedirectToHTTPS(t *testing.T) {
|
|||||||
a.IsBool(req.web.RedirectToHttps.IsOn == true)
|
a.IsBool(req.web.RedirectToHttps.IsOn == true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPRequest_Memory(t *testing.T) {
|
||||||
|
var stat1 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat1)
|
||||||
|
|
||||||
|
var requests = []*HTTPRequest{}
|
||||||
|
for i := 0; i < 1_000_000; i++ {
|
||||||
|
requests = append(requests, &HTTPRequest{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat2 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat2)
|
||||||
|
t.Log((stat2.HeapInuse-stat1.HeapInuse)/1024/1024, "MB,")
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ func (this *HTTPRequest) doTrafficLimit() {
|
|||||||
|
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
if len(config.NoticePageBody) != 0 {
|
if len(config.NoticePageBody) != 0 {
|
||||||
_, _ = this.writer.WriteString(config.NoticePageBody)
|
_, _ = this.writer.WriteString(this.Format(config.NoticePageBody))
|
||||||
} else {
|
} else {
|
||||||
_, _ = this.writer.WriteString(serverconfigs.DefaultTrafficLimitNoticePageBody)
|
_, _ = this.writer.WriteString(this.Format(serverconfigs.DefaultTrafficLimitNoticePageBody))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error())
|
remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error())
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 分解Range
|
// 分解Range
|
||||||
@@ -125,3 +129,27 @@ func httpRequestGenBoundary() string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%x", buf[:])
|
return fmt.Sprintf("%x", buf[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断状态是否为跳转
|
||||||
|
func httpStatusIsRedirect(statusCode int) bool {
|
||||||
|
return statusCode == http.StatusPermanentRedirect ||
|
||||||
|
statusCode == http.StatusTemporaryRedirect ||
|
||||||
|
statusCode == http.StatusMovedPermanently ||
|
||||||
|
statusCode == http.StatusSeeOther ||
|
||||||
|
statusCode == http.StatusFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成请求ID
|
||||||
|
var httpRequestTimestamp int64
|
||||||
|
var httpRequestId int32 = 1_000_000
|
||||||
|
|
||||||
|
func httpRequestNextId() string {
|
||||||
|
var unixTime = utils.UnixTimeMilli()
|
||||||
|
if unixTime > httpRequestTimestamp {
|
||||||
|
atomic.StoreInt32(&httpRequestId, 1_000_000)
|
||||||
|
httpRequestTimestamp = unixTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// timestamp + requestId + nodeId
|
||||||
|
return strconv.FormatInt(unixTime, 10) + teaconst.NodeIdString + strconv.Itoa(int(atomic.AddInt32(&httpRequestId, 1)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/assert"
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHTTPRequest_httpRequestParseContentRange(t *testing.T) {
|
func TestHTTPRequest_httpRequestParseContentRange(t *testing.T) {
|
||||||
@@ -53,3 +58,60 @@ func TestHTTPRequest_httpRequestParseContentRange(t *testing.T) {
|
|||||||
t.Log(set)
|
t.Log(set)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPRequest_httpRequestNextId(t *testing.T) {
|
||||||
|
teaconst.NodeId = 123
|
||||||
|
teaconst.NodeIdString = "123"
|
||||||
|
t.Log(httpRequestNextId())
|
||||||
|
t.Log(httpRequestNextId())
|
||||||
|
t.Log(httpRequestNextId())
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
t.Log(httpRequestNextId())
|
||||||
|
t.Log(httpRequestNextId())
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
t.Log(httpRequestNextId())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPRequest_httpRequestNextId_Concurrent(t *testing.T) {
|
||||||
|
var m = map[string]zero.Zero{}
|
||||||
|
var locker = sync.Mutex{}
|
||||||
|
|
||||||
|
var count = 4000
|
||||||
|
var wg = &sync.WaitGroup{}
|
||||||
|
wg.Add(count)
|
||||||
|
|
||||||
|
var countDuplicated = 0
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
var requestId = httpRequestNextId()
|
||||||
|
|
||||||
|
locker.Lock()
|
||||||
|
|
||||||
|
_, ok := m[requestId]
|
||||||
|
if ok {
|
||||||
|
t.Log("duplicated:", requestId)
|
||||||
|
countDuplicated++
|
||||||
|
}
|
||||||
|
|
||||||
|
m[requestId] = zero.New()
|
||||||
|
locker.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
t.Log("ok", countDuplicated, "duplicated")
|
||||||
|
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsTrue(countDuplicated == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkHTTPRequest_httpRequestNextId(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
teaconst.NodeIdString = "123"
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = httpRequestNextId()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,41 +7,40 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 调用WAF
|
// 调用WAF
|
||||||
func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
||||||
|
var remoteAddr = this.requestRemoteAddr(true)
|
||||||
|
|
||||||
|
// 检查是否为白名单直连
|
||||||
|
if !Tea.IsTesting() && sharedNodeConfig.IPIsAutoAllowed(remoteAddr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 当前连接是否已关闭
|
// 当前连接是否已关闭
|
||||||
var conn = this.RawReq.Context().Value(HTTPConnContextKey)
|
if this.isConnClosed() {
|
||||||
if conn != nil {
|
this.disableLog = true
|
||||||
if isClientConnClosed(conn.(net.Conn)) {
|
return true
|
||||||
this.disableLog = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是否在全局名单中
|
// 是否在全局名单中
|
||||||
var remoteAddr = this.requestRemoteAddr(true)
|
|
||||||
if !iplibrary.AllowIP(remoteAddr, this.Server.Id) {
|
if !iplibrary.AllowIP(remoteAddr, this.Server.Id) {
|
||||||
this.disableLog = true
|
this.disableLog = true
|
||||||
if conn != nil {
|
this.closeConn()
|
||||||
_ = conn.(net.Conn).Close()
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否在临时黑名单中
|
// 检查是否在临时黑名单中
|
||||||
if waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeService, this.Server.Id, remoteAddr) || waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, remoteAddr) {
|
if waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeService, this.Server.Id, remoteAddr) || waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, remoteAddr) {
|
||||||
this.disableLog = true
|
this.disableLog = true
|
||||||
if conn != nil {
|
this.closeConn()
|
||||||
_ = conn.(net.Conn).Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -190,17 +189,11 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.OnAction(func(action waf.ActionInterface) (goNext bool) {
|
|
||||||
switch action.Code() {
|
|
||||||
case waf.ActionTag:
|
|
||||||
this.tags = action.(*waf.TagAction).Tags
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
goNext, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
|
goNext, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
if !this.canIgnore(err) {
|
||||||
|
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,17 +247,11 @@ func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.OnAction(func(action waf.ActionInterface) (goNext bool) {
|
|
||||||
switch action.Code() {
|
|
||||||
case waf.ActionTag:
|
|
||||||
this.tags = action.(*waf.TagAction).Tags
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
goNext, ruleGroup, ruleSet, err := w.MatchResponse(this, resp, this.writer)
|
goNext, ruleGroup, ruleSet, err := w.MatchResponse(this, resp, this.writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
if !this.canIgnore(err) {
|
||||||
|
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,12 +287,12 @@ func (this *HTTPRequest) WAFRemoteIP() string {
|
|||||||
|
|
||||||
// WAFGetCacheBody 获取缓存中的Body
|
// WAFGetCacheBody 获取缓存中的Body
|
||||||
func (this *HTTPRequest) WAFGetCacheBody() []byte {
|
func (this *HTTPRequest) WAFGetCacheBody() []byte {
|
||||||
return this.bodyData
|
return this.requestBodyData
|
||||||
}
|
}
|
||||||
|
|
||||||
// WAFSetCacheBody 设置Body
|
// WAFSetCacheBody 设置Body
|
||||||
func (this *HTTPRequest) WAFSetCacheBody(body []byte) {
|
func (this *HTTPRequest) WAFSetCacheBody(body []byte) {
|
||||||
this.bodyData = body
|
this.requestBodyData = body
|
||||||
}
|
}
|
||||||
|
|
||||||
// WAFReadBody 读取Body
|
// WAFReadBody 读取Body
|
||||||
@@ -313,16 +300,14 @@ func (this *HTTPRequest) WAFReadBody(max int64) (data []byte, err error) {
|
|||||||
if this.RawReq.ContentLength > 0 {
|
if this.RawReq.ContentLength > 0 {
|
||||||
data, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, max))
|
data, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, max))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// WAFRestoreBody 恢复Body
|
// WAFRestoreBody 恢复Body
|
||||||
func (this *HTTPRequest) WAFRestoreBody(data []byte) {
|
func (this *HTTPRequest) WAFRestoreBody(data []byte) {
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
rawReader := bytes.NewBuffer(data)
|
this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(data), this.RawReq.Body))
|
||||||
buf := make([]byte, 1024)
|
|
||||||
_, _ = io.CopyBuffer(rawReader, this.RawReq.Body, buf)
|
|
||||||
this.RawReq.Body = ioutil.NopCloser(rawReader)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,14 +318,22 @@ func (this *HTTPRequest) WAFServerId() int64 {
|
|||||||
|
|
||||||
// WAFClose 关闭连接
|
// WAFClose 关闭连接
|
||||||
func (this *HTTPRequest) WAFClose() {
|
func (this *HTTPRequest) WAFClose() {
|
||||||
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
this.closeConn()
|
||||||
if requestConn == nil {
|
}
|
||||||
return
|
|
||||||
}
|
func (this *HTTPRequest) WAFOnAction(action interface{}) (goNext bool) {
|
||||||
conn, ok := requestConn.(net.Conn)
|
if action == nil {
|
||||||
if ok {
|
return true
|
||||||
_ = conn.Close()
|
}
|
||||||
return
|
|
||||||
}
|
instance, ok := action.(waf.ActionInterface)
|
||||||
return
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch instance.Code() {
|
||||||
|
case waf.ActionTag:
|
||||||
|
this.tags = append(this.tags, action.(*waf.TagAction).Tags...)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -42,7 +44,7 @@ func (this *HTTPRequest) doWebsocket() {
|
|||||||
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
||||||
originConn, err := OriginConnect(this.origin, this.RawReq.RemoteAddr)
|
originConn, err := OriginConnect(this.origin, this.RawReq.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -51,21 +53,22 @@ func (this *HTTPRequest) doWebsocket() {
|
|||||||
|
|
||||||
err = this.RawReq.Write(originConn)
|
err = this.RawReq.Write(originConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusBadGateway)
|
this.write50x(err, http.StatusBadGateway, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clientConn, _, err := this.writer.Hijack()
|
clientConn, _, err := this.writer.Hijack()
|
||||||
if err != nil || clientConn == nil {
|
if err != nil || clientConn == nil {
|
||||||
this.write50x(err, http.StatusInternalServerError)
|
this.write50x(err, http.StatusInternalServerError, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = clientConn.Close()
|
_ = clientConn.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
buf := make([]byte, 4*1024) // TODO 使用内存池
|
var buf = utils.BytePool4k.Get()
|
||||||
|
defer utils.BytePool4k.Put(buf)
|
||||||
for {
|
for {
|
||||||
n, err := originConn.Read(buf)
|
n, err := originConn.Read(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -81,6 +84,6 @@ func (this *HTTPRequest) doWebsocket() {
|
|||||||
}
|
}
|
||||||
_ = clientConn.Close()
|
_ = clientConn.Close()
|
||||||
_ = originConn.Close()
|
_ = originConn.Close()
|
||||||
}()
|
})
|
||||||
_, _ = io.Copy(originConn, clientConn)
|
_, _ = io.Copy(originConn, clientConn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -76,22 +78,6 @@ func NewHTTPWriter(req *HTTPRequest, httpResponseWriter http.ResponseWriter) *HT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset 重置
|
|
||||||
func (this *HTTPWriter) Reset(httpResponseWriter http.ResponseWriter) {
|
|
||||||
this.writer = httpResponseWriter
|
|
||||||
|
|
||||||
this.compressionConfig = nil
|
|
||||||
this.compressionWriter = nil
|
|
||||||
|
|
||||||
this.statusCode = 0
|
|
||||||
this.sentBodyBytes = 0
|
|
||||||
|
|
||||||
this.bodyCopying = false
|
|
||||||
this.body = nil
|
|
||||||
this.compressionBodyBuffer = nil
|
|
||||||
this.compressionBodyWriter = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCompression 设置内容压缩配置
|
// SetCompression 设置内容压缩配置
|
||||||
func (this *HTTPWriter) SetCompression(config *serverconfigs.HTTPCompressionConfig) {
|
func (this *HTTPWriter) SetCompression(config *serverconfigs.HTTPCompressionConfig) {
|
||||||
this.compressionConfig = config
|
this.compressionConfig = config
|
||||||
@@ -118,6 +104,14 @@ func (this *HTTPWriter) Prepare(size int64, status int) (delayHeaders bool) {
|
|||||||
this.PrepareCompression(size)
|
this.PrepareCompression(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否限速写入
|
||||||
|
if this.req.web != nil &&
|
||||||
|
this.req.web.RequestLimit != nil &&
|
||||||
|
this.req.web.RequestLimit.IsOn &&
|
||||||
|
this.req.web.RequestLimit.OutBandwidthPerConnBytes() > 0 {
|
||||||
|
this.writer = NewHTTPRateWriter(this.writer, this.req.web.RequestLimit.OutBandwidthPerConnBytes())
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,10 +415,12 @@ func (this *HTTPWriter) Close() {
|
|||||||
if this.isOk {
|
if this.isOk {
|
||||||
err := this.cacheWriter.Close()
|
err := this.cacheWriter.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
var expiredAt = this.cacheWriter.ExpiredAt()
|
||||||
this.cacheStorage.AddToList(&caches.Item{
|
this.cacheStorage.AddToList(&caches.Item{
|
||||||
Type: this.cacheWriter.ItemType(),
|
Type: this.cacheWriter.ItemType(),
|
||||||
Key: this.cacheWriter.Key(),
|
Key: this.cacheWriter.Key(),
|
||||||
ExpiredAt: this.cacheWriter.ExpiredAt(),
|
ExpiredAt: expiredAt,
|
||||||
|
StaleAt: expiredAt + int64(this.calculateStaleLife()),
|
||||||
HeaderSize: this.cacheWriter.HeaderSize(),
|
HeaderSize: this.cacheWriter.HeaderSize(),
|
||||||
BodySize: this.cacheWriter.BodySize(),
|
BodySize: this.cacheWriter.BodySize(),
|
||||||
Host: this.req.Host,
|
Host: this.req.Host,
|
||||||
@@ -566,6 +562,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
|
|
||||||
// 不支持Range
|
// 不支持Range
|
||||||
if len(this.Header().Get("Content-Range")) > 0 {
|
if len(this.Header().Get("Content-Range")) > 0 {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, not supported Content-Range")
|
this.Header().Set("X-Cache", "BYPASS, not supported Content-Range")
|
||||||
}
|
}
|
||||||
@@ -574,6 +571,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
|
|
||||||
// 如果允许 ChunkedEncoding,就无需尺寸的判断,因为此时的 size 为 -1
|
// 如果允许 ChunkedEncoding,就无需尺寸的判断,因为此时的 size 为 -1
|
||||||
if !cacheRef.AllowChunkedEncoding && size < 0 {
|
if !cacheRef.AllowChunkedEncoding && size < 0 {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, ChunkedEncoding")
|
this.Header().Set("X-Cache", "BYPASS, ChunkedEncoding")
|
||||||
}
|
}
|
||||||
@@ -581,6 +579,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
}
|
}
|
||||||
if size >= 0 && ((cacheRef.MaxSizeBytes() > 0 && size > cacheRef.MaxSizeBytes()) ||
|
if size >= 0 && ((cacheRef.MaxSizeBytes() > 0 && size > cacheRef.MaxSizeBytes()) ||
|
||||||
(cachePolicy.MaxSizeBytes() > 0 && size > cachePolicy.MaxSizeBytes()) || (cacheRef.MinSizeBytes() > size)) {
|
(cachePolicy.MaxSizeBytes() > 0 && size > cachePolicy.MaxSizeBytes()) || (cacheRef.MinSizeBytes() > size)) {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, Content-Length")
|
this.Header().Set("X-Cache", "BYPASS, Content-Length")
|
||||||
}
|
}
|
||||||
@@ -589,6 +588,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
|
|
||||||
// 检查状态
|
// 检查状态
|
||||||
if len(cacheRef.Status) > 0 && !lists.ContainsInt(cacheRef.Status, this.StatusCode()) {
|
if len(cacheRef.Status) > 0 && !lists.ContainsInt(cacheRef.Status, this.StatusCode()) {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, Status: "+types.String(this.StatusCode()))
|
this.Header().Set("X-Cache", "BYPASS, Status: "+types.String(this.StatusCode()))
|
||||||
}
|
}
|
||||||
@@ -602,6 +602,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
values := strings.Split(cacheControl, ",")
|
values := strings.Split(cacheControl, ",")
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
if cacheRef.ContainsCacheControl(strings.TrimSpace(value)) {
|
if cacheRef.ContainsCacheControl(strings.TrimSpace(value)) {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, Cache-Control: "+cacheControl)
|
this.Header().Set("X-Cache", "BYPASS, Cache-Control: "+cacheControl)
|
||||||
}
|
}
|
||||||
@@ -613,6 +614,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
|
|
||||||
// Set-Cookie
|
// Set-Cookie
|
||||||
if cacheRef.SkipResponseSetCookie && len(this.writer.Header().Get("Set-Cookie")) > 0 {
|
if cacheRef.SkipResponseSetCookie && len(this.writer.Header().Get("Set-Cookie")) > 0 {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, Set-Cookie")
|
this.Header().Set("X-Cache", "BYPASS, Set-Cookie")
|
||||||
}
|
}
|
||||||
@@ -621,6 +623,7 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
|
|
||||||
// 校验其他条件
|
// 校验其他条件
|
||||||
if cacheRef.Conds != nil && cacheRef.Conds.HasResponseConds() && !cacheRef.Conds.MatchResponse(this.req.Format) {
|
if cacheRef.Conds != nil && cacheRef.Conds.HasResponseConds() && !cacheRef.Conds.MatchResponse(this.req.Format) {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, ResponseConds")
|
this.Header().Set("X-Cache", "BYPASS, ResponseConds")
|
||||||
}
|
}
|
||||||
@@ -630,17 +633,40 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
// 打开缓存写入
|
// 打开缓存写入
|
||||||
storage := caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
storage := caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
|
this.req.varMapping["cache.status"] = "BYPASS"
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
this.Header().Set("X-Cache", "BYPASS, Storage")
|
this.Header().Set("X-Cache", "BYPASS, Storage")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.req.varMapping["cache.status"] = "UPDATING"
|
||||||
|
if addStatusHeader {
|
||||||
|
this.Header().Set("X-Cache", "UPDATING")
|
||||||
|
}
|
||||||
|
|
||||||
this.cacheStorage = storage
|
this.cacheStorage = storage
|
||||||
life := cacheRef.LifeSeconds()
|
life := cacheRef.LifeSeconds()
|
||||||
if life <= 60 { // 最小不能少于1分钟
|
|
||||||
|
if life <= 0 {
|
||||||
life = 60
|
life = 60
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 支持源站设置的max-age
|
||||||
|
if this.req.web.Cache != nil && this.req.web.Cache.EnableCacheControlMaxAge {
|
||||||
|
var cacheControl = this.Header().Get("Cache-Control")
|
||||||
|
var pieces = strings.Split(cacheControl, ";")
|
||||||
|
for _, piece := range pieces {
|
||||||
|
var eqIndex = strings.Index(piece, "=")
|
||||||
|
if eqIndex > 0 && piece[:eqIndex] == "max-age" {
|
||||||
|
var maxAge = types.Int64(piece[eqIndex+1:])
|
||||||
|
if maxAge > 0 {
|
||||||
|
life = maxAge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expiredAt := utils.UnixTime() + life
|
expiredAt := utils.UnixTime() + life
|
||||||
var cacheKey = this.req.cacheKey
|
var cacheKey = this.req.cacheKey
|
||||||
if this.webpIsEncoding {
|
if this.webpIsEncoding {
|
||||||
@@ -668,3 +694,32 @@ func (this *HTTPWriter) prepareCache(size int64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算stale时长
|
||||||
|
func (this *HTTPWriter) calculateStaleLife() int {
|
||||||
|
var staleLife = 600 // TODO 可以在缓存策略里设置此时间
|
||||||
|
var staleConfig = this.req.web.Cache.Stale
|
||||||
|
if staleConfig != nil && staleConfig.IsOn {
|
||||||
|
// 从Header中读取stale-if-error
|
||||||
|
var isDefinedInHeader = false
|
||||||
|
if staleConfig.SupportStaleIfErrorHeader {
|
||||||
|
var cacheControl = this.Header().Get("Cache-Control")
|
||||||
|
var pieces = strings.Split(cacheControl, ",")
|
||||||
|
for _, piece := range pieces {
|
||||||
|
var eqIndex = strings.Index(piece, "=")
|
||||||
|
if eqIndex > 0 && strings.TrimSpace(piece[:eqIndex]) == "stale-if-error" {
|
||||||
|
// 这里预示着如果stale-if-error=0,可以关闭stale功能
|
||||||
|
staleLife = types.Int(strings.TrimSpace(piece[eqIndex+1:]))
|
||||||
|
isDefinedInHeader = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义
|
||||||
|
if !isDefinedInHeader && staleConfig.Life != nil {
|
||||||
|
staleLife = types.Int(staleConfig.Life.Duration().Seconds())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return staleLife
|
||||||
|
}
|
||||||
|
|||||||
102
internal/nodes/http_writer_rate.go
Normal file
102
internal/nodes/http_writer_rate.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPRateWriter 限速写入
|
||||||
|
type HTTPRateWriter struct {
|
||||||
|
parentWriter http.ResponseWriter
|
||||||
|
|
||||||
|
rateBytes int
|
||||||
|
lastBytes int
|
||||||
|
timeCost time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTPRateWriter(writer http.ResponseWriter, rateBytes int64) http.ResponseWriter {
|
||||||
|
return &HTTPRateWriter{
|
||||||
|
parentWriter: writer,
|
||||||
|
rateBytes: types.Int(rateBytes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRateWriter) Header() http.Header {
|
||||||
|
return this.parentWriter.Header()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRateWriter) Write(data []byte) (int, error) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var left = this.rateBytes - this.lastBytes
|
||||||
|
|
||||||
|
if left <= 0 {
|
||||||
|
if this.timeCost > 0 && this.timeCost < 1*time.Second {
|
||||||
|
time.Sleep(1*time.Second - this.timeCost)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastBytes = 0
|
||||||
|
this.timeCost = 0
|
||||||
|
return this.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = len(data)
|
||||||
|
|
||||||
|
// n <= left
|
||||||
|
if n <= left {
|
||||||
|
this.lastBytes += n
|
||||||
|
|
||||||
|
var before = time.Now()
|
||||||
|
defer func() {
|
||||||
|
this.timeCost += time.Since(before)
|
||||||
|
}()
|
||||||
|
return this.parentWriter.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// n > left
|
||||||
|
var before = time.Now()
|
||||||
|
result, err := this.parentWriter.Write(data[:left])
|
||||||
|
this.timeCost += time.Since(before)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
this.lastBytes += left
|
||||||
|
|
||||||
|
return this.Write(data[left:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRateWriter) WriteHeader(statusCode int) {
|
||||||
|
this.parentWriter.WriteHeader(statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hijack Hijack
|
||||||
|
func (this *HTTPRateWriter) Hijack() (conn net.Conn, buf *bufio.ReadWriter, err error) {
|
||||||
|
if this.parentWriter == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hijack, ok := this.parentWriter.(http.Hijacker)
|
||||||
|
if ok {
|
||||||
|
return hijack.Hijack()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush Flush
|
||||||
|
func (this *HTTPRateWriter) Flush() {
|
||||||
|
if this.parentWriter == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
flusher, ok := this.parentWriter.(http.Flusher)
|
||||||
|
if ok {
|
||||||
|
flusher.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -55,11 +56,11 @@ func (this *Listener) listenTCP() error {
|
|||||||
}
|
}
|
||||||
protocol := this.group.Protocol()
|
protocol := this.group.Protocol()
|
||||||
|
|
||||||
netListener, err := this.createTCPListener()
|
tcpListener, err := this.createTCPListener()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
netListener = NewClientListener(netListener, protocol.IsHTTPFamily() || protocol.IsHTTPSFamily())
|
var netListener = NewClientListener1(tcpListener, protocol.IsHTTPFamily() || protocol.IsHTTPSFamily())
|
||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
remotelogs.Println("LISTENER", "quit "+this.group.FullAddr())
|
remotelogs.Println("LISTENER", "quit "+this.group.FullAddr())
|
||||||
_ = netListener.Close()
|
_ = netListener.Close()
|
||||||
@@ -72,6 +73,7 @@ func (this *Listener) listenTCP() error {
|
|||||||
Listener: netListener,
|
Listener: netListener,
|
||||||
}
|
}
|
||||||
case serverconfigs.ProtocolHTTPS, serverconfigs.ProtocolHTTPS4, serverconfigs.ProtocolHTTPS6:
|
case serverconfigs.ProtocolHTTPS, serverconfigs.ProtocolHTTPS4, serverconfigs.ProtocolHTTPS6:
|
||||||
|
netListener.SetIsTLS(true)
|
||||||
this.listener = &HTTPListener{
|
this.listener = &HTTPListener{
|
||||||
BaseListener: BaseListener{Group: this.group},
|
BaseListener: BaseListener{Group: this.group},
|
||||||
Listener: netListener,
|
Listener: netListener,
|
||||||
@@ -82,6 +84,7 @@ func (this *Listener) listenTCP() error {
|
|||||||
Listener: netListener,
|
Listener: netListener,
|
||||||
}
|
}
|
||||||
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolTLS4, serverconfigs.ProtocolTLS6:
|
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolTLS4, serverconfigs.ProtocolTLS6:
|
||||||
|
netListener.SetIsTLS(true)
|
||||||
this.listener = &TCPListener{
|
this.listener = &TCPListener{
|
||||||
BaseListener: BaseListener{Group: this.group},
|
BaseListener: BaseListener{Group: this.group},
|
||||||
Listener: netListener,
|
Listener: netListener,
|
||||||
@@ -97,7 +100,7 @@ func (this *Listener) listenTCP() error {
|
|||||||
|
|
||||||
this.listener.Init()
|
this.listener.Init()
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
err := this.listener.Serve()
|
err := this.listener.Serve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 在这里屏蔽accept错误,防止在优雅关闭的时候有多余的提示
|
// 在这里屏蔽accept错误,防止在优雅关闭的时候有多余的提示
|
||||||
@@ -109,7 +112,7 @@ func (this *Listener) listenTCP() error {
|
|||||||
// 打印其他错误
|
// 打印其他错误
|
||||||
remotelogs.Error("LISTENER", err.Error())
|
remotelogs.Error("LISTENER", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -129,12 +132,12 @@ func (this *Listener) listenUDP() error {
|
|||||||
Listener: listener,
|
Listener: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
err := this.listener.Serve()
|
err := this.listener.Serve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("LISTENER", err.Error())
|
remotelogs.Error("LISTENER", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"golang.org/x/net/http2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseListener struct {
|
type BaseListener struct {
|
||||||
@@ -35,48 +34,21 @@ func (this *BaseListener) CountActiveListeners() int {
|
|||||||
func (this *BaseListener) buildTLSConfig() *tls.Config {
|
func (this *BaseListener) buildTLSConfig() *tls.Config {
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
Certificates: nil,
|
Certificates: nil,
|
||||||
GetConfigForClient: func(info *tls.ClientHelloInfo) (config *tls.Config, e error) {
|
GetConfigForClient: func(configInfo *tls.ClientHelloInfo) (config *tls.Config, e error) {
|
||||||
ssl, _, err := this.matchSSL(info.ServerName)
|
ssl, _, err := this.matchSSL(configInfo.ServerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cipherSuites := ssl.TLSCipherSuites()
|
return ssl.TLSConfig(), nil
|
||||||
if !ssl.CipherSuitesIsOn || len(cipherSuites) == 0 {
|
|
||||||
cipherSuites = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
nextProto := []string{}
|
|
||||||
if ssl.HTTP2Enabled {
|
|
||||||
nextProto = []string{http2.NextProtoTLS}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
|
||||||
Certificates: nil,
|
|
||||||
MinVersion: ssl.TLSMinVersion(),
|
|
||||||
CipherSuites: cipherSuites,
|
|
||||||
GetCertificate: func(info *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
|
||||||
_, cert, err := this.matchSSL(info.ServerName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if cert == nil {
|
|
||||||
return nil, errors.New("no ssl certs found for '" + info.ServerName + "'")
|
|
||||||
}
|
|
||||||
return cert, nil
|
|
||||||
},
|
|
||||||
ClientAuth: sslconfigs.GoSSLClientAuthType(ssl.ClientAuthType),
|
|
||||||
ClientCAs: ssl.CAPool(),
|
|
||||||
|
|
||||||
NextProtos: nextProto,
|
|
||||||
}, nil
|
|
||||||
},
|
},
|
||||||
GetCertificate: func(info *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
GetCertificate: func(certInfo *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
||||||
_, cert, err := this.matchSSL(info.ServerName)
|
_, cert, err := this.matchSSL(certInfo.ServerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if cert == nil {
|
if cert == nil {
|
||||||
return nil, errors.New("no ssl certs found for '" + info.ServerName + "'")
|
return nil, errors.New("no ssl certs found for '" + certInfo.ServerName + "'")
|
||||||
}
|
}
|
||||||
return cert, nil
|
return cert, nil
|
||||||
},
|
},
|
||||||
@@ -137,7 +109,7 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(sslConfig.Certs) == 0 {
|
if len(sslConfig.Certs) == 0 {
|
||||||
remotelogs.ServerError(server.Id, "BASE_LISTENER", "no ssl certs found for '"+domain+"', server id: "+types.String(server.Id))
|
remotelogs.ServerError(server.Id, "BASE_LISTENER", "no ssl certs found for '"+domain+"', server id: "+types.String(server.Id), "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sslConfig, sslConfig.FirstCert(), nil
|
return sslConfig, sslConfig.FirstCert(), nil
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
func TestBaseListener_FindServer(t *testing.T) {
|
func TestBaseListener_FindServer(t *testing.T) {
|
||||||
sharedNodeConfig = &nodeconfigs.NodeConfig{}
|
sharedNodeConfig = &nodeconfigs.NodeConfig{}
|
||||||
|
|
||||||
var listener = &BaseListener{namedServers: map[string]*NamedServer{}}
|
var listener = &BaseListener{}
|
||||||
listener.Group = &serverconfigs.ServerAddressGroup{}
|
listener.Group = &serverconfigs.ServerAddressGroup{}
|
||||||
for i := 0; i < 1_000_000; i++ {
|
for i := 0; i < 1_000_000; i++ {
|
||||||
var server = &serverconfigs.ServerConfig{
|
var server = &serverconfigs.ServerConfig{
|
||||||
@@ -24,7 +24,7 @@ func TestBaseListener_FindServer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
_ = server.Init()
|
_ = server.Init()
|
||||||
listener.Group.Servers = append(listener.Group.Servers, server)
|
listener.Group.Add(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
var before = time.Now()
|
var before = time.Now()
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -16,7 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var httpErrorLogger = log.New(io.Discard, "", 0)
|
var httpErrorLogger = log.New(io.Discard, "", 0)
|
||||||
var metricNewConnMap = map[string]bool{} // remoteAddr => bool
|
var metricNewConnMap = map[string]zero.Zero{} // remoteAddr => bool
|
||||||
var metricNewConnMapLocker = &sync.Mutex{}
|
var metricNewConnMapLocker = &sync.Mutex{}
|
||||||
|
|
||||||
type contextKey struct {
|
type contextKey struct {
|
||||||
@@ -44,9 +47,10 @@ func (this *HTTPListener) Serve() error {
|
|||||||
this.httpServer = &http.Server{
|
this.httpServer = &http.Server{
|
||||||
Addr: this.addr,
|
Addr: this.addr,
|
||||||
Handler: this,
|
Handler: this,
|
||||||
ReadHeaderTimeout: 2 * time.Second, // TODO 改成可以配置
|
ReadTimeout: 1 * time.Hour, // TODO 改成可以配置
|
||||||
IdleTimeout: 2 * time.Minute, // TODO 改成可以配置
|
ReadHeaderTimeout: 3 * time.Second, // TODO 改成可以配置
|
||||||
ErrorLog: httpErrorLogger,
|
WriteTimeout: 1 * time.Hour, // TODO 改成可以配置
|
||||||
|
IdleTimeout: 75 * time.Second, // TODO 改成可以配置
|
||||||
ConnState: func(conn net.Conn, state http.ConnState) {
|
ConnState: func(conn net.Conn, state http.ConnState) {
|
||||||
switch state {
|
switch state {
|
||||||
case http.StateNew:
|
case http.StateNew:
|
||||||
@@ -55,22 +59,36 @@ func (this *HTTPListener) Serve() error {
|
|||||||
// 为指标存储连接信息
|
// 为指标存储连接信息
|
||||||
if sharedNodeConfig.HasHTTPConnectionMetrics() {
|
if sharedNodeConfig.HasHTTPConnectionMetrics() {
|
||||||
metricNewConnMapLocker.Lock()
|
metricNewConnMapLocker.Lock()
|
||||||
metricNewConnMap[conn.RemoteAddr().String()] = true
|
metricNewConnMap[conn.RemoteAddr().String()] = zero.New()
|
||||||
metricNewConnMapLocker.Unlock()
|
metricNewConnMapLocker.Unlock()
|
||||||
}
|
}
|
||||||
|
case http.StateActive, http.StateIdle, http.StateHijacked:
|
||||||
|
// Nothing to do
|
||||||
case http.StateClosed:
|
case http.StateClosed:
|
||||||
atomic.AddInt64(&this.countActiveConnections, -1)
|
atomic.AddInt64(&this.countActiveConnections, -1)
|
||||||
|
|
||||||
// 移除指标存储连接信息
|
// 移除指标存储连接信息
|
||||||
|
// 因为中途配置可能有改变,所以暂时不添加条件
|
||||||
metricNewConnMapLocker.Lock()
|
metricNewConnMapLocker.Lock()
|
||||||
delete(metricNewConnMap, conn.RemoteAddr().String())
|
delete(metricNewConnMap, conn.RemoteAddr().String())
|
||||||
metricNewConnMapLocker.Unlock()
|
metricNewConnMapLocker.Unlock()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
|
ConnContext: func(ctx context.Context, conn net.Conn) context.Context {
|
||||||
return context.WithValue(ctx, HTTPConnContextKey, c)
|
tlsConn, ok := conn.(*tls.Conn)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
conn = NewClientTLSConn(tlsConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.WithValue(ctx, HTTPConnContextKey, conn)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !Tea.IsTesting() {
|
||||||
|
this.httpServer.ErrorLog = httpErrorLogger
|
||||||
|
}
|
||||||
|
|
||||||
this.httpServer.SetKeepAlivesEnabled(true)
|
this.httpServer.SetKeepAlivesEnabled(true)
|
||||||
|
|
||||||
// HTTP协议
|
// HTTP协议
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -37,11 +44,11 @@ func NewListenerManager() *ListenerManager {
|
|||||||
manager.ticker = time.NewTicker(5 * time.Second)
|
manager.ticker = time.NewTicker(5 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for range manager.ticker.C {
|
for range manager.ticker.C {
|
||||||
manager.retryListeners()
|
manager.retryListeners()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
@@ -61,7 +68,7 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
this.lastConfig = node
|
this.lastConfig = node
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
err := node.Init()
|
err, _ := node.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -113,10 +120,24 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
if firstServer == nil {
|
if firstServer == nil {
|
||||||
remotelogs.Error("LISTENER_MANAGER", err.Error())
|
remotelogs.Error("LISTENER_MANAGER", err.Error())
|
||||||
} else {
|
} else {
|
||||||
remotelogs.ServerError(firstServer.Id, "LISTENER_MANAGER", err.Error())
|
// 当前占用的进程名
|
||||||
|
if strings.Contains(err.Error(), "in use") {
|
||||||
|
portIndex := strings.LastIndex(addr, ":")
|
||||||
|
if portIndex > 0 {
|
||||||
|
var port = addr[portIndex+1:]
|
||||||
|
var processName = this.findProcessNameWithPort(group.IsUDP(), port)
|
||||||
|
if len(processName) > 0 {
|
||||||
|
err = errors.New(err.Error() + " (the process using port: '" + processName + "')")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remotelogs.ServerError(firstServer.Id, "LISTENER_MANAGER", "listen '"+addr+"' failed: "+err.Error(), nodeconfigs.NodeLogTypeListenAddressFailed, maps.Map{"address": addr})
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
// TODO 是否是从错误中恢复
|
||||||
}
|
}
|
||||||
this.listenersMap[addr] = listener
|
this.listenersMap[addr] = listener
|
||||||
}
|
}
|
||||||
@@ -159,7 +180,37 @@ func (this *ListenerManager) retryListeners() {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
delete(this.retryListenerMap, addr)
|
delete(this.retryListenerMap, addr)
|
||||||
this.listenersMap[addr] = listener
|
this.listenersMap[addr] = listener
|
||||||
remotelogs.ServerSuccess(listener.group.FirstServer().Id, "LISTENER_MANAGER", "retry to listen '"+addr+"' successfully")
|
remotelogs.ServerSuccess(listener.group.FirstServer().Id, "LISTENER_MANAGER", "retry to listen '"+addr+"' successfully", nodeconfigs.NodeLogTypeListenAddressFailed, maps.Map{"address": addr})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ListenerManager) findProcessNameWithPort(isUdp bool, port string) string {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := exec.LookPath("ss")
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var option = "t"
|
||||||
|
if isUdp {
|
||||||
|
option = "u"
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd = exec.Command(path, "-"+option+"lpn", "sport = :"+port)
|
||||||
|
var output = &bytes.Buffer{}
|
||||||
|
cmd.Stdout = output
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches = regexp.MustCompile(`(?U)\(\("(.+)",pid=\d+,fd=\d+\)\)`).FindStringSubmatch(output.String())
|
||||||
|
if len(matches) > 1 {
|
||||||
|
return matches[1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -110,10 +112,10 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从源站读取
|
// 从源站读取
|
||||||
go func() {
|
goman.New(func() {
|
||||||
originBuffer := bytePool32k.Get()
|
originBuffer := utils.BytePool16k.Get()
|
||||||
defer func() {
|
defer func() {
|
||||||
bytePool32k.Put(originBuffer)
|
utils.BytePool16k.Put(originBuffer)
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
n, err := originConn.Read(originBuffer)
|
n, err := originConn.Read(originBuffer)
|
||||||
@@ -134,12 +136,12 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
// 从客户端读取
|
// 从客户端读取
|
||||||
clientBuffer := bytePool32k.Get()
|
clientBuffer := utils.BytePool16k.Get()
|
||||||
defer func() {
|
defer func() {
|
||||||
bytePool32k.Put(clientBuffer)
|
utils.BytePool16k.Put(clientBuffer)
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
n, err := conn.Read(clientBuffer)
|
n, err := conn.Read(clientBuffer)
|
||||||
@@ -177,7 +179,7 @@ func (this *TCPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
|||||||
}
|
}
|
||||||
conn, err = OriginConnect(origin, remoteAddr)
|
conn, err = OriginConnect(origin, remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ServerError(serverId, "TCP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error())
|
remotelogs.ServerError(serverId, "TCP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error(), "", nil)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -37,11 +38,11 @@ func (this *UDPListener) Serve() error {
|
|||||||
|
|
||||||
this.connMap = map[string]*UDPConn{}
|
this.connMap = map[string]*UDPConn{}
|
||||||
this.connTicker = utils.NewTicker(1 * time.Minute)
|
this.connTicker = utils.NewTicker(1 * time.Minute)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for this.connTicker.Next() {
|
for this.connTicker.Next() {
|
||||||
this.gcConns()
|
this.gcConns()
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
var buffer = make([]byte, 4*1024)
|
var buffer = make([]byte, 4*1024)
|
||||||
for {
|
for {
|
||||||
@@ -114,7 +115,7 @@ func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
|||||||
}
|
}
|
||||||
conn, err = OriginConnect(origin, remoteAddr.String())
|
conn, err = OriginConnect(origin, remoteAddr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ServerError(serverId, "UDP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error())
|
remotelogs.ServerError(serverId, "UDP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error(), "", nil)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
// PROXY Protocol
|
// PROXY Protocol
|
||||||
@@ -188,10 +189,10 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *ne
|
|||||||
stats.SharedTrafficStatManager.Add(server.Id, "", 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
stats.SharedTrafficStatManager.Add(server.Id, "", 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
buffer := bytePool32k.Get()
|
buffer := utils.BytePool4k.Get()
|
||||||
defer func() {
|
defer func() {
|
||||||
bytePool32k.Put(buffer)
|
utils.BytePool4k.Put(buffer)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -214,7 +215,7 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *ne
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/ratelimit"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
@@ -24,12 +26,15 @@ import (
|
|||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"github.com/iwind/gosock/pkg/gosock"
|
"github.com/iwind/gosock/pkg/gosock"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -46,11 +51,16 @@ type Node struct {
|
|||||||
isLoaded bool
|
isLoaded bool
|
||||||
sock *gosock.Sock
|
sock *gosock.Sock
|
||||||
locker sync.Mutex
|
locker sync.Mutex
|
||||||
|
|
||||||
|
maxCPU int32
|
||||||
|
maxThreads int
|
||||||
|
timezone string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNode() *Node {
|
func NewNode() *Node {
|
||||||
return &Node{
|
return &Node{
|
||||||
sock: gosock.NewTmpSock(teaconst.ProcessName),
|
sock: gosock.NewTmpSock(teaconst.ProcessName),
|
||||||
|
maxThreads: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +133,9 @@ func (this *Node) Start() {
|
|||||||
this.startSyncTimer()
|
this.startSyncTimer()
|
||||||
|
|
||||||
// 状态变更计时器
|
// 状态变更计时器
|
||||||
go NewNodeStatusExecutor().Listen()
|
goman.New(func() {
|
||||||
|
NewNodeStatusExecutor().Listen()
|
||||||
|
})
|
||||||
|
|
||||||
// 读取配置
|
// 读取配置
|
||||||
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
||||||
@@ -132,12 +144,19 @@ func (this *Node) Start() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
teaconst.NodeId = nodeConfig.Id
|
teaconst.NodeId = nodeConfig.Id
|
||||||
err = nodeConfig.Init()
|
teaconst.NodeIdString = types.String(teaconst.NodeId)
|
||||||
|
err, serverErrors := nodeConfig.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("NODE", "init node config failed: "+err.Error())
|
remotelogs.Error("NODE", "init node config failed: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(serverErrors) > 0 {
|
||||||
|
for _, serverErr := range serverErrors {
|
||||||
|
remotelogs.ServerError(serverErr.Id, "NODE", serverErr.Message, nodeconfigs.NodeLogTypeServerConfigInitFailed, maps.Map{})
|
||||||
|
}
|
||||||
|
}
|
||||||
sharedNodeConfig = nodeConfig
|
sharedNodeConfig = nodeConfig
|
||||||
|
this.onReload(nodeConfig)
|
||||||
|
|
||||||
// 发送事件
|
// 发送事件
|
||||||
events.Notify(events.EventLoaded)
|
events.Notify(events.EventLoaded)
|
||||||
@@ -146,13 +165,19 @@ func (this *Node) Start() {
|
|||||||
_ = utils.SetRLimit(1024 * 1024)
|
_ = utils.SetRLimit(1024 * 1024)
|
||||||
|
|
||||||
// 连接API
|
// 连接API
|
||||||
go NewAPIStream().Start()
|
goman.New(func() {
|
||||||
|
NewAPIStream().Start()
|
||||||
|
})
|
||||||
|
|
||||||
// 统计
|
// 统计
|
||||||
go stats.SharedTrafficStatManager.Start(func() *nodeconfigs.NodeConfig {
|
goman.New(func() {
|
||||||
return sharedNodeConfig
|
stats.SharedTrafficStatManager.Start(func() *nodeconfigs.NodeConfig {
|
||||||
|
return sharedNodeConfig
|
||||||
|
})
|
||||||
|
})
|
||||||
|
goman.New(func() {
|
||||||
|
stats.SharedHTTPRequestStatManager.Start()
|
||||||
})
|
})
|
||||||
go stats.SharedHTTPRequestStatManager.Start()
|
|
||||||
|
|
||||||
// 启动端口
|
// 启动端口
|
||||||
err = sharedListenerManager.Start(nodeConfig)
|
err = sharedListenerManager.Start(nodeConfig)
|
||||||
@@ -290,7 +315,9 @@ func (this *Node) loop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "nodeVersionChanged":
|
case "nodeVersionChanged":
|
||||||
go sharedUpgradeManager.Start()
|
goman.New(func() {
|
||||||
|
sharedUpgradeManager.Start()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +392,7 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
return errors.New("decode config failed: " + err.Error())
|
return errors.New("decode config failed: " + err.Error())
|
||||||
}
|
}
|
||||||
teaconst.NodeId = nodeConfig.Id
|
teaconst.NodeId = nodeConfig.Id
|
||||||
|
teaconst.NodeIdString = types.String(teaconst.NodeId)
|
||||||
|
|
||||||
// 写入到文件中
|
// 写入到文件中
|
||||||
err = nodeConfig.Save()
|
err = nodeConfig.Save()
|
||||||
@@ -372,16 +400,14 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nodeConfig.Init()
|
err, serverErrors := nodeConfig.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(serverErrors) > 0 {
|
||||||
// max cpu
|
for _, serverErr := range serverErrors {
|
||||||
if nodeConfig.MaxCPU > 0 && nodeConfig.MaxCPU < int32(runtime.NumCPU()) {
|
remotelogs.ServerError(serverErr.Id, "NODE", serverErr.Message, nodeconfigs.NodeLogTypeServerConfigInitFailed, maps.Map{})
|
||||||
runtime.GOMAXPROCS(int(nodeConfig.MaxCPU))
|
}
|
||||||
} else {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新配置
|
// 刷新配置
|
||||||
@@ -403,6 +429,7 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
sharedWAFManager.UpdatePolicies(nodeConfig.FindAllFirewallPolicies())
|
sharedWAFManager.UpdatePolicies(nodeConfig.FindAllFirewallPolicies())
|
||||||
iplibrary.SharedActionManager.UpdateActions(nodeConfig.FirewallActions)
|
iplibrary.SharedActionManager.UpdateActions(nodeConfig.FirewallActions)
|
||||||
sharedNodeConfig = nodeConfig
|
sharedNodeConfig = nodeConfig
|
||||||
|
this.onReload(nodeConfig)
|
||||||
|
|
||||||
metrics.SharedManager.Update(nodeConfig.MetricItems)
|
metrics.SharedManager.Update(nodeConfig.MetricItems)
|
||||||
|
|
||||||
@@ -426,7 +453,7 @@ func (this *Node) startSyncTimer() {
|
|||||||
remotelogs.Println("NODE", "quit sync timer")
|
remotelogs.Println("NODE", "quit sync timer")
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
})
|
})
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
@@ -449,7 +476,7 @@ func (this *Node) startSyncTimer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查集群设置
|
// 检查集群设置
|
||||||
@@ -517,7 +544,7 @@ func (this *Node) listenSock() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 启动监听
|
// 启动监听
|
||||||
go func() {
|
goman.New(func() {
|
||||||
this.sock.OnCommand(func(cmd *gosock.Command) {
|
this.sock.OnCommand(func(cmd *gosock.Command) {
|
||||||
switch cmd.Code {
|
switch cmd.Code {
|
||||||
case "pid":
|
case "pid":
|
||||||
@@ -550,7 +577,7 @@ func (this *Node) listenSock() error {
|
|||||||
events.Notify(events.EventQuit)
|
events.Notify(events.EventQuit)
|
||||||
|
|
||||||
// 监控连接数,如果连接数为0,则退出进程
|
// 监控连接数,如果连接数为0,则退出进程
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for {
|
for {
|
||||||
countActiveConnections := sharedListenerManager.TotalActiveConnections()
|
countActiveConnections := sharedListenerManager.TotalActiveConnections()
|
||||||
if countActiveConnections <= 0 {
|
if countActiveConnections <= 0 {
|
||||||
@@ -559,13 +586,55 @@ func (this *Node) listenSock() error {
|
|||||||
}
|
}
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
case "trackers":
|
case "trackers":
|
||||||
_ = cmd.Reply(&gosock.Command{
|
_ = cmd.Reply(&gosock.Command{
|
||||||
Params: map[string]interface{}{
|
Params: map[string]interface{}{
|
||||||
"labels": trackers.SharedManager.Labels(),
|
"labels": trackers.SharedManager.Labels(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
case "goman":
|
||||||
|
var posMap = map[string]maps.Map{} // file#line => Map
|
||||||
|
for _, instance := range goman.List() {
|
||||||
|
var pos = instance.File + "#" + types.String(instance.Line)
|
||||||
|
m, ok := posMap[pos]
|
||||||
|
if ok {
|
||||||
|
m["count"] = m["count"].(int) + 1
|
||||||
|
} else {
|
||||||
|
m = maps.Map{
|
||||||
|
"pos": pos,
|
||||||
|
"count": 1,
|
||||||
|
}
|
||||||
|
posMap[pos] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = []maps.Map{}
|
||||||
|
for _, m := range posMap {
|
||||||
|
result = append(result, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(result, func(i, j int) bool {
|
||||||
|
return result[i]["count"].(int) > result[j]["count"].(int)
|
||||||
|
})
|
||||||
|
|
||||||
|
_ = cmd.Reply(&gosock.Command{
|
||||||
|
Params: map[string]interface{}{
|
||||||
|
"total": runtime.NumGoroutine(),
|
||||||
|
"result": result,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case "conns":
|
||||||
|
ipConns, serverConns := sharedClientConnLimiter.Conns()
|
||||||
|
|
||||||
|
_ = cmd.Reply(&gosock.Command{
|
||||||
|
Params: map[string]interface{}{
|
||||||
|
"ipConns": ipConns,
|
||||||
|
"serverConns": serverConns,
|
||||||
|
"total": sharedListenerManager.TotalActiveConnections(),
|
||||||
|
"limiter": sharedConnectionsLimiter.Len(),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -573,7 +642,7 @@ func (this *Node) listenSock() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("NODE", err.Error())
|
logs.Println("NODE", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
|
|
||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
logs.Println("NODE", "quit unix sock")
|
logs.Println("NODE", "quit unix sock")
|
||||||
@@ -582,3 +651,60 @@ func (this *Node) listenSock() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重载配置调用
|
||||||
|
func (this *Node) onReload(config *nodeconfigs.NodeConfig) {
|
||||||
|
// max cpu
|
||||||
|
if config.MaxCPU != this.maxCPU {
|
||||||
|
if config.MaxCPU > 0 && config.MaxCPU < int32(runtime.NumCPU()) {
|
||||||
|
runtime.GOMAXPROCS(int(config.MaxCPU))
|
||||||
|
remotelogs.Println("NODE", "[CPU]set max cpu to '"+types.String(config.MaxCPU)+"'")
|
||||||
|
} else {
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
remotelogs.Println("NODE", "[CPU]set max cpu to '"+types.String(runtime.NumCPU())+"'")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.maxCPU = config.MaxCPU
|
||||||
|
}
|
||||||
|
|
||||||
|
// max threads
|
||||||
|
if config.MaxThreads != this.maxThreads {
|
||||||
|
if config.MaxThreads > 0 {
|
||||||
|
debug.SetMaxThreads(config.MaxThreads)
|
||||||
|
remotelogs.Println("NODE", "[THREADS]set max threads to '"+types.String(config.MaxThreads)+"'")
|
||||||
|
} else {
|
||||||
|
debug.SetMaxThreads(nodeconfigs.DefaultMaxThreads)
|
||||||
|
remotelogs.Println("NODE", "[THREADS]set max threads to '"+types.String(nodeconfigs.DefaultMaxThreads)+"'")
|
||||||
|
}
|
||||||
|
this.maxThreads = config.MaxThreads
|
||||||
|
}
|
||||||
|
|
||||||
|
// max tcp connections
|
||||||
|
if config.TCPMaxConnections <= 0 {
|
||||||
|
config.TCPMaxConnections = nodeconfigs.DefaultTCPMaxConnections
|
||||||
|
}
|
||||||
|
if config.TCPMaxConnections != sharedConnectionsLimiter.Count() {
|
||||||
|
remotelogs.Println("NODE", "[TCP]changed tcp max connections to '"+types.String(config.TCPMaxConnections)+"'")
|
||||||
|
|
||||||
|
sharedConnectionsLimiter.Close()
|
||||||
|
sharedConnectionsLimiter = ratelimit.NewCounter(config.TCPMaxConnections)
|
||||||
|
}
|
||||||
|
|
||||||
|
// timezone
|
||||||
|
var timeZone = config.TimeZone
|
||||||
|
if len(timeZone) == 0 {
|
||||||
|
timeZone = "Asia/Shanghai"
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.timezone != timeZone {
|
||||||
|
location, err := time.LoadLocation(timeZone)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("NODE", "[TIMEZONE]change time zone failed: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
remotelogs.Println("NODE", "[TIMEZONE]change time zone to '"+timeZone+"'")
|
||||||
|
time.Local = location
|
||||||
|
this.timezone = timeZone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
@@ -16,7 +17,9 @@ var SharedOriginStateManager = NewOriginStateManager()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
go SharedOriginStateManager.Start()
|
goman.New(func() {
|
||||||
|
SharedOriginStateManager.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -96,10 +97,10 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 启动Service
|
// 启动Service
|
||||||
go func() {
|
goman.New(func() {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
_ = exec.Command(systemctl, "start", teaconst.SystemdServiceName).Start()
|
_ = exec.Command(systemctl, "start", teaconst.SystemdServiceName).Start()
|
||||||
}()
|
})
|
||||||
|
|
||||||
if output == "enabled" {
|
if output == "enabled" {
|
||||||
// 检查文件路径是否变化
|
// 检查文件路径是否变化
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
@@ -23,7 +24,9 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventStart, func() {
|
events.On(events.EventStart, func() {
|
||||||
task := NewSyncAPINodesTask()
|
task := NewSyncAPINodesTask()
|
||||||
go task.Start()
|
goman.New(func() {
|
||||||
|
task.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,9 +134,9 @@ func (this *SyncAPINodesTask) testEndpoints(endpoints []string) bool {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer func() {
|
defer func() {
|
||||||
cancel()
|
cancelFunc()
|
||||||
}()
|
}()
|
||||||
var conn *grpc.ClientConn
|
var conn *grpc.ClientConn
|
||||||
if u.Scheme == "http" {
|
if u.Scheme == "http" {
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
||||||
|
|
||||||
package nodes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// 管理时区
|
|
||||||
var lastTimeZone = ""
|
|
||||||
|
|
||||||
events.On(events.EventReload, func() {
|
|
||||||
if sharedNodeConfig != nil {
|
|
||||||
var timeZone = sharedNodeConfig.TimeZone
|
|
||||||
if len(timeZone) == 0 {
|
|
||||||
timeZone = "Asia/Shanghai"
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastTimeZone != timeZone {
|
|
||||||
location, err := time.LoadLocation(timeZone)
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("TIMEZONE", "change time zone failed: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
remotelogs.Println("TIMEZONE", "change time zone to '"+timeZone+"'")
|
|
||||||
time.Local = location
|
|
||||||
lastTimeZone = timeZone
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"net"
|
"net"
|
||||||
@@ -42,7 +43,9 @@ func (this *TOAManager) Run(config *nodeconfigs.TOAConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("TOA", "quit error: "+err.Error())
|
remotelogs.Error("TOA", "quit error: "+err.Error())
|
||||||
}
|
}
|
||||||
_ = this.conn.Close()
|
if this.conn != nil {
|
||||||
|
_ = this.conn.Close()
|
||||||
|
}
|
||||||
this.conn = nil
|
this.conn = nil
|
||||||
this.pid = 0
|
this.pid = 0
|
||||||
}
|
}
|
||||||
@@ -65,7 +68,9 @@ func (this *TOAManager) Run(config *nodeconfigs.TOAConfig) error {
|
|||||||
}
|
}
|
||||||
this.pid = cmd.Process.Pid
|
this.pid = cmd.Process.Pid
|
||||||
|
|
||||||
go func() { _ = cmd.Wait() }()
|
goman.New(func() {
|
||||||
|
_ = cmd.Wait()
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -60,12 +61,12 @@ func (this *UpgradeManager) Start() {
|
|||||||
|
|
||||||
remotelogs.Println("UPGRADE_MANAGER", "upgrade successfully")
|
remotelogs.Println("UPGRADE_MANAGER", "upgrade successfully")
|
||||||
|
|
||||||
go func() {
|
goman.New(func() {
|
||||||
err = this.restart()
|
err = this.restart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("UPGRADE_MANAGER", err.Error())
|
logs.Println("UPGRADE_MANAGER", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *UpgradeManager) install() error {
|
func (this *UpgradeManager) install() error {
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ func (this *WAFManager) convertWAF(policy *firewallconfigs.HTTPFirewallPolicy) (
|
|||||||
Name: set.Name,
|
Name: set.Name,
|
||||||
Description: set.Description,
|
Description: set.Description,
|
||||||
Connector: set.Connector,
|
Connector: set.Connector,
|
||||||
|
IgnoreLocal: set.IgnoreLocal,
|
||||||
}
|
}
|
||||||
for _, a := range set.Actions {
|
for _, a := range set.Actions {
|
||||||
s.AddAction(a.Code, a.Options)
|
s.AddAction(a.Code, a.Options)
|
||||||
@@ -143,6 +144,7 @@ func (this *WAFManager) convertWAF(policy *firewallconfigs.HTTPFirewallPolicy) (
|
|||||||
Name: set.Name,
|
Name: set.Name,
|
||||||
Description: set.Description,
|
Description: set.Description,
|
||||||
Connector: set.Connector,
|
Connector: set.Connector,
|
||||||
|
IgnoreLocal: set.IgnoreLocal,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range set.Actions {
|
for _, a := range set.Actions {
|
||||||
|
|||||||
55
internal/ratelimit/counter.go
Normal file
55
internal/ratelimit/counter.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package ratelimit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
count int
|
||||||
|
sem chan zero.Zero
|
||||||
|
done chan zero.Zero
|
||||||
|
closeOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCounter(count int) *Counter {
|
||||||
|
return &Counter{
|
||||||
|
count: count,
|
||||||
|
sem: make(chan zero.Zero, count),
|
||||||
|
done: make(chan zero.Zero),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Counter) Count() int {
|
||||||
|
return this.count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len 已占用数量
|
||||||
|
func (this *Counter) Len() int {
|
||||||
|
return len(this.sem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Counter) Ack() bool {
|
||||||
|
select {
|
||||||
|
case this.sem <- zero.New():
|
||||||
|
return true
|
||||||
|
case <-this.done:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Counter) Release() {
|
||||||
|
select {
|
||||||
|
case <-this.sem:
|
||||||
|
default:
|
||||||
|
// 总是能Release成功
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Counter) Close() {
|
||||||
|
this.closeOnce.Do(func() {
|
||||||
|
close(this.done)
|
||||||
|
})
|
||||||
|
}
|
||||||
38
internal/ratelimit/counter_test.go
Normal file
38
internal/ratelimit/counter_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package ratelimit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCounter_ACK(t *testing.T) {
|
||||||
|
var counter = NewCounter(10)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
counter.Ack()
|
||||||
|
}
|
||||||
|
//counter.Release()
|
||||||
|
t.Log("waiting", time.Now().Unix())
|
||||||
|
counter.Ack()
|
||||||
|
t.Log("done", time.Now().Unix())
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
counter.Close()
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounter_Release(t *testing.T) {
|
||||||
|
var counter = NewCounter(10)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
counter.Ack()
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
counter.Release()
|
||||||
|
}
|
||||||
|
t.Log(len(counter.sem))
|
||||||
|
}
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
package remotelogs
|
package remotelogs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +22,10 @@ var logChan = make(chan *pb.NodeLog, 1024)
|
|||||||
func init() {
|
func init() {
|
||||||
// 定期上传日志
|
// 定期上传日志
|
||||||
ticker := time.NewTicker(60 * time.Second)
|
ticker := time.NewTicker(60 * time.Second)
|
||||||
go func() {
|
if Tea.IsTesting() {
|
||||||
|
ticker = time.NewTicker(10 * time.Second)
|
||||||
|
}
|
||||||
|
goman.New(func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
var tr = trackers.Begin("UPLOAD_REMOTE_LOGS")
|
var tr = trackers.Begin("UPLOAD_REMOTE_LOGS")
|
||||||
err := uploadLogs()
|
err := uploadLogs()
|
||||||
@@ -26,25 +34,20 @@ func init() {
|
|||||||
logs.Println("[LOG]" + err.Error())
|
logs.Println("[LOG]" + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Println 打印普通信息
|
// Println 打印普通信息
|
||||||
func Println(tag string, description string) {
|
func Println(tag string, description string) {
|
||||||
logs.Println("[" + tag + "]" + description)
|
logs.Println("[" + tag + "]" + description)
|
||||||
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
|
||||||
if nodeConfig == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case logChan <- &pb.NodeLog{
|
case logChan <- &pb.NodeLog{
|
||||||
Role: teaconst.Role,
|
Role: teaconst.Role,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Description: description,
|
Description: description,
|
||||||
Level: "info",
|
Level: "info",
|
||||||
NodeId: nodeConfig.Id,
|
NodeId: teaconst.NodeId,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now().Unix(),
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
@@ -56,18 +59,13 @@ func Println(tag string, description string) {
|
|||||||
func Warn(tag string, description string) {
|
func Warn(tag string, description string) {
|
||||||
logs.Println("[" + tag + "]" + description)
|
logs.Println("[" + tag + "]" + description)
|
||||||
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
|
||||||
if nodeConfig == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case logChan <- &pb.NodeLog{
|
case logChan <- &pb.NodeLog{
|
||||||
Role: teaconst.Role,
|
Role: teaconst.Role,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Description: description,
|
Description: description,
|
||||||
Level: "warning",
|
Level: "warning",
|
||||||
NodeId: nodeConfig.Id,
|
NodeId: teaconst.NodeId,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now().Unix(),
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
@@ -79,9 +77,10 @@ func Warn(tag string, description string) {
|
|||||||
func Error(tag string, description string) {
|
func Error(tag string, description string) {
|
||||||
logs.Println("[" + tag + "]" + description)
|
logs.Println("[" + tag + "]" + description)
|
||||||
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
// 忽略RPC连接错误
|
||||||
if nodeConfig == nil {
|
var level = "error"
|
||||||
return
|
if strings.Contains(description, "code = Unavailable desc") {
|
||||||
|
level = "warning"
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -89,8 +88,8 @@ func Error(tag string, description string) {
|
|||||||
Role: teaconst.Role,
|
Role: teaconst.Role,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
Description: description,
|
Description: description,
|
||||||
Level: "error",
|
Level: level,
|
||||||
NodeId: nodeConfig.Id,
|
NodeId: teaconst.NodeId,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now().Unix(),
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
@@ -111,12 +110,18 @@ func ErrorObject(tag string, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ServerError 打印服务相关错误信息
|
// ServerError 打印服务相关错误信息
|
||||||
func ServerError(serverId int64, tag string, description string) {
|
func ServerError(serverId int64, tag string, description string, logType nodeconfigs.NodeLogType, params maps.Map) {
|
||||||
logs.Println("[" + tag + "]" + description)
|
logs.Println("[" + tag + "]" + description)
|
||||||
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
// 参数
|
||||||
if nodeConfig == nil {
|
var paramsJSON []byte
|
||||||
return
|
if len(params) > 0 {
|
||||||
|
p, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[LOG]" + err.Error())
|
||||||
|
} else {
|
||||||
|
paramsJSON = p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -125,9 +130,11 @@ func ServerError(serverId int64, tag string, description string) {
|
|||||||
Tag: tag,
|
Tag: tag,
|
||||||
Description: description,
|
Description: description,
|
||||||
Level: "error",
|
Level: "error",
|
||||||
NodeId: nodeConfig.Id,
|
NodeId: teaconst.NodeId,
|
||||||
ServerId: serverId,
|
ServerId: serverId,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now().Unix(),
|
||||||
|
Type: logType,
|
||||||
|
ParamsJSON: paramsJSON,
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
|
|
||||||
@@ -135,12 +142,18 @@ func ServerError(serverId int64, tag string, description string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ServerSuccess 打印服务相关成功信息
|
// ServerSuccess 打印服务相关成功信息
|
||||||
func ServerSuccess(serverId int64, tag string, description string) {
|
func ServerSuccess(serverId int64, tag string, description string, logType nodeconfigs.NodeLogType, params maps.Map) {
|
||||||
logs.Println("[" + tag + "]" + description)
|
logs.Println("[" + tag + "]" + description)
|
||||||
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
// 参数
|
||||||
if nodeConfig == nil {
|
var paramsJSON []byte
|
||||||
return
|
if len(params) > 0 {
|
||||||
|
p, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[LOG]" + err.Error())
|
||||||
|
} else {
|
||||||
|
paramsJSON = p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -149,9 +162,11 @@ func ServerSuccess(serverId int64, tag string, description string) {
|
|||||||
Tag: tag,
|
Tag: tag,
|
||||||
Description: description,
|
Description: description,
|
||||||
Level: "success",
|
Level: "success",
|
||||||
NodeId: nodeConfig.Id,
|
NodeId: teaconst.NodeId,
|
||||||
ServerId: serverId,
|
ServerId: serverId,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now().Unix(),
|
||||||
|
Type: logType,
|
||||||
|
ParamsJSON: paramsJSON,
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
@@ -20,6 +21,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type StatItem struct {
|
||||||
|
Bytes int64
|
||||||
|
CountRequests int64
|
||||||
|
CountAttackRequests int64
|
||||||
|
AttackBytes int64
|
||||||
|
}
|
||||||
|
|
||||||
var SharedHTTPRequestStatManager = NewHTTPRequestStatManager()
|
var SharedHTTPRequestStatManager = NewHTTPRequestStatManager()
|
||||||
|
|
||||||
// HTTPRequestStatManager HTTP请求相关的统计
|
// HTTPRequestStatManager HTTP请求相关的统计
|
||||||
@@ -29,10 +37,10 @@ type HTTPRequestStatManager struct {
|
|||||||
userAgentChan chan string
|
userAgentChan chan string
|
||||||
firewallRuleGroupChan chan string
|
firewallRuleGroupChan chan string
|
||||||
|
|
||||||
cityMap map[string]int64 // serverId@country@province@city => count ,不需要加锁,因为我们是使用channel依次执行的
|
cityMap map[string]*StatItem // serverId@country@province@city => *StatItem ,不需要加锁,因为我们是使用channel依次执行的
|
||||||
providerMap map[string]int64 // serverId@provider => count
|
providerMap map[string]int64 // serverId@provider => count
|
||||||
systemMap map[string]int64 // serverId@system@version => count
|
systemMap map[string]int64 // serverId@system@version => count
|
||||||
browserMap map[string]int64 // serverId@browser@version => count
|
browserMap map[string]int64 // serverId@browser@version => count
|
||||||
|
|
||||||
dailyFirewallRuleGroupMap map[string]int64 // serverId@firewallRuleGroupId@action => count
|
dailyFirewallRuleGroupMap map[string]int64 // serverId@firewallRuleGroupId@action => count
|
||||||
|
|
||||||
@@ -45,7 +53,7 @@ func NewHTTPRequestStatManager() *HTTPRequestStatManager {
|
|||||||
ipChan: make(chan string, 10_000), // TODO 将来可以配置容量
|
ipChan: make(chan string, 10_000), // TODO 将来可以配置容量
|
||||||
userAgentChan: make(chan string, 10_000), // TODO 将来可以配置容量
|
userAgentChan: make(chan string, 10_000), // TODO 将来可以配置容量
|
||||||
firewallRuleGroupChan: make(chan string, 10_000), // TODO 将来可以配置容量
|
firewallRuleGroupChan: make(chan string, 10_000), // TODO 将来可以配置容量
|
||||||
cityMap: map[string]int64{},
|
cityMap: map[string]*StatItem{},
|
||||||
providerMap: map[string]int64{},
|
providerMap: map[string]int64{},
|
||||||
systemMap: map[string]int64{},
|
systemMap: map[string]int64{},
|
||||||
browserMap: map[string]int64{},
|
browserMap: map[string]int64{},
|
||||||
@@ -56,17 +64,17 @@ func NewHTTPRequestStatManager() *HTTPRequestStatManager {
|
|||||||
// Start 启动
|
// Start 启动
|
||||||
func (this *HTTPRequestStatManager) Start() {
|
func (this *HTTPRequestStatManager) Start() {
|
||||||
// 上传请求总数
|
// 上传请求总数
|
||||||
go func() {
|
goman.New(func() {
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
if this.totalAttackRequests > 0 {
|
if this.totalAttackRequests > 0 {
|
||||||
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemAttackRequests, maps.Map{"total": this.totalAttackRequests})
|
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemAttackRequests, maps.Map{"total": this.totalAttackRequests})
|
||||||
this.totalAttackRequests = 0
|
this.totalAttackRequests = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}()
|
})
|
||||||
|
|
||||||
loopTicker := time.NewTicker(1 * time.Second)
|
loopTicker := time.NewTicker(1 * time.Second)
|
||||||
uploadTicker := time.NewTicker(30 * time.Minute)
|
uploadTicker := time.NewTicker(30 * time.Minute)
|
||||||
@@ -107,7 +115,7 @@ func (this *HTTPRequestStatManager) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddRemoteAddr 添加客户端地址
|
// AddRemoteAddr 添加客户端地址
|
||||||
func (this *HTTPRequestStatManager) AddRemoteAddr(serverId int64, remoteAddr string) {
|
func (this *HTTPRequestStatManager) AddRemoteAddr(serverId int64, remoteAddr string, bytes int64, isAttack bool) {
|
||||||
if len(remoteAddr) == 0 {
|
if len(remoteAddr) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -122,8 +130,14 @@ func (this *HTTPRequestStatManager) AddRemoteAddr(serverId int64, remoteAddr str
|
|||||||
ip = remoteAddr[:index]
|
ip = remoteAddr[:index]
|
||||||
}
|
}
|
||||||
if len(ip) > 0 {
|
if len(ip) > 0 {
|
||||||
|
var s string
|
||||||
|
if isAttack {
|
||||||
|
s = strconv.FormatInt(serverId, 10) + "@" + ip + "@" + types.String(bytes) + "@1"
|
||||||
|
} else {
|
||||||
|
s = strconv.FormatInt(serverId, 10) + "@" + ip + "@" + types.String(bytes) + "@0"
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case this.ipChan <- strconv.FormatInt(serverId, 10) + "@" + ip:
|
case this.ipChan <- s:
|
||||||
default:
|
default:
|
||||||
// 超出容量我们就丢弃
|
// 超出容量我们就丢弃
|
||||||
}
|
}
|
||||||
@@ -168,16 +182,33 @@ Loop:
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case ipString := <-this.ipChan:
|
case ipString := <-this.ipChan:
|
||||||
atIndex := strings.Index(ipString, "@")
|
// serverId@ip@bytes@isAttack
|
||||||
if atIndex < 0 {
|
pieces := strings.Split(ipString, "@")
|
||||||
|
if len(pieces) < 4 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
serverId := ipString[:atIndex]
|
serverId := pieces[0]
|
||||||
ip := ipString[atIndex+1:]
|
ip := pieces[1]
|
||||||
|
|
||||||
if iplibrary.SharedLibrary != nil {
|
if iplibrary.SharedLibrary != nil {
|
||||||
result, err := iplibrary.SharedLibrary.Lookup(ip)
|
result, err := iplibrary.SharedLibrary.Lookup(ip)
|
||||||
if err == nil && result != nil {
|
if err == nil && result != nil {
|
||||||
this.cityMap[serverId+"@"+result.Country+"@"+result.Province+"@"+result.City]++
|
if len(result.Country) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = serverId + "@" + result.Country + "@" + result.Province + "@" + result.City
|
||||||
|
stat, ok := this.cityMap[key]
|
||||||
|
if !ok {
|
||||||
|
stat = &StatItem{}
|
||||||
|
this.cityMap[key] = stat
|
||||||
|
}
|
||||||
|
stat.Bytes += types.Int64(pieces[2])
|
||||||
|
stat.CountRequests++
|
||||||
|
if types.Int8(pieces[3]) == 1 {
|
||||||
|
stat.AttackBytes += types.Int64(pieces[2])
|
||||||
|
stat.CountAttackRequests++
|
||||||
|
}
|
||||||
|
|
||||||
if len(result.ISP) > 0 {
|
if len(result.ISP) > 0 {
|
||||||
this.providerMap[serverId+"@"+result.ISP]++
|
this.providerMap[serverId+"@"+result.ISP]++
|
||||||
@@ -237,14 +268,17 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
pbProviders := []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{}
|
pbProviders := []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{}
|
||||||
pbSystems := []*pb.UploadServerHTTPRequestStatRequest_System{}
|
pbSystems := []*pb.UploadServerHTTPRequestStatRequest_System{}
|
||||||
pbBrowsers := []*pb.UploadServerHTTPRequestStatRequest_Browser{}
|
pbBrowsers := []*pb.UploadServerHTTPRequestStatRequest_Browser{}
|
||||||
for k, count := range this.cityMap {
|
for k, stat := range this.cityMap {
|
||||||
pieces := strings.SplitN(k, "@", 4)
|
pieces := strings.SplitN(k, "@", 4)
|
||||||
pbCities = append(pbCities, &pb.UploadServerHTTPRequestStatRequest_RegionCity{
|
pbCities = append(pbCities, &pb.UploadServerHTTPRequestStatRequest_RegionCity{
|
||||||
ServerId: types.Int64(pieces[0]),
|
ServerId: types.Int64(pieces[0]),
|
||||||
CountryName: pieces[1],
|
CountryName: pieces[1],
|
||||||
ProvinceName: pieces[2],
|
ProvinceName: pieces[2],
|
||||||
CityName: pieces[3],
|
CityName: pieces[3],
|
||||||
Count: count,
|
CountRequests: stat.CountRequests,
|
||||||
|
CountAttackRequests: stat.CountAttackRequests,
|
||||||
|
Bytes: stat.Bytes,
|
||||||
|
AttackBytes: stat.AttackBytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for k, count := range this.providerMap {
|
for k, count := range this.providerMap {
|
||||||
@@ -300,7 +334,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 重置数据
|
// 重置数据
|
||||||
this.cityMap = map[string]int64{}
|
this.cityMap = map[string]*StatItem{}
|
||||||
this.providerMap = map[string]int64{}
|
this.providerMap = map[string]int64{}
|
||||||
this.systemMap = map[string]int64{}
|
this.systemMap = map[string]int64{}
|
||||||
this.browserMap = map[string]int64{}
|
this.browserMap = map[string]int64{}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
"github.com/TeaOSLab/EdgeNode/internal/monitor"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
@@ -55,17 +56,17 @@ func (this *TrafficStatManager) Start(configFunc func() *nodeconfigs.NodeConfig)
|
|||||||
this.configFunc = configFunc
|
this.configFunc = configFunc
|
||||||
|
|
||||||
// 上传请求总数
|
// 上传请求总数
|
||||||
go func() {
|
goman.New(func() {
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
if this.totalRequests > 0 {
|
if this.totalRequests > 0 {
|
||||||
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemRequests, maps.Map{"total": this.totalRequests})
|
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemRequests, maps.Map{"total": this.totalRequests})
|
||||||
this.totalRequests = 0
|
this.totalRequests = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}()
|
})
|
||||||
|
|
||||||
// 上传统计数据
|
// 上传统计数据
|
||||||
duration := 5 * time.Minute
|
duration := 5 * time.Minute
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package ttlcache
|
package ttlcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cache TTL缓存
|
// Cache TTL缓存
|
||||||
// 最大的缓存时间为30 * 86400
|
// 最大的缓存时间为30 * 86400
|
||||||
// Piece数据结构:
|
// Piece数据结构:
|
||||||
// Piece1 | Piece2 | Piece3 | ...
|
// Piece1 | Piece2 | Piece3 | ...
|
||||||
// [ Item1, Item2, ... | ...
|
// [ Item1, Item2, ... ] | ...
|
||||||
// KeyMap列表数据结构
|
// KeyMap列表数据结构
|
||||||
// { timestamp1 => [key1, key2, ...] }, ...
|
// { timestamp1 => [key1, key2, ...] }, ...
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
@@ -19,14 +18,13 @@ type Cache struct {
|
|||||||
maxItems int
|
maxItems int
|
||||||
|
|
||||||
gcPieceIndex int
|
gcPieceIndex int
|
||||||
ticker *utils.Ticker
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCache(opt ...OptionInterface) *Cache {
|
func NewCache(opt ...OptionInterface) *Cache {
|
||||||
countPieces := 128
|
countPieces := 128
|
||||||
maxItems := 2_000_000
|
maxItems := 2_000_000
|
||||||
|
|
||||||
var delta = systemMemoryGB() / 4
|
var delta = systemMemoryGB() / 8
|
||||||
if delta > 0 {
|
if delta > 0 {
|
||||||
maxItems *= delta
|
maxItems *= delta
|
||||||
}
|
}
|
||||||
@@ -56,13 +54,8 @@ func NewCache(opt ...OptionInterface) *Cache {
|
|||||||
cache.pieces = append(cache.pieces, NewPiece(maxItems/countPieces))
|
cache.pieces = append(cache.pieces, NewPiece(maxItems/countPieces))
|
||||||
}
|
}
|
||||||
|
|
||||||
// start timer
|
// Add to manager
|
||||||
go func() {
|
SharedManager.Add(cache)
|
||||||
cache.ticker = utils.NewTicker(5 * time.Second)
|
|
||||||
for cache.ticker.Next() {
|
|
||||||
cache.GC()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return cache
|
return cache
|
||||||
}
|
}
|
||||||
@@ -149,12 +142,10 @@ func (this *Cache) Clean() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Cache) Destroy() {
|
func (this *Cache) Destroy() {
|
||||||
|
SharedManager.Remove(this)
|
||||||
|
|
||||||
this.isDestroyed = true
|
this.isDestroyed = true
|
||||||
|
|
||||||
if this.ticker != nil {
|
|
||||||
this.ticker.Stop()
|
|
||||||
this.ticker = nil
|
|
||||||
}
|
|
||||||
for _, piece := range this.pieces {
|
for _, piece := range this.pieces {
|
||||||
piece.Destroy()
|
piece.Destroy()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,13 +123,18 @@ func TestCache_GC(t *testing.T) {
|
|||||||
func TestCache_GC2(t *testing.T) {
|
func TestCache_GC2(t *testing.T) {
|
||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
cache := NewCache()
|
cache1 := NewCache(NewPiecesOption(32))
|
||||||
for i := 0; i < 1_000_000; i++ {
|
for i := 0; i < 1_000_000; i++ {
|
||||||
cache.Write(strconv.Itoa(i), i, time.Now().Unix()+int64(rands.Int(0, 100)))
|
cache1.Write(strconv.Itoa(i), i, time.Now().Unix()+int64(rands.Int(0, 10)))
|
||||||
|
}
|
||||||
|
|
||||||
|
cache2 := NewCache(NewPiecesOption(5))
|
||||||
|
for i := 0; i < 1_000_000; i++ {
|
||||||
|
cache2.Write(strconv.Itoa(i), i, time.Now().Unix()+int64(rands.Int(0, 10)))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
t.Log(cache.Count(), "items")
|
t.Log(cache1.Count(), "items", cache2.Count(), "items")
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
internal/ttlcache/manager.go
Normal file
54
internal/ttlcache/manager.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package ttlcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SharedManager = NewManager()
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
ticker *time.Ticker
|
||||||
|
locker sync.Mutex
|
||||||
|
|
||||||
|
cacheMap map[*Cache]zero.Zero
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager() *Manager {
|
||||||
|
var manager = &Manager{
|
||||||
|
ticker: time.NewTicker(3 * time.Second),
|
||||||
|
cacheMap: map[*Cache]zero.Zero{},
|
||||||
|
}
|
||||||
|
|
||||||
|
goman.New(func() {
|
||||||
|
manager.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Manager) init() {
|
||||||
|
for range this.ticker.C {
|
||||||
|
this.locker.Lock()
|
||||||
|
for cache := range this.cacheMap {
|
||||||
|
cache.GC()
|
||||||
|
}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Manager) Add(cache *Cache) {
|
||||||
|
this.locker.Lock()
|
||||||
|
this.cacheMap[cache] = zero.New()
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Manager) Remove(cache *Cache) {
|
||||||
|
this.locker.Lock()
|
||||||
|
delete(this.cacheMap, cache)
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
var BytePool1024 = NewBytePool(20480, 1024)
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// pool for get byte slice
|
var BytePool1k = NewBytePool(20480, 1024)
|
||||||
|
var BytePool4k = NewBytePool(20480, 4*1024)
|
||||||
|
var BytePool16k = NewBytePool(40960, 16*1024)
|
||||||
|
var BytePool32k = NewBytePool(20480, 32*1024)
|
||||||
|
|
||||||
|
// BytePool pool for get byte slice
|
||||||
type BytePool struct {
|
type BytePool struct {
|
||||||
c chan []byte
|
c chan []byte
|
||||||
length int
|
maxSize int
|
||||||
|
length int
|
||||||
lastSize int
|
hasNew bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新对象
|
// NewBytePool 创建新对象
|
||||||
func NewBytePool(maxSize, length int) *BytePool {
|
func NewBytePool(maxSize, length int) *BytePool {
|
||||||
if maxSize <= 0 {
|
if maxSize <= 0 {
|
||||||
maxSize = 1024
|
maxSize = 1024
|
||||||
@@ -18,24 +27,47 @@ func NewBytePool(maxSize, length int) *BytePool {
|
|||||||
if length <= 0 {
|
if length <= 0 {
|
||||||
length = 128
|
length = 128
|
||||||
}
|
}
|
||||||
pool := &BytePool{
|
var pool = &BytePool{
|
||||||
c: make(chan []byte, maxSize),
|
c: make(chan []byte, maxSize),
|
||||||
length: length,
|
maxSize: maxSize,
|
||||||
|
length: length,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool.init()
|
||||||
|
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取一个新的byte slice
|
// 初始化
|
||||||
|
func (this *BytePool) init() {
|
||||||
|
var ticker = time.NewTicker(2 * time.Minute)
|
||||||
|
if Tea.IsTesting() {
|
||||||
|
ticker = time.NewTicker(5 * time.Second)
|
||||||
|
}
|
||||||
|
goman.New(func() {
|
||||||
|
for range ticker.C {
|
||||||
|
if this.hasNew {
|
||||||
|
this.hasNew = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Purge()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取一个新的byte slice
|
||||||
func (this *BytePool) Get() (b []byte) {
|
func (this *BytePool) Get() (b []byte) {
|
||||||
select {
|
select {
|
||||||
case b = <-this.c:
|
case b = <-this.c:
|
||||||
default:
|
default:
|
||||||
b = make([]byte, this.length)
|
b = make([]byte, this.length)
|
||||||
|
this.hasNew = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 放回一个使用过的byte slice
|
// Put 放回一个使用过的byte slice
|
||||||
func (this *BytePool) Put(b []byte) {
|
func (this *BytePool) Put(b []byte) {
|
||||||
if cap(b) != this.length {
|
if cap(b) != this.length {
|
||||||
return
|
return
|
||||||
@@ -47,7 +79,30 @@ func (this *BytePool) Put(b []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前的数量
|
// Length 单个字节slice长度
|
||||||
|
func (this *BytePool) Length() int {
|
||||||
|
return this.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size 当前的数量
|
||||||
func (this *BytePool) Size() int {
|
func (this *BytePool) Size() int {
|
||||||
return len(this.c)
|
return len(this.c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge 清理
|
||||||
|
func (this *BytePool) Purge() {
|
||||||
|
// 1%
|
||||||
|
var count = len(this.c) / 100
|
||||||
|
if count == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
select {
|
||||||
|
case <-this.c:
|
||||||
|
default:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,26 @@ func TestNewBytePool(t *testing.T) {
|
|||||||
a.IsTrue(len(pool.c) == 5)
|
a.IsTrue(len(pool.c) == 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBytePool_Memory(t *testing.T) {
|
||||||
|
var stat1 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat1)
|
||||||
|
|
||||||
|
var pool = NewBytePool(20480, 32*1024)
|
||||||
|
for i := 0; i < 20480; i++ {
|
||||||
|
pool.Put(make([]byte, 32*1024))
|
||||||
|
}
|
||||||
|
|
||||||
|
//pool.Purge()
|
||||||
|
|
||||||
|
//time.Sleep(60 * time.Second)
|
||||||
|
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
var stat2 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat2)
|
||||||
|
t.Log((stat2.HeapInuse-stat1.HeapInuse)/1024/1024, "MB,", pool.Size(), "slices")
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkBytePool_Get(b *testing.B) {
|
func BenchmarkBytePool_Get(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
|||||||
64
internal/utils/expires/id_key_map.go
Normal file
64
internal/utils/expires/id_key_map.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package expires
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type IdKeyMap struct {
|
||||||
|
idKeys map[int64]string // id => key
|
||||||
|
keyIds map[string]int64 // key => id
|
||||||
|
|
||||||
|
locker sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdKeyMap() *IdKeyMap {
|
||||||
|
return &IdKeyMap{
|
||||||
|
idKeys: map[int64]string{},
|
||||||
|
keyIds: map[string]int64{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IdKeyMap) Add(id int64, key string) {
|
||||||
|
oldKey, ok := this.idKeys[id]
|
||||||
|
if ok {
|
||||||
|
delete(this.keyIds, oldKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldId, ok := this.keyIds[key]
|
||||||
|
if ok {
|
||||||
|
delete(this.idKeys, oldId)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.idKeys[id] = key
|
||||||
|
this.keyIds[key] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IdKeyMap) Key(id int64) (key string, ok bool) {
|
||||||
|
key, ok = this.idKeys[id]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IdKeyMap) Id(key string) (id int64, ok bool) {
|
||||||
|
id, ok = this.keyIds[key]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IdKeyMap) DeleteId(id int64) {
|
||||||
|
key, ok := this.idKeys[id]
|
||||||
|
if ok {
|
||||||
|
delete(this.keyIds, key)
|
||||||
|
}
|
||||||
|
delete(this.idKeys, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IdKeyMap) DeleteKey(key string) {
|
||||||
|
id, ok := this.keyIds[key]
|
||||||
|
if ok {
|
||||||
|
delete(this.idKeys, id)
|
||||||
|
}
|
||||||
|
delete(this.keyIds, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *IdKeyMap) Len() int {
|
||||||
|
return len(this.idKeys)
|
||||||
|
}
|
||||||
46
internal/utils/expires/id_key_map_test.go
Normal file
46
internal/utils/expires/id_key_map_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package expires
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewIdKeyMap(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
|
||||||
|
var m = NewIdKeyMap()
|
||||||
|
m.Add(1, "1")
|
||||||
|
m.Add(1, "2")
|
||||||
|
m.Add(100, "100")
|
||||||
|
logs.PrintAsJSON(m.idKeys, t)
|
||||||
|
logs.PrintAsJSON(m.keyIds, t)
|
||||||
|
|
||||||
|
{
|
||||||
|
k, ok := m.Key(1)
|
||||||
|
a.IsTrue(ok)
|
||||||
|
a.IsTrue(k == "2")
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
_, ok := m.Key(2)
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.DeleteKey("2")
|
||||||
|
|
||||||
|
{
|
||||||
|
_, ok := m.Key(1)
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
logs.PrintAsJSON(m.idKeys, t)
|
||||||
|
logs.PrintAsJSON(m.keyIds, t)
|
||||||
|
|
||||||
|
m.DeleteId(100)
|
||||||
|
|
||||||
|
logs.PrintAsJSON(m.idKeys, t)
|
||||||
|
logs.PrintAsJSON(m.keyIds, t)
|
||||||
|
}
|
||||||
@@ -1,43 +1,53 @@
|
|||||||
package expires
|
package expires
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ItemMap = map[int64]bool
|
type ItemMap = map[int64]zero.Zero
|
||||||
|
|
||||||
type List struct {
|
type List struct {
|
||||||
expireMap map[int64]ItemMap // expires timestamp => map[id]bool
|
expireMap map[int64]ItemMap // expires timestamp => map[id]ItemMap
|
||||||
itemsMap map[int64]int64 // itemId => timestamp
|
itemsMap map[int64]int64 // itemId => timestamp
|
||||||
|
|
||||||
locker sync.Mutex
|
locker sync.Mutex
|
||||||
ticker *time.Ticker
|
|
||||||
|
gcCallback func(itemId int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewList() *List {
|
func NewList() *List {
|
||||||
return &List{
|
var list = &List{
|
||||||
expireMap: map[int64]ItemMap{},
|
expireMap: map[int64]ItemMap{},
|
||||||
itemsMap: map[int64]int64{},
|
itemsMap: map[int64]int64{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedManager.Add(list)
|
||||||
|
|
||||||
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add 添加条目
|
||||||
|
// 如果条目已经存在,则覆盖
|
||||||
func (this *List) Add(itemId int64, expiresAt int64) {
|
func (this *List) Add(itemId int64, expiresAt int64) {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
// 是否已经存在
|
// 是否已经存在
|
||||||
_, ok := this.itemsMap[itemId]
|
oldExpiresAt, ok := this.itemsMap[itemId]
|
||||||
if ok {
|
if ok {
|
||||||
this.removeItem(itemId)
|
if oldExpiresAt == expiresAt {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(this.expireMap, oldExpiresAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
expireItemMap, ok := this.expireMap[expiresAt]
|
expireItemMap, ok := this.expireMap[expiresAt]
|
||||||
if ok {
|
if ok {
|
||||||
expireItemMap[itemId] = true
|
expireItemMap[itemId] = zero.New()
|
||||||
} else {
|
} else {
|
||||||
expireItemMap = ItemMap{
|
expireItemMap = ItemMap{
|
||||||
itemId: true,
|
itemId: zero.New(),
|
||||||
}
|
}
|
||||||
this.expireMap[expiresAt] = expireItemMap
|
this.expireMap[expiresAt] = expireItemMap
|
||||||
}
|
}
|
||||||
@@ -56,33 +66,16 @@ func (this *List) GC(timestamp int64, callback func(itemId int64)) {
|
|||||||
itemMap := this.gcItems(timestamp)
|
itemMap := this.gcItems(timestamp)
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|
||||||
for itemId := range itemMap {
|
if callback != nil {
|
||||||
callback(itemId)
|
for itemId := range itemMap {
|
||||||
|
callback(itemId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *List) StartGC(callback func(itemId int64)) {
|
func (this *List) OnGC(callback func(itemId int64)) *List {
|
||||||
this.ticker = time.NewTicker(1 * time.Second)
|
this.gcCallback = callback
|
||||||
lastTimestamp := int64(0)
|
return this
|
||||||
for range this.ticker.C {
|
|
||||||
timestamp := time.Now().Unix()
|
|
||||||
if lastTimestamp == 0 {
|
|
||||||
lastTimestamp = timestamp - 3600
|
|
||||||
}
|
|
||||||
|
|
||||||
if timestamp >= lastTimestamp {
|
|
||||||
for i := lastTimestamp; i <= timestamp; i++ {
|
|
||||||
this.GC(i, callback)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := timestamp; i <= lastTimestamp; i++ {
|
|
||||||
this.GC(i, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 这样做是为了防止系统时钟突变
|
|
||||||
lastTimestamp = timestamp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *List) removeItem(itemId int64) {
|
func (this *List) removeItem(itemId int64) {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package expires
|
package expires
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
"math"
|
"math"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -24,12 +26,19 @@ func TestList_Add(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestList_Add_Overwrite(t *testing.T) {
|
func TestList_Add_Overwrite(t *testing.T) {
|
||||||
|
var timestamp = time.Now().Unix()
|
||||||
|
|
||||||
list := NewList()
|
list := NewList()
|
||||||
list.Add(1, time.Now().Unix()+1)
|
list.Add(1, timestamp+1)
|
||||||
list.Add(1, time.Now().Unix()+1)
|
list.Add(1, timestamp+1)
|
||||||
list.Add(1, time.Now().Unix()+2)
|
list.Add(1, timestamp+2)
|
||||||
logs.PrintAsJSON(list.expireMap, t)
|
logs.PrintAsJSON(list.expireMap, t)
|
||||||
logs.PrintAsJSON(list.itemsMap, t)
|
logs.PrintAsJSON(list.itemsMap, t)
|
||||||
|
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsTrue(len(list.itemsMap) == 1)
|
||||||
|
a.IsTrue(len(list.expireMap) == 1)
|
||||||
|
a.IsTrue(list.itemsMap[1] == timestamp+2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestList_Remove(t *testing.T) {
|
func TestList_Remove(t *testing.T) {
|
||||||
@@ -63,11 +72,13 @@ func TestList_Start_GC(t *testing.T) {
|
|||||||
list.Add(7, time.Now().Unix()+6)
|
list.Add(7, time.Now().Unix()+6)
|
||||||
list.Add(8, time.Now().Unix()+6)
|
list.Add(8, time.Now().Unix()+6)
|
||||||
|
|
||||||
|
list.OnGC(func(itemId int64) {
|
||||||
|
t.Log("gc:", itemId, timeutil.Format("H:i:s"))
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
list.StartGC(func(itemId int64) {
|
SharedManager.Add(list)
|
||||||
t.Log("gc:", itemId, timeutil.Format("H:i:s"))
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
})
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(20 * time.Second)
|
time.Sleep(20 * time.Second)
|
||||||
@@ -75,7 +86,10 @@ func TestList_Start_GC(t *testing.T) {
|
|||||||
|
|
||||||
func TestList_ManyItems(t *testing.T) {
|
func TestList_ManyItems(t *testing.T) {
|
||||||
list := NewList()
|
list := NewList()
|
||||||
for i := 0; i < 100_000; i++ {
|
for i := 0; i < 1_000; i++ {
|
||||||
|
list.Add(int64(i), time.Now().Unix())
|
||||||
|
}
|
||||||
|
for i := 0; i < 1_000; i++ {
|
||||||
list.Add(int64(i), time.Now().Unix()+1)
|
list.Add(int64(i), time.Now().Unix()+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,35 +99,69 @@ func TestList_ManyItems(t *testing.T) {
|
|||||||
count++
|
count++
|
||||||
})
|
})
|
||||||
t.Log("gc", count, "items")
|
t.Log("gc", count, "items")
|
||||||
t.Log(time.Since(now).Seconds()*1000, "ms")
|
t.Log(time.Now().Sub(now))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestList_Map_Performance(t *testing.T) {
|
func TestList_Map_Performance(t *testing.T) {
|
||||||
t.Log("max uint32", math.MaxUint32)
|
t.Log("max uint32", math.MaxUint32)
|
||||||
|
|
||||||
|
var timestamp = time.Now().Unix()
|
||||||
|
|
||||||
{
|
{
|
||||||
m := map[int64]int64{}
|
m := map[int64]int64{}
|
||||||
for i := 0; i < 1_000_000; i++ {
|
for i := 0; i < 1_000_000; i++ {
|
||||||
m[int64(i)] = time.Now().Unix()
|
m[int64(i)] = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for i := 0; i < 100_000; i++ {
|
for i := 0; i < 100_000; i++ {
|
||||||
delete(m, int64(i))
|
delete(m, int64(i))
|
||||||
}
|
}
|
||||||
t.Log(time.Since(now).Seconds()*1000, "ms")
|
t.Log(time.Now().Sub(now))
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m := map[uint64]int64{}
|
||||||
|
for i := 0; i < 1_000_000; i++ {
|
||||||
|
m[uint64(i)] = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
for i := 0; i < 100_000; i++ {
|
||||||
|
delete(m, uint64(i))
|
||||||
|
}
|
||||||
|
t.Log(time.Now().Sub(now))
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
m := map[uint32]int64{}
|
m := map[uint32]int64{}
|
||||||
for i := 0; i < 1_000_000; i++ {
|
for i := 0; i < 1_000_000; i++ {
|
||||||
m[uint32(i)] = time.Now().Unix()
|
m[uint32(i)] = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
for i := 0; i < 100_000; i++ {
|
for i := 0; i < 100_000; i++ {
|
||||||
delete(m, uint32(i))
|
delete(m, uint32(i))
|
||||||
}
|
}
|
||||||
t.Log(time.Since(now).Seconds()*1000, "ms")
|
t.Log(time.Now().Sub(now))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Map_Uint64(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
var timestamp = uint64(time.Now().Unix())
|
||||||
|
|
||||||
|
var i uint64
|
||||||
|
var count uint64 = 1_000_000
|
||||||
|
|
||||||
|
m := map[uint64]uint64{}
|
||||||
|
for i = 0; i < count; i++ {
|
||||||
|
m[i] = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
for i = 0; i < count; i++ {
|
||||||
|
_ = m[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
internal/utils/expires/manager.go
Normal file
72
internal/utils/expires/manager.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package expires
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SharedManager = NewManager()
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
listMap map[*List]zero.Zero
|
||||||
|
locker sync.Mutex
|
||||||
|
ticker *time.Ticker
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager() *Manager {
|
||||||
|
var manager = &Manager{
|
||||||
|
listMap: map[*List]zero.Zero{},
|
||||||
|
ticker: time.NewTicker(1 * time.Second),
|
||||||
|
}
|
||||||
|
goman.New(func() {
|
||||||
|
manager.init()
|
||||||
|
})
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Manager) init() {
|
||||||
|
var lastTimestamp = int64(0)
|
||||||
|
for range this.ticker.C {
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
if lastTimestamp == 0 {
|
||||||
|
lastTimestamp = timestamp - 3600
|
||||||
|
}
|
||||||
|
|
||||||
|
if timestamp >= lastTimestamp {
|
||||||
|
for i := lastTimestamp; i <= timestamp; i++ {
|
||||||
|
this.locker.Lock()
|
||||||
|
for list := range this.listMap {
|
||||||
|
list.GC(i, list.gcCallback)
|
||||||
|
}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := timestamp; i <= lastTimestamp; i++ {
|
||||||
|
this.locker.Lock()
|
||||||
|
for list := range this.listMap {
|
||||||
|
list.GC(i, list.gcCallback)
|
||||||
|
}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这样做是为了防止系统时钟突变
|
||||||
|
lastTimestamp = timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Manager) Add(list *List) {
|
||||||
|
this.locker.Lock()
|
||||||
|
this.listMap[list] = zero.New()
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Manager) Remove(list *List) {
|
||||||
|
this.locker.Lock()
|
||||||
|
delete(this.listMap, list)
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -15,7 +16,9 @@ var SharedFreeHoursManager = NewFreeHoursManager()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
go SharedFreeHoursManager.Start()
|
goman.New(func() {
|
||||||
|
SharedFreeHoursManager.Start()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user