From 36fbe57d5cfe56388b4ba250348d0363089bdc09 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Tue, 1 Nov 2022 00:49:47 +0800 Subject: [PATCH 1/9] add redis registry --- redis/LICENSE | 201 +++++++++++++++++++++++++++++++++++ redis/Makefile | 3 + redis/README.md | 100 +++++++++++++++++ redis/common.go | 130 ++++++++++++++++++++++ redis/example/client/main.go | 27 +++++ redis/example/server/main.go | 30 ++++++ redis/go.mod | 9 ++ redis/go.sum | 171 +++++++++++++++++++++++++++++ redis/mentor.go | 88 +++++++++++++++ redis/redis_test.go | 185 ++++++++++++++++++++++++++++++++ redis/registry.go | 84 +++++++++++++++ redis/resolver.go | 61 +++++++++++ 12 files changed, 1089 insertions(+) create mode 100644 redis/LICENSE create mode 100644 redis/Makefile create mode 100644 redis/README.md create mode 100644 redis/common.go create mode 100644 redis/example/client/main.go create mode 100644 redis/example/server/main.go create mode 100644 redis/go.mod create mode 100644 redis/go.sum create mode 100644 redis/mentor.go create mode 100644 redis/redis_test.go create mode 100644 redis/registry.go create mode 100644 redis/resolver.go diff --git a/redis/LICENSE b/redis/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/redis/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/redis/Makefile b/redis/Makefile new file mode 100644 index 0000000..7c8c08a --- /dev/null +++ b/redis/Makefile @@ -0,0 +1,3 @@ +prepare: + docker pull redis:latest + docker run --name dev-redis -p 6379:6379 -d redis:latest \ No newline at end of file diff --git a/redis/README.md b/redis/README.md new file mode 100644 index 0000000..2d202ed --- /dev/null +++ b/redis/README.md @@ -0,0 +1,100 @@ +# redis (*This is a community driven project*) + +Redis as service discovery for Hertz. + +## How to use? + +### Server + +**[example/server/main.go](example/server/main.go)** + +```go +package main + +import ( + "context" + "registry/redis" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/cloudwego/hertz/pkg/common/utils" + "github.com/cloudwego/hertz/pkg/protocol/consts" +) + +func main() { + r := redis.NewRedisRegistry("127.0.0.1:6379") + addr := "127.0.0.1:8888" + h := server.Default( + server.WithHostPorts(addr), + server.WithRegistry(r, ®istry.Info{ + ServiceName: "hertz.test.demo", + Addr: utils.NewNetAddr("tcp", addr), + Weight: 10, + Tags: nil, + }), + ) + h.GET("/ping", func(_ context.Context, ctx *app.RequestContext) { + ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) + }) + h.Spin() +} +``` + +### Client + +**[example/client/main.go](example/client/main.go)** + +```go +package main + +import ( + "context" + "registry/redis" + + "github.com/cloudwego/hertz/pkg/app/client" + "github.com/cloudwego/hertz/pkg/app/middlewares/client/sd" + "github.com/cloudwego/hertz/pkg/common/config" + "github.com/cloudwego/hertz/pkg/common/hlog" +) + +func main() { + cli, err := client.NewClient() + if err != nil { + panic(err) + } + r := redis.NewRedisResolver("127.0.0.1:6379") + cli.Use(sd.Discovery(r)) + for i := 0; i < 10; i++ { + status, body, err := cli.Get(context.Background(), nil, "http://hertz.test.demo/ping", config.WithSD(true)) + if err != nil { + hlog.Fatal(err) + } + hlog.Infof("HERTZ: code=%d,body=%s", status, string(body)) + } +} +``` + +## How to run example? + +### run docker + +```bash +make prepare +``` + +### run server + +```go +go run ./example/server/main.go +``` + +### run client + +```go +go run ./example/client/main.go +``` + +## Compatibility + +Redis client for Go [see](https://github.com/go-redis/redis) \ No newline at end of file diff --git a/redis/common.go b/redis/common.go new file mode 100644 index 0000000..e29b344 --- /dev/null +++ b/redis/common.go @@ -0,0 +1,130 @@ +package redis + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "net" + "time" + + "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/go-redis/redis/v8" +) + +const ( + Redis = "redis" + register = "register" + deregister = "deregister" + hertz = "hertz" + server = "server" + client = "client" + tcp = "tcp" +) + +const ( + defaultExpireTime = time.Second * 60 + defaultTickerTime = time.Second * 30 + defaultKeepAliveTime = time.Second * 60 + defaultMonitorTime = time.Second * 30 + defaultWeight = 10 +) + +type Option func(opts *redis.Options) + +func WithPassword(password string) Option { + return func(opts *redis.Options) { + opts.Password = password + } +} + +func WithDB(db int) Option { + return func(opts *redis.Options) { + opts.DB = db + } +} + +func WithTLSConfig(t *tls.Config) Option { + return func(opts *redis.Options) { + opts.TLSConfig = t + } +} + +func WithDialer(dialer func(ctx context.Context, network, addr string) (net.Conn, error)) Option { + return func(opts *redis.Options) { + opts.Dialer = dialer + } +} + +func WithReadTimeout(t time.Duration) Option { + return func(opts *redis.Options) { + opts.ReadTimeout = t + } +} + +func WithWriteTimeout(t time.Duration) Option { + return func(opts *redis.Options) { + opts.WriteTimeout = t + } +} + +type registryHash struct { + key string + field string + value string +} + +type registryInfo struct { + ServiceName string `json:"service_name"` + Addr string `json:"addr"` + Weight int `json:"weight"` + Tags map[string]string `json:"tags"` +} + +func validateRegistryInfo(info *registry.Info) error { + if info == nil { + return fmt.Errorf("registry.Info can not be empty") + } + if info.ServiceName == "" { + return fmt.Errorf("registry.Info ServiceName can not be empty") + } + if info.Addr == nil { + return fmt.Errorf("registry.Info Addr can not be empty") + } + return nil +} + +func prepareRegistryHash(info *registry.Info) (*registryHash, error) { + meta, err := json.Marshal(convertInfo(info)) + if err != nil { + return nil, err + } + return ®istryHash{ + key: fmt.Sprintf("/hertz/%s/%s", info.ServiceName, server), + field: info.Addr.String(), + value: string(meta), + }, nil +} + +func convertInfo(info *registry.Info) *registryInfo { + return ®istryInfo{ + ServiceName: info.ServiceName, + Addr: info.Addr.String(), + Weight: info.Weight, + Tags: info.Tags, + } +} + +func keepAlive(ctx context.Context, hash *registryHash, r *redisRegistry) { + ticker := time.NewTicker(defaultTickerTime) + defer ticker.Stop() + for { + select { + case <-ticker.C: + r.client.Expire(ctx, hash.key, defaultKeepAliveTime) + case <-ctx.Done(): + break + default: + } + } +} diff --git a/redis/example/client/main.go b/redis/example/client/main.go new file mode 100644 index 0000000..218a462 --- /dev/null +++ b/redis/example/client/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "context" + "registry/redis" + + "github.com/cloudwego/hertz/pkg/app/client" + "github.com/cloudwego/hertz/pkg/app/middlewares/client/sd" + "github.com/cloudwego/hertz/pkg/common/config" + "github.com/cloudwego/hertz/pkg/common/hlog" +) + +func main() { + cli, err := client.NewClient() + if err != nil { + panic(err) + } + r := redis.NewRedisResolver("127.0.0.1:6379") + cli.Use(sd.Discovery(r)) + for i := 0; i < 10; i++ { + status, body, err := cli.Get(context.Background(), nil, "http://hertz.test.demo/ping", config.WithSD(true)) + if err != nil { + hlog.Fatal(err) + } + hlog.Infof("HERTZ: code=%d,body=%s", status, string(body)) + } +} diff --git a/redis/example/server/main.go b/redis/example/server/main.go new file mode 100644 index 0000000..dd57ef6 --- /dev/null +++ b/redis/example/server/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "context" + "registry/redis" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/cloudwego/hertz/pkg/common/utils" + "github.com/cloudwego/hertz/pkg/protocol/consts" +) + +func main() { + r := redis.NewRedisRegistry("127.0.0.1:6379") + addr := "127.0.0.1:8888" + h := server.Default( + server.WithHostPorts(addr), + server.WithRegistry(r, ®istry.Info{ + ServiceName: "hertz.test.demo", + Addr: utils.NewNetAddr("tcp", addr), + Weight: 10, + Tags: nil, + }), + ) + h.GET("/ping", func(_ context.Context, ctx *app.RequestContext) { + ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) + }) + h.Spin() +} diff --git a/redis/go.mod b/redis/go.mod new file mode 100644 index 0000000..fb661a8 --- /dev/null +++ b/redis/go.mod @@ -0,0 +1,9 @@ +module registry/redis + +go 1.16 + +require ( + github.com/cloudwego/hertz v0.3.2 + github.com/go-redis/redis/v8 v8.11.5 + github.com/stretchr/testify v1.7.0 +) diff --git a/redis/go.sum b/redis/go.sum new file mode 100644 index 0000000..406fafd --- /dev/null +++ b/redis/go.sum @@ -0,0 +1,171 @@ +github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= +github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 h1:PtwsQyQJGxf8iaPptPNaduEIu9BnrNms+pcRdHAxZaM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= +github.com/bytedance/sonic v1.3.5 h1:xfBNhsG3QCC+AMCmCHxNQg0StI5IM/B9Jtwjqi5WlI0= +github.com/bytedance/sonic v1.3.5/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cloudwego/hertz v0.3.2 h1:YwGmozomDPiJhKfKjKggMUmfNBNHR9iQGVLjY3wJpsY= +github.com/cloudwego/hertz v0.3.2/go.mod h1:hnv3B7eZ6kMv7CKFHT2OC4LU0mA4s5XPyu/SbixLcrU= +github.com/cloudwego/netpoll v0.2.6 h1:vzN8cyayoa9RdCOG87tqkYO/j2hA4SMLC+vkcNUq6uI= +github.com/cloudwego/netpoll v0.2.6/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +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/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/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.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/goccy/go-json v0.9.4 h1:L8MLKG2mvVXiQu07qB6hmfqeSYQdOnqPot2GhsIwIaI= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= +github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= +github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc= +github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20200323222414-85ca7c5b95cd/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= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/redis/mentor.go b/redis/mentor.go new file mode 100644 index 0000000..35582b3 --- /dev/null +++ b/redis/mentor.go @@ -0,0 +1,88 @@ +package redis + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/cloudwego/hertz/pkg/common/hlog" +) + +var gm *mentor + +var form = make(map[string]addrs) + +type addrs []string + +type mentor struct { + mform map[string]addrs +} + +// newMentor use singleton +func newMentor() *mentor { + if gm != nil { + return gm + } + m := &mentor{mform: form} + gm = m + return gm +} + +func (m *mentor) subscribe(ctx context.Context, info *registry.Info, r *redisRegistry) { + sub := r.client.Subscribe(ctx, fmt.Sprintf("/%s/%s/%s", hertz, info.ServiceName, server)) + defer sub.Close() + r.wg.Done() + select { + case <-ctx.Done(): + return + default: + ch := sub.Channel() + for msg := range ch { + split := strings.Split(msg.Payload, "-") + if split[0] == register { + m.insertForm(split[1], split[2]) + hlog.Infof("HERTZ: service info %v", m.mform) + } else if split[0] == deregister { + m.removeAddr(split[1], split[2]) + hlog.Infof("HERTZ: service info %v", m.mform) + } else { + hlog.Warnf("HERTZ: invalid message %v", msg) + } + } + } + +} + +func (m *mentor) monitorTTL(ctx context.Context, hash *registryHash, info *registry.Info, r *redisRegistry) { + ticker := time.NewTicker(defaultMonitorTime) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if r.client.TTL(ctx, hash.key).Val() == -2 { + m.removeService(info.ServiceName) + } + case <-ctx.Done(): + break + default: + } + } +} + +func (m *mentor) insertForm(serviceName string, addr string) { + m.mform[serviceName] = append(m.mform[serviceName], addr) +} + +func (m *mentor) removeService(serviceName string) { + delete(m.mform, serviceName) +} + +func (m *mentor) removeAddr(serviceName string, addr string) { + for i, v := range m.mform[serviceName] { + if v == addr { + m.mform[serviceName] = append(m.mform[serviceName][:i], m.mform[serviceName][i+1:]...) + } + } +} diff --git a/redis/redis_test.go b/redis/redis_test.go new file mode 100644 index 0000000..90f8896 --- /dev/null +++ b/redis/redis_test.go @@ -0,0 +1,185 @@ +package redis + +import ( + "context" + "encoding/json" + "testing" + + "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/cloudwego/hertz/pkg/common/utils" + "github.com/go-redis/redis/v8" + "github.com/stretchr/testify/assert" +) + +var redisCli *redis.Client +var ctx = context.Background() + +func init() { + rdb := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"}) + redisCli = rdb +} + +// TestRegister Test the Registry in registry.go +func TestRegister(t *testing.T) { + tests := []struct { + info []*registry.Info + wantErr bool + }{ + { + // set single info + info: []*registry.Info{ + { + ServiceName: "hertz.test.demo1", + Addr: utils.NewNetAddr(tcp, "127.0.0.1:8888"), + Weight: 10, + Tags: nil, + }, + }, + wantErr: false, + }, + { + // set multi infos + info: []*registry.Info{ + { + ServiceName: "hertz.test.demo2", + Addr: utils.NewNetAddr(tcp, "127.0.0.1:9000"), + Weight: 15, + Tags: nil, + }, + { + ServiceName: "hertz.test.demo2", + Addr: utils.NewNetAddr(tcp, "127.0.0.1:9001"), + Weight: 20, + Tags: nil, + }, + }, + wantErr: false, + }, + } + for _, test := range tests { + r := NewRedisRegistry("127.0.0.1:6379") + for _, info := range test.info { + if err := r.Register(info); err != nil { + t.Errorf("info register err") + } + hash, err := prepareRegistryHash(info) + assert.False(t, err != nil) + val := redisCli.HGet(ctx, hash.key, hash.field).Val() + ri := ®istryInfo{} + err = json.Unmarshal([]byte(val), ri) + assert.False(t, err != nil) + assert.Equal(t, info.ServiceName, ri.ServiceName) + assert.Equal(t, info.Addr.String(), ri.Addr) + assert.Equal(t, info.Weight, ri.Weight) + assert.Equal(t, info.Tags, ri.Tags) + } + } +} + +// TestResolve Test the Resolver in resolver.go +func TestResolve(t *testing.T) { + type args struct { + Addr string + Weight int + Tags map[string]string + } + type info struct { + ServiceName string + Args []args + } + tests := []struct { + info *info + wantErr bool + }{ + { + // test one args + info: &info{ + ServiceName: "demo1.hertz.local", + Args: []args{ + { + Addr: "127.0.0.1:8888", + Weight: 10, + Tags: map[string]string{"hello": "world"}, + }, + }, + }, + wantErr: false, + }, + { + // test multi args + info: &info{ + ServiceName: "demo2.hertz.local", + Args: []args{ + { + Addr: "127.0.0.1:9001", + Weight: 10, + Tags: map[string]string{"cloudwego": "hertz"}, + }, + { + Addr: "127.0.0.1:9000", + Weight: 15, + Tags: map[string]string{"foo": "bar"}, + }, + }, + }, + wantErr: false, + }, + { + // test none args + info: &info{ + ServiceName: "demo3.hertz.local", + Args: []args{}, + }, + wantErr: false, + }, + } + for _, test := range tests { + for _, arg := range test.info.Args { + hash, err := prepareRegistryHash(®istry.Info{ + ServiceName: test.info.ServiceName, + Addr: utils.NewNetAddr(tcp, arg.Addr), + Weight: arg.Weight, + Tags: arg.Tags, + }) + assert.False(t, err != nil) + redisCli.HSet(ctx, hash.key, hash.field, hash.value) + } + r := NewRedisResolver("127.0.0.1:6379") + res, err := r.Resolve(context.Background(), test.info.ServiceName) + assert.False(t, err != nil) + if len(res.Instances) == 0 { + assert.Equal(t, res.CacheKey, test.info.ServiceName) + continue + } + assert.Equal(t, res.CacheKey, test.info.ServiceName) + for i, ins := range res.Instances { + args := test.info.Args[i] + assert.Equal(t, args.Addr, ins.Address().String()) + assert.Equal(t, args.Weight, ins.Weight()) + } + } +} + +// TestNewMentor test singleton +func TestNewMentor(t *testing.T) { + m1 := newMentor() + m2 := newMentor() + assert.Equal(t, m1, m2) +} + +// TestForm test form operation +func TestForm(t *testing.T) { + m := newMentor() + m.insertForm("hertz", "127.0.0.1:8000") + m.insertForm("hertz", "127.0.0.1:8001") + m.insertForm("cloudwego", "127.0.0.1:9999") + assert.Equal(t, map[string]addrs{ + "hertz": {"127.0.0.1:8000", "127.0.0.1:8001"}, + "cloudwego": {"127.0.0.1:9999"}, + }, m.mform) + m.removeService("cloudwego") + m.removeAddr("hertz", "127.0.0.1:8001") + assert.Equal(t, map[string]addrs{ + "hertz": {"127.0.0.1:8000"}, + }, m.mform) +} diff --git a/redis/registry.go b/redis/registry.go new file mode 100644 index 0000000..9ea18c0 --- /dev/null +++ b/redis/registry.go @@ -0,0 +1,84 @@ +package redis + +import ( + "context" + "fmt" + "sync" + + "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/go-redis/redis/v8" +) + +var _ registry.Registry = (*redisRegistry)(nil) + +type redisRegistry struct { + client *redis.Client + rctx *registryContext + mu sync.Mutex + wg sync.WaitGroup +} + +type registryContext struct { + ctx context.Context + cancel context.CancelFunc +} + +// NewRedisRegistry create a redis registry +func NewRedisRegistry(addr string, opts ...Option) registry.Registry { + redisOpts := &redis.Options{ + Addr: addr, + Password: "", + DB: 0, + } + for _, opt := range opts { + opt(redisOpts) + } + rdb := redis.NewClient(redisOpts) + return &redisRegistry{ + client: rdb, + } +} + +func (r *redisRegistry) Register(info *registry.Info) error { + if err := validateRegistryInfo(info); err != nil { + return err + } + rctx := registryContext{} + rctx.ctx, rctx.cancel = context.WithCancel(context.Background()) + m := newMentor() + r.wg.Add(1) + go m.subscribe(rctx.ctx, info, r) + r.wg.Wait() + rdb := r.client + hash, err := prepareRegistryHash(info) + if err != nil { + return err + } + r.mu.Lock() + r.rctx = &rctx + rdb.HSet(rctx.ctx, hash.key, hash.field, hash.value) + rdb.Expire(rctx.ctx, hash.key, defaultExpireTime) + rdb.Publish(rctx.ctx, hash.key, fmt.Sprintf("%s-%s-%s", register, info.ServiceName, info.Addr.String())) + r.mu.Unlock() + go m.monitorTTL(rctx.ctx, hash, info, r) + go keepAlive(rctx.ctx, hash, r) + return nil +} + +func (r *redisRegistry) Deregister(info *registry.Info) error { + if err := validateRegistryInfo(info); err != nil { + return err + } + rctx := r.rctx + rdb := r.client + hash, err := prepareRegistryHash(info) + if err != nil { + return err + } + r.mu.Lock() + rdb.HDel(rctx.ctx, hash.key, hash.field) + rdb.Publish(rctx.ctx, hash.key, fmt.Sprintf("%s-%s-%s", deregister, info.ServiceName, info.Addr.String())) + rctx.cancel() + r.mu.Unlock() + return nil +} diff --git a/redis/resolver.go b/redis/resolver.go new file mode 100644 index 0000000..c666913 --- /dev/null +++ b/redis/resolver.go @@ -0,0 +1,61 @@ +package redis + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/cloudwego/hertz/pkg/app/client/discovery" + "github.com/cloudwego/hertz/pkg/common/hlog" + "github.com/go-redis/redis/v8" +) + +var _ discovery.Resolver = (*redisResolver)(nil) + +type redisResolver struct { + client *redis.Client +} + +// NewRedisResolver create a redis resolver +func NewRedisResolver(addr string, opts ...Option) discovery.Resolver { + redisOpts := &redis.Options{Addr: addr} + for _, opt := range opts { + opt(redisOpts) + } + rdb := redis.NewClient(redisOpts) + return &redisResolver{ + client: rdb, + } +} + +func (r *redisResolver) Target(_ context.Context, target *discovery.TargetInfo) string { + return target.Host +} + +func (r *redisResolver) Resolve(ctx context.Context, desc string) (discovery.Result, error) { + rdb := r.client + fvs := rdb.HGetAll(ctx, fmt.Sprintf("/%s/%s/%s", hertz, desc, server)).Val() + var ( + ri registryInfo + its []discovery.Instance + ) + for f, v := range fvs { + err := json.Unmarshal([]byte(v), &ri) + if err != nil { + hlog.Warnf("HERTZ: fail to unmarshal with err: %v, ignore instance Addr: %v", err, f) + } + weight := ri.Weight + if weight <= 0 { + weight = defaultWeight + } + its = append(its, discovery.NewInstance(tcp, ri.Addr, weight, ri.Tags)) + } + return discovery.Result{ + CacheKey: desc, + Instances: its, + }, nil +} + +func (r *redisResolver) Name() string { + return Redis +} From c11e70dcceaacfc63a15657f62b1e12463476235 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Tue, 1 Nov 2022 00:59:24 +0800 Subject: [PATCH 2/9] remove license --- redis/LICENSE | 201 -------------------------------------------------- 1 file changed, 201 deletions(-) delete mode 100644 redis/LICENSE diff --git a/redis/LICENSE b/redis/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/redis/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. From a3ef21d4e1ea329d9a1e89491c303c508227fe93 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Wed, 2 Nov 2022 19:02:59 +0800 Subject: [PATCH 3/9] resolve ci --- redis/common.go | 15 ++++++++++++++- redis/example/client/main.go | 16 +++++++++++++++- redis/example/server/main.go | 16 +++++++++++++++- redis/go.mod | 2 +- redis/mentor.go | 15 ++++++++++++++- redis/redis_test.go | 29 +++++++++++++++++++++++++---- redis/registry.go | 14 ++++++++++++++ redis/resolver.go | 14 ++++++++++++++ 8 files changed, 112 insertions(+), 9 deletions(-) diff --git a/redis/common.go b/redis/common.go index e29b344..19a0492 100644 --- a/redis/common.go +++ b/redis/common.go @@ -1,3 +1,17 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package redis import ( @@ -124,7 +138,6 @@ func keepAlive(ctx context.Context, hash *registryHash, r *redisRegistry) { r.client.Expire(ctx, hash.key, defaultKeepAliveTime) case <-ctx.Done(): break - default: } } } diff --git a/redis/example/client/main.go b/redis/example/client/main.go index 218a462..910f883 100644 --- a/redis/example/client/main.go +++ b/redis/example/client/main.go @@ -1,13 +1,27 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( "context" - "registry/redis" "github.com/cloudwego/hertz/pkg/app/client" "github.com/cloudwego/hertz/pkg/app/middlewares/client/sd" "github.com/cloudwego/hertz/pkg/common/config" "github.com/cloudwego/hertz/pkg/common/hlog" + "github.com/hertz-contrib/registry/redis" ) func main() { diff --git a/redis/example/server/main.go b/redis/example/server/main.go index dd57ef6..bb3bbf1 100644 --- a/redis/example/server/main.go +++ b/redis/example/server/main.go @@ -1,14 +1,28 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( "context" - "registry/redis" "github.com/cloudwego/hertz/pkg/app" "github.com/cloudwego/hertz/pkg/app/server" "github.com/cloudwego/hertz/pkg/app/server/registry" "github.com/cloudwego/hertz/pkg/common/utils" "github.com/cloudwego/hertz/pkg/protocol/consts" + "github.com/hertz-contrib/registry/redis" ) func main() { diff --git a/redis/go.mod b/redis/go.mod index fb661a8..a1e7ba3 100644 --- a/redis/go.mod +++ b/redis/go.mod @@ -1,4 +1,4 @@ -module registry/redis +module github.com/hertz-contrib/registry/redis go 1.16 diff --git a/redis/mentor.go b/redis/mentor.go index 35582b3..5a8f239 100644 --- a/redis/mentor.go +++ b/redis/mentor.go @@ -1,3 +1,17 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package redis import ( @@ -66,7 +80,6 @@ func (m *mentor) monitorTTL(ctx context.Context, hash *registryHash, info *regis } case <-ctx.Done(): break - default: } } } diff --git a/redis/redis_test.go b/redis/redis_test.go index 90f8896..105d679 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -1,3 +1,17 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package redis import ( @@ -152,10 +166,17 @@ func TestResolve(t *testing.T) { continue } assert.Equal(t, res.CacheKey, test.info.ServiceName) - for i, ins := range res.Instances { - args := test.info.Args[i] - assert.Equal(t, args.Addr, ins.Address().String()) - assert.Equal(t, args.Weight, ins.Weight()) + addr := make(map[string]struct{}) + weight := make(map[int]struct{}) + for _, arg := range test.info.Args { + addr[arg.Addr] = struct{}{} + weight[arg.Weight] = struct{}{} + } + for _, ins := range res.Instances { + _, addrOK := addr[ins.Address().String()] + _, weightOK := weight[ins.Weight()] + assert.True(t, addrOK) + assert.True(t, weightOK) } } } diff --git a/redis/registry.go b/redis/registry.go index 9ea18c0..fba2e9e 100644 --- a/redis/registry.go +++ b/redis/registry.go @@ -1,3 +1,17 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package redis import ( diff --git a/redis/resolver.go b/redis/resolver.go index c666913..1a7299c 100644 --- a/redis/resolver.go +++ b/redis/resolver.go @@ -1,3 +1,17 @@ +// Copyright 2021 CloudWeGo Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package redis import ( From a9704946c738d239db2a1be01f1ea18ae5815132 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Thu, 3 Nov 2022 14:30:50 +0800 Subject: [PATCH 4/9] improve ci --- .github/workflows/tests.yml | 5 +++++ redis/common.go | 3 +-- redis/example/client/main.go | 2 +- redis/example/server/main.go | 2 +- redis/mentor.go | 2 +- redis/redis_test.go | 19 +------------------ redis/registry.go | 2 +- redis/resolver.go | 2 +- 8 files changed, 12 insertions(+), 25 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d0e588c..d6fdeda 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,6 +41,11 @@ jobs: image: 'xdockerh/eureka-server:latest' ports: - "8761:8761" + redis: + image: redis:latest + ports: + - '6379:6379' + steps: - uses: actions/checkout@v3 diff --git a/redis/common.go b/redis/common.go index 19a0492..5db491b 100644 --- a/redis/common.go +++ b/redis/common.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -32,7 +32,6 @@ const ( deregister = "deregister" hertz = "hertz" server = "server" - client = "client" tcp = "tcp" ) diff --git a/redis/example/client/main.go b/redis/example/client/main.go index 910f883..1b66291 100644 --- a/redis/example/client/main.go +++ b/redis/example/client/main.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/redis/example/server/main.go b/redis/example/server/main.go index bb3bbf1..a00bbba 100644 --- a/redis/example/server/main.go +++ b/redis/example/server/main.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/redis/mentor.go b/redis/mentor.go index 5a8f239..dec043f 100644 --- a/redis/mentor.go +++ b/redis/mentor.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/redis/redis_test.go b/redis/redis_test.go index 105d679..5ed1be1 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -187,20 +187,3 @@ func TestNewMentor(t *testing.T) { m2 := newMentor() assert.Equal(t, m1, m2) } - -// TestForm test form operation -func TestForm(t *testing.T) { - m := newMentor() - m.insertForm("hertz", "127.0.0.1:8000") - m.insertForm("hertz", "127.0.0.1:8001") - m.insertForm("cloudwego", "127.0.0.1:9999") - assert.Equal(t, map[string]addrs{ - "hertz": {"127.0.0.1:8000", "127.0.0.1:8001"}, - "cloudwego": {"127.0.0.1:9999"}, - }, m.mform) - m.removeService("cloudwego") - m.removeAddr("hertz", "127.0.0.1:8001") - assert.Equal(t, map[string]addrs{ - "hertz": {"127.0.0.1:8000"}, - }, m.mform) -} diff --git a/redis/registry.go b/redis/registry.go index fba2e9e..2872103 100644 --- a/redis/registry.go +++ b/redis/registry.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/redis/resolver.go b/redis/resolver.go index 1a7299c..3faa74e 100644 --- a/redis/resolver.go +++ b/redis/resolver.go @@ -1,4 +1,4 @@ -// Copyright 2021 CloudWeGo Authors. +// Copyright 2022 CloudWeGo Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 0e94fdc86692805468d41c28789414a006d78606 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Thu, 3 Nov 2022 21:51:14 +0800 Subject: [PATCH 5/9] gofumpt code --- redis/mentor.go | 5 ++--- redis/redis_test.go | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/redis/mentor.go b/redis/mentor.go index dec043f..fb5b9b7 100644 --- a/redis/mentor.go +++ b/redis/mentor.go @@ -66,7 +66,6 @@ func (m *mentor) subscribe(ctx context.Context, info *registry.Info, r *redisReg } } } - } func (m *mentor) monitorTTL(ctx context.Context, hash *registryHash, info *registry.Info, r *redisRegistry) { @@ -84,7 +83,7 @@ func (m *mentor) monitorTTL(ctx context.Context, hash *registryHash, info *regis } } -func (m *mentor) insertForm(serviceName string, addr string) { +func (m *mentor) insertForm(serviceName, addr string) { m.mform[serviceName] = append(m.mform[serviceName], addr) } @@ -92,7 +91,7 @@ func (m *mentor) removeService(serviceName string) { delete(m.mform, serviceName) } -func (m *mentor) removeAddr(serviceName string, addr string) { +func (m *mentor) removeAddr(serviceName, addr string) { for i, v := range m.mform[serviceName] { if v == addr { m.mform[serviceName] = append(m.mform[serviceName][:i], m.mform[serviceName][i+1:]...) diff --git a/redis/redis_test.go b/redis/redis_test.go index 5ed1be1..c22c82e 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -25,8 +25,10 @@ import ( "github.com/stretchr/testify/assert" ) -var redisCli *redis.Client -var ctx = context.Background() +var ( + redisCli *redis.Client + ctx = context.Background() +) func init() { rdb := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"}) From 22a0c471e74d6ba2464165f655439c50607ecade Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Sun, 6 Nov 2022 14:59:05 +0800 Subject: [PATCH 6/9] add license & fix typo --- licenses/LICENSE-go-redis | 25 +++++++++++++++++++++++++ redis/registry.go | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 licenses/LICENSE-go-redis diff --git a/licenses/LICENSE-go-redis b/licenses/LICENSE-go-redis new file mode 100644 index 0000000..7670df4 --- /dev/null +++ b/licenses/LICENSE-go-redis @@ -0,0 +1,25 @@ +Copyright (c) 2013 The github.com/go-redis/redis Authors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/redis/registry.go b/redis/registry.go index 2872103..1d88c79 100644 --- a/redis/registry.go +++ b/redis/registry.go @@ -37,7 +37,7 @@ type registryContext struct { cancel context.CancelFunc } -// NewRedisRegistry create a redis registry +// NewRedisRegistry creates a redis registry func NewRedisRegistry(addr string, opts ...Option) registry.Registry { redisOpts := &redis.Options{ Addr: addr, From ac14354c0cfa53e3492f1f693fae1d29794fdb37 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Tue, 8 Nov 2022 14:25:50 +0800 Subject: [PATCH 7/9] optimize code --- redis/common.go | 10 +++++++++- redis/mentor.go | 3 +-- redis/redis_test.go | 2 ++ redis/registry.go | 5 ++--- redis/resolver.go | 6 +++--- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/redis/common.go b/redis/common.go index 5db491b..6caf421 100644 --- a/redis/common.go +++ b/redis/common.go @@ -107,13 +107,21 @@ func validateRegistryInfo(info *registry.Info) error { return nil } +func generateKey(serviceName, serviceType string) string { + return fmt.Sprintf("/%s/%s/%s", hertz, serviceName, serviceType) +} + +func generateMsg(msgType, serviceName, serviceAddr string) string { + return fmt.Sprintf("%s-%s-%s", msgType, serviceName, serviceAddr) +} + func prepareRegistryHash(info *registry.Info) (*registryHash, error) { meta, err := json.Marshal(convertInfo(info)) if err != nil { return nil, err } return ®istryHash{ - key: fmt.Sprintf("/hertz/%s/%s", info.ServiceName, server), + key: generateKey(info.ServiceName, server), field: info.Addr.String(), value: string(meta), }, nil diff --git a/redis/mentor.go b/redis/mentor.go index fb5b9b7..bb392e4 100644 --- a/redis/mentor.go +++ b/redis/mentor.go @@ -16,7 +16,6 @@ package redis import ( "context" - "fmt" "strings" "time" @@ -45,7 +44,7 @@ func newMentor() *mentor { } func (m *mentor) subscribe(ctx context.Context, info *registry.Info, r *redisRegistry) { - sub := r.client.Subscribe(ctx, fmt.Sprintf("/%s/%s/%s", hertz, info.ServiceName, server)) + sub := r.client.Subscribe(ctx, generateKey(info.ServiceName, server)) defer sub.Close() r.wg.Done() select { diff --git a/redis/redis_test.go b/redis/redis_test.go index c22c82e..a02ecb2 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -37,6 +37,7 @@ func init() { // TestRegister Test the Registry in registry.go func TestRegister(t *testing.T) { + defer redisCli.FlushDB(ctx) tests := []struct { info []*registry.Info wantErr bool @@ -94,6 +95,7 @@ func TestRegister(t *testing.T) { // TestResolve Test the Resolver in resolver.go func TestResolve(t *testing.T) { + defer redisCli.FlushDB(ctx) type args struct { Addr string Weight int diff --git a/redis/registry.go b/redis/registry.go index 1d88c79..780b8fe 100644 --- a/redis/registry.go +++ b/redis/registry.go @@ -16,7 +16,6 @@ package redis import ( "context" - "fmt" "sync" "github.com/cloudwego/hertz/pkg/app/server/registry" @@ -72,7 +71,7 @@ func (r *redisRegistry) Register(info *registry.Info) error { r.rctx = &rctx rdb.HSet(rctx.ctx, hash.key, hash.field, hash.value) rdb.Expire(rctx.ctx, hash.key, defaultExpireTime) - rdb.Publish(rctx.ctx, hash.key, fmt.Sprintf("%s-%s-%s", register, info.ServiceName, info.Addr.String())) + rdb.Publish(rctx.ctx, hash.key, generateMsg(register, info.ServiceName, info.Addr.String())) r.mu.Unlock() go m.monitorTTL(rctx.ctx, hash, info, r) go keepAlive(rctx.ctx, hash, r) @@ -91,7 +90,7 @@ func (r *redisRegistry) Deregister(info *registry.Info) error { } r.mu.Lock() rdb.HDel(rctx.ctx, hash.key, hash.field) - rdb.Publish(rctx.ctx, hash.key, fmt.Sprintf("%s-%s-%s", deregister, info.ServiceName, info.Addr.String())) + rdb.Publish(rctx.ctx, hash.key, generateMsg(deregister, info.ServiceName, info.Addr.String())) rctx.cancel() r.mu.Unlock() return nil diff --git a/redis/resolver.go b/redis/resolver.go index 3faa74e..9ac44f4 100644 --- a/redis/resolver.go +++ b/redis/resolver.go @@ -17,7 +17,6 @@ package redis import ( "context" "encoding/json" - "fmt" "github.com/cloudwego/hertz/pkg/app/client/discovery" "github.com/cloudwego/hertz/pkg/common/hlog" @@ -30,7 +29,7 @@ type redisResolver struct { client *redis.Client } -// NewRedisResolver create a redis resolver +// NewRedisResolver creates a redis resolver func NewRedisResolver(addr string, opts ...Option) discovery.Resolver { redisOpts := &redis.Options{Addr: addr} for _, opt := range opts { @@ -48,7 +47,7 @@ func (r *redisResolver) Target(_ context.Context, target *discovery.TargetInfo) func (r *redisResolver) Resolve(ctx context.Context, desc string) (discovery.Result, error) { rdb := r.client - fvs := rdb.HGetAll(ctx, fmt.Sprintf("/%s/%s/%s", hertz, desc, server)).Val() + fvs := rdb.HGetAll(ctx, generateKey(desc, server)).Val() var ( ri registryInfo its []discovery.Instance @@ -57,6 +56,7 @@ func (r *redisResolver) Resolve(ctx context.Context, desc string) (discovery.Res err := json.Unmarshal([]byte(v), &ri) if err != nil { hlog.Warnf("HERTZ: fail to unmarshal with err: %v, ignore instance Addr: %v", err, f) + continue } weight := ri.Weight if weight <= 0 { From 7d5a08faf77eb2bd959c60d48b586cd5c9b2b410 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Wed, 9 Nov 2022 17:11:40 +0800 Subject: [PATCH 8/9] fix data race --- redis/mentor.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/redis/mentor.go b/redis/mentor.go index bb392e4..b207c3f 100644 --- a/redis/mentor.go +++ b/redis/mentor.go @@ -17,6 +17,7 @@ package redis import ( "context" "strings" + "sync" "time" "github.com/cloudwego/hertz/pkg/app/server/registry" @@ -31,6 +32,7 @@ type addrs []string type mentor struct { mform map[string]addrs + mu sync.Mutex } // newMentor use singleton @@ -55,11 +57,15 @@ func (m *mentor) subscribe(ctx context.Context, info *registry.Info, r *redisReg for msg := range ch { split := strings.Split(msg.Payload, "-") if split[0] == register { + m.mu.Lock() m.insertForm(split[1], split[2]) hlog.Infof("HERTZ: service info %v", m.mform) + m.mu.Unlock() } else if split[0] == deregister { + m.mu.Lock() m.removeAddr(split[1], split[2]) hlog.Infof("HERTZ: service info %v", m.mform) + m.mu.Unlock() } else { hlog.Warnf("HERTZ: invalid message %v", msg) } @@ -74,7 +80,9 @@ func (m *mentor) monitorTTL(ctx context.Context, hash *registryHash, info *regis select { case <-ticker.C: if r.client.TTL(ctx, hash.key).Val() == -2 { + m.mu.Lock() m.removeService(info.ServiceName) + m.mu.Unlock() } case <-ctx.Done(): break From e8ecbe149345536355302080095914a6e49cae52 Mon Sep 17 00:00:00 2001 From: Lorain <743804605@qq.com> Date: Thu, 10 Nov 2022 17:07:28 +0800 Subject: [PATCH 9/9] enrich ut --- redis/redis_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/redis/redis_test.go b/redis/redis_test.go index a02ecb2..3e28f9a 100644 --- a/redis/redis_test.go +++ b/redis/redis_test.go @@ -17,10 +17,18 @@ package redis import ( "context" "encoding/json" + "fmt" "testing" + "time" + "github.com/cloudwego/hertz/pkg/app" + hzcli "github.com/cloudwego/hertz/pkg/app/client" + "github.com/cloudwego/hertz/pkg/app/middlewares/client/sd" + hzsrv "github.com/cloudwego/hertz/pkg/app/server" "github.com/cloudwego/hertz/pkg/app/server/registry" + "github.com/cloudwego/hertz/pkg/common/config" "github.com/cloudwego/hertz/pkg/common/utils" + "github.com/cloudwego/hertz/pkg/protocol/consts" "github.com/go-redis/redis/v8" "github.com/stretchr/testify/assert" ) @@ -191,3 +199,51 @@ func TestNewMentor(t *testing.T) { m2 := newMentor() assert.Equal(t, m1, m2) } + +// TestRedisRegistryWithHertz Test redis registry complete workflow (service registry|service de-registry|service resolver) with hertz. +func TestRedisRegistryWithHertz(t *testing.T) { + addr := "127.0.0.1:8080" + redisAddr := "127.0.0.1:6379" + srvName := "hertz.with.registry" + r := NewRedisRegistry(redisAddr) + h := hzsrv.Default( + hzsrv.WithHostPorts(addr), + hzsrv.WithRegistry(r, ®istry.Info{ + ServiceName: srvName, + Addr: utils.NewNetAddr("tcp", addr), + Weight: 10, + Tags: nil, + }), + ) + h.GET("/ping", func(_ context.Context, ctx *app.RequestContext) { + ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) + }) + go h.Spin() + time.Sleep(4 * time.Second) + + hc, _ := hzcli.NewClient() + resolver := NewRedisResolver(redisAddr) + hc.Use(sd.Discovery(resolver)) + + url := fmt.Sprintf("http://%v/ping", srvName) + status, body, err := hc.Get(context.Background(), nil, url, config.WithSD(true)) + assert.Nil(t, err) + assert.Equal(t, 200, status) + assert.Equal(t, "{\"ping\":\"pong\"}", string(body)) + + opts := h.GetOptions() + assert.Equal(t, opts.RegistryInfo.ServiceName, srvName) + assert.Equal(t, opts.RegistryInfo.Addr.String(), addr) + assert.Equal(t, opts.RegistryInfo.Weight, 10) + assert.Nil(t, opts.RegistryInfo.Tags) + + if err := h.Shutdown(context.Background()); err != nil { + t.Errorf("HERTZ: Shutdown error=%v", err) + } + time.Sleep(5 * time.Second) + + status2, body2, err2 := hc.Get(context.Background(), nil, url, config.WithSD(true)) + assert.True(t, err2 != nil) + assert.Equal(t, 0, status2) + assert.Equal(t, "", string(body2)) +}