From 6a7f12075503b977dbc6adc166770e25cd2525ea Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Tue, 7 Jun 2022 20:18:22 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0lego=20=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/robot/user.go | 2 +- comm/core.go | 5 +- comm/usersession.go | 5 +- go.mod | 35 +- go.sum | 195 +--- lego/base/core.go | 85 ++ lego/base/rpcx/options.go | 66 ++ lego/base/rpcx/service.go | 358 ++++++++ lego/base/rpcx/servicesession.go | 64 ++ lego/core/cbase/modulebase.go | 58 ++ lego/core/cbase/modulecompbase.go | 19 + lego/core/cbase/moduleoptions.go | 13 + lego/core/cbase/servicebase.go | 177 ++++ lego/core/cbase/servicecompbase.go | 24 + lego/core/cbase/servicecompoptions.go | 17 + lego/core/core.go | 140 +++ lego/core/errorcode.go | 41 + lego/lego.go | 37 + lego/sys/blockcache/cache.go | 134 +++ lego/sys/blockcache/core.go | 44 + lego/sys/blockcache/options.go | 57 ++ lego/sys/blockcache/sys_test.go | 58 ++ lego/sys/cachego/cache.go | 820 +++++++++++++++++ lego/sys/cachego/cachego.go | 47 + lego/sys/cachego/core.go | 187 ++++ lego/sys/cachego/item.go | 19 + lego/sys/cachego/options.go | 38 + lego/sys/cron/core.go | 48 + lego/sys/cron/cron.go | 32 + lego/sys/cron/options.go | 28 + lego/sys/cron/sys_test.go | 127 +++ lego/sys/event/core.go | 55 ++ lego/sys/event/event.go | 89 ++ lego/sys/event/options.go | 28 + lego/sys/event/sys_test.go | 18 + lego/sys/gin/binding/binding.go | 73 ++ lego/sys/gin/binding/default_validator.go | 97 ++ lego/sys/gin/binding/form.go | 62 ++ lego/sys/gin/binding/form_mapping.go | 403 ++++++++ lego/sys/gin/binding/header.go | 34 + lego/sys/gin/binding/json.go | 55 ++ lego/sys/gin/binding/msgpack.go | 31 + .../sys/gin/binding/multipart_form_mapping.go | 74 ++ lego/sys/gin/binding/protobuf.go | 35 + lego/sys/gin/binding/query.go | 17 + lego/sys/gin/binding/uri.go | 14 + lego/sys/gin/binding/xml.go | 33 + lego/sys/gin/binding/yaml.go | 31 + lego/sys/gin/core.go | 73 ++ lego/sys/gin/engine/context.go | 858 ++++++++++++++++++ lego/sys/gin/engine/core.go | 49 + lego/sys/gin/engine/engine.go | 511 +++++++++++ lego/sys/gin/engine/errors.go | 199 ++++ lego/sys/gin/engine/fs.go | 26 + lego/sys/gin/engine/response_writer.go | 95 ++ lego/sys/gin/engine/routergroup.go | 169 ++++ lego/sys/gin/engine/tree.go | 838 +++++++++++++++++ lego/sys/gin/engine/utils.go | 201 ++++ lego/sys/gin/gin.go | 192 ++++ lego/sys/gin/middleware/cross/cross.go | 28 + lego/sys/gin/middleware/jwt/jwt.go | 83 ++ lego/sys/gin/middleware/logger/logger.go | 85 ++ lego/sys/gin/middleware/recovery/recovery.go | 162 ++++ lego/sys/gin/options.go | 76 ++ lego/sys/gin/render/data.go | 21 + lego/sys/gin/render/html.go | 84 ++ lego/sys/gin/render/json.go | 189 ++++ lego/sys/gin/render/protobuf.go | 32 + lego/sys/gin/render/reader.go | 44 + lego/sys/gin/render/redirect.go | 25 + lego/sys/gin/render/render.go | 19 + lego/sys/gin/render/text.go | 37 + lego/sys/gin/render/xml.go | 24 + lego/sys/gin/render/yaml.go | 32 + lego/sys/gin/sys_test.go | 42 + lego/sys/log/core.go | 79 ++ lego/sys/log/options.go | 114 +++ lego/sys/log/sys_test.go | 18 + lego/sys/log/zaplog.go | 190 ++++ lego/sys/mgo/core.go | 148 +++ lego/sys/mgo/mgo.go | 152 ++++ lego/sys/mgo/options.go | 64 ++ lego/sys/mgo/sys_test.go | 214 +++++ lego/sys/nacos/core.go | 98 ++ lego/sys/nacos/nacos.go | 143 +++ lego/sys/nacos/options.go | 86 ++ lego/sys/nacos/sys_test.go | 43 + lego/sys/proto/core.go | 63 ++ lego/sys/proto/defmessage.go | 218 +++++ lego/sys/proto/options.go | 68 ++ lego/sys/proto/proto.go | 48 + lego/sys/redis/cluster/core.go | 93 ++ lego/sys/redis/cluster/hash.go | 170 ++++ lego/sys/redis/cluster/key.go | 88 ++ lego/sys/redis/cluster/list.go | 205 +++++ lego/sys/redis/cluster/set.go | 185 ++++ lego/sys/redis/cluster/string.go | 180 ++++ lego/sys/redis/cluster/zset.go | 217 +++++ lego/sys/redis/core.go | 453 +++++++++ lego/sys/redis/lock.go | 53 ++ lego/sys/redis/options.go | 150 +++ lego/sys/redis/redis.go | 379 ++++++++ lego/sys/redis/single/core.go | 92 ++ lego/sys/redis/single/hash.go | 170 ++++ lego/sys/redis/single/key.go | 87 ++ lego/sys/redis/single/list.go | 205 +++++ lego/sys/redis/single/set.go | 185 ++++ lego/sys/redis/single/string.go | 180 ++++ lego/sys/redis/single/zset.go | 217 +++++ lego/sys/redis/sys_test.go | 143 +++ lego/sys/redis/utils.go | 1 + lego/sys/registry/consul.go | 391 ++++++++ lego/sys/registry/core.go | 81 ++ lego/sys/registry/nacos.go | 413 +++++++++ lego/sys/registry/options.go | 135 +++ lego/sys/registry/registry.go | 10 + lego/sys/registry/sys_test.go | 117 +++ lego/sys/registry/zookeeper.go | 10 + lego/sys/rpcx/client.go | 31 + lego/sys/rpcx/core.go | 69 ++ lego/sys/rpcx/options.go | 63 ++ lego/sys/rpcx/rpcx.go | 47 + lego/sys/rpcx/service.go | 47 + lego/sys/timewheel/core.go | 70 ++ lego/sys/timewheel/options.go | 74 ++ lego/sys/timewheel/task_pool.go | 34 + lego/sys/timewheel/timewheel.go | 435 +++++++++ lego/sys/timewheel/timewheel_test.go | 45 + lego/sys/workerpools/core.go | 39 + lego/sys/workerpools/options.go | 84 ++ lego/sys/workerpools/pools.go | 194 ++++ lego/sys/workerpools/sys_test.go | 40 + lego/utils/container/BeeMap.go | 84 ++ lego/utils/container/ConcurrentMap.go | 249 +++++ lego/utils/container/Deque.go | 243 +++++ lego/utils/container/Queue.go | 46 + lego/utils/container/addr/addr.go | 112 +++ lego/utils/container/addr/addr_test.go | 38 + lego/utils/container/id/Id.go | 18 + lego/utils/container/ip/ip.go | 54 ++ lego/utils/container/lkqueue.go | 83 ++ lego/utils/container/sortslice/interface.go | 35 + lego/utils/container/sortslice/uint32.go | 27 + lego/utils/container/version/test_version.go | 15 + lego/utils/container/version/version.go | 66 ++ lego/utils/convert/convert.go | 125 +++ lego/utils/copy/interface.go | 77 ++ lego/utils/flietools/flietools.go | 84 ++ lego/utils/flietools/ziptools.go | 45 + lego/utils/mapstructure/error.go | 32 + lego/utils/mapstructure/mapstructure.go | 857 +++++++++++++++++ modules/core.go | 3 +- modules/gate_comp.go | 6 +- modules/gateway/agent.go | 5 +- modules/gateway/agentmgr_comp.go | 4 +- modules/gateway/core.go | 4 +- modules/gateway/module.go | 6 +- modules/gateway/options.go | 2 +- modules/gateway/wservice_comp.go | 11 +- modules/mail/api_comp.go | 2 +- modules/mail/configure_comp.go | 4 +- modules/mail/module.go | 2 +- modules/modulebase.go | 9 +- modules/pack/api_comp.go | 4 +- modules/pack/configure_comp.go | 6 +- modules/pack/module.go | 2 +- modules/user/login_comp.go | 3 +- modules/user/module.go | 2 +- modules/web/api_comp.go | 10 +- modules/web/module.go | 2 +- modules/web/options.go | 2 +- services/comp_gateroute.go | 9 +- services/gateway/main.go | 6 +- services/servicebase.go | 4 +- services/web/main.go | 8 +- services/worker/main.go | 8 +- sys/cache/cache.go | 2 +- sys/cache/mail.go | 4 +- sys/cache/options.go | 2 +- sys/cache/pack.go | 4 +- sys/cache/user_test.go | 3 +- sys/configure/options.go | 2 +- sys/db/db.go | 2 +- sys/db/mail.go | 3 +- sys/db/options.go | 2 +- sys/db/pack.go | 3 +- sys/db/user.go | 3 +- sys/db/user_test.go | 3 +- utils/strings.go | 2 +- 189 files changed, 18115 insertions(+), 285 deletions(-) create mode 100644 lego/base/core.go create mode 100644 lego/base/rpcx/options.go create mode 100644 lego/base/rpcx/service.go create mode 100644 lego/base/rpcx/servicesession.go create mode 100644 lego/core/cbase/modulebase.go create mode 100644 lego/core/cbase/modulecompbase.go create mode 100644 lego/core/cbase/moduleoptions.go create mode 100644 lego/core/cbase/servicebase.go create mode 100644 lego/core/cbase/servicecompbase.go create mode 100644 lego/core/cbase/servicecompoptions.go create mode 100644 lego/core/core.go create mode 100644 lego/core/errorcode.go create mode 100644 lego/lego.go create mode 100644 lego/sys/blockcache/cache.go create mode 100644 lego/sys/blockcache/core.go create mode 100644 lego/sys/blockcache/options.go create mode 100644 lego/sys/blockcache/sys_test.go create mode 100644 lego/sys/cachego/cache.go create mode 100644 lego/sys/cachego/cachego.go create mode 100644 lego/sys/cachego/core.go create mode 100644 lego/sys/cachego/item.go create mode 100644 lego/sys/cachego/options.go create mode 100644 lego/sys/cron/core.go create mode 100644 lego/sys/cron/cron.go create mode 100644 lego/sys/cron/options.go create mode 100644 lego/sys/cron/sys_test.go create mode 100644 lego/sys/event/core.go create mode 100644 lego/sys/event/event.go create mode 100644 lego/sys/event/options.go create mode 100644 lego/sys/event/sys_test.go create mode 100644 lego/sys/gin/binding/binding.go create mode 100644 lego/sys/gin/binding/default_validator.go create mode 100644 lego/sys/gin/binding/form.go create mode 100644 lego/sys/gin/binding/form_mapping.go create mode 100644 lego/sys/gin/binding/header.go create mode 100644 lego/sys/gin/binding/json.go create mode 100644 lego/sys/gin/binding/msgpack.go create mode 100644 lego/sys/gin/binding/multipart_form_mapping.go create mode 100644 lego/sys/gin/binding/protobuf.go create mode 100644 lego/sys/gin/binding/query.go create mode 100644 lego/sys/gin/binding/uri.go create mode 100644 lego/sys/gin/binding/xml.go create mode 100644 lego/sys/gin/binding/yaml.go create mode 100644 lego/sys/gin/core.go create mode 100644 lego/sys/gin/engine/context.go create mode 100644 lego/sys/gin/engine/core.go create mode 100644 lego/sys/gin/engine/engine.go create mode 100644 lego/sys/gin/engine/errors.go create mode 100644 lego/sys/gin/engine/fs.go create mode 100644 lego/sys/gin/engine/response_writer.go create mode 100644 lego/sys/gin/engine/routergroup.go create mode 100644 lego/sys/gin/engine/tree.go create mode 100644 lego/sys/gin/engine/utils.go create mode 100644 lego/sys/gin/gin.go create mode 100644 lego/sys/gin/middleware/cross/cross.go create mode 100644 lego/sys/gin/middleware/jwt/jwt.go create mode 100644 lego/sys/gin/middleware/logger/logger.go create mode 100644 lego/sys/gin/middleware/recovery/recovery.go create mode 100644 lego/sys/gin/options.go create mode 100644 lego/sys/gin/render/data.go create mode 100644 lego/sys/gin/render/html.go create mode 100644 lego/sys/gin/render/json.go create mode 100644 lego/sys/gin/render/protobuf.go create mode 100644 lego/sys/gin/render/reader.go create mode 100644 lego/sys/gin/render/redirect.go create mode 100644 lego/sys/gin/render/render.go create mode 100644 lego/sys/gin/render/text.go create mode 100644 lego/sys/gin/render/xml.go create mode 100644 lego/sys/gin/render/yaml.go create mode 100644 lego/sys/gin/sys_test.go create mode 100644 lego/sys/log/core.go create mode 100644 lego/sys/log/options.go create mode 100644 lego/sys/log/sys_test.go create mode 100644 lego/sys/log/zaplog.go create mode 100644 lego/sys/mgo/core.go create mode 100644 lego/sys/mgo/mgo.go create mode 100644 lego/sys/mgo/options.go create mode 100644 lego/sys/mgo/sys_test.go create mode 100644 lego/sys/nacos/core.go create mode 100644 lego/sys/nacos/nacos.go create mode 100644 lego/sys/nacos/options.go create mode 100644 lego/sys/nacos/sys_test.go create mode 100644 lego/sys/proto/core.go create mode 100644 lego/sys/proto/defmessage.go create mode 100644 lego/sys/proto/options.go create mode 100644 lego/sys/proto/proto.go create mode 100644 lego/sys/redis/cluster/core.go create mode 100644 lego/sys/redis/cluster/hash.go create mode 100644 lego/sys/redis/cluster/key.go create mode 100644 lego/sys/redis/cluster/list.go create mode 100644 lego/sys/redis/cluster/set.go create mode 100644 lego/sys/redis/cluster/string.go create mode 100644 lego/sys/redis/cluster/zset.go create mode 100644 lego/sys/redis/core.go create mode 100644 lego/sys/redis/lock.go create mode 100644 lego/sys/redis/options.go create mode 100644 lego/sys/redis/redis.go create mode 100644 lego/sys/redis/single/core.go create mode 100644 lego/sys/redis/single/hash.go create mode 100644 lego/sys/redis/single/key.go create mode 100644 lego/sys/redis/single/list.go create mode 100644 lego/sys/redis/single/set.go create mode 100644 lego/sys/redis/single/string.go create mode 100644 lego/sys/redis/single/zset.go create mode 100644 lego/sys/redis/sys_test.go create mode 100644 lego/sys/redis/utils.go create mode 100644 lego/sys/registry/consul.go create mode 100644 lego/sys/registry/core.go create mode 100644 lego/sys/registry/nacos.go create mode 100644 lego/sys/registry/options.go create mode 100644 lego/sys/registry/registry.go create mode 100644 lego/sys/registry/sys_test.go create mode 100644 lego/sys/registry/zookeeper.go create mode 100644 lego/sys/rpcx/client.go create mode 100644 lego/sys/rpcx/core.go create mode 100644 lego/sys/rpcx/options.go create mode 100644 lego/sys/rpcx/rpcx.go create mode 100644 lego/sys/rpcx/service.go create mode 100644 lego/sys/timewheel/core.go create mode 100644 lego/sys/timewheel/options.go create mode 100644 lego/sys/timewheel/task_pool.go create mode 100644 lego/sys/timewheel/timewheel.go create mode 100644 lego/sys/timewheel/timewheel_test.go create mode 100644 lego/sys/workerpools/core.go create mode 100644 lego/sys/workerpools/options.go create mode 100644 lego/sys/workerpools/pools.go create mode 100644 lego/sys/workerpools/sys_test.go create mode 100644 lego/utils/container/BeeMap.go create mode 100644 lego/utils/container/ConcurrentMap.go create mode 100644 lego/utils/container/Deque.go create mode 100644 lego/utils/container/Queue.go create mode 100644 lego/utils/container/addr/addr.go create mode 100644 lego/utils/container/addr/addr_test.go create mode 100644 lego/utils/container/id/Id.go create mode 100644 lego/utils/container/ip/ip.go create mode 100644 lego/utils/container/lkqueue.go create mode 100644 lego/utils/container/sortslice/interface.go create mode 100644 lego/utils/container/sortslice/uint32.go create mode 100644 lego/utils/container/version/test_version.go create mode 100644 lego/utils/container/version/version.go create mode 100644 lego/utils/convert/convert.go create mode 100644 lego/utils/copy/interface.go create mode 100644 lego/utils/flietools/flietools.go create mode 100644 lego/utils/flietools/ziptools.go create mode 100644 lego/utils/mapstructure/error.go create mode 100644 lego/utils/mapstructure/mapstructure.go diff --git a/cmd/robot/user.go b/cmd/robot/user.go index cc4dd1b75..1c82da03b 100644 --- a/cmd/robot/user.go +++ b/cmd/robot/user.go @@ -4,7 +4,7 @@ import ( "go_dreamfactory/comm" "go_dreamfactory/pb" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/sys/log" ) func (r *Robot) handleUserMsg(msg *pb.UserMessage) { diff --git a/comm/core.go b/comm/core.go index a6d174229..79e227e42 100644 --- a/comm/core.go +++ b/comm/core.go @@ -4,8 +4,9 @@ import ( "go_dreamfactory/pb" "reflect" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" + "google.golang.org/protobuf/proto" ) diff --git a/comm/usersession.go b/comm/usersession.go index f8ecb4d27..d53026b8f 100644 --- a/comm/usersession.go +++ b/comm/usersession.go @@ -5,8 +5,9 @@ import ( "fmt" "go_dreamfactory/pb" - "github.com/liwei1dao/lego/base" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/sys/log" + "google.golang.org/protobuf/proto" ) diff --git a/go.mod b/go.mod index d3a36b9cb..d86cf2412 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,30 @@ module go_dreamfactory go 1.18 require ( + github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 + github.com/go-playground/validator/v10 v10.10.1 + github.com/go-redis/redis/v8 v8.11.5 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang/protobuf v1.5.2 github.com/gorilla/websocket v1.4.2 + github.com/hashicorp/consul/api v1.12.0 github.com/json-iterator/go v1.1.12 - github.com/liwei1dao/lego v0.0.0-20220531091126-a21bb3766fbc + github.com/mitchellh/hashstructure v1.1.0 + github.com/nacos-group/nacos-sdk-go v1.0.8 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/robfig/cron/v3 v3.0.1 + github.com/rs/xid v1.3.0 + github.com/satori/go.uuid v1.2.0 + github.com/smallnest/rpcx v1.7.4 github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.1 + github.com/ugorji/go/codec v1.2.7 go.mongodb.org/mongo-driver v1.5.1 + go.uber.org/zap v1.21.0 + golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 google.golang.org/protobuf v1.28.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -20,7 +35,6 @@ require ( github.com/apache/thrift v0.16.0 // indirect github.com/armon/go-metrics v0.3.10 // indirect github.com/aws/aws-sdk-go v1.34.28 // indirect - github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect github.com/cenk/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect @@ -39,15 +53,12 @@ require ( github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.10.1 // indirect - github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.3.0 // indirect github.com/grandcat/zeroconf v1.0.0 // indirect - github.com/hashicorp/consul/api v1.12.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.0 // indirect @@ -75,33 +86,25 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/miekg/dns v1.1.48 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nacos-group/nacos-sdk-go v1.0.8 // indirect - github.com/natefinch/lumberjack v2.0.0+incompatible // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/philhofer/fwd v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rpcxio/libkv v0.5.1-0.20210420120011-1fceaedca8a5 // indirect github.com/rs/cors v1.8.2 // indirect - github.com/rs/xid v1.3.0 // indirect github.com/rubyist/circuitbreaker v2.2.1+incompatible // indirect github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 // indirect - github.com/satori/go.uuid v1.2.0 // indirect github.com/smallnest/quick v0.0.0-20220103065406-780def6371e6 // indirect - github.com/smallnest/rpcx v1.7.4 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect github.com/tinylib/msgp v1.1.6 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect - github.com/ugorji/go/codec v1.2.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect @@ -115,19 +118,15 @@ require ( go.opentelemetry.io/otel/trace v1.6.3 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect - golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) - -// replace github.com/liwei1dao/lego => F:\work\go\lego diff --git a/go.sum b/go.sum index 77febd27a..fec3c0835 100644 --- a/go.sum +++ b/go.sum @@ -17,7 +17,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -30,7 +29,6 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -45,47 +43,25 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -firebase.google.com/go/v4 v4.7.1/go.mod h1:UgGSTOhEZVbB2L3dQ3z4pThDTiH869i8TDAZKnrHKbU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= -github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/ChimeraCoder/gojson v1.1.0/go.mod h1:nYbTQlu6hv8PETM15J927yM0zGj3njIldp72UT1MqSw= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/Shopify/sarama v1.30.1/go.mod h1:hGgx05L/DiW8XYBXeJdKIN6V2QUy2H6JqME5VT1NLRw= -github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/ZZMarquis/gm v1.3.2/go.mod h1:wWbjZYgruQVd7Bb8UkSN8ujU931kx2XUW6nZLCiDE0Q= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.885 h1:H7Mq9zsKIMvBaw7rEmbWULvSTF2UI/9lFvvm4d/NZdk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.885/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= -github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= -github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM= -github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= -github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/pulsar-client-go v0.8.0/go.mod h1:kpFNN3AqZWQEGcRnhu0rNhWLI91+6RDYqPbmNEiIsWs= -github.com/apache/pulsar-client-go/oauth2 v0.0.0-20220120090717-25e59572242e/go.mod h1:Xee4tgYLFpYcPMcTfBYWE1uKRzeciodGTSEDMzsR6i8= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAFAONtv2Dr7HUI= -github.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= @@ -93,13 +69,10 @@ github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8 github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.32.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= -github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= -github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -107,7 +80,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= @@ -133,30 +105,17 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= 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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= -github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-jump v0.0.0-20170409065014-e1f439676b57/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 h1:0wH6nO9QEa02Qx8sIQGw6ieKdz+BXjpccSOo9vXNl4U= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0= -github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.3.1/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc= github.com/edwingeng/doublejump v0.0.0-20210724020454-c82f1bcb3280 h1:t04U/MQCFMNbKQ1YdqsZe8MQIgYB4gGOX87CQIrStqw= github.com/edwingeng/doublejump v0.0.0-20210724020454-c82f1bcb3280/go.mod h1:+RhzcuwqMSuWjEJrOKR19t+Wc8Ca7OuUYQ//CReOIwI= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -176,18 +135,13 @@ github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 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.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -195,18 +149,13 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534 h1:dhy9OQKGBh4zVXbjwbxxHjRxMJtLXj3zfgpBYQaR4Q4= github.com/go-ping/ping v0.0.0-20211130115550-779d1e919534/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -227,7 +176,6 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= @@ -252,20 +200,13 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= -github.com/gocolly/colly/v2 v2.1.0/go.mod h1:I2MuhsLjQ+Ex+IzK3afNS8/1qP3AedHOusRPcRdC5o0= -github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godror/godror v0.25.3/go.mod h1:JgtdZ1iSaNoioa/B53BVVWji9J9iGPDDj2763T5d1So= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -302,7 +243,6 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -335,7 +275,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -348,13 +287,8 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE= @@ -362,7 +296,6 @@ github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfc github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= @@ -386,9 +319,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= -github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -397,14 +329,12 @@ github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -429,38 +359,21 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb-client-go/v2 v2.8.2/go.mod h1:x7Jo5UHHl+w8wu8UnGiNobDDHygojXwJX4mx7rXGKMk= github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/jawher/mow.cli v1.0.4/go.mod h1:5hQj2V8g+qYmLUVWqu4Wuja1pI57M83EChYLVZ0sMKk= -github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg= -github.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko= -github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jlaffaye/ftp v0.0.0-20220309012535-813c8a838452/go.mod h1:oZaomI+9/et52UBjvNU9LCIqmgt816+7ljXCx0EIPzo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -473,17 +386,13 @@ github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSg github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kavu/go_reuseport v1.5.0 h1:UNuiY2OblcqAtVDE8Gsg1kZz8zbBWg907sP1ceBV+bk= github.com/kavu/go_reuseport v1.5.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU= -github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -493,7 +402,6 @@ github.com/klauspost/reedsolomon v1.9.16 h1:mR0AwphBwqFv/I3B9AHtNKvzuowI1vrj8/3U github.com/klauspost/reedsolomon v1.9.16/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -506,8 +414,6 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo= @@ -516,20 +422,12 @@ github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgU github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linkedin/goavro/v2 v2.9.8/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA= -github.com/liwei1dao/dm v0.0.0-20211103094420-938edf103cf0/go.mod h1:YH8wwRWv57a88ZbPtflEhwCQDrcm9L9S8wl9y1m8SnQ= -github.com/liwei1dao/lego v0.0.0-20220531091126-a21bb3766fbc h1:rbrKM+1M18uV1v+fvI/CeQhygPO57UtFo2UiU+NWQJs= -github.com/liwei1dao/lego v0.0.0-20220531091126-a21bb3766fbc/go.mod h1:eEEe2/9fK64Bo1Mj30Hwdt5BZ8IwKbZ6ZMgCTu8DQ+4= github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0= github.com/lucas-clemente/quic-go v0.27.0 h1:v6WY87q9zD4dKASbG8hy/LpzAVNzEQzw8sEIeloJsc4= github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= @@ -542,25 +440,20 @@ github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWCh github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= github.com/marten-seemann/qtls-go1-18 v0.1.1 h1:qp7p7XXUFL7fpBvSS1sWD+uSqPvzNQK43DH+/qEkj0Y= github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -574,10 +467,8 @@ github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXx github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= @@ -595,20 +486,11 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nacos-group/nacos-sdk-go v1.0.8 h1:8pEm05Cdav9sQgJSv5kyvlgfz0SzFUUGI3pWX6SiSnM= github.com/nacos-group/nacos-sdk-go v1.0.8/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.1.0/go.mod h1:n3cvmLfBfnpV4JJRN7lRYCyZnw48ksGsbThGXEk4w9M= -github.com/nats-io/nats-server/v2 v2.1.9/go.mod h1:9qVyoewoYXzG1ME9ox0HwkkzyYvnlBDugfR4Gg/8uHU= -github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -631,7 +513,6 @@ github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -642,8 +523,6 @@ github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1 h1:5Dl+ADmsGerAqH github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -658,8 +537,6 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -667,14 +544,10 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= @@ -696,8 +569,6 @@ github.com/rubyist/circuitbreaker v2.2.1+incompatible/go.mod h1:Ycs3JgJADPuzJDwf github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414 h1:AJNDS0kP60X8wwWFvbLPwDuojxubj9pbfK7pjHw0vKg= github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= @@ -706,7 +577,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= -github.com/shirou/gopsutil v3.20.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= @@ -734,15 +604,12 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smallnest/quick v0.0.0-20220103065406-780def6371e6 h1:J8xk0QMMrqfDLqU0m07pYRRiOIlE7I3dNWAp/pAHqfo= github.com/smallnest/quick v0.0.0-20220103065406-780def6371e6/go.mod h1:h+J5yoLzf3XMKtM9l4vOaUtS4e+si6T3sKDtheJ15wc= github.com/smallnest/rpcx v1.7.4 h1:u6ADk/Ep8BqtAoJZO7LbniWsP+nqeAtcbaPm2D4eOXg= github.com/smallnest/rpcx v1.7.4/go.mod h1:TSciUoPlm8MYxnC7ErCz5ZymOFxOTbhN9cRgEI6Degs= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= -github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -750,7 +617,6 @@ github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -763,7 +629,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -778,7 +643,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= -github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= @@ -795,20 +659,16 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8= github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/wolfogre/go-pprof-practice v0.0.0-20190402114113-8ce266a210ee/go.mod h1:bwtOR7TyudY1IW9h5rVzpkyOkcwPPxinKAxIrdMnN+Q= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= @@ -868,31 +728,21 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM= golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/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-20190125153040-c74c464bbbf2/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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -902,10 +752,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/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= @@ -933,7 +781,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -962,14 +809,11 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/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-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -979,7 +823,6 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -987,7 +830,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1041,9 +883,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1054,7 +894,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1070,10 +909,7 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1081,9 +917,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210223095934-7937bea0104d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1093,7 +927,6 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1102,7 +935,6 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1119,14 +951,10 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/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-20181030000716-a0a13e073c7b/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-20190206041539-40960b6deb8e/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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1135,7 +963,6 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1185,7 +1012,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1199,10 +1025,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -1276,7 +1098,6 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1334,7 +1155,6 @@ gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 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= @@ -1359,7 +1179,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/lego/base/core.go b/lego/base/core.go new file mode 100644 index 000000000..efc0baff7 --- /dev/null +++ b/lego/base/core.go @@ -0,0 +1,85 @@ +package base + +import ( + "context" + + "go_dreamfactory/lego/core" + + "github.com/smallnest/rpcx/client" +) + +type Result struct { + Index string + Result interface{} + Err error +} + +type ISingleService interface { + core.IService +} + +type IClusterServiceBase interface { + core.IService + GetTag() string //获取集群标签 + GetCategory() core.S_Category //服务类别 例如游戏服 + GetRpcId() string //获取rpc通信id + GetPreWeight() float64 //集群服务负载值 暂时可以不用理会 + SetPreWeight(weight int32) //设置服务器权重 +} + +type IClusterServiceSession interface { + GetId() string + GetIp() string + GetRpcId() string + GetType() string + GetVersion() string + SetVersion(v string) + GetPreWeight() float64 + SetPreWeight(p float64) + Done() + CallNR(_func core.Rpc_Key, params ...interface{}) (err error) + Call(_func core.Rpc_Key, params ...interface{}) (interface{}, error) +} + +type IClusterService interface { + IClusterServiceBase + GetSessionsByCategory(category core.S_Category) (ss []IClusterServiceSession) //按服务类别获取服务列表 + DefauleRpcRouteRules(stype string, sip string) (ss IClusterServiceSession, err error) //默认rpc路由规则 + RpcInvokeById(sId string, rkey core.Rpc_Key, iscall bool, arg ...interface{}) (result interface{}, err error) + RpcInvokeByIds(sId []string, rkey core.Rpc_Key, iscall bool, arg ...interface{}) (results map[string]*Result, err error) //执行远程服务Rpc方法 + RpcInvokeByType(sType string, rkey core.Rpc_Key, iscall bool, arg ...interface{}) (result interface{}, err error) //根据路由规则执行远程方法 + RpcInvokeByIp(sIp, sType string, rkey core.Rpc_Key, iscall bool, arg ...interface{}) (result interface{}, err error) //根据目标IP和类型执行远程方法 + RpcInvokeByIps(sIp []string, sType string, rkey core.Rpc_Key, iscall bool, arg ...interface{}) (results map[string]*Result, err error) //根据目标IP集和类型执行远程方法 + ReleaseRpc(rkey core.Rpc_Key, arg ...interface{}) //发布Rpc + Register(id core.Rpc_Key, f interface{}) //注册RPC远程方法 + RegisterGO(id core.Rpc_Key, f interface{}) //注册RPC远程方法 + //废弃此方法 + Subscribe(id core.Rpc_Key, f interface{}) (err error) //订阅Rpc + UnSubscribe(id core.Rpc_Key, f interface{}) (err error) //订阅Rpc +} + +type IRPCXServiceSession interface { + GetId() string + GetIp() string + GetRpcId() string + GetType() string + GetVersion() string + SetVersion(v string) + GetPreWeight() float64 + SetPreWeight(p float64) + Done() + Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error + Go(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) (*client.Call, error) +} + +type IRPCXService interface { + IClusterServiceBase + DefauleRpcRouteRules(stype string, sip string) (ss IRPCXServiceSession, err error) //默认rpc路由规则 + Register(rcvr interface{}) (err error) + RegisterFunction(fn interface{}) (err error) + RegisterFunctionName(name string, fn interface{}) (err error) + RpcCallById(sId string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (err error) + RpcGoById(sId string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (call *client.Call, err error) + RpcCallByType(sType string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (err error) + RpcGoByType(sType string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (call *client.Call, err error) +} diff --git a/lego/base/rpcx/options.go b/lego/base/rpcx/options.go new file mode 100644 index 000000000..0950c6431 --- /dev/null +++ b/lego/base/rpcx/options.go @@ -0,0 +1,66 @@ +package rpcx + +import ( + "fmt" + "io/ioutil" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/utils/container/ip" + + "gopkg.in/yaml.v2" +) + +type Option func(*Options) + +type Options struct { + ConfPath string + Version string //服务版本 + Setting core.ServiceSttings +} + +func SetConfPath(v string) Option { + return func(o *Options) { + o.ConfPath = v + } +} + +func SetVersion(v string) Option { + return func(o *Options) { + o.Version = v + } +} + +// func SetSetting(v core.ServiceSttings) Option { +// return func(o *Options) { +// o.Setting = v +// } +// } + +func newOptions(option ...Option) *Options { + options := &Options{ + ConfPath: "conf/cluster.yaml", + } + for _, o := range option { + o(options) + } + // confpath := fmt.Sprintf("conf/%s.toml", options.Id) + yamlFile, err := ioutil.ReadFile(options.ConfPath) + if err != nil { + panic(fmt.Sprintf("读取服务配置【%s】文件失败err:%v:", options.ConfPath, err)) + } + err = yaml.Unmarshal(yamlFile, &options.Setting) + if err != nil { + panic(fmt.Sprintf("读取服务配置【%s】文件失败err:%v:", options.ConfPath, err)) + } + if len(options.Setting.Id) == 0 || len(options.Setting.Type) == 0 || len(options.Setting.Tag) == 0 { + panic(fmt.Sprintf("[%s] 配置缺少必要配置: %+v", options.ConfPath, options)) + } + if len(options.Setting.Ip) == 0 { + if ipinfo := ip.GetEthernetInfo(); ipinfo != nil { //获取以太网Ip地址 + options.Setting.Ip = ipinfo.IP + } else { + options.Setting.Ip = ip.GetOutboundIP() //局域网ip + } + } + return options +} diff --git a/lego/base/rpcx/service.go b/lego/base/rpcx/service.go new file mode 100644 index 000000000..22df48b74 --- /dev/null +++ b/lego/base/rpcx/service.go @@ -0,0 +1,358 @@ +package rpcx + +import ( + "context" + "fmt" + "runtime" + "sync" + + "go_dreamfactory/lego" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" + "go_dreamfactory/lego/sys/cron" + "go_dreamfactory/lego/sys/event" + "go_dreamfactory/lego/sys/log" + "go_dreamfactory/lego/sys/registry" + "go_dreamfactory/lego/sys/rpcx" + "go_dreamfactory/lego/utils/container/sortslice" + "go_dreamfactory/lego/utils/container/version" + + "github.com/smallnest/rpcx/client" +) + +type RPCXService struct { + cbase.ServiceBase + opts *Options + serverList sync.Map + rpcxService base.IRPCXService + IsInClustered bool + lock sync.RWMutex //服务锁 +} + +func (this *RPCXService) GetTag() string { + return this.opts.Setting.Tag +} +func (this *RPCXService) GetId() string { + return this.opts.Setting.Id +} +func (this *RPCXService) GetType() string { + return this.opts.Setting.Type +} +func (this *RPCXService) GetIp() string { + return this.opts.Setting.Ip +} +func (this *RPCXService) GetPort() int { + return this.opts.Setting.Port +} +func (this *RPCXService) GetCategory() core.S_Category { + return this.opts.Setting.Category +} + +func (this *RPCXService) GetVersion() string { + return this.opts.Version +} +func (this *RPCXService) GetSettings() core.ServiceSttings { + return this.opts.Setting +} +func (this *RPCXService) GetRpcId() string { + return "" +} + +func (this *RPCXService) GetPreWeight() float64 { + return float64(1) / float64(runtime.NumGoroutine()) +} + +func (this *RPCXService) SetPreWeight(weight int32) { + +} + +func (this *RPCXService) Options() *Options { + return this.opts +} +func (this *RPCXService) Configure(opts ...Option) { + this.opts = newOptions(opts...) +} + +func (this *RPCXService) Init(service core.IService) (err error) { + this.rpcxService = service.(base.IRPCXService) + return this.ServiceBase.Init(service) +} + +func (this *RPCXService) InitSys() { + if err := log.OnInit(this.opts.Setting.Sys["log"]); err != nil { + panic(fmt.Sprintf("Sys log Init err:%v", err)) + } else { + log.Infof("Sys log Init success !") + } + if err := event.OnInit(this.opts.Setting.Sys["event"]); err != nil { + log.Panicf(fmt.Sprintf("Sys event Init err:%v", err)) + } else { + log.Infof("Sys event Init success !") + } + if err := registry.OnInit(this.opts.Setting.Sys["registry"], registry.SetService(this.rpcxService), registry.SetListener(this.rpcxService.(registry.IListener))); err != nil { + log.Panicf(fmt.Sprintf("Sys registry Init err:%v", err)) + } else { + log.Infof("Sys registry Init success !") + } + if err := rpcx.OnInit(this.opts.Setting.Sys["rpcx"], rpcx.SetServiceId(this.GetId()), rpcx.SetPort(this.GetPort())); err != nil { + log.Panicf(fmt.Sprintf("Sys rpcx Init err:%v", err)) + } else { + log.Infof("Sys rpcx Init success !") + } + event.Register(core.Event_ServiceStartEnd, func() { //阻塞 先注册服务集群 保证其他服务能及时发现 + if err := rpcx.Start(); err != nil { + log.Panicf(fmt.Sprintf("Sys rpcx Start err:%v", err)) + } + if err := registry.Start(); err != nil { + log.Panicf(fmt.Sprintf("Sys registry Start err:%v", err)) + } + }) +} + +func (this *RPCXService) Start() (err error) { + if err = this.ServiceBase.Start(); err != nil { + return + } + return +} + +func (this *RPCXService) Run(mod ...core.IModule) { + modules := make([]core.IModule, 0) + modules = append(modules, mod...) + this.ServiceBase.Run(modules...) +} + +func (this *RPCXService) Destroy() (err error) { + if err = rpcx.Stop(); err != nil { + return + } + if err = registry.Stop(); err != nil { + return + } + cron.Stop() + err = this.ServiceBase.Destroy() + return +} + +//注册服务会话 当有新的服务加入时 +func (this *RPCXService) FindServiceHandlefunc(node registry.ServiceNode) { + this.lock.Lock() + if _, ok := this.serverList.Load(node.Id); !ok { + if s, err := NewServiceSession(&node); err != nil { + log.Errorf("创建服务会话失败【%s】 err:%v", node.Id, err) + } else { + this.serverList.Store(node.Id, s) + } + } + this.lock.Unlock() + if this.IsInClustered { + event.TriggerEvent(core.Event_FindNewService, node) //触发发现新的服务事件 + } else { + if node.Id == this.opts.Setting.Id { //发现自己 加入集群成功 + this.IsInClustered = true + event.TriggerEvent(core.Event_RegistryStart) + } + } +} + +//更新服务会话 当有新的服务加入时 +func (this *RPCXService) UpDataServiceHandlefunc(node registry.ServiceNode) { + if ss, ok := this.serverList.Load(node.Id); ok { //已经在缓存中 需要更新节点信息 + session := ss.(base.IRPCXServiceSession) + if session.GetRpcId() != node.RpcId { + if s, err := NewServiceSession(&node); err != nil { + log.Errorf("更新服务会话失败【%s】 err:%v", node.Id, err) + } else { + this.serverList.Store(node.Id, s) + } + event.TriggerEvent(core.Event_FindNewService, node) //触发发现新的服务事件 + } else { + if session.GetVersion() != node.Version { + session.SetVersion(node.Version) + } + if session.GetPreWeight() != node.PreWeight { + session.SetPreWeight(node.PreWeight) + } + event.TriggerEvent(core.Event_UpDataOldService, node) //触发发现新的服务事件 + } + } +} + +//注销服务会话 +func (this *RPCXService) LoseServiceHandlefunc(sId string) { + this.lock.Lock() + session, ok := this.serverList.Load(sId) + if ok && session != nil { + session.(base.IRPCXServiceSession).Done() + this.serverList.Delete(sId) + } + this.lock.Unlock() + event.TriggerEvent(core.Event_LoseService, sId) //触发发现新的服务事件 +} + +func (this *RPCXService) getServiceSessionByType(sType string, sIp string) (ss []base.IRPCXServiceSession, err error) { + ss = make([]base.IRPCXServiceSession, 0) + if nodes := registry.GetServiceByType(sType); nodes == nil { + log.Errorf("获取目标类型 type【%s】ip [%s] 服务集失败", sType, sIp) + return nil, err + } else { + if sIp == core.AutoIp { + for _, v := range nodes { + if s, ok := this.serverList.Load(v.Id); ok { + ss = append(ss, s.(base.IRPCXServiceSession)) + } else { + s, err = NewServiceSession(v) + if err != nil { + log.Errorf("创建服务会话失败【%s】 err:%v", v.Id, err) + continue + } else { + this.serverList.Store(v.Id, s) + ss = append(ss, s.(base.IRPCXServiceSession)) + } + } + } + } else { + for _, v := range nodes { + if v.IP == sIp { + if s, ok := this.serverList.Load(v.Id); ok { + ss = append(ss, s.(base.IRPCXServiceSession)) + } else { + s, err = NewServiceSession(v) + if err != nil { + log.Errorf("创建服务会话失败【%s】 err:%v", v.Id, err) + continue + } else { + this.serverList.Store(v.Id, s) + ss = append(ss, s.(base.IRPCXServiceSession)) + } + } + } + } + } + } + return +} + +//默认路由规则 +func (this *RPCXService) DefauleRpcRouteRules(stype string, sip string) (ss base.IRPCXServiceSession, err error) { + if s, e := this.getServiceSessionByType(stype, sip); e != nil { + return nil, e + } else { + ss := make([]interface{}, len(s)) + for i, v := range s { + ss[i] = v + } + if len(ss) > 0 { + //排序找到最优服务 + sortslice.Sort(ss, func(a interface{}, b interface{}) int8 { + as := a.(base.IRPCXServiceSession) + bs := b.(base.IRPCXServiceSession) + if iscompare := version.CompareStrVer(as.GetVersion(), bs.GetVersion()); iscompare != 0 { + return iscompare + } else { + if as.GetPreWeight() < bs.GetPreWeight() { + return 1 + } else if as.GetPreWeight() > bs.GetPreWeight() { + return -1 + } else { + return 0 + } + } + }) + return ss[0].(base.IRPCXServiceSession), nil + } else { + return nil, fmt.Errorf("未找到IP[%s]类型%s】的服务信息", sip, stype) + } + } +} + +//注册服务对象 +func (this *RPCXService) Register(rcvr interface{}) (err error) { + err = rpcx.Register(rcvr) + return +} + +//注册服务方法 +func (this *RPCXService) RegisterFunction(fn interface{}) (err error) { + err = rpcx.RegisterFunction(fn) + return +} + +//注册服务方法 自定义方法名称 +func (this *RPCXService) RegisterFunctionName(name string, fn interface{}) (err error) { + err = rpcx.RegisterFunctionName(name, fn) + return +} + +//同步 执行目标远程服务方法 +func (this *RPCXService) RpcCallById(sId string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (err error) { + defer lego.Recover(fmt.Sprintf("RpcCallById sId:%s rkey:%v arg %v", sId, serviceMethod, args)) + this.lock.RLock() + defer this.lock.RUnlock() + ss, ok := this.serverList.Load(sId) + if !ok { + if node, err := registry.GetServiceById(sId); err != nil { + log.Errorf("未找到目标服务【%s】节点 err:%v", sId, err) + return fmt.Errorf("No Found " + sId) + } else { + ss, err = NewServiceSession(node) + if err != nil { + return fmt.Errorf(fmt.Sprintf("创建服务会话失败【%s】 err:%v", sId, err)) + } else { + this.serverList.Store(node.Id, ss) + } + } + } + err = ss.(base.IRPCXServiceSession).Call(ctx, serviceMethod, args, reply) + return +} + +//异步 执行目标远程服务方法 +func (this *RPCXService) RpcGoById(sId string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (call *client.Call, err error) { + defer lego.Recover(fmt.Sprintf("RpcGoById sId:%s rkey:%v arg %v", sId, serviceMethod, args)) + this.lock.RLock() + defer this.lock.RUnlock() + ss, ok := this.serverList.Load(sId) + if !ok { + if node, err := registry.GetServiceById(sId); err != nil { + log.Errorf("未找到目标服务【%s】节点 err:%v", sId, err) + return nil, fmt.Errorf("No Found " + sId) + } else { + ss, err = NewServiceSession(node) + if err != nil { + return nil, fmt.Errorf(fmt.Sprintf("创建服务会话失败【%s】 err:%v", sId, err)) + } else { + this.serverList.Store(node.Id, ss) + } + } + } + call, err = ss.(base.IRPCXServiceSession).Go(ctx, serviceMethod, args, reply) + return +} + +func (this *RPCXService) RpcCallByType(sType string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (err error) { + defer lego.Recover(fmt.Sprintf("RpcCallByType sType:%s rkey:%s arg %v", sType, serviceMethod, args)) + this.lock.RLock() + defer this.lock.RUnlock() + ss, err := this.rpcxService.DefauleRpcRouteRules(sType, core.AutoIp) + if err != nil { + log.Errorf("未找到目标服务【%s】节点 err:%v", sType, err) + return err + } + err = ss.Call(ctx, serviceMethod, args, reply) + return +} + +func (this *RPCXService) RpcGoByType(sType string, serviceMethod string, ctx context.Context, args interface{}, reply interface{}) (call *client.Call, err error) { + defer lego.Recover(fmt.Sprintf("RpcCallByType sType:%s rkey:%s arg %v", sType, serviceMethod, args)) + this.lock.RLock() + defer this.lock.RUnlock() + ss, err := this.rpcxService.DefauleRpcRouteRules(sType, core.AutoIp) + if err != nil { + log.Errorf("未找到目标服务【%s】节点 err:%v", sType, err) + return nil, err + } + call, err = ss.Go(ctx, serviceMethod, args, reply) + return +} diff --git a/lego/base/rpcx/servicesession.go b/lego/base/rpcx/servicesession.go new file mode 100644 index 000000000..44085e5af --- /dev/null +++ b/lego/base/rpcx/servicesession.go @@ -0,0 +1,64 @@ +package rpcx + +import ( + "context" + "fmt" + + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/sys/registry" + "go_dreamfactory/lego/sys/rpcx" + + "github.com/smallnest/rpcx/client" +) + +func NewServiceSession(node *registry.ServiceNode) (ss base.IRPCXServiceSession, err error) { + session := new(ServiceSession) + session.node = node + session.client, err = rpcx.NewRpcClient(fmt.Sprintf("%s:%d", node.IP, node.Port), node.Id) + ss = session + return +} + +type ServiceSession struct { + node *registry.ServiceNode + client rpcx.IRPCXClient +} + +func (this *ServiceSession) GetId() string { + return this.node.Id +} +func (this *ServiceSession) GetIp() string { + return this.node.IP +} + +func (this *ServiceSession) GetRpcId() string { + return this.node.RpcId +} + +func (this *ServiceSession) GetType() string { + return this.node.Type +} +func (this *ServiceSession) GetVersion() string { + return this.node.Version +} +func (this *ServiceSession) SetVersion(v string) { + this.node.Version = v +} + +func (this *ServiceSession) GetPreWeight() float64 { + return this.node.PreWeight +} + +func (this *ServiceSession) SetPreWeight(p float64) { + this.node.PreWeight = p +} +func (this *ServiceSession) Done() { + this.client.Stop() +} +func (this *ServiceSession) Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error { + return this.client.Call(ctx, serviceMethod, args, reply) +} + +func (this *ServiceSession) Go(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) (*client.Call, error) { + return this.client.Go(ctx, serviceMethod, args, reply, nil) +} diff --git a/lego/core/cbase/modulebase.go b/lego/core/cbase/modulebase.go new file mode 100644 index 000000000..d1e2c849e --- /dev/null +++ b/lego/core/cbase/modulebase.go @@ -0,0 +1,58 @@ +package cbase + +import ( + "go_dreamfactory/lego/core" +) + +type ModuleBase struct { + comps []core.IModuleComp +} + +func (this *ModuleBase) NewOptions() (options core.IModuleOptions) { + return new(ModuleOptions) +} + +func (this *ModuleBase) Init(service core.IService, module core.IModule, options core.IModuleOptions) (err error) { + module.OnInstallComp() + for _, v := range this.comps { + err = v.Init(service, module, v, options) + if err != nil { + return + } + } + return +} + +func (this *ModuleBase) OnInstallComp() { + +} + +func (this *ModuleBase) Start() (err error) { + for _, v := range this.comps { + err = v.Start() + if err != nil { + return + } + } + return +} + +func (this *ModuleBase) Run(closeSig chan bool) (err error) { + + return +} + +func (this *ModuleBase) Destroy() (err error) { + for _, v := range this.comps { + err = v.Destroy() + if err != nil { + return + } + } + return +} + +func (this *ModuleBase) RegisterComp(comp core.IModuleComp) interface{} { + this.comps = append(this.comps, comp) + return comp +} diff --git a/lego/core/cbase/modulecompbase.go b/lego/core/cbase/modulecompbase.go new file mode 100644 index 000000000..0bcf3918f --- /dev/null +++ b/lego/core/cbase/modulecompbase.go @@ -0,0 +1,19 @@ +package cbase + +import ( + "go_dreamfactory/lego/core" +) + +type ModuleCompBase struct{} + +func (this *ModuleCompBase) Init(service core.IService, module core.IModule, comp core.IModuleComp, options core.IModuleOptions) (err error) { + return +} + +func (this *ModuleCompBase) Start() (err error) { + return +} + +func (this *ModuleCompBase) Destroy() (err error) { + return +} diff --git a/lego/core/cbase/moduleoptions.go b/lego/core/cbase/moduleoptions.go new file mode 100644 index 000000000..765bf2401 --- /dev/null +++ b/lego/core/cbase/moduleoptions.go @@ -0,0 +1,13 @@ +package cbase + +import "go_dreamfactory/lego/utils/mapstructure" + +type ModuleOptions struct { +} + +func (this *ModuleOptions) LoadConfig(settings map[string]interface{}) (err error) { + if settings != nil { + mapstructure.Decode(settings, &this) + } + return +} diff --git a/lego/core/cbase/servicebase.go b/lego/core/cbase/servicebase.go new file mode 100644 index 000000000..b1696fe67 --- /dev/null +++ b/lego/core/cbase/servicebase.go @@ -0,0 +1,177 @@ +package cbase + +import ( + "fmt" + "os" + "os/signal" + "sync" + "syscall" + + "go_dreamfactory/lego" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/event" + "go_dreamfactory/lego/sys/log" +) + +type defaultModule struct { + seetring map[string]interface{} + mi core.IModule + closeSig chan bool + wg sync.WaitGroup +} + +func (this *defaultModule) run() { + this.mi.Run(this.closeSig) + this.wg.Done() +} +func (this *defaultModule) destroy() (err error) { + defer lego.Recover(fmt.Sprintf("Module :%s destroy", this.mi.GetType())) + err = this.mi.Destroy() + if err != nil { + err = fmt.Errorf("关闭模块【%s】失败 err:%s", this.mi.GetType(), err.Error()) + } + return +} + +type ServiceBase struct { + closesig chan string + Service core.IService + comps map[core.S_Comps]core.IServiceComp + modules map[core.M_Modules]*defaultModule +} + +func (this *ServiceBase) Init(service core.IService) (err error) { + this.closesig = make(chan string, 1) + this.Service = service + this.modules = make(map[core.M_Modules]*defaultModule) + this.Service.InitSys() + for _, v := range this.comps { + options := v.NewOptions() + if o, ok := service.GetSettings().Comps[string(v.GetName())]; ok { + options.LoadConfig(o) + } + err = v.Init(this.Service, v, options) + if err != nil { + return + } + } + log.Infof("服务[%s] 初始化完成!", this.Service.GetId()) + return nil +} + +//配置服务组件 +func (this *ServiceBase) OnInstallComp(cops ...core.IServiceComp) { + this.comps = make(map[core.S_Comps]core.IServiceComp) + for _, v := range cops { + if _, ok := this.comps[v.GetName()]; ok { + log.Errorf("覆盖注册组件【%s】", v.GetName()) + } + this.comps[v.GetName()] = v + } +} + +func (this *ServiceBase) Start() (err error) { + for _, v := range this.comps { + err = v.Start() + if err != nil { + return + } + } + log.Infof("服务[%s:%s] 启动完成!", this.Service.GetId(), this.Service.GetVersion()) + return +} + +func (this *ServiceBase) Run(mod ...core.IModule) { + for _, v := range mod { + if sf, ok := this.Service.GetSettings().Modules[string(v.GetType())]; ok { + this.modules[v.GetType()] = &defaultModule{ + seetring: sf, + mi: v, + closeSig: make(chan bool, 1), + } + } else { + this.modules[v.GetType()] = &defaultModule{ + seetring: make(map[string]interface{}), + mi: v, + closeSig: make(chan bool, 1), + } + log.Warnf("注册模块【%s】 没有对应的配置信息", v.GetType()) + } + } + for _, v := range this.modules { + options := v.mi.NewOptions() + if err := options.LoadConfig(v.seetring); err == nil { + err := v.mi.Init(this.Service, v.mi, options) + if err != nil { + log.Panicf(fmt.Sprintf("初始化模块【%s】错误 err:%v", v.mi.GetType(), err)) + } + } else { + log.Panicf(fmt.Sprintf("模块【%s】 Options:%v 配置错误 err:%v", v.mi.GetType(), v.seetring, err)) + } + } + for _, v := range this.modules { + err := v.mi.Start() + if err != nil { + log.Panicf(fmt.Sprintf("启动模块【%s】错误 err:%v", v.mi.GetType(), err)) + } + } + for _, v := range this.modules { + v.wg.Add(1) + go v.run() + } + event.TriggerEvent(core.Event_ServiceStartEnd) //广播事件 + //监听外部关闭服务信号 + c := make(chan os.Signal, 1) + //添加进程结束信号 + signal.Notify(c, + os.Interrupt, //退出信号 ctrl+c退出 + os.Kill, //kill 信号 + syscall.SIGHUP, //终端控制进程结束(终端连接断开) + syscall.SIGINT, //用户发送INTR字符(Ctrl+C)触发 + syscall.SIGTERM, //结束程序(可以被捕获、阻塞或忽略) + syscall.SIGQUIT) //用户发送QUIT字符(Ctrl+/)触发 + select { + case sig := <-c: + log.Errorf("服务[%s] 关闭 signal = %v\n", this.Service.GetId(), sig) + case <-this.closesig: + log.Errorf("服务[%s] 关闭\n", this.Service.GetId()) + } +} + +func (this *ServiceBase) Close(closemsg string) { + this.closesig <- closemsg +} + +func (this *ServiceBase) Destroy() (err error) { + for _, v := range this.modules { + v.closeSig <- true + v.wg.Wait() + err = v.destroy() + if err != nil { + return + } + } + for _, v := range this.comps { + err = v.Destroy() + if err != nil { + return + } + } + return +} + +func (this *ServiceBase) GetModule(ModuleName core.M_Modules) (module core.IModule, err error) { + if v, ok := this.modules[ModuleName]; ok { + return v.mi, nil + } else { + return nil, fmt.Errorf("未装配模块【%s】", ModuleName) + } +} + +func (this *ServiceBase) GetComp(CompName core.S_Comps) (comp core.IServiceComp, err error) { + if v, ok := this.comps[CompName]; ok { + return v, nil + } else { + return nil, fmt.Errorf("Service 未装配组件【%s】", CompName) + } +} diff --git a/lego/core/cbase/servicecompbase.go b/lego/core/cbase/servicecompbase.go new file mode 100644 index 000000000..0c4a407dc --- /dev/null +++ b/lego/core/cbase/servicecompbase.go @@ -0,0 +1,24 @@ +package cbase + +import ( + "go_dreamfactory/lego/core" +) + +type ServiceCompBase struct { +} + +func (this *ServiceCompBase) NewOptions() (options core.ICompOptions) { + return new(ServiceCompOptions) +} + +func (this *ServiceCompBase) Init(service core.IService, comp core.IServiceComp, options core.ICompOptions) (err error) { + return +} + +func (this *ServiceCompBase) Start() (err error) { + return +} + +func (this *ServiceCompBase) Destroy() (err error) { + return +} diff --git a/lego/core/cbase/servicecompoptions.go b/lego/core/cbase/servicecompoptions.go new file mode 100644 index 000000000..2e81b8779 --- /dev/null +++ b/lego/core/cbase/servicecompoptions.go @@ -0,0 +1,17 @@ +package cbase + +import ( + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/utils/mapstructure" +) + +type ServiceCompOptions struct { + core.ICompOptions +} + +func (this *ServiceCompOptions) LoadConfig(settings map[string]interface{}) (err error) { + if settings != nil { + mapstructure.Decode(settings, &this) + } + return +} diff --git a/lego/core/core.go b/lego/core/core.go new file mode 100644 index 000000000..84765881c --- /dev/null +++ b/lego/core/core.go @@ -0,0 +1,140 @@ +package core + +type S_Category string //服务类别 例如 网关服务 游戏服务 业务服务 主要用于服务功能分类 +type M_Modules string //模块类型 +type S_Comps string //服务器组件类型 +type ErrorCode int32 //错误码 +type Event_Key string //事件Key +type Rpc_Key string //RPC +type Redis_Key string //Redis缓存 +type SqlTable string //数据库表定义 +type CustomRoute uint8 //自定义网关 + +const ( + AutoIp = "0.0.0.0" + AllIp = "255.255.255.255" +) + +const ( //默认事件 + Event_ServiceStartEnd Event_Key = "ServiceStartEnd" //服务完全启动完毕 + Event_FindNewService Event_Key = "FindNewService" //发现新的服务 + Event_UpDataOldService Event_Key = "UpDataOldService" //发现新的服务 + Event_LoseService Event_Key = "LoseService" //丢失服务 + Event_RegistryStart Event_Key = "RegistryStart" //注册表系统启动成功 +) + +const ( + S_Category_SystemService S_Category = "SystemService" //系统服务类型 + S_Category_GateService S_Category = "GateService" //网关服务类型 + S_Category_BusinessService S_Category = "BusinessService" //业务服务器 +) + +type ServiceSttings struct { + Id string //服务Id + Type string //服务类型 (相同的服务可以启动多个) + Tag string //服务集群标签 (相同标签的集群服务可以互相发现和发现) + Category S_Category //服务列表 (用于区分集群服务下相似业务功能的服务器 例如:游戏服务器) + Ip string //服务所在Ip () + Port int //服务rpcx监听端口 + Comps map[string]map[string]interface{} //服务组件配置 + Sys map[string]map[string]interface{} //服务系统配置 + Modules map[string]map[string]interface{} //服务模块配置 +} + +type IService interface { + GetId() string //获取服务id + GetType() string //获取服务类型 + GetVersion() string //获取服务版本 + GetIp() string //获取服务器运ip + GetPort() int //服务默认端口 + GetSettings() ServiceSttings //获取服务配置表信息 + Init(service IService) (err error) //初始化接口 + InitSys() //初始化系统 + OnInstallComp(cops ...IServiceComp) //组装服务组件 + Start() (err error) //启动服务 + Run(mods ...IModule) //运行服务 + Close(closemsg string) //关闭服务 + Destroy() (err error) //销毁服务 + GetComp(CompName S_Comps) (comp IServiceComp, err error) //获取组件 + GetModule(ModuleName M_Modules) (module IModule, err error) //获取模块 +} +type IServiceComp interface { + GetName() S_Comps + NewOptions() (options ICompOptions) + Init(service IService, comp IServiceComp, options ICompOptions) (err error) + Start() (err error) + Destroy() (err error) +} +type IModule interface { + GetType() M_Modules + NewOptions() (options IModuleOptions) + Init(service IService, module IModule, options IModuleOptions) (err error) + OnInstallComp() + Start() (err error) + Run(closeSig chan bool) (err error) + Destroy() (err error) +} + +type ICompOptions interface { + LoadConfig(settings map[string]interface{}) (err error) +} + +type IModuleOptions interface { + LoadConfig(settings map[string]interface{}) (err error) +} + +type IModuleComp interface { + Init(service IService, module IModule, comp IModuleComp, options IModuleOptions) (err error) + Start() (err error) + Destroy() (err error) +} + +type IUserSession interface { + GetSessionId() string + GetIP() string + GetGateId() string + SendMsg(comdId uint16, msgId uint16, msg interface{}) (err error) + Close() (err error) +} +type IServiceMonitor interface { + IModule + RegisterServiceSettingItem(comp S_Comps, name string, iswrite bool, value interface{}, f func(newvalue string) (err error)) //注册服务级别的Setting + RegisterModuleSettingItem(module M_Modules, name string, iswrite bool, value interface{}, f func(newvalue string) (err error)) //注册模块级别的Setting +} + +//Monitor 数据 +type ( + SettingItem struct { + ItemName string + IsWrite bool + Data interface{} + } + ServiceMonitor struct { //服务监听 + ServiceId string //服务Id + ServiceType string //服务类型 + ServiceCategory S_Category //服务列表 + ServiceVersion string //服务版本 + ServiceTag string //服务集群 + Pid int32 //进程Id + Pname string //进程名称 + MemoryUsed float64 //内存使用量 + CpuUsed float64 //Cpu使用量 + TotalGoroutine int //总的协程数 + CurrPreWeight float64 //服务权重 + CompsSetting map[S_Comps]*CompsSetting //服务器配置信息 + SysSetting map[string]*SysSetting //服务器系统配置信息 + ModuleMonitor map[M_Modules]*ModuleMonitor //模块监听信息 + } + CompsSetting struct { //模块监听 + CompName string //系统名称 + Setting map[string]*SettingItem //系统配置信息 + } + SysSetting struct { //模块监听 + SysName string //系统名称 + Setting map[string]*SettingItem //系统配置信息 + } + ModuleMonitor struct { //模块监听 + ModuleName M_Modules //模块名称 + Setting map[string]*SettingItem //模块配置信息 + } +) diff --git a/lego/core/errorcode.go b/lego/core/errorcode.go new file mode 100644 index 000000000..4ee26b89a --- /dev/null +++ b/lego/core/errorcode.go @@ -0,0 +1,41 @@ +package core + +import "fmt" + +///内置错误码 0-1000 请外部应用服务不要占用 +const ( + ErrorCode_Success ErrorCode = 0 //成功 + ErrorCode_NoFindService ErrorCode = 10 //没有找到远程服务器 + ErrorCode_RpcFuncExecutionError ErrorCode = 11 //Rpc方法执行错误 + ErrorCode_CacheReadError ErrorCode = 12 //缓存读取失败 + ErrorCode_SqlExecutionError ErrorCode = 13 //数据库执行错误 + ErrorCode_ReqParameterError ErrorCode = 14 //请求参数错误 + ErrorCode_SignError ErrorCode = 15 //签名错误 + ErrorCode_InsufficientPermissions ErrorCode = 16 //权限不足 + ErrorCode_NoLogin ErrorCode = 17 //未登录 + //gate + ErrorCode_NoRoute ErrorCode = 201 //没有路由 + ErrorCode_LocalRouteExecutionError ErrorCode = 202 //本地路由执行错误 +) + +var ErrorCodeMsg = map[ErrorCode]string{ + ErrorCode_Success: "成功", + ErrorCode_NoFindService: "没有找到远程服务器", + ErrorCode_RpcFuncExecutionError: "Rpc方法执行错误", + ErrorCode_CacheReadError: "缓存读取失败", + ErrorCode_SqlExecutionError: "数据库执行错误", + ErrorCode_ReqParameterError: "请求参数错误", + ErrorCode_SignError: "签名错误", + ErrorCode_InsufficientPermissions: "权限不足", + ErrorCode_NoLogin: "未登录", + ErrorCode_NoRoute: "没有路由", + ErrorCode_LocalRouteExecutionError: "本地路由执行错误", +} + +func GetErrorCodeMsg(code ErrorCode) string { + if v, ok := ErrorCodeMsg[code]; ok { + return v + } else { + return fmt.Sprintf("ErrorCode:%d", code) + } +} diff --git a/lego/lego.go b/lego/lego.go new file mode 100644 index 000000000..45b57cb85 --- /dev/null +++ b/lego/lego.go @@ -0,0 +1,37 @@ +package lego + +import ( + "runtime" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" +) + +//启动服务 +func Run(service core.IService, mod ...core.IModule) { + cpuNum := runtime.NumCPU() //获得当前设备的cpu核心数 + runtime.GOMAXPROCS(cpuNum) //设置需要用到的cpu数量 + err := service.Init(service) + if err != nil { + log.Panicf("服务初始化失败 err=%s", err.Error()) + } + err = service.Start() + if err != nil { + log.Panicf("服务启动失败 err=%s", err.Error()) + } + service.Run(mod...) + err = service.Destroy() + if err != nil { + log.Panicf("服务销毁失败 err=%s", err.Error()) + } + log.Infof("服务【%s】关闭成功", service.GetId()) +} + +//错误采集 +func Recover(tag string) { + if r := recover(); r != nil { + buf := make([]byte, 1024) + l := runtime.Stack(buf, false) + log.Errorf("%s - %v: %s", tag, r, buf[:l]) + } +} diff --git a/lego/sys/blockcache/cache.go b/lego/sys/blockcache/cache.go new file mode 100644 index 000000000..44a3c0934 --- /dev/null +++ b/lego/sys/blockcache/cache.go @@ -0,0 +1,134 @@ +package blockcache + +import ( + "sync" + "sync/atomic" + "unsafe" + + "go_dreamfactory/lego/utils/container" +) + +func newSys(options Options) (sys *Cache, err error) { + sys = &Cache{ + options: options, + inpip: make(chan interface{}), + outpip: make(chan interface{}), + outnotic: make(chan struct{}), + element: container.NewLKQueue(), + free: options.CacheMaxSzie, + } + go sys.run() + return +} + +type Item struct { + Size int64 + Value interface{} +} + +type Cache struct { + options Options + inpip chan interface{} + outpip chan interface{} + outnotic chan struct{} + instate int32 + outruning int32 + element *container.LKQueue + free int64 + close int32 + wg sync.WaitGroup +} + +func (this *Cache) In() chan<- interface{} { + return this.inpip +} + +func (this *Cache) Out() <-chan interface{} { + return this.outpip +} + +func (this *Cache) Close() { + atomic.StoreInt32(&this.close, 1) + close(this.inpip) + close(this.outnotic) + this.wg.Wait() + close(this.outpip) +} + +func (this *Cache) run() { + for v := range this.inpip { + siez := int64(unsafe.Sizeof(v)) + if siez > this.options.CacheMaxSzie { //异常数据 + this.Errorf("item size:%d large CacheMaxSzie:%d", siez, this.options.CacheMaxSzie) + continue + } else if siez > atomic.LoadInt64(&this.free) { //空间不足 + atomic.StoreInt32(&this.instate, 1) + locp: + for _ = range this.outnotic { + if siez > atomic.LoadInt64(&this.free) { + atomic.StoreInt32(&this.instate, 1) + } else { + this.element.Enqueue(&Item{Size: siez, Value: v}) + atomic.AddInt64(&this.free, -1*siez) + break locp + } + } + } else { + this.element.Enqueue(&Item{Size: siez, Value: v}) + atomic.AddInt64(&this.free, -1*siez) + } + if atomic.CompareAndSwapInt32(&this.outruning, 0, 1) { + this.wg.Add(1) + go func() { + locp: + for { + v := this.element.Dequeue() + if v != nil && atomic.LoadInt32(&this.close) == 0 { + item := v.(*Item) + atomic.AddInt64(&this.free, item.Size) + if atomic.CompareAndSwapInt32(&this.instate, 1, 0) { + this.outnotic <- struct{}{} + } + this.outpip <- item.Value + } else { + break locp + } + } + atomic.StoreInt32(&this.outruning, 0) + this.wg.Done() + }() + } + } +} + +///日志*********************************************************************** +func (this *Cache) Debugf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Debugf("[SYS BlockCache] "+format, a) + } +} +func (this *Cache) Infof(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Infof("[SYS BlockCache] "+format, a) + } +} +func (this *Cache) Warnf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Warnf("[SYS BlockCache] "+format, a) + } +} +func (this *Cache) Errorf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Errorf("[SYS BlockCache] "+format, a) + } +} +func (this *Cache) Panicf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Panicf("[SYS BlockCache] "+format, a) + } +} +func (this *Cache) Fatalf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Fatalf("[SYS BlockCache] "+format, a) + } +} diff --git a/lego/sys/blockcache/core.go b/lego/sys/blockcache/core.go new file mode 100644 index 000000000..657f94e4b --- /dev/null +++ b/lego/sys/blockcache/core.go @@ -0,0 +1,44 @@ +package blockcache + +/* +限容堵塞缓冲池系统 +设置最大内存大小 当缓存区为存满是直接写入 写满后进入堵塞状态 等待缓存区释放 +*/ + +type ( + ISys interface { + In() chan<- interface{} + Out() <-chan interface{} + Close() + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + if defsys, err = newSys(newOptions(config, option...)); err == nil { + + } + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + if sys, err = newSys(newOptionsByOption(option...)); err == nil { + + } + return +} + +func In() chan<- interface{} { + return defsys.In() +} + +func Out() <-chan interface{} { + return defsys.Out() +} + +func Close() { + defsys.Close() +} diff --git a/lego/sys/blockcache/options.go b/lego/sys/blockcache/options.go new file mode 100644 index 000000000..babd4519e --- /dev/null +++ b/lego/sys/blockcache/options.go @@ -0,0 +1,57 @@ +package blockcache + +import ( + "go_dreamfactory/lego/sys/log" + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { + CacheMaxSzie int64 + Debug bool //日志是否开启 + Log log.ILog +} + +func SetCacheMaxSzie(v int64) Option { + return func(o *Options) { + o.CacheMaxSzie = v + } +} + +func SetDebug(v bool) Option { + return func(o *Options) { + o.Debug = v + } +} +func SetLog(v log.ILog) Option { + return func(o *Options) { + o.Log = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + CacheMaxSzie: 1024 * 1024 * 100, + Debug: true, + Log: log.Clone(log.SetLoglayer(2)), + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + CacheMaxSzie: 1024 * 1024 * 100, + Debug: true, + Log: log.Clone(log.SetLoglayer(2)), + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/blockcache/sys_test.go b/lego/sys/blockcache/sys_test.go new file mode 100644 index 000000000..e46c6b9d4 --- /dev/null +++ b/lego/sys/blockcache/sys_test.go @@ -0,0 +1,58 @@ +package blockcache_test + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "testing" + "time" + + "go_dreamfactory/lego/sys/blockcache" + "go_dreamfactory/lego/sys/log" +) + +func Test_sys(t *testing.T) { + if err := log.OnInit(nil, log.SetLoglevel(log.DebugLevel), log.SetDebugMode(true)); err != nil { + fmt.Printf("log init err:%v", err) + return + } + log.Debugf("log init succ") + if sys, err := blockcache.NewSys(blockcache.SetCacheMaxSzie(100)); err != nil { + log.Debugf("livego init err:%v", err) + return + } else { + closeSignal := make(chan struct{}) + go func() { + locp: + for { + select { + case <-closeSignal: + break locp + default: + sys.In() <- "liwei1dao" + log.Debugf("In:liwei1dao") + } + } + log.Debugf("In:End") + }() + go func() { + for v := range sys.Out() { + log.Debugf("Out:%v", v) + time.Sleep(time.Second) + } + log.Debugf("Out:End") + }() + go func() { + time.Sleep(time.Second * 3) + closeSignal <- struct{}{} + sys.Close() + }() + } + sigterm := make(chan os.Signal, 1) + signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) + select { + case <-sigterm: + fmt.Printf("terminating: via signal\n") + } +} diff --git a/lego/sys/cachego/cache.go b/lego/sys/cachego/cache.go new file mode 100644 index 000000000..865ac7240 --- /dev/null +++ b/lego/sys/cachego/cache.go @@ -0,0 +1,820 @@ +package cachego + +import ( + "encoding/gob" + "fmt" + "io" + "os" + "time" +) + +func (this *CacheGo) SetDefault(k string, x interface{}) { + this.Set(k, x, this.options.Expiration) +} + +func (this *CacheGo) Replace(k string, x interface{}, d time.Duration) error { + this.mu.Lock() + _, found := this.get(k) + if !found { + this.mu.Unlock() + return fmt.Errorf("Item %s doesn't exist", k) + } + this.set(k, x, d) + this.mu.Unlock() + return nil +} + +func (this *CacheGo) GetWithExpiration(k string) (interface{}, time.Time, bool) { + this.mu.RLock() + // "Inlining" of get and Expired + item, found := this.items[k] + if !found { + this.mu.RUnlock() + return nil, time.Time{}, false + } + + if item.Expiration > 0 { + if time.Now().UnixNano() > item.Expiration { + this.mu.RUnlock() + return nil, time.Time{}, false + } + + // Return the item and the expiration time + this.mu.RUnlock() + return item.Object, time.Unix(0, item.Expiration), true + } + + // If expiration <= 0 (i.e. no expiration time set) then return the item + // and a zeroed time.Time + this.mu.RUnlock() + return item.Object, time.Time{}, true +} + +func (this *CacheGo) Increment(k string, n int64) error { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + switch v.Object.(type) { + case int: + v.Object = v.Object.(int) + int(n) + case int8: + v.Object = v.Object.(int8) + int8(n) + case int16: + v.Object = v.Object.(int16) + int16(n) + case int32: + v.Object = v.Object.(int32) + int32(n) + case int64: + v.Object = v.Object.(int64) + n + case uint: + v.Object = v.Object.(uint) + uint(n) + case uintptr: + v.Object = v.Object.(uintptr) + uintptr(n) + case uint8: + v.Object = v.Object.(uint8) + uint8(n) + case uint16: + v.Object = v.Object.(uint16) + uint16(n) + case uint32: + v.Object = v.Object.(uint32) + uint32(n) + case uint64: + v.Object = v.Object.(uint64) + uint64(n) + case float32: + v.Object = v.Object.(float32) + float32(n) + case float64: + v.Object = v.Object.(float64) + float64(n) + default: + this.mu.Unlock() + return fmt.Errorf("The value for %s is not an integer", k) + } + this.items[k] = v + this.mu.Unlock() + return nil +} + +func (this *CacheGo) IncrementFloat(k string, n float64) error { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + switch v.Object.(type) { + case float32: + v.Object = v.Object.(float32) + float32(n) + case float64: + v.Object = v.Object.(float64) + n + default: + this.mu.Unlock() + return fmt.Errorf("The value for %s does not have type float32 or float64", k) + } + this.items[k] = v + this.mu.Unlock() + return nil +} + +func (this *CacheGo) IncrementInt(k string, n int) (int, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementInt8(k string, n int8) (int8, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int8) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int8", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementInt16(k string, n int16) (int16, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int16) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int16", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementInt32(k string, n int32) (int32, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int32) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int32", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementInt64(k string, n int64) (int64, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int64) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int64", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementUint(k string, n uint) (uint, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementUintptr(k string, n uintptr) (uintptr, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uintptr) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uintptr", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementUint8(k string, n uint8) (uint8, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint8) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint8", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementUint16(k string, n uint16) (uint16, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint16) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint16", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementUint32(k string, n uint32) (uint32, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint32) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint32", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementUint64(k string, n uint64) (uint64, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint64) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint64", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementFloat32(k string, n float32) (float32, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float32) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float32", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) IncrementFloat64(k string, n float64) (float64, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float64) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float64", k) + } + nv := rv + n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) Decrement(k string, n int64) error { + // TODO: Implement Increment and Decrement more cleanly. + // (Cannot do Increment(k, n*-1) for uints.) + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return fmt.Errorf("Item not found") + } + switch v.Object.(type) { + case int: + v.Object = v.Object.(int) - int(n) + case int8: + v.Object = v.Object.(int8) - int8(n) + case int16: + v.Object = v.Object.(int16) - int16(n) + case int32: + v.Object = v.Object.(int32) - int32(n) + case int64: + v.Object = v.Object.(int64) - n + case uint: + v.Object = v.Object.(uint) - uint(n) + case uintptr: + v.Object = v.Object.(uintptr) - uintptr(n) + case uint8: + v.Object = v.Object.(uint8) - uint8(n) + case uint16: + v.Object = v.Object.(uint16) - uint16(n) + case uint32: + v.Object = v.Object.(uint32) - uint32(n) + case uint64: + v.Object = v.Object.(uint64) - uint64(n) + case float32: + v.Object = v.Object.(float32) - float32(n) + case float64: + v.Object = v.Object.(float64) - float64(n) + default: + this.mu.Unlock() + return fmt.Errorf("The value for %s is not an integer", k) + } + this.items[k] = v + this.mu.Unlock() + return nil +} + +func (this *CacheGo) DecrementFloat(k string, n float64) error { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + switch v.Object.(type) { + case float32: + v.Object = v.Object.(float32) - float32(n) + case float64: + v.Object = v.Object.(float64) - n + default: + this.mu.Unlock() + return fmt.Errorf("The value for %s does not have type float32 or float64", k) + } + this.items[k] = v + this.mu.Unlock() + return nil +} + +func (this *CacheGo) DecrementInt(k string, n int) (int, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementInt8(k string, n int8) (int8, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int8) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int8", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} +func (this *CacheGo) DecrementInt16(k string, n int16) (int16, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int16) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int16", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementInt32(k string, n int32) (int32, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int32) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int32", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementInt64(k string, n int64) (int64, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int64) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int64", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} +func (this *CacheGo) DecrementUint(k string, n uint) (uint, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementUintptr(k string, n uintptr) (uintptr, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uintptr) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uintptr", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementUint8(k string, n uint8) (uint8, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint8) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint8", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} +func (this *CacheGo) DecrementUint16(k string, n uint16) (uint16, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint16) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint16", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementUint32(k string, n uint32) (uint32, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint32) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint32", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} + +func (this *CacheGo) DecrementUint64(k string, n uint64) (uint64, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint64) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint64", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} +func (this *CacheGo) DecrementFloat32(k string, n float32) (float32, error) { + this.mu.Lock() + v, found := this.items[k] + if !found || v.Expired() { + this.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float32) + if !ok { + this.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float32", k) + } + nv := rv - n + v.Object = nv + this.items[k] = v + this.mu.Unlock() + return nv, nil +} +func (this *CacheGo) OnEvicted(f func(string, interface{})) { + this.mu.Lock() + this.onEvicted = f + this.mu.Unlock() +} +func (this *CacheGo) Set(k string, x interface{}, d time.Duration) { + var e int64 + if d > 0 { + e = time.Now().Add(d).UnixNano() + } + this.mu.Lock() + this.items[k] = Item{ + Object: x, + Expiration: e, + } + this.mu.Unlock() +} +func (this *CacheGo) Add(k string, x interface{}, d time.Duration) error { + this.mu.Lock() + _, found := this.get(k) + if found { + this.mu.Unlock() + return fmt.Errorf("Item %s already exists", k) + } + this.set(k, x, d) + this.mu.Unlock() + return nil +} +func (this *CacheGo) DeleteExpired() { + var evictedItems []keyAndValue + now := time.Now().UnixNano() + this.mu.Lock() + for k, v := range this.items { + // "Inlining" of expired + if v.Expiration > 0 && now > v.Expiration { + ov, evicted := this.delete(k) + if evicted { + evictedItems = append(evictedItems, keyAndValue{k, ov}) + } + } + } + this.mu.Unlock() + for _, v := range evictedItems { + this.onEvicted(v.key, v.value) + } +} + +func (this *CacheGo) SaveFile(fname string) error { + fp, err := os.Create(fname) + if err != nil { + return err + } + err = this.Save(fp) + if err != nil { + fp.Close() + return err + } + return fp.Close() +} + +func (this *CacheGo) Save(w io.Writer) (err error) { + enc := gob.NewEncoder(w) + defer func() { + if x := recover(); x != nil { + err = fmt.Errorf("Error registering item types with Gob library") + } + }() + this.mu.RLock() + defer this.mu.RUnlock() + for _, v := range this.items { + gob.Register(v.Object) + } + err = enc.Encode(&this.items) + return +} + +func (this *CacheGo) Load(r io.Reader) error { + dec := gob.NewDecoder(r) + items := map[string]Item{} + err := dec.Decode(&items) + if err == nil { + this.mu.Lock() + defer this.mu.Unlock() + for k, v := range items { + ov, found := this.items[k] + if !found || ov.Expired() { + this.items[k] = v + } + } + } + return err +} + +func (this *CacheGo) LoadFile(fname string) error { + fp, err := os.Open(fname) + if err != nil { + return err + } + err = this.Load(fp) + if err != nil { + fp.Close() + return err + } + return fp.Close() +} + +func (this *CacheGo) Items() map[string]Item { + this.mu.RLock() + defer this.mu.RUnlock() + m := make(map[string]Item, len(this.items)) + now := time.Now().UnixNano() + for k, v := range this.items { + if v.Expiration > 0 { + if now > v.Expiration { + continue + } + } + m[k] = v + } + return m +} + +func (this *CacheGo) ItemCount() int { + this.mu.RLock() + n := len(this.items) + this.mu.RUnlock() + return n +} + +func (this *CacheGo) Flush() { + this.mu.Lock() + this.items = map[string]Item{} + this.mu.Unlock() +} + +func (this *CacheGo) get(k string) (interface{}, bool) { + item, found := this.items[k] + if !found { + return nil, false + } + // "Inlining" of Expired + if item.Expiration > 0 { + if time.Now().UnixNano() > item.Expiration { + return nil, false + } + } + return item.Object, true +} +func (this *CacheGo) set(k string, x interface{}, d time.Duration) { + var e int64 + if d > 0 { + e = time.Now().Add(d).UnixNano() + } + this.items[k] = Item{ + Object: x, + Expiration: e, + } +} +func (this *CacheGo) delete(k string) (interface{}, bool) { + if this.onEvicted != nil { + if v, found := this.items[k]; found { + delete(this.items, k) + return v.Object, true + } + } + delete(this.items, k) + return nil, false +} diff --git a/lego/sys/cachego/cachego.go b/lego/sys/cachego/cachego.go new file mode 100644 index 000000000..c1f445fe8 --- /dev/null +++ b/lego/sys/cachego/cachego.go @@ -0,0 +1,47 @@ +package cachego + +import ( + "sync" + "time" +) + +func newSys(options Options) (sys *CacheGo, err error) { + sys = &CacheGo{ + options: options, + items: make(map[string]Item), + } + err = sys.init() + return +} + +type CacheGo struct { + options Options + items map[string]Item + mu sync.RWMutex + onEvicted func(string, interface{}) + stop chan bool +} + +func (this *CacheGo) init() (err error) { + if this.options.CleanupInterval > 0 { + go this.run() + } + return +} + +func (this *CacheGo) run() { + ticker := time.NewTicker(time.Duration(this.options.CleanupInterval) * time.Second) + for { + select { + case <-ticker.C: + this.DeleteExpired() + case <-this.stop: + ticker.Stop() + return + } + } +} + +func (this *CacheGo) Clsoe() { + this.stop <- true +} diff --git a/lego/sys/cachego/core.go b/lego/sys/cachego/core.go new file mode 100644 index 000000000..b0390161c --- /dev/null +++ b/lego/sys/cachego/core.go @@ -0,0 +1,187 @@ +package cachego + +/* +本地缓存系统 +*/ +import ( + "io" + "time" +) + +type ( + ISys interface { + Flush() + ItemCount() int + Items() map[string]Item + SaveFile(fname string) error + Save(w io.Writer) (err error) + Load(r io.Reader) error + LoadFile(fname string) error + Add(k string, x interface{}, d time.Duration) error + Set(k string, x interface{}, d time.Duration) + OnEvicted(f func(string, interface{})) + Increment(k string, n int64) error + IncrementFloat(k string, n float64) error + IncrementInt(k string, n int) (int, error) + IncrementInt8(k string, n int8) (int8, error) + IncrementInt16(k string, n int16) (int16, error) + IncrementInt32(k string, n int32) (int32, error) + IncrementInt64(k string, n int64) (int64, error) + IncrementUint(k string, n uint) (uint, error) + IncrementUintptr(k string, n uintptr) (uintptr, error) + IncrementUint8(k string, n uint8) (uint8, error) + IncrementUint16(k string, n uint16) (uint16, error) + IncrementUint32(k string, n uint32) (uint32, error) + IncrementUint64(k string, n uint64) (uint64, error) + IncrementFloat32(k string, n float32) (float32, error) + IncrementFloat64(k string, n float64) (float64, error) + Decrement(k string, n int64) error + DecrementFloat(k string, n float64) error + DecrementInt(k string, n int) (int, error) + DecrementInt8(k string, n int8) (int8, error) + DecrementInt16(k string, n int16) (int16, error) + DecrementInt32(k string, n int32) (int32, error) + DecrementInt64(k string, n int64) (int64, error) + DecrementUint(k string, n uint) (uint, error) + DecrementUintptr(k string, n uintptr) (uintptr, error) + DecrementUint8(k string, n uint8) (uint8, error) + DecrementUint16(k string, n uint16) (uint16, error) + DecrementUint32(k string, n uint32) (uint32, error) + DecrementUint64(k string, n uint64) (uint64, error) + DecrementFloat32(k string, n float32) (float32, error) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + if defsys, err = newSys(newOptions(config, option...)); err == nil { + } + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + if sys, err = newSys(newOptionsByOption(option...)); err == nil { + } + return +} + +func Flush() { + defsys.Flush() +} +func ItemCount() int { + return defsys.ItemCount() +} +func Items() map[string]Item { + return defsys.Items() +} +func SaveFile(fname string) error { + return defsys.SaveFile(fname) +} +func Save(w io.Writer) (err error) { + return defsys.Save(w) +} +func Load(r io.Reader) error { + return defsys.Load(r) +} +func LoadFile(fname string) error { + return defsys.LoadFile(fname) +} +func Add(k string, x interface{}, d time.Duration) error { + return defsys.Add(k, x, d) +} +func Set(k string, x interface{}, d time.Duration) { + defsys.Set(k, x, d) +} +func OnEvicted(f func(string, interface{})) { + defsys.OnEvicted(f) +} +func Increment(k string, n int64) error { + return defsys.Increment(k, n) +} +func IncrementFloat(k string, n float64) error { + return defsys.IncrementFloat(k, n) +} +func IncrementInt(k string, n int) (int, error) { + return defsys.IncrementInt(k, n) +} +func IncrementInt8(k string, n int8) (int8, error) { + return defsys.IncrementInt8(k, n) +} +func IncrementInt16(k string, n int16) (int16, error) { + return defsys.IncrementInt16(k, n) +} +func IncrementInt32(k string, n int32) (int32, error) { + return defsys.IncrementInt32(k, n) +} +func IncrementInt64(k string, n int64) (int64, error) { + return defsys.IncrementInt64(k, n) +} +func IncrementUint(k string, n uint) (uint, error) { + return defsys.IncrementUint(k, n) +} +func IncrementUintptr(k string, n uintptr) (uintptr, error) { + return defsys.IncrementUintptr(k, n) +} +func IncrementUint8(k string, n uint8) (uint8, error) { + return defsys.IncrementUint8(k, n) +} +func IncrementUint16(k string, n uint16) (uint16, error) { + return defsys.IncrementUint16(k, n) +} +func IncrementUint32(k string, n uint32) (uint32, error) { + return defsys.IncrementUint32(k, n) +} +func IncrementUint64(k string, n uint64) (uint64, error) { + return defsys.IncrementUint64(k, n) +} +func IncrementFloat32(k string, n float32) (float32, error) { + return defsys.IncrementFloat32(k, n) +} +func IncrementFloat64(k string, n float64) (float64, error) { + return defsys.IncrementFloat64(k, n) +} +func Decrement(k string, n int64) error { + return defsys.Decrement(k, n) +} +func DecrementFloat(k string, n float64) error { + return defsys.DecrementFloat(k, n) +} +func DecrementInt(k string, n int) (int, error) { + return defsys.DecrementInt(k, n) +} +func DecrementInt8(k string, n int8) (int8, error) { + return defsys.DecrementInt8(k, n) +} +func DecrementInt16(k string, n int16) (int16, error) { + return defsys.DecrementInt16(k, n) +} +func DecrementInt32(k string, n int32) (int32, error) { + return defsys.DecrementInt32(k, n) +} +func DecrementInt64(k string, n int64) (int64, error) { + return defsys.DecrementInt64(k, n) +} +func DecrementUint(k string, n uint) (uint, error) { + return defsys.DecrementUint(k, n) +} +func DecrementUintptr(k string, n uintptr) (uintptr, error) { + return defsys.DecrementUintptr(k, n) +} +func DecrementUint8(k string, n uint8) (uint8, error) { + return defsys.DecrementUint8(k, n) +} +func DecrementUint16(k string, n uint16) (uint16, error) { + return defsys.DecrementUint16(k, n) +} +func DecrementUint32(k string, n uint32) (uint32, error) { + return defsys.DecrementUint32(k, n) +} +func DecrementUint64(k string, n uint64) (uint64, error) { + return defsys.DecrementUint64(k, n) +} +func DecrementFloat32(k string, n float32) (float32, error) { + return defsys.DecrementFloat32(k, n) +} diff --git a/lego/sys/cachego/item.go b/lego/sys/cachego/item.go new file mode 100644 index 000000000..6468b46a5 --- /dev/null +++ b/lego/sys/cachego/item.go @@ -0,0 +1,19 @@ +package cachego + +import "time" + +type keyAndValue struct { + key string + value interface{} +} +type Item struct { + Object interface{} + Expiration int64 +} + +func (item Item) Expired() bool { + if item.Expiration == 0 { + return false + } + return time.Now().UnixNano() > item.Expiration +} diff --git a/lego/sys/cachego/options.go b/lego/sys/cachego/options.go new file mode 100644 index 000000000..a9b39825f --- /dev/null +++ b/lego/sys/cachego/options.go @@ -0,0 +1,38 @@ +package cachego + +import ( + "time" + + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { + Expiration time.Duration + CleanupInterval time.Duration +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + Expiration: -1, + CleanupInterval: 0, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + Expiration: -1, + CleanupInterval: 0, + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/cron/core.go b/lego/sys/cron/core.go new file mode 100644 index 000000000..bcbaca5b0 --- /dev/null +++ b/lego/sys/cron/core.go @@ -0,0 +1,48 @@ +package cron + +import ( + tcron "github.com/robfig/cron/v3" +) + +type ( + ISys interface { + Start() + Stop() + AddFunc(spec string, cmd func()) (tcron.EntryID, error) + Remove(id tcron.EntryID) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + if defsys, err = newSys(newOptions(config, option...)); err == nil { + Start() + } + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + if sys, err = newSys(newOptionsByOption(option...)); err == nil { + Start() + } + return +} + +func Start() { + defsys.Start() +} + +func Stop() { + defsys.Stop() +} + +func AddFunc(spec string, cmd func()) (tcron.EntryID, error) { + return defsys.AddFunc(spec, cmd) +} + +func Remove(id tcron.EntryID) { + defsys.Remove(id) +} diff --git a/lego/sys/cron/cron.go b/lego/sys/cron/cron.go new file mode 100644 index 000000000..07346c9a5 --- /dev/null +++ b/lego/sys/cron/cron.go @@ -0,0 +1,32 @@ +package cron + +import ( + tcron "github.com/robfig/cron/v3" +) + +func newSys(options Options) (sys *Cron, err error) { + parser := tcron.NewParser(tcron.Second | tcron.Minute | tcron.Hour | tcron.Dom | tcron.Month | tcron.Dow | tcron.Descriptor) + sys = &Cron{options: options, cron: tcron.New(tcron.WithParser(parser))} + return +} + +type Cron struct { + cron *tcron.Cron + options Options +} + +func (this *Cron) Start() { + this.cron.Start() +} + +func (this *Cron) Stop() { + this.cron.Stop() +} + +func (this *Cron) AddFunc(spec string, cmd func()) (tcron.EntryID, error) { + return this.cron.AddFunc(spec, cmd) +} + +func (this *Cron) Remove(id tcron.EntryID) { + this.cron.Remove(id) +} diff --git a/lego/sys/cron/options.go b/lego/sys/cron/options.go new file mode 100644 index 000000000..bdffb83a3 --- /dev/null +++ b/lego/sys/cron/options.go @@ -0,0 +1,28 @@ +package cron + +import ( + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{} + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{} + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/cron/sys_test.go b/lego/sys/cron/sys_test.go new file mode 100644 index 000000000..b30a89d28 --- /dev/null +++ b/lego/sys/cron/sys_test.go @@ -0,0 +1,127 @@ +package cron_test + +import ( + "fmt" + "testing" + "time" + + "go_dreamfactory/lego/sys/cron" +) + +// 1. cron表达式格式: +// {秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)} +// 2. cron表达式各占位符解释: +// {秒数} ==> 允许值范围: 0~59 ,不允许为空值,若值不合法,调度器将抛出SchedulerException异常 +// "*" 代表每隔1秒钟触发; +// "," 代表在指定的秒数触发,比如"0,15,45"代表0秒、15秒和45秒时触发任务 +// "-" 代表在指定的范围内触发,比如"25-45"代表从25秒开始触发到45秒结束触发,每隔1秒触发1次 +// "/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"0"),后面的值代表偏移量,比如"0/20"或者"*/20"代表从0秒钟开始,每隔20秒钟触发1次,即0秒触发1次,20秒触发1次,40秒触发1次;"5/20"代表5秒触发1次,25秒触发1次,45秒触发1次;"10-45/20"代表在[10,45]内步进20秒命中的时间点触发,即10秒触发1次,30秒触发1次 +// {分钟} ==> 允许值范围: 0~59 ,不允许为空值,若值不合法,调度器将抛出SchedulerException异常 +// "*" 代表每隔1分钟触发; +// "," 代表在指定的分钟触发,比如"10,20,40"代表10分钟、20分钟和40分钟时触发任务 +// "-" 代表在指定的范围内触发,比如"5-30"代表从5分钟开始触发到30分钟结束触 发,每隔1分钟触发 +// "/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"0"),后面的值代表偏移量,比如"0/25"或者"*/25"代表从0分钟开始,每隔25分钟触发1次,即0分钟触发1次,第25分钟触发1次,第50分钟触发1次;"5/25"代表5分钟触发1次,30分钟触发1次,55分钟触发1次;"10-45/20"代表在[10,45]内步进20分钟命中的时间点触发,即10分钟触发1次,30分钟触发1次 +// {小时} ==> 允许值范围: 0~23 ,不允许为空值,若值不合法,调度器将抛出SchedulerException异常 +// "*" 代表每隔1小时触发; +// "," 代表在指定的时间点触发,比如"10,20,23"代表10点钟、20点钟和23点触发任务 +// "-" 代表在指定的时间段内触发,比如"20-23"代表从20点开始触发到23点结束触发,每隔1小时触发 +// "/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"0"),后面的值代表偏移量,比如"0/1"或者"*/1"代表从0点开始触发,每隔1小时触发1次;"1/2"代表从1点开始触发,以后每隔2小时触发一次;"19-20/2"表达式将只在19点触发 +// {日期} ==> 允许值范围: 1~31 ,不允许为空值,若值不合法,调度器将抛出SchedulerException异常 +// "*" 代表每天触发; +// "?" 与{星期}互斥,即意味着若明确指定{星期}触发,则表示{日期}无意义,以免引起 冲突和混乱 +// "," 代表在指定的日期触发,比如"1,10,20"代表1号、10号和20号这3天触发 +// "-" 代表在指定的日期范围内触发,比如"10-15"代表从10号开始触发到15号结束触发,每隔1天触发 +// "/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"1"),后面的值代表偏移量,比如"1/5"或者"*/5"代表从1号开始触发,每隔5天触发1次;"10/5"代表从10号开始触发,以后每隔5天触发一次;"1-10/2"表达式意味着在[1,10]范围内,每隔2天触发,即1号,3号,5号,7号,9号触发 +// "L" 如果{日期}占位符如果是"L",即意味着当月的最后一天触发 +// "W "意味着在本月内离当天最近的工作日触发,所谓最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离为0;所谓本月内的说法,就是不能跨月取到最近工作日,即使前/后月份的最后一天/第一天确实满足最近工作日;因此,"LW"则意味着本月的最后一个工作日触发,"W"强烈依赖{月份} +// "C" 根据日历触发,由于使用较少,暂时不做解释 +// {月份} ==> 允许值范围: 1~12 (JAN-DEC),不允许为空值,若值不合法,调度器将抛出SchedulerException异常 +// "*" 代表每个月都触发; +// "," 代表在指定的月份触发,比如"1,6,12"代表1月份、6月份和12月份触发任务 +// "-" 代表在指定的月份范围内触发,比如"1-6"代表从1月份开始触发到6月份结束触发,每隔1个月触发 +// "/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"1"),后面的值代表偏移量,比如"1/2"或者"*/2"代表从1月份开始触发,每隔2个月触发1次;"6/6"代表从6月份开始触发,以后每隔6个月触发一次;"1-6/12"表达式意味着每年1月份触发 +// {星期} ==> 允许值范围: 1~7 (SUN-SAT),1代表星期天(一星期的第一天),以此类推,7代表星期六(一星期的最后一天),不允许为空值,若值不合法,调度器将抛出SchedulerException异常 +// "*" 代表每星期都触发; +// "?" 与{日期}互斥,即意味着若明确指定{日期}触发,则表示{星期}无意义,以免引起冲突和混乱 +// "," 代表在指定的星期约定触发,比如"1,3,5"代表星期天、星期二和星期四触发 +// "-" 代表在指定的星期范围内触发,比如"2-4"代表从星期一开始触发到星期三结束触发,每隔1天触发 +// "/" 代表触发步进(step),"/"前面的值代表初始值("*"等同"1"),后面的值代表偏移量,比如"1/3"或者"*/3"代表从星期天开始触发,每隔3天触发1次;"1-5/2"表达式意味着在[1,5]范围内,每隔2天触发,即星期天、星期二、星期四触发 +// "L" 如果{星期}占位符如果是"L",即意味着星期的的最后一天触发,即星期六触发,L= 7或者 L = SAT,因此,"5L"意味着一个月的最后一个星期四触发 +// "#" 用来指定具体的周数,"#"前面代表星期,"#"后面代表本月第几周,比如"2#2"表示本月第二周的星期一,"5#3"表示本月第三周的星期四,因此,"5L"这种形式只不过是"#"的特殊形式而已 +// "C" 根据日历触发,由于使用较少,暂时不做解释 +// {年份} ==> 允许值范围: 1970~2099 ,允许为空,若值不合法,调度器将抛出SchedulerException异常 +// "*"代表每年都触发; +// ","代表在指定的年份才触发,比如"2011,2012,2013"代表2011年、2012年和2013年触发任务 +// "-"代表在指定的年份范围内触发,比如"2011-2020"代表从2011年开始触发到2020年结束触发,每隔1年触发 +// "/"代表触发步进(step),"/"前面的值代表初始值("*"等同"1970"),后面的值代表偏移量,比如"2011/2"或者"*/2"代表从2011年开始触发,每隔2年触发1次 +// 注意:除了{日期}和{星期}可以使用"?"来实现互斥,表达无意义的信息之外,其他占位符都要具有具体的时间含义,且依赖关系为:年->月->日期(星期)->小时->分钟->秒数 +// 3. cron表达式的强大魅力在于灵活的横向和纵向组合以及简单的语法,用cron表达式几乎可以写出任何你想要触发的时间点 +// 经典案例: +// "30 * * * * ?" 每半分钟触发任务 +// "30 10 * * * ?" 每小时的10分30秒触发任务 +// "30 10 1 * * ?" 每天1点10分30秒触发任务 +// "30 10 1 20 * ?" 每月20号1点10分30秒触发任务 +// "30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务 +// "30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务 +// "30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务 +// "30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务 +// "15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务 +// "15-45 * * * * ?" 15到45秒内,每秒都触发任务 +// "15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次 +// "15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次 +// "0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次 +// "0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务 +// "0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务 +// "0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务 +// "0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务 +// "0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务 + +// 一些cron表达式案例 +// */5 * * * * ? 每隔5秒执行一次 +// 0 */1 * * * ? 每隔1分钟执行一次 +// 0 0 5-15 * * ? 每天5-15点整点触发 +// 0 0/3 * * * ? 每三分钟触发一次 +// 0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发 +// 0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发 +// 0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 +// 0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 +// 0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 + +// 0 0 12 ? * WED 表示每个星期三中午12点 +// 0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点 +// 0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发 +// 0 15 10 ? * MON-FRI 周一至周五的上午10:15触发 +// 0 0 23 L * ? 每月最后一天23点执行一次 +// 0 15 10 L * ? 每月最后一日的上午10:15触发 +// 0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发 +// 0 15 10 * * ? 2005 2005年的每天上午10:15触发 +// 0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发 +// 0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发 + +func Test_sys(t *testing.T) { + if err := cron.OnInit(nil); err == nil { + // AddFunc("@every 1s", func() { //每一秒 + // fmt.Printf("@every 1s") + // }) + // AddFunc("@every 1m", func() { //每一分 + // fmt.Printf("@every 1m") + // }) + // AddFunc("@hourly", func() { //每一小时 + // fmt.Printf("@hourly") + // }) + // AddFunc("@daily", func() { //每天凌晨 + // fmt.Printf("@daily") + // }) + // AddFunc("@daily", func() { //每天凌晨 + // fmt.Printf("@daily") + // }) + // AddFunc("*/5 * * * * ?", func() { //每天凌晨 + // fmt.Printf("*/5 * * * * ?") + // }) + cron.AddFunc("30/59 0/2 * * * ?", func() { //每隔90秒 + fmt.Printf("*/5 * * * * ?") + }) + + } + time.Sleep(time.Minute * 5) +} diff --git a/lego/sys/event/core.go b/lego/sys/event/core.go new file mode 100644 index 000000000..030b35cec --- /dev/null +++ b/lego/sys/event/core.go @@ -0,0 +1,55 @@ +package event + +import ( + "reflect" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" +) + +type ( + FunctionInfo struct { + Function reflect.Value + Goroutine bool + } + ISys interface { + Register(eId core.Event_Key, f interface{}) (err error) + RegisterGO(eId core.Event_Key, f interface{}) (err error) + RemoveEvent(eId core.Event_Key, f interface{}) (err error) + TriggerEvent(eId core.Event_Key, agr ...interface{}) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Register(eId core.Event_Key, f interface{}) (err error) { + return defsys.Register(eId, f) +} + +func RegisterGO(eId core.Event_Key, f interface{}) (err error) { + return defsys.Register(eId, f) +} + +func RemoveEvent(eId core.Event_Key, f interface{}) (err error) { + return defsys.RemoveEvent(eId, f) +} + +func TriggerEvent(eId core.Event_Key, agr ...interface{}) { + if defsys != nil { + defsys.TriggerEvent(eId, agr...) + } else { + log.Warnf("event no start") + } +} diff --git a/lego/sys/event/event.go b/lego/sys/event/event.go new file mode 100644 index 000000000..606b66eba --- /dev/null +++ b/lego/sys/event/event.go @@ -0,0 +1,89 @@ +package event + +import ( + "fmt" + "reflect" + + "go_dreamfactory/lego" + "go_dreamfactory/lego/core" +) + +func newSys(options Options) (sys *EventSys, err error) { + sys = &EventSys{ + functions: make(map[core.Event_Key][]*FunctionInfo), + } + return +} + +type EventSys struct { + functions map[core.Event_Key][]*FunctionInfo +} + +func (this *EventSys) Register(eId core.Event_Key, f interface{}) (err error) { + if _, ok := this.functions[eId]; !ok { + this.functions[eId] = []*FunctionInfo{} + } + if this.checkIsRegister(eId, f) { + return fmt.Errorf("Register the same event repeatedly [%s] method", eId) + } + this.functions[eId] = append(this.functions[eId], &FunctionInfo{ + Function: reflect.ValueOf(f), + Goroutine: false, + }) + return +} + +func (this *EventSys) RegisterGO(eId core.Event_Key, f interface{}) (err error) { + if _, ok := this.functions[eId]; !ok { + this.functions[eId] = []*FunctionInfo{} + } + if this.checkIsRegister(eId, f) { + return fmt.Errorf("Register the same event repeatedly [%s] method", eId) + } + this.functions[eId] = append(this.functions[eId], &FunctionInfo{ + Function: reflect.ValueOf(f), + Goroutine: true, + }) + return +} + +func (this *EventSys) checkIsRegister(eId core.Event_Key, f interface{}) bool { + if _, ok := this.functions[eId]; !ok { + return false + } + for _, v := range this.functions[eId] { + if v.Function == reflect.ValueOf(f) { + return true + } + } + return false +} + +//移除事件 +func (this *EventSys) RemoveEvent(eId core.Event_Key, f interface{}) (err error) { + for i, v := range this.functions[eId] { + if v.Function == reflect.ValueOf(f) { + this.functions[eId] = append(this.functions[eId][0:i], this.functions[eId][i+1:]...) + return + } + } + return fmt.Errorf("Unregistered [%s] event", eId) +} + +//触发 +func (this *EventSys) TriggerEvent(eId core.Event_Key, agr ...interface{}) { + defer lego.Recover(fmt.Sprintf("event TriggerEvent:%s", eId)) + if v, ok := this.functions[eId]; ok { + for _, f := range v { + in := make([]reflect.Value, len(agr)) + for j, a := range agr { + in[j] = reflect.ValueOf(a) + } + if f.Goroutine { + go f.Function.Call(in) + } else { + f.Function.Call(in) + } + } + } +} diff --git a/lego/sys/event/options.go b/lego/sys/event/options.go new file mode 100644 index 000000000..17bdda13e --- /dev/null +++ b/lego/sys/event/options.go @@ -0,0 +1,28 @@ +package event + +import ( + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{} + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{} + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/event/sys_test.go b/lego/sys/event/sys_test.go new file mode 100644 index 000000000..543ec5721 --- /dev/null +++ b/lego/sys/event/sys_test.go @@ -0,0 +1,18 @@ +package event_test + +import ( + "fmt" + "testing" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/event" +) + +func Test_sys(t *testing.T) { + if err := event.OnInit(nil); err == nil { + event.Register(core.Event_Key("TestEvent"), func() { + fmt.Printf("TestEvent TriggerEvent") + }) + event.TriggerEvent(core.Event_Key("TestEvent")) + } +} diff --git a/lego/sys/gin/binding/binding.go b/lego/sys/gin/binding/binding.go new file mode 100644 index 000000000..ec3d691e2 --- /dev/null +++ b/lego/sys/gin/binding/binding.go @@ -0,0 +1,73 @@ +package binding + +import "net/http" + +// Content-Type MIME of the most common data formats. +const ( + MIMEJSON = "application/json" + MIMEHTML = "text/html" + MIMEXML = "application/xml" + MIMEXML2 = "text/xml" + MIMEPlain = "text/plain" + MIMEPOSTForm = "application/x-www-form-urlencoded" + MIMEMultipartPOSTForm = "multipart/form-data" + MIMEPROTOBUF = "application/x-protobuf" + MIMEMSGPACK = "application/x-msgpack" + MIMEMSGPACK2 = "application/msgpack" + MIMEYAML = "application/x-yaml" +) + +type Binding interface { + Name() string + Bind(*http.Request, interface{}) error +} + +type StructValidator interface { + ValidateStruct(interface{}) error + Engine() interface{} +} + +var Validator StructValidator = &defaultValidator{} +var ( + JSON = jsonBinding{} + XML = xmlBinding{} + Form = formBinding{} + Query = queryBinding{} + FormPost = formPostBinding{} + FormMultipart = formMultipartBinding{} + ProtoBuf = protobufBinding{} + MsgPack = msgpackBinding{} + YAML = yamlBinding{} + Uri = uriBinding{} + Header = headerBinding{} +) + +func Default(method, contentType string) Binding { + if method == http.MethodGet { + return Form + } + + switch contentType { + case MIMEJSON: + return JSON + case MIMEXML, MIMEXML2: + return XML + case MIMEPROTOBUF: + return ProtoBuf + case MIMEMSGPACK, MIMEMSGPACK2: + return MsgPack + case MIMEYAML: + return YAML + case MIMEMultipartPOSTForm: + return FormMultipart + default: // case MIMEPOSTForm: + return Form + } +} + +func validate(obj interface{}) error { + if Validator == nil { + return nil + } + return Validator.ValidateStruct(obj) +} diff --git a/lego/sys/gin/binding/default_validator.go b/lego/sys/gin/binding/default_validator.go new file mode 100644 index 000000000..bd8764b19 --- /dev/null +++ b/lego/sys/gin/binding/default_validator.go @@ -0,0 +1,97 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "fmt" + "reflect" + "strings" + "sync" + + "github.com/go-playground/validator/v10" +) + +type defaultValidator struct { + once sync.Once + validate *validator.Validate +} + +type SliceValidationError []error + +// Error concatenates all error elements in SliceValidationError into a single string separated by \n. +func (err SliceValidationError) Error() string { + n := len(err) + switch n { + case 0: + return "" + default: + var b strings.Builder + if err[0] != nil { + fmt.Fprintf(&b, "[%d]: %s", 0, err[0].Error()) + } + if n > 1 { + for i := 1; i < n; i++ { + if err[i] != nil { + b.WriteString("\n") + fmt.Fprintf(&b, "[%d]: %s", i, err[i].Error()) + } + } + } + return b.String() + } +} + +var _ StructValidator = &defaultValidator{} + +// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type. +func (v *defaultValidator) ValidateStruct(obj interface{}) error { + if obj == nil { + return nil + } + + value := reflect.ValueOf(obj) + switch value.Kind() { + case reflect.Ptr: + return v.ValidateStruct(value.Elem().Interface()) + case reflect.Struct: + return v.validateStruct(obj) + case reflect.Slice, reflect.Array: + count := value.Len() + validateRet := make(SliceValidationError, 0) + for i := 0; i < count; i++ { + if err := v.ValidateStruct(value.Index(i).Interface()); err != nil { + validateRet = append(validateRet, err) + } + } + if len(validateRet) == 0 { + return nil + } + return validateRet + default: + return nil + } +} + +// validateStruct receives struct type +func (v *defaultValidator) validateStruct(obj interface{}) error { + v.lazyinit() + return v.validate.Struct(obj) +} + +// Engine returns the underlying validator engine which powers the default +// Validator instance. This is useful if you want to register custom validations +// or struct level validations. See validator GoDoc for more info - +// https://pkg.go.dev/github.com/go-playground/validator/v10 +func (v *defaultValidator) Engine() interface{} { + v.lazyinit() + return v.validate +} + +func (v *defaultValidator) lazyinit() { + v.once.Do(func() { + v.validate = validator.New() + v.validate.SetTagName("binding") + }) +} diff --git a/lego/sys/gin/binding/form.go b/lego/sys/gin/binding/form.go new file mode 100644 index 000000000..fa2a6540a --- /dev/null +++ b/lego/sys/gin/binding/form.go @@ -0,0 +1,62 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "errors" + "net/http" +) + +const defaultMemory = 32 << 20 + +type formBinding struct{} +type formPostBinding struct{} +type formMultipartBinding struct{} + +func (formBinding) Name() string { + return "form" +} + +func (formBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseForm(); err != nil { + return err + } + if err := req.ParseMultipartForm(defaultMemory); err != nil && !errors.Is(err, http.ErrNotMultipart) { + return err + } + if err := mapForm(obj, req.Form); err != nil { + return err + } + return validate(obj) +} + +func (formPostBinding) Name() string { + return "form-urlencoded" +} + +func (formPostBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseForm(); err != nil { + return err + } + if err := mapForm(obj, req.PostForm); err != nil { + return err + } + return validate(obj) +} + +func (formMultipartBinding) Name() string { + return "multipart/form-data" +} + +func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { + if err := req.ParseMultipartForm(defaultMemory); err != nil { + return err + } + if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil { + return err + } + + return validate(obj) +} diff --git a/lego/sys/gin/binding/form_mapping.go b/lego/sys/gin/binding/form_mapping.go new file mode 100644 index 000000000..a5798633f --- /dev/null +++ b/lego/sys/gin/binding/form_mapping.go @@ -0,0 +1,403 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "go_dreamfactory/lego/utils/convert" +) + +var ( + errUnknownType = errors.New("unknown type") + + // ErrConvertMapStringSlice can not covert to map[string][]string + ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings") + + // ErrConvertToMapString can not convert to map[string]string + ErrConvertToMapString = errors.New("can not convert to map of strings") +) + +func mapURI(ptr interface{}, m map[string][]string) error { + return mapFormByTag(ptr, m, "uri") +} + +func mapForm(ptr interface{}, form map[string][]string) error { + return mapFormByTag(ptr, form, "form") +} + +func MapFormWithTag(ptr interface{}, form map[string][]string, tag string) error { + return mapFormByTag(ptr, form, tag) +} + +var emptyField = reflect.StructField{} + +func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error { + // Check if ptr is a map + ptrVal := reflect.ValueOf(ptr) + var pointed interface{} + if ptrVal.Kind() == reflect.Ptr { + ptrVal = ptrVal.Elem() + pointed = ptrVal.Interface() + } + if ptrVal.Kind() == reflect.Map && + ptrVal.Type().Key().Kind() == reflect.String { + if pointed != nil { + ptr = pointed + } + return setFormMap(ptr, form) + } + + return mappingByPtr(ptr, formSource(form), tag) +} + +// setter tries to set value on a walking by fields of a struct +type setter interface { + TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSet bool, err error) +} + +type formSource map[string][]string + +var _ setter = formSource(nil) + +// TrySet tries to set a value by request's form source (like map[string][]string) +func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSet bool, err error) { + return setByForm(value, field, form, tagValue, opt) +} + +func mappingByPtr(ptr interface{}, setter setter, tag string) error { + _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag) + return err +} + +func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { + if field.Tag.Get(tag) == "-" { // just ignoring this field + return false, nil + } + + vKind := value.Kind() + + if vKind == reflect.Ptr { + var isNew bool + vPtr := value + if value.IsNil() { + isNew = true + vPtr = reflect.New(value.Type().Elem()) + } + isSet, err := mapping(vPtr.Elem(), field, setter, tag) + if err != nil { + return false, err + } + if isNew && isSet { + value.Set(vPtr) + } + return isSet, nil + } + + if vKind != reflect.Struct || !field.Anonymous { + ok, err := tryToSetValue(value, field, setter, tag) + if err != nil { + return false, err + } + if ok { + return true, nil + } + } + + if vKind == reflect.Struct { + tValue := value.Type() + + var isSet bool + for i := 0; i < value.NumField(); i++ { + sf := tValue.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + ok, err := mapping(value.Field(i), sf, setter, tag) + if err != nil { + return false, err + } + isSet = isSet || ok + } + return isSet, nil + } + return false, nil +} + +type setOptions struct { + isDefaultExists bool + defaultValue string +} + +func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) { + var tagValue string + var setOpt setOptions + + tagValue = field.Tag.Get(tag) + tagValue, opts := head(tagValue, ",") + + if tagValue == "" { // default value is FieldName + tagValue = field.Name + } + if tagValue == "" { // when field is "emptyField" variable + return false, nil + } + + var opt string + for len(opts) > 0 { + opt, opts = head(opts, ",") + + if k, v := head(opt, "="); k == "default" { + setOpt.isDefaultExists = true + setOpt.defaultValue = v + } + } + + return setter.TrySet(value, field, tagValue, setOpt) +} + +func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSet bool, err error) { + vs, ok := form[tagValue] + if !ok && !opt.isDefaultExists { + return false, nil + } + + switch value.Kind() { + case reflect.Slice: + if !ok { + vs = []string{opt.defaultValue} + } + return true, setSlice(vs, value, field) + case reflect.Array: + if !ok { + vs = []string{opt.defaultValue} + } + if len(vs) != value.Len() { + return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String()) + } + return true, setArray(vs, value, field) + default: + var val string + if !ok { + val = opt.defaultValue + } + + if len(vs) > 0 { + val = vs[0] + } + return true, setWithProperType(val, value, field) + } +} + +func setWithProperType(val string, value reflect.Value, field reflect.StructField) error { + switch value.Kind() { + case reflect.Int: + return setIntField(val, 0, value) + case reflect.Int8: + return setIntField(val, 8, value) + case reflect.Int16: + return setIntField(val, 16, value) + case reflect.Int32: + return setIntField(val, 32, value) + case reflect.Int64: + switch value.Interface().(type) { + case time.Duration: + return setTimeDuration(val, value) + } + return setIntField(val, 64, value) + case reflect.Uint: + return setUintField(val, 0, value) + case reflect.Uint8: + return setUintField(val, 8, value) + case reflect.Uint16: + return setUintField(val, 16, value) + case reflect.Uint32: + return setUintField(val, 32, value) + case reflect.Uint64: + return setUintField(val, 64, value) + case reflect.Bool: + return setBoolField(val, value) + case reflect.Float32: + return setFloatField(val, 32, value) + case reflect.Float64: + return setFloatField(val, 64, value) + case reflect.String: + value.SetString(val) + case reflect.Struct: + switch value.Interface().(type) { + case time.Time: + return setTimeField(val, field, value) + } + return json.Unmarshal(convert.StringToBytes(val), value.Addr().Interface()) + case reflect.Map: + return json.Unmarshal(convert.StringToBytes(val), value.Addr().Interface()) + default: + return errUnknownType + } + return nil +} + +func setIntField(val string, bitSize int, field reflect.Value) error { + if val == "" { + val = "0" + } + intVal, err := strconv.ParseInt(val, 10, bitSize) + if err == nil { + field.SetInt(intVal) + } + return err +} + +func setUintField(val string, bitSize int, field reflect.Value) error { + if val == "" { + val = "0" + } + uintVal, err := strconv.ParseUint(val, 10, bitSize) + if err == nil { + field.SetUint(uintVal) + } + return err +} + +func setBoolField(val string, field reflect.Value) error { + if val == "" { + val = "false" + } + boolVal, err := strconv.ParseBool(val) + if err == nil { + field.SetBool(boolVal) + } + return err +} + +func setFloatField(val string, bitSize int, field reflect.Value) error { + if val == "" { + val = "0.0" + } + floatVal, err := strconv.ParseFloat(val, bitSize) + if err == nil { + field.SetFloat(floatVal) + } + return err +} + +func setTimeField(val string, structField reflect.StructField, value reflect.Value) error { + timeFormat := structField.Tag.Get("time_format") + if timeFormat == "" { + timeFormat = time.RFC3339 + } + + switch tf := strings.ToLower(timeFormat); tf { + case "unix", "unixnano": + tv, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return err + } + + d := time.Duration(1) + if tf == "unixnano" { + d = time.Second + } + + t := time.Unix(tv/int64(d), tv%int64(d)) + value.Set(reflect.ValueOf(t)) + return nil + } + + if val == "" { + value.Set(reflect.ValueOf(time.Time{})) + return nil + } + + l := time.Local + if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC { + l = time.UTC + } + + if locTag := structField.Tag.Get("time_location"); locTag != "" { + loc, err := time.LoadLocation(locTag) + if err != nil { + return err + } + l = loc + } + + t, err := time.ParseInLocation(timeFormat, val, l) + if err != nil { + return err + } + + value.Set(reflect.ValueOf(t)) + return nil +} + +func setArray(vals []string, value reflect.Value, field reflect.StructField) error { + for i, s := range vals { + err := setWithProperType(s, value.Index(i), field) + if err != nil { + return err + } + } + return nil +} + +func setSlice(vals []string, value reflect.Value, field reflect.StructField) error { + slice := reflect.MakeSlice(value.Type(), len(vals), len(vals)) + err := setArray(vals, slice, field) + if err != nil { + return err + } + value.Set(slice) + return nil +} + +func setTimeDuration(val string, value reflect.Value) error { + d, err := time.ParseDuration(val) + if err != nil { + return err + } + value.Set(reflect.ValueOf(d)) + return nil +} + +func head(str, sep string) (head string, tail string) { + idx := strings.Index(str, sep) + if idx < 0 { + return str, "" + } + return str[:idx], str[idx+len(sep):] +} + +func setFormMap(ptr interface{}, form map[string][]string) error { + el := reflect.TypeOf(ptr).Elem() + + if el.Kind() == reflect.Slice { + ptrMap, ok := ptr.(map[string][]string) + if !ok { + return ErrConvertMapStringSlice + } + for k, v := range form { + ptrMap[k] = v + } + + return nil + } + + ptrMap, ok := ptr.(map[string]string) + if !ok { + return ErrConvertToMapString + } + for k, v := range form { + ptrMap[k] = v[len(v)-1] // pick last + } + + return nil +} diff --git a/lego/sys/gin/binding/header.go b/lego/sys/gin/binding/header.go new file mode 100644 index 000000000..b99302af8 --- /dev/null +++ b/lego/sys/gin/binding/header.go @@ -0,0 +1,34 @@ +package binding + +import ( + "net/http" + "net/textproto" + "reflect" +) + +type headerBinding struct{} + +func (headerBinding) Name() string { + return "header" +} + +func (headerBinding) Bind(req *http.Request, obj interface{}) error { + + if err := mapHeader(obj, req.Header); err != nil { + return err + } + + return validate(obj) +} + +func mapHeader(ptr interface{}, h map[string][]string) error { + return mappingByPtr(ptr, headerSource(h), "header") +} + +type headerSource map[string][]string + +var _ setter = headerSource(nil) + +func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (bool, error) { + return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt) +} diff --git a/lego/sys/gin/binding/json.go b/lego/sys/gin/binding/json.go new file mode 100644 index 000000000..a0713af9d --- /dev/null +++ b/lego/sys/gin/binding/json.go @@ -0,0 +1,55 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "net/http" +) + +// EnableDecoderUseNumber is used to call the UseNumber method on the JSON +// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an +// interface{} as a Number instead of as a float64. +var EnableDecoderUseNumber = false + +// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method +// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to +// return an error when the destination is a struct and the input contains object +// keys which do not match any non-ignored, exported fields in the destination. +var EnableDecoderDisallowUnknownFields = false + +type jsonBinding struct{} + +func (jsonBinding) Name() string { + return "json" +} + +func (jsonBinding) Bind(req *http.Request, obj interface{}) error { + if req == nil || req.Body == nil { + return errors.New("invalid request") + } + return decodeJSON(req.Body, obj) +} + +func (jsonBinding) BindBody(body []byte, obj interface{}) error { + return decodeJSON(bytes.NewReader(body), obj) +} + +func decodeJSON(r io.Reader, obj interface{}) error { + decoder := json.NewDecoder(r) + if EnableDecoderUseNumber { + decoder.UseNumber() + } + if EnableDecoderDisallowUnknownFields { + decoder.DisallowUnknownFields() + } + if err := decoder.Decode(obj); err != nil { + return err + } + return validate(obj) +} diff --git a/lego/sys/gin/binding/msgpack.go b/lego/sys/gin/binding/msgpack.go new file mode 100644 index 000000000..2a84b8db7 --- /dev/null +++ b/lego/sys/gin/binding/msgpack.go @@ -0,0 +1,31 @@ +package binding + +import ( + "bytes" + "io" + "net/http" + + "github.com/ugorji/go/codec" +) + +type msgpackBinding struct{} + +func (msgpackBinding) Name() string { + return "msgpack" +} + +func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { + return decodeMsgPack(req.Body, obj) +} + +func (msgpackBinding) BindBody(body []byte, obj interface{}) error { + return decodeMsgPack(bytes.NewReader(body), obj) +} + +func decodeMsgPack(r io.Reader, obj interface{}) error { + cdc := new(codec.MsgpackHandle) + if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { + return err + } + return validate(obj) +} diff --git a/lego/sys/gin/binding/multipart_form_mapping.go b/lego/sys/gin/binding/multipart_form_mapping.go new file mode 100644 index 000000000..c4d7ed742 --- /dev/null +++ b/lego/sys/gin/binding/multipart_form_mapping.go @@ -0,0 +1,74 @@ +// Copyright 2019 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "errors" + "mime/multipart" + "net/http" + "reflect" +) + +type multipartRequest http.Request + +var _ setter = (*multipartRequest)(nil) + +var ( + // ErrMultiFileHeader multipart.FileHeader invalid + ErrMultiFileHeader = errors.New("unsupported field type for multipart.FileHeader") + + // ErrMultiFileHeaderLenInvalid array for []*multipart.FileHeader len invalid + ErrMultiFileHeaderLenInvalid = errors.New("unsupported len of array for []*multipart.FileHeader") +) + +// TrySet tries to set a value by the multipart request with the binding a form file +func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (bool, error) { + if files := r.MultipartForm.File[key]; len(files) != 0 { + return setByMultipartFormFile(value, field, files) + } + + return setByForm(value, field, r.MultipartForm.Value, key, opt) +} + +func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSet bool, err error) { + switch value.Kind() { + case reflect.Ptr: + switch value.Interface().(type) { + case *multipart.FileHeader: + value.Set(reflect.ValueOf(files[0])) + return true, nil + } + case reflect.Struct: + switch value.Interface().(type) { + case multipart.FileHeader: + value.Set(reflect.ValueOf(*files[0])) + return true, nil + } + case reflect.Slice: + slice := reflect.MakeSlice(value.Type(), len(files), len(files)) + isSet, err = setArrayOfMultipartFormFiles(slice, field, files) + if err != nil || !isSet { + return isSet, err + } + value.Set(slice) + return true, nil + case reflect.Array: + return setArrayOfMultipartFormFiles(value, field, files) + } + return false, ErrMultiFileHeader +} + +func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSet bool, err error) { + if value.Len() != len(files) { + return false, ErrMultiFileHeaderLenInvalid + } + for i := range files { + set, err := setByMultipartFormFile(value.Index(i), field, files[i:i+1]) + if err != nil || !set { + return set, err + } + } + return true, nil +} diff --git a/lego/sys/gin/binding/protobuf.go b/lego/sys/gin/binding/protobuf.go new file mode 100644 index 000000000..c71eb80cd --- /dev/null +++ b/lego/sys/gin/binding/protobuf.go @@ -0,0 +1,35 @@ +package binding + +import ( + "errors" + "io/ioutil" + "net/http" + + "google.golang.org/protobuf/proto" +) + +type protobufBinding struct{} + +func (protobufBinding) Name() string { + return "protobuf" +} + +func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + return err + } + return b.BindBody(buf, obj) +} + +func (protobufBinding) BindBody(body []byte, obj interface{}) error { + msg, ok := obj.(proto.Message) + if !ok { + return errors.New("obj is not ProtoMessage") + } + if err := proto.Unmarshal(body, msg); err != nil { + return err + } + return nil + +} diff --git a/lego/sys/gin/binding/query.go b/lego/sys/gin/binding/query.go new file mode 100644 index 000000000..8c13fa35e --- /dev/null +++ b/lego/sys/gin/binding/query.go @@ -0,0 +1,17 @@ +package binding + +import "net/http" + +type queryBinding struct{} + +func (queryBinding) Name() string { + return "query" +} + +func (queryBinding) Bind(req *http.Request, obj interface{}) error { + values := req.URL.Query() + if err := mapForm(obj, values); err != nil { + return err + } + return validate(obj) +} diff --git a/lego/sys/gin/binding/uri.go b/lego/sys/gin/binding/uri.go new file mode 100644 index 000000000..d21d64aad --- /dev/null +++ b/lego/sys/gin/binding/uri.go @@ -0,0 +1,14 @@ +package binding + +type uriBinding struct{} + +func (uriBinding) Name() string { + return "uri" +} + +func (uriBinding) BindUri(m map[string][]string, obj interface{}) error { + if err := mapURI(obj, m); err != nil { + return err + } + return validate(obj) +} diff --git a/lego/sys/gin/binding/xml.go b/lego/sys/gin/binding/xml.go new file mode 100644 index 000000000..4e9011496 --- /dev/null +++ b/lego/sys/gin/binding/xml.go @@ -0,0 +1,33 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import ( + "bytes" + "encoding/xml" + "io" + "net/http" +) + +type xmlBinding struct{} + +func (xmlBinding) Name() string { + return "xml" +} + +func (xmlBinding) Bind(req *http.Request, obj interface{}) error { + return decodeXML(req.Body, obj) +} + +func (xmlBinding) BindBody(body []byte, obj interface{}) error { + return decodeXML(bytes.NewReader(body), obj) +} +func decodeXML(r io.Reader, obj interface{}) error { + decoder := xml.NewDecoder(r) + if err := decoder.Decode(obj); err != nil { + return err + } + return validate(obj) +} diff --git a/lego/sys/gin/binding/yaml.go b/lego/sys/gin/binding/yaml.go new file mode 100644 index 000000000..da2efcd73 --- /dev/null +++ b/lego/sys/gin/binding/yaml.go @@ -0,0 +1,31 @@ +package binding + +import ( + "bytes" + "io" + "net/http" + + "gopkg.in/yaml.v2" +) + +type yamlBinding struct{} + +func (yamlBinding) Name() string { + return "yaml" +} + +func (yamlBinding) Bind(req *http.Request, obj interface{}) error { + return decodeYAML(req.Body, obj) +} + +func (yamlBinding) BindBody(body []byte, obj interface{}) error { + return decodeYAML(bytes.NewReader(body), obj) +} + +func decodeYAML(r io.Reader, obj interface{}) error { + decoder := yaml.NewDecoder(r) + if err := decoder.Decode(obj); err != nil { + return err + } + return validate(obj) +} diff --git a/lego/sys/gin/core.go b/lego/sys/gin/core.go new file mode 100644 index 000000000..bd388b0ad --- /dev/null +++ b/lego/sys/gin/core.go @@ -0,0 +1,73 @@ +package gin + +import ( + "net/http" + + "go_dreamfactory/lego/sys/gin/engine" +) + +type ISys interface { + engine.IRoutes + Close() (err error) +} + +var defsys ISys + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Close() (err error) { + return defsys.Close() +} + +func Use(handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.Use(handlers...) +} +func Handle(httpMethod string, relativePath string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.Handle(httpMethod, relativePath, handlers...) +} +func Any(relativePath string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.Any(relativePath, handlers...) +} +func GET(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.GET(httpMethod, handlers...) +} +func POST(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.POST(httpMethod, handlers...) +} +func DELETE(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.DELETE(httpMethod, handlers...) +} +func PATCH(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.PATCH(httpMethod, handlers...) +} +func PUT(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.PUT(httpMethod, handlers...) +} +func OPTIONS(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.OPTIONS(httpMethod, handlers...) +} +func HEAD(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.HEAD(httpMethod, handlers...) +} +func StaticFile(relativePath string, filepath string) engine.IRoutes { + return defsys.StaticFile(relativePath, filepath) +} +func StaticFileFS(relativePath string, filepath string, fs http.FileSystem) engine.IRoutes { + return defsys.StaticFileFS(relativePath, filepath, fs) +} + +func Static(relativePath string, root string) engine.IRoutes { + return defsys.Static(relativePath, root) +} + +func StaticFS(relativePath string, fs http.FileSystem) engine.IRoutes { + return defsys.StaticFS(relativePath, fs) +} diff --git a/lego/sys/gin/engine/context.go b/lego/sys/gin/engine/context.go new file mode 100644 index 000000000..c01369042 --- /dev/null +++ b/lego/sys/gin/engine/context.go @@ -0,0 +1,858 @@ +package engine + +import ( + "errors" + "io" + "io/ioutil" + "math" + "mime/multipart" + "net" + "net/http" + "net/url" + "os" + "strings" + "sync" + "time" + + "go_dreamfactory/lego/sys/gin/binding" + "go_dreamfactory/lego/sys/gin/render" +) + +const ( + MIMEJSON = binding.MIMEJSON + MIMEHTML = binding.MIMEHTML + MIMEXML = binding.MIMEXML + MIMEXML2 = binding.MIMEXML2 + MIMEPlain = binding.MIMEPlain + MIMEPOSTForm = binding.MIMEPOSTForm + MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm + MIMEYAML = binding.MIMEYAML +) +const abortIndex int8 = math.MaxInt8 >> 1 + +func newContext(sys ISys, engine *Engine, params *Params, skippedNodes *[]skippedNode) *Context { + return &Context{ + engine: engine, + params: params, + skippedNodes: skippedNodes, + writermem: ResponseWriter{log: sys}, + } +} + +type Context struct { + Sys ISys + engine *Engine + writermem ResponseWriter + Request *http.Request + Writer IResponseWriter + Params Params + handlers HandlersChain + index int8 + fullPath string + params *Params + skippedNodes *[]skippedNode + mu sync.RWMutex + Keys map[string]interface{} + Errors errorMsgs + Accepted []string + queryCache url.Values + formCache url.Values + sameSite http.SameSite +} + +func (this *Context) Copy() *Context { + cp := Context{ + writermem: this.writermem, + Request: this.Request, + Params: this.Params, + engine: this.engine, + } + cp.writermem.ResponseWriter = nil + cp.Writer = &cp.writermem + cp.index = abortIndex + cp.handlers = nil + cp.Keys = map[string]interface{}{} + for k, v := range this.Keys { + cp.Keys[k] = v + } + paramCopy := make([]Param, len(cp.Params)) + copy(paramCopy, cp.Params) + cp.Params = paramCopy + return &cp +} + +func (this *Context) HandlerName() string { + return nameOfFunction(this.handlers.Last()) +} + +func (this *Context) HandlerNames() []string { + hn := make([]string, 0, len(this.handlers)) + for _, val := range this.handlers { + hn = append(hn, nameOfFunction(val)) + } + return hn +} + +func (this *Context) Handler() HandlerFunc { + return this.handlers.Last() +} + +/* + FullPath 返回匹配的路由完整路径。 对于未找到的路线 + 返回一个空字符串。 +*/ +func (c *Context) FullPath() string { + return c.fullPath +} + +func (this *Context) Next() { + this.index++ + for this.index < int8(len(this.handlers)) { + this.handlers[this.index](this) + this.index++ + } +} + +/* + 如果当前上下文被中止,IsAborted 返回 true。 +*/ +func (this *Context) IsAborted() bool { + return this.index >= abortIndex +} + +/* + Abort 防止挂起的处理程序被调用。 请注意,这不会停止当前处理程序。 + 假设你有一个授权中间件来验证当前请求是否被授权。 + 如果授权失败(例如:密码不匹配),调用 Abort 以确保剩余的 handlers + 因为这个请求没有被调用。 +*/ +func (this *Context) Abort() { + this.index = abortIndex +} + +/* + AbortWithStatus 调用 `Abort()` 并使用指定的状态代码写入标头。 + 例如,验证请求失败的尝试可以使用:context.AbortWithStatus(401)。 +*/ +func (this *Context) AbortWithStatus(code int) { + this.Status(code) + this.Writer.WriteHeaderNow() + this.Abort() +} + +func (this *Context) AbortWithStatusJSON(code int, jsonObj interface{}) { + this.Abort() + this.JSON(code, jsonObj) +} + +func (this *Context) AbortWithError(code int, err error) *Error { + this.AbortWithStatus(code) + return this.Error(err) +} + +func (this *Context) Set(key string, value interface{}) { + this.mu.Lock() + if this.Keys == nil { + this.Keys = make(map[string]interface{}) + } + + this.Keys[key] = value + this.mu.Unlock() +} + +func (this *Context) Get(key string) (value interface{}, exists bool) { + this.mu.RLock() + value, exists = this.Keys[key] + this.mu.RUnlock() + return +} + +/* +如果存在,MustGet 返回给定键的值,否则抛出异常。 +*/ +func (this *Context) MustGet(key string) interface{} { + if value, exists := this.Get(key); exists { + return value + } + panic("Key \"" + key + "\" does not exist") +} + +func (this *Context) GetString(key string) (s string) { + if val, ok := this.Get(key); ok && val != nil { + s, _ = val.(string) + } + return +} + +func (this *Context) GetBool(key string) (b bool) { + if val, ok := this.Get(key); ok && val != nil { + b, _ = val.(bool) + } + return +} + +func (this *Context) GetInt(key string) (i int) { + if val, ok := this.Get(key); ok && val != nil { + i, _ = val.(int) + } + return +} +func (this *Context) GetInt64(key string) (i64 int64) { + if val, ok := this.Get(key); ok && val != nil { + i64, _ = val.(int64) + } + return +} +func (this *Context) GetUint(key string) (ui uint) { + if val, ok := this.Get(key); ok && val != nil { + ui, _ = val.(uint) + } + return +} + +func (c *Context) GetUInt32(key string) (i uint32) { + if val, ok := c.Get(key); ok && val != nil { + i, _ = val.(uint32) + } + return +} + +func (this *Context) GetUint64(key string) (ui64 uint64) { + if val, ok := this.Get(key); ok && val != nil { + ui64, _ = val.(uint64) + } + return +} +func (this *Context) GetFloat64(key string) (f64 float64) { + if val, ok := this.Get(key); ok && val != nil { + f64, _ = val.(float64) + } + return +} +func (this *Context) GetTime(key string) (t time.Time) { + if val, ok := this.Get(key); ok && val != nil { + t, _ = val.(time.Time) + } + return +} +func (this *Context) GetDuration(key string) (d time.Duration) { + if val, ok := this.Get(key); ok && val != nil { + d, _ = val.(time.Duration) + } + return +} +func (this *Context) GetStringSlice(key string) (ss []string) { + if val, ok := this.Get(key); ok && val != nil { + ss, _ = val.([]string) + } + return +} +func (this *Context) GetStringMap(key string) (sm map[string]interface{}) { + if val, ok := this.Get(key); ok && val != nil { + sm, _ = val.(map[string]interface{}) + } + return +} +func (this *Context) GetStringMapString(key string) (sms map[string]string) { + if val, ok := this.Get(key); ok && val != nil { + sms, _ = val.(map[string]string) + } + return +} + +func (this *Context) GetStringMapStringSlice(key string) (smss map[string][]string) { + if val, ok := this.Get(key); ok && val != nil { + smss, _ = val.(map[string][]string) + } + return +} + +func (this *Context) Header(key, value string) { + if value == "" { + this.Writer.Header().Del(key) + return + } + this.Writer.Header().Set(key, value) +} + +// Status sets the HTTP response code. +func (this *Context) Status(code int) { + this.Writer.WriteHeader(code) +} + +func (this *Context) Param(key string) string { + return this.Params.ByName(key) +} + +func (this *Context) AddParam(key, value string) { + this.Params = append(this.Params, Param{Key: key, Value: value}) +} + +func (this *Context) Query(key string) (value string) { + value, _ = this.GetQuery(key) + return +} + +func (this *Context) DefaultQuery(key, defaultValue string) string { + if value, ok := this.GetQuery(key); ok { + return value + } + return defaultValue +} + +func (this *Context) GetQuery(key string) (string, bool) { + if values, ok := this.GetQueryArray(key); ok { + return values[0], ok + } + return "", false +} + +func (this *Context) initQueryCache() { + if this.queryCache == nil { + if this.Request != nil { + this.queryCache = this.Request.URL.Query() + } else { + this.queryCache = url.Values{} + } + } +} + +func (this *Context) GetQueryArray(key string) (values []string, ok bool) { + this.initQueryCache() + values, ok = this.queryCache[key] + return +} + +func (this *Context) QueryMap(key string) (dicts map[string]string) { + dicts, _ = this.GetQueryMap(key) + return +} + +func (this *Context) GetQueryMap(key string) (map[string]string, bool) { + this.initQueryCache() + return this.get(this.queryCache, key) +} + +func (this *Context) PostForm(key string) (value string) { + value, _ = this.GetPostForm(key) + return +} + +func (this *Context) GetPostForm(key string) (string, bool) { + if values, ok := this.GetPostFormArray(key); ok { + return values[0], ok + } + return "", false +} + +func (this *Context) initFormCache() { + if this.formCache == nil { + this.formCache = make(url.Values) + req := this.Request + if err := req.ParseMultipartForm(this.engine.MaxMultipartMemory); err != nil { + if !errors.Is(err, http.ErrNotMultipart) { + this.Sys.Errorf("error on parse multipart form array: %v", err) + } + } + this.formCache = req.PostForm + } +} + +func (this *Context) GetPostFormArray(key string) (values []string, ok bool) { + this.initFormCache() + values, ok = this.formCache[key] + return +} + +func (this *Context) PostFormMap(key string) (dicts map[string]string) { + dicts, _ = this.GetPostFormMap(key) + return +} + +func (this *Context) GetPostFormMap(key string) (map[string]string, bool) { + this.initFormCache() + return this.get(this.formCache, key) +} + +func (this *Context) FormFile(name string) (*multipart.FileHeader, error) { + if this.Request.MultipartForm == nil { + if err := this.Request.ParseMultipartForm(this.engine.MaxMultipartMemory); err != nil { + return nil, err + } + } + f, fh, err := this.Request.FormFile(name) + if err != nil { + return nil, err + } + f.Close() + return fh, err +} + +/* + MultipartForm 是解析后的多部分表单,包括文件上传。 +*/ +func (this *Context) MultipartForm() (*multipart.Form, error) { + err := this.Request.ParseMultipartForm(this.engine.MaxMultipartMemory) + return this.Request.MultipartForm, err +} + +/* + 保存上传文件 +*/ +func (this *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { + src, err := file.Open() + if err != nil { + return err + } + defer src.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, src) + return err +} + +func (this *Context) GetRawData() ([]byte, error) { + return ioutil.ReadAll(this.Request.Body) +} + +//序列化-------------------------------------------------------------------------------------------- +func (this *Context) Bind(obj interface{}) error { + b := binding.Default(this.Request.Method, this.ContentType()) + return this.MustBindWith(obj, b) +} + +func (this *Context) ShouldBindJSON(obj interface{}) error { + return this.ShouldBindWith(obj, binding.JSON) +} + +func (this *Context) MustBindWith(obj interface{}, b binding.Binding) error { + if err := this.ShouldBindWith(obj, b); err != nil { + this.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck + return err + } + return nil +} + +func (this *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { + return b.Bind(this.Request, obj) +} +func (this *Context) ShouldBindUri(obj interface{}) error { + m := make(map[string][]string) + for _, v := range this.Params { + m[v.Key] = []string{v.Value} + } + return binding.Uri.BindUri(m, obj) +} + +func (this *Context) BindJSON(obj interface{}) error { + return this.MustBindWith(obj, binding.JSON) +} +func (this *Context) BindXML(obj interface{}) error { + return this.MustBindWith(obj, binding.XML) +} +func (this *Context) BindQuery(obj interface{}) error { + return this.MustBindWith(obj, binding.Query) +} + +func (this *Context) BindYAML(obj interface{}) error { + return this.MustBindWith(obj, binding.YAML) +} + +func (this *Context) BindHeader(obj interface{}) error { + return this.MustBindWith(obj, binding.Header) +} + +func (this *Context) BindUri(obj interface{}) error { + if err := this.ShouldBindUri(obj); err != nil { + this.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck + return err + } + return nil +} + +//输出----------------------------------------------------------------------------------------- +func (this *Context) HTML(code int, name string, obj interface{}) { + instance := this.engine.HTMLRender.Instance(name, obj) + this.Render(code, instance) +} + +func (this *Context) IndentedJSON(code int, obj interface{}) { + this.Render(code, render.IndentedJSON{Data: obj}) +} + +func (this *Context) SecureJSON(code int, obj interface{}) { + this.Render(code, render.SecureJSON{Prefix: this.engine.secureJSONPrefix, Data: obj}) +} + +func (this *Context) JSONP(code int, obj interface{}) { + callback := this.DefaultQuery("callback", "") + if callback == "" { + this.Render(code, render.JSON{Data: obj}) + return + } + this.Render(code, render.JsonpJSON{Callback: callback, Data: obj}) +} + +func (this *Context) JSON(code int, obj interface{}) { + this.Render(code, render.JSON{Data: obj}) +} + +func (this *Context) AsciiJSON(code int, obj interface{}) { + this.Render(code, render.AsciiJSON{Data: obj}) +} + +func (this *Context) PureJSON(code int, obj interface{}) { + this.Render(code, render.PureJSON{Data: obj}) +} + +func (this *Context) XML(code int, obj interface{}) { + this.Render(code, render.XML{Data: obj}) +} + +func (this *Context) YAML(code int, obj interface{}) { + this.Render(code, render.YAML{Data: obj}) +} + +func (this *Context) ProtoBuf(code int, obj interface{}) { + this.Render(code, render.ProtoBuf{Data: obj}) +} + +func (this *Context) String(code int, format string, values ...interface{}) { + this.Render(code, render.String{Format: format, Data: values}) +} + +func (this *Context) Redirect(code int, location string) { + this.Render(-1, render.Redirect{ + Code: code, + Location: location, + Request: this.Request, + }) +} + +func (this *Context) Data(code int, contentType string, data []byte) { + this.Render(code, render.Data{ + ContentType: contentType, + Data: data, + }) +} + +func (this *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) { + this.Render(code, render.Reader{ + Headers: extraHeaders, + ContentType: contentType, + ContentLength: contentLength, + Reader: reader, + }) +} + +func (this *Context) File(filepath string) { + http.ServeFile(this.Writer, this.Request, filepath) +} + +/*渲染页面接口*/ +func (this *Context) Render(code int, r render.Render) { + this.Status(code) + + if !bodyAllowedForStatus(code) { + r.WriteContentType(this.Writer) + this.Writer.WriteHeaderNow() + return + } + if err := r.Render(this.Writer); err != nil { + panic(err) + } +} + +func (this *Context) FileFromFS(filepath string, fs http.FileSystem) { + defer func(old string) { + this.Request.URL.Path = old + }(this.Request.URL.Path) + this.Request.URL.Path = filepath + http.FileServer(fs).ServeHTTP(this.Writer, this.Request) +} + +/* +以高效的方式将指定的文件写入正文流 +在客户端,通常会使用给定的文件名下载文件 +*/ +func (this *Context) FileAttachment(filepath, filename string) { + if isASCII(filename) { + this.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`) + } else { + this.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename)) + } + http.ServeFile(this.Writer, this.Request, filepath) +} + +func (this *Context) Stream(step func(w io.Writer) bool) bool { + w := this.Writer + clientGone := w.CloseNotify() + for { + select { + case <-clientGone: + return true + default: + keepOpen := step(w) + w.Flush() + if !keepOpen { + return false + } + } + } +} + +type Negotiate struct { + Offered []string + HTMLName string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + YAMLData interface{} + Data interface{} +} + +func (this *Context) Negotiate(code int, config Negotiate) { + switch this.NegotiateFormat(config.Offered...) { + case binding.MIMEJSON: + data := chooseData(config.JSONData, config.Data) + this.JSON(code, data) + + case binding.MIMEHTML: + data := chooseData(config.HTMLData, config.Data) + this.HTML(code, config.HTMLName, data) + + case binding.MIMEXML: + data := chooseData(config.XMLData, config.Data) + this.XML(code, data) + + case binding.MIMEYAML: + data := chooseData(config.YAMLData, config.Data) + this.YAML(code, data) + + default: + this.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck + } +} + +func (this *Context) NegotiateFormat(offered ...string) string { + assert1(len(offered) > 0, "you must provide at least one offer") + + if this.Accepted == nil { + this.Accepted = parseAccept(this.requestHeader("Accept")) + } + if len(this.Accepted) == 0 { + return offered[0] + } + for _, accepted := range this.Accepted { + for _, offer := range offered { + // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers, + // therefore we can just iterate over the string without casting it into []rune + i := 0 + for ; i < len(accepted); i++ { + if accepted[i] == '*' || offer[i] == '*' { + return offer + } + if accepted[i] != offer[i] { + break + } + } + if i == len(accepted) { + return offer + } + } + } + return "" +} + +func (this *Context) SetAccepted(formats ...string) { + this.Accepted = formats +} + +func (this *Context) Deadline() (deadline time.Time, ok bool) { + if this.Request == nil || this.Request.Context() == nil { + return + } + return this.Request.Context().Deadline() +} + +func (this *Context) Done() <-chan struct{} { + if this.Request == nil || this.Request.Context() == nil { + return nil + } + return this.Request.Context().Done() +} + +func (this *Context) Err() error { + if this.Request == nil || this.Request.Context() == nil { + return nil + } + return this.Request.Context().Err() +} + +func (c *Context) Value(key interface{}) interface{} { + if key == 0 { + return c.Request + } + if keyAsString, ok := key.(string); ok { + if val, exists := c.Get(keyAsString); exists { + return val + } + } + if c.Request == nil || c.Request.Context() == nil { + return nil + } + return c.Request.Context().Value(key) +} + +func (this *Context) ContentType() string { + return filterFlags(this.requestHeader("Content-Type")) +} + +func (this *Context) RemoteIP() string { + ip, _, err := net.SplitHostPort(strings.TrimSpace(this.Request.RemoteAddr)) + if err != nil { + return "" + } + return ip +} + +func (this *Context) ClientIP() string { + // 检查我们是否在受信任的平台上运行,如果出错则继续向后运行 + if this.engine.TrustedPlatform != "" { + // Developers can define their own header of Trusted Platform or use predefined constants + if addr := this.requestHeader(this.engine.TrustedPlatform); addr != "" { + return addr + } + } + + /* + // 它还检查 remoteIP 是否是受信任的代理。 + // 为了执行此验证,它将查看 IP 是否包含在至少一个 CIDR 块中 + // 由 Engine.SetTrustedProxies() 定义 + */ + remoteIP := net.ParseIP(this.RemoteIP()) + if remoteIP == nil { + return "" + } + trusted := this.engine.isTrustedProxy(remoteIP) + + if trusted && this.engine.ForwardedByClientIP && this.engine.RemoteIPHeaders != nil { + for _, headerName := range this.engine.RemoteIPHeaders { + ip, valid := this.engine.validateHeader(this.requestHeader(headerName)) + if valid { + return ip + } + } + } + return remoteIP.String() +} + +func (this *Context) IsWebsocket() bool { + if strings.Contains(strings.ToLower(this.requestHeader("Connection")), "upgrade") && + strings.EqualFold(this.requestHeader("Upgrade"), "websocket") { + return true + } + return false +} + +func (this *Context) SetSameSite(samesite http.SameSite) { + this.sameSite = samesite +} + +func (this *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { + if path == "" { + path = "/" + } + http.SetCookie(this.Writer, &http.Cookie{ + Name: name, + Value: url.QueryEscape(value), + MaxAge: maxAge, + Path: path, + Domain: domain, + SameSite: this.sameSite, + Secure: secure, + HttpOnly: httpOnly, + }) +} + +func (this *Context) Cookie(name string) (string, error) { + cookie, err := this.Request.Cookie(name) + if err != nil { + return "", err + } + val, _ := url.QueryUnescape(cookie.Value) + return val, nil +} + +func (this *Context) Error(err error) *Error { + if err == nil { + panic("err is nil") + } + + var parsedError *Error + ok := errors.As(err, &parsedError) + if !ok { + parsedError = &Error{ + Err: err, + Type: ErrorTypePrivate, + } + } + + this.Errors = append(this.Errors, parsedError) + return parsedError +} + +func (this *Context) get(m map[string][]string, key string) (map[string]string, bool) { + dicts := make(map[string]string) + exist := false + for k, v := range m { + if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key { + if j := strings.IndexByte(k[i+1:], ']'); j >= 1 { + exist = true + dicts[k[i+1:][:j]] = v[0] + } + } + } + return dicts, exist +} + +func (this *Context) requestHeader(key string) string { + return this.Request.Header.Get(key) +} + +func (this *Context) reset() { + this.Writer = &this.writermem + this.Params = this.Params[:0] + this.handlers = nil + this.index = -1 + + this.fullPath = "" + this.Keys = nil + this.Errors = this.Errors[:0] + this.Accepted = nil + this.queryCache = nil + this.formCache = nil + this.sameSite = 0 + *this.params = (*this.params)[:0] + *this.skippedNodes = (*this.skippedNodes)[:0] +} + +/* + bodyAllowedForStatus 是 http.bodyAllowedForStatus 非导出函数的副本。 +*/ +func bodyAllowedForStatus(status int) bool { + switch { + case status >= 100 && status <= 199: + return false + case status == http.StatusNoContent: + return false + case status == http.StatusNotModified: + return false + } + return true +} diff --git a/lego/sys/gin/engine/core.go b/lego/sys/gin/engine/core.go new file mode 100644 index 000000000..0e67b2542 --- /dev/null +++ b/lego/sys/gin/engine/core.go @@ -0,0 +1,49 @@ +package engine + +import ( + "net/http" + + "go_dreamfactory/lego/sys/log" +) + +type HandlerFunc func(*Context) +type HandlersChain []HandlerFunc + +func (c HandlersChain) Last() HandlerFunc { + if length := len(c); length > 0 { + return c[length-1] + } + return nil +} + +type RouteInfo struct { + Method string + Path string + Handler string + HandlerFunc HandlerFunc +} + +type RoutesInfo []RouteInfo + +type ISys interface { + log.Ilogf + Debug() bool +} + +type IRoutes interface { + Group(relativePath string, handlers ...HandlerFunc) IRoutes + Use(...HandlerFunc) IRoutes + Handle(string, string, ...HandlerFunc) IRoutes + Any(string, ...HandlerFunc) IRoutes + GET(string, ...HandlerFunc) IRoutes + POST(string, ...HandlerFunc) IRoutes + DELETE(string, ...HandlerFunc) IRoutes + PATCH(string, ...HandlerFunc) IRoutes + PUT(string, ...HandlerFunc) IRoutes + OPTIONS(string, ...HandlerFunc) IRoutes + HEAD(string, ...HandlerFunc) IRoutes + StaticFile(string, string) IRoutes + StaticFileFS(string, string, http.FileSystem) IRoutes + Static(string, string) IRoutes + StaticFS(string, http.FileSystem) IRoutes +} diff --git a/lego/sys/gin/engine/engine.go b/lego/sys/gin/engine/engine.go new file mode 100644 index 000000000..ad96b617f --- /dev/null +++ b/lego/sys/gin/engine/engine.go @@ -0,0 +1,511 @@ +package engine + +import ( + "html/template" + "net" + "net/http" + "path" + "strings" + "sync" + + "go_dreamfactory/lego/sys/gin/render" + "go_dreamfactory/lego/utils/convert" + + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" +) + +/* + 默认文件上传的最大尺寸 +*/ +const defaultMultipartMemory = 32 << 20 // 32 MB +/* + 默认可信代理 +*/ +var defaultTrustedCIDRs = []*net.IPNet{ + { // 0.0.0.0/0 (IPv4) + IP: net.IP{0x0, 0x0, 0x0, 0x0}, + Mask: net.IPMask{0x0, 0x0, 0x0, 0x0}, + }, + { // ::/0 (IPv6) + IP: net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + Mask: net.IPMask{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + }, +} + +func NewEngine(sys ISys) (engine *Engine) { + engine = &Engine{ + RouterGroup: RouterGroup{ + Handlers: nil, + basePath: "/", + root: true, + }, + sys: sys, + FuncMap: template.FuncMap{}, + RedirectTrailingSlash: true, + RedirectFixedPath: false, + HandleMethodNotAllowed: false, + ForwardedByClientIP: true, + RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"}, + UseRawPath: false, + RemoveExtraSlash: false, + UnescapePathValues: true, + MaxMultipartMemory: defaultMultipartMemory, + trees: make(methodTrees, 0, 9), + delims: render.Delims{Left: "{{", Right: "}}"}, + secureJSONPrefix: "while(1);", + trustedProxies: []string{"0.0.0.0/0"}, + trustedCIDRs: defaultTrustedCIDRs, + } + engine.RouterGroup.engine = engine + engine.pool.New = func() interface{} { + return engine.allocateContext() + } + return +} + +var ( + default404Body = []byte("404 page not found") + default405Body = []byte("405 method not allowed") +) +var mimePlain = []string{MIMEPlain} + +type Engine struct { + RouterGroup + sys ISys + UseRawPath bool + /* + 如果启用,路由器尝试修复当前请求路径,如果没有 + 如果没有 + 已为其注册句柄。 + 第一个多余的路径元素,如 ../ 或 // 被删除。 + 之后路由器对清理后的路径进行不区分大小写的查找。 + 如果可以找到该路由的句柄,则路由器进行重定向 + 到正确的路径,GET 请求的状态码为 301,而 GET 请求的状态码为 307 + 所有其他请求方法。 + 例如 /FOO 和 /..//Foo 可以重定向到 /foo。 + RedirectTrailingSlash 与此选项无关。 + */ + RedirectFixedPath bool + /* + 如果为真,路径值将不转义 + */ + UnescapePathValues bool + /* + 如果当前路由无法匹配,但启用自动重定向 + 带有(不带)尾部斜杠的路径的处理程序存在。 + 例如,如果 /foo/ 被请求,但路由只存在于 /foo,则 + 对于 GET 请求,客户端被重定向到 /foo,http 状态码为 301 + 对于所有其他请求方法,则为 307。 + */ + RedirectTrailingSlash bool // + + /* + 如果启用,路由器检查是否允许其他方法 + 当前路由,如果当前请求无法路由。 + 如果是这种情况,则使用“不允许的方法”回答请求 + 和 HTTP 状态码 405。 + 如果不允许其他方法,则将请求委托给 NotFound + 处理程序。 + */ + HandleMethodNotAllowed bool + /* + 可以从 URL 中解析出一个参数,即使带有额外的斜杠。 + */ + RemoveExtraSlash bool + /* + TrustedPlatform 如果设置为值 gin.Platform* 的常量,则信任由设置的标头 + 那个平台,比如判断客户端IP + */ + TrustedPlatform string + /* + ForwardedByClientIP 如果启用,客户端 IP 将从请求的标头中解析 + 匹配存储在 `(*gin.Engine).RemoteIPHeaders` 中的那些。 如果没有 IP + fetched, 它回退到从获取的 IP + `(*gin.Context).Request.RemoteAddr`。 + ForwardedByClientIP 布尔值 + */ + ForwardedByClientIP bool + /* + RemoteIPHeaders 用于获取客户端 IP 时的 headers 列表 + `(*gin.Engine).ForwardedByClientIP` 为 `true` 并且 + `(*gin.Context).Request.RemoteAddr` 被至少一个匹配 + 由 `(*gin.Engine).SetTrustedProxies()` 定义的列表的网络来源。 + */ + RemoteIPHeaders []string + /* + 文件上传的最大尺寸 + */ + MaxMultipartMemory int64 + /* + 是否使用H2C + */ + UseH2C bool + delims render.Delims + secureJSONPrefix string + HTMLRender render.HTMLRender + FuncMap template.FuncMap + noRoute HandlersChain + noMethod HandlersChain + allNoRoute HandlersChain + allNoMethod HandlersChain + pool sync.Pool + trees methodTrees + maxParams uint16 + maxSections uint16 + trustedProxies []string + trustedCIDRs []*net.IPNet +} + +func (this *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { + c := this.pool.Get().(*Context) + c.writermem.reset(w) + c.Request = req + c.reset() + this.handleHTTPRequest(c) + this.pool.Put(c) +} + +func (this *Engine) Handler() http.Handler { + if !this.UseH2C { + return this + } + + h2s := &http2.Server{} + return h2c.NewHandler(this, h2s) +} + +/* + 使用中间件 +*/ +func (this *Engine) Use(middleware ...HandlerFunc) IRoutes { + this.RouterGroup.Use(middleware...) + this.rebuild404Handlers() + this.rebuild405Handlers() + return this +} + +/* + LoadHTMLGlob 加载由 glob 模式标识的 HTML 文件 + 并将结果与 HTML 渲染器相关联。 +*/ +func (this *Engine) LoadHTMLGlob(pattern string) { + left := this.delims.Left + right := this.delims.Right + templ := template.Must(template.New("").Delims(left, right).Funcs(this.FuncMap).ParseGlob(pattern)) + + if this.sys.Debug() { + this.debugPrintLoadTemplate(templ) + this.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: this.FuncMap, Delims: this.delims} + return + } + + this.SetHTMLTemplate(templ) +} + +/* +LoadHTMLFiles 加载一段 HTML 文件 +并将结果与 HTML 渲染器相关联。 +*/ +func (this *Engine) LoadHTMLFiles(files ...string) { + if this.sys.Debug() { + this.HTMLRender = render.HTMLDebug{Files: files, FuncMap: this.FuncMap, Delims: this.delims} + return + } + templ := template.Must(template.New("").Delims(this.delims.Left, this.delims.Right).Funcs(this.FuncMap).ParseFiles(files...)) + this.SetHTMLTemplate(templ) +} + +func (this *Engine) SetHTMLTemplate(templ *template.Template) { + if len(this.trees) > 0 { + this.sys.Warnf(`Since SetHTMLTemplate() is NOT thread-safe. It should only be called + at initialization. ie. before any route is registered or the router is listening in a socket: + router := gin.Default() + router.SetHTMLTemplate(template) // << good place + `) + + } + this.HTMLRender = render.HTMLProduction{Template: templ.Funcs(this.FuncMap)} +} + +/* + 设置template FuncMap +*/ +func (engine *Engine) SetFuncMap(funcMap template.FuncMap) { + engine.FuncMap = funcMap +} + +/* + 404 处理路由 +*/ +func (this *Engine) NoRoute(handlers ...HandlerFunc) { + this.noRoute = handlers + this.rebuild404Handlers() +} + +/* + 没有找到对应的方法 +*/ +func (this *Engine) NoMethod(handlers ...HandlerFunc) { + this.noMethod = handlers + this.rebuild405Handlers() +} + +func (engine *Engine) Routes() (routes RoutesInfo) { + for _, tree := range engine.trees { + routes = iterate("", tree.method, routes, tree.root) + } + return routes +} + +/* + 设置信任代理 +*/ +func (this *Engine) SetTrustedProxies(trustedProxies []string) error { + this.trustedProxies = trustedProxies + return this.parseTrustedProxies() +} + +func (this *Engine) addRoute(method, path string, handlers HandlersChain) { + assert1(path[0] == '/', "path must begin with '/'") + assert1(method != "", "HTTP method can not be empty") + assert1(len(handlers) > 0, "there must be at least one handler") + if this.sys.Debug() { + nuHandlers := len(handlers) + handlerName := nameOfFunction(handlers.Last()) + this.sys.Debugf("%s:%s --> %s handlers:%d", method, path, handlerName, nuHandlers) + } + root := this.trees.get(method) + if root == nil { + root = new(node) + root.fullPath = "/" + this.trees = append(this.trees, methodTree{method: method, root: root}) + } + root.addRoute(path, handlers) + // Update maxParams + if paramsCount := countParams(path); paramsCount > this.maxParams { + this.maxParams = paramsCount + } + + if sectionsCount := countSections(path); sectionsCount > this.maxSections { + this.maxSections = sectionsCount + } +} + +func (this *Engine) handleHTTPRequest(c *Context) { + httpMethod := c.Request.Method + rPath := c.Request.URL.Path + unescape := false + if this.UseRawPath && len(c.Request.URL.RawPath) > 0 { + rPath = c.Request.URL.RawPath + unescape = this.UnescapePathValues + } + if this.RemoveExtraSlash { + rPath = cleanPath(rPath) + } + t := this.trees + for i, tl := 0, len(t); i < tl; i++ { + if t[i].method != httpMethod { + continue + } + root := t[i].root + // Find route in tree + value := root.getValue(rPath, c.params, c.skippedNodes, unescape) + if value.params != nil { + c.Params = *value.params + } + if value.handlers != nil { + c.handlers = value.handlers + c.fullPath = value.fullPath + c.Next() + c.writermem.WriteHeaderNow() + return + } + if httpMethod != http.MethodConnect && rPath != "/" { + if value.tsr && this.RedirectTrailingSlash { + this.redirectTrailingSlash(c) + return + } + if this.RedirectFixedPath && this.redirectFixedPath(c, root, this.RedirectFixedPath) { + return + } + } + break + } + + if this.HandleMethodNotAllowed { + for _, tree := range this.trees { + if tree.method == httpMethod { + continue + } + if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil { + c.handlers = this.allNoMethod + this.serveError(c, http.StatusMethodNotAllowed, default405Body) + return + } + } + } + c.handlers = this.allNoRoute + this.serveError(c, http.StatusNotFound, default404Body) +} + +func (this *Engine) rebuild404Handlers() { + this.allNoRoute = this.combineHandlers(this.noRoute) +} + +func (this *Engine) rebuild405Handlers() { + this.allNoMethod = this.combineHandlers(this.noMethod) +} + +func (this *Engine) IsUnsafeTrustedProxies() bool { + return this.isTrustedProxy(net.ParseIP("0.0.0.0")) || this.isTrustedProxy(net.ParseIP("::")) +} + +//validateHeader 将解析 X-Forwarded-For 标头并返回受信任的客户端 IP 地址 +func (this *Engine) validateHeader(header string) (clientIP string, valid bool) { + if header == "" { + return "", false + } + items := strings.Split(header, ",") + for i := len(items) - 1; i >= 0; i-- { + ipStr := strings.TrimSpace(items[i]) + ip := net.ParseIP(ipStr) + if ip == nil { + break + } + + // X-Forwarded-For is appended by proxy + // Check IPs in reverse order and stop when find untrusted proxy + if (i == 0) || (!this.isTrustedProxy(ip)) { + return ipStr, true + } + } + return "", false +} + +///目标Ip是否是可信 +func (this *Engine) isTrustedProxy(ip net.IP) bool { + if this.trustedCIDRs == nil { + return false + } + for _, cidr := range this.trustedCIDRs { + if cidr.Contains(ip) { + return true + } + } + return false +} + +func (this *Engine) serveError(c *Context, code int, defaultMessage []byte) { + c.writermem.status = code + c.Next() + if c.writermem.Written() { + return + } + if c.writermem.Status() == code { + c.writermem.Header()["Content-Type"] = mimePlain + _, err := c.Writer.Write(defaultMessage) + if err != nil { + this.sys.Errorf("[SYS-Gin] cannot write message to writer during serve error: %v", err) + } + return + } + c.writermem.WriteHeaderNow() +} + +func (this *Engine) redirectFixedPath(c *Context, root *node, trailingSlash bool) bool { + req := c.Request + rPath := req.URL.Path + + if fixedPath, ok := root.findCaseInsensitivePath(cleanPath(rPath), trailingSlash); ok { + req.URL.Path = convert.BytesToString(fixedPath) + this.redirectRequest(c) + return true + } + return false +} + +func (this *Engine) redirectTrailingSlash(c *Context) { + req := c.Request + p := req.URL.Path + if prefix := path.Clean(c.Request.Header.Get("X-Forwarded-Prefix")); prefix != "." { + p = prefix + "/" + req.URL.Path + } + req.URL.Path = p + "/" + if length := len(p); length > 1 && p[length-1] == '/' { + req.URL.Path = p[:length-1] + } + this.redirectRequest(c) +} + +func (this *Engine) redirectRequest(c *Context) { + req := c.Request + rPath := req.URL.Path + rURL := req.URL.String() + + code := http.StatusMovedPermanently // Permanent redirect, request with GET method + if req.Method != http.MethodGet { + code = http.StatusTemporaryRedirect + } + this.sys.Debugf("redirecting request %d: %s --> %s", code, rPath, rURL) + http.Redirect(c.Writer, req, rURL, code) + c.writermem.WriteHeaderNow() +} + +func (this *Engine) parseTrustedProxies() error { + trustedCIDRs, err := this.prepareTrustedCIDRs() + this.trustedCIDRs = trustedCIDRs + return err +} + +func (this *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) { + if this.trustedProxies == nil { + return nil, nil + } + + cidr := make([]*net.IPNet, 0, len(this.trustedProxies)) + for _, trustedProxy := range this.trustedProxies { + if !strings.Contains(trustedProxy, "/") { + ip := parseIP(trustedProxy) + if ip == nil { + return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy} + } + + switch len(ip) { + case net.IPv4len: + trustedProxy += "/32" + case net.IPv6len: + trustedProxy += "/128" + } + } + _, cidrNet, err := net.ParseCIDR(trustedProxy) + if err != nil { + return cidr, err + } + cidr = append(cidr, cidrNet) + } + return cidr, nil +} + +func (this *Engine) allocateContext() *Context { + v := make(Params, 0, this.maxParams) + skippedNodes := make([]skippedNode, 0, this.maxSections) + return &Context{Sys: this.sys, engine: this, params: &v, skippedNodes: &skippedNodes} +} + +//日志接口------------------------------------------------------------- +func (this *Engine) debugPrintLoadTemplate(tmpl *template.Template) { + if this.sys.Debug() { + var buf strings.Builder + for _, tmpl := range tmpl.Templates() { + buf.WriteString("\t- ") + buf.WriteString(tmpl.Name()) + buf.WriteString("\n") + } + format := "Loaded HTML Templates (%d): \n%s\n" + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + this.sys.Debugf(format, len(tmpl.Templates()), buf.String()) + } +} diff --git a/lego/sys/gin/engine/errors.go b/lego/sys/gin/engine/errors.go new file mode 100644 index 000000000..3c30a1578 --- /dev/null +++ b/lego/sys/gin/engine/errors.go @@ -0,0 +1,199 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package engine + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "reflect" + "strings" +) + +// H is a shortcut for map[string]interface{} +type H map[string]interface{} + +// MarshalXML allows type H to be used with xml.Marshal. +func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + start.Name = xml.Name{ + Space: "", + Local: "map", + } + if err := e.EncodeToken(start); err != nil { + return err + } + for key, value := range h { + elem := xml.StartElement{ + Name: xml.Name{Space: "", Local: key}, + Attr: []xml.Attr{}, + } + if err := e.EncodeElement(value, elem); err != nil { + return err + } + } + + return e.EncodeToken(xml.EndElement{Name: start.Name}) +} + +// ErrorType is an unsigned 64-bit error code as defined in the gin spec. +type ErrorType uint64 + +const ( + // ErrorTypeBind is used when Context.Bind() fails. + ErrorTypeBind ErrorType = 1 << 63 + // ErrorTypeRender is used when Context.Render() fails. + ErrorTypeRender ErrorType = 1 << 62 + // ErrorTypePrivate indicates a private error. + ErrorTypePrivate ErrorType = 1 << 0 + // ErrorTypePublic indicates a public error. + ErrorTypePublic ErrorType = 1 << 1 + // ErrorTypeAny indicates any other error. + ErrorTypeAny ErrorType = 1<<64 - 1 + // ErrorTypeNu indicates any other error. + ErrorTypeNu = 2 +) + +// Error represents a error's specification. +type Error struct { + Err error + Type ErrorType + Meta interface{} +} + +type errorMsgs []*Error + +var _ error = &Error{} + +// SetType sets the error's type. +func (msg *Error) SetType(flags ErrorType) *Error { + msg.Type = flags + return msg +} + +// SetMeta sets the error's meta data. +func (msg *Error) SetMeta(data interface{}) *Error { + msg.Meta = data + return msg +} + +// JSON creates a properly formatted JSON +func (msg *Error) JSON() interface{} { + jsonData := H{} + if msg.Meta != nil { + value := reflect.ValueOf(msg.Meta) + switch value.Kind() { + case reflect.Struct: + return msg.Meta + case reflect.Map: + for _, key := range value.MapKeys() { + jsonData[key.String()] = value.MapIndex(key).Interface() + } + default: + jsonData["meta"] = msg.Meta + } + } + if _, ok := jsonData["error"]; !ok { + jsonData["error"] = msg.Error() + } + return jsonData +} + +// MarshalJSON implements the json.Marshaller interface. +func (msg *Error) MarshalJSON() ([]byte, error) { + return json.Marshal(msg.JSON()) +} + +// Error implements the error interface. +func (msg Error) Error() string { + return msg.Err.Error() +} + +// IsType judges one error. +func (msg *Error) IsType(flags ErrorType) bool { + return (msg.Type & flags) > 0 +} + +// Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap() +func (msg *Error) Unwrap() error { + return msg.Err +} + +// ByType returns a readonly copy filtered the byte. +// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic. +func (a errorMsgs) ByType(typ ErrorType) errorMsgs { + if len(a) == 0 { + return nil + } + if typ == ErrorTypeAny { + return a + } + var result errorMsgs + for _, msg := range a { + if msg.IsType(typ) { + result = append(result, msg) + } + } + return result +} + +// Last returns the last error in the slice. It returns nil if the array is empty. +// Shortcut for errors[len(errors)-1]. +func (a errorMsgs) Last() *Error { + if length := len(a); length > 0 { + return a[length-1] + } + return nil +} + +// Errors returns an array with all the error messages. +// Example: +// c.Error(errors.New("first")) +// c.Error(errors.New("second")) +// c.Error(errors.New("third")) +// c.Errors.Errors() // == []string{"first", "second", "third"} +func (a errorMsgs) Errors() []string { + if len(a) == 0 { + return nil + } + errorStrings := make([]string, len(a)) + for i, err := range a { + errorStrings[i] = err.Error() + } + return errorStrings +} + +func (a errorMsgs) JSON() interface{} { + switch length := len(a); length { + case 0: + return nil + case 1: + return a.Last().JSON() + default: + jsonData := make([]interface{}, length) + for i, err := range a { + jsonData[i] = err.JSON() + } + return jsonData + } +} + +// MarshalJSON implements the json.Marshaller interface. +func (a errorMsgs) MarshalJSON() ([]byte, error) { + return json.Marshal(a.JSON()) +} + +func (a errorMsgs) String() string { + if len(a) == 0 { + return "" + } + var buffer strings.Builder + for i, msg := range a { + fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err) + if msg.Meta != nil { + fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) + } + } + return buffer.String() +} diff --git a/lego/sys/gin/engine/fs.go b/lego/sys/gin/engine/fs.go new file mode 100644 index 000000000..25f1ac911 --- /dev/null +++ b/lego/sys/gin/engine/fs.go @@ -0,0 +1,26 @@ +package engine + +import "net/http" + +type onlyFilesFS struct { + fs http.FileSystem +} +type neuteredReaddirFile struct { + http.File +} + +func Dir(root string, listDirectory bool) http.FileSystem { + fs := http.Dir(root) + if listDirectory { + return fs + } + return &onlyFilesFS{fs} +} + +func (fs onlyFilesFS) Open(name string) (http.File, error) { + f, err := fs.fs.Open(name) + if err != nil { + return nil, err + } + return neuteredReaddirFile{f}, nil +} diff --git a/lego/sys/gin/engine/response_writer.go b/lego/sys/gin/engine/response_writer.go new file mode 100644 index 000000000..1b420c165 --- /dev/null +++ b/lego/sys/gin/engine/response_writer.go @@ -0,0 +1,95 @@ +package engine + +import ( + "bufio" + "net" + "net/http" + + "go_dreamfactory/lego/sys/log" +) + +const ( + noWritten = -1 + defaultStatus = http.StatusOK +) + +type IResponseWriter interface { + http.ResponseWriter + http.Hijacker + http.Flusher + http.CloseNotifier + /* + 返回当前请求的 HTTP 响应状态码。 + */ + Status() int + /* + 大小返回已经写入响应 http 正文的字节数 + */ + Size() int + /* + WriteHeaderNow 强制写入 http 标头(状态码 + 标头)。 + */ + WriteHeaderNow() +} +type ResponseWriter struct { + http.ResponseWriter + log log.Ilogf + size int + status int +} + +func (this *ResponseWriter) reset(writer http.ResponseWriter) { + this.ResponseWriter = writer + this.size = noWritten + this.status = defaultStatus +} + +func (this *ResponseWriter) WriteHeader(code int) { + if code > 0 && this.status != code { + if this.Written() { + this.log.Warnf("Headers were already written. Wanted to override status code %d with %d", this.status, code) + } + this.status = code + } +} + +func (this *ResponseWriter) Status() int { + return this.status +} + +func (this *ResponseWriter) Size() int { + return this.size +} +func (this *ResponseWriter) Written() bool { + return this.size != noWritten +} + +// Hijack implements the http.Hijacker interface. +func (this *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if this.size < 0 { + this.size = 0 + } + return this.ResponseWriter.(http.Hijacker).Hijack() +} + +func (this *ResponseWriter) CloseNotify() <-chan bool { + return this.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +func (this *ResponseWriter) Flush() { + this.WriteHeaderNow() + this.ResponseWriter.(http.Flusher).Flush() +} + +func (this *ResponseWriter) WriteHeaderNow() { + if !this.Written() { + this.size = 0 + this.ResponseWriter.WriteHeader(this.status) + } +} +func (this *ResponseWriter) Pusher() (pusher http.Pusher) { + if pusher, ok := this.ResponseWriter.(http.Pusher); ok { + return pusher + } + return nil +} diff --git a/lego/sys/gin/engine/routergroup.go b/lego/sys/gin/engine/routergroup.go new file mode 100644 index 000000000..870e95fcc --- /dev/null +++ b/lego/sys/gin/engine/routergroup.go @@ -0,0 +1,169 @@ +package engine + +import ( + "net/http" + "path" + "regexp" + "strings" +) + +var ( + regEnLetter = regexp.MustCompile("^[A-Z]+$") + anyMethods = []string{ + http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, + http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect, + http.MethodTrace, + } +) + +type RouterGroup struct { + Handlers HandlersChain + basePath string + engine *Engine + root bool +} + +func (this *RouterGroup) BasePath() string { + return this.basePath +} + +func (this *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { + this.Handlers = append(this.Handlers, middleware...) + return this.returnObj() +} + +func (this *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) IRoutes { + return &RouterGroup{ + Handlers: this.combineHandlers(handlers), + basePath: this.calculateAbsolutePath(relativePath), + engine: this.engine, + } +} + +func (this *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes { + if matched := regEnLetter.MatchString(httpMethod); !matched { + panic("http method " + httpMethod + " is not valid") + } + return this.handle(httpMethod, relativePath, handlers) +} + +func (this *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodPost, relativePath, handlers) +} + +func (this *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodGet, relativePath, handlers) +} + +func (this *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodDelete, relativePath, handlers) +} + +func (this *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodPatch, relativePath, handlers) +} + +func (this *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodPut, relativePath, handlers) +} + +func (this *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodOptions, relativePath, handlers) +} + +func (this *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes { + return this.handle(http.MethodHead, relativePath, handlers) +} + +func (this *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes { + for _, method := range anyMethods { + this.handle(method, relativePath, handlers) + } + return this.returnObj() +} + +func (this *RouterGroup) StaticFile(relativePath, filepath string) IRoutes { + return this.staticFileHandler(relativePath, func(c *Context) { + c.File(filepath) + }) +} + +func (this *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes { + return this.staticFileHandler(relativePath, func(c *Context) { + c.FileFromFS(filepath, fs) + }) +} + +func (this *RouterGroup) Static(relativePath, root string) IRoutes { + return this.StaticFS(relativePath, Dir(root, false)) +} + +func (this *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes { + if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { + panic("URL parameters can not be used when serving a static folder") + } + handler := this.createStaticHandler(relativePath, fs) + urlPattern := path.Join(relativePath, "/*filepath") + + // Register GET and HEAD handlers + this.GET(urlPattern, handler) + this.HEAD(urlPattern, handler) + return this.returnObj() +} + +func (this *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { + absolutePath := this.calculateAbsolutePath(relativePath) + handlers = this.combineHandlers(handlers) + this.engine.addRoute(httpMethod, absolutePath, handlers) + return this.returnObj() +} + +func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc { + absolutePath := group.calculateAbsolutePath(relativePath) + fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) + return func(c *Context) { + if _, noListing := fs.(*onlyFilesFS); noListing { + c.Writer.WriteHeader(http.StatusNotFound) + } + file := c.Param("filepath") + f, err := fs.Open(file) + if err != nil { + c.Writer.WriteHeader(http.StatusNotFound) + c.handlers = group.engine.noRoute + c.index = -1 + return + } + f.Close() + + fileServer.ServeHTTP(c.Writer, c.Request) + } +} + +func (this *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { + finalSize := len(this.Handlers) + len(handlers) + assert1(finalSize < int(abortIndex), "too many handlers") + mergedHandlers := make(HandlersChain, finalSize) + copy(mergedHandlers, this.Handlers) + copy(mergedHandlers[len(this.Handlers):], handlers) + return mergedHandlers +} + +func (this *RouterGroup) calculateAbsolutePath(relativePath string) string { + return joinPaths(this.basePath, relativePath) +} + +func (this *RouterGroup) staticFileHandler(relativePath string, handler HandlerFunc) IRoutes { + if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") { + panic("URL parameters can not be used when serving a static file") + } + this.GET(relativePath, handler) + this.HEAD(relativePath, handler) + return this.returnObj() +} + +func (this *RouterGroup) returnObj() IRoutes { + if this.root { + return this.engine + } + return this +} diff --git a/lego/sys/gin/engine/tree.go b/lego/sys/gin/engine/tree.go new file mode 100644 index 000000000..de840910e --- /dev/null +++ b/lego/sys/gin/engine/tree.go @@ -0,0 +1,838 @@ +package engine + +import ( + "bytes" + "net/url" + "strings" + "unicode" + "unicode/utf8" + + "go_dreamfactory/lego/utils/convert" +) + +var ( + strColon = []byte(":") + strStar = []byte("*") + strSlash = []byte("/") +) + +type Param struct { + Key string + Value string +} + +type Params []Param + +func (ps Params) Get(name string) (string, bool) { + for _, entry := range ps { + if entry.Key == name { + return entry.Value, true + } + } + return "", false +} + +func (ps Params) ByName(name string) (va string) { + va, _ = ps.Get(name) + return +} + +type nodeType uint8 +type nodeValue struct { + handlers HandlersChain + params *Params + tsr bool + fullPath string +} +type skippedNode struct { + path string + node *node + paramsCount int16 +} + +const ( + root nodeType = iota + 1 + param + catchAll +) + +type node struct { + path string + indices string + wildChild bool + nType nodeType + priority uint32 + children []*node // child nodes, at most 1 :param style node at the end of the array + handlers HandlersChain + fullPath string +} + +func (n *node) addChild(child *node) { + if n.wildChild && len(n.children) > 0 { + wildcardChild := n.children[len(n.children)-1] + n.children = append(n.children[:len(n.children)-1], child, wildcardChild) + } else { + n.children = append(n.children, child) + } +} + +func (n *node) addRoute(path string, handlers HandlersChain) { + fullPath := path + n.priority++ + // Empty tree + if len(n.path) == 0 && len(n.children) == 0 { + n.insertChild(path, fullPath, handlers) + n.nType = root + return + } + parentFullPathIndex := 0 +walk: + for { + // Find the longest common prefix. + // This also implies that the common prefix contains no ':' or '*' + // since the existing key can't contain those chars. + i := longestCommonPrefix(path, n.path) + + // Split edge + if i < len(n.path) { + child := node{ + path: n.path[i:], + wildChild: n.wildChild, + indices: n.indices, + children: n.children, + handlers: n.handlers, + priority: n.priority - 1, + fullPath: n.fullPath, + } + + n.children = []*node{&child} + // []byte for proper unicode char conversion, see #65 + n.indices = convert.BytesToString([]byte{n.path[i]}) + n.path = path[:i] + n.handlers = nil + n.wildChild = false + n.fullPath = fullPath[:parentFullPathIndex+i] + } + + // Make new node a child of this node + if i < len(path) { + path = path[i:] + c := path[0] + + // '/' after param + if n.nType == param && c == '/' && len(n.children) == 1 { + parentFullPathIndex += len(n.path) + n = n.children[0] + n.priority++ + continue walk + } + + // Check if a child with the next path byte exists + for i, max := 0, len(n.indices); i < max; i++ { + if c == n.indices[i] { + parentFullPathIndex += len(n.path) + i = n.incrementChildPrio(i) + n = n.children[i] + continue walk + } + } + + // Otherwise insert it + if c != ':' && c != '*' && n.nType != catchAll { + // []byte for proper unicode char conversion, see #65 + n.indices += convert.BytesToString([]byte{c}) + child := &node{ + fullPath: fullPath, + } + n.addChild(child) + n.incrementChildPrio(len(n.indices) - 1) + n = child + } else if n.wildChild { + // inserting a wildcard node, need to check if it conflicts with the existing wildcard + n = n.children[len(n.children)-1] + n.priority++ + + // Check if the wildcard matches + if len(path) >= len(n.path) && n.path == path[:len(n.path)] && + // Adding a child to a catchAll is not possible + n.nType != catchAll && + // Check for longer wildcard, e.g. :name and :names + (len(n.path) >= len(path) || path[len(n.path)] == '/') { + continue walk + } + + // Wildcard conflict + pathSeg := path + if n.nType != catchAll { + pathSeg = strings.SplitN(pathSeg, "/", 2)[0] + } + prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path + panic("'" + pathSeg + + "' in new path '" + fullPath + + "' conflicts with existing wildcard '" + n.path + + "' in existing prefix '" + prefix + + "'") + } + + n.insertChild(path, fullPath, handlers) + return + } + + // Otherwise add handle to current node + if n.handlers != nil { + panic("handlers are already registered for path '" + fullPath + "'") + } + n.handlers = handlers + n.fullPath = fullPath + return + } +} + +func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) { + for { + // Find prefix until first wildcard + wildcard, i, valid := findWildcard(path) + if i < 0 { // No wildcard found + break + } + + // The wildcard name must only contain one ':' or '*' character + if !valid { + panic("only one wildcard per path segment is allowed, has: '" + + wildcard + "' in path '" + fullPath + "'") + } + + // check if the wildcard has a name + if len(wildcard) < 2 { + panic("wildcards must be named with a non-empty name in path '" + fullPath + "'") + } + + if wildcard[0] == ':' { // param + if i > 0 { + // Insert prefix before the current wildcard + n.path = path[:i] + path = path[i:] + } + + child := &node{ + nType: param, + path: wildcard, + fullPath: fullPath, + } + n.addChild(child) + n.wildChild = true + n = child + n.priority++ + + // if the path doesn't end with the wildcard, then there + // will be another subpath starting with '/' + if len(wildcard) < len(path) { + path = path[len(wildcard):] + + child := &node{ + priority: 1, + fullPath: fullPath, + } + n.addChild(child) + n = child + continue + } + + // Otherwise we're done. Insert the handle in the new leaf + n.handlers = handlers + return + } + + // catchAll + if i+len(wildcard) != len(path) { + panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'") + } + + if len(n.path) > 0 && n.path[len(n.path)-1] == '/' { + pathSeg := strings.SplitN(n.children[0].path, "/", 2)[0] + panic("catch-all wildcard '" + path + + "' in new path '" + fullPath + + "' conflicts with existing path segment '" + pathSeg + + "' in existing prefix '" + n.path + pathSeg + + "'") + } + + // currently fixed width 1 for '/' + i-- + if path[i] != '/' { + panic("no / before catch-all in path '" + fullPath + "'") + } + + n.path = path[:i] + + // First node: catchAll node with empty path + child := &node{ + wildChild: true, + nType: catchAll, + fullPath: fullPath, + } + + n.addChild(child) + n.indices = string('/') + n = child + n.priority++ + + // second node: node holding the variable + child = &node{ + path: path[i:], + nType: catchAll, + handlers: handlers, + priority: 1, + fullPath: fullPath, + } + n.children = []*node{child} + + return + } + + // If no wildcard was found, simply insert the path and handle + n.path = path + n.handlers = handlers + n.fullPath = fullPath +} +func (n *node) incrementChildPrio(pos int) int { + cs := n.children + cs[pos].priority++ + prio := cs[pos].priority + + // Adjust position (move to front) + newPos := pos + for ; newPos > 0 && cs[newPos-1].priority < prio; newPos-- { + // Swap node positions + cs[newPos-1], cs[newPos] = cs[newPos], cs[newPos-1] + } + + // Build new index char string + if newPos != pos { + n.indices = n.indices[:newPos] + // Unchanged prefix, might be empty + n.indices[pos:pos+1] + // The index char we move + n.indices[newPos:pos] + n.indices[pos+1:] // Rest without char at 'pos' + } + + return newPos +} + +func (n *node) getValue(path string, params *Params, skippedNodes *[]skippedNode, unescape bool) (value nodeValue) { + var globalParamsCount int16 + +walk: // Outer loop for walking the tree + for { + prefix := n.path + if len(path) > len(prefix) { + if path[:len(prefix)] == prefix { + path = path[len(prefix):] + + // Try all the non-wildcard children first by matching the indices + idxc := path[0] + for i, c := range []byte(n.indices) { + if c == idxc { + // strings.HasPrefix(n.children[len(n.children)-1].path, ":") == n.wildChild + if n.wildChild { + index := len(*skippedNodes) + *skippedNodes = (*skippedNodes)[:index+1] + (*skippedNodes)[index] = skippedNode{ + path: prefix + path, + node: &node{ + path: n.path, + wildChild: n.wildChild, + nType: n.nType, + priority: n.priority, + children: n.children, + handlers: n.handlers, + fullPath: n.fullPath, + }, + paramsCount: globalParamsCount, + } + } + + n = n.children[i] + continue walk + } + } + + if !n.wildChild { + // If the path at the end of the loop is not equal to '/' and the current node has no child nodes + // the current node needs to roll back to last vaild skippedNode + if path != "/" { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk + } + } + } + + // Nothing found. + // We can recommend to redirect to the same URL without a + // trailing slash if a leaf exists for that path. + value.tsr = path == "/" && n.handlers != nil + return + } + + // Handle wildcard child, which is always at the end of the array + n = n.children[len(n.children)-1] + globalParamsCount++ + + switch n.nType { + case param: + // fix truncate the parameter + // tree_test.go line: 204 + + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // Save param value + if params != nil && cap(*params) > 0 { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + val := path[:end] + if unescape { + if v, err := url.QueryUnescape(val); err == nil { + val = v + } + } + (*value.params)[i] = Param{ + Key: n.path[1:], + Value: val, + } + } + + // we need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + path = path[end:] + n = n.children[0] + continue walk + } + + // ... but we can't + value.tsr = len(path) == end+1 + return + } + + if value.handlers = n.handlers; value.handlers != nil { + value.fullPath = n.fullPath + return + } + if len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists for TSR recommendation + n = n.children[0] + value.tsr = (n.path == "/" && n.handlers != nil) || (n.path == "" && n.indices == "/") + } + return + + case catchAll: + // Save param value + if params != nil { + if value.params == nil { + value.params = params + } + // Expand slice within preallocated capacity + i := len(*value.params) + *value.params = (*value.params)[:i+1] + val := path + if unescape { + if v, err := url.QueryUnescape(path); err == nil { + val = v + } + } + (*value.params)[i] = Param{ + Key: n.path[2:], + Value: val, + } + } + + value.handlers = n.handlers + value.fullPath = n.fullPath + return + + default: + panic("invalid node type") + } + } + } + + if path == prefix { + // If the current path does not equal '/' and the node does not have a registered handle and the most recently matched node has a child node + // the current node needs to roll back to last vaild skippedNode + if n.handlers == nil && path != "/" { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk + } + } + // n = latestNode.children[len(latestNode.children)-1] + } + // We should have reached the node containing the handle. + // Check if this node has a handle registered. + if value.handlers = n.handlers; value.handlers != nil { + value.fullPath = n.fullPath + return + } + + // If there is no handle for this route, but this route has a + // wildcard child, there must be a handle for this path with an + // additional trailing slash + if path == "/" && n.wildChild && n.nType != root { + value.tsr = true + return + } + + // No handle found. Check if a handle for this path + a + // trailing slash exists for trailing slash recommendation + for i, c := range []byte(n.indices) { + if c == '/' { + n = n.children[i] + value.tsr = (len(n.path) == 1 && n.handlers != nil) || + (n.nType == catchAll && n.children[0].handlers != nil) + return + } + } + + return + } + + // Nothing found. We can recommend to redirect to the same URL with an + // extra trailing slash if a leaf exists for that path + value.tsr = path == "/" || + (len(prefix) == len(path)+1 && prefix[len(path)] == '/' && + path == prefix[:len(prefix)-1] && n.handlers != nil) + + // roll back to last valid skippedNode + if !value.tsr && path != "/" { + for l := len(*skippedNodes); l > 0; { + skippedNode := (*skippedNodes)[l-1] + *skippedNodes = (*skippedNodes)[:l-1] + if strings.HasSuffix(skippedNode.path, path) { + path = skippedNode.path + n = skippedNode.node + if value.params != nil { + *value.params = (*value.params)[:skippedNode.paramsCount] + } + globalParamsCount = skippedNode.paramsCount + continue walk + } + } + } + + return + } +} + +func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) ([]byte, bool) { + const stackBufSize = 128 + + // Use a static sized buffer on the stack in the common case. + // If the path is too long, allocate a buffer on the heap instead. + buf := make([]byte, 0, stackBufSize) + if length := len(path) + 1; length > stackBufSize { + buf = make([]byte, 0, length) + } + + ciPath := n.findCaseInsensitivePathRec( + path, + buf, // Preallocate enough memory for new path + [4]byte{}, // Empty rune buffer + fixTrailingSlash, + ) + + return ciPath, ciPath != nil +} + +//使用的递归不区分大小写查找函数 +func (n *node) findCaseInsensitivePathRec(path string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) []byte { + npLen := len(n.path) + +walk: // Outer loop for walking the tree + for len(path) >= npLen && (npLen == 0 || strings.EqualFold(path[1:npLen], n.path[1:])) { + // Add common prefix to result + oldPath := path + path = path[npLen:] + ciPath = append(ciPath, n.path...) + + if len(path) == 0 { + // We should have reached the node containing the handle. + // Check if this node has a handle registered. + if n.handlers != nil { + return ciPath + } + + // No handle found. + // Try to fix the path by adding a trailing slash + if fixTrailingSlash { + for i, c := range []byte(n.indices) { + if c == '/' { + n = n.children[i] + if (len(n.path) == 1 && n.handlers != nil) || + (n.nType == catchAll && n.children[0].handlers != nil) { + return append(ciPath, '/') + } + return nil + } + } + } + return nil + } + + // If this node does not have a wildcard (param or catchAll) child, + // we can just look up the next child node and continue to walk down + // the tree + if !n.wildChild { + // Skip rune bytes already processed + rb = shiftNRuneBytes(rb, npLen) + + if rb[0] != 0 { + // Old rune not finished + idxc := rb[0] + for i, c := range []byte(n.indices) { + if c == idxc { + // continue with child node + n = n.children[i] + npLen = len(n.path) + continue walk + } + } + } else { + // Process a new rune + var rv rune + + // Find rune start. + // Runes are up to 4 byte long, + // -4 would definitely be another rune. + var off int + for max := min(npLen, 3); off < max; off++ { + if i := npLen - off; utf8.RuneStart(oldPath[i]) { + // read rune from cached path + rv, _ = utf8.DecodeRuneInString(oldPath[i:]) + break + } + } + + // Calculate lowercase bytes of current rune + lo := unicode.ToLower(rv) + utf8.EncodeRune(rb[:], lo) + + // Skip already processed bytes + rb = shiftNRuneBytes(rb, off) + + idxc := rb[0] + for i, c := range []byte(n.indices) { + // Lowercase matches + if c == idxc { + // must use a recursive approach since both the + // uppercase byte and the lowercase byte might exist + // as an index + if out := n.children[i].findCaseInsensitivePathRec( + path, ciPath, rb, fixTrailingSlash, + ); out != nil { + return out + } + break + } + } + + // If we found no match, the same for the uppercase rune, + // if it differs + if up := unicode.ToUpper(rv); up != lo { + utf8.EncodeRune(rb[:], up) + rb = shiftNRuneBytes(rb, off) + + idxc := rb[0] + for i, c := range []byte(n.indices) { + // Uppercase matches + if c == idxc { + // Continue with child node + n = n.children[i] + npLen = len(n.path) + continue walk + } + } + } + } + + // Nothing found. We can recommend to redirect to the same URL + // without a trailing slash if a leaf exists for that path + if fixTrailingSlash && path == "/" && n.handlers != nil { + return ciPath + } + return nil + } + + n = n.children[0] + switch n.nType { + case param: + // Find param end (either '/' or path end) + end := 0 + for end < len(path) && path[end] != '/' { + end++ + } + + // Add param value to case insensitive path + ciPath = append(ciPath, path[:end]...) + + // We need to go deeper! + if end < len(path) { + if len(n.children) > 0 { + // Continue with child node + n = n.children[0] + npLen = len(n.path) + path = path[end:] + continue + } + + // ... but we can't + if fixTrailingSlash && len(path) == end+1 { + return ciPath + } + return nil + } + + if n.handlers != nil { + return ciPath + } + + if fixTrailingSlash && len(n.children) == 1 { + // No handle found. Check if a handle for this path + a + // trailing slash exists + n = n.children[0] + if n.path == "/" && n.handlers != nil { + return append(ciPath, '/') + } + } + + return nil + + case catchAll: + return append(ciPath, path...) + + default: + panic("invalid node type") + } + } + + // Nothing found. + // Try to fix the path by adding / removing a trailing slash + if fixTrailingSlash { + if path == "/" { + return ciPath + } + if len(path)+1 == npLen && n.path[len(path)] == '/' && + strings.EqualFold(path[1:], n.path[1:len(path)]) && n.handlers != nil { + return append(ciPath, n.path...) + } + } + return nil +} + +type methodTree struct { + method string + root *node +} + +type methodTrees []methodTree + +func (trees methodTrees) get(method string) *node { + for _, tree := range trees { + if tree.method == method { + return tree.root + } + } + return nil +} + +/* +将数组中的字节向左移动 n 个字节 +*/ +func shiftNRuneBytes(rb [4]byte, n int) [4]byte { + switch n { + case 0: + return rb + case 1: + return [4]byte{rb[1], rb[2], rb[3], 0} + case 2: + return [4]byte{rb[2], rb[3]} + case 3: + return [4]byte{rb[3]} + default: + return [4]byte{} + } +} + +func longestCommonPrefix(a, b string) int { + i := 0 + max := min(len(a), len(b)) + for i < max && a[i] == b[i] { + i++ + } + return i +} + +func findWildcard(path string) (wildcard string, i int, valid bool) { + // Find start + for start, c := range []byte(path) { + // A wildcard starts with ':' (param) or '*' (catch-all) + if c != ':' && c != '*' { + continue + } + + // Find end and check for invalid characters + valid = true + for end, c := range []byte(path[start+1:]) { + switch c { + case '/': + return path[start : start+1+end], start, valid + case ':', '*': + valid = false + } + } + return path[start:], start, valid + } + return "", -1, false +} + +func countParams(path string) uint16 { + var n uint16 + s := convert.StringToBytes(path) + n += uint16(bytes.Count(s, strColon)) + n += uint16(bytes.Count(s, strStar)) + return n +} + +func countSections(path string) uint16 { + s := convert.StringToBytes(path) + return uint16(bytes.Count(s, strSlash)) +} +func min(a, b int) int { + if a <= b { + return a + } + return b +} diff --git a/lego/sys/gin/engine/utils.go b/lego/sys/gin/engine/utils.go new file mode 100644 index 000000000..155bb9c5e --- /dev/null +++ b/lego/sys/gin/engine/utils.go @@ -0,0 +1,201 @@ +package engine + +import ( + "net" + "path" + "reflect" + "runtime" + "strings" + "unicode" +) + +/* + 解析一个 IP 的字符串表示并返回一个 net.IP + 最小字节表示,如果输入无效,则为零。 +*/ +func parseIP(ip string) net.IP { + parsedIP := net.ParseIP(ip) + if ipv4 := parsedIP.To4(); ipv4 != nil { + return ipv4 + } + return parsedIP +} + +func lastChar(str string) uint8 { + if str == "" { + panic("The length of the string can't be 0") + } + return str[len(str)-1] +} + +func assert1(guard bool, text string) { + if !guard { + panic(text) + } +} + +func nameOfFunction(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} + +func joinPaths(absolutePath, relativePath string) string { + if relativePath == "" { + return absolutePath + } + + finalPath := path.Join(absolutePath, relativePath) + if lastChar(relativePath) == '/' && lastChar(finalPath) != '/' { + return finalPath + "/" + } + return finalPath +} + +func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo { + path += root.path + if len(root.handlers) > 0 { + handlerFunc := root.handlers.Last() + routes = append(routes, RouteInfo{ + Method: method, + Path: path, + Handler: nameOfFunction(handlerFunc), + HandlerFunc: handlerFunc, + }) + } + for _, child := range root.children { + routes = iterate(path, method, routes, child) + } + return routes +} +func filterFlags(content string) string { + for i, char := range content { + if char == ' ' || char == ';' { + return content[:i] + } + } + return content +} + +func cleanPath(p string) string { + const stackBufSize = 128 + if p == "" { + return "/" + } + buf := make([]byte, 0, stackBufSize) + n := len(p) + r := 1 + w := 1 + if p[0] != '/' { + r = 0 + if n+1 > stackBufSize { + buf = make([]byte, n+1) + } else { + buf = buf[:n+1] + } + buf[0] = '/' + } + + trailing := n > 1 && p[n-1] == '/' + + for r < n { + switch { + case p[r] == '/': + r++ + case p[r] == '.' && r+1 == n: + trailing = true + r++ + case p[r] == '.' && p[r+1] == '/': + r += 2 + + case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): + r += 3 + if w > 1 { + w-- + if len(buf) == 0 { + for w > 1 && p[w] != '/' { + w-- + } + } else { + for w > 1 && buf[w] != '/' { + w-- + } + } + } + default: + if w > 1 { + bufApp(&buf, p, w, '/') + w++ + } + for r < n && p[r] != '/' { + bufApp(&buf, p, w, p[r]) + w++ + r++ + } + } + } + if trailing && w > 1 { + bufApp(&buf, p, w, '/') + w++ + } + if len(buf) == 0 { + return p[:w] + } + return string(buf[:w]) +} + +func bufApp(buf *[]byte, s string, w int, c byte) { + b := *buf + if len(b) == 0 { + // No modification of the original string so far. + // If the next character is the same as in the original string, we do + // not yet have to allocate a buffer. + if s[w] == c { + return + } + + // Otherwise use either the stack buffer, if it is large enough, or + // allocate a new buffer on the heap, and copy all previous characters. + length := len(s) + if length > cap(b) { + *buf = make([]byte, length) + } else { + *buf = (*buf)[:length] + } + b = *buf + + copy(b, s[:w]) + } + b[w] = c +} + +func parseAccept(acceptHeader string) []string { + parts := strings.Split(acceptHeader, ",") + out := make([]string, 0, len(parts)) + for _, part := range parts { + if i := strings.IndexByte(part, ';'); i > 0 { + part = part[:i] + } + if part = strings.TrimSpace(part); part != "" { + out = append(out, part) + } + } + return out +} + +func chooseData(custom, wildcard interface{}) interface{} { + if custom != nil { + return custom + } + if wildcard != nil { + return wildcard + } + panic("negotiation config is invalid") +} + +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > unicode.MaxASCII { + return false + } + } + return true +} diff --git a/lego/sys/gin/gin.go b/lego/sys/gin/gin.go new file mode 100644 index 000000000..30c21eea3 --- /dev/null +++ b/lego/sys/gin/gin.go @@ -0,0 +1,192 @@ +package gin + +import ( + "context" + "errors" + "fmt" + "net" + "net/http" + + "go_dreamfactory/lego/sys/gin/engine" + "go_dreamfactory/lego/sys/gin/middleware/logger" + "go_dreamfactory/lego/sys/gin/middleware/recovery" +) + +func newSys(options Options) (sys *Gin, err error) { + sys = &Gin{ + options: options, + } + sys.engine = engine.NewEngine(sys) + ///添加基础中间件 + sys.engine.Use(logger.Logger([]string{}), recovery.Recovery()) + if options.CertFile != "" && options.KeyFile != "" { + sys.RunTLS(options.ListenPort, options.CertFile, options.KeyFile) + } else { + sys.Run(options.ListenPort) + } + return +} + +type Gin struct { + options Options + server *http.Server + engine *engine.Engine +} + +func (this *Gin) Run(listenPort int) (err error) { + defer func() { + if err != nil { + this.Errorf("Run err:%v", err) + } + }() + // if this.engine.IsUnsafeTrustedProxies() { + // this.Warnf("You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + // "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + + // } + this.Debugf("Listening and serving HTTP on:%d", listenPort) + this.server = &http.Server{ + Addr: fmt.Sprintf(":%d", listenPort), + Handler: this.engine.Handler(), + } + go func() { + if err := this.server.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { + this.Errorf("Run err:%v", err) + } + }() + // err = http.ListenAndServe(fmt.Sprintf(":%d", this.options.ListenPort), this.Handler()) + return +} + +func (this *Gin) RunTLS(listenPort int, certFile, keyFile string) (err error) { + this.Debugf("Listening and serving HTTPS on :%d", listenPort) + defer func() { + if err != nil { + this.Errorf("RunTLS err:%v", err) + } + }() + + // if this.engine.IsUnsafeTrustedProxies() { + // this.Warnf("You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + // "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + // } + this.server = &http.Server{ + Addr: fmt.Sprintf(":%d", listenPort), + Handler: this.engine.Handler(), + } + go func() { + if err := this.server.ListenAndServeTLS(certFile, keyFile); err != nil && errors.Is(err, http.ErrServerClosed) { + this.Errorf("listen err:%s", err) + } + }() + // err = http.ListenAndServeTLS(addr, certFile, keyFile, this.Handler()) + return +} + +func (this *Gin) RunListener(listener net.Listener) (err error) { + this.Debugf("Listening and serving HTTP on listener what's bind with address@%s", listener.Addr()) + defer func() { + if err != nil { + this.Errorf("Run err:%v", err) + } + + }() + // if this.engine.IsUnsafeTrustedProxies() { + // this.Warnf("You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" + + // "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.") + + // } + err = http.Serve(listener, this.engine.Handler()) + return +} + +func (this *Gin) Close() (err error) { + if err = this.server.Shutdown(context.Background()); err != nil { + this.Errorf("Close err:%v", err) + } + this.server.Close() + return +} +func (this *Gin) Group(relativePath string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.Group(relativePath, handlers...) +} +func (this *Gin) Use(handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.Use(handlers...) +} +func (this *Gin) Handle(httpMethod string, relativePath string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.Handle(httpMethod, relativePath, handlers...) +} +func (this *Gin) Any(relativePath string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.Any(relativePath, handlers...) +} +func (this *Gin) GET(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.GET(httpMethod, handlers...) +} +func (this *Gin) POST(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.POST(httpMethod, handlers...) +} +func (this *Gin) DELETE(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.DELETE(httpMethod, handlers...) +} +func (this *Gin) PATCH(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return defsys.PATCH(httpMethod, handlers...) +} +func (this *Gin) PUT(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.PUT(httpMethod, handlers...) +} +func (this *Gin) OPTIONS(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.OPTIONS(httpMethod, handlers...) +} +func (this *Gin) HEAD(httpMethod string, handlers ...engine.HandlerFunc) engine.IRoutes { + return this.engine.HEAD(httpMethod, handlers...) +} +func (this *Gin) StaticFile(relativePath string, filepath string) engine.IRoutes { + return this.engine.StaticFile(relativePath, filepath) +} +func (this *Gin) StaticFileFS(relativePath string, filepath string, fs http.FileSystem) engine.IRoutes { + return this.engine.StaticFileFS(relativePath, filepath, fs) +} + +func (this *Gin) Static(relativePath string, root string) engine.IRoutes { + return this.engine.Static(relativePath, root) +} + +func (this *Gin) StaticFS(relativePath string, fs http.FileSystem) engine.IRoutes { + return this.engine.StaticFS(relativePath, fs) +} + +///日志*********************************************************************** +func (this *Gin) Debug() bool { + return this.options.Debug +} + +func (this *Gin) Debugf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Debugf("[SYS Gin] "+format, a...) + } +} +func (this *Gin) Infof(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Infof("[SYS Gin] "+format, a...) + } +} +func (this *Gin) Warnf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Warnf("[SYS Gin] "+format, a...) + } +} +func (this *Gin) Errorf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Errorf("[SYS Gin] "+format, a...) + } +} +func (this *Gin) Panicf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Panicf("[SYS Gin] "+format, a...) + } +} +func (this *Gin) Fatalf(format string, a ...interface{}) { + if this.options.Debug { + this.options.Log.Fatalf("[SYS Gin] "+format, a...) + } +} diff --git a/lego/sys/gin/middleware/cross/cross.go b/lego/sys/gin/middleware/cross/cross.go new file mode 100644 index 000000000..551e173e9 --- /dev/null +++ b/lego/sys/gin/middleware/cross/cross.go @@ -0,0 +1,28 @@ +/* +解决跨域 中间件 +*/ +package cross + +import ( + "net/http" + + "go_dreamfactory/lego/sys/gin/engine" +) + +func handlerCors() engine.HandlerFunc { + return func(c *engine.Context) { + method := c.Request.Method + origin := c.Request.Header.Get("Origin") //请求头部 + if origin != "" { + c.Header("Access-Control-Allow-Origin", origin) + c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") + c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type,X-Token, Accept, Authorization") + c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") + c.Header("Access-Control-Allow-Credentials", "true") + } //允许类型校验 + if method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + } + c.Next() + } +} diff --git a/lego/sys/gin/middleware/jwt/jwt.go b/lego/sys/gin/middleware/jwt/jwt.go new file mode 100644 index 000000000..7d606dcf3 --- /dev/null +++ b/lego/sys/gin/middleware/jwt/jwt.go @@ -0,0 +1,83 @@ +package jwt + +import ( + "net/http" + "strings" + "time" + + "go_dreamfactory/lego/sys/gin/engine" + + "github.com/golang-jwt/jwt" +) + +func NewJWT(key string) *JWT { + return &JWT{jwtkey: key} +} + +type JWT struct { + jwtkey string +} + +// CreateToken 生成token +func (this *JWT) CreateToken(Id string) (string, error) { + expireTime := time.Now().Add(2 * time.Hour) //过期时间 + nowTime := time.Now() //当前时间 + claims := jwt.StandardClaims{ + Id: Id, //用户Id + ExpiresAt: expireTime.Unix(), //过期时间戳 + IssuedAt: nowTime.Unix(), //当前时间戳 + Issuer: "blogLeo", //颁发者签名 + Subject: "userToken", //签名主题 + + } + tokenStruct := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return tokenStruct.SignedString([]byte(this.jwtkey)) +} + +// CheckToken 验证token +func (this *JWT) CheckToken(token string) (*jwt.StandardClaims, bool) { + tokenObj, _ := jwt.ParseWithClaims(token, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return this.jwtkey, nil + }) + if key, _ := tokenObj.Claims.(*jwt.StandardClaims); tokenObj.Valid { + return key, true + } else { + return nil, false + } +} + +// JwtMiddleware jwt中间件 +func (this *JWT) JwtMiddleware() engine.HandlerFunc { + return func(c *engine.Context) { + //从请求头中获取token + tokenStr := c.Request.Header.Get("Authorization") + //用户不存在 + if tokenStr == "" { + c.JSON(http.StatusOK, engine.H{"code": 0, "msg": "用户不存在"}) + c.Abort() //阻止执行 + return + } + //token格式错误 + tokenSlice := strings.SplitN(tokenStr, " ", 2) + if len(tokenSlice) != 2 && tokenSlice[0] != "Bearer" { + c.JSON(http.StatusOK, engine.H{"code": 0, "msg": "token格式错误"}) + c.Abort() //阻止执行 + return + } + //验证token + tokenStruck, ok := this.CheckToken(tokenSlice[1]) + if !ok { + c.JSON(http.StatusOK, engine.H{"code": 0, "msg": "token不正确"}) + c.Abort() //阻止执行 + return + } + //token超时 + if time.Now().Unix() > tokenStruck.ExpiresAt { + c.JSON(http.StatusOK, engine.H{"code": 0, "msg": "token过期"}) + c.Abort() //阻止执行 + return + } + c.Set("UserId", tokenStruck.Id) + c.Next() + } +} diff --git a/lego/sys/gin/middleware/logger/logger.go b/lego/sys/gin/middleware/logger/logger.go new file mode 100644 index 000000000..71b293a51 --- /dev/null +++ b/lego/sys/gin/middleware/logger/logger.go @@ -0,0 +1,85 @@ +package logger + +import ( + "fmt" + "net/http" + "time" + + "go_dreamfactory/lego/sys/gin/engine" +) + +type LogFormatterParams struct { + Request *http.Request + // TimeStamp shows the time after the server returns a response. + TimeStamp time.Time + // StatusCode is HTTP response code. + StatusCode int + // Latency is how much time the server cost to process a certain request. + Latency time.Duration + // ClientIP equals Context's ClientIP method. + ClientIP string + // Method is the HTTP method given to the request. + Method string + // Path is a path the client requests. + Path string + // ErrorMessage is set if error has occurred in processing the request. + ErrorMessage string + // isTerm shows whether gin's output descriptor refers to a terminal. + isTerm bool + // BodySize is the size of the Response Body + BodySize int + // Keys are the keys set on the request's context. + Keys map[string]interface{} +} + +func Logger(SkipPaths []string) engine.HandlerFunc { + var skip map[string]struct{} + if length := len(SkipPaths); length > 0 { + skip = make(map[string]struct{}, length) + + for _, path := range SkipPaths { + skip[path] = struct{}{} + } + } + return func(c *engine.Context) { + // Start timer + start := time.Now() + path := c.Request.URL.Path + raw := c.Request.URL.RawQuery + + // Process request + c.Next() + + // Log only when path is not being skipped + if _, ok := skip[path]; !ok { + param := LogFormatterParams{ + Request: c.Request, + Keys: c.Keys, + } + // Stop timer + param.TimeStamp = time.Now() + param.Latency = param.TimeStamp.Sub(start) + + param.ClientIP = c.ClientIP() + param.Method = c.Request.Method + param.StatusCode = c.Writer.Status() + param.ErrorMessage = c.Errors.ByType(engine.ErrorTypePrivate).String() + + param.BodySize = c.Writer.Size() + + if raw != "" { + path = path + "?" + raw + } + param.Path = path + c.Sys.Debugf(fmt.Sprintf("[FORMATTER TEST] %v | %3d | %13v | %15s | %-7s %s\n%s", + param.TimeStamp.Format("2006/01/02 - 15:04:05"), + param.StatusCode, + param.Latency, + param.ClientIP, + param.Method, + param.Path, + param.ErrorMessage, + )) + } + } +} diff --git a/lego/sys/gin/middleware/recovery/recovery.go b/lego/sys/gin/middleware/recovery/recovery.go new file mode 100644 index 000000000..9b2695a8e --- /dev/null +++ b/lego/sys/gin/middleware/recovery/recovery.go @@ -0,0 +1,162 @@ +package recovery + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "os" + "runtime" + "strings" + "time" + + "go_dreamfactory/lego/sys/gin/engine" +) + +const ( + green = "\033[97;42m" + white = "\033[90;47m" + yellow = "\033[90;43m" + red = "\033[97;41m" + blue = "\033[97;44m" + magenta = "\033[97;45m" + cyan = "\033[97;46m" + reset = "\033[0m" +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +// RecoveryFunc defines the function passable to CustomRecovery. +type RecoveryFunc func(c *engine.Context, err interface{}) + +func Recovery() engine.HandlerFunc { + return CustomRecoveryWithWriter(defaultHandleRecovery) +} + +func defaultHandleRecovery(c *engine.Context, err interface{}) { + c.AbortWithStatus(http.StatusInternalServerError) +} + +// CustomRecoveryWithWriter returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it. +func CustomRecoveryWithWriter(handle RecoveryFunc) engine.HandlerFunc { + return func(c *engine.Context) { + defer func() { + if err := recover(); err != nil { + // Check for a broken connection, as it is not really a + // condition that warrants a panic stack trace. + var brokenPipe bool + if ne, ok := err.(*net.OpError); ok { + var se *os.SyscallError + if errors.As(ne, &se) { + if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { + brokenPipe = true + } + } + } + + stack := stack(3) + httpRequest, _ := httputil.DumpRequest(c.Request, false) + headers := strings.Split(string(httpRequest), "\r\n") + for idx, header := range headers { + current := strings.Split(header, ":") + if current[0] == "Authorization" { + headers[idx] = current[0] + ": *" + } + } + headersToStr := strings.Join(headers, "\r\n") + if brokenPipe { + c.Sys.Errorf("%s\n%s%s", err, headersToStr, reset) + } else if c.Sys.Debug() { + c.Sys.Errorf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", + timeFormat(time.Now()), headersToStr, err, stack, reset) + } else { + c.Sys.Errorf("[Recovery] %s panic recovered:\n%s\n%s%s", + timeFormat(time.Now()), err, stack, reset) + } + + if brokenPipe { + // If the connection is dead, we can't write a status to it. + c.Error(err.(error)) // nolint: errcheck + c.Abort() + } else { + handle(c, err) + } + } + }() + c.Next() + } +} + +// stack returns a nicely formatted stack frame, skipping skip frames. +func stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := ioutil.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contain dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 { + name = name[lastSlash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.Replace(name, centerDot, dot, -1) + return name +} + +// timeFormat returns a customized time string for logger. +func timeFormat(t time.Time) string { + return t.Format("2006/01/02 - 15:04:05") +} diff --git a/lego/sys/gin/options.go b/lego/sys/gin/options.go new file mode 100644 index 000000000..4111841ce --- /dev/null +++ b/lego/sys/gin/options.go @@ -0,0 +1,76 @@ +package gin + +import ( + "go_dreamfactory/lego/sys/log" + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { + ListenPort int //监听端口 + CertFile string //tls文件 + KeyFile string //tls文件 + Debug bool //日志是否开启 + Log log.ILog +} + +func SetListenPort(v int) Option { + return func(o *Options) { + o.ListenPort = v + } +} + +func SetCertFile(v string) Option { + return func(o *Options) { + o.CertFile = v + } +} + +func SetKeyFile(v string) Option { + return func(o *Options) { + o.KeyFile = v + } +} + +func SetDebug(v bool) Option { + return func(o *Options) { + o.Debug = v + } +} + +func SetLog(v log.ILog) Option { + return func(o *Options) { + o.Log = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + ListenPort: 8080, + CertFile: "", + KeyFile: "", + Debug: true, + Log: log.Clone(log.SetLoglayer(2)), + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + ListenPort: 8080, + CertFile: "", + KeyFile: "", + Debug: true, + Log: log.Clone(log.SetLoglayer(2)), + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/gin/render/data.go b/lego/sys/gin/render/data.go new file mode 100644 index 000000000..5bbcc36f0 --- /dev/null +++ b/lego/sys/gin/render/data.go @@ -0,0 +1,21 @@ +package render + +import "net/http" + +// Data contains ContentType and bytes data. +type Data struct { + ContentType string + Data []byte +} + +// Render (Data) writes data with custom ContentType. +func (r Data) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + _, err = w.Write(r.Data) + return +} + +// WriteContentType (Data) writes custom ContentType. +func (r Data) WriteContentType(w http.ResponseWriter) { + writeContentType(w, []string{r.ContentType}) +} diff --git a/lego/sys/gin/render/html.go b/lego/sys/gin/render/html.go new file mode 100644 index 000000000..899fd96ad --- /dev/null +++ b/lego/sys/gin/render/html.go @@ -0,0 +1,84 @@ +package render + +import ( + "html/template" + "net/http" +) + +var htmlContentType = []string{"text/html; charset=utf-8"} + +/* +Delims 表示一组用于 HTML 模板渲染的左右分隔符。 +*/ +type Delims struct { + // Left delimiter, defaults to {{. + Left string + // Right delimiter, defaults to }}. + Right string +} + +/* +包含模板引用及其分隔符 +*/ +type HTMLRender interface { + Instance(string, interface{}) Render +} +type HTMLProduction struct { + Template *template.Template + Delims Delims +} + +func (r HTMLProduction) Instance(name string, data interface{}) Render { + return HTML{ + Template: r.Template, + Name: name, + Data: data, + } +} + +type HTMLDebug struct { + Files []string + Glob string + Delims Delims + FuncMap template.FuncMap +} + +// 返回一个实现Render接口的HTML实例。 +func (r HTMLDebug) Instance(name string, data interface{}) Render { + return HTML{ + Template: r.loadTemplate(), + Name: name, + Data: data, + } +} +func (r HTMLDebug) loadTemplate() *template.Template { + if r.FuncMap == nil { + r.FuncMap = template.FuncMap{} + } + if len(r.Files) > 0 { + return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...)) + } + if r.Glob != "" { + return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob)) + } + panic("the HTML debug render was created without files or glob pattern") +} + +type HTML struct { + Template *template.Template + Name string + Data interface{} +} + +func (r HTML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + if r.Name == "" { + return r.Template.Execute(w, r.Data) + } + return r.Template.ExecuteTemplate(w, r.Name, r.Data) +} + +func (r HTML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, htmlContentType) +} diff --git a/lego/sys/gin/render/json.go b/lego/sys/gin/render/json.go new file mode 100644 index 000000000..6db6fae97 --- /dev/null +++ b/lego/sys/gin/render/json.go @@ -0,0 +1,189 @@ +package render + +import ( + "bytes" + "encoding/json" + "fmt" + "html/template" + "net/http" + + "go_dreamfactory/lego/utils/convert" +) + +// JSON contains the given interface object. +type JSON struct { + Data interface{} +} + +// IndentedJSON contains the given interface object. +type IndentedJSON struct { + Data interface{} +} + +// SecureJSON contains the given interface object and its prefix. +type SecureJSON struct { + Prefix string + Data interface{} +} + +// JsonpJSON contains the given interface object its callback. +type JsonpJSON struct { + Callback string + Data interface{} +} + +// AsciiJSON contains the given interface object. +type AsciiJSON struct { + Data interface{} +} + +// PureJSON contains the given interface object. +type PureJSON struct { + Data interface{} +} + +var ( + jsonContentType = []string{"application/json; charset=utf-8"} + jsonpContentType = []string{"application/javascript; charset=utf-8"} + jsonASCIIContentType = []string{"application/json"} +) + +// Render (JSON) writes data with custom ContentType. +func (r JSON) Render(w http.ResponseWriter) (err error) { + if err = WriteJSON(w, r.Data); err != nil { + panic(err) + } + return +} + +// WriteContentType (JSON) writes JSON ContentType. +func (r JSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} + +// WriteJSON marshals the given interface object and writes it with custom ContentType. +func WriteJSON(w http.ResponseWriter, obj interface{}) error { + writeContentType(w, jsonContentType) + jsonBytes, err := json.Marshal(obj) + if err != nil { + return err + } + _, err = w.Write(jsonBytes) + return err +} + +// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType. +func (r IndentedJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + jsonBytes, err := json.MarshalIndent(r.Data, "", " ") + if err != nil { + return err + } + _, err = w.Write(jsonBytes) + return err +} + +// WriteContentType (IndentedJSON) writes JSON ContentType. +func (r IndentedJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} + +// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType. +func (r SecureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + jsonBytes, err := json.Marshal(r.Data) + if err != nil { + return err + } + // if the jsonBytes is array values + if bytes.HasPrefix(jsonBytes, convert.StringToBytes("[")) && bytes.HasSuffix(jsonBytes, + convert.StringToBytes("]")) { + if _, err = w.Write(convert.StringToBytes(r.Prefix)); err != nil { + return err + } + } + _, err = w.Write(jsonBytes) + return err +} + +// WriteContentType (SecureJSON) writes JSON ContentType. +func (r SecureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} + +// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType. +func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + ret, err := json.Marshal(r.Data) + if err != nil { + return err + } + + if r.Callback == "" { + _, err = w.Write(ret) + return err + } + + callback := template.JSEscapeString(r.Callback) + if _, err = w.Write(convert.StringToBytes(callback)); err != nil { + return err + } + + if _, err = w.Write(convert.StringToBytes("(")); err != nil { + return err + } + + if _, err = w.Write(ret); err != nil { + return err + } + + if _, err = w.Write(convert.StringToBytes(");")); err != nil { + return err + } + + return nil +} + +// WriteContentType (JsonpJSON) writes Javascript ContentType. +func (r JsonpJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonpContentType) +} + +// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType. +func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + ret, err := json.Marshal(r.Data) + if err != nil { + return err + } + + var buffer bytes.Buffer + for _, r := range convert.BytesToString(ret) { + cvt := string(r) + if r >= 128 { + cvt = fmt.Sprintf("\\u%04x", int64(r)) + } + buffer.WriteString(cvt) + } + + _, err = w.Write(buffer.Bytes()) + return err +} + +// WriteContentType (AsciiJSON) writes JSON ContentType. +func (r AsciiJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonASCIIContentType) +} + +// Render (PureJSON) writes custom ContentType and encodes the given interface object. +func (r PureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + encoder := json.NewEncoder(w) + encoder.SetEscapeHTML(false) + return encoder.Encode(r.Data) +} + +// WriteContentType (PureJSON) writes custom ContentType. +func (r PureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} diff --git a/lego/sys/gin/render/protobuf.go b/lego/sys/gin/render/protobuf.go new file mode 100644 index 000000000..a4993721f --- /dev/null +++ b/lego/sys/gin/render/protobuf.go @@ -0,0 +1,32 @@ +package render + +import ( + "net/http" + + "google.golang.org/protobuf/proto" +) + +// ProtoBuf contains the given interface object. +type ProtoBuf struct { + Data interface{} +} + +var protobufContentType = []string{"application/x-protobuf"} + +// Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType. +func (r ProtoBuf) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + bytes, err := proto.Marshal(r.Data.(proto.Message)) + if err != nil { + return err + } + + _, err = w.Write(bytes) + return err +} + +// WriteContentType (ProtoBuf) writes ProtoBuf ContentType. +func (r ProtoBuf) WriteContentType(w http.ResponseWriter) { + writeContentType(w, protobufContentType) +} diff --git a/lego/sys/gin/render/reader.go b/lego/sys/gin/render/reader.go new file mode 100644 index 000000000..e08504e69 --- /dev/null +++ b/lego/sys/gin/render/reader.go @@ -0,0 +1,44 @@ +package render + +import ( + "io" + "net/http" + "strconv" +) + +// Reader contains the IO reader and its length, and custom ContentType and other headers. +type Reader struct { + ContentType string + ContentLength int64 + Reader io.Reader + Headers map[string]string +} + +// Render (Reader) writes data with custom ContentType and headers. +func (r Reader) Render(w http.ResponseWriter) (err error) { + r.WriteContentType(w) + if r.ContentLength >= 0 { + if r.Headers == nil { + r.Headers = map[string]string{} + } + r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10) + } + r.writeHeaders(w, r.Headers) + _, err = io.Copy(w, r.Reader) + return +} + +// WriteContentType (Reader) writes custom ContentType. +func (r Reader) WriteContentType(w http.ResponseWriter) { + writeContentType(w, []string{r.ContentType}) +} + +// writeHeaders writes custom Header. +func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) { + header := w.Header() + for k, v := range headers { + if header.Get(k) == "" { + header.Set(k, v) + } + } +} diff --git a/lego/sys/gin/render/redirect.go b/lego/sys/gin/render/redirect.go new file mode 100644 index 000000000..fed05f8f9 --- /dev/null +++ b/lego/sys/gin/render/redirect.go @@ -0,0 +1,25 @@ +package render + +import ( + "fmt" + "net/http" +) + +// Redirect contains the http request reference and redirects status code and location. +type Redirect struct { + Code int + Request *http.Request + Location string +} + +// Render (Redirect) redirects the http request to new location and writes redirect response. +func (r Redirect) Render(w http.ResponseWriter) error { + if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated { + panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code)) + } + http.Redirect(w, r.Request, r.Location, r.Code) + return nil +} + +// WriteContentType (Redirect) don't write any ContentType. +func (r Redirect) WriteContentType(http.ResponseWriter) {} diff --git a/lego/sys/gin/render/render.go b/lego/sys/gin/render/render.go new file mode 100644 index 000000000..611692f7a --- /dev/null +++ b/lego/sys/gin/render/render.go @@ -0,0 +1,19 @@ +package render + +import ( + "net/http" +) + +type Render interface { + // Render 使用自定义 ContentType 写入数据。 + Render(http.ResponseWriter) error + // WriteContentType 写入自定义 ContentType。 + WriteContentType(w http.ResponseWriter) +} + +func writeContentType(w http.ResponseWriter, value []string) { + header := w.Header() + if val := header["Content-Type"]; len(val) == 0 { + header["Content-Type"] = value + } +} diff --git a/lego/sys/gin/render/text.go b/lego/sys/gin/render/text.go new file mode 100644 index 000000000..a516da5a6 --- /dev/null +++ b/lego/sys/gin/render/text.go @@ -0,0 +1,37 @@ +package render + +import ( + "fmt" + "net/http" + + "go_dreamfactory/lego/utils/convert" +) + +// String contains the given interface object slice and its format. +type String struct { + Format string + Data []interface{} +} + +var plainContentType = []string{"text/plain; charset=utf-8"} + +// Render (String) writes data with custom ContentType. +func (r String) Render(w http.ResponseWriter) error { + return WriteString(w, r.Format, r.Data) +} + +// WriteContentType (String) writes Plain ContentType. +func (r String) WriteContentType(w http.ResponseWriter) { + writeContentType(w, plainContentType) +} + +// WriteString writes data according to its format and write custom ContentType. +func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) { + writeContentType(w, plainContentType) + if len(data) > 0 { + _, err = fmt.Fprintf(w, format, data...) + return + } + _, err = w.Write(convert.StringToBytes(format)) + return +} diff --git a/lego/sys/gin/render/xml.go b/lego/sys/gin/render/xml.go new file mode 100644 index 000000000..2f79684d5 --- /dev/null +++ b/lego/sys/gin/render/xml.go @@ -0,0 +1,24 @@ +package render + +import ( + "encoding/xml" + "net/http" +) + +// XML contains the given interface object. +type XML struct { + Data interface{} +} + +var xmlContentType = []string{"application/xml; charset=utf-8"} + +// Render (XML) encodes the given interface object and writes data with custom ContentType. +func (r XML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + return xml.NewEncoder(w).Encode(r.Data) +} + +// WriteContentType (XML) writes XML ContentType for response. +func (r XML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, xmlContentType) +} diff --git a/lego/sys/gin/render/yaml.go b/lego/sys/gin/render/yaml.go new file mode 100644 index 000000000..757744352 --- /dev/null +++ b/lego/sys/gin/render/yaml.go @@ -0,0 +1,32 @@ +package render + +import ( + "net/http" + + "gopkg.in/yaml.v2" +) + +// YAML contains the given interface object. +type YAML struct { + Data interface{} +} + +var yamlContentType = []string{"application/x-yaml; charset=utf-8"} + +// Render (YAML) marshals the given interface object and writes data with custom ContentType. +func (r YAML) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + + bytes, err := yaml.Marshal(r.Data) + if err != nil { + return err + } + + _, err = w.Write(bytes) + return err +} + +// WriteContentType (YAML) writes YAML ContentType for response. +func (r YAML) WriteContentType(w http.ResponseWriter) { + writeContentType(w, yamlContentType) +} diff --git a/lego/sys/gin/sys_test.go b/lego/sys/gin/sys_test.go new file mode 100644 index 000000000..c2a8299e3 --- /dev/null +++ b/lego/sys/gin/sys_test.go @@ -0,0 +1,42 @@ +package gin_test + +import ( + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + "testing" + + "go_dreamfactory/lego/sys/gin" + "go_dreamfactory/lego/sys/gin/engine" + "go_dreamfactory/lego/sys/log" +) + +func Test_sys(t *testing.T) { + if err := log.OnInit(nil); err != nil { + fmt.Printf("log init err:%v", err) + return + } + if sys, err := gin.NewSys(); err != nil { + fmt.Printf("gin init err:%v", err) + } else { + sys.GET("/test", func(c *engine.Context) { + c.JSON(http.StatusOK, "hello") + }) + } + + //监听外部关闭服务信号 + c := make(chan os.Signal, 1) + //添加进程结束信号 + signal.Notify(c, + os.Interrupt, //退出信号 ctrl+c退出 + syscall.SIGHUP, //终端控制进程结束(终端连接断开) + syscall.SIGINT, //用户发送INTR字符(Ctrl+C)触发 + syscall.SIGTERM, //结束程序(可以被捕获、阻塞或忽略) + syscall.SIGQUIT) //用户发送QUIT字符(Ctrl+/)触发 + select { + case sig := <-c: + fmt.Println("关闭 signal\n", sig) + } +} diff --git a/lego/sys/log/core.go b/lego/sys/log/core.go new file mode 100644 index 000000000..63f23324d --- /dev/null +++ b/lego/sys/log/core.go @@ -0,0 +1,79 @@ +package log + +import ( + "fmt" + "os" + "strings" + + "go_dreamfactory/lego/utils/flietools" +) + +type ( + LogStrut interface { + ToString() (str string) + } + Field struct { + Key string + Value interface{} + } + Ilogf interface { + Debugf(format string, a ...interface{}) + Infof(format string, a ...interface{}) + Warnf(format string, a ...interface{}) + Errorf(format string, a ...interface{}) + Panicf(format string, a ...interface{}) + Fatalf(format string, a ...interface{}) + } + ILog interface { + Clone(option ...Option) ILog + Debug(msg string, fields ...Field) + Info(msg string, fields ...Field) + Warn(msg string, fields ...Field) + Error(msg string, fields ...Field) + Panic(msg string, fields ...Field) + Fatal(msg string, fields ...Field) + Ilogf + } +) + +var ( + defsys ILog +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} +func NewSys(option ...Option) (sys ILog, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Clone(option ...Option) ILog { + return defsys.Clone(option...) +} + +func Debug(msg string, fields ...Field) { defsys.Debug(msg, fields...) } +func Info(msg string, fields ...Field) { defsys.Info(msg, fields...) } +func Warn(msg string, fields ...Field) { defsys.Warn(msg, fields...) } +func Error(msg string, fields ...Field) { defsys.Error(msg, fields...) } +func Panic(msg string, fields ...Field) { defsys.Panic(msg, fields...) } +func Fatal(msg string, fields ...Field) { defsys.Fatal(msg, fields...) } +func Debugf(format string, a ...interface{}) { defsys.Debugf(format, a...) } +func Infof(format string, a ...interface{}) { defsys.Infof(format, a...) } +func Warnf(format string, a ...interface{}) { defsys.Warnf(format, a...) } +func Errorf(format string, a ...interface{}) { defsys.Errorf(format, a...) } +func Panicf(format string, a ...interface{}) { defsys.Panicf(format, a...) } +func Fatalf(format string, a ...interface{}) { defsys.Fatalf(format, a...) } + +//创建日志文件 +func createlogfile(logpath string) error { + logdir := string(logpath[0:strings.LastIndex(logpath, "/")]) + if !flietools.IsExist(logdir) { + err := os.MkdirAll(logdir, os.ModePerm) + if err != nil { + return fmt.Errorf("创建日志路径失败 1" + err.Error()) + } + } + return nil +} diff --git a/lego/sys/log/options.go b/lego/sys/log/options.go new file mode 100644 index 000000000..a87adfbd1 --- /dev/null +++ b/lego/sys/log/options.go @@ -0,0 +1,114 @@ +package log + +import ( + "go_dreamfactory/lego/utils/mapstructure" +) + +type Loglevel int8 + +const ( + DebugLevel Loglevel = iota + InfoLevel + WarnLevel + ErrorLevel + PanicLevel + FatalLevel +) + +type LogEncoder int8 + +const ( + Console LogEncoder = iota + JSON +) + +type Option func(*Options) +type Options struct { + FileName string //日志文件名包含 + Loglevel Loglevel //日志输出级别 + Debugmode bool //是否debug模式 + Encoder LogEncoder //日志输出样式 + Loglayer int //日志堆栈信息打印层级 + LogMaxSize int //每个日志文件最大尺寸 单位 M 默认 1024M + LogMaxBackups int //最多保留备份个数 默认 10个 + LogMaxAge int //文件最多保存多少天 默认 7天 +} + +func SetFileName(v string) Option { + return func(o *Options) { + o.FileName = v + } +} + +func SetLoglevel(v Loglevel) Option { + return func(o *Options) { + o.Loglevel = v + } +} + +func SetDebugMode(v bool) Option { + return func(o *Options) { + o.Debugmode = v + } +} +func SetEncoder(v LogEncoder) Option { + return func(o *Options) { + o.Encoder = v + } +} +func SetLoglayer(v int) Option { + return func(o *Options) { + o.Loglayer = v + } +} +func SetLogMaxSize(v int) Option { + return func(o *Options) { + o.LogMaxSize = v + } +} +func SetLogMaxBackups(v int) Option { + return func(o *Options) { + o.LogMaxBackups = v + } +} +func SetLogMaxAge(v int) Option { + return func(o *Options) { + o.LogMaxAge = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + FileName: "./lego.log", + Loglevel: WarnLevel, + Debugmode: false, + Encoder: Console, + Loglayer: 2, + LogMaxSize: 1024, + LogMaxBackups: 10, + LogMaxAge: 7, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + FileName: "./lego.log", + Loglevel: WarnLevel, + Debugmode: false, + Loglayer: 2, + LogMaxSize: 1024, + LogMaxBackups: 10, + LogMaxAge: 7, + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/log/sys_test.go b/lego/sys/log/sys_test.go new file mode 100644 index 000000000..14cbfc5f7 --- /dev/null +++ b/lego/sys/log/sys_test.go @@ -0,0 +1,18 @@ +package log_test + +import ( + "testing" + + "go_dreamfactory/lego/sys/log" +) + +func Test_sys(t *testing.T) { + if err := log.OnInit(map[string]interface{}{ + "FileName": "./test.log", + "Loglevel": log.FatalLevel, + "Debugmode": true, + "Loglayer": 2, + }); err == nil { + log.Infof("测试日志接口代码!") + } +} diff --git a/lego/sys/log/zaplog.go b/lego/sys/log/zaplog.go new file mode 100644 index 000000000..c967002a6 --- /dev/null +++ b/lego/sys/log/zaplog.go @@ -0,0 +1,190 @@ +package log + +import ( + "os" + "time" + + "github.com/natefinch/lumberjack" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func newSys(options Options) (sys *Logger, err error) { + createlogfile(options.FileName) + var allCore []zapcore.Core + hook := lumberjack.Logger{ + Filename: options.FileName, //日志文件路径 + MaxSize: options.LogMaxSize, //每个日志文件保存的最大尺寸 单位:M + MaxBackups: options.LogMaxBackups, //最多保留备份个数 + MaxAge: options.LogMaxAge, //文件最多保存多少天 + Compress: false, //是否压缩 disabled by default + LocalTime: true, //使用本地时间 + } + var level zapcore.Level + switch options.Loglevel { + case DebugLevel: + level = zap.DebugLevel + case InfoLevel: + level = zap.InfoLevel + case WarnLevel: + level = zap.WarnLevel + case ErrorLevel: + level = zap.ErrorLevel + case PanicLevel: + level = zap.PanicLevel + case FatalLevel: + level = zap.FatalLevel + default: + level = zap.InfoLevel + } + fileWriter := zapcore.AddSync(&hook) + consoleDebugging := zapcore.Lock(os.Stdout) + var encoderConfig zapcore.EncoderConfig + timeFormat := func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006/01/02 15:04:05.000")) + } + _, err = os.OpenFile(options.FileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + if err != nil { + return + } + if options.Debugmode { + //重新生成文件 + encoderConfig = zap.NewDevelopmentEncoderConfig() + encoderConfig.EncodeTime = timeFormat + allCore = append(allCore, zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), consoleDebugging, level)) + } else { + encoderConfig = zap.NewProductionEncoderConfig() + encoderConfig.EncodeTime = timeFormat + } + if options.Encoder == Console { + allCore = append(allCore, zapcore.NewCore(zapcore.NewConsoleEncoder(encoderConfig), fileWriter, level)) + } else { + allCore = append(allCore, zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), fileWriter, level)) + } + core := zapcore.NewTee(allCore...) + tlog := zap.New(core).WithOptions(zap.AddCaller(), zap.AddCallerSkip(options.Loglayer)) + sys = &Logger{ + core: core, + tlog: tlog, + log: tlog.Sugar(), + } + return +} + +type Logger struct { + core zapcore.Core + tlog *zap.Logger + log *zap.SugaredLogger +} + +func FieldTozapField(fields ...Field) (fds []zap.Field) { + fds = make([]zap.Field, 0) + for _, v := range fields { + field := zap.Field{Key: v.Key} + + switch v.Value.(type) { + case []byte: + field.Type = zapcore.BinaryType + field.Interface = v.Value + case bool: + field.Type = zapcore.BoolType + if v.Value.(bool) { + field.Integer = 1 + } else { + field.Integer = 0 + } + case byte: + field.Type = zapcore.Uint8Type + field.Integer = int64(v.Value.(byte)) + case time.Duration: + field.Type = zapcore.DurationType + field.Integer = int64(v.Value.(time.Duration)) + case float64: + field.Type = zapcore.Float64Type + field.Integer = v.Value.(int64) + case float32: + field.Type = zapcore.Float32Type + field.Integer = v.Value.(int64) + case int64: + field.Type = zapcore.Int64Type + field.Integer = v.Value.(int64) + case uint64: + field.Type = zapcore.Uint64Type + field.Integer = int64(v.Value.(uint64)) + case int32: + field.Type = zapcore.Int32Type + field.Integer = int64(v.Value.(int32)) + case uint32: + field.Type = zapcore.Uint64Type + field.Integer = int64(v.Value.(uint32)) + case int16: + field.Type = zapcore.Int16Type + field.Integer = int64(v.Value.(int16)) + case uint16: + field.Type = zapcore.Uint64Type + field.Integer = int64(v.Value.(uint16)) + case int8: + field.Type = zapcore.Int8Type + field.Integer = int64(v.Value.(int8)) + case string: + field.Type = zapcore.StringType + field.String = v.Value.(string) + case LogStrut: + field.Type = zapcore.StringType + field.String = v.Value.(LogStrut).ToString() + default: + field.Type = zapcore.UnknownType + field.Interface = v.Value + } + fds = append(fds, field) + } + return +} + +func (this *Logger) Clone(option ...Option) (sys ILog) { + options := newOptionsByOption(option...) + tlog := zap.New(this.core).WithOptions(zap.AddCaller(), zap.AddCallerSkip(options.Loglayer)) + sys = &Logger{ + core: this.core, + tlog: tlog, + log: tlog.Sugar(), + } + return +} + +func (this *Logger) Debug(msg string, fields ...Field) { + this.tlog.Debug(msg, FieldTozapField(fields...)...) +} +func (this *Logger) Info(msg string, fields ...Field) { + this.tlog.Info(msg, FieldTozapField(fields...)...) +} +func (this *Logger) Warn(msg string, fields ...Field) { + this.tlog.Warn(msg, FieldTozapField(fields...)...) +} +func (this *Logger) Error(msg string, fields ...Field) { + this.tlog.Error(msg, FieldTozapField(fields...)...) +} +func (this *Logger) Panic(msg string, fields ...Field) { + this.tlog.Panic(msg, FieldTozapField(fields...)...) +} +func (this *Logger) Fatal(msg string, fields ...Field) { + this.tlog.Fatal(msg, FieldTozapField(fields...)...) +} +func (this *Logger) Debugf(format string, a ...interface{}) { + this.log.Debugf(format, a...) +} +func (this *Logger) Infof(format string, a ...interface{}) { + this.log.Infof(format, a...) +} +func (this *Logger) Warnf(format string, a ...interface{}) { + this.log.Warnf(format, a...) +} +func (this *Logger) Errorf(format string, a ...interface{}) { + this.log.Errorf(format, a...) +} +func (this *Logger) Panicf(format string, a ...interface{}) { + this.log.Panicf(format, a...) +} +func (this *Logger) Fatalf(format string, a ...interface{}) { + this.log.Fatalf(format, a...) +} diff --git a/lego/sys/mgo/core.go b/lego/sys/mgo/core.go new file mode 100644 index 000000000..691a206ce --- /dev/null +++ b/lego/sys/mgo/core.go @@ -0,0 +1,148 @@ +package mgo + +import ( + "context" + + "go_dreamfactory/lego/core" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type ( + ISys interface { + Close() (err error) + ListCollectionNames(filter interface{}, opts ...*options.ListCollectionsOptions) ([]string, error) + Collection(sqltable core.SqlTable) *mongo.Collection + CreateIndex(sqltable core.SqlTable, model mongo.IndexModel, opts ...*options.CreateIndexesOptions) (string, error) + DeleteIndex(sqltable core.SqlTable, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) + UseSession(fn func(sessionContext mongo.SessionContext) error) error + CountDocuments(sqltable core.SqlTable, filter interface{}, opts ...*options.CountOptions) (int64, error) + CountDocumentsByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error) + Find(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) + FindByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) + FindOne(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult + FindOneAndUpdate(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult + InsertOne(sqltable core.SqlTable, data interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) + InsertMany(sqltable core.SqlTable, data []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) + InsertManyByCtx(sqltable core.SqlTable, ctx context.Context, data []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) + UpdateOne(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) + UpdateMany(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) + UpdateManyByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) + FindOneAndDelete(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOneAndDeleteOptions) *mongo.SingleResult + DeleteOne(sqltable core.SqlTable, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) + DeleteMany(sqltable core.SqlTable, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) + DeleteManyByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) + Aggregate(sqltable core.SqlTable, pipeline interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error) + } +) + +var ( + MongodbNil = mongo.ErrNoDocuments //数据为空错误 +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Close() (err error) { + return defsys.Close() +} + +func ListCollectionNames(filter interface{}, opts ...*options.ListCollectionsOptions) ([]string, error) { + return defsys.ListCollectionNames(filter, opts...) +} + +func Collection(sqltable core.SqlTable) *mongo.Collection { + return defsys.Collection(sqltable) +} + +func CreateIndex(sqltable core.SqlTable, model mongo.IndexModel, opts ...*options.CreateIndexesOptions) (string, error) { + return defsys.CreateIndex(sqltable, model, opts...) +} +func DeleteIndex(sqltable core.SqlTable, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) { + return defsys.DeleteIndex(sqltable, name, opts...) +} + +func UseSession(fn func(sessionContext mongo.SessionContext) error) error { + return defsys.UseSession(fn) +} + +func CountDocuments(sqltable core.SqlTable, filter interface{}, opts ...*options.CountOptions) (int64, error) { + return defsys.CountDocuments(sqltable, filter, opts...) +} + +func CountDocumentsByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error) { + return defsys.CountDocumentsByCtx(sqltable, ctx, filter, opts...) +} + +func Find(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) { + return defsys.Find(sqltable, filter, opts...) +} + +func FindOne(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult { + return defsys.FindOne(sqltable, filter, opts...) +} + +func FindByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) { + return defsys.FindByCtx(sqltable, ctx, filter, opts...) +} + +func FindOneAndUpdate(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult { + return defsys.FindOneAndUpdate(sqltable, filter, update, opts...) +} + +func InsertOne(sqltable core.SqlTable, data interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) { + return defsys.InsertOne(sqltable, data, opts...) +} + +func InsertMany(sqltable core.SqlTable, data []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) { + return defsys.InsertMany(sqltable, data, opts...) +} + +func InsertManyByCtx(sqltable core.SqlTable, ctx context.Context, data []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) { + return defsys.InsertManyByCtx(sqltable, ctx, data, opts...) +} + +func UpdateOne(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + return defsys.UpdateOne(sqltable, filter, update, opts...) +} + +func UpdateMany(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + return defsys.UpdateMany(sqltable, filter, update, opts...) +} + +func UpdateManyByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + return defsys.UpdateManyByCtx(sqltable, ctx, filter, update, opts...) +} + +func FindOneAndDelete(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOneAndDeleteOptions) *mongo.SingleResult { + return defsys.FindOneAndDelete(sqltable, filter, opts...) +} + +func DeleteOne(sqltable core.SqlTable, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + return defsys.DeleteOne(sqltable, filter, opts...) +} + +func DeleteMany(sqltable core.SqlTable, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + return defsys.DeleteMany(sqltable, filter, opts...) +} + +func DeleteManyByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + return defsys.DeleteManyByCtx(sqltable, ctx, filter, opts...) +} + +func Aggregate(sqltable core.SqlTable, pipeline interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error) { + return defsys.Aggregate(sqltable, pipeline, opts...) +} diff --git a/lego/sys/mgo/mgo.go b/lego/sys/mgo/mgo.go new file mode 100644 index 000000000..734e69c61 --- /dev/null +++ b/lego/sys/mgo/mgo.go @@ -0,0 +1,152 @@ +package mgo + +import ( + "context" + "fmt" + "time" + + "go_dreamfactory/lego/core" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readconcern" + "go.mongodb.org/mongo-driver/mongo/readpref" + "go.mongodb.org/mongo-driver/mongo/writeconcern" +) + +func newSys(options Options) (sys *Mongodb, err error) { + sys = &Mongodb{options: options} + err = sys.init() + return +} + +type Mongodb struct { + options Options + Client *mongo.Client + Database *mongo.Database +} + +func (this *Mongodb) init() (err error) { + want, err := readpref.New(readpref.SecondaryMode) //表示只使用辅助节点 + if err != nil { + return fmt.Errorf("数据库设置辅助节点 err=%s", err.Error()) + } + wc := writeconcern.New(writeconcern.WMajority()) + readconcern.Majority() + //链接mongo服务 + opt := options.Client().ApplyURI(this.options.MongodbUrl) + opt.SetLocalThreshold(3 * time.Second) //只使用与mongo操作耗时小于3秒的 + opt.SetMaxConnIdleTime(5 * time.Second) //指定连接可以保持空闲的最大毫秒数 + opt.SetMaxPoolSize(this.options.MaxPoolSize) //使用最大的连接数 + opt.SetReadPreference(want) //表示只使用辅助节点 + opt.SetReadConcern(readconcern.Majority()) //指定查询应返回实例的最新数据确认为,已写入副本集中的大多数成员 + opt.SetWriteConcern(wc) //请求确认写操作传播到大多数mongod实例 + if client, err := mongo.Connect(this.getContext(), opt); err != nil { + return fmt.Errorf("连接数据库错误 err=%s", err.Error()) + } else { + this.Client = client + if err = client.Ping(this.getContext(), readpref.Primary()); err != nil { + return fmt.Errorf("数据库不可用 err=%s", err.Error()) + } + this.Database = client.Database(this.options.MongodbDatabase) + + } + return +} + +func (this *Mongodb) Close() (err error) { + err = this.Client.Disconnect(this.getContext()) + return +} + +func (this *Mongodb) ListCollectionNames(filter interface{}, opts ...*options.ListCollectionsOptions) ([]string, error) { + return this.Database.ListCollectionNames(this.getContext(), filter, opts...) +} + +func (this *Mongodb) Collection(sqltable core.SqlTable) *mongo.Collection { + return this.Client.Database(this.options.MongodbDatabase).Collection(string(sqltable)) +} + +func (this *Mongodb) getContext() (ctx context.Context) { + ctx, _ = context.WithTimeout(context.Background(), this.options.TimeOut) + return +} + +func (this *Mongodb) CreateIndex(sqltable core.SqlTable, model mongo.IndexModel, opts ...*options.CreateIndexesOptions) (string, error) { + return this.Collection(sqltable).Indexes().CreateOne(this.getContext(), model, opts...) +} + +func (this *Mongodb) DeleteIndex(sqltable core.SqlTable, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) { + return this.Collection(sqltable).Indexes().DropOne(this.getContext(), name, opts...) +} + +func (this *Mongodb) UseSession(fn func(sessionContext mongo.SessionContext) error) error { + return this.Client.UseSession(this.getContext(), fn) +} + +func (this *Mongodb) CountDocuments(sqltable core.SqlTable, filter interface{}, opts ...*options.CountOptions) (int64, error) { + return this.Collection(sqltable).CountDocuments(this.getContext(), filter, opts...) +} +func (this *Mongodb) CountDocumentsByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.CountOptions) (int64, error) { + return this.Collection(sqltable).CountDocuments(ctx, filter, opts...) +} +func (this *Mongodb) Find(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) { + return this.Collection(sqltable).Find(this.getContext(), filter, opts...) +} + +func (this *Mongodb) FindByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.FindOptions) (*mongo.Cursor, error) { + return this.Collection(sqltable).Find(ctx, filter, opts...) +} + +func (this *Mongodb) FindOne(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult { + return this.Collection(sqltable).FindOne(this.getContext(), filter, opts...) +} + +func (this *Mongodb) FindOneAndUpdate(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.FindOneAndUpdateOptions) *mongo.SingleResult { + return this.Collection(sqltable).FindOneAndUpdate(this.getContext(), filter, update, opts...) +} + +func (this *Mongodb) InsertOne(sqltable core.SqlTable, data interface{}, opts ...*options.InsertOneOptions) (*mongo.InsertOneResult, error) { + return this.Collection(sqltable).InsertOne(this.getContext(), data, opts...) +} + +func (this *Mongodb) InsertMany(sqltable core.SqlTable, data []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) { + return this.Collection(sqltable).InsertMany(this.getContext(), data, opts...) +} + +func (this *Mongodb) InsertManyByCtx(sqltable core.SqlTable, ctx context.Context, data []interface{}, opts ...*options.InsertManyOptions) (*mongo.InsertManyResult, error) { + return this.Collection(sqltable).InsertMany(ctx, data, opts...) +} + +func (this *Mongodb) UpdateOne(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + return this.Collection(sqltable).UpdateOne(this.getContext(), filter, update, opts...) +} + +func (this *Mongodb) UpdateMany(sqltable core.SqlTable, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + return this.Collection(sqltable).UpdateMany(this.getContext(), filter, update, opts...) +} + +func (this *Mongodb) UpdateManyByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, update interface{}, opts ...*options.UpdateOptions) (*mongo.UpdateResult, error) { + return this.Collection(sqltable).UpdateMany(ctx, filter, update, opts...) +} + +func (this *Mongodb) FindOneAndDelete(sqltable core.SqlTable, filter interface{}, opts ...*options.FindOneAndDeleteOptions) *mongo.SingleResult { + return this.Collection(sqltable).FindOneAndDelete(this.getContext(), filter, opts...) +} + +func (this *Mongodb) DeleteOne(sqltable core.SqlTable, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + return this.Collection(sqltable).DeleteOne(this.getContext(), filter, opts...) +} + +func (this *Mongodb) DeleteMany(sqltable core.SqlTable, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + return this.Collection(sqltable).DeleteMany(this.getContext(), filter, opts...) +} + +func (this *Mongodb) DeleteManyByCtx(sqltable core.SqlTable, ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*mongo.DeleteResult, error) { + return this.Collection(sqltable).DeleteMany(ctx, filter, opts...) +} + +func (this *Mongodb) Aggregate(sqltable core.SqlTable, pipeline interface{}, opts ...*options.AggregateOptions) (*mongo.Cursor, error) { + return this.Collection(sqltable).Aggregate(this.getContext(), pipeline, opts...) +} diff --git a/lego/sys/mgo/options.go b/lego/sys/mgo/options.go new file mode 100644 index 000000000..18c1b95ae --- /dev/null +++ b/lego/sys/mgo/options.go @@ -0,0 +1,64 @@ +package mgo + +import ( + "time" + + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { + MongodbUrl string + MongodbDatabase string + MaxPoolSize uint64 + TimeOut time.Duration +} + +func SetMongodbUrl(v string) Option { + return func(o *Options) { + o.MongodbUrl = v + } +} + +func SetMongodbDatabase(v string) Option { + return func(o *Options) { + o.MongodbDatabase = v + } +} + +func SetMaxPoolSize(v uint64) Option { + return func(o *Options) { + o.MaxPoolSize = v + } +} + +func SetTimeOut(v time.Duration) Option { + return func(o *Options) { + o.TimeOut = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + MaxPoolSize: 1000, + TimeOut: time.Second * 3, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + MaxPoolSize: 1000, + TimeOut: time.Second * 3, + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/mgo/sys_test.go b/lego/sys/mgo/sys_test.go new file mode 100644 index 000000000..3064f2d2f --- /dev/null +++ b/lego/sys/mgo/sys_test.go @@ -0,0 +1,214 @@ +package mgo_test + +import ( + "context" + "fmt" + "testing" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/mgo" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/x/bsonx" +) + +const ( + Sql_UserDataTable core.SqlTable = "userdata" //用户 +) + +type TestData struct { + Id uint32 `bson:"_id"` //用户id + NiceName string //昵称 + Sex int32 //性别 1男 2女 + HeadUrl string //头像Id +} + +//测试 系统 直连模式 +func Test_sys(t *testing.T) { + if err := mgo.OnInit(map[string]interface{}{ + "MongodbUrl": "mongodb://127.0.0.1:37017", + "MongodbDatabase": "testdb", + }); err == nil { + if _, err := mgo.UpdateOne(Sql_UserDataTable, + bson.M{"_id": 10001}, + bson.M{"$set": bson.M{ + "nicename": "liwei1dao", + "headurl": "http://test.web.com", + "sex": 1, + }}, + new(options.UpdateOptions).SetUpsert(true)); err != nil { + fmt.Printf("UpdateOne errr:%v", err) + } + + data := &TestData{} + if err := mgo.FindOne(Sql_UserDataTable, bson.M{"_id": 10002}).Decode(data); err != nil { + fmt.Printf("FindOne errr:%v", err) + } else { + fmt.Printf("FindOne data:%+v", data) + } + } else { + fmt.Printf("OnInit errr:%v", err) + } +} + +//测试 事务 副本集模式 +func Test_Affair(t *testing.T) { + if err := mgo.OnInit(map[string]interface{}{ + "MongodbUrl": "mongodb://root:123@127.0.0.1:37017,127.0.0.1:37018,127.0.0.1:37019/admin?replicaSet=mongoReplSet", + "MongodbDatabase": "testdb", + }); err == nil { + err = mgo.UseSession(func(sessionContext mongo.SessionContext) error { + err := sessionContext.StartTransaction() + if err != nil { + fmt.Println(err) + return err + } + col := mgo.Collection(Sql_UserDataTable) + + //在事务内写一条id为“222”的记录 + _, err = col.InsertOne(sessionContext, bson.M{"_id": 10004, "nicename": "liwei2dao", "headurl": "http://test1.web.com", "sex": 2}) + if err != nil { + fmt.Println(err) + return err + } + //在事务内写一条id为“333”的记录 + _, err = col.InsertOne(sessionContext, bson.M{"_id": 10005, "nicename": "liwei3dao", "headurl": "http://test1.web.com", "sex": 2}) + if err != nil { + sessionContext.AbortTransaction(sessionContext) + return err + } else { + sessionContext.CommitTransaction(sessionContext) + } + return nil + }) + fmt.Printf("FindOne errr:%v", err) + } else { + fmt.Printf("FindOne errr:%v", err) + } +} + +//测试创建索引 过期索引 +func Test_CreateTTLIndex(t *testing.T) { + sys, err := mgo.NewSys(mgo.SetMongodbUrl("mongodb://47.90.84.157:9094"), mgo.SetMongodbDatabase("square")) + if err != nil { + fmt.Printf("start sys Fail err:%v", err) + return + } + + indexModel := mongo.IndexModel{ + Keys: bsonx.Doc{{"expire_date", bsonx.Int32(1)}}, // 设置TTL索引列"expire_date" + Options: options.Index().SetExpireAfterSeconds(1 * 24 * 3600), // 设置过期时间1天,即,条目过期一天过自动删除 + } + str, err := sys.CreateIndex(core.SqlTable("dynamics"), indexModel) + if err != nil { + fmt.Printf("CreateIndex err:%v", err) + } else { + fmt.Printf("CreateIndex str:%v", str) + } +} + +//测试创建索引 地图索引 +func Test_CreateIndex(t *testing.T) { + sys, err := mgo.NewSys(mgo.SetMongodbUrl("mongodb://47.90.84.157:9094"), mgo.SetMongodbDatabase("square")) + if err != nil { + fmt.Printf("start sys Fail err:%v", err) + return + } + + indexModel := mongo.IndexModel{ + Keys: bson.M{"location": "2dsphere"}, + Options: options.Index().SetBits(11111), + } + str, err := sys.CreateIndex(core.SqlTable("dynamics"), indexModel) + if err != nil { + fmt.Printf("CreateIndex err:%v", err) + } else { + fmt.Printf("CreateIndex str:%v", str) + } +} + +//创建复合索引 +func Test_CreateCompoundIndex(t *testing.T) { + sys, err := mgo.NewSys(mgo.SetMongodbUrl("mongodb://47.90.84.157:9094"), mgo.SetMongodbDatabase("lego_yl")) + if err != nil { + fmt.Printf("start sys Fail err:%v", err) + return + } + str, err := sys.CreateIndex(core.SqlTable("unreadmsg"), mongo.IndexModel{Keys: bson.M{"channeltype": 1, "targetid": 1, "uid": 1, "unreadmsg.sendtime": 1}}, nil) + if err != nil { + fmt.Printf("CreateIndex err:%v", err) + } else { + fmt.Printf("CreateIndex str:%v", str) + } +} + +//测试删除索引 地图索引 +func Test_DeleteIndex(t *testing.T) { + sys, err := mgo.NewSys(mgo.SetMongodbUrl("mongodb://47.90.84.157:9094"), mgo.SetMongodbDatabase("square")) + if err != nil { + fmt.Printf("start sys Fail err:%v", err) + return + } + _, err = sys.DeleteIndex(core.SqlTable("dynamics"), "location_2dsphere", nil) + if err != nil { + fmt.Printf("DeleteIndex err:%v", err) + } else { + fmt.Printf("DeleteIndex succ") + } +} + +//测试附近查找 +/* +db..find( { : + { $near : + { $geometry : + { type : "Point" ,coordinates : [ , ] } , + $maxDistance : + } + } + }) +*/ +func Test_QueryRound(t *testing.T) { + sys, err := mgo.NewSys(mgo.SetMongodbUrl("mongodb://47.90.84.157:9094"), mgo.SetMongodbDatabase("square")) + if err != nil { + fmt.Printf("start sys Fail err:%v", err) + return + } + filter := bson.M{ + "location": bson.D{ + {"$near", bson.D{ + {"$geometry", bson.M{"type": "Point", "coordinates": []float64{113.867584, 22.56532}}}, + {"$maxDistance", 10000}, + }}, + }, + } + _, err = sys.Find(core.SqlTable("dynamics"), filter) + if err != nil { + fmt.Printf("CreateIndex err:%v", err) + } else { + fmt.Printf("CreateIndex succ") + } + return +} + +//聚合查询演示 +func Test_Aggregate(t *testing.T) { + sys, err := mgo.NewSys(mgo.SetMongodbUrl("mongodb://47.90.84.157:9094"), mgo.SetMongodbDatabase("square")) + if err != nil { + fmt.Printf("start sys Fail err:%v", err) + return + } + result := make([]bson.M, 0) + var cursor *mongo.Cursor + matchStage := bson.D{{"$match", bson.M{"_id": 979332}}} + unwindStage := bson.D{{"$unwind", "$chatmsg"}} + match2Stage := bson.D{{"$match", bson.M{"chatmsg.sendtime": bson.M{"$gt": 1611890940100, "$lt": 1611893135929}}}} + groupStage := bson.D{{"$group", bson.M{"_id": 979332, "chatmsg": bson.M{"$push": "$chatmsg"}}}} + projectStage := bson.D{{"$project", bson.M{"_id": 1, "chatmsg": 1}}} + if cursor, err = sys.Aggregate(core.SqlTable("grouprecorddata"), mongo.Pipeline{matchStage, unwindStage, match2Stage, groupStage, projectStage}); err == nil { + err = cursor.All(context.Background(), &result) + } + return +} diff --git a/lego/sys/nacos/core.go b/lego/sys/nacos/core.go new file mode 100644 index 000000000..bb5879888 --- /dev/null +++ b/lego/sys/nacos/core.go @@ -0,0 +1,98 @@ +package nacos + +import ( + "github.com/nacos-group/nacos-sdk-go/model" + "github.com/nacos-group/nacos-sdk-go/vo" +) + +type ( + ISys interface { + Naming_RegisterInstance(service vo.RegisterInstanceParam) (success bool, err error) + Naming_DeregisterInstance(service vo.DeregisterInstanceParam) (success bool, err error) + Naming_GetService(param vo.GetServiceParam) (services model.Service, err error) + Naming_SelectAllInstances(param vo.SelectAllInstancesParam) (instances []model.Instance, err error) + Naming_SelectInstances(param vo.SelectInstancesParam) (instances []model.Instance, err error) + Naming_SelectOneHealthyInstance(param vo.SelectOneHealthInstanceParam) (instance *model.Instance, err error) + Naming_Subscribe(param *vo.SubscribeParam) (err error) + Naming_Unsubscribe(param *vo.SubscribeParam) (err error) + Naming_GetAllServicesInfo(param vo.GetAllServiceInfoParam) (serviceInfos model.ServiceList, err error) + Config_GetConfig(param vo.ConfigParam) (string, error) + Config_PublishConfig(param vo.ConfigParam) (bool, error) + Config_DeleteConfig(param vo.ConfigParam) (bool, error) + Config_ListenConfig(params vo.ConfigParam) (err error) + Config_CancelListenConfig(params vo.ConfigParam) (err error) + Config_SearchConfig(param vo.SearchConfigParam) (*model.ConfigPage, error) + Config_PublishAggr(param vo.ConfigParam) (published bool, err error) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +///注册服务 +func Naming_RegisterInstance(service vo.RegisterInstanceParam) (success bool, err error) { + return defsys.Naming_RegisterInstance(service) +} + +///注销服务 +func Naming_DeregisterInstance(service vo.DeregisterInstanceParam) (success bool, err error) { + return defsys.Naming_DeregisterInstance(service) +} + +///获取服务 +func Naming_GetService(param vo.GetServiceParam) (services model.Service, err error) { + return defsys.Naming_GetService(param) +} + +///获取全部服务实例 +func Naming_SelectAllInstances(param vo.SelectAllInstancesParam) (instances []model.Instance, err error) { + return defsys.Naming_SelectAllInstances(param) +} +func Naming_SelectInstances(param vo.SelectInstancesParam) (instances []model.Instance, err error) { + return defsys.Naming_SelectInstances(param) +} +func Naming_SelectOneHealthyInstance(param vo.SelectOneHealthInstanceParam) (instance *model.Instance, err error) { + return defsys.Naming_SelectOneHealthyInstance(param) +} +func Naming_Subscribe(param *vo.SubscribeParam) (err error) { + return defsys.Naming_Subscribe(param) +} +func Naming_Unsubscribe(param *vo.SubscribeParam) (err error) { + return defsys.Naming_Unsubscribe(param) +} +func Naming_GetAllServicesInfo(param vo.GetAllServiceInfoParam) (serviceInfos model.ServiceList, err error) { + return defsys.Naming_GetAllServicesInfo(param) +} + +func Config_GetConfig(param vo.ConfigParam) (string, error) { + return defsys.Config_GetConfig(param) +} +func Config_PublishConfig(param vo.ConfigParam) (bool, error) { + return defsys.Config_PublishConfig(param) +} +func Config_DeleteConfig(param vo.ConfigParam) (bool, error) { + return defsys.Config_DeleteConfig(param) +} +func Config_ListenConfig(params vo.ConfigParam) (err error) { + return defsys.Config_ListenConfig(params) +} +func Config_CancelListenConfig(params vo.ConfigParam) (err error) { + return defsys.Config_ListenConfig(params) +} +func Config_SearchConfig(param vo.SearchConfigParam) (*model.ConfigPage, error) { + return defsys.Config_SearchConfig(param) +} +func Config_PublishAggr(param vo.ConfigParam) (published bool, err error) { + return defsys.Config_PublishAggr(param) +} diff --git a/lego/sys/nacos/nacos.go b/lego/sys/nacos/nacos.go new file mode 100644 index 000000000..5b4aba79b --- /dev/null +++ b/lego/sys/nacos/nacos.go @@ -0,0 +1,143 @@ +package nacos + +import ( + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/config_client" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + "github.com/nacos-group/nacos-sdk-go/common/constant" + "github.com/nacos-group/nacos-sdk-go/model" + "github.com/nacos-group/nacos-sdk-go/vo" +) + +func newSys(options Options) (sys *Nacos, err error) { + sys = &Nacos{ + options: options, + } + // 创建clientConfig + clientConfig := constant.ClientConfig{ + NamespaceId: options.NamespaceId, + TimeoutMs: options.TimeoutMs, + NotLoadCacheAtStart: true, + Username: options.Username, + Password: options.Password, + LogDir: "./nacos/log", + CacheDir: "./nacos/cache", + RotateTime: "1h", + MaxAge: 3, + LogLevel: "debug", + } + // 至少一个ServerConfig + serverConfigs := []constant.ServerConfig{ + { + IpAddr: options.NacosAddr, + ContextPath: "/nacos", + Port: options.Port, + Scheme: "http", + }, + } + if sys.options.NacosClientType == NamingClient || sys.options.NacosClientType == All { + // 创建服务发现客户端的另一种方式 (推荐) + sys.namingClient, err = clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, + ) + } + if sys.options.NacosClientType == ConfigClient || sys.options.NacosClientType == All { + // 创建服务发现客户端的另一种方式 (推荐) + sys.configClient, err = clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, + ) + } + return +} + +type Nacos struct { + options Options + namingClient naming_client.INamingClient + configClient config_client.IConfigClient +} + +///注册服务 +func (this *Nacos) Naming_RegisterInstance(service vo.RegisterInstanceParam) (success bool, err error) { + success, err = this.namingClient.RegisterInstance(service) + return +} + +///注销服务 +func (this *Nacos) Naming_DeregisterInstance(service vo.DeregisterInstanceParam) (success bool, err error) { + success, err = this.namingClient.DeregisterInstance(service) + return +} + +///获取服务 +func (this *Nacos) Naming_GetService(param vo.GetServiceParam) (services model.Service, err error) { + services, err = this.namingClient.GetService(param) + return +} + +/// SelectAllInstance可以返回全部实例列表,包括healthy=false,enable=false,weight<=0 +func (this *Nacos) Naming_SelectAllInstances(param vo.SelectAllInstancesParam) (instances []model.Instance, err error) { + instances, err = this.namingClient.SelectAllInstances(param) + return +} + +///选中实例列表 +func (this *Nacos) Naming_SelectInstances(param vo.SelectInstancesParam) (instances []model.Instance, err error) { + instances, err = this.namingClient.SelectInstances(param) + return +} + +/// SelectOneHealthyInstance将会按加权随机轮询的负载均衡策略返回一个健康的实例 实例必须满足的条件:health=true,enable=true and weight>0 +func (this *Nacos) Naming_SelectOneHealthyInstance(param vo.SelectOneHealthInstanceParam) (instance *model.Instance, err error) { + instance, err = this.namingClient.SelectOneHealthyInstance(param) + return +} + +/// Subscribe key=serviceName+groupName+cluster 注意:我们可以在相同的key添加多个SubscribeCallback. +func (this *Nacos) Naming_Subscribe(param *vo.SubscribeParam) (err error) { + err = this.namingClient.Subscribe(param) + return +} + +func (this *Nacos) Naming_Unsubscribe(param *vo.SubscribeParam) (err error) { + err = this.namingClient.Unsubscribe(param) + return +} + +func (this *Nacos) Naming_GetAllServicesInfo(param vo.GetAllServiceInfoParam) (serviceInfos model.ServiceList, err error) { + serviceInfos, err = this.namingClient.GetAllServicesInfo(param) + return +} + +func (this *Nacos) Config_GetConfig(param vo.ConfigParam) (string, error) { + return this.configClient.GetConfig(param) +} + +func (this *Nacos) Config_PublishConfig(param vo.ConfigParam) (bool, error) { + return this.configClient.PublishConfig(param) +} + +func (this *Nacos) Config_DeleteConfig(param vo.ConfigParam) (bool, error) { + return this.configClient.PublishConfig(param) +} + +func (this *Nacos) Config_ListenConfig(params vo.ConfigParam) (err error) { + return this.configClient.ListenConfig(params) +} + +func (this *Nacos) Config_CancelListenConfig(params vo.ConfigParam) (err error) { + return this.configClient.CancelListenConfig(params) +} + +func (this *Nacos) Config_SearchConfig(param vo.SearchConfigParam) (*model.ConfigPage, error) { + return this.configClient.SearchConfig(param) +} + +func (this *Nacos) Config_PublishAggr(param vo.ConfigParam) (published bool, err error) { + return this.configClient.PublishAggr(param) +} diff --git a/lego/sys/nacos/options.go b/lego/sys/nacos/options.go new file mode 100644 index 000000000..54343cff9 --- /dev/null +++ b/lego/sys/nacos/options.go @@ -0,0 +1,86 @@ +package nacos + +import ( + "go_dreamfactory/lego/utils/mapstructure" +) + +type NacosClientType uint8 + +const ( + NamingClient NacosClientType = iota + ConfigClient + All +) + +type Option func(*Options) +type Options struct { + NacosClientType NacosClientType + NamespaceId string + NacosAddr string + Port uint64 + Username string + Password string + TimeoutMs uint64 +} + +func SetNacosClientType(v NacosClientType) Option { + return func(o *Options) { + o.NacosClientType = v + } +} +func SetNamespaceId(v string) Option { + return func(o *Options) { + o.NamespaceId = v + } +} +func SetNacosAddr(v string) Option { + return func(o *Options) { + o.NacosAddr = v + } +} + +func SetPort(v uint64) Option { + return func(o *Options) { + o.Port = v + } +} + +func SetUsername(v string) Option { + return func(o *Options) { + o.Username = v + } +} +func SetPassword(v string) Option { + return func(o *Options) { + o.Password = v + } +} + +func SetTimeoutMs(v uint64) Option { + return func(o *Options) { + o.TimeoutMs = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + TimeoutMs: 5000, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + TimeoutMs: 5000, + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/nacos/sys_test.go b/lego/sys/nacos/sys_test.go new file mode 100644 index 000000000..2a9dcfebf --- /dev/null +++ b/lego/sys/nacos/sys_test.go @@ -0,0 +1,43 @@ +package nacos_test + +import ( + "fmt" + "testing" + + "go_dreamfactory/lego/sys/nacos" + + "github.com/nacos-group/nacos-sdk-go/vo" +) + +func Test_SysInit(t *testing.T) { + sys, err := nacos.NewSys( + nacos.SetNacosAddr("127.0.0.1"), + nacos.SetPort(8848), + nacos.SetNamespaceId("17d02ef9-afaa-4878-ad6c-9fd697f3b628"), + ) + if err != nil { + fmt.Printf("启动系统错误err:%v", err) + } + succ, err := sys.Naming_RegisterInstance(vo.RegisterInstanceParam{ + Ip: "127.0.0.1", + Port: 8848, + Weight: 0.69, + GroupName: "demo", + ClusterName: "demo", + ServiceName: "demo1", + Metadata: map[string]string{ + "tag": "demo", + }, + }) + fmt.Printf("sys RegisterInstance succ:%v err:%v", succ, err) + // time.Sleep(time.Second * 3) + serviceInfos, err := sys.Naming_GetAllServicesInfo(vo.GetAllServiceInfoParam{ + NameSpace: "17d02ef9-afaa-4878-ad6c-9fd697f3b628", + GroupName: "demo", + }) + + fmt.Printf("sys GetAllServicesInfo serviceInfos:%+v err:%v", serviceInfos, err) + + // succ, err = sys.DeregisterInstance() + // fmt.Printf("sys DeregisterInstance succ:%v err:%v", succ, err) +} diff --git a/lego/sys/proto/core.go b/lego/sys/proto/core.go new file mode 100644 index 000000000..710654ec7 --- /dev/null +++ b/lego/sys/proto/core.go @@ -0,0 +1,63 @@ +package proto + +import ( + "bufio" + "reflect" +) + +type ( + IMessage interface { + GetComId() uint16 + GetMsgId() uint16 + GetMsgLen() uint32 + GetBuffer() []byte + ToStriing() string + } + IMessageFactory interface { + SetMessageConfig(MsgProtoType ProtoType, IsUseBigEndian bool) + DecodeMessageBybufio(r *bufio.Reader) (message IMessage, err error) + DecodeMessageBybytes(buffer []byte) (message IMessage, err error) + EncodeToMesage(comId uint16, msgId uint16, msg interface{}) (message IMessage) + EncodeToByte(message IMessage) (buffer []byte) + RpcEncodeMessage(d interface{}) ([]byte, error) + RpcDecodeMessage(dataType reflect.Type, d []byte) (interface{}, error) + } + ISys interface { + DecodeMessageBybufio(r *bufio.Reader) (message IMessage, err error) + DecodeMessageBybytes(buffer []byte) (message IMessage, err error) + EncodeToMesage(comId uint16, msgId uint16, msg interface{}) (message IMessage) + EncodeToByte(message IMessage) (buffer []byte) + ByteDecodeToStruct(t reflect.Type, d []byte) (interface{}, error) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func DecodeMessageBybufio(r *bufio.Reader) (IMessage, error) { + return defsys.DecodeMessageBybufio(r) +} +func DecodeMessageBybytes(buffer []byte) (msg IMessage, err error) { + return defsys.DecodeMessageBybytes(buffer) +} +func EncodeToMesage(comId uint16, msgId uint16, msg interface{}) (message IMessage) { + return defsys.EncodeToMesage(comId, msgId, msg) +} +func EncodeToByte(message IMessage) (buffer []byte) { + return defsys.EncodeToByte(message) +} + +func ByteDecodeToStruct(t reflect.Type, d []byte) (interface{}, error) { + return defsys.ByteDecodeToStruct(t, d) +} diff --git a/lego/sys/proto/defmessage.go b/lego/sys/proto/defmessage.go new file mode 100644 index 000000000..08c23912f --- /dev/null +++ b/lego/sys/proto/defmessage.go @@ -0,0 +1,218 @@ +package proto + +import ( + "bufio" + "encoding/binary" + "encoding/json" + "fmt" + "io" + "reflect" + + "go_dreamfactory/lego/sys/rpc" + + "github.com/golang/protobuf/proto" +) + +//默认消息体 +type DefMessage struct { + ComId uint16 //主Id + MsgId uint16 //次Id + MsgLen uint32 //消息体长度 + Buffer []byte //消息体 +} + +func (this *DefMessage) GetComId() uint16 { + return this.ComId +} + +func (this *DefMessage) GetMsgId() uint16 { + return this.MsgId +} + +func (this *DefMessage) GetMsgLen() uint32 { + return this.MsgLen +} + +func (this *DefMessage) GetBuffer() []byte { + return this.Buffer +} + +func (this *DefMessage) ToStriing() string { + return fmt.Sprintf("ComId:%d MsgId:%d MsgLen:%d", this.ComId, this.MsgId, this.MsgLen) +} + +//默认消息工厂 +type DefMessageFactory struct { + msgProtoType ProtoType //消息体类型 + isUseBigEndian bool //消息传输码 + msgheadsize uint32 //消息头大小 + msgmaxleng uint32 //消息体最大长度 +} + +func (this *DefMessageFactory) SetMessageConfig(MsgProtoType ProtoType, IsUseBigEndian bool) { + this.msgProtoType = MsgProtoType + this.isUseBigEndian = IsUseBigEndian + this.msgheadsize = 8 + this.msgmaxleng = 1024 * 1204 * 3 + //自己rpc消息解析 + rpc.OnRegisterRpcData(&DefMessage{}, this.RpcEncodeMessage, this.RpcDecodeMessage) +} + +func (this *DefMessageFactory) DecodeMessageBybufio(r *bufio.Reader) (message IMessage, err error) { + msg := &DefMessage{} + msg.ComId, err = this.readUInt16(r) + if err != nil { + return msg, err + } + msg.MsgId, err = this.readUInt16(r) + if err != nil { + return msg, err + } + msg.MsgLen, err = this.readUInt32(r) + if err != nil { + return msg, err + } + if msg.MsgLen > this.msgmaxleng { + return nil, fmt.Errorf("DecodeMessageBybufio err msg.MsgLen:%d Super long", msg.MsgLen) + } + msg.Buffer = make([]byte, msg.MsgLen) + _, err = io.ReadFull(r, msg.Buffer) + return msg, err +} + +func (this *DefMessageFactory) DecodeMessageBybytes(buffer []byte) (message IMessage, err error) { + if uint32(len(buffer)) >= this.msgheadsize { + msg := &DefMessage{} + if this.isUseBigEndian { + msg.ComId = binary.BigEndian.Uint16(buffer[0:]) + msg.MsgId = binary.BigEndian.Uint16(buffer[2:]) + msg.MsgLen = binary.BigEndian.Uint32(buffer[4:]) + } else { + msg.ComId = binary.LittleEndian.Uint16(buffer[0:]) + msg.MsgId = binary.LittleEndian.Uint16(buffer[2:]) + msg.MsgLen = binary.LittleEndian.Uint32(buffer[4:]) + } + + if uint32(len(buffer)) >= msg.MsgLen+this.msgheadsize { + msg.Buffer = buffer[this.msgheadsize : msg.MsgLen+this.msgheadsize] + return msg, nil + } else { + return nil, fmt.Errorf("DecodeMessageBybytes err package:%v msg.MsgLen:%d", buffer, msg.MsgLen) + } + } + return nil, fmt.Errorf("DecodeMessageBybytes err package:%v", buffer) +} + +func (this *DefMessageFactory) EncodeToMesage(comId uint16, msgId uint16, msg interface{}) (message IMessage) { + defmessage := &DefMessage{ + ComId: uint16(comId), + MsgId: uint16(msgId), + } + if this.msgProtoType == Proto_Buff { + defmessage.Buffer, _ = proto.Marshal(msg.(proto.Message)) + } else { + defmessage.Buffer, _ = json.Marshal(msg) + } + defmessage.MsgLen = uint32(len(defmessage.Buffer)) + return defmessage +} + +func (this *DefMessageFactory) EncodeToByte(message IMessage) (buffer []byte) { + if this.isUseBigEndian { + buffer := []byte{} + _msg := make([]byte, 2) + binary.BigEndian.PutUint16(_msg, message.GetComId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 2) + binary.BigEndian.PutUint16(_msg, message.GetMsgId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 4) + binary.BigEndian.PutUint32(_msg, message.GetMsgLen()) + buffer = append(buffer, _msg...) + buffer = append(buffer, message.GetBuffer()...) + return buffer + } else { + buffer := []byte{} + _msg := make([]byte, 2) + binary.LittleEndian.PutUint16(_msg, message.GetComId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 2) + binary.LittleEndian.PutUint16(_msg, message.GetMsgId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 4) + binary.LittleEndian.PutUint32(_msg, message.GetMsgLen()) + buffer = append(buffer, _msg...) + buffer = append(buffer, message.GetBuffer()...) + return buffer + } +} + +func (this *DefMessageFactory) RpcEncodeMessage(d interface{}) ([]byte, error) { + message := d.(IMessage) + if this.isUseBigEndian { + buffer := []byte{} + _msg := make([]byte, 2) + binary.BigEndian.PutUint16(_msg, message.GetComId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 2) + binary.BigEndian.PutUint16(_msg, message.GetMsgId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 4) + binary.BigEndian.PutUint32(_msg, message.GetMsgLen()) + buffer = append(buffer, _msg...) + buffer = append(buffer, message.GetBuffer()...) + return buffer, nil + } else { + buffer := []byte{} + _msg := make([]byte, 2) + binary.LittleEndian.PutUint16(_msg, message.GetComId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 2) + binary.LittleEndian.PutUint16(_msg, message.GetMsgId()) + buffer = append(buffer, _msg...) + _msg = make([]byte, 4) + binary.LittleEndian.PutUint32(_msg, message.GetMsgLen()) + buffer = append(buffer, _msg...) + buffer = append(buffer, message.GetBuffer()...) + return buffer, nil + } +} + +func (this *DefMessageFactory) RpcDecodeMessage(dataType reflect.Type, d []byte) (interface{}, error) { + return this.DecodeMessageBybytes(d) +} + +func (this *DefMessageFactory) readByte(r *bufio.Reader) (byte, error) { + buf := make([]byte, 1) + _, err := io.ReadFull(r, buf[:1]) + if err != nil { + return 0, err + } + return buf[0], nil +} + +func (this *DefMessageFactory) readUInt16(r *bufio.Reader) (uint16, error) { + buf := make([]byte, 2) + _, err := io.ReadFull(r, buf[:2]) + if err != nil { + return 0, err + } + if this.isUseBigEndian { + return binary.BigEndian.Uint16(buf[:2]), nil + } else { + return binary.LittleEndian.Uint16(buf[:2]), nil + } +} + +func (this *DefMessageFactory) readUInt32(r *bufio.Reader) (uint32, error) { + buf := make([]byte, 4) + _, err := io.ReadFull(r, buf[:4]) + if err != nil { + return 0, err + } + if this.isUseBigEndian { + return binary.BigEndian.Uint32(buf[:4]), nil + } else { + return binary.LittleEndian.Uint32(buf[:4]), nil + } +} diff --git a/lego/sys/proto/options.go b/lego/sys/proto/options.go new file mode 100644 index 000000000..e895b9e82 --- /dev/null +++ b/lego/sys/proto/options.go @@ -0,0 +1,68 @@ +package proto + +import ( + "go_dreamfactory/lego/utils/mapstructure" +) + +type ProtoType uint8 + +const ( + Proto_Json ProtoType = 1 + Proto_Buff ProtoType = 2 +) + +type Option func(*Options) +type Options struct { + MsgProtoType ProtoType + IsUseBigEndian bool + MessageFactory IMessageFactory +} + +func SetMsgProtoType(v ProtoType) Option { + return func(o *Options) { + o.MsgProtoType = v + } +} + +func SetIsUseBigEndian(v bool) Option { + return func(o *Options) { + o.IsUseBigEndian = v + } +} + +func SetMessageFactory(v IMessageFactory) Option { + return func(o *Options) { + o.MessageFactory = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + MsgProtoType: Proto_Buff, + IsUseBigEndian: false, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + if options.MessageFactory == nil { + options.MessageFactory = new(DefMessageFactory) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + MsgProtoType: Proto_Buff, + IsUseBigEndian: false, + } + for _, o := range opts { + o(&options) + } + if options.MessageFactory == nil { + options.MessageFactory = new(DefMessageFactory) + } + return options +} diff --git a/lego/sys/proto/proto.go b/lego/sys/proto/proto.go new file mode 100644 index 000000000..f3f927f0f --- /dev/null +++ b/lego/sys/proto/proto.go @@ -0,0 +1,48 @@ +package proto + +import ( + "bufio" + "encoding/json" + "reflect" + + "github.com/golang/protobuf/proto" +) + +func newSys(options Options) (sys *Proto, err error) { + sys = &Proto{ + options: options, + } + options.MessageFactory.SetMessageConfig(options.MsgProtoType, options.IsUseBigEndian) + return +} + +type Proto struct { + options Options +} + +func (this *Proto) DecodeMessageBybufio(r *bufio.Reader) (message IMessage, err error) { + return this.options.MessageFactory.DecodeMessageBybufio(r) +} + +func (this *Proto) DecodeMessageBybytes(buffer []byte) (message IMessage, err error) { + return this.options.MessageFactory.DecodeMessageBybytes(buffer) +} + +func (this *Proto) EncodeToMesage(comId uint16, msgId uint16, msg interface{}) (message IMessage) { + return this.options.MessageFactory.EncodeToMesage(comId, msgId, msg) +} + +func (this *Proto) EncodeToByte(message IMessage) (buffer []byte) { + return this.options.MessageFactory.EncodeToByte(message) +} + +func (this *Proto) ByteDecodeToStruct(t reflect.Type, d []byte) (data interface{}, err error) { + if this.options.MsgProtoType == Proto_Json { + data = reflect.New(t.Elem()).Interface() + err = json.Unmarshal(d, data) + } else { + data = reflect.New(t.Elem()).Interface() + err = proto.UnmarshalMerge(d, data.(proto.Message)) + } + return +} diff --git a/lego/sys/redis/cluster/core.go b/lego/sys/redis/cluster/core.go new file mode 100644 index 000000000..7d1ccdf91 --- /dev/null +++ b/lego/sys/redis/cluster/core.go @@ -0,0 +1,93 @@ +package cluster + +import ( + "context" + "time" + + "github.com/go-redis/redis/v8" +) + +func NewSys(RedisUrl []string, RedisPassword string, timeOut time.Duration, + encode func(value interface{}) (result []byte, err error), + decode func(value []byte, result interface{}) (err error), +) (sys *Redis, err error) { + var ( + client *redis.ClusterClient + ) + client = redis.NewClusterClient(&redis.ClusterOptions{ + Addrs: RedisUrl, + Password: RedisPassword, + }) + sys = &Redis{ + client: client, + timeOut: timeOut, + Encode: encode, + Decode: decode, + } + _, err = sys.Ping() + return +} + +type Redis struct { + client *redis.ClusterClient + timeOut time.Duration + Encode func(value interface{}) (result []byte, err error) + Decode func(value []byte, result interface{}) (err error) +} + +func (this *Redis) getContext() (ctx context.Context) { + ctx, _ = context.WithTimeout(context.Background(), this.timeOut) + return +} + +///事务 +func (this *Redis) Close() (err error) { + err = this.client.Close() + return +} + +/// Ping +func (this *Redis) Ping() (string, error) { + return this.client.Ping(this.getContext()).Result() +} + +/// 命令接口 +func (this *Redis) Do(ctx context.Context, args ...interface{}) *redis.Cmd { + return this.client.Do(ctx, args...) +} + +///批处理 +func (this *Redis) Pipeline(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + _, err = this.client.Pipelined(ctx, fn) + return +} + +///事务 +func (this *Redis) TxPipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + _, err = this.client.TxPipelined(ctx, fn) + return +} + +///监控 +func (this *Redis) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) (err error) { + agrs := make([]string, len(keys)) + for i, v := range keys { + agrs[i] = string(v) + } + err = this.client.Watch(ctx, fn, agrs...) + return +} + +//锁 +func (this *Redis) Lock(key string, outTime int) (result bool, err error) { + cmd := redis.NewBoolCmd(this.getContext(), "set", key, 1, "ex", outTime, "nx") + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} + +//锁 +func (this *Redis) UnLock(key string) (err error) { + err = this.Delete(key) + return +} diff --git a/lego/sys/redis/cluster/hash.go b/lego/sys/redis/cluster/hash.go new file mode 100644 index 000000000..875e1235c --- /dev/null +++ b/lego/sys/redis/cluster/hash.go @@ -0,0 +1,170 @@ +package cluster + +import ( + "reflect" + + "github.com/go-redis/redis/v8" +) + +/* +Redis Hdel 命令用于删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略 +*/ +func (this *Redis) HDel(key string, fields ...string) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "HDEL") + agrs = append(agrs, key) + for _, v := range fields { + agrs = append(agrs, v) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Hexists 命令用于查看哈希表的指定字段是否存在 +*/ +func (this *Redis) HExists(key string, field string) (result bool, err error) { + result, err = this.client.Do(this.getContext(), "HEXISTS", key, field).Bool() + return +} + +/* +Redis Hget 命令用于返回哈希表中指定字段的值 +*/ +func (this *Redis) HGet(key string, field string, value interface{}) (err error) { + var resultvalue string + if resultvalue = this.client.Do(this.getContext(), "HSET", key, field).String(); resultvalue != string(redis.Nil) { + err = this.Decode([]byte(resultvalue), value) + } + return +} + +/* +Redis Hgetall 命令用于返回哈希表中,所有的字段和值。 +在返回值里,紧跟每个字段名(field name)之后是字段的值(value),所以返回值的长度是哈希表大小的两倍 +*/ +func (this *Redis) HGetAll(key string, valuetype reflect.Type) (result []interface{}, err error) { + cmd := redis.NewStringSliceCmd(this.getContext(), "HGETALL", key) + this.client.Process(this.getContext(), cmd) + var _result []string + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Hincrby 命令用于为哈希表中的字段值加上指定增量值。 +增量也可以为负数,相当于对指定字段进行减法操作。 +如果哈希表的 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 +如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 。 +对一个储存字符串值的字段执行 HINCRBY 命令将造成一个错误。 +本操作的值被限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) HIncrBy(key string, field string, value int) (err error) { + err = this.client.Do(this.getContext(), "HINCRBY", key, field, value).Err() + return +} + +/* +Redis Hincrbyfloat 命令用于为哈希表中的字段值加上指定浮点数增量值。 +如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 +*/ +func (this *Redis) HIncrByFloat(key string, field string, value float32) (err error) { + err = this.client.Do(this.getContext(), "HINCRBYFLOAT", key, field, value).Err() + return +} + +/* +Redis Hkeys 命令用于获取哈希表中的所有域(field) +*/ +func (this *Redis) Hkeys(key string) (result []string, err error) { + cmd := redis.NewStringSliceCmd(this.getContext(), "HKEYS", key) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} + +/* +Redis Hlen 命令用于获取哈希表中字段的数量 +*/ +func (this *Redis) Hlen(key string) (result int, err error) { + result, err = this.client.Do(this.getContext(), "HLEN", key).Int() + return +} + +/* +Redis Hmget 命令用于返回哈希表中,一个或多个给定字段的值。 +如果指定的字段不存在于哈希表,那么返回一个 nil 值 +*/ +func (this *Redis) HMGet(key string, valuetype reflect.Type, fields ...string) (result []interface{}, err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "HMGET") + agrs = append(agrs, key) + for _, v := range fields { + agrs = append(agrs, v) + } + cmd := redis.NewStringSliceCmd(this.getContext(), agrs...) + this.client.Process(this.getContext(), cmd) + var _result []string + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Hmset 命令用于同时将多个 field-value (字段-值)对设置到哈希表中。 +此命令会覆盖哈希表中已存在的字段。 +如果哈希表不存在,会创建一个空哈希表,并执行 HMSET 操作 +*/ +func (this *Redis) HMSet(key string, value map[string]interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "HMSET") + agrs = append(agrs, key) + for k, v := range value { + result, _ := this.Encode(v) + agrs = append(agrs, k, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Hset 命令用于为哈希表中的字段赋值 +如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作 +如果字段已经存在于哈希表中,旧值将被覆盖 +*/ +func (this *Redis) HSet(key string, field string, value interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "HSET", key, field, resultvalue).Err() + } + return +} + +/* +Redis Hsetnx 命令用于为哈希表中不存在的的字段赋值 +如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作 +如果字段已经存在于哈希表中,操作无效 +如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令 +*/ +func (this *Redis) HSetNX(key string, field string, value interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "HSETNX", key, field, resultvalue).Err() + } + return +} diff --git a/lego/sys/redis/cluster/key.go b/lego/sys/redis/cluster/key.go new file mode 100644 index 000000000..2db92cd50 --- /dev/null +++ b/lego/sys/redis/cluster/key.go @@ -0,0 +1,88 @@ +package cluster + +import ( + "github.com/go-redis/redis/v8" +) + +/* Key *******************************************************************************/ + +///删除redis key +func (this *Redis) Delete(key string) (err error) { + err = this.client.Do(this.getContext(), "DEL", key).Err() + + return +} + +///判断是否存在key +func (this *Redis) ExistsKey(key string) (iskeep bool, err error) { + iskeep, err = this.client.Do(this.getContext(), "EXISTS", key).Bool() + return +} + +///设置key的过期时间 单位以秒级 +func (this *Redis) ExpireKey(key string, expire int) (err error) { + err = this.client.Do(this.getContext(), "EXPIRE", key, expire).Err() + return +} + +///设置key的过期时间戳 秒级时间戳 +func (this *Redis) ExpireatKey(key string, expire_unix int64) (err error) { + err = this.client.Do(this.getContext(), "EXPIREAT", key, expire_unix).Err() + return +} + +///设置key的过期时间 单位以毫秒级 +func (this *Redis) Pexpirekey(key string, expire int) (err error) { + err = this.client.Do(this.getContext(), "PEXPIRE", key, expire).Err() + return +} + +///设置key的过期时间戳 单位以豪秒级 +func (this *Redis) PexpireatKey(key string, expire_unix int64) (err error) { + err = this.client.Do(this.getContext(), "PEXPIREAT", key, expire_unix).Err() + return +} + +///移除Key的过期时间 +func (this *Redis) PersistKey(key string) (err error) { + err = this.client.Do(this.getContext(), "PERSIST", key).Err() + return +} + +///获取key剩余过期时间 单位毫秒 +func (this *Redis) PttlKey(key string) (leftexpire int64, err error) { + leftexpire, err = this.client.Do(this.getContext(), "PTTL", key).Int64() + return +} + +///获取key剩余过期时间 单位秒 +func (this *Redis) TtlKey(key string) (leftexpire int64, err error) { + leftexpire, err = this.client.Do(this.getContext(), "TTL", key).Int64() + return +} + +///重命名Key +func (this *Redis) RenameKye(oldkey string, newkey string) (err error) { + err = this.client.Do(this.getContext(), "RENAME", oldkey, newkey).Err() + return +} + +///重命名key 在新的 key 不存在时修改 key 的名称 +func (this *Redis) RenamenxKey(oldkey string, newkey string) (err error) { + err = this.client.Do(this.getContext(), "RENAMENX", oldkey, newkey).Err() + return +} + +///判断是否存在key pattern:key* +func (this *Redis) Keys(pattern string) (keys []string, err error) { + cmd := redis.NewStringSliceCmd(this.getContext(), "KEYS", string(pattern)) + this.client.Process(this.getContext(), cmd) + keys, err = cmd.Result() + return +} + +///获取键类型 +func (this *Redis) Type(key string) (ty string, err error) { + ty, err = this.client.Type(this.getContext(), key).Result() + return +} diff --git a/lego/sys/redis/cluster/list.go b/lego/sys/redis/cluster/list.go new file mode 100644 index 000000000..9e5a98c7a --- /dev/null +++ b/lego/sys/redis/cluster/list.go @@ -0,0 +1,205 @@ +package cluster + +import ( + "fmt" + "reflect" + + "github.com/go-redis/redis/v8" +) + +/* +Redis Lindex 命令用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 +*/ +func (this *Redis) Lindex(key string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "LINDEX", key).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Linsert 命令用于在列表的元素前或者后插入元素。当指定元素不存在于列表中时,不执行任何操作。 +当列表不存在时,被视为空列表,不执行任何操作。 +如果 key 不是列表类型,返回一个错误 +*/ +func (this *Redis) Linsert(key string, isbefore bool, tager interface{}, value interface{}) (err error) { + var ( + tagervalue []byte + resultvalue []byte + ) + if tagervalue, err = this.Encode(tager); err == nil { + if resultvalue, err = this.Encode(value); err == nil { + if isbefore { + err = this.client.Do(this.getContext(), "LINSERT", key, "BEFORE", tagervalue, resultvalue).Err() + } else { + err = this.client.Do(this.getContext(), "LINSERT", key, "AFTER", tagervalue, resultvalue).Err() + } + } + } + return +} + +/* +Redis Llen 命令用于返回列表的长度。 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 如果 key 不是列表类型,返回一个错误 +*/ +func (this *Redis) Llen(key string) (result int, err error) { + result, err = this.client.Do(this.getContext(), "LLEN", key).Int() + return +} + +/* +Redis Lpop 命令用于移除并返回列表的第一个元素 +*/ +func (this *Redis) LPop(key string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "LPOP", key).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Lpush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误 +*/ +func (this *Redis) LPush(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "LPUSH") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Lpushx 将一个值插入到已存在的列表头部,列表不存在时操作无效 +*/ +func (this *Redis) LPushX(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "LPUSHX") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素, +以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 +*/ +func (this *Redis) LRange(key string, start, end int, valuetype reflect.Type) (result []interface{}, err error) { + var _result []string + cmd := redis.NewStringSliceCmd(this.getContext(), "LRANGE", key, start, end) + this.client.Process(this.getContext(), cmd) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。 +COUNT 的值可以是以下几种: +count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。 +count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。 +count = 0 : 移除表中所有与 VALUE 相等的值 +*/ +func (this *Redis) LRem(key string, count int, target interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(target); err == nil { + err = this.client.Do(this.getContext(), "LREM", key, count, resultvalue).Err() + } + return +} + +/* +Redis Lset 通过索引来设置元素的值。 +当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误 +*/ +func (this *Redis) LSet(key string, index int, value interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "LSET", key, index, resultvalue).Err() + } + return +} + +/* +Redis Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 +下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标, +以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 +*/ +func (this *Redis) Ltrim(key string, start, stop int) (err error) { + err = this.client.Do(this.getContext(), "LTRIM", key, start, stop).Err() + return +} + +/* +Redis Rpop 命令用于移除列表的最后一个元素,返回值为移除的元素 +*/ +func (this *Redis) Rpop(key string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "RPOP", key).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Rpoplpush 命令用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回 +*/ +func (this *Redis) RPopLPush(oldkey string, newkey string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "RPOPLPUSH", oldkey, newkey).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。 +如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。 +注意:在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值 +*/ +func (this *Redis) RPush(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "RPUSH") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Rpushx 命令用于将一个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效 +*/ +func (this *Redis) RPushX(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "RPUSHX") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} diff --git a/lego/sys/redis/cluster/set.go b/lego/sys/redis/cluster/set.go new file mode 100644 index 000000000..74d5561f6 --- /dev/null +++ b/lego/sys/redis/cluster/set.go @@ -0,0 +1,185 @@ +package cluster + +import "reflect" + +/* +Redis Sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。 +假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合。 +当集合 key 不是集合类型时,返回一个错误。 +*/ +func (this *Redis) SAdd(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "SADD") + agrs = append(agrs, key) + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Scard 命令返回集合中元素的数量 +*/ +func (this *Redis) SCard(key string) (result int64, err error) { + result, err = this.client.SCard(this.getContext(), key).Result() + return +} + +/* +Redis Sdiff 命令返回第一个集合与其他集合之间的差异,也可以认为说第一个集合中独有的元素。不存在的集合 key 将视为空集。 +差集的结果来自前面的 FIRST_KEY ,而不是后面的 OTHER_KEY1,也不是整个 FIRST_KEY OTHER_KEY1..OTHER_KEYN 的差集。 +实例: +*/ +func (this *Redis) SDiff(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SDiff(this.getContext(), keys...) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Sdiffstore 命令将给定集合之间的差集合存储在指定的集合中。 +*/ +func (this *Redis) SDiffStore(destination string, keys ...string) (result int64, err error) { + result, err = this.client.SDiffStore(this.getContext(), destination, keys...).Result() + return +} + +/* +Redis Sismember 命令返回给定所有给定集合的交集。 不存在的集合 key 被视为空集。 当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。 +*/ +func (this *Redis) SInter(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SInter(this.getContext(), keys...) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Sinterstore 决定将给定集合之间的交集在指定的集合中。如果指定的集合已经存在,则将其覆盖 +*/ +func (this *Redis) SInterStore(destination string, keys ...string) (result int64, err error) { + result, err = this.client.SInterStore(this.getContext(), destination, keys...).Result() + return +} + +/* +Redis Sismember 命令判断成员元素是否是集合的成员 +*/ +func (this *Redis) Sismember(key string, value interface{}) (iskeep bool, err error) { + iskeep, err = this.client.SIsMember(this.getContext(), key, value).Result() + return +} + +/* +Redis Smembers 号召返回集合中的所有成员。 +*/ +func (this *Redis) SMembers(valuetype reflect.Type, key string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SMembers(this.getContext(), key) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Smove 命令将指定成员 member 元素从 source 集合移动到 destination 集合。 +SMOVE 是原子性操作。 +如果 source 集合不存在或不包含指定的 member 元素,则 SMOVE 命令不执行任何操作,仅返回 0 。否则, member 元素从 source 集合中被移除,并添加到 destination 集合中去。 +当 destination 集合已经包含 member 元素时, SMOVE 命令只是简单地将 source 集合中的 member 元素删除。 +当 source 或 destination 不是集合类型时,返回一个错误。 +*/ +func (this *Redis) SMove(source string, destination string, member interface{}) (result bool, err error) { + result, err = this.client.SMove(this.getContext(), source, destination, member).Result() + return +} + +/* +Redis Spop命令用于移除集合中的指定键的一个或多个随机元素,移除后会返回移除的元素。 +该命令类似于Srandmember命令,但SPOP将随机元素从集合中移除并返回,而Srandmember则返回元素,而不是对集合进行任何改动。 +*/ +func (this *Redis) Spop(key string) (result string, err error) { + result, err = this.client.SPop(this.getContext(), key).Result() + return +} + +/* +Redis Srandmember 命令用于返回集合中的一个随机元素。 +从 Redis 2.6 版本开始, Srandmember 命令接受可选的 count 参数: +如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。 +如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 +该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 Srandmember 则仅仅返回随机元素,而不对集合进行任何改动。 +*/ +func (this *Redis) Srandmember(key string) (result string, err error) { + result, err = this.client.SRandMember(this.getContext(), key).Result() + return +} + +/* +Redis Srem 呼吁用于移除集合中的一个或多个元素元素,不存在的元素元素会被忽略。 +当键不是集合类型,返回一个错误。 +在 Redis 2.4 版本以前,SREM 只接受个别成员值。 +*/ +func (this *Redis) SRem(key string, members ...interface{}) (result int64, err error) { + result, err = this.client.SRem(this.getContext(), key, members...).Result() + return +} + +/* +Redis Sunion 命令返回给定集合的并集。 +*/ +func (this *Redis) SUnion(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SUnion(this.getContext(), keys...) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Sunionstore 命令将给定集合的并集存储在指定的集合 destination 中。如果 destination 已经存在,则将其覆盖。 +*/ +func (this *Redis) Sunionstore(destination string, keys ...string) (result int64, err error) { + result, err = this.client.SUnionStore(this.getContext(), destination, keys...).Result() + return +} + +/* +Redis Sscan 用于继承集合中键的元素,Sscan 继承自Scan。 +*/ +func (this *Redis) Sscan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + keys, cursor, err = this.client.SScan(this.getContext(), key, _cursor, match, count).Result() + return +} diff --git a/lego/sys/redis/cluster/string.go b/lego/sys/redis/cluster/string.go new file mode 100644 index 000000000..17fc6f1a9 --- /dev/null +++ b/lego/sys/redis/cluster/string.go @@ -0,0 +1,180 @@ +package cluster + +import ( + "fmt" + "time" + + "github.com/go-redis/redis/v8" +) + +/* String *******************************************************************************/ +/* +命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型。 +*/ +func (this *Redis) Set(key string, value interface{}, expiration time.Duration) (err error) { + var result []byte + if result, err = this.Encode(value); err == nil { + err = this.client.Set(this.getContext(), string(key), result, expiration).Err() + } + return +} + +/* +指定的 key 不存在时,为 key 设置指定的值 +*/ +func (this *Redis) SetNX(key string, value interface{}) (result int64, err error) { + // var _value []byte + // if result, err = this.Encode(value); err == nil { + // err = this.client.Do(this.getContext(), "SETNX", key, result).Err() + cmd := redis.NewIntCmd(this.getContext(), "SETNX", key, value) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + // } + return +} + +/* +同时设置一个或多个 key-value 对 +*/ +func (this *Redis) MSet(keyvalues map[string]interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "MSET") + for k, v := range keyvalues { + result, _ := this.Encode(v) + agrs = append(agrs, k, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +命令用于所有给定 key 都不存在时,同时设置一个或多个 key-value 对 +*/ +func (this *Redis) MSetNX(keyvalues map[string]interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "MSETNX") + for k, v := range keyvalues { + result, _ := this.Encode(v) + agrs = append(agrs, k, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Incr 命令将 key 中储存的数字值增一。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内。 +*/ +func (this *Redis) Incr(key string) (err error) { + err = this.client.Do(this.getContext(), "INCR", key).Err() + return +} + +/* +Redis Incrby 命令将 key 中储存的数字加上指定的增量值。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) IncrBY(key string, value int) (err error) { + err = this.client.Do(this.getContext(), "INCRBY", key, value).Err() + return +} + +/* +Redis Incrbyfloat 命令为 key 中所储存的值加上指定的浮点数增量值。 +如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,再执行加法操作 +*/ +func (this *Redis) Incrbyfloat(key string, value float32) (err error) { + err = this.client.Do(this.getContext(), "INCRBYFLOAT", key, value).Err() + return +} + +/* +Redis Decr 命令将 key 中储存的数字值减一。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) Decr(key string, value int) (err error) { + err = this.client.Do(this.getContext(), "DECR", key, value).Err() + return +} + +/* +Redis Decrby 命令将 key 所储存的值减去指定的减量值。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) DecrBy(key string, value int) (err error) { + err = this.client.Do(this.getContext(), "DECRBY", key, value).Err() + return +} + +/* +Redis Append 命令用于为指定的 key 追加值。 +如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 +如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。 +*/ +func (this *Redis) Append(key string, value interface{}) (err error) { + var result []byte + if result, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "APPEND", key, result).Err() + } + return +} + +/* +命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型 +*/ +func (this *Redis) Get(key string, value interface{}) (err error) { + var result []byte + if result, err = this.client.Get(this.getContext(), key).Bytes(); err == nil { + err = this.Decode(result, value) + } + return +} + +/* +设置指定 key 的值,并返回 key 的旧值 +*/ +func (this *Redis) GetSet(key string, value interface{}, result interface{}) (err error) { + var ( + data string + _value []byte + ) + if _value, err = this.Encode(value); err == nil { + if data = this.client.Do(this.getContext(), "GETSET", key, _value).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), result) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + } + return +} + +/* +返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil +*/ +func (this *Redis) MGet(keys ...string) (result []string, err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "MGET") + for _, v := range keys { + agrs = append(agrs, v) + } + cmd := redis.NewStringSliceCmd(this.getContext(), agrs...) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} + +///判断是否存在key pattern:key* +func (this *Redis) INCRBY(key string, amount int64) (result int64, err error) { + cmd := redis.NewIntCmd(this.getContext(), "INCRBY", key, amount) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} diff --git a/lego/sys/redis/cluster/zset.go b/lego/sys/redis/cluster/zset.go new file mode 100644 index 000000000..206710e22 --- /dev/null +++ b/lego/sys/redis/cluster/zset.go @@ -0,0 +1,217 @@ +package cluster + +import ( + "reflect" + + "github.com/go-redis/redis/v8" +) + +/* +Redis ZAdd 向有序集合添加一个或多个成员,或者更新已存在成员的分数 +*/ +func (this *Redis) ZAdd(key string, members ...*redis.Z) (err error) { + this.client.ZAdd(this.getContext(), key, members...) + return +} + +/* +Redis Zcard 用于计算集合中元素的数量。 +*/ +func (this *Redis) ZCard(key string) (result int64, err error) { + result, err = this.client.ZCard(this.getContext(), key).Result() + return +} + +/* +Redis ZCount 用于计算集合中指定的范围内的数量 +*/ +func (this *Redis) ZCount(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZCount(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZIncrBy 有序集合中对指定成员的分数加上增量 increment +*/ +func (this *Redis) ZIncrBy(key string, increment float64, member string) (result float64, err error) { + result, err = this.client.ZIncrBy(this.getContext(), key, increment, member).Result() + return +} + +/* +Redis ZInterStore 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中 +*/ +func (this *Redis) ZInterStore(destination string, store *redis.ZStore) (result int64, err error) { + result, err = this.client.ZInterStore(this.getContext(), destination, store).Result() + return +} + +/* +Redis ZLexCount 在有序集合中计算指定字典区间内成员数量 +*/ +func (this *Redis) ZLexCount(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZLexCount(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZRange 通过索引区间返回有序集合指定区间内的成员 +*/ +func (this *Redis) ZRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRange(this.getContext(), key, start, stop) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRangeByLex 通过字典区间返回有序集合的成员 +*/ +func (this *Redis) ZRangeByLex(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRangeByLex(this.getContext(), key, opt) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRangeByScore 通过分数返回有序集合指定区间内的成员 +*/ +func (this *Redis) ZRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRangeByScore(this.getContext(), key, opt) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRank 返回有序集合中指定成员的索引 +*/ +func (this *Redis) ZRank(key string, member string) (result int64, err error) { + result, err = this.client.ZRank(this.getContext(), key, member).Result() + return +} + +/* +Redis ZRem 移除有序集合中的一个或多个成员 +*/ +func (this *Redis) ZRem(key string, members ...interface{}) (result int64, err error) { + result, err = this.client.ZRem(this.getContext(), key, members...).Result() + return +} + +/* +Redis ZRemRangeByLex 移除有序集合中给定的字典区间的所有成员 +*/ +func (this *Redis) ZRemRangeByLex(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZRemRangeByLex(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZRemRangeByRank 移除有序集合中给定的排名区间的所有成员 +*/ +func (this *Redis) ZRemRangeByRank(key string, start int64, stop int64) (result int64, err error) { + result, err = this.client.ZRemRangeByRank(this.getContext(), key, start, stop).Result() + return +} + +/* +Redis ZRemRangeByScore 移除有序集合中给定的分数区间的所有成员 +*/ +func (this *Redis) ZRemRangeByScore(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZRemRangeByScore(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZRevRange 返回有序集中指定区间内的成员,通过索引,分数从高到低 ZREVRANGE +*/ +func (this *Redis) ZRevRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRevRange(this.getContext(), key, start, stop) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRevRangeByScore 返回有序集中指定分数区间内的成员,分数从高到低排序 +*/ +func (this *Redis) ZRevRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRevRangeByScore(this.getContext(), key, opt) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRevRank 返回有序集中指定分数区间内的成员,分数从高到低排序 +*/ +func (this *Redis) ZRevRank(key string, member string) (result int64, err error) { + result, err = this.client.ZRevRank(this.getContext(), key, member).Result() + return +} + +/* +Redis ZScore 返回有序集中指定分数区间内的成员,分数从高到低排序 +*/ +func (this *Redis) ZScore(key string, member string) (result float64, err error) { + result, err = this.client.ZScore(this.getContext(), key, member).Result() + return +} + +/* +Redis ZScore 返回有序集中指定分数区间内的成员,分数从高到低排序 ZUNIONSTORE +*/ +func (this *Redis) ZUnionStore(dest string, store *redis.ZStore) (result int64, err error) { + result, err = this.client.ZUnionStore(this.getContext(), dest, store).Result() + return +} + +/* +Redis ZScan 迭代有序集合中的元素(包括元素成员和元素分值) +*/ +func (this *Redis) ZScan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + keys, cursor, err = this.client.ZScan(this.getContext(), key, _cursor, match, count).Result() + return +} diff --git a/lego/sys/redis/core.go b/lego/sys/redis/core.go new file mode 100644 index 000000000..90af5e2c7 --- /dev/null +++ b/lego/sys/redis/core.go @@ -0,0 +1,453 @@ +package redis + +import ( + "context" + "reflect" + "time" + + "github.com/go-redis/redis/v8" +) + +type ( + IRedis interface { + Close() (err error) + Do(ctx context.Context, args ...interface{}) *redis.Cmd + Lock(key string, outTime int) (result bool, err error) + UnLock(key string) (err error) + Pipeline(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) + TxPipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) + Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) (err error) + /*Key*/ + Delete(key string) (err error) + ExistsKey(key string) (iskeep bool, err error) + ExpireKey(key string, expire int) (err error) + ExpireatKey(key string, expire_unix int64) (err error) + Pexpirekey(key string, expire int) (err error) + PexpireatKey(key string, expire_unix int64) (err error) + PersistKey(key string) (err error) + PttlKey(key string) (leftexpire int64, err error) + TtlKey(key string) (leftexpire int64, err error) + RenameKye(oldkey string, newkey string) (err error) + RenamenxKey(oldkey string, newkey string) (err error) + Keys(pattern string) (keys []string, err error) + Type(key string) (ty string, err error) + /*String*/ + Set(key string, value interface{}, expiration time.Duration) (err error) + SetNX(key string, value interface{}) (result int64, err error) + MSet(keyvalues map[string]interface{}) (err error) + MSetNX(keyvalues map[string]interface{}) (err error) + Incr(key string) (err error) + IncrBY(key string, value int) (err error) + Incrbyfloat(key string, value float32) (err error) + Decr(key string, value int) (err error) + DecrBy(key string, value int) (err error) + Append(key string, value interface{}) (err error) + Get(key string, value interface{}) (err error) + GetSet(key string, value interface{}, result interface{}) (err error) + MGet(keys ...string) (result []string, err error) + INCRBY(key string, amount int64) (result int64, err error) + /*List*/ + Lindex(key string, value interface{}) (err error) + Linsert(key string, isbefore bool, tager interface{}, value interface{}) (err error) + Llen(key string) (result int, err error) + LPop(key string, value interface{}) (err error) + LPush(key string, values ...interface{}) (err error) + LPushX(key string, values ...interface{}) (err error) + LRange(key string, start, end int, valuetype reflect.Type) (result []interface{}, err error) + LRem(key string, count int, target interface{}) (err error) + LSet(key string, index int, value interface{}) (err error) + Ltrim(key string, start, stop int) (err error) + Rpop(key string, value interface{}) (err error) + RPopLPush(oldkey string, newkey string, value interface{}) (err error) + RPush(key string, values ...interface{}) (err error) + RPushX(key string, values ...interface{}) (err error) + /*Hash*/ + HDel(key string, fields ...string) (err error) + HExists(key string, field string) (result bool, err error) + HGet(key string, field string, value interface{}) (err error) + HGetAll(key string, valuetype reflect.Type) (result []interface{}, err error) + HIncrBy(key string, field string, value int) (err error) + HIncrByFloat(key string, field string, value float32) (err error) + Hkeys(key string) (result []string, err error) + Hlen(key string) (result int, err error) + HMGet(key string, valuetype reflect.Type, fields ...string) (result []interface{}, err error) + HMSet(key string, value map[string]interface{}) (err error) + HSet(key string, field string, value interface{}) (err error) + HSetNX(key string, field string, value interface{}) (err error) + /*Set*/ + SAdd(key string, values ...interface{}) (err error) + SCard(key string) (result int64, err error) + SDiff(valuetype reflect.Type, keys ...string) (result []interface{}, err error) + SDiffStore(destination string, keys ...string) (result int64, err error) + SInter(valuetype reflect.Type, keys ...string) (result []interface{}, err error) + SInterStore(destination string, keys ...string) (result int64, err error) + Sismember(key string, value interface{}) (iskeep bool, err error) + SMembers(valuetype reflect.Type, key string) (result []interface{}, err error) + SMove(source string, destination string, member interface{}) (result bool, err error) + Spop(key string) (result string, err error) + Srandmember(key string) (result string, err error) + SRem(key string, members ...interface{}) (result int64, err error) + SUnion(valuetype reflect.Type, keys ...string) (result []interface{}, err error) + Sunionstore(destination string, keys ...string) (result int64, err error) + Sscan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) + /*ZSet*/ + ZAdd(key string, members ...*redis.Z) (err error) + ZCard(key string) (result int64, err error) + ZCount(key string, min string, max string) (result int64, err error) + ZIncrBy(key string, increment float64, member string) (result float64, err error) + ZInterStore(destination string, store *redis.ZStore) (result int64, err error) + ZLexCount(key string, min string, max string) (result int64, err error) + ZRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) + ZRangeByLex(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) + ZRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) + ZRank(key string, member string) (result int64, err error) + ZRem(key string, members ...interface{}) (result int64, err error) + ZRemRangeByLex(key string, min string, max string) (result int64, err error) + ZRemRangeByRank(key string, start int64, stop int64) (result int64, err error) + ZRemRangeByScore(key string, min string, max string) (result int64, err error) + ZRevRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) + ZRevRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) + ZRevRank(key string, member string) (result int64, err error) + ZScore(key string, member string) (result float64, err error) + ZUnionStore(dest string, store *redis.ZStore) (result int64, err error) + ZScan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) + } + + ISys interface { + IRedis + Encode(value interface{}) (result []byte, err error) + Decode(value []byte, result interface{}) (err error) + /*Lock*/ + NewRedisMutex(key string, opt ...RMutexOption) (result *RedisMutex, err error) + } +) + +const ( + RedisNil = redis.Nil //数据为空错误 + TxFailedErr = redis.TxFailedErr +) + +var defsys ISys + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Close() (err error) { + return defsys.Close() +} +func Do(ctx context.Context, args ...interface{}) *redis.Cmd { + return defsys.Do(ctx, args...) +} + +func Pipeline(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + return defsys.Pipeline(ctx, fn) +} +func TxPipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + return defsys.TxPipelined(ctx, fn) +} +func Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) (err error) { + return defsys.Watch(ctx, fn) +} + +func Encode(value interface{}) (result []byte, err error) { + return defsys.Encode(value) +} +func Decode(value []byte, result interface{}) (err error) { + return defsys.Decode(value, result) +} +func Delete(key string) (err error) { + return defsys.Delete(key) +} +func ExistsKey(key string) (iskeep bool, err error) { + return defsys.ExistsKey(key) + +} +func ExpireKey(key string, expire int) (err error) { + return defsys.ExpireKey(key, expire) +} +func ExpireatKey(key string, expire_unix int64) (err error) { + return defsys.ExpireatKey(key, expire_unix) +} +func Pexpirekey(key string, expire int) (err error) { + return defsys.Pexpirekey(key, expire) +} +func PexpireatKey(key string, expire_unix int64) (err error) { + return defsys.PexpireatKey(key, expire_unix) +} +func PersistKey(key string) (err error) { + return defsys.PersistKey(key) +} +func PttlKey(key string) (leftexpire int64, err error) { + return defsys.PttlKey(key) +} +func TtlKey(key string) (leftexpire int64, err error) { + return defsys.TtlKey(key) +} +func RenameKye(oldkey string, newkey string) (err error) { + return defsys.RenameKye(oldkey, newkey) +} +func RenamenxKey(oldkey string, newkey string) (err error) { + return defsys.RenamenxKey(oldkey, newkey) +} +func Keys(pattern string) (keys []string, err error) { + return defsys.Keys(pattern) +} + +///获取键类型 +func Type(key string) (ty string, err error) { + return defsys.Type(key) +} + +/*String*/ +func Set(key string, value interface{}, expiration time.Duration) (err error) { + return defsys.Set(key, value, expiration) +} +func SetNX(key string, value interface{}) (result int64, err error) { + return defsys.SetNX(key, value) +} +func MSet(keyvalues map[string]interface{}) (err error) { + return defsys.MSet(keyvalues) +} +func MSetNX(keyvalues map[string]interface{}) (err error) { + return defsys.MSetNX(keyvalues) +} +func Incr(key string) (err error) { + return defsys.Incr(key) +} +func IncrBY(key string, value int) (err error) { + return defsys.IncrBY(key, value) +} +func Incrbyfloat(key string, value float32) (err error) { + return defsys.Incrbyfloat(key, value) +} +func Decr(key string, value int) (err error) { + return defsys.Decr(key, value) +} +func DecrBy(key string, value int) (err error) { + return defsys.DecrBy(key, value) +} +func Append(key string, value interface{}) (err error) { + return defsys.Append(key, value) +} +func Get(key string, value interface{}) (err error) { + return defsys.Get(key, value) +} +func GetSet(key string, value interface{}, result interface{}) (err error) { + return defsys.GetSet(key, value, result) +} +func MGet(keys ...string) (result []string, err error) { + return defsys.MGet(keys...) +} +func INCRBY(key string, amount int64) (result int64, err error) { + return defsys.INCRBY(key, amount) +} + +/*Lock*/ +func NewRedisMutex(key string, opt ...RMutexOption) (result *RedisMutex, err error) { + return defsys.NewRedisMutex(key, opt...) +} + +func Lock(key string, outTime int) (result bool, err error) { + return defsys.Lock(key, outTime) +} +func UnLock(key string) (err error) { + return defsys.UnLock(key) +} + +/*List*/ +func Lindex(key string, value interface{}) (err error) { + return defsys.Lindex(key, value) +} +func Linsert(key string, isbefore bool, tager interface{}, value interface{}) (err error) { + return defsys.Linsert(key, isbefore, tager, value) +} +func Llen(key string) (result int, err error) { + return defsys.Llen(key) +} +func LPop(key string, value interface{}) (err error) { + return defsys.LPop(key, value) +} +func LPush(key string, values ...interface{}) (err error) { + return defsys.LPush(key, values...) +} +func LPushX(key string, values ...interface{}) (err error) { + return defsys.LPushX(key, values...) +} +func LRange(key string, start, end int, valuetype reflect.Type) (result []interface{}, err error) { + return defsys.LRange(key, start, end, valuetype) +} +func LRem(key string, count int, target interface{}) (err error) { + return defsys.LRem(key, count, target) +} +func LSet(key string, index int, value interface{}) (err error) { + return defsys.LSet(key, index, value) +} +func Ltrim(key string, start, stop int) (err error) { + return defsys.Ltrim(key, start, stop) +} +func Rpop(key string, value interface{}) (err error) { + return defsys.Rpop(key, value) +} +func RPopLPush(oldkey string, newkey string, value interface{}) (err error) { + return defsys.RPopLPush(oldkey, newkey, value) +} +func RPush(key string, values ...interface{}) (err error) { + return defsys.RPush(key, values...) +} +func RPushX(key string, values ...interface{}) (err error) { + return defsys.RPushX(key, values...) +} + +/*Hash*/ +func HDel(key string, fields ...string) (err error) { + return defsys.HDel(key, fields...) +} +func HExists(key string, field string) (result bool, err error) { + return defsys.HExists(key, field) +} +func HGet(key string, field string, value interface{}) (err error) { + return defsys.HGet(key, field, value) +} +func HGetAll(key string, valuetype reflect.Type) (result []interface{}, err error) { + return defsys.HGetAll(key, valuetype) +} +func HIncrBy(key string, field string, value int) (err error) { + return defsys.HIncrBy(key, field, value) +} +func HIncrByFloat(key string, field string, value float32) (err error) { + return defsys.HIncrByFloat(key, field, value) +} +func Hkeys(key string) (result []string, err error) { + return defsys.Hkeys(key) +} +func Hlen(key string) (result int, err error) { + return defsys.Hlen(key) +} +func HMGet(key string, valuetype reflect.Type, fields ...string) (result []interface{}, err error) { + return defsys.HMGet(key, valuetype, fields...) +} +func HMSet(key string, value map[string]interface{}) (err error) { + return defsys.HMSet(key, value) +} +func HSet(key string, field string, value interface{}) (err error) { + return defsys.HSet(key, field, value) +} +func HSetNX(key string, field string, value interface{}) (err error) { + return defsys.HSetNX(key, field, value) +} + +/*Set*/ +func SAdd(key string, values ...interface{}) (err error) { + return defsys.SAdd(key, values...) +} +func SCard(key string) (result int64, err error) { + return defsys.SCard(key) +} +func SDiff(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + return defsys.SDiff(valuetype, keys...) +} +func SDiffStore(destination string, keys ...string) (result int64, err error) { + return defsys.SDiffStore(destination, keys...) +} +func SInter(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + return defsys.SInter(valuetype, keys...) +} +func SInterStore(destination string, keys ...string) (result int64, err error) { + return defsys.SInterStore(destination, keys...) +} +func Sismember(key string, value interface{}) (iskeep bool, err error) { + return defsys.Sismember(key, value) +} +func SMembers(valuetype reflect.Type, key string) (result []interface{}, err error) { + return defsys.SMembers(valuetype, key) +} +func SMove(source string, destination string, member interface{}) (result bool, err error) { + return defsys.SMove(source, destination, member) +} +func Spop(key string) (result string, err error) { + return defsys.Spop(key) +} +func Srandmember(key string) (result string, err error) { + return defsys.Srandmember(key) +} +func SRem(key string, members ...interface{}) (result int64, err error) { + return defsys.SRem(key, members...) +} +func SUnion(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + return defsys.SUnion(valuetype, keys...) +} +func Sunionstore(destination string, keys ...string) (result int64, err error) { + return defsys.Sunionstore(destination, keys...) +} +func Sscan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + return defsys.Sscan(key, _cursor, match, count) +} + +/*ZSet*/ +func ZAdd(key string, members ...*redis.Z) (err error) { + return defsys.ZAdd(key, members...) +} +func ZCard(key string) (result int64, err error) { + return defsys.ZCard(key) +} +func ZCount(key string, min string, max string) (result int64, err error) { + return defsys.ZCount(key, min, max) +} +func ZIncrBy(key string, increment float64, member string) (result float64, err error) { + return defsys.ZIncrBy(key, increment, member) +} +func ZInterStore(destination string, store *redis.ZStore) (result int64, err error) { + return defsys.ZInterStore(destination, store) +} +func ZLexCount(key string, min string, max string) (result int64, err error) { + return defsys.ZLexCount(key, min, max) +} +func ZRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + return defsys.ZRange(valuetype, key, start, stop) +} +func ZRangeByLex(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + return defsys.ZRangeByLex(valuetype, key, opt) +} +func ZRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + return defsys.ZRangeByScore(valuetype, key, opt) +} +func ZRank(key string, member string) (result int64, err error) { + return defsys.ZRank(key, member) +} +func ZRem(key string, members ...interface{}) (result int64, err error) { + return defsys.ZRem(key, members...) +} +func ZRemRangeByLex(key string, min string, max string) (result int64, err error) { + return defsys.ZRemRangeByLex(key, min, max) +} +func ZRemRangeByRank(key string, start int64, stop int64) (result int64, err error) { + return defsys.ZRemRangeByRank(key, start, stop) +} +func ZRemRangeByScore(key string, min string, max string) (result int64, err error) { + return defsys.ZRemRangeByScore(key, min, max) +} +func ZRevRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + return defsys.ZRevRange(valuetype, key, start, stop) +} +func ZRevRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + return defsys.ZRevRangeByScore(valuetype, key, opt) +} +func ZRevRank(key string, member string) (result int64, err error) { + return defsys.ZRevRank(key, member) +} +func ZScore(key string, member string) (result float64, err error) { + return defsys.ZScore(key, member) +} +func ZUnionStore(dest string, store *redis.ZStore) (result int64, err error) { + return defsys.ZUnionStore(dest, store) +} +func ZScan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + return defsys.ZScan(key, _cursor, match, count) +} diff --git a/lego/sys/redis/lock.go b/lego/sys/redis/lock.go new file mode 100644 index 000000000..2d12b1c48 --- /dev/null +++ b/lego/sys/redis/lock.go @@ -0,0 +1,53 @@ +package redis + +import ( + "errors" + "time" +) + +/* +Redis Scard 命令返回集合中元素的数量 +*/ +func (this *Redis) NewRedisMutex(key string, opt ...RMutexOption) (result *RedisMutex, err error) { + opts := newRMutexOptions(opt...) + result = &RedisMutex{ + sys: this.client, + key: key, + expiry: opts.expiry, + delay: opts.delay, + } + return +} + +type RedisMutex struct { + sys IRedis + key string + expiry int //过期时间 单位秒 + delay time.Duration +} + +//此接口未阻塞接口 +func (this *RedisMutex) Lock() (err error) { + wait := make(chan error) + go func() { + start := time.Now() + for int(time.Now().Sub(start).Seconds()) <= this.expiry { + if result, err := this.sys.Lock(this.key, this.expiry); err == nil && result { + wait <- nil + return + } else if err == nil && !result { + time.Sleep(this.delay) + } else { + wait <- err + return + } + } + wait <- errors.New("time out") + }() + err = <-wait + return +} + +func (this *RedisMutex) Unlock() { + this.sys.UnLock(this.key) +} diff --git a/lego/sys/redis/options.go b/lego/sys/redis/options.go new file mode 100644 index 000000000..93fd9b111 --- /dev/null +++ b/lego/sys/redis/options.go @@ -0,0 +1,150 @@ +package redis + +import ( + "time" + + "go_dreamfactory/lego/utils/mapstructure" +) + +type RedisType int8 + +const ( + Redis_Single RedisType = iota + Redis_Cluster +) + +///redis 存储数据格式化类型 +type RedisStorageTyoe int8 + +const ( + JsonData RedisStorageTyoe = iota + ProtoData +) + +type Option func(*Options) +type Options struct { + RedisType RedisType + Redis_Single_Addr string + Redis_Single_Password string + Redis_Single_DB int + Redis_Single_PoolSize int + Redis_Cluster_Addr []string + Redis_Cluster_Password string + RedisStorageType RedisStorageTyoe + TimeOut time.Duration +} + +func SetRedisType(v RedisType) Option { + return func(o *Options) { + o.RedisType = v + } +} + +///RedisUrl = "127.0.0.1:6379" +func SetRedis_Single_Addr(v string) Option { + return func(o *Options) { + o.Redis_Single_Addr = v + } +} + +func SetRedis_Single_Password(v string) Option { + return func(o *Options) { + o.Redis_Single_Password = v + } +} +func SetRedis_Single_DB(v int) Option { + return func(o *Options) { + o.Redis_Single_DB = v + } +} + +func SetRedis_Single_PoolSize(v int) Option { + return func(o *Options) { + o.Redis_Single_PoolSize = v + } +} +func Redis_Cluster_Addr(v []string) Option { + return func(o *Options) { + o.Redis_Cluster_Addr = v + } +} + +func SetRedis_Cluster_Password(v string) Option { + return func(o *Options) { + o.Redis_Cluster_Password = v + } +} +func SetRedisStorageType(v RedisStorageTyoe) Option { + return func(o *Options) { + o.RedisStorageType = v + } +} + +func SetTimeOut(v time.Duration) Option { + return func(o *Options) { + o.TimeOut = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + Redis_Single_Addr: "127.0.0.1:6379", + Redis_Single_Password: "", + Redis_Single_DB: 1, + Redis_Cluster_Addr: []string{"127.0.0.1:6379"}, + Redis_Cluster_Password: "", + TimeOut: time.Second * 3, + Redis_Single_PoolSize: 100, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + Redis_Single_Addr: "127.0.0.1:6379", + Redis_Single_Password: "", + Redis_Single_DB: 1, + Redis_Cluster_Addr: []string{"127.0.0.1:6379"}, + Redis_Cluster_Password: "", + TimeOut: time.Second * 3, + Redis_Single_PoolSize: 100, + } + for _, o := range opts { + o(&options) + } + return options +} + +type RMutexOption func(*RMutexOptions) +type RMutexOptions struct { + expiry int + delay time.Duration +} + +func SetExpiry(v int) RMutexOption { + return func(o *RMutexOptions) { + o.expiry = v + } +} +func Setdelay(v time.Duration) RMutexOption { + return func(o *RMutexOptions) { + o.delay = v + } +} + +func newRMutexOptions(opts ...RMutexOption) RMutexOptions { + opt := RMutexOptions{ + expiry: 5, + delay: time.Millisecond * 50, + } + for _, o := range opts { + o(&opt) + } + return opt +} diff --git a/lego/sys/redis/redis.go b/lego/sys/redis/redis.go new file mode 100644 index 000000000..af1e22f0c --- /dev/null +++ b/lego/sys/redis/redis.go @@ -0,0 +1,379 @@ +package redis + +import ( + "context" + "encoding/json" + "fmt" + "reflect" + "time" + + "go_dreamfactory/lego/sys/redis/cluster" + "go_dreamfactory/lego/sys/redis/single" + + "github.com/go-redis/redis/v8" + "google.golang.org/protobuf/proto" +) + +func newSys(options Options) (sys *Redis, err error) { + sys = &Redis{options: options} + err = sys.init() + return +} + +type Redis struct { + options Options + client IRedis +} + +func (this *Redis) init() (err error) { + if this.options.RedisType == Redis_Single { + this.client, err = single.NewSys( + this.options.Redis_Single_Addr, + this.options.Redis_Single_Password, + this.options.Redis_Single_DB, + this.options.Redis_Single_PoolSize, + this.options.TimeOut, + this.Encode, + this.Decode, + ) + } else if this.options.RedisType == Redis_Cluster { + this.client, err = cluster.NewSys( + this.options.Redis_Cluster_Addr, + this.options.Redis_Cluster_Password, + this.options.TimeOut, + this.Encode, + this.Decode, + ) + } else { + err = fmt.Errorf("init Redis err:RedisType - %d", this.options.RedisType) + } + return +} + +func (this *Redis) Close() (err error) { + return this.client.Close() +} +func (this *Redis) Do(ctx context.Context, args ...interface{}) *redis.Cmd { + return this.client.Do(ctx, args...) +} +func (this *Redis) Pipeline(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + return this.client.Pipeline(ctx, fn) +} +func (this *Redis) TxPipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + return this.client.TxPipelined(ctx, fn) +} +func (this *Redis) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) (err error) { + return this.client.Watch(ctx, fn) +} +func (this *Redis) Lock(key string, outTime int) (result bool, err error) { + return this.client.Lock(key, outTime) +} +func (this *Redis) UnLock(key string) (err error) { + return this.client.UnLock(key) +} + +///数据编码 +func (this *Redis) Encode(value interface{}) (result []byte, err error) { + if this.options.RedisStorageType == JsonData { + result, err = json.Marshal(value) + } else { + if _, ok := value.(proto.Message); ok { + result, err = proto.Marshal(value.(proto.Message)) + } else { + result, err = json.Marshal(value) + } + } + return +} + +func (this *Redis) Decode(value []byte, result interface{}) (err error) { + if this.options.RedisStorageType == JsonData { + err = json.Unmarshal(value, result) + } else { + if _, ok := result.(proto.Message); ok { + err = proto.Unmarshal(value, result.(proto.Message)) + } else { + err = json.Unmarshal(value, result) + } + } + return +} + +func (this *Redis) Delete(key string) (err error) { + return this.client.Delete(key) +} + +func (this *Redis) ExistsKey(key string) (iskeep bool, err error) { + return this.client.ExistsKey(key) +} + +func (this *Redis) ExpireKey(key string, expire int) (err error) { + return this.client.ExpireKey(key, expire) +} +func (this *Redis) ExpireatKey(key string, expire_unix int64) (err error) { + return this.client.ExpireatKey(key, expire_unix) +} +func (this *Redis) Pexpirekey(key string, expire int) (err error) { + return this.client.Pexpirekey(key, expire) +} +func (this *Redis) PexpireatKey(key string, expire_unix int64) (err error) { + return this.client.PexpireatKey(key, expire_unix) +} +func (this *Redis) PersistKey(key string) (err error) { + return this.client.PersistKey(key) +} +func (this *Redis) PttlKey(key string) (leftexpire int64, err error) { + return this.client.PttlKey(key) +} +func (this *Redis) TtlKey(key string) (leftexpire int64, err error) { + return this.client.TtlKey(key) +} +func (this *Redis) RenameKye(oldkey string, newkey string) (err error) { + return this.client.RenameKye(oldkey, newkey) +} +func (this *Redis) RenamenxKey(oldkey string, newkey string) (err error) { + return this.client.RenamenxKey(oldkey, newkey) +} +func (this *Redis) Keys(pattern string) (keys []string, err error) { + return this.client.Keys(pattern) +} + +///获取键类型 +func (this *Redis) Type(key string) (ty string, err error) { + return this.client.Type(key) +} + +/*String*/ +func (this *Redis) Set(key string, value interface{}, expiration time.Duration) (err error) { + return this.client.Set(key, value, expiration) +} +func (this *Redis) SetNX(key string, value interface{}) (result int64, err error) { + return this.client.SetNX(key, value) +} +func (this *Redis) MSet(keyvalues map[string]interface{}) (err error) { + return this.client.MSet(keyvalues) +} +func (this *Redis) MSetNX(keyvalues map[string]interface{}) (err error) { + return this.client.MSetNX(keyvalues) +} +func (this *Redis) Incr(key string) (err error) { + return this.client.Incr(key) +} +func (this *Redis) IncrBY(key string, value int) (err error) { + return this.client.IncrBY(key, value) +} +func (this *Redis) Incrbyfloat(key string, value float32) (err error) { + return this.client.Incrbyfloat(key, value) +} +func (this *Redis) Decr(key string, value int) (err error) { + return this.client.Decr(key, value) +} +func (this *Redis) DecrBy(key string, value int) (err error) { + return this.client.DecrBy(key, value) +} +func (this *Redis) Append(key string, value interface{}) (err error) { + return this.client.Append(key, value) +} +func (this *Redis) Get(key string, value interface{}) (err error) { + return this.client.Get(key, value) +} +func (this *Redis) GetSet(key string, value interface{}, result interface{}) (err error) { + return this.client.GetSet(key, value, result) +} +func (this *Redis) MGet(keys ...string) (result []string, err error) { + return this.client.MGet(keys...) +} +func (this *Redis) INCRBY(key string, amount int64) (result int64, err error) { + return this.client.INCRBY(key, amount) +} + +/*List*/ +func (this *Redis) Lindex(key string, value interface{}) (err error) { + return this.client.Lindex(key, value) +} +func (this *Redis) Linsert(key string, isbefore bool, tager interface{}, value interface{}) (err error) { + return this.client.Linsert(key, isbefore, tager, value) +} +func (this *Redis) Llen(key string) (result int, err error) { + return this.client.Llen(key) +} +func (this *Redis) LPop(key string, value interface{}) (err error) { + return this.client.LPop(key, value) +} +func (this *Redis) LPush(key string, values ...interface{}) (err error) { + return this.client.LPush(key, values...) +} +func (this *Redis) LPushX(key string, values ...interface{}) (err error) { + return this.client.LPushX(key, values...) +} +func (this *Redis) LRange(key string, start, end int, valuetype reflect.Type) (result []interface{}, err error) { + return this.client.LRange(key, start, end, valuetype) +} +func (this *Redis) LRem(key string, count int, target interface{}) (err error) { + return this.client.LRem(key, count, target) +} +func (this *Redis) LSet(key string, index int, value interface{}) (err error) { + return this.client.LSet(key, index, value) +} +func (this *Redis) Ltrim(key string, start, stop int) (err error) { + return this.client.Ltrim(key, start, stop) +} +func (this *Redis) Rpop(key string, value interface{}) (err error) { + return this.client.Rpop(key, value) +} +func (this *Redis) RPopLPush(oldkey string, newkey string, value interface{}) (err error) { + return this.client.RPopLPush(oldkey, newkey, value) +} +func (this *Redis) RPush(key string, values ...interface{}) (err error) { + return this.client.RPush(key, values...) +} +func (this *Redis) RPushX(key string, values ...interface{}) (err error) { + return this.client.RPushX(key, values...) +} + +/*Hash*/ +func (this *Redis) HDel(key string, fields ...string) (err error) { + return this.client.HDel(key, fields...) +} +func (this *Redis) HExists(key string, field string) (result bool, err error) { + return this.client.HExists(key, field) +} +func (this *Redis) HGet(key string, field string, value interface{}) (err error) { + return this.client.HGet(key, field, value) +} +func (this *Redis) HGetAll(key string, valuetype reflect.Type) (result []interface{}, err error) { + return this.client.HGetAll(key, valuetype) +} +func (this *Redis) HIncrBy(key string, field string, value int) (err error) { + return this.client.HIncrBy(key, field, value) +} +func (this *Redis) HIncrByFloat(key string, field string, value float32) (err error) { + return this.client.HIncrByFloat(key, field, value) +} +func (this *Redis) Hkeys(key string) (result []string, err error) { + return this.client.Hkeys(key) +} +func (this *Redis) Hlen(key string) (result int, err error) { + return this.client.Hlen(key) +} +func (this *Redis) HMGet(key string, valuetype reflect.Type, fields ...string) (result []interface{}, err error) { + return this.client.HMGet(key, valuetype, fields...) +} +func (this *Redis) HMSet(key string, value map[string]interface{}) (err error) { + return this.client.HMSet(key, value) +} +func (this *Redis) HSet(key string, field string, value interface{}) (err error) { + return this.client.HSet(key, field, value) +} +func (this *Redis) HSetNX(key string, field string, value interface{}) (err error) { + return this.client.HSetNX(key, field, value) +} + +/*Set*/ +func (this *Redis) SAdd(key string, values ...interface{}) (err error) { + return this.client.SAdd(key, values...) +} +func (this *Redis) SCard(key string) (result int64, err error) { + return this.client.SCard(key) +} +func (this *Redis) SDiff(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + return this.client.SDiff(valuetype, keys...) +} +func (this *Redis) SDiffStore(destination string, keys ...string) (result int64, err error) { + return this.client.SDiffStore(destination, keys...) +} +func (this *Redis) SInter(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + return this.client.SInter(valuetype, keys...) +} +func (this *Redis) SInterStore(destination string, keys ...string) (result int64, err error) { + return this.client.SInterStore(destination, keys...) +} +func (this *Redis) Sismember(key string, value interface{}) (iskeep bool, err error) { + return this.client.Sismember(key, value) +} +func (this *Redis) SMembers(valuetype reflect.Type, key string) (result []interface{}, err error) { + return this.client.SMembers(valuetype, key) +} +func (this *Redis) SMove(source string, destination string, member interface{}) (result bool, err error) { + return this.client.SMove(source, destination, member) +} +func (this *Redis) Spop(key string) (result string, err error) { + return this.client.Spop(key) +} +func (this *Redis) Srandmember(key string) (result string, err error) { + return this.client.Srandmember(key) +} +func (this *Redis) SRem(key string, members ...interface{}) (result int64, err error) { + return this.client.SRem(key, members...) +} +func (this *Redis) SUnion(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + return this.client.SUnion(valuetype, keys...) +} +func (this *Redis) Sunionstore(destination string, keys ...string) (result int64, err error) { + return this.client.Sunionstore(destination, keys...) +} +func (this *Redis) Sscan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + return this.client.Sscan(key, _cursor, match, count) +} + +/*ZSet*/ +func (this *Redis) ZAdd(key string, members ...*redis.Z) (err error) { + return this.client.ZAdd(key, members...) +} +func (this *Redis) ZCard(key string) (result int64, err error) { + return this.client.ZCard(key) +} +func (this *Redis) ZCount(key string, min string, max string) (result int64, err error) { + return this.client.ZCount(key, min, max) +} +func (this *Redis) ZIncrBy(key string, increment float64, member string) (result float64, err error) { + return this.client.ZIncrBy(key, increment, member) +} +func (this *Redis) ZInterStore(destination string, store *redis.ZStore) (result int64, err error) { + return this.client.ZInterStore(destination, store) +} +func (this *Redis) ZLexCount(key string, min string, max string) (result int64, err error) { + return this.client.ZLexCount(key, min, max) +} +func (this *Redis) ZRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + return this.client.ZRange(valuetype, key, start, stop) +} +func (this *Redis) ZRangeByLex(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + return this.client.ZRangeByLex(valuetype, key, opt) +} +func (this *Redis) ZRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + return this.client.ZRangeByScore(valuetype, key, opt) +} +func (this *Redis) ZRank(key string, member string) (result int64, err error) { + return this.client.ZRank(key, member) +} +func (this *Redis) ZRem(key string, members ...interface{}) (result int64, err error) { + return this.client.ZRem(key, members...) +} +func (this *Redis) ZRemRangeByLex(key string, min string, max string) (result int64, err error) { + return this.client.ZRemRangeByLex(key, min, max) +} +func (this *Redis) ZRemRangeByRank(key string, start int64, stop int64) (result int64, err error) { + return this.client.ZRemRangeByRank(key, start, stop) +} +func (this *Redis) ZRemRangeByScore(key string, min string, max string) (result int64, err error) { + return this.client.ZRemRangeByScore(key, min, max) +} +func (this *Redis) ZRevRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + return this.client.ZRevRange(valuetype, key, start, stop) +} +func (this *Redis) ZRevRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + return this.client.ZRevRangeByScore(valuetype, key, opt) +} +func (this *Redis) ZRevRank(key string, member string) (result int64, err error) { + return this.client.ZRevRank(key, member) +} +func (this *Redis) ZScore(key string, member string) (result float64, err error) { + return this.client.ZScore(key, member) +} +func (this *Redis) ZUnionStore(dest string, store *redis.ZStore) (result int64, err error) { + return this.client.ZUnionStore(dest, store) +} +func (this *Redis) ZScan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + return this.client.ZScan(key, _cursor, match, count) +} diff --git a/lego/sys/redis/single/core.go b/lego/sys/redis/single/core.go new file mode 100644 index 000000000..8ea81ce6e --- /dev/null +++ b/lego/sys/redis/single/core.go @@ -0,0 +1,92 @@ +package single + +import ( + "context" + "time" + + "github.com/go-redis/redis/v8" +) + +func NewSys(RedisUrl, RedisPassword string, RedisDB, PoolSize int, timeOut time.Duration, + encode func(value interface{}) (result []byte, err error), + decode func(value []byte, result interface{}) (err error), +) (sys *Redis, err error) { + var ( + client *redis.Client + ) + client = redis.NewClient(&redis.Options{ + Addr: RedisUrl, + Password: RedisPassword, + DB: RedisDB, + PoolSize: PoolSize, // 连接池大小 + }) + sys = &Redis{ + client: client, + timeOut: timeOut, + Encode: encode, + Decode: decode, + } + _, err = sys.Ping() + return +} + +type Redis struct { + client *redis.Client + timeOut time.Duration + Encode func(value interface{}) (result []byte, err error) + Decode func(value []byte, result interface{}) (err error) +} + +func (this *Redis) getContext() (ctx context.Context) { + ctx, _ = context.WithTimeout(context.Background(), this.timeOut) + return +} +func (this *Redis) Close() (err error) { + return this.client.Close() +} + +/// Ping +func (this *Redis) Ping() (string, error) { + return this.client.Ping(this.getContext()).Result() +} + +/// 命令接口 +func (this *Redis) Do(ctx context.Context, args ...interface{}) *redis.Cmd { + return this.client.Do(ctx, args...) +} + +///批处理 +func (this *Redis) Pipeline(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + _, err = this.client.Pipelined(ctx, fn) + return +} + +///事务 +func (this *Redis) TxPipelined(ctx context.Context, fn func(pipe redis.Pipeliner) error) (err error) { + _, err = this.client.TxPipelined(ctx, fn) + return +} + +///监控 +func (this *Redis) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) (err error) { + agrs := make([]string, len(keys)) + for i, v := range keys { + agrs[i] = string(v) + } + err = this.client.Watch(ctx, fn, agrs...) + return +} + +//锁 +func (this *Redis) Lock(key string, outTime int) (result bool, err error) { + cmd := redis.NewBoolCmd(this.getContext(), "set", key, 1, "ex", outTime, "nx") + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} + +//锁 +func (this *Redis) UnLock(key string) (err error) { + err = this.Delete(key) + return +} diff --git a/lego/sys/redis/single/hash.go b/lego/sys/redis/single/hash.go new file mode 100644 index 000000000..91204da1c --- /dev/null +++ b/lego/sys/redis/single/hash.go @@ -0,0 +1,170 @@ +package single + +import ( + "reflect" + + "github.com/go-redis/redis/v8" +) + +/* +Redis Hdel 命令用于删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略 +*/ +func (this *Redis) HDel(key string, fields ...string) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "HDEL") + agrs = append(agrs, key) + for _, v := range fields { + agrs = append(agrs, v) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Hexists 命令用于查看哈希表的指定字段是否存在 +*/ +func (this *Redis) HExists(key string, field string) (result bool, err error) { + result, err = this.client.Do(this.getContext(), "HEXISTS", key, field).Bool() + return +} + +/* +Redis Hget 命令用于返回哈希表中指定字段的值 +*/ +func (this *Redis) HGet(key string, field string, value interface{}) (err error) { + var resultvalue string + if resultvalue = this.client.Do(this.getContext(), "HSET", key, field).String(); resultvalue != string(redis.Nil) { + err = this.Decode([]byte(resultvalue), value) + } + return +} + +/* +Redis Hgetall 命令用于返回哈希表中,所有的字段和值。 +在返回值里,紧跟每个字段名(field name)之后是字段的值(value),所以返回值的长度是哈希表大小的两倍 +*/ +func (this *Redis) HGetAll(key string, valuetype reflect.Type) (result []interface{}, err error) { + cmd := redis.NewStringSliceCmd(this.getContext(), "HGETALL", key) + this.client.Process(this.getContext(), cmd) + var _result []string + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Hincrby 命令用于为哈希表中的字段值加上指定增量值。 +增量也可以为负数,相当于对指定字段进行减法操作。 +如果哈希表的 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 +如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 。 +对一个储存字符串值的字段执行 HINCRBY 命令将造成一个错误。 +本操作的值被限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) HIncrBy(key string, field string, value int) (err error) { + err = this.client.Do(this.getContext(), "HINCRBY", key, field, value).Err() + return +} + +/* +Redis Hincrbyfloat 命令用于为哈希表中的字段值加上指定浮点数增量值。 +如果指定的字段不存在,那么在执行命令前,字段的值被初始化为 0 +*/ +func (this *Redis) HIncrByFloat(key string, field string, value float32) (err error) { + err = this.client.Do(this.getContext(), "HINCRBYFLOAT", key, field, value).Err() + return +} + +/* +Redis Hkeys 命令用于获取哈希表中的所有域(field) +*/ +func (this *Redis) Hkeys(key string) (result []string, err error) { + cmd := redis.NewStringSliceCmd(this.getContext(), "HKEYS", key) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} + +/* +Redis Hlen 命令用于获取哈希表中字段的数量 +*/ +func (this *Redis) Hlen(key string) (result int, err error) { + result, err = this.client.Do(this.getContext(), "HLEN", key).Int() + return +} + +/* +Redis Hmget 命令用于返回哈希表中,一个或多个给定字段的值。 +如果指定的字段不存在于哈希表,那么返回一个 nil 值 +*/ +func (this *Redis) HMGet(key string, valuetype reflect.Type, fields ...string) (result []interface{}, err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "HMGET") + agrs = append(agrs, key) + for _, v := range fields { + agrs = append(agrs, v) + } + cmd := redis.NewStringSliceCmd(this.getContext(), agrs...) + this.client.Process(this.getContext(), cmd) + var _result []string + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Hmset 命令用于同时将多个 field-value (字段-值)对设置到哈希表中。 +此命令会覆盖哈希表中已存在的字段。 +如果哈希表不存在,会创建一个空哈希表,并执行 HMSET 操作 +*/ +func (this *Redis) HMSet(key string, value map[string]interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "HMSET") + agrs = append(agrs, key) + for k, v := range value { + result, _ := this.Encode(v) + agrs = append(agrs, k, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Hset 命令用于为哈希表中的字段赋值 +如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作 +如果字段已经存在于哈希表中,旧值将被覆盖 +*/ +func (this *Redis) HSet(key string, field string, value interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "HSET", key, field, resultvalue).Err() + } + return +} + +/* +Redis Hsetnx 命令用于为哈希表中不存在的的字段赋值 +如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作 +如果字段已经存在于哈希表中,操作无效 +如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令 +*/ +func (this *Redis) HSetNX(key string, field string, value interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "HSETNX", key, field, resultvalue).Err() + } + return +} diff --git a/lego/sys/redis/single/key.go b/lego/sys/redis/single/key.go new file mode 100644 index 000000000..8d6b46ec5 --- /dev/null +++ b/lego/sys/redis/single/key.go @@ -0,0 +1,87 @@ +package single + +import ( + "github.com/go-redis/redis/v8" +) + +/* Key *******************************************************************************/ + +///删除redis key +func (this *Redis) Delete(key string) (err error) { + err = this.client.Do(this.getContext(), "DEL", key).Err() + return +} + +///判断是否存在key +func (this *Redis) ExistsKey(key string) (iskeep bool, err error) { + iskeep, err = this.client.Do(this.getContext(), "EXISTS", key).Bool() + return +} + +///设置key的过期时间 单位以秒级 +func (this *Redis) ExpireKey(key string, expire int) (err error) { + err = this.client.Do(this.getContext(), "EXPIRE", key, expire).Err() + return +} + +///设置key的过期时间戳 秒级时间戳 +func (this *Redis) ExpireatKey(key string, expire_unix int64) (err error) { + err = this.client.Do(this.getContext(), "EXPIREAT", key, expire_unix).Err() + return +} + +///设置key的过期时间 单位以毫秒级 +func (this *Redis) Pexpirekey(key string, expire int) (err error) { + err = this.client.Do(this.getContext(), "PEXPIRE", key, expire).Err() + return +} + +///设置key的过期时间戳 单位以豪秒级 +func (this *Redis) PexpireatKey(key string, expire_unix int64) (err error) { + err = this.client.Do(this.getContext(), "PEXPIREAT", key, expire_unix).Err() + return +} + +///移除Key的过期时间 +func (this *Redis) PersistKey(key string) (err error) { + err = this.client.Do(this.getContext(), "PERSIST", key).Err() + return +} + +///获取key剩余过期时间 单位毫秒 +func (this *Redis) PttlKey(key string) (leftexpire int64, err error) { + leftexpire, err = this.client.Do(this.getContext(), "PTTL", key).Int64() + return +} + +///获取key剩余过期时间 单位秒 +func (this *Redis) TtlKey(key string) (leftexpire int64, err error) { + leftexpire, err = this.client.Do(this.getContext(), "TTL", key).Int64() + return +} + +///重命名Key +func (this *Redis) RenameKye(oldkey string, newkey string) (err error) { + err = this.client.Do(this.getContext(), "RENAME", oldkey, newkey).Err() + return +} + +///重命名key 在新的 key 不存在时修改 key 的名称 +func (this *Redis) RenamenxKey(oldkey string, newkey string) (err error) { + err = this.client.Do(this.getContext(), "RENAMENX", oldkey, newkey).Err() + return +} + +///判断是否存在key pattern:key* +func (this *Redis) Keys(pattern string) (keys []string, err error) { + cmd := redis.NewStringSliceCmd(this.getContext(), "KEYS", string(pattern)) + this.client.Process(this.getContext(), cmd) + keys, err = cmd.Result() + return +} + +///获取键类型 +func (this *Redis) Type(key string) (ty string, err error) { + ty, err = this.client.Type(this.getContext(), key).Result() + return +} diff --git a/lego/sys/redis/single/list.go b/lego/sys/redis/single/list.go new file mode 100644 index 000000000..18a0011d8 --- /dev/null +++ b/lego/sys/redis/single/list.go @@ -0,0 +1,205 @@ +package single + +import ( + "fmt" + "reflect" + + "github.com/go-redis/redis/v8" +) + +/* +Redis Lindex 命令用于通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 +*/ +func (this *Redis) Lindex(key string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "LINDEX", key).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Linsert 命令用于在列表的元素前或者后插入元素。当指定元素不存在于列表中时,不执行任何操作。 +当列表不存在时,被视为空列表,不执行任何操作。 +如果 key 不是列表类型,返回一个错误 +*/ +func (this *Redis) Linsert(key string, isbefore bool, tager interface{}, value interface{}) (err error) { + var ( + tagervalue []byte + resultvalue []byte + ) + if tagervalue, err = this.Encode(tager); err == nil { + if resultvalue, err = this.Encode(value); err == nil { + if isbefore { + err = this.client.Do(this.getContext(), "LINSERT", key, "BEFORE", tagervalue, resultvalue).Err() + } else { + err = this.client.Do(this.getContext(), "LINSERT", key, "AFTER", tagervalue, resultvalue).Err() + } + } + } + return +} + +/* +Redis Llen 命令用于返回列表的长度。 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 。 如果 key 不是列表类型,返回一个错误 +*/ +func (this *Redis) Llen(key string) (result int, err error) { + result, err = this.client.Do(this.getContext(), "LLEN", key).Int() + return +} + +/* +Redis Lpop 命令用于移除并返回列表的第一个元素 +*/ +func (this *Redis) LPop(key string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "LPOP", key).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Lpush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误 +*/ +func (this *Redis) LPush(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "LPUSH") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Lpushx 将一个值插入到已存在的列表头部,列表不存在时操作无效 +*/ +func (this *Redis) LPushX(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "LPUSHX") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素, +以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 +*/ +func (this *Redis) LRange(key string, start, end int, valuetype reflect.Type) (result []interface{}, err error) { + var _result []string + cmd := redis.NewStringSliceCmd(this.getContext(), "LRANGE", key, start, end) + this.client.Process(this.getContext(), cmd) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。 +COUNT 的值可以是以下几种: +count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。 +count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。 +count = 0 : 移除表中所有与 VALUE 相等的值 +*/ +func (this *Redis) LRem(key string, count int, target interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(target); err == nil { + err = this.client.Do(this.getContext(), "LREM", key, count, resultvalue).Err() + } + return +} + +/* +Redis Lset 通过索引来设置元素的值。 +当索引参数超出范围,或对一个空列表进行 LSET 时,返回一个错误 +*/ +func (this *Redis) LSet(key string, index int, value interface{}) (err error) { + var resultvalue []byte + if resultvalue, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "LSET", key, index, resultvalue).Err() + } + return +} + +/* +Redis Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 +下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标, +以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推 +*/ +func (this *Redis) Ltrim(key string, start, stop int) (err error) { + err = this.client.Do(this.getContext(), "LTRIM", key, start, stop).Err() + return +} + +/* +Redis Rpop 命令用于移除列表的最后一个元素,返回值为移除的元素 +*/ +func (this *Redis) Rpop(key string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "RPOP", key).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Rpoplpush 命令用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回 +*/ +func (this *Redis) RPopLPush(oldkey string, newkey string, value interface{}) (err error) { + var data string + if data = this.client.Do(this.getContext(), "RPOPLPUSH", oldkey, newkey).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), value) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + return +} + +/* +Redis Rpush 命令用于将一个或多个值插入到列表的尾部(最右边)。 +如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。 +注意:在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值 +*/ +func (this *Redis) RPush(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "RPUSH") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Rpushx 命令用于将一个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效 +*/ +func (this *Redis) RPushX(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "RPUSHX") + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} diff --git a/lego/sys/redis/single/set.go b/lego/sys/redis/single/set.go new file mode 100644 index 000000000..2e8c86d43 --- /dev/null +++ b/lego/sys/redis/single/set.go @@ -0,0 +1,185 @@ +package single + +import "reflect" + +/* +Redis Sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。 +假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合。 +当集合 key 不是集合类型时,返回一个错误。 +*/ +func (this *Redis) SAdd(key string, values ...interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "SADD") + agrs = append(agrs, key) + for _, v := range values { + result, _ := this.Encode(v) + agrs = append(agrs, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Scard 命令返回集合中元素的数量 +*/ +func (this *Redis) SCard(key string) (result int64, err error) { + result, err = this.client.SCard(this.getContext(), key).Result() + return +} + +/* +Redis Sdiff 命令返回第一个集合与其他集合之间的差异,也可以认为说第一个集合中独有的元素。不存在的集合 key 将视为空集。 +差集的结果来自前面的 FIRST_KEY ,而不是后面的 OTHER_KEY1,也不是整个 FIRST_KEY OTHER_KEY1..OTHER_KEYN 的差集。 +实例: +*/ +func (this *Redis) SDiff(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SDiff(this.getContext(), keys...) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Sdiffstore 命令将给定集合之间的差集合存储在指定的集合中。 +*/ +func (this *Redis) SDiffStore(destination string, keys ...string) (result int64, err error) { + result, err = this.client.SDiffStore(this.getContext(), destination, keys...).Result() + return +} + +/* +Redis Sismember 命令返回给定所有给定集合的交集。 不存在的集合 key 被视为空集。 当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。 +*/ +func (this *Redis) SInter(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SInter(this.getContext(), keys...) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Sinterstore 决定将给定集合之间的交集在指定的集合中。如果指定的集合已经存在,则将其覆盖 +*/ +func (this *Redis) SInterStore(destination string, keys ...string) (result int64, err error) { + result, err = this.client.SInterStore(this.getContext(), destination, keys...).Result() + return +} + +/* +Redis Sismember 命令判断成员元素是否是集合的成员 +*/ +func (this *Redis) Sismember(key string, value interface{}) (iskeep bool, err error) { + iskeep, err = this.client.SIsMember(this.getContext(), key, value).Result() + return +} + +/* +Redis Smembers 号召返回集合中的所有成员。 +*/ +func (this *Redis) SMembers(valuetype reflect.Type, key string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SMembers(this.getContext(), key) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Smove 命令将指定成员 member 元素从 source 集合移动到 destination 集合。 +SMOVE 是原子性操作。 +如果 source 集合不存在或不包含指定的 member 元素,则 SMOVE 命令不执行任何操作,仅返回 0 。否则, member 元素从 source 集合中被移除,并添加到 destination 集合中去。 +当 destination 集合已经包含 member 元素时, SMOVE 命令只是简单地将 source 集合中的 member 元素删除。 +当 source 或 destination 不是集合类型时,返回一个错误。 +*/ +func (this *Redis) SMove(source string, destination string, member interface{}) (result bool, err error) { + result, err = this.client.SMove(this.getContext(), source, destination, member).Result() + return +} + +/* +Redis Spop命令用于移除集合中的指定键的一个或多个随机元素,移除后会返回移除的元素。 +该命令类似于Srandmember命令,但SPOP将随机元素从集合中移除并返回,而Srandmember则返回元素,而不是对集合进行任何改动。 +*/ +func (this *Redis) Spop(key string) (result string, err error) { + result, err = this.client.SPop(this.getContext(), key).Result() + return +} + +/* +Redis Srandmember 命令用于返回集合中的一个随机元素。 +从 Redis 2.6 版本开始, Srandmember 命令接受可选的 count 参数: +如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。 +如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 +该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 Srandmember 则仅仅返回随机元素,而不对集合进行任何改动。 +*/ +func (this *Redis) Srandmember(key string) (result string, err error) { + result, err = this.client.SRandMember(this.getContext(), key).Result() + return +} + +/* +Redis Srem 呼吁用于移除集合中的一个或多个元素元素,不存在的元素元素会被忽略。 +当键不是集合类型,返回一个错误。 +在 Redis 2.4 版本以前,SREM 只接受个别成员值。 +*/ +func (this *Redis) SRem(key string, members ...interface{}) (result int64, err error) { + result, err = this.client.SRem(this.getContext(), key, members...).Result() + return +} + +/* +Redis Sunion 命令返回给定集合的并集。 +*/ +func (this *Redis) SUnion(valuetype reflect.Type, keys ...string) (result []interface{}, err error) { + var _result []string + cmd := this.client.SUnion(this.getContext(), keys...) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis Sunionstore 命令将给定集合的并集存储在指定的集合 destination 中。如果 destination 已经存在,则将其覆盖。 +*/ +func (this *Redis) Sunionstore(destination string, keys ...string) (result int64, err error) { + result, err = this.client.SUnionStore(this.getContext(), destination, keys...).Result() + return +} + +/* +Redis Sscan 用于继承集合中键的元素,Sscan 继承自Scan。 +*/ +func (this *Redis) Sscan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + keys, cursor, err = this.client.SScan(this.getContext(), key, _cursor, match, count).Result() + return +} diff --git a/lego/sys/redis/single/string.go b/lego/sys/redis/single/string.go new file mode 100644 index 000000000..468d6026b --- /dev/null +++ b/lego/sys/redis/single/string.go @@ -0,0 +1,180 @@ +package single + +import ( + "fmt" + "time" + + "github.com/go-redis/redis/v8" +) + +/* String *******************************************************************************/ +/* +命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型。 +*/ +func (this *Redis) Set(key string, value interface{}, expiration time.Duration) (err error) { + var result []byte + if result, err = this.Encode(value); err == nil { + err = this.client.Set(this.getContext(), string(key), result, expiration).Err() + } + return +} + +/* +指定的 key 不存在时,为 key 设置指定的值 +*/ +func (this *Redis) SetNX(key string, value interface{}) (result int64, err error) { + // var _value []byte + // if result, err = this.Encode(value); err == nil { + // err = this.client.Do(this.getContext(), "SETNX", key, result).Err() + cmd := redis.NewIntCmd(this.getContext(), "SETNX", key, value) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + // } + return +} + +/* +同时设置一个或多个 key-value 对 +*/ +func (this *Redis) MSet(keyvalues map[string]interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "MSET") + for k, v := range keyvalues { + result, _ := this.Encode(v) + agrs = append(agrs, k, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +命令用于所有给定 key 都不存在时,同时设置一个或多个 key-value 对 +*/ +func (this *Redis) MSetNX(keyvalues map[string]interface{}) (err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "MSETNX") + for k, v := range keyvalues { + result, _ := this.Encode(v) + agrs = append(agrs, k, result) + } + err = this.client.Do(this.getContext(), agrs...).Err() + return +} + +/* +Redis Incr 命令将 key 中储存的数字值增一。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内。 +*/ +func (this *Redis) Incr(key string) (err error) { + err = this.client.Do(this.getContext(), "INCR", key).Err() + return +} + +/* +Redis Incrby 命令将 key 中储存的数字加上指定的增量值。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) IncrBY(key string, value int) (err error) { + err = this.client.Do(this.getContext(), "INCRBY", key, value).Err() + return +} + +/* +Redis Incrbyfloat 命令为 key 中所储存的值加上指定的浮点数增量值。 +如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,再执行加法操作 +*/ +func (this *Redis) Incrbyfloat(key string, value float32) (err error) { + err = this.client.Do(this.getContext(), "INCRBYFLOAT", key, value).Err() + return +} + +/* +Redis Decr 命令将 key 中储存的数字值减一。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) Decr(key string, value int) (err error) { + err = this.client.Do(this.getContext(), "DECR", key, value).Err() + return +} + +/* +Redis Decrby 命令将 key 所储存的值减去指定的减量值。 +如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。 +如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 +本操作的值限制在 64 位(bit)有符号数字表示之内 +*/ +func (this *Redis) DecrBy(key string, value int) (err error) { + err = this.client.Do(this.getContext(), "DECRBY", key, value).Err() + return +} + +/* +Redis Append 命令用于为指定的 key 追加值。 +如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 +如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。 +*/ +func (this *Redis) Append(key string, value interface{}) (err error) { + var result []byte + if result, err = this.Encode(value); err == nil { + err = this.client.Do(this.getContext(), "APPEND", key, result).Err() + } + return +} + +/* +命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型 +*/ +func (this *Redis) Get(key string, value interface{}) (err error) { + var result []byte + if result, err = this.client.Get(this.getContext(), key).Bytes(); err == nil { + err = this.Decode(result, value) + } + return +} + +/* +设置指定 key 的值,并返回 key 的旧值 +*/ +func (this *Redis) GetSet(key string, value interface{}, result interface{}) (err error) { + var ( + data string + _value []byte + ) + if _value, err = this.Encode(value); err == nil { + if data = this.client.Do(this.getContext(), "GETSET", key, _value).String(); data != string(redis.Nil) { + err = this.Decode([]byte(data), result) + } else { + err = fmt.Errorf(string(redis.Nil)) + } + } + return +} + +/* +返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil +*/ +func (this *Redis) MGet(keys ...string) (result []string, err error) { + agrs := make([]interface{}, 0) + agrs = append(agrs, "MGET") + for _, v := range keys { + agrs = append(agrs, v) + } + cmd := redis.NewStringSliceCmd(this.getContext(), agrs...) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} + +///判断是否存在key pattern:key* +func (this *Redis) INCRBY(key string, amount int64) (result int64, err error) { + cmd := redis.NewIntCmd(this.getContext(), "INCRBY", key, amount) + this.client.Process(this.getContext(), cmd) + result, err = cmd.Result() + return +} diff --git a/lego/sys/redis/single/zset.go b/lego/sys/redis/single/zset.go new file mode 100644 index 000000000..cadf0f13e --- /dev/null +++ b/lego/sys/redis/single/zset.go @@ -0,0 +1,217 @@ +package single + +import ( + "reflect" + + "github.com/go-redis/redis/v8" +) + +/* +Redis ZAdd 向有序集合添加一个或多个成员,或者更新已存在成员的分数 +*/ +func (this *Redis) ZAdd(key string, members ...*redis.Z) (err error) { + this.client.ZAdd(this.getContext(), key, members...) + return +} + +/* +Redis Zcard 用于计算集合中元素的数量。 +*/ +func (this *Redis) ZCard(key string) (result int64, err error) { + result, err = this.client.ZCard(this.getContext(), key).Result() + return +} + +/* +Redis ZCount 用于计算集合中指定的范围内的数量 +*/ +func (this *Redis) ZCount(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZCount(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZIncrBy 有序集合中对指定成员的分数加上增量 increment +*/ +func (this *Redis) ZIncrBy(key string, increment float64, member string) (result float64, err error) { + result, err = this.client.ZIncrBy(this.getContext(), key, increment, member).Result() + return +} + +/* +Redis ZInterStore 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中 +*/ +func (this *Redis) ZInterStore(destination string, store *redis.ZStore) (result int64, err error) { + result, err = this.client.ZInterStore(this.getContext(), destination, store).Result() + return +} + +/* +Redis ZLexCount 在有序集合中计算指定字典区间内成员数量 +*/ +func (this *Redis) ZLexCount(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZLexCount(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZRange 通过索引区间返回有序集合指定区间内的成员 +*/ +func (this *Redis) ZRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRange(this.getContext(), key, start, stop) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRangeByLex 通过字典区间返回有序集合的成员 +*/ +func (this *Redis) ZRangeByLex(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRangeByLex(this.getContext(), key, opt) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRangeByScore 通过分数返回有序集合指定区间内的成员 +*/ +func (this *Redis) ZRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRangeByScore(this.getContext(), key, opt) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRank 返回有序集合中指定成员的索引 +*/ +func (this *Redis) ZRank(key string, member string) (result int64, err error) { + result, err = this.client.ZRank(this.getContext(), key, member).Result() + return +} + +/* +Redis ZRem 移除有序集合中的一个或多个成员 +*/ +func (this *Redis) ZRem(key string, members ...interface{}) (result int64, err error) { + result, err = this.client.ZRem(this.getContext(), key, members...).Result() + return +} + +/* +Redis ZRemRangeByLex 移除有序集合中给定的字典区间的所有成员 +*/ +func (this *Redis) ZRemRangeByLex(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZRemRangeByLex(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZRemRangeByRank 移除有序集合中给定的排名区间的所有成员 +*/ +func (this *Redis) ZRemRangeByRank(key string, start int64, stop int64) (result int64, err error) { + result, err = this.client.ZRemRangeByRank(this.getContext(), key, start, stop).Result() + return +} + +/* +Redis ZRemRangeByScore 移除有序集合中给定的分数区间的所有成员 +*/ +func (this *Redis) ZRemRangeByScore(key string, min string, max string) (result int64, err error) { + result, err = this.client.ZRemRangeByScore(this.getContext(), key, min, max).Result() + return +} + +/* +Redis ZRevRange 返回有序集中指定区间内的成员,通过索引,分数从高到低 ZREVRANGE +*/ +func (this *Redis) ZRevRange(valuetype reflect.Type, key string, start int64, stop int64) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRevRange(this.getContext(), key, start, stop) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRevRangeByScore 返回有序集中指定分数区间内的成员,分数从高到低排序 +*/ +func (this *Redis) ZRevRangeByScore(valuetype reflect.Type, key string, opt *redis.ZRangeBy) (result []interface{}, err error) { + var _result []string + cmd := this.client.ZRevRangeByScore(this.getContext(), key, opt) + if _result, err = cmd.Result(); err == nil { + result = make([]interface{}, len(_result)) + for i, v := range _result { + temp := reflect.New(valuetype.Elem()).Interface() + if err = this.Decode([]byte(v), &temp); err == nil { + result[i] = temp + } + } + } + return +} + +/* +Redis ZRevRank 返回有序集中指定分数区间内的成员,分数从高到低排序 +*/ +func (this *Redis) ZRevRank(key string, member string) (result int64, err error) { + result, err = this.client.ZRevRank(this.getContext(), key, member).Result() + return +} + +/* +Redis ZScore 返回有序集中指定分数区间内的成员,分数从高到低排序 +*/ +func (this *Redis) ZScore(key string, member string) (result float64, err error) { + result, err = this.client.ZScore(this.getContext(), key, member).Result() + return +} + +/* +Redis ZScore 返回有序集中指定分数区间内的成员,分数从高到低排序 ZUNIONSTORE +*/ +func (this *Redis) ZUnionStore(dest string, store *redis.ZStore) (result int64, err error) { + result, err = this.client.ZUnionStore(this.getContext(), dest, store).Result() + return +} + +/* +Redis ZScan 迭代有序集合中的元素(包括元素成员和元素分值) +*/ +func (this *Redis) ZScan(key string, _cursor uint64, match string, count int64) (keys []string, cursor uint64, err error) { + keys, cursor, err = this.client.ZScan(this.getContext(), key, _cursor, match, count).Result() + return +} diff --git a/lego/sys/redis/sys_test.go b/lego/sys/redis/sys_test.go new file mode 100644 index 000000000..7d924ce30 --- /dev/null +++ b/lego/sys/redis/sys_test.go @@ -0,0 +1,143 @@ +package redis_test + +import ( + "encoding/json" + "fmt" + "sync" + "testing" + "time" + + "go_dreamfactory/lego/sys/redis" +) + +func Test_SysIPV6(t *testing.T) { + err := redis.OnInit(map[string]interface{}{ + "Redis_Single_Addr": "172.27.100.143:6382", + "Redis_Single_DB": 0, + "Redis_Single_Password": "idss@sjzt", + }) + if err != nil { + fmt.Printf("Redis:err:%v \n", err) + return + } + fmt.Printf("Redis:succ \n") + if err = redis.Set("liwei1dao", 123, -1); err != nil { + fmt.Printf("Redis:err:%v \n", err) + } +} + +func Test_Redis_ExpireatKey(t *testing.T) { + err := redis.OnInit(map[string]interface{}{ + "Redis_Single_Addr": "172.20.27.145:10001", + "Redis_Single_DB": 0, + "Redis_Single_Password": "li13451234", + }) + if err != nil { + fmt.Printf("Redis:err:%v \n", err) + return + } + fmt.Printf("Redis:succ \n") + if err = redis.Set("liwei1dao", 123, -1); err != nil { + fmt.Printf("Redis:err:%v \n", err) + } + if err = redis.ExpireKey("liwei1dao", 120); err != nil { + fmt.Printf("Redis:err:%v \n", err) + } + fmt.Printf("Redis:end \n") +} + +func Test_JsonMarshal(t *testing.T) { + result, _ := json.Marshal(100) + fmt.Printf("结果%s \n", string(result)) +} + +func Test_Redis_SetNX(t *testing.T) { + err := redis.OnInit(map[string]interface{}{ + "Redis_Single_Addr": "172.20.27.145:10001", + "RedisDB": 0, + "Redis_Single_Password": "li13451234", + }) + if err != nil { + fmt.Printf("Redis:err:%v \n", err) + return + } + + wg := new(sync.WaitGroup) + wg.Add(20) + for i := 0; i < 20; i++ { + go func(index int) { + result, err := redis.SetNX("liwei1dao", index) + fmt.Printf("Redis index:%d result:%d err:%v \n", index, result, err) + wg.Done() + }(i) + } + wg.Wait() + fmt.Printf("Redis:end \n") +} +func Test_Redis_Lock(t *testing.T) { + err := redis.OnInit(map[string]interface{}{ + "Redis_Single_Addr": "172.20.27.145:10001", + "Redis_Single_DB": 0, + "Redis_Single_Password": "li13451234", + }) + if err != nil { + fmt.Printf("Redis:err:%v \n", err) + return + } + result, err := redis.Lock("liwei2dao", 100000) + fmt.Printf("Redis result:%v err:%v \n", result, err) +} + +func Test_Redis_Mutex(t *testing.T) { + err := redis.OnInit(map[string]interface{}{ + "Redis_Single_Addr": "172.20.27.145:10001", + "Redis_Single_DB": 0, + "Redis_Single_Password": "li13451234", + }) + if err != nil { + fmt.Printf("Redis:err:%v \n", err) + return + } + + wg := new(sync.WaitGroup) + wg.Add(20) + for i := 0; i < 20; i++ { + go func(index int) { + if lock, err := redis.NewRedisMutex("liwei1dao_lock"); err != nil { + fmt.Printf("NewRedisMutex index:%d err:%v\n", index, err) + } else { + fmt.Printf("Lock 0 index:%d time:%s\n", index, time.Now().Format("2006/01/02 15:04:05 000")) + err = lock.Lock() + fmt.Printf("Lock 1 index:%d time:%s err:%v \n", index, time.Now().Format("2006/01/02 15:04:05 000"), err) + value := 0 + redis.Get("liwei1dao", &value) + redis.Set("liwei1dao", value+1, -1) + lock.Unlock() + fmt.Printf("Lock 2 index:%d time:%s\n", index, time.Now().Format("2006/01/02 15:04:05 000")) + } + wg.Done() + }(i) + } + wg.Wait() + fmt.Printf("Redis:end \n") +} + +func Test_Redis_Type(t *testing.T) { + err := redis.OnInit(map[string]interface{}{ + "Redis_Single_Addr": "172.20.27.145:10001", + "Redis_Single_DB": 1, + "Redis_Single_Password": "li13451234", + }) + if err != nil { + fmt.Printf("Redis:err:%v \n", err) + return + } + fmt.Printf("Redis:succ \n") + + if ty, err := redis.Type("test_set"); err != nil { + fmt.Printf("Test_Redis_Type:err:%v \n", err) + } else { + fmt.Printf("Test_Redis_Type:%s \n", ty) + } + +} diff --git a/lego/sys/redis/utils.go b/lego/sys/redis/utils.go new file mode 100644 index 000000000..65a229e12 --- /dev/null +++ b/lego/sys/redis/utils.go @@ -0,0 +1 @@ +package redis diff --git a/lego/sys/registry/consul.go b/lego/sys/registry/consul.go new file mode 100644 index 000000000..9b0dca305 --- /dev/null +++ b/lego/sys/registry/consul.go @@ -0,0 +1,391 @@ +package registry + +import ( + "fmt" + "strconv" + "sync" + "time" + + hash "github.com/mitchellh/hashstructure" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" + + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/api/watch" +) + +func newConsul(options Options) (sys *Consul_Registry, err error) { + sys = &Consul_Registry{ + options: options, + } + config := api.DefaultConfig() + if len(options.Consul_Addr) > 0 { + config.Address = options.Consul_Addr + } + if sys.client, err = api.NewClient(config); err != nil { + return nil, err + } + if sys.snodeWP, err = watch.Parse(map[string]interface{}{"type": "services"}); err != nil { + return nil, err + } + sys.snodeWP.Handler = sys.shandler + sys.options.Consul_Addr = config.Address + sys.shash = make(map[string]uint64) + sys.services = make(map[string]*ServiceNode) + sys.rpcsubs = make(map[core.Rpc_Key][]*ServiceNode) + sys.watchers = make(map[string]*watch.Plan) + sys.closeSig = make(chan bool) + sys.isstart = false + return +} + +type Consul_Registry struct { + options Options + client *api.Client + snodeWP *watch.Plan + isstart bool + closeSig chan bool + shash map[string]uint64 + slock sync.RWMutex + services map[string]*ServiceNode + rlock sync.RWMutex + rpcsubs map[core.Rpc_Key][]*ServiceNode + watchers map[string]*watch.Plan +} + +func (this *Consul_Registry) Start() (err error) { + if err = this.getServices(); err != nil { + return + } + if err = this.registerSNode(&ServiceNode{ + Tag: this.options.Service.GetTag(), + Id: this.options.Service.GetId(), + IP: this.options.Service.GetIp(), + Port: this.options.Service.GetPort(), + Type: this.options.Service.GetType(), + Category: this.options.Service.GetCategory(), + Version: this.options.Service.GetVersion(), + RpcId: this.options.Service.GetRpcId(), + PreWeight: this.options.Service.GetPreWeight(), + }); err != nil { + return + } + go this.snodeWP.Run(this.options.Consul_Addr) + go this.run() + this.isstart = true + return +} + +func (this *Consul_Registry) Stop() (err error) { + this.closeSig <- true + this.isstart = false + this.snodeWP.Stop() + this.deregisterSNode() + return +} +func (this *Consul_Registry) PushServiceInfo() (err error) { + if this.isstart { + err = this.registerSNode(&ServiceNode{ + Tag: this.options.Service.GetTag(), + Id: this.options.Service.GetId(), + IP: this.options.Service.GetIp(), + Port: this.options.Service.GetPort(), + Type: this.options.Service.GetType(), + Category: this.options.Service.GetCategory(), + Version: this.options.Service.GetVersion(), + RpcId: this.options.Service.GetRpcId(), + PreWeight: this.options.Service.GetPreWeight(), + }) + } + return +} +func (this *Consul_Registry) GetServiceById(sId string) (n *ServiceNode, err error) { + this.slock.RLock() + n, ok := this.services[sId] + this.slock.RUnlock() + if !ok { + return nil, fmt.Errorf("No Found %s", sId) + } + return +} +func (this *Consul_Registry) GetServiceByType(sType string) (n []*ServiceNode) { + this.slock.RLock() + defer this.slock.RUnlock() + n = make([]*ServiceNode, 0) + for _, v := range this.services { + if v.Type == sType { + n = append(n, v) + } + } + return +} +func (this *Consul_Registry) GetAllServices() (n []*ServiceNode) { + this.slock.RLock() + defer this.slock.RUnlock() + n = make([]*ServiceNode, 0) + for _, v := range this.services { + n = append(n, v) + } + return +} +func (this *Consul_Registry) GetServiceByCategory(category core.S_Category) (n []*ServiceNode) { + this.slock.RLock() + defer this.slock.RUnlock() + n = make([]*ServiceNode, 0) + for _, v := range this.services { + if v.Category == category { + n = append(n, v) + } + } + return +} +func (this *Consul_Registry) GetRpcSubById(rId core.Rpc_Key) (n []*ServiceNode) { + n = make([]*ServiceNode, 0) + this.rlock.RLock() + d, ok := this.rpcsubs[rId] + this.rlock.RUnlock() + if ok { + n = d + } + return +} + +//内部函数------------------------------------------------------------------------------------------------------------------------- +func (this *Consul_Registry) run() { + t := time.NewTicker(time.Duration(this.options.Consul_RegisterInterval) * time.Second) +locp: + for { + select { + case <-t.C: + err := this.PushServiceInfo() + if err != nil { + log.Warnf("service run Server.Register error: ", err) + } + case <-this.closeSig: + break locp + } + } + log.Infof("Sys Registry is succ exit") +} + +func (this *Consul_Registry) getServices() (err error) { + services, err := this.client.Agent().Services() + if err == nil { + for _, v := range services { + if v.Tags[0] == this.options.Service.GetTag() { //自能读取相同标签的服务 + this.addandupdataServiceNode(v) + } + } + } + return err +} +func (this *Consul_Registry) registerSNode(snode *ServiceNode) (err error) { + h, err := hash.Hash(snode, nil) + if err != nil { + return err + } + + if v, ok := this.shash[snode.Id]; ok && v != 0 && v == h { + if err := this.client.Agent().PassTTL("service:"+snode.Id, ""); err == nil { + return nil + } + } + deregTTL := this.getDeregisterTTL(time.Second * time.Duration(this.options.Consul_RegisterTTL)) + check := &api.AgentServiceCheck{ + TTL: fmt.Sprintf("%v", time.Second*time.Duration(this.options.Consul_RegisterTTL)), + DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL), + } + asr := &api.AgentServiceRegistration{ + ID: snode.Id, + Name: snode.Type, + Tags: []string{this.options.Service.GetTag()}, + Check: check, + Meta: map[string]string{ + "tag": snode.Tag, + "ip": snode.IP, + "port": fmt.Sprintf("%d", snode.Port), + "category": string(snode.Category), + "version": snode.Version, + "rpcid": snode.RpcId, + "preweight": fmt.Sprintf("%f", snode.PreWeight), + }, + } + if err := this.client.Agent().ServiceRegister(asr); err != nil { + return err + } + return this.client.Agent().PassTTL("service:"+snode.Id, "") +} +func (this *Consul_Registry) deregisterSNode() (err error) { + return this.client.Agent().ServiceDeregister(this.options.Service.GetId()) +} + +func (this *Consul_Registry) addandupdataServiceNode(as *api.AgentService) (sn *ServiceNode, err error) { + + tag := as.Meta["tag"] + ip := as.Meta["ip"] + port, err := strconv.ParseInt(as.Meta["port"], 10, 64) + if err != nil { + log.Errorf("port 读取服务节点异常:%s err:%v", as.Meta["port"], err) + return + } + category := as.Meta["category"] + version := as.Meta["version"] + rpcid := as.Meta["rpcid"] + preweight, err := strconv.ParseFloat(as.Meta["preweight"], 64) + if err != nil { + log.Errorf("registry 读取服务节点异常:%s err:%v", as.Meta["preweight"], err) + return + } + + snode := &ServiceNode{ + Tag: tag, + Type: as.Service, + Category: core.S_Category(category), + Id: as.ID, + IP: ip, + Port: int(port), + Version: version, + RpcId: rpcid, + PreWeight: preweight, + } + if h, err := hash.Hash(snode, nil); err == nil { + _, ok := this.services[snode.Id] + if !ok { + log.Infof("发现新的服务【%s:%s】", snode.Id, snode.Version) + this.rlock.Lock() + this.services[snode.Id] = snode + this.shash[snode.Id] = h + this.rlock.Unlock() + if this.options.Listener != nil { //异步通知 + go this.options.Listener.FindServiceHandlefunc(*snode) + } + } else { + this.rlock.RLock() + v, ok := this.shash[snode.Id] + this.rlock.RUnlock() + if !ok || v != h { //校验不一致 + // log.Debugf("更新服务【%s】", snode.Id) + this.rlock.Lock() + this.services[snode.Id] = snode + this.shash[snode.Id] = h + this.rlock.Unlock() + if this.options.Listener != nil { //异步通知 + go this.options.Listener.UpDataServiceHandlefunc(*snode) + } + } + } + } else { + log.Errorf("registry 校验服务 hash 值异常:%s", err.Error()) + } + return +} + +func (this *Consul_Registry) removeServiceNode(sId string) { + this.rlock.RLock() + _, ok := this.services[sId] + this.rlock.RUnlock() + if !ok { + return + } + log.Infof("丢失服务【%s】", sId) + this.rlock.Lock() + delete(this.shash, sId) + delete(this.services, sId) + this.rlock.Unlock() + if this.options.Listener != nil { //异步通知 + go this.options.Listener.LoseServiceHandlefunc(sId) + } +} + +func (this *Consul_Registry) shandler(idx uint64, data interface{}) { + services, ok := data.(map[string][]string) + if !ok { + return + } + for k, v := range services { + if len(v) > 0 { + if v[0] == this.options.Service.GetTag() { + _, ok := this.watchers[k] + if ok { + continue + } + wp, err := watch.Parse(map[string]interface{}{ + "type": "service", + "service": k, + }) + if err == nil { + wp.Handler = this.snodehandler + go wp.Run(this.options.Consul_Addr) + this.watchers[k] = wp + } + } + } + } + temp := make(map[string]*ServiceNode) + this.rlock.RLock() + for k, v := range this.services { + temp[k] = v + } + this.rlock.RUnlock() + for k, v := range this.watchers { + if _, ok := services[k]; !ok { //不存在了 + v.Stop() + delete(this.watchers, k) + for k1, v1 := range temp { + if v1.Type == k { + this.removeServiceNode(k1) + } + } + } + } +} + +func (this *Consul_Registry) snodehandler(idx uint64, data interface{}) { + entries, ok := data.([]*api.ServiceEntry) + if !ok { + return + } + stype := "" + serviceMap := map[string]struct{}{} + for _, v := range entries { + isdele := false + for _, check := range v.Checks { + if check.Status == "critical" { + this.removeServiceNode(v.Service.ID) + isdele = true + break + } + } + if !isdele { + this.addandupdataServiceNode(v.Service) + stype = v.Service.Service + serviceMap[v.Service.ID] = struct{}{} + } + } + + temp := make(map[string]*ServiceNode) + this.rlock.RLock() + for k, v := range this.services { + temp[k] = v + } + this.rlock.RUnlock() + for _, v := range temp { + if v.Type == stype { + if _, ok := serviceMap[v.Id]; !ok { + this.removeServiceNode(v.Id) + } + } + } +} + +func (this *Consul_Registry) getDeregisterTTL(t time.Duration) time.Duration { + // splay slightly for the watcher? + splay := time.Second * 5 + deregTTL := t + splay + // consul has a minimum timeout on deregistration of 1 minute. + if t < time.Minute { + deregTTL = time.Minute + splay + } + return deregTTL +} diff --git a/lego/sys/registry/core.go b/lego/sys/registry/core.go new file mode 100644 index 000000000..d5b2915af --- /dev/null +++ b/lego/sys/registry/core.go @@ -0,0 +1,81 @@ +package registry + +import "go_dreamfactory/lego/core" + +type ( + IListener interface { + FindServiceHandlefunc(snode ServiceNode) + UpDataServiceHandlefunc(snode ServiceNode) + LoseServiceHandlefunc(sId string) + } + ServiceNode struct { + Tag string `json:"Tag"` //服务集群标签 + Type string `json:"Type"` //服务类型 + Category core.S_Category `json:"Category"` //服务列别 + Id string `json:"Id"` //服务Id + Version string `json:"Version"` //服务版本 + IP string `json:"Ip"` //服务Ip + Port int `json:"Port"` //端口 + RpcId string `json:"RpcId"` //服务通信Id + PreWeight float64 `json:"PreWeight"` //服务负载权重 + } + + ISys interface { + Start() error + Stop() error + PushServiceInfo() (err error) + GetServiceById(sId string) (n *ServiceNode, err error) + GetServiceByType(sType string) (n []*ServiceNode) + GetAllServices() (n []*ServiceNode) + GetServiceByCategory(category core.S_Category) (n []*ServiceNode) + GetRpcSubById(rId core.Rpc_Key) (n []*ServiceNode) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Start() error { + return defsys.Start() +} +func Stop() error { + return defsys.Stop() +} + +func PushServiceInfo() (err error) { + return defsys.PushServiceInfo() +} + +func GetServiceById(sId string) (n *ServiceNode, err error) { + return defsys.GetServiceById(sId) +} +func GetServiceByType(sType string) (n []*ServiceNode) { + return defsys.GetServiceByType(sType) +} + +func GetAllServices() (n []*ServiceNode) { + return defsys.GetAllServices() +} + +func GetServiceByCategory(category core.S_Category) (n []*ServiceNode) { + return defsys.GetServiceByCategory(category) +} + +func GetRpcSubById(rId core.Rpc_Key) (n []*ServiceNode) { + return defsys.GetRpcSubById(rId) +} + +func SubscribeRpc(rpc core.Rpc_Key) { + +} diff --git a/lego/sys/registry/nacos.go b/lego/sys/registry/nacos.go new file mode 100644 index 000000000..b453737d5 --- /dev/null +++ b/lego/sys/registry/nacos.go @@ -0,0 +1,413 @@ +package registry + +import ( + "context" + "fmt" + "sync" + "time" + + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" + + hash "github.com/mitchellh/hashstructure" + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + "github.com/nacos-group/nacos-sdk-go/common/constant" + "github.com/nacos-group/nacos-sdk-go/model" + "github.com/nacos-group/nacos-sdk-go/vo" +) + +func newNacos(options Options) (sys *Nacos_Registry, err error) { + ctx, cancel := context.WithCancel(context.TODO()) + sys = &Nacos_Registry{ + options: options, + ctx: ctx, + cancel: cancel, + shash: make(map[string]uint64), + services: make(map[string]*ServiceNode), + subscribe: make(map[string]*vo.SubscribeParam), + rpcsubs: make(map[core.Rpc_Key][]*ServiceNode), + } + // 创建clientConfig + clientConfig := constant.ClientConfig{ + NamespaceId: options.Nacos_NamespaceId, + TimeoutMs: options.Nacos_TimeoutMs, + BeatInterval: options.Nacos_BeatInterval, + Username: options.Nacos_UserName, + Password: options.Nacos_Password, + NotLoadCacheAtStart: true, + UpdateCacheWhenEmpty: true, + LogDir: "nacos/log", + CacheDir: "nacos/cache", + RotateTime: "1h", + MaxAge: 3, + LogLevel: "error", + } + // 至少一个ServerConfig + serverConfigs := []constant.ServerConfig{ + { + IpAddr: options.Nacos_NacosAddr, + ContextPath: "/nacos", + Port: options.Nacos_Port, + Scheme: "http", + }, + } + + // 创建服务发现客户端的另一种方式 (推荐) + sys.client, err = clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, + ) + return +} + +type Nacos_Registry struct { + options Options + client naming_client.INamingClient + isstart bool + ctx context.Context + cancel context.CancelFunc + slock sync.RWMutex + shash map[string]uint64 + subscribe map[string]*vo.SubscribeParam + services map[string]*ServiceNode + rlock sync.RWMutex + rpcsubs map[core.Rpc_Key][]*ServiceNode +} + +func (this *Nacos_Registry) Start() (err error) { + if err = this.getServices(); err != nil && err.Error() != "instance list is empty!" { + return + } + if err = this.registerSNode(&ServiceNode{ + Tag: this.options.Service.GetTag(), + Id: this.options.Service.GetId(), + IP: this.options.Service.GetIp(), + Port: this.options.Service.GetPort(), + Type: this.options.Service.GetType(), + Category: this.options.Service.GetCategory(), + Version: this.options.Service.GetVersion(), + RpcId: this.options.Service.GetRpcId(), + PreWeight: this.options.Service.GetPreWeight(), + }); err != nil { + return + } + ctx1, _ := context.WithCancel(this.ctx) + go this.run(ctx1) + ctx2, _ := context.WithCancel(this.ctx) + go this.listener(ctx2) + this.isstart = true + return +} + +func (this *Nacos_Registry) Stop() (err error) { + this.cancel() + this.isstart = false + return +} + +func (this *Nacos_Registry) PushServiceInfo() (err error) { + if this.isstart { + err = this.registerSNode(&ServiceNode{ + Tag: this.options.Service.GetTag(), + Id: this.options.Service.GetId(), + IP: this.options.Service.GetIp(), + Port: this.options.Service.GetPort(), + Type: this.options.Service.GetType(), + Category: this.options.Service.GetCategory(), + Version: this.options.Service.GetVersion(), + RpcId: this.options.Service.GetRpcId(), + PreWeight: this.options.Service.GetPreWeight(), + }) + } + return +} +func (this *Nacos_Registry) GetServiceById(sId string) (n *ServiceNode, err error) { + this.slock.RLock() + n, ok := this.services[sId] + this.slock.RUnlock() + if !ok { + return nil, fmt.Errorf("No Found %s", sId) + } + return +} +func (this *Nacos_Registry) GetServiceByType(sType string) (n []*ServiceNode) { + this.slock.RLock() + defer this.slock.RUnlock() + n = make([]*ServiceNode, 0) + for _, v := range this.services { + if v.Type == sType { + n = append(n, v) + } + } + return +} +func (this *Nacos_Registry) GetAllServices() (n []*ServiceNode) { + this.slock.RLock() + defer this.slock.RUnlock() + n = make([]*ServiceNode, 0) + for _, v := range this.services { + n = append(n, v) + } + return +} +func (this *Nacos_Registry) GetServiceByCategory(category core.S_Category) (n []*ServiceNode) { + this.slock.RLock() + defer this.slock.RUnlock() + n = make([]*ServiceNode, 0) + for _, v := range this.services { + if v.Category == category { + n = append(n, v) + } + } + return +} +func (this *Nacos_Registry) GetRpcSubById(rId core.Rpc_Key) (n []*ServiceNode) { + n = make([]*ServiceNode, 0) + this.rlock.RLock() + d, ok := this.rpcsubs[rId] + this.rlock.RUnlock() + if ok { + n = d + } + return +} + +//内部函数------------------------------------------------------------------------------------------------------------------------- +func (this *Nacos_Registry) run(ctx context.Context) { + t := time.NewTicker(time.Duration(this.options.Consul_RegisterInterval) * time.Second) + defer t.Stop() +locp: + for { + select { + case <-t.C: + err := this.PushServiceInfo() + if err != nil { + log.Warnf("service run Server.Register error: %v", err) + } + case <-ctx.Done(): + break locp + } + } + log.Infof("Sys Registry is succ exit run") +} + +func (this *Nacos_Registry) listener(ctx context.Context) { + var ( + slist model.ServiceList + instances []model.Instance + err error + ) + t := time.NewTicker(time.Duration(this.options.Nacos_RegisterTTL) * time.Second) + defer t.Stop() +locp: + for { + select { + case <-t.C: + if slist, err = this.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + NameSpace: this.options.Nacos_NamespaceId, + GroupName: this.options.Service.GetTag(), + PageNo: 1, + PageSize: 100, + }); err == nil { + var service = make(map[string]struct{}) + for _, v := range slist.Doms { + if instances, err = this.client.SelectInstances(vo.SelectInstancesParam{ + ServiceName: v, + GroupName: this.options.Service.GetTag(), + HealthyOnly: true, + }); err == nil { + for _, v1 := range instances { + if v1.Enable && v1.Healthy { + service[v] = struct{}{} + this.addandupdataServiceNode(v1) + } + } + } + } + temp := make(map[string]*ServiceNode) + this.rlock.RLock() + for k, v := range this.services { + temp[k] = v + } + this.rlock.RUnlock() + for k, v := range temp { + if _, ok := service[k]; !ok { //不存在了 移除 + this.removeServiceNode(v.Id) + } + } + } + case <-ctx.Done(): + break locp + } + } + log.Infof("Sys Registry is succ exit listener") +} + +func (this *Nacos_Registry) getServices() (err error) { + var ( + slist model.ServiceList + instances []model.Instance + ) + if slist, err = this.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + NameSpace: this.options.Nacos_NamespaceId, + GroupName: this.options.Service.GetTag(), + PageNo: 1, + PageSize: 20, + }); err == nil { + for _, v := range slist.Doms { + this.rlock.RLock() + _, ok := this.services[v] + this.rlock.RUnlock() + if !ok { + if instances, err = this.client.SelectInstances(vo.SelectInstancesParam{ + ServiceName: v, + GroupName: this.options.Service.GetTag(), + HealthyOnly: true, + }); err == nil { + for _, v1 := range instances { + if v1.Enable && v1.Healthy { + this.addandupdataServiceNode(v1) + } + } + } + } + } + } + return err +} +func (this *Nacos_Registry) registerSNode(snode *ServiceNode) (err error) { + h, err := hash.Hash(snode, nil) + if err != nil { + return err + } + if v, ok := this.shash[snode.Id]; ok && v != 0 && v == h { //没变化不用注册 + return + } + var ( + // success bool + ) + this.deregisterSNode() + _, err = this.client.RegisterInstance(vo.RegisterInstanceParam{ + Ip: snode.IP, + Port: uint64(snode.Port), + Weight: snode.PreWeight, + GroupName: snode.Tag, + ServiceName: snode.Id, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{ + "id": snode.Id, + "tag": snode.Tag, + "type": string(snode.Type), + "category": string(snode.Category), + "version": fmt.Sprintf("%v", snode.Version), + "rpcid": snode.RpcId, + }, + }) + return +} +func (this *Nacos_Registry) deregisterSNode() (err error) { + _, err = this.client.DeregisterInstance(vo.DeregisterInstanceParam{ + Ip: this.options.Service.GetIp(), + Port: 8848, + ServiceName: this.options.Service.GetId(), + GroupName: this.options.Service.GetTag(), + }) + return +} +func (this *Nacos_Registry) addandupdataServiceNode(as model.Instance) (sn *ServiceNode, err error) { + var ( + version string + rpcid string + snode *ServiceNode + h uint64 + ) + version = as.Metadata["version"] + rpcid = as.Metadata["rpcid"] + snode = &ServiceNode{ + Tag: as.Metadata["tag"], + Type: as.Metadata["type"], + Category: core.S_Category(as.Metadata["category"]), + Id: as.Metadata["id"], + IP: as.Ip, + Port: int(as.Port), + Version: version, + RpcId: rpcid, + PreWeight: as.Weight, + } + if h, err = hash.Hash(snode, nil); err == nil { + _, ok := this.services[snode.Id] + if !ok { + log.Infof("发现新的服务【%s:%s】", snode.Id, snode.Version) + sub := &vo.SubscribeParam{ + ServiceName: snode.Id, + GroupName: snode.Tag, + SubscribeCallback: this.subscribecallback, + } + this.rlock.Lock() + this.services[snode.Id] = snode + this.shash[snode.Id] = h + this.subscribe[snode.Id] = sub + this.rlock.Unlock() + if this.options.Listener != nil { //异步通知 + go this.options.Listener.FindServiceHandlefunc(*snode) + } + err = this.client.Subscribe(sub) + } else { + this.rlock.RLock() + v, ok := this.shash[snode.Id] + this.rlock.RUnlock() + if !ok || v != h { //校验不一致 + // log.Debugf("更新服务【%s】", snode.Id) + this.rlock.Lock() + this.services[snode.Id] = snode + this.shash[snode.Id] = h + this.rlock.Unlock() + if this.options.Listener != nil { //异步通知 + go this.options.Listener.UpDataServiceHandlefunc(*snode) + } + } + } + } else { + log.Errorf("registry 校验服务 hash 值异常:%s", err.Error()) + } + return +} +func (this *Nacos_Registry) removeServiceNode(sId string) { + this.rlock.RLock() + _, ok := this.services[sId] + sub, _ := this.subscribe[sId] + this.rlock.RUnlock() + if !ok { + return + } + log.Infof("丢失服务【%s】", sId) + this.rlock.Lock() + delete(this.services, sId) + delete(this.subscribe, sId) + delete(this.shash, sId) + this.rlock.Unlock() + this.client.Unsubscribe(sub) + if this.options.Listener != nil { //异步通知 + go this.options.Listener.LoseServiceHandlefunc(sId) + } +} + +func (this *Nacos_Registry) subscribecallback(services []model.SubscribeService, err error) { + for _, v := range services { + // log.Debugf("subscribecallback:%+v", v) + this.addandupdataServiceNode(model.Instance{ + Valid: v.Valid, + Enable: v.Enable, + ServiceName: v.ServiceName, + Ip: v.Ip, + Port: v.Port, + Metadata: v.Metadata, + Weight: v.Weight, + }) + } +} diff --git a/lego/sys/registry/options.go b/lego/sys/registry/options.go new file mode 100644 index 000000000..342b78caf --- /dev/null +++ b/lego/sys/registry/options.go @@ -0,0 +1,135 @@ +package registry + +import ( + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/utils/mapstructure" +) + +type RegistryType uint8 + +const ( + Registry_Consul RegistryType = iota + Registry_Nacos + Registry_Zookeeper +) + +type ( + Option func(*Options) + Options struct { + RegistryType RegistryType + Service base.IClusterServiceBase + Listener IListener + Consul_Addr string + Consul_RegisterInterval int //定期注册 + Consul_RegisterTTL int + Nacos_NamespaceId string + Nacos_NacosAddr string + Nacos_Port uint64 + Nacos_UserName string + Nacos_Password string + Nacos_TimeoutMs uint64 //连接超时 ms + Nacos_BeatInterval int64 //心跳间隔 ms + Nacos_RegisterTTL int + } +) + +func SetRegistryType(v RegistryType) Option { + return func(o *Options) { + o.RegistryType = v + } +} +func SetService(v base.IClusterServiceBase) Option { + return func(o *Options) { + o.Service = v + } +} +func SetListener(v IListener) Option { + return func(o *Options) { + o.Listener = v + } +} +func SetConsul_Addr(v string) Option { + return func(o *Options) { + o.Consul_Addr = v + } +} +func SetConsul_RegisterInterval(v int) Option { + return func(o *Options) { + o.Consul_RegisterInterval = v + } +} +func SetConsul_RegisterTTL(v int) Option { + return func(o *Options) { + o.Consul_RegisterTTL = v + } +} +func SetNacos_NamespaceId(v string) Option { + return func(o *Options) { + o.Nacos_NamespaceId = v + } +} +func SetNacos_NacosAddr(v string) Option { + return func(o *Options) { + o.Nacos_NacosAddr = v + } +} + +func SetNacos_Port(v uint64) Option { + return func(o *Options) { + o.Nacos_Port = v + } +} + +func SetNacos_UserName(v string) Option { + return func(o *Options) { + o.Nacos_UserName = v + } +} + +func SetNacos_Password(v string) Option { + return func(o *Options) { + o.Nacos_Password = v + } +} + +func SetNacos_TimeoutMs(v uint64) Option { + return func(o *Options) { + o.Nacos_TimeoutMs = v + } +} +func SetNacos_BeatInterval(v int64) Option { + return func(o *Options) { + o.Nacos_BeatInterval = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + Consul_RegisterInterval: 10, + Consul_RegisterTTL: 15, + Nacos_TimeoutMs: 10000, + Nacos_BeatInterval: 5000, + Nacos_RegisterTTL: 8, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + Consul_RegisterInterval: 10, + Consul_RegisterTTL: 30, + Nacos_TimeoutMs: 10000, + Nacos_BeatInterval: 5000, + Nacos_RegisterTTL: 8, + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/registry/registry.go b/lego/sys/registry/registry.go new file mode 100644 index 000000000..4fa7070d5 --- /dev/null +++ b/lego/sys/registry/registry.go @@ -0,0 +1,10 @@ +package registry + +func newSys(options Options) (sys ISys, err error) { + if options.RegistryType == Registry_Consul { + sys, err = newConsul(options) + } else if options.RegistryType == Registry_Nacos { + sys, err = newNacos(options) + } + return +} diff --git a/lego/sys/registry/sys_test.go b/lego/sys/registry/sys_test.go new file mode 100644 index 000000000..c8c6897d0 --- /dev/null +++ b/lego/sys/registry/sys_test.go @@ -0,0 +1,117 @@ +package registry_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/consul/api" + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/common/constant" + "github.com/nacos-group/nacos-sdk-go/vo" +) + +func Test_Sys_Nacose(t *testing.T) { + // 创建clientConfig + clientConfig := constant.ClientConfig{ + NamespaceId: "ac1b23d5-1c14-4485-9e08-f3cde8c83163", + } + // 至少一个ServerConfig + serverConfigs := []constant.ServerConfig{ + { + IpAddr: "172.20.27.126", + ContextPath: "/nacos", + Port: 8888, + Scheme: "http", + }, + } + + // 创建服务发现客户端的另一种方式 (推荐) + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, + ) + if err != nil { + fmt.Printf("初始化系统失败\n") + } + + // if succ, err := client.RegisterInstance(vo.RegisterInstanceParam{ + // Ip: "127.0.0.2", + // Port: 8848, + // Weight: 1, + // ServiceName: "test_1", + // Enable: true, + // Healthy: true, + // Ephemeral: true, + // Metadata: map[string]string{ + // "type": "test", + // "category": "test", + // "version": fmt.Sprintf("%e", 1.0), + // "rpcid": "rwercsfsdwer", + // "rpcsubscribe": "{}", + // }, + // }); err != nil { + // fmt.Printf("RegisterInstance err:%v\n", err) + // } else { + // fmt.Printf("RegisterInstance succ:%v\n", succ) + // } + + if slist, err := client.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + NameSpace: "ac1b23d5-1c14-4485-9e08-f3cde8c83163", + GroupName: "datacollector", + PageNo: 1, + PageSize: 20, + }); err != nil { + fmt.Printf("GetAllServicesInfo err:%v\n", err) + } else { + fmt.Printf("GetAllServicesInfo :%+v\n", slist) + for _, v := range slist.Doms { + if instances, err := client.SelectInstances(vo.SelectInstancesParam{ + ServiceName: v, + GroupName: "datacollector", + HealthyOnly: true, + }); err == nil { + fmt.Printf("instances :%+v\n", instances) + } else { + fmt.Printf("instances err:%v\n", err) + } + // if services, err := client.SelectInstances(vo.SelectInstancesParam{ + // ServiceName: v, + // HealthyOnly: true, + // }); err != nil { + // fmt.Printf("SelectInstances err:%v\n", err) + // } else { + // fmt.Printf("SelectInstances :%+v\n", services) + // } + // if services, err := client.SelectAllInstances(vo.SelectAllInstancesParam{ + // ServiceName: v, + // }); err != nil { + // fmt.Printf("SelectAllInstances err:%v\n", err) + // } else { + // fmt.Printf("SelectAllInstances :%+v\n", services) + // } + } + } +} + +func Test_Sys_Consul(t *testing.T) { + config := api.DefaultConfig() + config.Address = "172.20.27.145:10003" + if client, err := api.NewClient(config); err != nil { + fmt.Printf("NewClient Err:%v\n", err) + return + } else { + if err := client.Agent().ServiceRegister(&api.AgentServiceRegistration{ + ID: "test", + Name: "test", + Tags: []string{"test"}, + Meta: map[string]string{}, + }); err != nil { + fmt.Printf("ServiceRegister Err:%v\n", err) + return + } else { + fmt.Printf("ServiceRegister Succ\n") + } + } +} diff --git a/lego/sys/registry/zookeeper.go b/lego/sys/registry/zookeeper.go new file mode 100644 index 000000000..c04b90f74 --- /dev/null +++ b/lego/sys/registry/zookeeper.go @@ -0,0 +1,10 @@ +package registry + +func newZookeeper(options Options) (sys *Zookeeper_Registry, err error) { + + return +} + +type Zookeeper_Registry struct { + options Options +} diff --git a/lego/sys/rpcx/client.go b/lego/sys/rpcx/client.go new file mode 100644 index 000000000..3a3425bd8 --- /dev/null +++ b/lego/sys/rpcx/client.go @@ -0,0 +1,31 @@ +package rpcx + +import ( + "context" + + "github.com/smallnest/rpcx/client" +) + +func newClient(addr string, sId string) (c *Client, err error) { + c = &Client{} + d, err := client.NewPeer2PeerDiscovery("tcp@"+addr, "") + c.xclient = client.NewXClient(sId, client.Failfast, client.RandomSelect, d, client.DefaultOption) + return +} + +type Client struct { + xclient client.XClient +} + +func (this *Client) Stop() (err error) { + err = this.xclient.Close() + return +} + +func (this *Client) Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) (err error) { + err = this.xclient.Call(ctx, string(serviceMethod), args, reply) + return +} +func (this *Client) Go(ctx context.Context, serviceMethod string, args interface{}, reply interface{}, done chan *client.Call) (*client.Call, error) { + return this.xclient.Go(ctx, string(serviceMethod), args, reply, done) +} diff --git a/lego/sys/rpcx/core.go b/lego/sys/rpcx/core.go new file mode 100644 index 000000000..c1f6f48af --- /dev/null +++ b/lego/sys/rpcx/core.go @@ -0,0 +1,69 @@ +package rpcx + +import ( + "context" + + "github.com/smallnest/rpcx/client" +) + +type ( + ISys interface { + IRPCXServer + NewRpcClient(addr, sId string) (clent IRPCXClient, err error) + } + + IRPCXServer interface { + Start() (err error) + Stop() (err error) + Register(rcvr interface{}) (err error) + RegisterFunction(fn interface{}) (err error) + RegisterFunctionName(name string, fn interface{}) (err error) + UnregisterAll() (err error) + } + + IRPCXClient interface { + Stop() (err error) + Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error + Go(ctx context.Context, serviceMethod string, args interface{}, reply interface{}, done chan *client.Call) (*client.Call, error) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Start() (err error) { + return defsys.Start() +} + +func Stop() (err error) { + return defsys.Stop() +} + +func Register(rcvr interface{}) (err error) { + return defsys.Register(rcvr) +} +func RegisterFunction(fn interface{}) (err error) { + return defsys.RegisterFunction(fn) +} +func RegisterFunctionName(name string, fn interface{}) (err error) { + return defsys.RegisterFunctionName(name, fn) +} + +func UnregisterAll() (err error) { + return defsys.UnregisterAll() +} + +func NewRpcClient(addr, sId string) (clent IRPCXClient, err error) { + return defsys.NewRpcClient(addr, sId) +} diff --git a/lego/sys/rpcx/options.go b/lego/sys/rpcx/options.go new file mode 100644 index 000000000..aab6fd0b1 --- /dev/null +++ b/lego/sys/rpcx/options.go @@ -0,0 +1,63 @@ +package rpcx + +import ( + "go_dreamfactory/lego/sys/log" + "go_dreamfactory/lego/utils/mapstructure" + + "github.com/smallnest/rpcx/client" +) + +type Option func(*Options) +type Options struct { + ServiceId string //服务id + Port int //监听地址 + FailMode client.FailMode //失败模式 + Debug bool //日志是否开启 + Log log.ILog +} + +func SetServiceId(v string) Option { + return func(o *Options) { + o.ServiceId = v + } +} +func SetPort(v int) Option { + return func(o *Options) { + o.Port = v + } +} +func SetDebug(v bool) Option { + return func(o *Options) { + o.Debug = v + } +} +func SetLog(v log.ILog) Option { + return func(o *Options) { + o.Log = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + Debug: true, + Log: log.Clone(log.SetLoglayer(2)), + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + Debug: true, + Log: log.Clone(log.SetLoglayer(2)), + } + for _, o := range opts { + o(&options) + } + return options +} diff --git a/lego/sys/rpcx/rpcx.go b/lego/sys/rpcx/rpcx.go new file mode 100644 index 000000000..9bc9a1272 --- /dev/null +++ b/lego/sys/rpcx/rpcx.go @@ -0,0 +1,47 @@ +package rpcx + +func newSys(options Options) (sys *RPCX, err error) { + sys = &RPCX{ + options: options, + service: newService(options), + } + return +} + +type RPCX struct { + options Options + service IRPCXServer +} + +func (this *RPCX) Start() (err error) { + this.service.Start() + return +} + +func (this *RPCX) Stop() (err error) { + err = this.service.Stop() + return +} + +func (this *RPCX) Register(rcvr interface{}) (err error) { + err = this.service.Register(rcvr) + return +} + +func (this *RPCX) RegisterFunction(fn interface{}) (err error) { + err = this.service.RegisterFunction(fn) + return +} + +func (this *RPCX) RegisterFunctionName(name string, fn interface{}) (err error) { + err = this.service.RegisterFunctionName(name, fn) + return +} + +func (this *RPCX) UnregisterAll() (err error) { + return this.service.UnregisterAll() +} + +func (this *RPCX) NewRpcClient(addr, sId string) (clent IRPCXClient, err error) { + return newClient(addr, sId) +} diff --git a/lego/sys/rpcx/service.go b/lego/sys/rpcx/service.go new file mode 100644 index 000000000..86c2dc5bb --- /dev/null +++ b/lego/sys/rpcx/service.go @@ -0,0 +1,47 @@ +package rpcx + +import ( + "fmt" + + "github.com/smallnest/rpcx/server" +) + +func newService(options Options) (s *Service) { + s = &Service{ + server: server.NewServer(), + options: options, + } + return +} + +type Service struct { + server *server.Server + options Options +} + +func (this *Service) Start() (err error) { + go this.server.Serve("tcp", fmt.Sprintf(":%d", this.options.Port)) + return +} + +func (this *Service) Stop() (err error) { + err = this.server.Close() + return +} + +func (this *Service) Register(rcvr interface{}) (err error) { + err = this.server.RegisterName(this.options.ServiceId, rcvr, "") + return +} +func (this *Service) RegisterFunction(fn interface{}) (err error) { + err = this.server.RegisterFunction(this.options.ServiceId, fn, "") + return +} +func (this *Service) RegisterFunctionName(name string, fn interface{}) (err error) { + err = this.server.RegisterFunctionName(this.options.ServiceId, name, fn, "") + return +} +func (this *Service) UnregisterAll() (err error) { + err = this.server.UnregisterAll() + return +} diff --git a/lego/sys/timewheel/core.go b/lego/sys/timewheel/core.go new file mode 100644 index 000000000..3a0af3ce2 --- /dev/null +++ b/lego/sys/timewheel/core.go @@ -0,0 +1,70 @@ +package timewheel + +import ( + "time" +) + +type ( + ISys interface { + Start() + Stop() + Add(delay time.Duration, handler func(*Task, ...interface{}), args ...interface{}) *Task + AddCron(delay time.Duration, handler func(*Task, ...interface{}), args ...interface{}) *Task + Remove(task *Task) error + NewTimer(delay time.Duration) *Timer + NewTicker(delay time.Duration) *Ticker + AfterFunc(delay time.Duration, callback func()) *Timer + After(delay time.Duration) <-chan time.Time + Sleep(delay time.Duration) + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + if defsys, err = newsys(newOptions(config, option...)); err == nil { + defsys.Start() + } + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + if sys, err = newsys(newOptionsByOption(option...)); err == nil { + sys.Start() + } + return +} + +func Add(delay time.Duration, handler func(*Task, ...interface{}), args ...interface{}) *Task { + return defsys.Add(delay, handler, args...) +} + +func AddCron(delay time.Duration, handler func(*Task, ...interface{}), args ...interface{}) *Task { + return defsys.AddCron(delay, handler, args...) +} + +func Remove(task *Task) error { + return defsys.Remove(task) +} + +func NewTimer(delay time.Duration) *Timer { + return defsys.NewTimer(delay) +} + +func NewTicker(delay time.Duration) *Ticker { + return defsys.NewTicker(delay) +} + +func AfterFunc(delay time.Duration, callback func()) *Timer { + return defsys.AfterFunc(delay, callback) +} + +func After(delay time.Duration) <-chan time.Time { + return defsys.After(delay) +} + +func Sleep(delay time.Duration) { + defsys.Sleep(delay) +} diff --git a/lego/sys/timewheel/options.go b/lego/sys/timewheel/options.go new file mode 100644 index 000000000..bc0819e90 --- /dev/null +++ b/lego/sys/timewheel/options.go @@ -0,0 +1,74 @@ +package timewheel + +import ( + "go_dreamfactory/lego/sys/log" + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { + Tick int //单位毫秒 + BucketsNum int + IsSyncPool bool +} + +func SetTick(v int) Option { + return func(o *Options) { + o.Tick = v + } +} + +func SetBucketsNum(v int) Option { + return func(o *Options) { + o.BucketsNum = v + } +} + +func SetIsSyncPool(v bool) Option { + return func(o *Options) { + o.IsSyncPool = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + Tick: 1000, + BucketsNum: 1, + IsSyncPool: true, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + if options.Tick < 100 { + log.Errorf("创建时间轮参数异常 Tick 必须大于 100 ms ") + options.Tick = 100 + } + if options.BucketsNum < 0 { + log.Errorf("创建时间轮参数异常 BucketsNum 必须大于 0 ") + options.BucketsNum = 1 + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + Tick: 1000, + BucketsNum: 1, + IsSyncPool: true, + } + for _, o := range opts { + o(&options) + } + if options.Tick < 100 { + log.Warnf("创建时间轮参数异常 Tick 必须大于 100 ms ") + options.Tick = 100 + } + if options.BucketsNum < 0 { + log.Warnf("创建时间轮参数异常 BucketsNum 必须大于 0 ") + options.BucketsNum = 1 + } + return options +} diff --git a/lego/sys/timewheel/task_pool.go b/lego/sys/timewheel/task_pool.go new file mode 100644 index 000000000..dc14a4ac5 --- /dev/null +++ b/lego/sys/timewheel/task_pool.go @@ -0,0 +1,34 @@ +package timewheel + +import ( + "sync" +) + +var incr = 0 + +var ( + defaultTaskPool = newTaskPool() +) + +type taskPool struct { + bp *sync.Pool +} + +func newTaskPool() *taskPool { + return &taskPool{ + bp: &sync.Pool{ + New: func() interface{} { + return &Task{} + }, + }, + } +} + +func (pool *taskPool) get() *Task { + return pool.bp.Get().(*Task) +} + +func (pool *taskPool) put(obj *Task) { + obj.Reset() + pool.bp.Put(obj) +} diff --git a/lego/sys/timewheel/timewheel.go b/lego/sys/timewheel/timewheel.go new file mode 100644 index 000000000..dbde2bedc --- /dev/null +++ b/lego/sys/timewheel/timewheel.go @@ -0,0 +1,435 @@ +package timewheel + +import ( + "context" + "sync" + "sync/atomic" + "time" +) + +// 创建一个时间轮 +func newsys(options Options) (sys *TimeWheel, err error) { + sys = &TimeWheel{ + // tick + tick: time.Millisecond * time.Duration(options.Tick), + tickQueue: make(chan time.Time, 10), + + // store + bucketsNum: options.BucketsNum, + bucketIndexes: make(map[taskID]int, 1024*100), + buckets: make([]map[taskID]*Task, options.BucketsNum), + currentIndex: 0, + + // signal + addC: make(chan *Task, 1024*5), + removeC: make(chan *Task, 1024*2), + stopC: make(chan struct{}), + syncPool: options.IsSyncPool, + } + + for i := 0; i < options.BucketsNum; i++ { + sys.buckets[i] = make(map[taskID]*Task, 16) + } + + return +} + +const ( + typeTimer taskType = iota + typeTicker + + modeIsCircle = true + modeNotCircle = false + + modeIsAsync = true + modeNotAsync = false +) + +type ( + taskType int64 + taskID int64 + Task struct { + delay time.Duration + id taskID + round int + args []interface{} + callback func(*Task, ...interface{}) + async bool + stop bool + circle bool + } + TimeWheel struct { + randomID int64 + tick time.Duration + ticker *time.Ticker + tickQueue chan time.Time + bucketsNum int + buckets []map[taskID]*Task // key: added item, value: *Task + bucketIndexes map[taskID]int // key: added item, value: bucket position + currentIndex int + onceStart sync.Once + addC chan *Task + removeC chan *Task + stopC chan struct{} + exited bool + syncPool bool + } +) + +// for sync.Pool +func (t *Task) Reset() { + t.round = 0 + t.callback = nil + + t.async = false + t.stop = false + t.circle = false +} + +//启动时间轮 +func (this *TimeWheel) Start() { + // onlye once start + this.onceStart.Do( + func() { + this.ticker = time.NewTicker(this.tick) + go this.schduler() + go this.tickGenerator() + }, + ) +} + +func (this *TimeWheel) Add(delay time.Duration, handler func(*Task, ...interface{}), args ...interface{}) *Task { + return this.addAny(delay, modeNotCircle, modeIsAsync, handler, args...) +} + +// AddCron add interval task +func (this *TimeWheel) AddCron(delay time.Duration, handler func(*Task, ...interface{}), args ...interface{}) *Task { + return this.addAny(delay, modeIsCircle, modeIsAsync, handler, args...) +} + +func (this *TimeWheel) Remove(task *Task) error { + this.removeC <- task + return nil +} + +//停止时间轮 +func (this *TimeWheel) Stop() { + this.stopC <- struct{}{} +} + +func (this *TimeWheel) tickGenerator() { + if this.tickQueue == nil { + return + } + + for !this.exited { + select { + case <-this.ticker.C: + select { + case this.tickQueue <- time.Now(): + default: + panic("raise long time blocking") + } + } + } +} + +//调度器 +func (this *TimeWheel) schduler() { + queue := this.ticker.C + if this.tickQueue != nil { + queue = this.tickQueue + } + + for { + select { + case <-queue: + this.handleTick() + case task := <-this.addC: + this.put(task) + case key := <-this.removeC: + this.remove(key) + case <-this.stopC: + this.exited = true + this.ticker.Stop() + return + } + } +} + +//清理 +func (this *TimeWheel) collectTask(task *Task) { + index := this.bucketIndexes[task.id] + delete(this.bucketIndexes, task.id) + delete(this.buckets[index], task.id) + + if this.syncPool && !task.circle { + defaultTaskPool.put(task) + } +} + +func (this *TimeWheel) handleTick() { + bucket := this.buckets[this.currentIndex] + for k, task := range bucket { + if task.stop { + this.collectTask(task) + continue + } + + if bucket[k].round > 0 { + bucket[k].round-- + continue + } + + if task.async { + go task.callback(task, task.args...) + } else { + // optimize gopool + task.callback(task, task.args...) + } + + // circle + if task.circle { + this.collectTask(task) + this.putCircle(task, modeIsCircle) + continue + } + + // gc + this.collectTask(task) + } + + if this.currentIndex == this.bucketsNum-1 { + this.currentIndex = 0 + return + } + + this.currentIndex++ +} + +func (this *TimeWheel) addAny(delay time.Duration, circle, async bool, callback func(*Task, ...interface{}), agr ...interface{}) *Task { + if delay <= 0 { + delay = this.tick + } + + id := this.genUniqueID() + + var task *Task + if this.syncPool { + task = defaultTaskPool.get() + } else { + task = new(Task) + } + + task.delay = delay + task.id = id + task.args = agr + task.callback = callback + task.circle = circle + task.async = async // refer to src/runtime/time.go + + this.addC <- task + return task +} + +func (this *TimeWheel) put(task *Task) { + this.store(task, false) +} + +func (this *TimeWheel) putCircle(task *Task, circleMode bool) { + this.store(task, circleMode) +} + +func (this *TimeWheel) store(task *Task, circleMode bool) { + round := this.calculateRound(task.delay) + index := this.calculateIndex(task.delay) + + if round > 0 && circleMode { + task.round = round - 1 + } else { + task.round = round + } + + this.bucketIndexes[task.id] = index + this.buckets[index][task.id] = task +} + +func (this *TimeWheel) calculateRound(delay time.Duration) (round int) { + delaySeconds := delay.Seconds() + tickSeconds := this.tick.Seconds() + round = int(delaySeconds / tickSeconds / float64(this.bucketsNum)) + return +} + +func (this *TimeWheel) calculateIndex(delay time.Duration) (index int) { + delaySeconds := delay.Seconds() + tickSeconds := this.tick.Seconds() + index = (int(float64(this.currentIndex) + delaySeconds/tickSeconds)) % this.bucketsNum + return +} + +func (this *TimeWheel) remove(task *Task) { + this.collectTask(task) +} + +func (this *TimeWheel) NewTimer(delay time.Duration) *Timer { + queue := make(chan bool, 1) // buf = 1, refer to src/time/sleep.go + task := this.addAny(delay, + modeNotCircle, + modeNotAsync, + func(*Task, ...interface{}) { + notfiyChannel(queue) + }, + ) + + // init timer + ctx, cancel := context.WithCancel(context.Background()) + timer := &Timer{ + this: this, + C: queue, // faster + task: task, + Ctx: ctx, + cancel: cancel, + } + + return timer +} + +func (this *TimeWheel) AfterFunc(delay time.Duration, callback func()) *Timer { + queue := make(chan bool, 1) + task := this.addAny(delay, + modeNotCircle, modeIsAsync, + func(*Task, ...interface{}) { + callback() + notfiyChannel(queue) + }, + ) + + // init timer + ctx, cancel := context.WithCancel(context.Background()) + timer := &Timer{ + this: this, + C: queue, // faster + task: task, + Ctx: ctx, + cancel: cancel, + fn: callback, + } + + return timer +} + +func (this *TimeWheel) NewTicker(delay time.Duration) *Ticker { + queue := make(chan bool, 1) + task := this.addAny(delay, + modeIsCircle, + modeNotAsync, + func(*Task, ...interface{}) { + notfiyChannel(queue) + }, + ) + + // init ticker + ctx, cancel := context.WithCancel(context.Background()) + ticker := &Ticker{ + task: task, + this: this, + C: queue, + Ctx: ctx, + cancel: cancel, + } + + return ticker +} + +func (this *TimeWheel) After(delay time.Duration) <-chan time.Time { + queue := make(chan time.Time, 1) + this.addAny(delay, + modeNotCircle, modeNotAsync, + func(*Task, ...interface{}) { + queue <- time.Now() + }, + ) + return queue +} + +func (this *TimeWheel) Sleep(delay time.Duration) { + queue := make(chan bool, 1) + this.addAny(delay, + modeNotCircle, modeNotAsync, + func(*Task, ...interface{}) { + queue <- true + }, + ) + <-queue +} + +// similar to golang std timer +type Timer struct { + task *Task + this *TimeWheel + fn func() // external custom func + C chan bool + + cancel context.CancelFunc + Ctx context.Context +} + +func (t *Timer) Reset(delay time.Duration) { + var task *Task + if t.fn != nil { // use AfterFunc + task = t.this.addAny(delay, + modeNotCircle, modeIsAsync, // must async mode + func(*Task, ...interface{}) { + t.fn() + notfiyChannel(t.C) + }, + ) + } else { + task = t.this.addAny(delay, + modeNotCircle, modeNotAsync, + func(*Task, ...interface{}) { + notfiyChannel(t.C) + }, + ) + } + + t.task = task +} + +func (t *Timer) Stop() { + t.task.stop = true + t.cancel() + t.this.Remove(t.task) +} + +func (t *Timer) StopFunc(callback func()) { + t.fn = callback +} + +type Ticker struct { + this *TimeWheel + task *Task + cancel context.CancelFunc + + C chan bool + Ctx context.Context +} + +func (t *Ticker) Stop() { + t.task.stop = true + t.cancel() + t.this.Remove(t.task) +} + +func notfiyChannel(q chan bool) { + select { + case q <- true: + default: + } +} + +func (this *TimeWheel) genUniqueID() taskID { + id := atomic.AddInt64(&this.randomID, 1) + return taskID(id) +} diff --git a/lego/sys/timewheel/timewheel_test.go b/lego/sys/timewheel/timewheel_test.go new file mode 100644 index 000000000..cfcd50815 --- /dev/null +++ b/lego/sys/timewheel/timewheel_test.go @@ -0,0 +1,45 @@ +package timewheel_test + +import ( + "fmt" + "testing" + "time" + + "go_dreamfactory/lego/sys/timewheel" +) + +func checkTimeCost(t *testing.T, start, end time.Time, before int, after int) bool { + due := end.Sub(start) + if due > time.Duration(after)*time.Millisecond { + t.Error("delay run") + return false + } + + if due < time.Duration(before)*time.Millisecond { + t.Error("run ahead") + return false + } + + return true +} + +func TestAddFunc(t *testing.T) { + tw, _ := timewheel.NewSys(timewheel.SetTick(100), timewheel.SetBucketsNum(10)) + tw.Start() + defer tw.Stop() + + for index := 1; index < 6; index++ { + queue := make(chan bool, 0) + start := time.Now() + tw.Add(time.Duration(index)*time.Second, func(*timewheel.Task, ...interface{}) { + queue <- true + }) + + <-queue + + before := index*1000 - 200 + after := index*1000 + 200 + checkTimeCost(t, start, time.Now(), before, after) + fmt.Println("time since: ", time.Since(start).String()) + } +} diff --git a/lego/sys/workerpools/core.go b/lego/sys/workerpools/core.go new file mode 100644 index 000000000..f7806fba1 --- /dev/null +++ b/lego/sys/workerpools/core.go @@ -0,0 +1,39 @@ +package workerpools + +import "context" + +type ( + ISys interface { + Stop() + StopWait() + IsStop() bool + Submit(task func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}), agrs ...interface{}) + SubmitWait(task func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}), agrs ...interface{}) + WaitingQueueSize() int + } +) + +var ( + defsys ISys +) + +func OnInit(config map[string]interface{}, option ...Option) (err error) { + defsys, err = newSys(newOptions(config, option...)) + return +} + +func NewSys(option ...Option) (sys ISys, err error) { + sys, err = newSys(newOptionsByOption(option...)) + return +} + +func Stop() { defsys.Stop() } +func StopWait() { defsys.StopWait() } +func IsStop() bool { return defsys.IsStop() } +func Submit(task func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}), agrs ...interface{}) { + defsys.Submit(task, agrs...) +} +func SubmitWait(task func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}), agrs ...interface{}) { + defsys.SubmitWait(task, agrs...) +} +func WaitingQueueSize() int { return defsys.WaitingQueueSize() } diff --git a/lego/sys/workerpools/options.go b/lego/sys/workerpools/options.go new file mode 100644 index 000000000..9973c8f48 --- /dev/null +++ b/lego/sys/workerpools/options.go @@ -0,0 +1,84 @@ +package workerpools + +import ( + "time" + + "go_dreamfactory/lego/utils/mapstructure" +) + +type Option func(*Options) +type Options struct { + DefWrokers int + MaxWorkers int + Tasktimeout time.Duration //任务执行操超时间 + IdleTimeoutSec time.Duration //超时释放空闲工作人员 +} + +func SetDefWorkers(v int) Option { + return func(o *Options) { + o.DefWrokers = v + } +} + +func SetMaxWorkers(v int) Option { + return func(o *Options) { + o.MaxWorkers = v + } +} + +func SetTaskTimeOut(v time.Duration) Option { + return func(o *Options) { + o.Tasktimeout = v + } +} + +func newOptions(config map[string]interface{}, opts ...Option) Options { + options := Options{ + DefWrokers: 10, + MaxWorkers: 20, + Tasktimeout: time.Second * 3, + } + if config != nil { + mapstructure.Decode(config, &options) + } + for _, o := range opts { + o(&options) + } + if options.DefWrokers < 1 { + options.DefWrokers = 10 + } + if options.MaxWorkers < 1 { + options.MaxWorkers = 20 + } + if options.Tasktimeout < time.Millisecond { + options.Tasktimeout = time.Second * 3 + } + if options.IdleTimeoutSec < time.Second*5 { + options.IdleTimeoutSec = time.Second * 5 + } + return options +} + +func newOptionsByOption(opts ...Option) Options { + options := Options{ + DefWrokers: 10, + MaxWorkers: 20, + Tasktimeout: time.Second * 3, + } + for _, o := range opts { + o(&options) + } + if options.DefWrokers < 1 { + options.DefWrokers = 10 + } + if options.MaxWorkers < 1 { + options.MaxWorkers = 20 + } + if options.Tasktimeout < time.Millisecond { + options.Tasktimeout = time.Second * 3 + } + if options.IdleTimeoutSec < time.Second*5 { + options.IdleTimeoutSec = time.Second * 5 + } + return options +} diff --git a/lego/sys/workerpools/pools.go b/lego/sys/workerpools/pools.go new file mode 100644 index 000000000..fb96252aa --- /dev/null +++ b/lego/sys/workerpools/pools.go @@ -0,0 +1,194 @@ +package workerpools + +import ( + "context" + "sync" + "time" + + "go_dreamfactory/lego/utils/container" +) + +func newSys(options Options) (sys *WorkerPool, err error) { + sys = &WorkerPool{ + taskQueue: make(chan *Task, 1), + maxWorkers: options.MaxWorkers, + readyWorkers: make(chan chan *Task, options.DefWrokers), + timeout: options.IdleTimeoutSec, + tasktimeout: options.Tasktimeout, + stoppedChan: make(chan struct{}), + } + go sys.dispatch() + return +} + +type Task struct { + f func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}) + agrs []interface{} +} + +type WorkerPool struct { + maxWorkers int + timeout time.Duration //超时释放空闲工作人员 + tasktimeout time.Duration //任务执行操超时间 + taskQueue chan *Task + readyWorkers chan chan *Task + stoppedChan chan struct{} + waitingQueue container.Deque + stopMutex sync.Mutex + stopped bool +} + +func (p *WorkerPool) Stop() { + p.stop(false) +} +func (p *WorkerPool) StopWait() { + p.stop(true) +} +func (p *WorkerPool) IsStop() bool { + p.stopMutex.Lock() + defer p.stopMutex.Unlock() + return p.stopped +} +func (p *WorkerPool) Submit(task func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}), agrs ...interface{}) { + if task != nil { + p.taskQueue <- &Task{f: task, agrs: agrs} + } +} +func (p *WorkerPool) SubmitWait(task func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}), agrs ...interface{}) { + if task == nil { + return + } + doneChan := make(chan struct{}) + p.taskQueue <- &Task{f: func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}) { + task(ctx, cancel, agrs...) + close(doneChan) + }, agrs: agrs} + <-doneChan +} +func (p *WorkerPool) WaitingQueueSize() int { + return p.waitingQueue.Len() +} +func (p *WorkerPool) dispatch() { + defer close(p.stoppedChan) + timeout := time.NewTimer(p.timeout) + var ( + workerCount int + task *Task + ok, wait bool + workerTaskChan chan *Task + ) + startReady := make(chan chan *Task) +Loop: + for { + if p.waitingQueue.Len() != 0 { + select { + case task, ok = <-p.taskQueue: + if !ok { + break Loop + } + if task == nil { + wait = true + break Loop + } + p.waitingQueue.PushBack(task) + case workerTaskChan = <-p.readyWorkers: + // A worker is ready, so give task to worker. + workerTaskChan <- p.waitingQueue.PopFront().(*Task) + } + continue + } + timeout.Reset(p.timeout) + select { + case task, ok = <-p.taskQueue: + if !ok || task == nil { + break Loop + } + // Got a task to do. + select { + case workerTaskChan = <-p.readyWorkers: + // A worker is ready, so give task to worker. + workerTaskChan <- task + default: + // No workers ready. + // Create a new worker, if not at max. + if workerCount < p.maxWorkers { + workerCount++ + go func(t *Task) { + startWorker(startReady, p.readyWorkers, p.tasktimeout) + // Submit the task when the new worker. + taskChan := <-startReady + taskChan <- t + }(task) + } else { + // Enqueue task to be executed by next available worker. + p.waitingQueue.PushBack(task) + } + } + case <-timeout.C: + // Timed out waiting for work to arrive. Kill a ready worker. + if workerCount > 0 { + select { + case workerTaskChan = <-p.readyWorkers: + // A worker is ready, so kill. + close(workerTaskChan) + workerCount-- + default: + // No work, but no ready workers. All workers are busy. + } + } + } + } + + // If instructed to wait for all queued tasks, then remove from queue and + // give to workers until queue is empty. + if wait { + for p.waitingQueue.Len() != 0 { + workerTaskChan = <-p.readyWorkers + // A worker is ready, so give task to worker. + workerTaskChan <- p.waitingQueue.PopFront().(*Task) + } + } + + // Stop all remaining workers as they become ready. + for workerCount > 0 { + workerTaskChan = <-p.readyWorkers + close(workerTaskChan) + workerCount-- + } +} +func startWorker(startReady, readyWorkers chan chan *Task, taskouttime time.Duration) { + go func() { + taskChan := make(chan *Task) + var task *Task + var ok bool + // Register availability on starReady channel. + startReady <- taskChan + for { + // Read task from dispatcher. + task, ok = <-taskChan + if !ok { + break + } + ctx, cancel := context.WithTimeout(context.Background(), taskouttime) + go task.f(ctx, cancel, task.agrs...) + select { + case <-ctx.Done(): + cancel() + } + readyWorkers <- taskChan + } + }() +} +func (p *WorkerPool) stop(wait bool) { + p.stopMutex.Lock() + defer p.stopMutex.Unlock() + if p.stopped { + return + } + p.stopped = true + if wait { + p.taskQueue <- nil + } + close(p.taskQueue) + <-p.stoppedChan +} diff --git a/lego/sys/workerpools/sys_test.go b/lego/sys/workerpools/sys_test.go new file mode 100644 index 000000000..299b51e2b --- /dev/null +++ b/lego/sys/workerpools/sys_test.go @@ -0,0 +1,40 @@ +package workerpools_test + +import ( + "context" + "testing" + "time" + + "go_dreamfactory/lego/sys/workerpools" +) + +func Test_workerpools(t *testing.T) { + pools, _ := workerpools.NewSys(workerpools.SetMaxWorkers(1), workerpools.SetTaskTimeOut(time.Second*4)) + go func() { + pools.Submit(func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}) { + agr0 := agrs[0].(string) + time.Sleep(time.Second * 6) + t.Logf(agr0) + cancel() + }, "liwei1dao") + }() + + // go func() { + // time.Sleep(time.Second * 2) + // pools.Submit(func(ctx context.Context, cancel context.CancelFunc) { + // time.Sleep(time.Second * 1) + // t.Logf("liwei1dao") + // }) + // }() + + go func() { + time.Sleep(time.Second * 1) + pools.Submit(func(ctx context.Context, cancel context.CancelFunc, agrs ...interface{}) { + agr0 := agrs[0].(string) + t.Logf(agr0) + cancel() + }, "liwei2dao") + }() + + time.Sleep(time.Second * 10) +} diff --git a/lego/utils/container/BeeMap.go b/lego/utils/container/BeeMap.go new file mode 100644 index 000000000..aaa1dd214 --- /dev/null +++ b/lego/utils/container/BeeMap.go @@ -0,0 +1,84 @@ +package container + +import "sync" + +// BeeMap is a map with lock + +type BeeMap struct { + lock *sync.RWMutex + bm map[interface{}]interface{} +} + +// NewBeeMap return new safemap +func NewBeeMap() *BeeMap { + return &BeeMap{ + lock: new(sync.RWMutex), + bm: make(map[interface{}]interface{}), + } +} + +// Get from maps return the k's value +func (m *BeeMap) Get(k interface{}) interface{} { + m.lock.RLock() + if val, ok := m.bm[k]; ok { + m.lock.RUnlock() + return val + } + m.lock.RUnlock() + return nil +} + +// Set Maps the given key and value. Returns false +// if the key is already in the map and changes nothing. +func (m *BeeMap) Set(k interface{}, v interface{}) bool { + m.lock.Lock() + if val, ok := m.bm[k]; !ok { + m.bm[k] = v + m.lock.Unlock() + } else if val != v { + m.bm[k] = v + m.lock.Unlock() + } else { + m.lock.Unlock() + return false + } + return true +} + +// Check Returns true if k is exist in the map. +func (m *BeeMap) Check(k interface{}) bool { + m.lock.RLock() + if _, ok := m.bm[k]; !ok { + m.lock.RUnlock() + return false + } + m.lock.RUnlock() + return true +} + +// Delete the given key and value. +func (m *BeeMap) Delete(k interface{}) { + m.lock.Lock() + delete(m.bm, k) + m.lock.Unlock() +} + +func (m *BeeMap) DeleteAll() { + m.lock.Lock() + for k, _ := range m.bm { + delete(m.bm, k) + } + + m.lock.Unlock() +} + +// Items returns all items in safemap. +func (m *BeeMap) Items() map[interface{}]interface{} { + m.lock.RLock() + r := make(map[interface{}]interface{}) + for k, v := range m.bm { + r[k] = v + } + m.lock.RUnlock() + return r +} diff --git a/lego/utils/container/ConcurrentMap.go b/lego/utils/container/ConcurrentMap.go new file mode 100644 index 000000000..c41c49e8a --- /dev/null +++ b/lego/utils/container/ConcurrentMap.go @@ -0,0 +1,249 @@ +package container + +import ( + "encoding/json" + "sync" +) + +var SHARD_COUNT = 32 + +type ConcurrentMap []*ConcurrentMapShared + +type ConcurrentMapShared struct { + items map[string]interface{} + sync.RWMutex +} + +func NewConcurrentMap() ConcurrentMap { + m := make(ConcurrentMap, SHARD_COUNT) + for i := 0; i < SHARD_COUNT; i++ { + m[i] = &ConcurrentMapShared{items: make(map[string]interface{})} + } + return m +} + +func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared { + return m[uint(fnv32(key))%uint(SHARD_COUNT)] +} +func (m ConcurrentMap) MSet(data map[string]interface{}) { + for key, value := range data { + shard := m.GetShard(key) + shard.Lock() + shard.items[key] = value + shard.Unlock() + } +} +func (m ConcurrentMap) Set(key string, value interface{}) { + // Get map shard. + shard := m.GetShard(key) + shard.Lock() + shard.items[key] = value + shard.Unlock() +} +func (m ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res interface{}) { + shard := m.GetShard(key) + shard.Lock() + v, ok := shard.items[key] + res = cb(ok, v, value) + shard.items[key] = res + shard.Unlock() + return res +} +func (m ConcurrentMap) SetIfAbsent(key string, value interface{}) bool { + // Get map shard. + shard := m.GetShard(key) + shard.Lock() + _, ok := shard.items[key] + if !ok { + shard.items[key] = value + } + shard.Unlock() + return !ok +} +func (m ConcurrentMap) Get(key string) (interface{}, bool) { + shard := m.GetShard(key) + shard.RLock() + val, ok := shard.items[key] + shard.RUnlock() + return val, ok +} +func (m ConcurrentMap) Count() int { + count := 0 + for i := 0; i < SHARD_COUNT; i++ { + shard := m[i] + shard.RLock() + count += len(shard.items) + shard.RUnlock() + } + return count +} +func (m ConcurrentMap) Has(key string) bool { + // Get shard + shard := m.GetShard(key) + shard.RLock() + // See if element is within shard. + _, ok := shard.items[key] + shard.RUnlock() + return ok +} +func (m ConcurrentMap) Remove(key string) { + // Try to get shard. + shard := m.GetShard(key) + shard.Lock() + delete(shard.items, key) + shard.Unlock() +} +func (m ConcurrentMap) RemoveCb(key string, cb RemoveCb) bool { + // Try to get shard. + shard := m.GetShard(key) + shard.Lock() + v, ok := shard.items[key] + remove := cb(key, v, ok) + if remove && ok { + delete(shard.items, key) + } + shard.Unlock() + return remove +} +func (m ConcurrentMap) Pop(key string) (v interface{}, exists bool) { + // Try to get shard. + shard := m.GetShard(key) + shard.Lock() + v, exists = shard.items[key] + delete(shard.items, key) + shard.Unlock() + return v, exists +} +func (m ConcurrentMap) IsEmpty() bool { + return m.Count() == 0 +} +func (m ConcurrentMap) Iter() <-chan Tuple { + chans := snapshot(m) + ch := make(chan Tuple) + go fanIn(chans, ch) + return ch +} +func (m ConcurrentMap) IterBuffered() <-chan Tuple { + chans := snapshot(m) + total := 0 + for _, c := range chans { + total += cap(c) + } + ch := make(chan Tuple, total) + go fanIn(chans, ch) + return ch +} +func (m ConcurrentMap) Items() map[string]interface{} { + tmp := make(map[string]interface{}) + + // Insert items to temporary map. + for item := range m.IterBuffered() { + tmp[item.Key] = item.Val + } + + return tmp +} +func (m ConcurrentMap) IterCb(fn IterCb) { + for idx := range m { + shard := (m)[idx] + shard.RLock() + for key, value := range shard.items { + fn(key, value) + } + shard.RUnlock() + } +} +func (m ConcurrentMap) Keys() []string { + count := m.Count() + ch := make(chan string, count) + go func() { + // Foreach shard. + wg := sync.WaitGroup{} + wg.Add(SHARD_COUNT) + for _, shard := range m { + go func(shard *ConcurrentMapShared) { + // Foreach key, value pair. + shard.RLock() + for key := range shard.items { + ch <- key + } + shard.RUnlock() + wg.Done() + }(shard) + } + wg.Wait() + close(ch) + }() + + // Generate keys + keys := make([]string, 0, count) + for k := range ch { + keys = append(keys, k) + } + return keys +} +func (m ConcurrentMap) MarshalJSON() ([]byte, error) { + // Create a temporary map, which will hold all item spread across shards. + tmp := make(map[string]interface{}) + + // Insert items to temporary map. + for item := range m.IterBuffered() { + tmp[item.Key] = item.Val + } + return json.Marshal(tmp) +} + +type UpsertCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{} +type RemoveCb func(key string, v interface{}, exists bool) bool +type Tuple struct { + Key string + Val interface{} +} + +func snapshot(m ConcurrentMap) (chans []chan Tuple) { + chans = make([]chan Tuple, SHARD_COUNT) + wg := sync.WaitGroup{} + wg.Add(SHARD_COUNT) + // Foreach shard. + for index, shard := range m { + go func(index int, shard *ConcurrentMapShared) { + // Foreach key, value pair. + shard.RLock() + chans[index] = make(chan Tuple, len(shard.items)) + wg.Done() + for key, val := range shard.items { + chans[index] <- Tuple{key, val} + } + shard.RUnlock() + close(chans[index]) + }(index, shard) + } + wg.Wait() + return chans +} + +type IterCb func(key string, v interface{}) + +func fanIn(chans []chan Tuple, out chan Tuple) { + wg := sync.WaitGroup{} + wg.Add(len(chans)) + for _, ch := range chans { + go func(ch chan Tuple) { + for t := range ch { + out <- t + } + wg.Done() + }(ch) + } + wg.Wait() + close(out) +} +func fnv32(key string) uint32 { + hash := uint32(2166136261) + const prime32 = uint32(16777619) + for i := 0; i < len(key); i++ { + hash *= prime32 + hash ^= uint32(key[i]) + } + return hash +} diff --git a/lego/utils/container/Deque.go b/lego/utils/container/Deque.go new file mode 100644 index 000000000..a8aed11fc --- /dev/null +++ b/lego/utils/container/Deque.go @@ -0,0 +1,243 @@ +package container + +// minCapacity is the smallest capacity that deque may have. +// Must be power of 2 for bitwise modulus: x % n == x & (n - 1). +const minCapacity = 16 + +// Deque represents a single instance of the deque data structure. +type Deque struct { + buf []interface{} + head int + tail int + count int + minCap int +} + +// Len returns the number of elements currently stored in the queue. +func (q *Deque) Len() int { + return q.count +} + +// PushBack appends an element to the back of the queue. Implements FIFO when +// elements are removed with PopFront(), and LIFO when elements are removed +// with PopBack(). +func (q *Deque) PushBack(elem interface{}) { + q.growIfFull() + + q.buf[q.tail] = elem + // Calculate new tail position. + q.tail = q.next(q.tail) + q.count++ +} + +// PushFront prepends an element to the front of the queue. +func (q *Deque) PushFront(elem interface{}) { + q.growIfFull() + + // Calculate new head position. + q.head = q.prev(q.head) + q.buf[q.head] = elem + q.count++ +} + +// PopFront removes and returns the element from the front of the queue. +// Implements FIFO when used with PushBack(). If the queue is empty, the call +// panics. +func (q *Deque) PopFront() interface{} { + if q.count <= 0 { + panic("deque: PopFront() called on empty queue") + } + ret := q.buf[q.head] + q.buf[q.head] = nil + // Calculate new head position. + q.head = q.next(q.head) + q.count-- + + q.shrinkIfExcess() + return ret +} + +// PopBack removes and returns the element from the back of the queue. +// Implements LIFO when used with PushBack(). If the queue is empty, the call +// panics. +func (q *Deque) PopBack() interface{} { + if q.count <= 0 { + panic("deque: PopBack() called on empty queue") + } + + // Calculate new tail position + q.tail = q.prev(q.tail) + + // Remove value at tail. + ret := q.buf[q.tail] + q.buf[q.tail] = nil + q.count-- + + q.shrinkIfExcess() + return ret +} + +// Front returns the element at the front of the queue. This is the element +// that would be returned by PopFront(). This call panics if the queue is +// empty. +func (q *Deque) Front() interface{} { + if q.count <= 0 { + panic("deque: Front() called when empty") + } + return q.buf[q.head] +} + +// Back returns the element at the back of the queue. This is the element +// that would be returned by PopBack(). This call panics if the queue is +// empty. +func (q *Deque) Back() interface{} { + if q.count <= 0 { + panic("deque: Back() called when empty") + } + return q.buf[q.prev(q.tail)] +} + +// At returns the element at index i in the queue without removing the element +// from the queue. This method accepts only non-negative index values. At(0) +// refers to the first element and is the same as Front(). At(Len()-1) refers +// to the last element and is the same as Back(). If the index is invalid, the +// call panics. +// +// The purpose of At is to allow Deque to serve as a more general purpose +// circular buffer, where items are only added to and removed from the ends of +// the deque, but may be read from any place within the deque. Consider the +// case of a fixed-size circular log buffer: A new entry is pushed onto one end +// and when full the oldest is popped from the other end. All the log entries +// in the buffer must be readable without altering the buffer contents. +func (q *Deque) At(i int) interface{} { + if i < 0 || i >= q.count { + panic("deque: At() called with index out of range") + } + // bitwise modulus + return q.buf[(q.head+i)&(len(q.buf)-1)] +} + +// Clear removes all elements from the queue, but retains the current capacity. +// This is useful when repeatedly reusing the queue at high frequency to avoid +// GC during reuse. The queue will not be resized smaller as long as items are +// only added. Only when items are removed is the queue subject to getting +// resized smaller. +func (q *Deque) Clear() { + // bitwise modulus + modBits := len(q.buf) - 1 + for h := q.head; h != q.tail; h = (h + 1) & modBits { + q.buf[h] = nil + } + q.head = 0 + q.tail = 0 + q.count = 0 +} + +// Rotate rotates the deque n steps front-to-back. If n is negative, rotates +// back-to-front. Having Deque provide Rotate() avoids resizing that could +// happen if implementing rotation using only Pop and Push methods. +func (q *Deque) Rotate(n int) { + if q.count <= 1 { + return + } + // Rotating a multiple of q.count is same as no rotation. + n %= q.count + if n == 0 { + return + } + + modBits := len(q.buf) - 1 + // If no empty space in buffer, only move head and tail indexes. + if q.head == q.tail { + // Calculate new head and tail using bitwise modulus. + q.head = (q.head + n) & modBits + q.tail = (q.tail + n) & modBits + return + } + + if n < 0 { + // Rotate back to front. + for ; n < 0; n++ { + // Calculate new head and tail using bitwise modulus. + q.head = (q.head - 1) & modBits + q.tail = (q.tail - 1) & modBits + // Put tail value at head and remove value at tail. + q.buf[q.head] = q.buf[q.tail] + q.buf[q.tail] = nil + } + return + } + + // Rotate front to back. + for ; n > 0; n-- { + // Put head value at tail and remove value at head. + q.buf[q.tail] = q.buf[q.head] + q.buf[q.head] = nil + // Calculate new head and tail using bitwise modulus. + q.head = (q.head + 1) & modBits + q.tail = (q.tail + 1) & modBits + } +} + +// SetMinCapacity sets a minimum capacity of 2^minCapacityExp. If the value of +// the minimum capacity is less than or equal to the minimum allowed, then +// capacity is set to the minimum allowed. This may be called at anytime to +// set a new minimum capacity. +// +// Setting a larger minimum capacity may be used to prevent resizing when the +// number of stored items changes frequently across a wide range. +func (q *Deque) SetMinCapacity(minCapacityExp uint) { + if 1< minCapacity { + q.minCap = 1 << minCapacityExp + } else { + q.minCap = minCapacity + } +} + +// prev returns the previous buffer position wrapping around buffer. +func (q *Deque) prev(i int) int { + return (i - 1) & (len(q.buf) - 1) // bitwise modulus +} + +// next returns the next buffer position wrapping around buffer. +func (q *Deque) next(i int) int { + return (i + 1) & (len(q.buf) - 1) // bitwise modulus +} + +// growIfFull resizes up if the buffer is full. +func (q *Deque) growIfFull() { + if len(q.buf) == 0 { + if q.minCap == 0 { + q.minCap = minCapacity + } + q.buf = make([]interface{}, q.minCap) + return + } + if q.count == len(q.buf) { + q.resize() + } +} + +// shrinkIfExcess resize down if the buffer 1/4 full. +func (q *Deque) shrinkIfExcess() { + if len(q.buf) > q.minCap && (q.count<<2) == len(q.buf) { + q.resize() + } +} + +// resize resizes the deque to fit exactly twice its current contents. This is +// used to grow the queue when it is full, and also to shrink it when it is +// only a quarter full. +func (q *Deque) resize() { + newBuf := make([]interface{}, q.count<<1) + if q.tail > q.head { + copy(newBuf, q.buf[q.head:q.tail]) + } else { + n := copy(newBuf, q.buf[q.head:]) + copy(newBuf[n:], q.buf[:q.tail]) + } + + q.head = 0 + q.tail = q.count + q.buf = newBuf +} diff --git a/lego/utils/container/Queue.go b/lego/utils/container/Queue.go new file mode 100644 index 000000000..31875b814 --- /dev/null +++ b/lego/utils/container/Queue.go @@ -0,0 +1,46 @@ +package container + +import ( + "container/list" + "fmt" + "sync" +) + +type Queue struct { + sync.Mutex + data *list.List +} + +func NewQueue() *Queue { + q := new(Queue) + q.data = list.New() + return q +} + +func (q *Queue) Len() int { + return q.data.Len() +} + +func (q *Queue) Push(v interface{}) { + defer q.Unlock() + q.Lock() + q.data.PushFront(v) +} + +func (q *Queue) Pop() interface{} { + defer q.Unlock() + q.Lock() + iter := q.data.Back() + if iter == nil { + return nil + } + v := iter.Value + q.data.Remove(iter) + return v +} + +func (q *Queue) Dump() { + for iter := q.data.Back(); iter != nil; iter = iter.Prev() { + fmt.Println("item:", iter.Value) + } +} diff --git a/lego/utils/container/addr/addr.go b/lego/utils/container/addr/addr.go new file mode 100644 index 000000000..4d283e52c --- /dev/null +++ b/lego/utils/container/addr/addr.go @@ -0,0 +1,112 @@ +package addr + +import ( + "fmt" + "net" +) + +var ( + privateBlocks []*net.IPNet +) + +func init() { + for _, b := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10"} { + if _, block, err := net.ParseCIDR(b); err == nil { + privateBlocks = append(privateBlocks, block) + } + } +} + +func isPrivateIP(ipAddr string) bool { + ip := net.ParseIP(ipAddr) + for _, priv := range privateBlocks { + if priv.Contains(ip) { + return true + } + } + return false +} + +// Extract returns a real ip +func Extract(addr string) (string, error) { + // if addr specified then its returned + if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]") { + return addr, nil + } + + addrs, err := net.InterfaceAddrs() + if err != nil { + return "", fmt.Errorf("Failed to get interface addresses! Err: %v", err) + } + + var ipAddr []byte + + for _, rawAddr := range addrs { + var ip net.IP + switch addr := rawAddr.(type) { + case *net.IPAddr: + ip = addr.IP + case *net.IPNet: + ip = addr.IP + default: + continue + } + + if ip.To4() == nil { + continue + } + + if !isPrivateIP(ip.String()) { + continue + } + + ipAddr = ip + break + } + + if ipAddr == nil { + return "", fmt.Errorf("No private IP address found, and explicit IP not provided") + } + + return net.IP(ipAddr).String(), nil +} + +// IPs returns all known ips +func IPs() []string { + ifaces, err := net.Interfaces() + if err != nil { + return nil + } + + var ipAddrs []string + + for _, i := range ifaces { + addrs, err := i.Addrs() + if err != nil { + continue + } + + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + + if ip == nil { + continue + } + + ip = ip.To4() + if ip == nil { + continue + } + + ipAddrs = append(ipAddrs, ip.String()) + } + } + + return ipAddrs +} diff --git a/lego/utils/container/addr/addr_test.go b/lego/utils/container/addr/addr_test.go new file mode 100644 index 000000000..ad23b128d --- /dev/null +++ b/lego/utils/container/addr/addr_test.go @@ -0,0 +1,38 @@ +package addr + +import ( + "net" + "testing" +) + +func TestExtractor(t *testing.T) { + testData := []struct { + addr string + expect string + parse bool + }{ + {"127.0.0.1", "127.0.0.1", false}, + {"10.0.0.1", "10.0.0.1", false}, + {"", "", true}, + {"0.0.0.0", "", true}, + {"[::]", "", true}, + } + + for _, d := range testData { + addr, err := Extract(d.addr) + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + if d.parse { + ip := net.ParseIP(addr) + if ip == nil { + t.Error("Unexpected nil IP") + } + + } else if addr != d.expect { + t.Errorf("Expected %s got %s", d.expect, addr) + } + } + +} diff --git a/lego/utils/container/id/Id.go b/lego/utils/container/id/Id.go new file mode 100644 index 000000000..5ae426d4e --- /dev/null +++ b/lego/utils/container/id/Id.go @@ -0,0 +1,18 @@ +package id + +import ( + "github.com/rs/xid" + uuid "github.com/satori/go.uuid" +) + +//xid +func NewXId() string { + id := xid.New() + return id.String() +} + +//uuid +func NewUUId() string { + u1 := uuid.NewV4() + return u1.String() +} diff --git a/lego/utils/container/ip/ip.go b/lego/utils/container/ip/ip.go new file mode 100644 index 000000000..f8386b563 --- /dev/null +++ b/lego/utils/container/ip/ip.go @@ -0,0 +1,54 @@ +package ip + +import ( + "encoding/json" + "io/ioutil" + "net" + "net/http" + + "github.com/axgle/mahonia" +) + +type IPInfo struct { + IP string `json:"ip"` + Pro string `json:"pro"` + ProCode string `json:"proCode"` + City string `json:"city"` + CityCode string `json:"cityCode"` + Region string `json:"region"` + RegionCode string `json:"regionCode"` + Addr string `json:"addr"` + RegionNames string `json:"regionNames"` + Err string `json:"err"` +} + +//获取以太网IP +func GetEthernetInfo() *IPInfo { + url := "http://whois.pconline.com.cn/ipJson.jsp?json=true" + resp, err := http.Get(url) + if err != nil { + return nil + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil + } + bodystr := mahonia.NewDecoder("gbk").ConvertString(string(body)) + var result IPInfo + if err := json.Unmarshal([]byte(bodystr), &result); err != nil { + return nil + } + return &result +} + +//获取本地Ip +func GetOutboundIP() string { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return "" + } + defer conn.Close() + localAddr := conn.LocalAddr().(*net.UDPAddr) + return localAddr.IP.String() +} diff --git a/lego/utils/container/lkqueue.go b/lego/utils/container/lkqueue.go new file mode 100644 index 000000000..c4891c32f --- /dev/null +++ b/lego/utils/container/lkqueue.go @@ -0,0 +1,83 @@ +package container + +import ( + "sync/atomic" + "unsafe" +) + +// LKQueue is a lock-free unbounded queue. +type LKQueue struct { + head unsafe.Pointer + tail unsafe.Pointer + size int32 +} +type node struct { + value interface{} + next unsafe.Pointer +} + +// NewLKQueue returns an empty queue. +func NewLKQueue() *LKQueue { + n := unsafe.Pointer(&node{}) + return &LKQueue{head: n, tail: n, size: 0} +} + +// Enqueue puts the given value v at the tail of the queue. +func (q *LKQueue) Enqueue(v interface{}) { + n := &node{value: v} + for { + tail := load(&q.tail) + next := load(&tail.next) + if tail == load(&q.tail) { // are tail and next consistent? + if next == nil { + if cas(&tail.next, next, n) { + cas(&q.tail, tail, n) // Enqueue is done. try to swing tail to the inserted node + atomic.AddInt32(&q.size, 1) + return + } + } else { // tail was not pointing to the last node + // try to swing Tail to the next node + cas(&q.tail, tail, next) + } + } + } +} + +// Dequeue removes and returns the value at the head of the queue. +// It returns nil if the queue is empty. +func (q *LKQueue) Dequeue() interface{} { + for { + head := load(&q.head) + tail := load(&q.tail) + next := load(&head.next) + if head == load(&q.head) { // are head, tail, and next consistent? + if head == tail { // is queue empty or tail falling behind? + if next == nil { // is queue empty? + return nil + } + // tail is falling behind. try to advance it + cas(&q.tail, tail, next) + } else { + // read value before CAS otherwise another dequeue might free the next node + v := next.value + if cas(&q.head, head, next) { + atomic.AddInt32(&q.size, -1) + return v // Dequeue is done. return + } + } + } + } +} + +func (q *LKQueue) Size() int32 { + return atomic.LoadInt32(&q.size) +} + +func load(p *unsafe.Pointer) (n *node) { + return (*node)(atomic.LoadPointer(p)) +} + +func cas(p *unsafe.Pointer, old, new *node) (ok bool) { + return atomic.CompareAndSwapPointer( + p, unsafe.Pointer(old), unsafe.Pointer(new)) +} diff --git a/lego/utils/container/sortslice/interface.go b/lego/utils/container/sortslice/interface.go new file mode 100644 index 000000000..1dc5314fe --- /dev/null +++ b/lego/utils/container/sortslice/interface.go @@ -0,0 +1,35 @@ +package sortslice + +//排序工具 +func quickSort(arr []interface{}, start, end int, compete func(a interface{}, b interface{}) int8) { + if start < end { + i, j := start, end + key := arr[(start+end)/2] + for i <= j { + for compete(arr[i], key) == -1 { + i++ + } + for compete(arr[j], key) == 1 { + j-- + } + if i <= j { + arr[i], arr[j] = arr[j], arr[i] + i++ + j-- + } + } + if start < j { + quickSort(arr, start, j, compete) + } + if end > i { + quickSort(arr, i, end, compete) + } + } +} + +func Sort(a []interface{}, compete func(a interface{}, b interface{}) int8) { + if len(a) < 2 { + return + } + quickSort(a, 0, len(a)-1, compete) +} diff --git a/lego/utils/container/sortslice/uint32.go b/lego/utils/container/sortslice/uint32.go new file mode 100644 index 000000000..c466100c7 --- /dev/null +++ b/lego/utils/container/sortslice/uint32.go @@ -0,0 +1,27 @@ +package sortslice + +import ( + "fmt" + "hash/crc32" + "sort" +) + +type uInt32Slice []uint32 + +func (p uInt32Slice) Len() int { return len(p) } +func (p uInt32Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p uInt32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +// Sort is a convenience method. +func (p uInt32Slice) Sort() { sort.Sort(p) } + +func GetSessionId(session []uint32) (Id uint32) { + s := uInt32Slice(session) + s.Sort() + Session := "" + for _, v := range s { + Session = Session + fmt.Sprintf("%d", v) + } + Id = crc32.ChecksumIEEE([]byte(Session)) + return +} diff --git a/lego/utils/container/version/test_version.go b/lego/utils/container/version/test_version.go new file mode 100644 index 000000000..beffea127 --- /dev/null +++ b/lego/utils/container/version/test_version.go @@ -0,0 +1,15 @@ +package version + +import ( + "fmt" + "testing" + "time" +) + +func Test_Version(t *testing.T) { + versionA := "1.2.3a " + versionB := "1.2.3b " + fmt.Println(CompareStrVer(versionA, versionB)) + time.LoadLocation("Local ") + fmt.Println(time.Now()) +} diff --git a/lego/utils/container/version/version.go b/lego/utils/container/version/version.go new file mode 100644 index 000000000..5d25ffb2a --- /dev/null +++ b/lego/utils/container/version/version.go @@ -0,0 +1,66 @@ +package version + +import ( + "strings" +) + +func CompareStrVer(verA, verB string) int8 { + verStrArrA := spliteStrByNet(verA) + verStrArrB := spliteStrByNet(verB) + lenStrA := len(verStrArrA) + lenStrB := len(verStrArrB) + if lenStrA > lenStrB { + return 1 + } + if lenStrA < lenStrB { + return -1 + } + return compareArrStrVers(verStrArrA, verStrArrB) +} + +// 比较版本号字符串数组 +func compareArrStrVers(verA, verB []string) int8 { + for index, _ := range verA { + littleResult := compareLittleVer(verA[index], verB[index]) + if littleResult != 0 { + return littleResult + } + } + return 0 +} + +// +// 比较小版本号字符串 +// +func compareLittleVer(verA, verB string) int8 { + bytesA := []byte(verA) + bytesB := []byte(verB) + lenA := len(bytesA) + lenB := len(bytesB) + if lenA > lenB { + return 1 + } + if lenA < lenB { + return -1 + } + //如果长度相等则按byte位进行比较 + return compareByBytes(bytesA, bytesB) +} + +// 按byte位进行比较小版本号 +func compareByBytes(verA, verB []byte) int8 { + for index, _ := range verA { + if verA[index] > verB[index] { + return 1 + } + if verA[index] < verB[index] { + return -1 + } + } + return 0 +} + +// 按“.”分割版本号为小版本号的字符串数组 +func spliteStrByNet(strV string) []string { + return strings.Split(strV, ". ") +} diff --git a/lego/utils/convert/convert.go b/lego/utils/convert/convert.go new file mode 100644 index 000000000..5a3367a90 --- /dev/null +++ b/lego/utils/convert/convert.go @@ -0,0 +1,125 @@ +package convert + +import ( + "encoding/binary" + "math" + "unsafe" +) + +func ByteToBytes(v byte) []byte { + return []byte{v} +} + +func BoolToBytes(v bool) []byte { + var buf = make([]byte, 1) + if v { + buf[0] = 1 + } else { + buf[0] = 0 + } + return buf +} +func Int8ToBytes(v int8) []byte { + return []byte{(byte(v))} +} +func Int16ToBytes(v int16) []byte { + var buf = make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(v)) + return buf +} +func UInt16ToBytes(v uint16) []byte { + var buf = make([]byte, 2) + binary.BigEndian.PutUint16(buf, v) + return buf +} +func IntToBytes(v int) []byte { + var buf = make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(v)) + return buf +} +func Int32ToBytes(v int32) []byte { + var buf = make([]byte, 4) + binary.BigEndian.PutUint32(buf, uint32(v)) + return buf +} +func UInt32ToBytes(v uint32) []byte { + var buf = make([]byte, 4) + binary.BigEndian.PutUint32(buf, v) + return buf +} +func Int64ToBytes(v int64) []byte { + var buf = make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(v)) + return buf +} +func UInt64ToBytes(v uint64) []byte { + var buf = make([]byte, 8) + binary.BigEndian.PutUint64(buf, v) + return buf +} +func Float32ToBytes(v float32) []byte { + bits := math.Float32bits(v) + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, bits) + return bytes +} +func Float64ToBytes(v float64) []byte { + bits := math.Float64bits(v) + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, bits) + return bytes +} +func StringToBytes(s string) []byte { + return *(*[]byte)(unsafe.Pointer( + &struct { + string + Cap int + }{s, len(s)}, + )) +} + +func BytesTobyte(buf []byte) byte { + var data byte = buf[0] + return data +} +func BytesToBool(buf []byte) bool { + var data bool = buf[0] != 0 + return data +} +func BytesToInt8(buf []byte) int8 { + var data int8 = int8(buf[0]) + return data +} +func BytesToInt16(buf []byte) int16 { + return int16(binary.BigEndian.Uint16(buf)) +} +func BytesToUInt16(buf []byte) uint16 { + return binary.BigEndian.Uint16(buf) +} +func BytesToInt(buf []byte) int { + return int(binary.BigEndian.Uint32(buf)) +} +func BytesToInt32(buf []byte) int32 { + return int32(binary.BigEndian.Uint32(buf)) +} +func BytesToUInt32(buf []byte) uint32 { + return binary.BigEndian.Uint32(buf) +} +func BytesToInt64(buf []byte) int64 { + return int64(binary.BigEndian.Uint64(buf)) +} +func BytesToUInt64(buf []byte) uint64 { + return binary.BigEndian.Uint64(buf) +} +func BytesToFloat32(buf []byte) float32 { + bits := binary.LittleEndian.Uint32(buf) + return math.Float32frombits(bits) +} +func BytesToFloat64(buf []byte) float64 { + bits := binary.LittleEndian.Uint64(buf) + return math.Float64frombits(bits) +} + +func BytesToString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/lego/utils/copy/interface.go b/lego/utils/copy/interface.go new file mode 100644 index 000000000..d92f38acd --- /dev/null +++ b/lego/utils/copy/interface.go @@ -0,0 +1,77 @@ +package copy + +import ( + "reflect" + "time" +) + +func CopyInterface(src interface{}) interface{} { + if src == nil { + return nil + } + original := reflect.ValueOf(src) + cpy := reflect.New(original.Type()).Elem() + copyRecursive(original, cpy) + + return cpy.Interface() +} + +func copyRecursive(src, dst reflect.Value) { + switch src.Kind() { + case reflect.Ptr: + originalValue := src.Elem() + + if !originalValue.IsValid() { + return + } + dst.Set(reflect.New(originalValue.Type())) + copyRecursive(originalValue, dst.Elem()) + + case reflect.Interface: + if src.IsNil() { + return + } + originalValue := src.Elem() + copyValue := reflect.New(originalValue.Type()).Elem() + copyRecursive(originalValue, copyValue) + dst.Set(copyValue) + + case reflect.Struct: + t, ok := src.Interface().(time.Time) + if ok { + dst.Set(reflect.ValueOf(t)) + return + } + for i := 0; i < src.NumField(); i++ { + if src.Type().Field(i).PkgPath != "" { + continue + } + copyRecursive(src.Field(i), dst.Field(i)) + } + + case reflect.Slice: + if src.IsNil() { + return + } + dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Cap())) + for i := 0; i < src.Len(); i++ { + copyRecursive(src.Index(i), dst.Index(i)) + } + + case reflect.Map: + if src.IsNil() { + return + } + dst.Set(reflect.MakeMap(src.Type())) + for _, key := range src.MapKeys() { + originalValue := src.MapIndex(key) + copyValue := reflect.New(originalValue.Type()).Elem() + copyRecursive(originalValue, copyValue) + copyKey := CopyInterface(key.Interface()) + dst.SetMapIndex(reflect.ValueOf(copyKey), copyValue) + } + + default: + dst.Set(src) + } +} diff --git a/lego/utils/flietools/flietools.go b/lego/utils/flietools/flietools.go new file mode 100644 index 000000000..50bbdf7c7 --- /dev/null +++ b/lego/utils/flietools/flietools.go @@ -0,0 +1,84 @@ +package flietools + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "strings" +) + +//判断文件或文件夹是否存在 +func IsExist(path string) bool { + _, err := os.Stat(path) + if err != nil { + if os.IsExist(err) { + return true + } + if os.IsNotExist(err) { + return false + } + return false + } + return true +} + +//获取去除文件后缀的文件名称 +func GetFileNameSubSuffix(filepath string) string { + var fileSuffix string + fileSuffix = path.Ext(filepath) + var filenameOnly string + filenameOnly = strings.TrimSuffix(filepath, fileSuffix) + return filenameOnly +} + +//读取json文件到结构体中 参数必须是指针 +func ReadJsonFileToStruct(path string, d interface{}) error { + var data []byte + buf := new(bytes.Buffer) + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + r := bufio.NewReader(f) + for { + line, err := r.ReadSlice('\n') + if err != nil { + if len(line) > 0 { + buf.Write(line) + } + break + } + if !strings.HasPrefix(strings.TrimLeft(string(line), "\t "), "//") { + buf.Write(line) + } + } + data = buf.Bytes() + return json.Unmarshal(data, d) +} + +//将数据写入json文件中 +func WrietStructToJsonFile(path string, d interface{}) error { + data, err := json.Marshal(d) + if err != nil { + return err + } + err = ioutil.WriteFile(path, data, 0644) + return err +} + +//创建目录文件 +func CreateDirectory(logpath string) error { + logdir := string(logpath[0:strings.LastIndex(logpath, "/")]) + if !IsExist(logdir) { + err := os.MkdirAll(logdir, os.ModePerm) + if err != nil { + return fmt.Errorf("创建目录文件失败 1" + err.Error()) + } + } + return nil +} diff --git a/lego/utils/flietools/ziptools.go b/lego/utils/flietools/ziptools.go new file mode 100644 index 000000000..898a379f4 --- /dev/null +++ b/lego/utils/flietools/ziptools.go @@ -0,0 +1,45 @@ +package flietools + +import ( + "archive/zip" + "io" + "os" + "path/filepath" +) + +func Unzip(zipFile string, destDir string) error { + zipReader, err := zip.OpenReader(zipFile) + if err != nil { + return err + } + defer zipReader.Close() + + for _, f := range zipReader.File { + fpath := filepath.Join(destDir, f.Name) + if f.FileInfo().IsDir() { + os.MkdirAll(fpath, os.ModePerm) + } else { + if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return err + } + + inFile, err := f.Open() + if err != nil { + return err + } + defer inFile.Close() + + outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + defer outFile.Close() + + _, err = io.Copy(outFile, inFile) + if err != nil { + return err + } + } + } + return nil +} diff --git a/lego/utils/mapstructure/error.go b/lego/utils/mapstructure/error.go new file mode 100644 index 000000000..a25061567 --- /dev/null +++ b/lego/utils/mapstructure/error.go @@ -0,0 +1,32 @@ +package mapstructure + +import ( + "fmt" + "strings" +) + +// CodeError implements the error interface and can represents multiple +// errors that occur in the course of a single decode. +type Error struct { + Errors []string +} + +func (e *Error) Error() string { + points := make([]string, len(e.Errors)) + for i, err := range e.Errors { + points[i] = fmt.Sprintf("* %s", err) + } + + return fmt.Sprintf( + "%d error(s) decoding:\n\n%s", + len(e.Errors), strings.Join(points, "\n")) +} + +func appendErrors(errors []string, err error) []string { + switch e := err.(type) { + case *Error: + return append(errors, e.Errors...) + default: + return append(errors, e.Error()) + } +} diff --git a/lego/utils/mapstructure/mapstructure.go b/lego/utils/mapstructure/mapstructure.go new file mode 100644 index 000000000..e3b02201c --- /dev/null +++ b/lego/utils/mapstructure/mapstructure.go @@ -0,0 +1,857 @@ +// The mapstructure package exposes functionality to convert an +// abitrary map[string]interface{} into a native Go structure. +// +// The Go structure can be arbitrarily complex, containing slices, +// other structs, etc. and the decoder will properly decode nested +// maps and so on into the proper structures in the native Go struct. +// See the examples to see what the decoder is capable of. +package mapstructure + +import ( + "errors" + "fmt" + "reflect" + "sort" + "strconv" + "strings" +) + +type DecodeHookFunc func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) + +// DecoderConfig is the configuration that is used to create a new decoder +// and allows customization of various aspects of decoding. +type DecoderConfig struct { + // DecodeHook, if set, will be called before any decoding and any + // type conversion (if WeaklyTypedInput is on). This lets you modify + // the values before they're set down onto the resulting struct. + // + // If an error is returned, the entire decode will fail with that + // error. + DecodeHook DecodeHookFunc + + // If ErrorUnused is true, then it is an error for there to exist + // keys in the original map that were unused in the decoding process + // (extra keys). + ErrorUnused bool + + // If WeaklyTypedInput is true, the decoder will make the following + // "weak" conversions: + // + // - bools to string (true = "1", false = "0") + // - numbers to string (core 10) + // - bools to int/uint (true = 1, false = 0) + // - strings to int/uint (core implied by prefix) + // - int to bool (true if value != 0) + // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, + // FALSE, false, False. Anything else is an error) + // - empty array = empty map and vice versa + // + WeaklyTypedInput bool + + // Metadata is the struct that will contain extra metadata about + // the decoding. If this is nil, then no metadata will be tracked. + Metadata *Metadata + + // Result is a pointer to the struct that will contain the decoded + // value. + Result interface{} + + // The tag name that mapstructure reads for field names. This + // defaults to "mapstructure" + TagName string +} + +// A Decoder takes a raw interface value and turns it into structured +// data, keeping track of rich error information along the way in case +// anything goes wrong. Unlike the basic top-level Decode method, you can +// more finely control how the Decoder behaves using the DecoderConfig +// structure. The top-level Decode method is just a convenience that sets +// up the most basic Decoder. +type Decoder struct { + config *DecoderConfig +} + +// Metadata contains information about decoding a structure that +// is tedious or difficult to get otherwise. +type Metadata struct { + // Keys are the keys of the structure which were successfully decoded + Keys []string + + // Unused is a slice of keys that were found in the raw value but + // weren't decoded since there was no matching field in the result interface + Unused []string +} + +// Decode takes a map and uses reflection to convert it into the +// given Go native structure. val must be a pointer to a struct. +func Decode(m interface{}, rawVal interface{}) error { + config := &DecoderConfig{ + Metadata: nil, + Result: rawVal, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(m) +} + +// DecodePath takes a map and uses reflection to convert it into the +// given Go native structure. Tags are used to specify the mapping +// between fields in the map and structure +func DecodePath(m map[string]interface{}, rawVal interface{}) error { + config := &DecoderConfig{ + Metadata: nil, + Result: nil, + } + + decoder, err := NewPathDecoder(config) + if err != nil { + return err + } + + _, err = decoder.DecodePath(m, rawVal) + return err +} + +// DecodeSlicePath decodes a slice of maps against a slice of structures that +// contain specified tags +func DecodeSlicePath(ms []map[string]interface{}, rawSlice interface{}) error { + reflectRawSlice := reflect.TypeOf(rawSlice) + rawKind := reflectRawSlice.Kind() + rawElement := reflectRawSlice.Elem() + + if (rawKind == reflect.Ptr && rawElement.Kind() != reflect.Slice) || + (rawKind != reflect.Ptr && rawKind != reflect.Slice) { + return fmt.Errorf("Incompatible Value, Looking For Slice : %v : %v", rawKind, rawElement.Kind()) + } + + config := &DecoderConfig{ + Metadata: nil, + Result: nil, + } + + decoder, err := NewPathDecoder(config) + if err != nil { + return err + } + + // Create a slice large enough to decode all the values + valSlice := reflect.MakeSlice(rawElement, len(ms), len(ms)) + + // Iterate over the maps and decode each one + for index, m := range ms { + sliceElementType := rawElement.Elem() + if sliceElementType.Kind() != reflect.Ptr { + // A slice of objects + obj := reflect.New(rawElement.Elem()) + decoder.DecodePath(m, reflect.Indirect(obj)) + indexVal := valSlice.Index(index) + indexVal.Set(reflect.Indirect(obj)) + } else { + // A slice of pointers + obj := reflect.New(rawElement.Elem().Elem()) + decoder.DecodePath(m, reflect.Indirect(obj)) + indexVal := valSlice.Index(index) + indexVal.Set(obj) + } + } + + // Set the new slice + reflect.ValueOf(rawSlice).Elem().Set(valSlice) + return nil +} + +// NewDecoder returns a new decoder for the given configuration. Once +// a decoder has been returned, the same configuration must not be used +// again. +func NewDecoder(config *DecoderConfig) (*Decoder, error) { + val := reflect.ValueOf(config.Result) + if val.Kind() != reflect.Ptr { + return nil, errors.New("result must be a pointer") + } + + val = val.Elem() + if !val.CanAddr() { + return nil, errors.New("result must be addressable (a pointer)") + } + + if config.Metadata != nil { + if config.Metadata.Keys == nil { + config.Metadata.Keys = make([]string, 0) + } + + if config.Metadata.Unused == nil { + config.Metadata.Unused = make([]string, 0) + } + } + + if config.TagName == "" { + config.TagName = "mapstructure" + } + + result := &Decoder{ + config: config, + } + + return result, nil +} + +// NewPathDecoder returns a new decoder for the given configuration. +// This is used to decode path specific structures +func NewPathDecoder(config *DecoderConfig) (*Decoder, error) { + if config.Metadata != nil { + if config.Metadata.Keys == nil { + config.Metadata.Keys = make([]string, 0) + } + + if config.Metadata.Unused == nil { + config.Metadata.Unused = make([]string, 0) + } + } + + if config.TagName == "" { + config.TagName = "mapstructure" + } + + result := &Decoder{ + config: config, + } + + return result, nil +} + +// Decode decodes the given raw interface to the target pointer specified +// by the configuration. +func (d *Decoder) Decode(raw interface{}) error { + return d.decode("", raw, reflect.ValueOf(d.config.Result).Elem()) +} + +// DecodePath decodes the raw interface against the map based on the +// specified tags +func (d *Decoder) DecodePath(m map[string]interface{}, rawVal interface{}) (bool, error) { + decoded := false + + var val reflect.Value + reflectRawValue := reflect.ValueOf(rawVal) + kind := reflectRawValue.Kind() + + // Looking for structs and pointers to structs + switch kind { + case reflect.Ptr: + val = reflectRawValue.Elem() + if val.Kind() != reflect.Struct { + return decoded, fmt.Errorf("Incompatible Type : %v : Looking For Struct", kind) + } + case reflect.Struct: + var ok bool + val, ok = rawVal.(reflect.Value) + if ok == false { + return decoded, fmt.Errorf("Incompatible Type : %v : Looking For reflect.Value", kind) + } + default: + return decoded, fmt.Errorf("Incompatible Type : %v", kind) + } + + // Iterate over the fields in the struct + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + typeField := val.Type().Field(i) + tag := typeField.Tag + tagValue := tag.Get("jpath") + + // Is this a field without a tag + if tagValue == "" { + if valueField.Kind() == reflect.Struct { + // We have a struct that may have indivdual tags. Process separately + d.DecodePath(m, valueField) + continue + } else if valueField.Kind() == reflect.Ptr && reflect.TypeOf(valueField).Kind() == reflect.Struct { + // We have a pointer to a struct + if valueField.IsNil() { + // Create the object since it doesn't exist + valueField.Set(reflect.New(valueField.Type().Elem())) + decoded, _ = d.DecodePath(m, valueField.Elem()) + if decoded == false { + // If nothing was decoded for this object return the pointer to nil + valueField.Set(reflect.NewAt(valueField.Type().Elem(), nil)) + } + continue + } + + d.DecodePath(m, valueField.Elem()) + continue + } + } + + // Use mapstructure to populate the fields + keys := strings.Split(tagValue, ".") + data := d.findData(m, keys) + if data != nil { + if valueField.Kind() == reflect.Slice { + // Ignore a slice of maps - This sucks but not sure how to check + if strings.Contains(valueField.Type().String(), "map[") { + goto normal_decode + } + + // We have a slice + mapSlice := data.([]interface{}) + if len(mapSlice) > 0 { + // Test if this is a slice of more maps + _, ok := mapSlice[0].(map[string]interface{}) + if ok == false { + goto normal_decode + } + + // Extract the maps out and run it through DecodeSlicePath + ms := make([]map[string]interface{}, len(mapSlice)) + for index, m2 := range mapSlice { + ms[index] = m2.(map[string]interface{}) + } + + DecodeSlicePath(ms, valueField.Addr().Interface()) + continue + } + } + normal_decode: + decoded = true + err := d.decode("", data, valueField) + if err != nil { + return false, err + } + } + } + + return decoded, nil +} + +// Decodes an unknown data type into a specific reflection value. +func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error { + if data == nil { + // If the data is nil, then we don't set anything. + return nil + } + + dataVal := reflect.ValueOf(data) + if !dataVal.IsValid() { + // If the data value is invalid, then we just set the value + // to be the zero value. + val.Set(reflect.Zero(val.Type())) + return nil + } + + if d.config.DecodeHook != nil { + // We have a DecodeHook, so let's pre-process the data. + var err error + data, err = d.config.DecodeHook(d.getKind(dataVal), d.getKind(val), data) + if err != nil { + return err + } + } + + var err error + dataKind := d.getKind(val) + switch dataKind { + case reflect.Bool: + err = d.decodeBool(name, data, val) + case reflect.Interface: + err = d.decodeBasic(name, data, val) + case reflect.String: + err = d.decodeString(name, data, val) + case reflect.Int: + err = d.decodeInt(name, data, val) + case reflect.Uint: + err = d.decodeUint(name, data, val) + case reflect.Float32: + err = d.decodeFloat(name, data, val) + case reflect.Struct: + err = d.decodeStruct(name, data, val) + case reflect.Map: + err = d.decodeMap(name, data, val) + case reflect.Slice: + err = d.decodeSlice(name, data, val) + default: + // If we reached this point then we weren't able to decode it + return fmt.Errorf("%s: unsupported type: %s", name, dataKind) + } + + // If we reached here, then we successfully decoded SOMETHING, so + // mark the key as used if we're tracking metadata. + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + + return err +} + +// findData locates the data by walking the keys down the map +func (d *Decoder) findData(m map[string]interface{}, keys []string) interface{} { + if len(keys) == 1 { + if value, ok := m[keys[0]]; ok == true { + return value + } + return nil + } + + if value, ok := m[keys[0]]; ok == true { + if m, ok := value.(map[string]interface{}); ok == true { + return d.findData(m, keys[1:]) + } + } + + return nil +} + +func (d *Decoder) getKind(val reflect.Value) reflect.Kind { + kind := val.Kind() + + switch { + case kind >= reflect.Int && kind <= reflect.Int64: + return reflect.Int + case kind >= reflect.Uint && kind <= reflect.Uint64: + return reflect.Uint + case kind >= reflect.Float32 && kind <= reflect.Float64: + return reflect.Float32 + default: + return kind + } +} + +// This decodes a basic type (bool, int, string, etc.) and sets the +// value to "data" of that type. +func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + dataValType := dataVal.Type() + if !dataValType.AssignableTo(val.Type()) { + return fmt.Errorf( + "'%s' expected type '%s', got '%s'", + name, val.Type(), dataValType) + } + + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + dataKind := d.getKind(dataVal) + + switch { + case dataKind == reflect.String: + val.SetString(dataVal.String()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetString("1") + } else { + val.SetString("0") + } + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatInt(dataVal.Int(), 10)) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + dataKind := d.getKind(dataVal) + + switch { + case dataKind == reflect.Int: + val.SetInt(dataVal.Int()) + case dataKind == reflect.Uint: + val.SetInt(int64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetInt(int64(dataVal.Float())) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetInt(1) + } else { + val.SetInt(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits()) + if err == nil { + val.SetInt(i) + } else { + return fmt.Errorf("cannot parse '%s' as int: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + dataKind := d.getKind(dataVal) + + switch { + case dataKind == reflect.Int: + val.SetUint(uint64(dataVal.Int())) + case dataKind == reflect.Uint: + val.SetUint(dataVal.Uint()) + case dataKind == reflect.Float32: + val.SetUint(uint64(dataVal.Float())) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetUint(1) + } else { + val.SetUint(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits()) + if err == nil { + val.SetUint(i) + } else { + return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + dataKind := d.getKind(dataVal) + + switch { + case dataKind == reflect.Bool: + val.SetBool(dataVal.Bool()) + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Int() != 0) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Uint() != 0) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Float() != 0) + case dataKind == reflect.String && d.config.WeaklyTypedInput: + b, err := strconv.ParseBool(dataVal.String()) + if err == nil { + val.SetBool(b) + } else if dataVal.String() == "" { + val.SetBool(false) + } else { + return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.ValueOf(data) + dataKind := d.getKind(dataVal) + + switch { + case dataKind == reflect.Int: + val.SetFloat(float64(dataVal.Int())) + case dataKind == reflect.Uint: + val.SetFloat(float64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetFloat(float64(dataVal.Float())) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetFloat(1) + } else { + val.SetFloat(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits()) + if err == nil { + val.SetFloat(f) + } else { + return fmt.Errorf("cannot parse '%s' as float: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + + return nil +} + +func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // Make a new map to hold our result + mapType := reflect.MapOf(valKeyType, valElemType) + valMap := reflect.MakeMap(mapType) + + // Check input type + dataVal := reflect.Indirect(reflect.ValueOf(data)) + if dataVal.Kind() != reflect.Map { + // Accept empty array/slice instead of an empty map in weakly typed mode + if d.config.WeaklyTypedInput && + (dataVal.Kind() == reflect.Slice || dataVal.Kind() == reflect.Array) && + dataVal.Len() == 0 { + val.Set(valMap) + return nil + } else { + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + } + } + + // Accumulate errors + errors := make([]string, 0) + + for _, k := range dataVal.MapKeys() { + fieldName := fmt.Sprintf("%s[%s]", name, k) + + // First decode the key into the proper type + currentKey := reflect.Indirect(reflect.New(valKeyType)) + if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { + errors = appendErrors(errors, err) + continue + } + + // Next decode the data into the proper type + v := dataVal.MapIndex(k).Interface() + currentVal := reflect.Indirect(reflect.New(valElemType)) + if err := d.decode(fieldName, v, currentVal); err != nil { + errors = appendErrors(errors, err) + continue + } + + valMap.SetMapIndex(currentKey, currentVal) + } + + // Set the built up map to the value + val.Set(valMap) + + // If we had errors, return those + if len(errors) > 0 { + return &Error{errors} + } + + return nil +} + +func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + + // Make a new slice to hold our result, same size as the original data. + sliceType := reflect.SliceOf(valElemType) + valSlice := reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + // Accept empty map instead of array/slice in weakly typed mode + if d.config.WeaklyTypedInput && dataVal.Kind() == reflect.Map && dataVal.Len() == 0 { + val.Set(valSlice) + return nil + } else { + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + } + } + + // Accumulate any errors + errors := make([]string, 0) + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + currentField := valSlice.Index(i) + + fieldName := fmt.Sprintf("%s[%d]", name, i) + if err := d.decode(fieldName, currentData, currentField); err != nil { + errors = appendErrors(errors, err) + } + } + + // Finally, set the value to the slice we built up + val.Set(valSlice) + + // If there were errors, we return those + if len(errors) > 0 { + return &Error{errors} + } + + return nil +} + +func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + if dataValKind != reflect.Map { + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataValKind) + } + + dataValType := dataVal.Type() + if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { + return fmt.Errorf( + "'%s' needs a map with string keys, has '%s' keys", + name, dataValType.Key().Kind()) + } + + dataValKeys := make(map[reflect.Value]struct{}) + dataValKeysUnused := make(map[interface{}]struct{}) + for _, dataValKey := range dataVal.MapKeys() { + dataValKeys[dataValKey] = struct{}{} + dataValKeysUnused[dataValKey.Interface()] = struct{}{} + } + + errors := make([]string, 0) + + // This slice will keep track of all the structs we'll be decoding. + // There can be more than one struct if there are embedded structs + // that are squashed. + structs := make([]reflect.Value, 1, 5) + structs[0] = val + + // Compile the list of all the fields that we're going to be decoding + // from all the structs. + fields := make(map[*reflect.StructField]reflect.Value) + for len(structs) > 0 { + structVal := structs[0] + structs = structs[1:] + + structType := structVal.Type() + for i := 0; i < structType.NumField(); i++ { + fieldType := structType.Field(i) + + if fieldType.Anonymous { + fieldKind := fieldType.Type.Kind() + if fieldKind != reflect.Struct { + errors = appendErrors(errors, + fmt.Errorf("%s: unsupported type: %s", fieldType.Name, fieldKind)) + continue + } + + // We have an embedded field. We "squash" the fields down + // if specified in the tag. + squash := false + tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") + for _, tag := range tagParts[1:] { + if tag == "squash" { + squash = true + break + } + } + + if squash { + structs = append(structs, val.FieldByName(fieldType.Name)) + continue + } + } + + // Normal struct field, store it away + fields[&fieldType] = structVal.Field(i) + } + } + + for fieldType, field := range fields { + fieldName := fieldType.Name + + tagValue := fieldType.Tag.Get(d.config.TagName) + tagValue = strings.SplitN(tagValue, ",", 2)[0] + if tagValue != "" { + fieldName = tagValue + } + + rawMapKey := reflect.ValueOf(fieldName) + rawMapVal := dataVal.MapIndex(rawMapKey) + if !rawMapVal.IsValid() { + // Do a slower search by iterating over each key and + // doing case-insensitive search. + for dataValKey, _ := range dataValKeys { + mK, ok := dataValKey.Interface().(string) + if !ok { + // Not a string key + continue + } + + if strings.EqualFold(mK, fieldName) { + rawMapKey = dataValKey + rawMapVal = dataVal.MapIndex(dataValKey) + break + } + } + + if !rawMapVal.IsValid() { + // There was no matching key in the map for the value in + // the struct. Just ignore. + continue + } + } + + // Delete the key we're using from the unused map so we stop tracking + delete(dataValKeysUnused, rawMapKey.Interface()) + + if !field.IsValid() { + // This should never happen + panic("field is not valid") + } + + // If we can't set the field, then it is unexported or something, + // and we just continue onwards. + if !field.CanSet() { + continue + } + + // If the name is empty string, then we're at the root, and we + // don't dot-join the fields. + if name != "" { + fieldName = fmt.Sprintf("%s.%s", name, fieldName) + } + + if err := d.decode(fieldName, rawMapVal.Interface(), field); err != nil { + errors = appendErrors(errors, err) + } + } + + if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { + keys := make([]string, 0, len(dataValKeysUnused)) + for rawKey, _ := range dataValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) + errors = appendErrors(errors, err) + } + + if len(errors) > 0 { + return &Error{errors} + } + + // Add the unused keys to the list of unused keys if we're tracking metadata + if d.config.Metadata != nil { + for rawKey, _ := range dataValKeysUnused { + key := rawKey.(string) + if name != "" { + key = fmt.Sprintf("%s.%s", name, key) + } + + d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) + } + } + + return nil +} diff --git a/modules/core.go b/modules/core.go index 64a62035e..f6f0e550d 100644 --- a/modules/core.go +++ b/modules/core.go @@ -3,7 +3,8 @@ package modules import ( "go_dreamfactory/pb" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" + "google.golang.org/protobuf/proto" ) diff --git a/modules/gate_comp.go b/modules/gate_comp.go index be6e14e4f..b9ae39de7 100644 --- a/modules/gate_comp.go +++ b/modules/gate_comp.go @@ -9,9 +9,9 @@ import ( "unicode" "unicode/utf8" - "github.com/liwei1dao/lego/base" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" ) var typeOfContext = reflect.TypeOf((*context.Context)(nil)).Elem() diff --git a/modules/gateway/agent.go b/modules/gateway/agent.go index ac7ca93f2..413ed8de1 100644 --- a/modules/gateway/agent.go +++ b/modules/gateway/agent.go @@ -8,9 +8,10 @@ import ( "sync" "sync/atomic" + "go_dreamfactory/lego/sys/log" + "go_dreamfactory/lego/utils/container/id" + "github.com/gorilla/websocket" - "github.com/liwei1dao/lego/sys/log" - "github.com/liwei1dao/lego/utils/container/id" "google.golang.org/protobuf/proto" ) diff --git a/modules/gateway/agentmgr_comp.go b/modules/gateway/agentmgr_comp.go index 5c6043111..04dbea280 100644 --- a/modules/gateway/agentmgr_comp.go +++ b/modules/gateway/agentmgr_comp.go @@ -5,8 +5,8 @@ import ( "go_dreamfactory/pb" "sync" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" ) type AgentMgr_Comp struct { diff --git a/modules/gateway/core.go b/modules/gateway/core.go index c0313200c..4605d0010 100644 --- a/modules/gateway/core.go +++ b/modules/gateway/core.go @@ -3,8 +3,8 @@ package gateway import ( "go_dreamfactory/pb" - "github.com/liwei1dao/lego/base" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/core" ) type ( diff --git a/modules/gateway/module.go b/modules/gateway/module.go index 718758e6c..12e54fda5 100644 --- a/modules/gateway/module.go +++ b/modules/gateway/module.go @@ -4,9 +4,9 @@ import ( "go_dreamfactory/comm" "go_dreamfactory/modules" - "github.com/liwei1dao/lego/base" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" ) func NewModule() core.IModule { diff --git a/modules/gateway/options.go b/modules/gateway/options.go index 2cc769d01..9ca5f3e6f 100644 --- a/modules/gateway/options.go +++ b/modules/gateway/options.go @@ -1,7 +1,7 @@ package gateway import ( - "github.com/liwei1dao/lego/utils/mapstructure" + "go_dreamfactory/lego/utils/mapstructure" ) type ( diff --git a/modules/gateway/wservice_comp.go b/modules/gateway/wservice_comp.go index d77a9f696..be7304e2a 100644 --- a/modules/gateway/wservice_comp.go +++ b/modules/gateway/wservice_comp.go @@ -3,12 +3,13 @@ package gateway import ( "net/http" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" + "go_dreamfactory/lego/sys/gin" + "go_dreamfactory/lego/sys/gin/engine" + "go_dreamfactory/lego/sys/log" + "github.com/gorilla/websocket" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" - "github.com/liwei1dao/lego/sys/gin" - "github.com/liwei1dao/lego/sys/gin/engine" - "github.com/liwei1dao/lego/sys/log" ) type WSService_Comp struct { diff --git a/modules/mail/api_comp.go b/modules/mail/api_comp.go index 4c938fc40..068521e19 100644 --- a/modules/mail/api_comp.go +++ b/modules/mail/api_comp.go @@ -3,7 +3,7 @@ package mail import ( "go_dreamfactory/modules" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" ) const ( diff --git a/modules/mail/configure_comp.go b/modules/mail/configure_comp.go index 5ba11ce3b..34790e964 100644 --- a/modules/mail/configure_comp.go +++ b/modules/mail/configure_comp.go @@ -1,8 +1,8 @@ package mail import ( - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" ) // 邮件配置管理组件 diff --git a/modules/mail/module.go b/modules/mail/module.go index 8ec9fa452..9b9f4c614 100644 --- a/modules/mail/module.go +++ b/modules/mail/module.go @@ -4,7 +4,7 @@ import ( "go_dreamfactory/comm" "go_dreamfactory/modules" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" ) /* diff --git a/modules/modulebase.go b/modules/modulebase.go index 253916490..773f830da 100644 --- a/modules/modulebase.go +++ b/modules/modulebase.go @@ -5,10 +5,11 @@ import ( "go_dreamfactory/comm" "go_dreamfactory/pb" - "github.com/liwei1dao/lego/base" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" + "go_dreamfactory/lego/sys/log" + "google.golang.org/protobuf/proto" ) diff --git a/modules/pack/api_comp.go b/modules/pack/api_comp.go index 92e17a0aa..8d2cfbaf5 100644 --- a/modules/pack/api_comp.go +++ b/modules/pack/api_comp.go @@ -7,8 +7,8 @@ import ( "go_dreamfactory/pb" "go_dreamfactory/sys/cache" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" ) const ( diff --git a/modules/pack/configure_comp.go b/modules/pack/configure_comp.go index aacc0e6cb..cc45c8f7c 100644 --- a/modules/pack/configure_comp.go +++ b/modules/pack/configure_comp.go @@ -6,9 +6,9 @@ import ( "go_dreamfactory/sys/configure" cfg "go_dreamfactory/sys/configure/structs" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" + "go_dreamfactory/lego/sys/log" ) const ( diff --git a/modules/pack/module.go b/modules/pack/module.go index 614479cdb..e9871b0c7 100644 --- a/modules/pack/module.go +++ b/modules/pack/module.go @@ -4,7 +4,7 @@ import ( "go_dreamfactory/comm" "go_dreamfactory/modules" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" ) /* diff --git a/modules/user/login_comp.go b/modules/user/login_comp.go index 447eeb821..c6fd8c6f6 100644 --- a/modules/user/login_comp.go +++ b/modules/user/login_comp.go @@ -10,7 +10,8 @@ import ( cfg "go_dreamfactory/sys/configure/structs" "go_dreamfactory/sys/db" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/sys/log" + "go.mongodb.org/mongo-driver/mongo" ) diff --git a/modules/user/module.go b/modules/user/module.go index 47afe980f..255b209bf 100644 --- a/modules/user/module.go +++ b/modules/user/module.go @@ -4,7 +4,7 @@ import ( "go_dreamfactory/comm" "go_dreamfactory/modules" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" ) func NewModule() core.IModule { diff --git a/modules/web/api_comp.go b/modules/web/api_comp.go index b41c68dc4..ff5c86d32 100644 --- a/modules/web/api_comp.go +++ b/modules/web/api_comp.go @@ -5,11 +5,11 @@ import ( "go_dreamfactory/sys/db" "net/http" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" - "github.com/liwei1dao/lego/sys/gin" - "github.com/liwei1dao/lego/sys/gin/engine" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" + "go_dreamfactory/lego/sys/gin" + "go_dreamfactory/lego/sys/gin/engine" + "go_dreamfactory/lego/sys/log" ) type Api_Comp struct { diff --git a/modules/web/module.go b/modules/web/module.go index 0524d2d72..14ad174bc 100644 --- a/modules/web/module.go +++ b/modules/web/module.go @@ -5,7 +5,7 @@ import ( "go_dreamfactory/modules" cfg "go_dreamfactory/sys/configure/structs" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" ) func NewModule() core.IModule { diff --git a/modules/web/options.go b/modules/web/options.go index 43e368bd9..3750894b2 100644 --- a/modules/web/options.go +++ b/modules/web/options.go @@ -1,7 +1,7 @@ package web import ( - "github.com/liwei1dao/lego/utils/mapstructure" + "go_dreamfactory/lego/utils/mapstructure" ) type ( diff --git a/services/comp_gateroute.go b/services/comp_gateroute.go index a84783f8e..6fa17f3fc 100644 --- a/services/comp_gateroute.go +++ b/services/comp_gateroute.go @@ -7,10 +7,11 @@ import ( "reflect" "sync" - "github.com/liwei1dao/lego/base" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/core/cbase" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/base" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/core/cbase" + "go_dreamfactory/lego/sys/log" + "google.golang.org/protobuf/proto" ) diff --git a/services/gateway/main.go b/services/gateway/main.go index 6fd0e8387..f53abb136 100644 --- a/services/gateway/main.go +++ b/services/gateway/main.go @@ -5,9 +5,9 @@ import ( "go_dreamfactory/modules/gateway" "go_dreamfactory/services" - "github.com/liwei1dao/lego" - "github.com/liwei1dao/lego/base/rpcx" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego" + "go_dreamfactory/lego/base/rpcx" + "go_dreamfactory/lego/core" ) var ( diff --git a/services/servicebase.go b/services/servicebase.go index 1da8b2afa..eb8634a8a 100644 --- a/services/servicebase.go +++ b/services/servicebase.go @@ -4,8 +4,8 @@ import ( "fmt" "go_dreamfactory/sys/configure" - "github.com/liwei1dao/lego/base/rpcx" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/base/rpcx" + "go_dreamfactory/lego/sys/log" ) type ServiceBase struct { diff --git a/services/web/main.go b/services/web/main.go index 43e73f3b3..686219c60 100644 --- a/services/web/main.go +++ b/services/web/main.go @@ -8,10 +8,10 @@ import ( "go_dreamfactory/sys/cache" "go_dreamfactory/sys/db" - "github.com/liwei1dao/lego" - "github.com/liwei1dao/lego/base/rpcx" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego" + "go_dreamfactory/lego/base/rpcx" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" ) var ( diff --git a/services/worker/main.go b/services/worker/main.go index 97ea5f94c..541459e6e 100644 --- a/services/worker/main.go +++ b/services/worker/main.go @@ -10,10 +10,10 @@ import ( "go_dreamfactory/sys/cache" "go_dreamfactory/sys/db" - "github.com/liwei1dao/lego" - "github.com/liwei1dao/lego/base/rpcx" - "github.com/liwei1dao/lego/core" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego" + "go_dreamfactory/lego/base/rpcx" + "go_dreamfactory/lego/core" + "go_dreamfactory/lego/sys/log" ) var ( diff --git a/sys/cache/cache.go b/sys/cache/cache.go index 170cedcf3..8fa30047e 100644 --- a/sys/cache/cache.go +++ b/sys/cache/cache.go @@ -1,6 +1,6 @@ package cache -import "github.com/liwei1dao/lego/sys/redis" +import "go_dreamfactory/lego/sys/redis" func newSys(options Options) (sys *Cache, err error) { sys = &Cache{options: options} diff --git a/sys/cache/mail.go b/sys/cache/mail.go index d42970c83..2c2ad6d34 100644 --- a/sys/cache/mail.go +++ b/sys/cache/mail.go @@ -5,8 +5,8 @@ import ( "go_dreamfactory/pb" "go_dreamfactory/sys/db" - "github.com/liwei1dao/lego/sys/mgo" - "github.com/liwei1dao/lego/sys/redis" + "go_dreamfactory/lego/sys/mgo" + "go_dreamfactory/lego/sys/redis" ) const ( //Redis diff --git a/sys/cache/options.go b/sys/cache/options.go index 6e6a800f5..2f11c6188 100644 --- a/sys/cache/options.go +++ b/sys/cache/options.go @@ -3,7 +3,7 @@ package cache import ( "errors" - "github.com/liwei1dao/lego/utils/mapstructure" + "go_dreamfactory/lego/utils/mapstructure" ) type Option func(*Options) diff --git a/sys/cache/pack.go b/sys/cache/pack.go index 47a7ac56f..15a96f42f 100644 --- a/sys/cache/pack.go +++ b/sys/cache/pack.go @@ -5,8 +5,8 @@ import ( "go_dreamfactory/pb" "go_dreamfactory/sys/db" - "github.com/liwei1dao/lego/sys/mgo" - "github.com/liwei1dao/lego/sys/redis" + "go_dreamfactory/lego/sys/mgo" + "go_dreamfactory/lego/sys/redis" ) const ( //Redis diff --git a/sys/cache/user_test.go b/sys/cache/user_test.go index 0f2a97533..d434d2cfe 100644 --- a/sys/cache/user_test.go +++ b/sys/cache/user_test.go @@ -5,7 +5,8 @@ import ( "log" "testing" - "github.com/liwei1dao/lego/sys/redis" + "go_dreamfactory/lego/sys/redis" + "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson/primitive" ) diff --git a/sys/configure/options.go b/sys/configure/options.go index f0990c4a9..5917e51f4 100644 --- a/sys/configure/options.go +++ b/sys/configure/options.go @@ -1,7 +1,7 @@ package configure import ( - "github.com/liwei1dao/lego/utils/mapstructure" + "go_dreamfactory/lego/utils/mapstructure" ) type Option func(*Options) diff --git a/sys/db/db.go b/sys/db/db.go index bac81395f..832f8b9ad 100644 --- a/sys/db/db.go +++ b/sys/db/db.go @@ -1,7 +1,7 @@ package db import ( - "github.com/liwei1dao/lego/sys/mgo" + "go_dreamfactory/lego/sys/mgo" ) func newSys(options Options) (sys *DB, err error) { diff --git a/sys/db/mail.go b/sys/db/mail.go index 5772e2e42..790ea1376 100644 --- a/sys/db/mail.go +++ b/sys/db/mail.go @@ -3,7 +3,8 @@ package db import ( "go_dreamfactory/pb" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" + "go.mongodb.org/mongo-driver/bson" ) diff --git a/sys/db/options.go b/sys/db/options.go index 5afe3deb7..eaa059085 100644 --- a/sys/db/options.go +++ b/sys/db/options.go @@ -3,7 +3,7 @@ package db import ( "errors" - "github.com/liwei1dao/lego/utils/mapstructure" + "go_dreamfactory/lego/utils/mapstructure" ) type Option func(*Options) diff --git a/sys/db/pack.go b/sys/db/pack.go index b159c3caf..f56eea9d4 100644 --- a/sys/db/pack.go +++ b/sys/db/pack.go @@ -4,7 +4,8 @@ import ( "fmt" "go_dreamfactory/pb" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) diff --git a/sys/db/user.go b/sys/db/user.go index 12b4688d5..683bd4e32 100644 --- a/sys/db/user.go +++ b/sys/db/user.go @@ -3,7 +3,8 @@ package db import ( "go_dreamfactory/pb" - "github.com/liwei1dao/lego/core" + "go_dreamfactory/lego/core" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo/options" diff --git a/sys/db/user_test.go b/sys/db/user_test.go index b0128ad23..25e3e97b3 100644 --- a/sys/db/user_test.go +++ b/sys/db/user_test.go @@ -6,7 +6,8 @@ import ( "os" "testing" - "github.com/liwei1dao/lego/sys/mgo" + "go_dreamfactory/lego/sys/mgo" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson/primitive" diff --git a/utils/strings.go b/utils/strings.go index 883d73472..34d87e3ed 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -3,7 +3,7 @@ package utils import ( "strings" - "github.com/liwei1dao/lego/sys/log" + "go_dreamfactory/lego/sys/log" ) func ParseP(p string) (string, string, bool) {