├── .DS_Store
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── README_ENGLISH.md
├── admin
├── admin.go
└── html
│ └── index.html
├── business_client
├── business_client.go
├── business_client_conf.go
└── gateway_client.go
├── discovery
├── .DS_Store
├── discover.go
├── etcd_discovery
│ └── etcd_discovery.go
└── redis_discovery
│ └── redis_discovery.go
├── dns
└── dns.go
├── docs
└── performance_test.md
├── go.mod
├── go.sum
├── grpcs
├── main.go
└── socket_cluster_gateway
│ ├── config
│ └── config.go
│ ├── handler
│ └── gateway.go
│ ├── logic
│ └── gateway_logic
│ │ ├── is_online.go
│ │ ├── send_to_client_id.go
│ │ └── send_to_client_ids.go
│ ├── proto
│ └── gateway
│ │ ├── gateway.pb.go
│ │ ├── gateway.proto
│ │ └── gateway_grpc.pb.go
│ └── socket_cluster_gateway.go
├── logx
├── fileLogger.go
├── filelogger_test.go
├── log.go
├── log
│ └── 2023-08-19.log
├── logx.go
├── stdlogger.go
└── stdlogger_test.go
├── node
├── .DS_Store
├── common.go
├── conf.pb.go
├── conf.proto
├── const.go
├── msg.pb.go
├── msg.proto
├── node.go
├── node_conf.go
├── plugin.go
└── session.go
├── pic
├── business_client.png
└── socket_cluster.png
├── protocol
├── .DS_Store
├── flow_proto.go
├── protocol.go
├── quic_protocol
│ ├── quic_connect.go
│ ├── quic_protocol.go
│ └── quic_test.go
├── session.go
├── tcp_protocol
│ ├── tcp_connection.go
│ └── tcp_protocol.go
├── tls.go
└── ws_protocol
│ ├── ws_connection.go
│ └── ws_protocol.go
├── server
└── server.go
├── session_storage
├── redis_storage
│ └── redis_storage.go
└── session_storage.go
└── unsafehash
└── segment_hash.go
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | main
2 | .idea
3 | .vscode
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "git.ignoreLimitWarning": true
3 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2020-2030 Weblazy
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # socket-cluster
2 | 即时通讯框架,如果大家觉得好用,右上角帮忙点个star吧。(^_^)
3 | > 欢迎感兴趣的小伙伴一同开发,解决日常消息推送,长连接需求。
4 | - 后端(golang): https://github.com/weblazy/socket-cluster
5 | - 使用案例:
6 | - 在线体验: http://web.xiaoyuantongbbs.cn:3333/login.html#/
7 | - 后端源码: https://github.com/weblazy/socket-cluster-examples
8 | - 前端源码: https://github.com/weblazy/socket-cluster-web
9 | - 使用文档: http://doc.xiaoyuantongbbs.cn:4000/
10 | - 后端好用的工具库(golang): https://github.com/weblazy/easy
11 | # 框架库依赖
12 | - redis
13 |
14 | # 框架使用方式
15 | ```
16 | // 客户端协议组件
17 | protocolHandler := &ws_protocol.WsProtocol{}
18 | // 在线状态存储组件
19 | sessionStorageHandler := redis_storage.NewRedisStorage([]*redis_storage.RedisNode{&redis_storage.RedisNode{
20 | RedisConf: &redis.Options{Addr: redisHost, Password: redisPassword, DB: 0},
21 | Position: 10000,
22 | }})
23 | // 服务发现组件
24 | discoveryHandler := redis_discovery.NewRedisDiscovery(&redis.Options{Addr: redisHost, Password: redisPassword, DB: 0})
25 | // 启动服务
26 | common.NodeInfo, err = node.NewNode(node.NewNodeConf(*host, protocolHandler, sessionStorageHandler, discoveryHandler, onMsg).WithPort(*port))
27 | if err != nil {
28 | logx.Info(err)
29 | }
30 | ```
31 | ```
32 | func onMsg(context *node.Context) {
33 | logx.Info("msg:", string(context.Msg))
34 | }
35 | ```
36 |
37 | # 架构框图
38 | 
39 |
40 | - 使用business_client将业务和节点分离部署
41 | 
42 |
43 | # 联系我们
44 | - 技术支持/合作/咨询请联系作者QQ: 2276282419
45 | - 作者邮箱: 2276282419@qq.com
46 | - 即时通讯技术交流QQ群: 33280853
47 |
--------------------------------------------------------------------------------
/README_ENGLISH.md:
--------------------------------------------------------------------------------
1 | # socket-cluster
2 | 即时通讯框架
3 | # Contact Us
4 |
5 | Users Mail Group: join in pika_users@groups.163.com
6 |
7 | Developers Mail Group: join in pika_developers@groups.163.com
8 |
9 | QQ group: 294254078
10 |
--------------------------------------------------------------------------------
/admin/admin.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "io"
5 | "text/template"
6 |
7 | "github.com/labstack/echo/v4"
8 | )
9 |
10 | func main() {
11 | e := echo.New()
12 | // Initialize the template engine
13 | temp := &Template{
14 | // The purpose of the precompilation process is to optimize the speed of later rendering of the template file
15 | templates: template.Must(template.ParseGlob("html/*.html")),
16 | }
17 | e.Renderer = temp
18 | e.GET("/index", Index)
19 | e.Start(":9529")
20 | }
21 |
22 | // Custom template engine
23 | type Template struct {
24 | templates *template.Template
25 | }
26 |
27 | // Render Implement interface, Render function
28 | func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
29 | // Invokes the template engine to render the template
30 | return t.templates.ExecuteTemplate(w, name, data)
31 | }
32 |
33 | // Index
34 | func Index(c echo.Context) error {
35 | params := make(map[string]interface{})
36 | params["list"] = []map[string]interface{}{map[string]interface{}{
37 | "ip": "127.0.0.1",
38 | "uuid": "9527",
39 | "count": "100",
40 | }, map[string]interface{}{
41 | "ip": "127.0.0.2",
42 | "uuid": "9528",
43 | "count": "101",
44 | }}
45 | return c.Render(200, "index.html", params)
46 | }
47 |
--------------------------------------------------------------------------------
/admin/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Socket-Cluster
7 |
43 |
44 |
45 |
46 | Socket-Cluster
47 |
48 |
49 |
50 | ip |
51 | uuid |
52 | count |
53 |
54 | {{ range $k, $v := .list }}
55 |
56 | {{ $v.ip }} |
57 | {{ $v.uuid }} |
58 | {{ $v.count }} |
59 |
60 | {{ end }}
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/business_client/business_client.go:
--------------------------------------------------------------------------------
1 | package business_client
2 |
3 | import (
4 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
5 | )
6 |
7 | type (
8 | BusinessClient interface {
9 | // IsOnline gets the online status of the clientId
10 | IsOnline(clientId string) (bool, error)
11 | // SendToClientId sends a message to a clientId
12 | SendToClientId(req *gateway.SendToClientIdRequest) (*gateway.SendToClientIdResponse, error)
13 | // SendToClientId sends a message to multiple clientIds
14 | SendToClientIds(req *gateway.SendToClientIdsRequest) (*gateway.SendToClientIdsResponse, error)
15 | }
16 |
17 | // Node communication node
18 | // businessClient struct {
19 | // BusinessClient
20 | // businessClientConf *BusinessClientConf
21 | // // Key: socket address
22 | // // Value: protocol.Connection
23 | // nodeIpMap goutil.Map // Receive messages forwarded by other nodes
24 | // nodeIdMap goutil.Map // Receive messages forwarded by other nodes
25 | // nodeTimeout int64 // Node heartbeat timeout time
26 | // clientTimeout int64 // Client heartbeat timeout time
27 |
28 | // internalProtocolHandler protocol.Protocol // Direct protocol between node and node
29 | // }
30 | )
31 |
32 | // // NewNode return a Node
33 | // func NewBusinessClient(cfg *BusinessClientConf) (BusinessClient, error) {
34 | // businessClientObj := &businessClient{
35 | // businessClientConf: cfg,
36 | // nodeIpMap: goutil.AtomicMap(),
37 | // nodeIdMap: goutil.AtomicMap(),
38 | // nodeTimeout: cfg.nodePingInterval * 3,
39 | // }
40 | // go businessClientObj.watchService()
41 | // return businessClientObj, nil
42 | // }
43 |
44 | // // SendToClientId Send message to a clientId
45 | // func (this *businessClient) SendToClientId(clientId string, req []byte) error {
46 | // if req == nil {
47 | // return fmt.Errorf("message is nil")
48 | // }
49 | // ipArr, err := this.businessClientConf.sessionStorageHandler.GetIps(clientId)
50 | // if err == nil {
51 | // mapreduce.MapVoid(func(source chan<- interface{}) {
52 | // for key := range ipArr {
53 | // source <- ipArr[key]
54 | // }
55 | // }, func(item interface{}) {
56 | // ip := item.(string)
57 |
58 | // connect, ok := this.nodeIdMap.Load(ip)
59 | // if ok {
60 | // conn, ok := connect.(protocol.Connection)
61 | // if !ok {
62 | // return
63 | // }
64 | // clientsMsg := node.ClientsMsg{
65 | // ReceiveClientIds: []string{clientId},
66 | // Data: req,
67 | // }
68 | // clientsMsgBytes, err := proto.Marshal(&clientsMsg)
69 | // transReq := node.Msg{
70 | // MsgType: node.ClientMsgType,
71 | // Data: clientsMsgBytes,
72 | // }
73 | // reqBytes, err := proto.Marshal(&transReq)
74 | // err = conn.WriteMsg(reqBytes)
75 | // if err != nil {
76 | // logx.LogHandler.Error(err)
77 | // }
78 | // } else {
79 | // logx.LogHandler.Errorf("node:%s not online", ip)
80 | // return
81 | // }
82 |
83 | // })
84 | // }
85 | // return nil
86 | // }
87 |
88 | // type BatchData struct {
89 | // ip string
90 | // clientIds []string
91 | // }
92 |
93 | // // SendToClientIds Sending messages to multiple clients
94 | // func (this *businessClient) SendToClientIds(clientIds []string, req []byte) error {
95 | // if req == nil {
96 | // return fmt.Errorf("message is nil")
97 | // }
98 | // clientMap, err := this.businessClientConf.sessionStorageHandler.GetClientsIps(clientIds)
99 | // if err != nil {
100 | // return err
101 | // }
102 | // // Concurrent sends to other nodes
103 | // mapreduce.MapVoid(func(source chan<- interface{}) {
104 | // for k1 := range clientMap {
105 | // source <- &BatchData{ip: k1, clientIds: clientMap[k1]}
106 | // }
107 | // }, func(item interface{}) {
108 | // batchData := item.(*BatchData)
109 | // connect, ok := this.nodeIdMap.Load(batchData.ip)
110 | // if ok {
111 | // conn, ok := connect.(protocol.Connection)
112 | // if !ok {
113 | // return
114 | // }
115 | // ids := make([]string, 0)
116 | // for k1 := range batchData.clientIds {
117 | // ids = append(ids, batchData.clientIds[k1])
118 | // }
119 | // clientsMsg := node.ClientsMsg{
120 | // ReceiveClientIds: ids,
121 | // Data: req,
122 | // }
123 | // clientsMsgBytes, err := proto.Marshal(&clientsMsg)
124 | // if err != nil {
125 | // logx.LogHandler.Error(err)
126 | // }
127 | // msg := node.Msg{
128 | // MsgType: node.ClientMsgType,
129 | // Data: clientsMsgBytes,
130 | // }
131 | // msgBytes, err := proto.Marshal(&msg)
132 | // if err != nil {
133 | // logx.LogHandler.Error(err)
134 | // }
135 | // err = conn.WriteMsg(msgBytes)
136 | // if err != nil {
137 | // logx.LogHandler.Error(err)
138 | // }
139 | // } else {
140 | // logx.LogHandler.Error("node:%s not online", batchData.ip)
141 | // return
142 | // }
143 |
144 | // })
145 |
146 | // return nil
147 | // }
148 |
149 | // // IsOnline determine if a clientId is online
150 | // func (this *businessClient) IsOnline(clientId string) bool {
151 | // addrArr, err := this.businessClientConf.sessionStorageHandler.GetIps(clientId)
152 | // if err != nil {
153 | // logx.LogHandler.Error(err)
154 | // return false
155 | // }
156 | // if len(addrArr) > 0 {
157 | // return true
158 | // }
159 | // return false
160 | // }
161 |
162 | // // watchService
163 | // func (this *businessClient) watchService() {
164 | // watchChan := make(chan discovery.EventType, 1)
165 | // go this.businessClientConf.discoveryHandler.WatchService(watchChan)
166 | // go func() {
167 | // for {
168 | // select {
169 | // case _, ok := <-watchChan:
170 | // if !ok {
171 | // logx.LogHandler.Infof("channel close\n")
172 | // return
173 | // }
174 | // err := this.updateNodeList()
175 | // if err != nil {
176 | // logx.LogHandler.Error(err)
177 | // }
178 | // }
179 | // }
180 | // }()
181 | // // init
182 | // err := this.updateNodeList()
183 | // if err != nil {
184 | // logx.LogHandler.Error(err)
185 | // }
186 | // }
187 |
188 | // // UpdateNodeList Add handles addition request
189 | // func (this *businessClient) updateNodeList() error {
190 | // nodeMap := make(map[string]int)
191 | // for k1 := range this.businessClientConf.hostList {
192 | // nodeList, port, err := dns.DnsParse(this.businessClientConf.hostList[k1])
193 | // if err != nil {
194 | // logx.LogHandler.Error(err)
195 | // return err
196 | // }
197 | // for k2 := range nodeList {
198 | // nodeMap[nodeList[k2]+":"+port] = 1
199 | // }
200 | // }
201 |
202 | // for k1 := range nodeMap {
203 | // addr := k1
204 | // // Connection already exists
205 | // _, ok := this.nodeIpMap.LoadOrStore(addr, "")
206 | // if ok {
207 | // continue
208 | // }
209 | // conn, err := this.businessClientConf.internalProtocolHandler.Dial(addr)
210 | // if err != nil {
211 | // logx.LogHandler.Errorf("dial:%s", err.Error())
212 | // this.nodeIpMap.Delete(addr)
213 | // continue
214 | // }
215 |
216 | // auth := node.AuthMsg{
217 | // Password: this.businessClientConf.password,
218 | // }
219 | // authBytes, err := proto.Marshal(&auth)
220 | // if err != nil {
221 | // logx.LogHandler.Error(err)
222 | // }
223 | // data := node.Msg{
224 | // MsgType: node.AuthBusinessClientMsgType,
225 | // Data: authBytes,
226 | // }
227 | // reqBytes, err := proto.Marshal(&data)
228 | // if err != nil {
229 | // logx.LogHandler.Error(err)
230 | // }
231 | // err = conn.WriteMsg(reqBytes)
232 | // if err != nil {
233 | // logx.LogHandler.Info(err)
234 | // }
235 | // this.nodeIpMap.Store(addr, conn)
236 | // go func(addr string, conn protocol.Connection) {
237 | // defer func(addr string, conn protocol.Connection) {
238 | // nodeId, _ := this.nodeIpMap.Load(addr)
239 | // if nodeId != "" {
240 | // this.nodeIdMap.Delete(nodeId)
241 | // }
242 | // this.nodeIpMap.Delete(addr)
243 | // if conn != nil {
244 | // conn.Close()
245 | // }
246 | // }(addr, conn)
247 | // for {
248 | // msg, err := conn.ReadMsg()
249 | // if err != nil {
250 | // break
251 | // }
252 | // this.onTransServerMsg(addr, conn, msg)
253 | // }
254 | // }(addr, conn)
255 | // }
256 | // return nil
257 | // }
258 |
259 | // // OnTransMsg handle internal communication node messages
260 | // func (this *businessClient) onTransServerMsg(addr string, conn protocol.Connection, msg []byte) {
261 | // var transMsg node.Msg
262 | // err := proto.Unmarshal(msg, &transMsg)
263 | // if err != nil {
264 | // logx.LogHandler.Error(err)
265 | // return
266 | // }
267 | // switch transMsg.MsgType {
268 |
269 | // case node.PingMsgType: // The heartbeat message
270 | // case node.BindNodeIdMsgType:
271 | // var bindNodeIdMsg node.BindNodeIdMsg
272 | // err = proto.Unmarshal(transMsg.Data, &bindNodeIdMsg)
273 | // if err != nil {
274 | // logx.LogHandler.Error(err)
275 | // }
276 | // this.nodeIdMap.Store(bindNodeIdMsg.NodeId, conn)
277 | // this.nodeIpMap.Store(addr, bindNodeIdMsg.NodeId)
278 | // default: // The unknow message
279 | // logx.LogHandler.Info(transMsg)
280 | // }
281 |
282 | // }
283 |
284 | // // SendPing send node Heartbeat
285 | // func (this *businessClient) sendPing() {
286 | // go func() {
287 | // for {
288 | // time.Sleep(time.Duration(this.businessClientConf.nodePingInterval) * time.Second)
289 |
290 | // // send to other node
291 | // this.nodeIpMap.Range(func(k, v interface{}) bool {
292 | // conn, ok := v.(protocol.Connection)
293 | // if !ok {
294 | // return true
295 | // }
296 |
297 | // err := conn.WriteMsg(node.PingMsg)
298 | // if err != nil {
299 | // logx.LogHandler.Error(err)
300 | // }
301 | // return true
302 | // })
303 | // }
304 | // }()
305 | // }
306 |
--------------------------------------------------------------------------------
/business_client/business_client_conf.go:
--------------------------------------------------------------------------------
1 | package business_client
2 |
3 | import (
4 | "github.com/weblazy/socket-cluster/discovery"
5 | "github.com/weblazy/socket-cluster/node"
6 | "github.com/weblazy/socket-cluster/session_storage"
7 | )
8 |
9 | type (
10 | // NodeConf node config
11 | BusinessClientConf struct {
12 | password string // Password for auth when connect to other node
13 | discoveryHandler discovery.ServiceDiscovery // Discover service
14 | sessionStorageHandler session_storage.SessionStorage // On-line state storage components
15 | }
16 | )
17 |
18 | // NewNodeConf creates a new NodeConf.
19 | func NewBusinessClientConf(discoveryHandler discovery.ServiceDiscovery, sessionStorageHandler session_storage.SessionStorage) *BusinessClientConf {
20 | return &BusinessClientConf{
21 | password: node.DefaultPassword,
22 | discoveryHandler: discoveryHandler,
23 | sessionStorageHandler: sessionStorageHandler,
24 | }
25 |
26 | }
27 |
28 | // WithPassword sets the password for transport node
29 | func (conf *BusinessClientConf) WithPassword(password string) *BusinessClientConf {
30 | conf.password = password
31 | return conf
32 | }
33 |
--------------------------------------------------------------------------------
/business_client/gateway_client.go:
--------------------------------------------------------------------------------
1 | package business_client
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/weblazy/core/mapreduce"
8 | "github.com/weblazy/easy/econfig"
9 | "github.com/weblazy/easy/elog"
10 | "github.com/weblazy/easy/grpc/grpc_client"
11 | "github.com/weblazy/easy/grpc/grpc_client/grpc_client_config"
12 | "github.com/weblazy/goutil"
13 | "github.com/weblazy/socket-cluster/discovery"
14 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
15 | "github.com/weblazy/socket-cluster/logx"
16 | "go.uber.org/zap"
17 | )
18 |
19 | type GatewayClient struct {
20 | nodeIpMap goutil.Map // Receive messages forwarded by other nodes
21 | nodeIdMap goutil.Map // Receive messages forwarded by other nodes
22 | businessClientConf *BusinessClientConf
23 | }
24 |
25 | func NewGatewayClient(cfg *BusinessClientConf) *GatewayClient {
26 | gatewayClient := GatewayClient{
27 | businessClientConf: cfg,
28 | nodeIpMap: goutil.AtomicMap(),
29 | nodeIdMap: goutil.AtomicMap(),
30 | }
31 | watchChan := make(chan discovery.EventType, 1)
32 | go gatewayClient.businessClientConf.discoveryHandler.WatchService(watchChan)
33 | go func() {
34 | for {
35 | select {
36 | case _, ok := <-watchChan:
37 | if !ok {
38 | logx.LogHandler.Infof("channel close\n")
39 | return
40 | }
41 | err := gatewayClient.updateNodeList()
42 | if err != nil {
43 | logx.LogHandler.Error(err)
44 | }
45 | }
46 | }
47 | }()
48 | err := gatewayClient.updateNodeList()
49 | if err != nil {
50 | elog.ErrorCtx(context.Background(), "updateNodeList", zap.Error(err))
51 | }
52 | return &gatewayClient
53 | }
54 |
55 | func (g *GatewayClient) updateNodeList() error {
56 | nodeMap, err := g.businessClientConf.discoveryHandler.GetServerList()
57 | if err != nil {
58 | return err
59 | }
60 | for k1 := range nodeMap {
61 | addr := k1
62 | // Connection already exists
63 | _, ok := g.nodeIpMap.LoadOrStore(addr, "")
64 | if ok {
65 | continue
66 | }
67 | cfg := grpc_client_config.DefaultConfig()
68 | cfg.Addr = addr
69 | client := grpc_client.NewGrpcClient(cfg)
70 | gatewayClient := gateway.NewGatewayServiceClient(client)
71 | g.nodeIpMap.Store(addr, gatewayClient)
72 | }
73 | return nil
74 | }
75 |
76 | func (g *GatewayClient) SendToClientId(req *gateway.SendToClientIdRequest) (*gateway.SendToClientIdResponse, error) {
77 | if req == nil {
78 | return nil, fmt.Errorf("message is nil")
79 | }
80 | ipArr, err := g.businessClientConf.sessionStorageHandler.GetIps(req.ClientId)
81 | if err == nil {
82 | mapreduce.MapVoid(func(source chan<- interface{}) {
83 | for key := range ipArr {
84 | source <- ipArr[key]
85 | }
86 | }, func(item interface{}) {
87 | ip := item.(string)
88 | connect, ok := g.nodeIpMap.Load(ip)
89 | if ok {
90 | conn, ok := connect.(gateway.GatewayServiceClient)
91 | if !ok {
92 | return
93 | }
94 | clientsMsg := &gateway.SendToClientIdRequest{
95 | ClientId: req.ClientId,
96 | Data: req.Data,
97 | }
98 |
99 | resp, err := conn.SendToClientId(context.Background(), clientsMsg)
100 | if econfig.GlobalViper.GetBool("BaseConfig.Debug") {
101 | elog.InfoCtx(context.Background(), "testlogmsg", zap.Any("clientsMsg", clientsMsg), zap.String("ip", ip), zap.Any("resp", resp))
102 | }
103 | if err != nil {
104 | elog.InfoCtx(context.Background(), "msg", zap.Any("req", req), zap.Error(err))
105 | }
106 | } else {
107 | elog.InfoCtx(context.Background(), "node not online", zap.Any("req", req), zap.String("ip", ip))
108 | return
109 | }
110 |
111 | })
112 | }
113 | return nil, nil
114 | }
115 |
116 | type BatchData struct {
117 | ip string
118 | clientIds []string
119 | }
120 |
121 | func (g *GatewayClient) SendToClientIds(req *gateway.SendToClientIdsRequest) (*gateway.SendToClientIdsResponse, error) {
122 | if req == nil {
123 | return &gateway.SendToClientIdsResponse{
124 | Code: -1,
125 | Msg: "message is nil",
126 | }, nil
127 | }
128 | clientMap, err := g.businessClientConf.sessionStorageHandler.GetClientsIps(req.ClientIds)
129 | if err != nil {
130 | return &gateway.SendToClientIdsResponse{
131 | Code: -1,
132 | Msg: err.Error(),
133 | }, nil
134 | }
135 | // Concurrent sends to other nodes
136 | mapreduce.MapVoid(func(source chan<- interface{}) {
137 | for k1 := range clientMap {
138 | source <- &BatchData{ip: k1, clientIds: clientMap[k1]}
139 | }
140 | }, func(item interface{}) {
141 | batchData := item.(*BatchData)
142 | connect, ok := g.nodeIpMap.Load(batchData.ip)
143 | if ok {
144 | conn, ok := connect.(gateway.GatewayServiceClient)
145 | if !ok {
146 | return
147 | }
148 | ids := make([]string, 0)
149 | for k1 := range batchData.clientIds {
150 | ids = append(ids, batchData.clientIds[k1])
151 | }
152 |
153 | _, err = conn.SendToClientIds(context.Background(), &gateway.SendToClientIdsRequest{
154 | ClientIds: ids,
155 | Data: req.Data,
156 | })
157 | if err != nil {
158 | elog.InfoCtx(context.Background(), "msg", zap.Any("req", req), zap.Error(err))
159 | }
160 | } else {
161 | logx.LogHandler.Error("node:%s not online", batchData.ip)
162 | return
163 | }
164 |
165 | })
166 |
167 | return &gateway.SendToClientIdsResponse{}, nil
168 | }
169 | func (g *GatewayClient) IsOnline(clientId string) (bool, error) {
170 | return g.businessClientConf.sessionStorageHandler.IsOnline(clientId), nil
171 | }
172 |
--------------------------------------------------------------------------------
/discovery/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/discovery/.DS_Store
--------------------------------------------------------------------------------
/discovery/discover.go:
--------------------------------------------------------------------------------
1 | package discovery
2 |
3 | type EventType int32
4 |
5 | const (
6 | PUT EventType = 0
7 | DELETE EventType = 1
8 | )
9 |
10 | type WatchChan chan EventType
11 |
12 | // ServiceDiscovery
13 | type ServiceDiscovery interface {
14 | // SetNodeAddr sets nodeAddr
15 | SetNodeAddr(nodeAddr string)
16 | // WatchService Listens for a new node to start
17 | WatchService(watchChan WatchChan)
18 | // UpdateInfo Updates the information for this node
19 | UpdateInfo([]byte) error
20 | // Register registers the NodeID and notify other nodes
21 | Register() error
22 | // WatchService Listens for a new node to start
23 | GetServerList() (map[string]string, error)
24 | }
25 |
--------------------------------------------------------------------------------
/discovery/etcd_discovery/etcd_discovery.go:
--------------------------------------------------------------------------------
1 | package etcd_discovery
2 |
3 | import (
4 | "context"
5 | "log"
6 | "sync"
7 | "time"
8 |
9 | "github.com/weblazy/socket-cluster/discovery"
10 | "github.com/weblazy/socket-cluster/logx"
11 | "github.com/weblazy/socket-cluster/node"
12 | "go.etcd.io/etcd/api/v3/mvccpb"
13 | clientv3 "go.etcd.io/etcd/client/v3"
14 | )
15 |
16 | const defaultDialTimeout = 5 * time.Second
17 |
18 | // EtcdDiscovery
19 | type EtcdDiscovery struct {
20 | discovery.ServiceDiscovery
21 | cli *clientv3.Client // etcd client
22 | serverList map[string]string // service list
23 | lock sync.Mutex
24 | lease int64
25 | nodeAddr string
26 | key string
27 | val string // value
28 | leaseID clientv3.LeaseID
29 | keepAliveChan <-chan *clientv3.LeaseKeepAliveResponse // chan for renewal of lease
30 | }
31 |
32 | // NewEtcdDiscovery return a EtcdDiscovery
33 | func NewEtcdDiscovery(conf clientv3.Config) *EtcdDiscovery {
34 | cli, err := clientv3.New(conf)
35 | if err != nil {
36 | log.Fatal(err)
37 | }
38 | return &EtcdDiscovery{
39 | cli: cli,
40 | serverList: make(map[string]string),
41 | key: node.NodeAddress,
42 | }
43 | }
44 |
45 | // SetNodeAddr sets a nodeAddr
46 | func (this *EtcdDiscovery) SetNodeAddr(nodeAddr string) {
47 | this.nodeAddr = nodeAddr
48 | }
49 |
50 | // WatchService Listens for a new node to start
51 | func (s *EtcdDiscovery) WatchService(watchChan discovery.WatchChan) {
52 | prefix := s.key
53 | rch := s.cli.Watch(context.Background(), prefix, clientv3.WithPrefix())
54 | for wresp := range rch {
55 | for _, ev := range wresp.Events {
56 | switch ev.Type {
57 | case mvccpb.PUT: // Modify or add
58 | watchChan <- discovery.PUT
59 | case mvccpb.DELETE: // delete
60 |
61 | }
62 | }
63 | }
64 | }
65 |
66 | // Close closes the etcd client
67 | func (s *EtcdDiscovery) Close() error {
68 | return s.cli.Close()
69 | }
70 |
71 | // Register registers the nodeId and notify other nodes
72 | func (s *EtcdDiscovery) Register() error {
73 | //sets the lease time
74 | resp, err := s.cli.Grant(context.Background(), s.lease)
75 | if err != nil {
76 | return err
77 | }
78 | // register and bind lease
79 | _, err = s.cli.Put(context.Background(), s.key, "string(value)", clientv3.WithLease(resp.ID))
80 | if err != nil {
81 | return err
82 | }
83 | // Set up renewal time to send renewal request
84 | leaseRespChan, err := s.cli.KeepAlive(context.Background(), resp.ID)
85 |
86 | if err != nil {
87 | return err
88 | }
89 | s.leaseID = resp.ID
90 | s.keepAliveChan = leaseRespChan
91 | go s.ListenLeaseRespChan()
92 | return nil
93 | }
94 |
95 | func (s *EtcdDiscovery) GetServerList() (map[string]string, error) {
96 | resp, err := s.cli.Get(context.Background(), s.key)
97 | if err != nil {
98 | return nil, err
99 | }
100 | for _, obj := range resp.Kvs {
101 | s.serverList[string(obj.Key)] = string(obj.Value)
102 | }
103 | return s.serverList, nil
104 | }
105 |
106 | // ListenLeaseRespChan Monitor lease renewals
107 | func (s *EtcdDiscovery) ListenLeaseRespChan() {
108 | for leaseKeepResp := range s.keepAliveChan {
109 | logx.LogHandler.Info("续约成功", leaseKeepResp)
110 | }
111 | logx.LogHandler.Infof("关闭续租")
112 | }
113 |
--------------------------------------------------------------------------------
/discovery/redis_discovery/redis_discovery.go:
--------------------------------------------------------------------------------
1 | package redis_discovery
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "sort"
7 | "time"
8 |
9 | "github.com/spf13/cast"
10 | "github.com/weblazy/easy/db/eredis"
11 | "github.com/weblazy/easy/sortx"
12 | "github.com/weblazy/socket-cluster/discovery"
13 | "github.com/weblazy/socket-cluster/logx"
14 | "github.com/weblazy/socket-cluster/node"
15 | )
16 |
17 | // RedisDiscovery
18 | type RedisDiscovery struct {
19 | discovery.ServiceDiscovery
20 | nodeAddr string
21 | adminRedis *eredis.RedisClient
22 | key string
23 | timeout int64
24 | }
25 |
26 | // NewRedisDiscovery return a RedisDiscovery
27 | func NewRedisDiscovery(adminRedis *eredis.RedisClient) *RedisDiscovery {
28 | return &RedisDiscovery{
29 | adminRedis: adminRedis,
30 | timeout: 120,
31 | key: node.NodeAddress,
32 | }
33 | }
34 |
35 | // SetNodeAddr sets a addr
36 | func (this *RedisDiscovery) SetNodeAddr(addr string) {
37 | this.nodeAddr = addr
38 | }
39 |
40 | // WatchService Listens for a new node to start
41 | func (this *RedisDiscovery) WatchService(watchChan discovery.WatchChan) {
42 | go func() {
43 | pb := this.adminRedis.Subscribe(context.Background(), this.key)
44 | for mg := range pb.Channel() {
45 | data := make(map[string]interface{})
46 | err := json.Unmarshal([]byte(mg.Payload), &data)
47 | if err != nil {
48 | logx.LogHandler.Error(err)
49 | return
50 | }
51 | watchChan <- discovery.PUT
52 | }
53 | }()
54 | }
55 |
56 | // Close closes the redis
57 | func (s *RedisDiscovery) Close() error {
58 | return s.adminRedis.Close()
59 | }
60 |
61 | // Register registers the NodeID and notify other nodes
62 | func (this *RedisDiscovery) Register() error {
63 | err := this.adminRedis.Publish(context.Background(), this.key, this.nodeAddr).Err()
64 | if err != nil {
65 | return err
66 | }
67 | return nil
68 | }
69 |
70 | // UpdateInfo Update the information for this node
71 | func (this *RedisDiscovery) UpdateInfo(nodeInfoByte []byte) error {
72 | err := this.adminRedis.HSet(context.Background(), node.NodeAddress, this.nodeAddr, string(nodeInfoByte)).Err()
73 | if err != nil {
74 | return err
75 | }
76 | err = this.adminRedis.Expire(context.Background(), node.NodeAddress, time.Duration(this.timeout*int64(time.Second))).Err()
77 | if err != nil {
78 | return err
79 | }
80 | return nil
81 | }
82 |
83 | // GetInfo get node information
84 | func (this *RedisDiscovery) GetInfo() ([]string, error) {
85 | list := make([]string, 0)
86 | now := time.Now().Unix()
87 | addrMap, err := this.adminRedis.HGetAll(context.Background(), node.NodeAddress).Result()
88 | if err != nil {
89 | return nil, err
90 | }
91 | sortList := sortx.NewSortList(sortx.DESC)
92 | expire := now - this.timeout
93 | for k1 := range addrMap {
94 | addrObj := make(map[string]interface{})
95 | err := json.Unmarshal([]byte(addrMap[k1]), &addrObj)
96 | if err != nil {
97 | return nil, err
98 | }
99 | if cast.ToInt64(addrObj["timestamp"]) > expire {
100 | sortList.List = append(sortList.List, sortx.Sort{
101 | Obj: k1,
102 | Sort: cast.ToFloat64(addrObj["client_count"]),
103 | })
104 | }
105 | }
106 | sort.Sort(sortList)
107 | for k1 := range sortList.List {
108 | list = append(list, sortList.List[k1].Obj.(string))
109 | }
110 | return list, nil
111 | }
112 |
113 | func (this *RedisDiscovery) GetServerList() (map[string]string, error) {
114 | addrMap, err := this.adminRedis.HGetAll(context.Background(), node.NodeAddress).Result()
115 | if err != nil {
116 | return nil, err
117 | }
118 | resp := map[string]string{}
119 | for k, v := range addrMap {
120 | resp[k] = v
121 | }
122 | return resp, nil
123 | }
124 |
--------------------------------------------------------------------------------
/dns/dns.go:
--------------------------------------------------------------------------------
1 | package dns
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "net"
8 | "strings"
9 | )
10 |
11 | type Target struct {
12 | Scheme string
13 | Authority string
14 | Endpoint string
15 | }
16 |
17 | // parseTarget takes the user input target string and default port, returns formatted host and port info.
18 | // If target doesn't specify a port, set the port to be the defaultPort.
19 | // If target is in IPv6 format and host-name is enclosed in square brackets, brackets
20 | // are stripped when setting the host.
21 | // examples:
22 | // target: "www.google.com" defaultPort: "443" returns host: "www.google.com", port: "443"
23 | // target: "ipv4-host:80" defaultPort: "443" returns host: "ipv4-host", port: "80"
24 | // target: "[ipv6-host]" defaultPort: "443" returns host: "ipv6-host", port: "443"
25 | // target: ":80" defaultPort: "443" returns host: "localhost", port: "80"
26 | func ParsePort(target, defaultPort string) (host, port string, err error) {
27 | if target == "" {
28 | return "", "", errors.New("")
29 | }
30 | if ip := net.ParseIP(target); ip != nil {
31 | // target is an IPv4 or IPv6(without brackets) address
32 | return target, defaultPort, nil
33 | }
34 | if host, port, err = net.SplitHostPort(target); err == nil {
35 | if port == "" {
36 | // If the port field is empty (target ends with colon), e.g. "[::1]:", this is an error.
37 | return "", "", errors.New("")
38 | }
39 | // target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
40 | if host == "" {
41 | // Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
42 | host = "localhost"
43 | }
44 | return host, port, nil
45 | }
46 | if host, port, err = net.SplitHostPort(target + ":" + defaultPort); err == nil {
47 | // target doesn't have port
48 | return host, port, nil
49 | }
50 | return "", "", fmt.Errorf("invalid target address %v, error info: %v", target, err)
51 | }
52 |
53 | // split2 returns the values from strings.SplitN(s, sep, 2).
54 | // If sep is not found, it returns ("", "", false) instead.
55 | func split2(s, sep string) (string, string, bool) {
56 | spl := strings.SplitN(s, sep, 2)
57 | if len(spl) < 2 {
58 | return "", "", false
59 | }
60 | return spl[0], spl[1], true
61 | }
62 |
63 | // ParseTarget splits target into a resolver.Target struct containing scheme,
64 | // authority and endpoint.
65 | //
66 | // If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
67 | // target}.
68 | func ParseScheme(target string) (ret Target) {
69 | var ok bool
70 | ret.Scheme, ret.Endpoint, ok = split2(target, "://")
71 | if !ok {
72 | return Target{Endpoint: target}
73 | }
74 | ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
75 | if !ok {
76 | return Target{Endpoint: target}
77 | }
78 | return ret
79 | }
80 |
81 | // Resolve the DNS domain name
82 | func DnsParse(domain string) ([]string, string, error) {
83 | target := ParseScheme(domain)
84 | host, port, _ := ParsePort(target.Endpoint, "80")
85 | ipList, err := net.DefaultResolver.LookupHost(context.Background(), host)
86 | return ipList, port, err
87 | }
88 |
--------------------------------------------------------------------------------
/docs/performance_test.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/docs/performance_test.md
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/weblazy/socket-cluster
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/go-redis/redis/v8 v8.11.5
7 | github.com/golang/protobuf v1.5.3
8 | github.com/gorilla/websocket v1.5.0
9 | github.com/labstack/echo/v4 v4.9.0
10 | github.com/onsi/ginkgo/v2 v2.9.5 // indirect
11 | github.com/satori/go.uuid v1.2.0
12 | github.com/spf13/cast v1.5.0
13 | github.com/urfave/cli/v2 v2.25.7
14 | github.com/weblazy/core v1.1.1
15 | github.com/weblazy/easy v1.1.10-0.20230819085829-bbe510ae9fab
16 | github.com/weblazy/goutil v1.1.2
17 | go.etcd.io/etcd/api/v3 v3.5.9
18 | go.etcd.io/etcd/client/v3 v3.5.9
19 | go.uber.org/zap v1.21.0
20 | golang.org/x/crypto v0.4.0 // indirect
21 | google.golang.org/grpc v1.51.0
22 | google.golang.org/protobuf v1.28.1
23 |
24 | )
25 |
--------------------------------------------------------------------------------
/grpcs/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "os"
6 |
7 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway"
8 |
9 | "github.com/urfave/cli/v2"
10 | "github.com/weblazy/easy/elog"
11 | "github.com/weblazy/easy/print"
12 | )
13 |
14 | const (
15 | ProjectName = "socket_cluster"
16 | ProjectVersion = "v1.0.0"
17 | )
18 |
19 | func main() {
20 | // 打印Banner
21 | print.PrintBanner(ProjectName)
22 | // 配置cli参数
23 | cliApp := cli.NewApp()
24 | cliApp.Name = ProjectName
25 | cliApp.Version = ProjectVersion
26 |
27 | // 指定命令运行的函数
28 | cliApp.Commands = []*cli.Command{
29 | socket_cluster_gateway.Cmd,
30 | }
31 |
32 | // 启动cli
33 | if err := cliApp.Run(os.Args); err != nil {
34 | elog.ErrorCtx(context.Background(), "Failed to start application", elog.FieldError(err))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/weblazy/easy/grpc/grpc_server/grpc_server_config"
5 | )
6 |
7 | type Config struct {
8 | BaseConfig struct{}
9 | GrpcServerConfig *grpc_server_config.Config
10 | }
11 |
12 | var Conf = Config{
13 | BaseConfig: struct{}{},
14 | GrpcServerConfig: grpc_server_config.DefaultConfig(),
15 | }
16 |
17 | var LocalConfig = ""
18 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/handler/gateway.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/logic/gateway_logic"
7 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
8 | "github.com/weblazy/socket-cluster/node"
9 | "go.uber.org/zap"
10 |
11 | "github.com/weblazy/easy/code_err"
12 | "github.com/weblazy/easy/econfig"
13 | "github.com/weblazy/easy/elog"
14 | )
15 |
16 | type GatewayService struct {
17 | gateway.UnimplementedGatewayServiceServer
18 | Node node.Node
19 | }
20 |
21 | func NewGatewayService(n node.Node) *GatewayService {
22 | return &GatewayService{
23 | Node: n,
24 | }
25 | }
26 |
27 | func (h *GatewayService) IsOnline(ctx context.Context, req *gateway.IsOnlineRequest) (*gateway.IsOnlineResponse, error) {
28 | svcCtx := &gateway_logic.IsOnlineCtx{
29 | Log: code_err.NewLog(ctx),
30 | Req: req,
31 | Res: new(gateway.IsOnlineResponse),
32 | }
33 | err := gateway_logic.IsOnline(svcCtx)
34 | if err != nil {
35 | svcCtx.Res.Code = err.Code
36 | svcCtx.Res.Msg = err.Msg
37 | }
38 | return svcCtx.Res, nil
39 | }
40 |
41 | func (h *GatewayService) SendToClientId(ctx context.Context, req *gateway.SendToClientIdRequest) (*gateway.SendToClientIdResponse, error) {
42 | if econfig.GlobalViper.GetBool("BaseConfig.Debug") {
43 | elog.InfoCtx(ctx, "SendToClientId", zap.Any("req", req))
44 | }
45 | resp := gateway.SendToClientIdResponse{}
46 | err := h.Node.SendToClientId(req.ClientId, req.Data)
47 | if err != nil {
48 | resp.Code = -1
49 | resp.Msg = err.Error()
50 | }
51 | return &resp, nil
52 | }
53 |
54 | func (h *GatewayService) SendToClientIds(ctx context.Context, req *gateway.SendToClientIdsRequest) (*gateway.SendToClientIdsResponse, error) {
55 | resp := gateway.SendToClientIdsResponse{}
56 | err := h.Node.SendToClientIds(req.ClientIds, req.Data)
57 | if err != nil {
58 | resp.Code = -1
59 | resp.Msg = err.Error()
60 | }
61 | return &resp, nil
62 | }
63 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/logic/gateway_logic/is_online.go:
--------------------------------------------------------------------------------
1 | package gateway_logic
2 |
3 | import (
4 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
5 |
6 | "github.com/weblazy/easy/code_err"
7 | )
8 |
9 | type IsOnlineCtx struct {
10 | *code_err.Log
11 | Req *gateway.IsOnlineRequest
12 | Res *gateway.IsOnlineResponse
13 | }
14 |
15 | func IsOnline(ctx *IsOnlineCtx) *code_err.CodeErr {
16 | return nil
17 | }
18 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/logic/gateway_logic/send_to_client_id.go:
--------------------------------------------------------------------------------
1 | package gateway_logic
2 |
3 | import (
4 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
5 |
6 | "github.com/weblazy/easy/code_err"
7 | )
8 |
9 | type SendToClientIdCtx struct {
10 | *code_err.Log
11 | Req *gateway.SendToClientIdRequest
12 | Res *gateway.SendToClientIdResponse
13 | }
14 |
15 | func SendToClientId(ctx *SendToClientIdCtx) *code_err.CodeErr {
16 | return nil
17 | }
18 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/logic/gateway_logic/send_to_client_ids.go:
--------------------------------------------------------------------------------
1 | package gateway_logic
2 |
3 | import (
4 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
5 |
6 | "github.com/weblazy/easy/code_err"
7 | )
8 |
9 | type SendToClientIdsCtx struct {
10 | *code_err.Log
11 | Req *gateway.SendToClientIdsRequest
12 | Res *gateway.SendToClientIdsResponse
13 | }
14 |
15 | func SendToClientIds(ctx *SendToClientIdsCtx) *code_err.CodeErr {
16 | return nil
17 | }
18 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/proto/gateway/gateway.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // versions:
3 | // protoc-gen-go v1.26.0
4 | // protoc v3.14.0
5 | // source: grpcs/socket_cluster_gateway/proto/gateway/gateway.proto
6 |
7 | package gateway
8 |
9 | import (
10 | reflect "reflect"
11 | sync "sync"
12 |
13 | protoreflect "google.golang.org/protobuf/reflect/protoreflect"
14 | protoimpl "google.golang.org/protobuf/runtime/protoimpl"
15 | )
16 |
17 | const (
18 | // Verify that this generated code is sufficiently up-to-date.
19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
20 | // Verify that runtime/protoimpl is sufficiently up-to-date.
21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
22 | )
23 |
24 | type IsOnlineRequest struct {
25 | state protoimpl.MessageState
26 | sizeCache protoimpl.SizeCache
27 | unknownFields protoimpl.UnknownFields
28 |
29 | ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
30 | }
31 |
32 | func (x *IsOnlineRequest) Reset() {
33 | *x = IsOnlineRequest{}
34 | if protoimpl.UnsafeEnabled {
35 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[0]
36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
37 | ms.StoreMessageInfo(mi)
38 | }
39 | }
40 |
41 | func (x *IsOnlineRequest) String() string {
42 | return protoimpl.X.MessageStringOf(x)
43 | }
44 |
45 | func (*IsOnlineRequest) ProtoMessage() {}
46 |
47 | func (x *IsOnlineRequest) ProtoReflect() protoreflect.Message {
48 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[0]
49 | if protoimpl.UnsafeEnabled && x != nil {
50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
51 | if ms.LoadMessageInfo() == nil {
52 | ms.StoreMessageInfo(mi)
53 | }
54 | return ms
55 | }
56 | return mi.MessageOf(x)
57 | }
58 |
59 | // Deprecated: Use IsOnlineRequest.ProtoReflect.Descriptor instead.
60 | func (*IsOnlineRequest) Descriptor() ([]byte, []int) {
61 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP(), []int{0}
62 | }
63 |
64 | func (x *IsOnlineRequest) GetClientId() string {
65 | if x != nil {
66 | return x.ClientId
67 | }
68 | return ""
69 | }
70 |
71 | type IsOnlineResponse struct {
72 | state protoimpl.MessageState
73 | sizeCache protoimpl.SizeCache
74 | unknownFields protoimpl.UnknownFields
75 |
76 | Code int64 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
77 | Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"`
78 | IsOnline bool `protobuf:"varint,3,opt,name=is_online,json=isOnline,proto3" json:"is_online,omitempty"`
79 | }
80 |
81 | func (x *IsOnlineResponse) Reset() {
82 | *x = IsOnlineResponse{}
83 | if protoimpl.UnsafeEnabled {
84 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[1]
85 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
86 | ms.StoreMessageInfo(mi)
87 | }
88 | }
89 |
90 | func (x *IsOnlineResponse) String() string {
91 | return protoimpl.X.MessageStringOf(x)
92 | }
93 |
94 | func (*IsOnlineResponse) ProtoMessage() {}
95 |
96 | func (x *IsOnlineResponse) ProtoReflect() protoreflect.Message {
97 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[1]
98 | if protoimpl.UnsafeEnabled && x != nil {
99 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
100 | if ms.LoadMessageInfo() == nil {
101 | ms.StoreMessageInfo(mi)
102 | }
103 | return ms
104 | }
105 | return mi.MessageOf(x)
106 | }
107 |
108 | // Deprecated: Use IsOnlineResponse.ProtoReflect.Descriptor instead.
109 | func (*IsOnlineResponse) Descriptor() ([]byte, []int) {
110 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP(), []int{1}
111 | }
112 |
113 | func (x *IsOnlineResponse) GetCode() int64 {
114 | if x != nil {
115 | return x.Code
116 | }
117 | return 0
118 | }
119 |
120 | func (x *IsOnlineResponse) GetMsg() string {
121 | if x != nil {
122 | return x.Msg
123 | }
124 | return ""
125 | }
126 |
127 | func (x *IsOnlineResponse) GetIsOnline() bool {
128 | if x != nil {
129 | return x.IsOnline
130 | }
131 | return false
132 | }
133 |
134 | type SendToClientIdRequest struct {
135 | state protoimpl.MessageState
136 | sizeCache protoimpl.SizeCache
137 | unknownFields protoimpl.UnknownFields
138 |
139 | ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
140 | Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
141 | }
142 |
143 | func (x *SendToClientIdRequest) Reset() {
144 | *x = SendToClientIdRequest{}
145 | if protoimpl.UnsafeEnabled {
146 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[2]
147 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
148 | ms.StoreMessageInfo(mi)
149 | }
150 | }
151 |
152 | func (x *SendToClientIdRequest) String() string {
153 | return protoimpl.X.MessageStringOf(x)
154 | }
155 |
156 | func (*SendToClientIdRequest) ProtoMessage() {}
157 |
158 | func (x *SendToClientIdRequest) ProtoReflect() protoreflect.Message {
159 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[2]
160 | if protoimpl.UnsafeEnabled && x != nil {
161 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
162 | if ms.LoadMessageInfo() == nil {
163 | ms.StoreMessageInfo(mi)
164 | }
165 | return ms
166 | }
167 | return mi.MessageOf(x)
168 | }
169 |
170 | // Deprecated: Use SendToClientIdRequest.ProtoReflect.Descriptor instead.
171 | func (*SendToClientIdRequest) Descriptor() ([]byte, []int) {
172 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP(), []int{2}
173 | }
174 |
175 | func (x *SendToClientIdRequest) GetClientId() string {
176 | if x != nil {
177 | return x.ClientId
178 | }
179 | return ""
180 | }
181 |
182 | func (x *SendToClientIdRequest) GetData() []byte {
183 | if x != nil {
184 | return x.Data
185 | }
186 | return nil
187 | }
188 |
189 | type SendToClientIdResponse struct {
190 | state protoimpl.MessageState
191 | sizeCache protoimpl.SizeCache
192 | unknownFields protoimpl.UnknownFields
193 |
194 | Code int64 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
195 | Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"`
196 | }
197 |
198 | func (x *SendToClientIdResponse) Reset() {
199 | *x = SendToClientIdResponse{}
200 | if protoimpl.UnsafeEnabled {
201 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[3]
202 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
203 | ms.StoreMessageInfo(mi)
204 | }
205 | }
206 |
207 | func (x *SendToClientIdResponse) String() string {
208 | return protoimpl.X.MessageStringOf(x)
209 | }
210 |
211 | func (*SendToClientIdResponse) ProtoMessage() {}
212 |
213 | func (x *SendToClientIdResponse) ProtoReflect() protoreflect.Message {
214 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[3]
215 | if protoimpl.UnsafeEnabled && x != nil {
216 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
217 | if ms.LoadMessageInfo() == nil {
218 | ms.StoreMessageInfo(mi)
219 | }
220 | return ms
221 | }
222 | return mi.MessageOf(x)
223 | }
224 |
225 | // Deprecated: Use SendToClientIdResponse.ProtoReflect.Descriptor instead.
226 | func (*SendToClientIdResponse) Descriptor() ([]byte, []int) {
227 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP(), []int{3}
228 | }
229 |
230 | func (x *SendToClientIdResponse) GetCode() int64 {
231 | if x != nil {
232 | return x.Code
233 | }
234 | return 0
235 | }
236 |
237 | func (x *SendToClientIdResponse) GetMsg() string {
238 | if x != nil {
239 | return x.Msg
240 | }
241 | return ""
242 | }
243 |
244 | type SendToClientIdsRequest struct {
245 | state protoimpl.MessageState
246 | sizeCache protoimpl.SizeCache
247 | unknownFields protoimpl.UnknownFields
248 |
249 | ClientIds []string `protobuf:"bytes,1,rep,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"`
250 | Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
251 | }
252 |
253 | func (x *SendToClientIdsRequest) Reset() {
254 | *x = SendToClientIdsRequest{}
255 | if protoimpl.UnsafeEnabled {
256 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[4]
257 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
258 | ms.StoreMessageInfo(mi)
259 | }
260 | }
261 |
262 | func (x *SendToClientIdsRequest) String() string {
263 | return protoimpl.X.MessageStringOf(x)
264 | }
265 |
266 | func (*SendToClientIdsRequest) ProtoMessage() {}
267 |
268 | func (x *SendToClientIdsRequest) ProtoReflect() protoreflect.Message {
269 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[4]
270 | if protoimpl.UnsafeEnabled && x != nil {
271 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
272 | if ms.LoadMessageInfo() == nil {
273 | ms.StoreMessageInfo(mi)
274 | }
275 | return ms
276 | }
277 | return mi.MessageOf(x)
278 | }
279 |
280 | // Deprecated: Use SendToClientIdsRequest.ProtoReflect.Descriptor instead.
281 | func (*SendToClientIdsRequest) Descriptor() ([]byte, []int) {
282 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP(), []int{4}
283 | }
284 |
285 | func (x *SendToClientIdsRequest) GetClientIds() []string {
286 | if x != nil {
287 | return x.ClientIds
288 | }
289 | return nil
290 | }
291 |
292 | func (x *SendToClientIdsRequest) GetData() []byte {
293 | if x != nil {
294 | return x.Data
295 | }
296 | return nil
297 | }
298 |
299 | type SendToClientIdsResponse struct {
300 | state protoimpl.MessageState
301 | sizeCache protoimpl.SizeCache
302 | unknownFields protoimpl.UnknownFields
303 |
304 | Code int64 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
305 | Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"`
306 | }
307 |
308 | func (x *SendToClientIdsResponse) Reset() {
309 | *x = SendToClientIdsResponse{}
310 | if protoimpl.UnsafeEnabled {
311 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[5]
312 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
313 | ms.StoreMessageInfo(mi)
314 | }
315 | }
316 |
317 | func (x *SendToClientIdsResponse) String() string {
318 | return protoimpl.X.MessageStringOf(x)
319 | }
320 |
321 | func (*SendToClientIdsResponse) ProtoMessage() {}
322 |
323 | func (x *SendToClientIdsResponse) ProtoReflect() protoreflect.Message {
324 | mi := &file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[5]
325 | if protoimpl.UnsafeEnabled && x != nil {
326 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
327 | if ms.LoadMessageInfo() == nil {
328 | ms.StoreMessageInfo(mi)
329 | }
330 | return ms
331 | }
332 | return mi.MessageOf(x)
333 | }
334 |
335 | // Deprecated: Use SendToClientIdsResponse.ProtoReflect.Descriptor instead.
336 | func (*SendToClientIdsResponse) Descriptor() ([]byte, []int) {
337 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP(), []int{5}
338 | }
339 |
340 | func (x *SendToClientIdsResponse) GetCode() int64 {
341 | if x != nil {
342 | return x.Code
343 | }
344 | return 0
345 | }
346 |
347 | func (x *SendToClientIdsResponse) GetMsg() string {
348 | if x != nil {
349 | return x.Msg
350 | }
351 | return ""
352 | }
353 |
354 | var File_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto protoreflect.FileDescriptor
355 |
356 | var file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDesc = []byte{
357 | 0x0a, 0x38, 0x67, 0x72, 0x70, 0x63, 0x73, 0x2f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x63,
358 | 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x70,
359 | 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x67, 0x61, 0x74,
360 | 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x67, 0x61, 0x74, 0x65,
361 | 0x77, 0x61, 0x79, 0x22, 0x2e, 0x0a, 0x0f, 0x49, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x52,
362 | 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
363 | 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
364 | 0x74, 0x49, 0x64, 0x22, 0x55, 0x0a, 0x10, 0x49, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x52,
365 | 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18,
366 | 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d,
367 | 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x1b, 0x0a,
368 | 0x09, 0x69, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
369 | 0x52, 0x08, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x48, 0x0a, 0x15, 0x53, 0x65,
370 | 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75,
371 | 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
372 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64,
373 | 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
374 | 0x64, 0x61, 0x74, 0x61, 0x22, 0x3e, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c,
375 | 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12,
376 | 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f,
377 | 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
378 | 0x03, 0x6d, 0x73, 0x67, 0x22, 0x4b, 0x0a, 0x16, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c,
379 | 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
380 | 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03,
381 | 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x12, 0x12, 0x0a,
382 | 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74,
383 | 0x61, 0x22, 0x3f, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e,
384 | 0x74, 0x49, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04,
385 | 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65,
386 | 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d,
387 | 0x73, 0x67, 0x32, 0xfa, 0x01, 0x0a, 0x0e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65,
388 | 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x49, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e,
389 | 0x65, 0x12, 0x18, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x49, 0x73, 0x4f, 0x6e,
390 | 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x61,
391 | 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x49, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65,
392 | 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f,
393 | 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
394 | 0x61, 0x79, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
395 | 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
396 | 0x61, 0x79, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
397 | 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0f, 0x53, 0x65, 0x6e,
398 | 0x64, 0x54, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x12, 0x1f, 0x2e, 0x67,
399 | 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c, 0x69,
400 | 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e,
401 | 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x43, 0x6c,
402 | 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
403 | 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x62, 0x06, 0x70, 0x72,
404 | 0x6f, 0x74, 0x6f, 0x33,
405 | }
406 |
407 | var (
408 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescOnce sync.Once
409 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescData = file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDesc
410 | )
411 |
412 | func file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescGZIP() []byte {
413 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescOnce.Do(func() {
414 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescData = protoimpl.X.CompressGZIP(file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescData)
415 | })
416 | return file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDescData
417 | }
418 |
419 | var file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
420 | var file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_goTypes = []interface{}{
421 | (*IsOnlineRequest)(nil), // 0: gateway.IsOnlineRequest
422 | (*IsOnlineResponse)(nil), // 1: gateway.IsOnlineResponse
423 | (*SendToClientIdRequest)(nil), // 2: gateway.SendToClientIdRequest
424 | (*SendToClientIdResponse)(nil), // 3: gateway.SendToClientIdResponse
425 | (*SendToClientIdsRequest)(nil), // 4: gateway.SendToClientIdsRequest
426 | (*SendToClientIdsResponse)(nil), // 5: gateway.SendToClientIdsResponse
427 | }
428 | var file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_depIdxs = []int32{
429 | 0, // 0: gateway.GatewayService.IsOnline:input_type -> gateway.IsOnlineRequest
430 | 2, // 1: gateway.GatewayService.SendToClientId:input_type -> gateway.SendToClientIdRequest
431 | 4, // 2: gateway.GatewayService.SendToClientIds:input_type -> gateway.SendToClientIdsRequest
432 | 1, // 3: gateway.GatewayService.IsOnline:output_type -> gateway.IsOnlineResponse
433 | 3, // 4: gateway.GatewayService.SendToClientId:output_type -> gateway.SendToClientIdResponse
434 | 5, // 5: gateway.GatewayService.SendToClientIds:output_type -> gateway.SendToClientIdsResponse
435 | 3, // [3:6] is the sub-list for method output_type
436 | 0, // [0:3] is the sub-list for method input_type
437 | 0, // [0:0] is the sub-list for extension type_name
438 | 0, // [0:0] is the sub-list for extension extendee
439 | 0, // [0:0] is the sub-list for field type_name
440 | }
441 |
442 | func init() { file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_init() }
443 | func file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_init() {
444 | if File_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto != nil {
445 | return
446 | }
447 | if !protoimpl.UnsafeEnabled {
448 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
449 | switch v := v.(*IsOnlineRequest); i {
450 | case 0:
451 | return &v.state
452 | case 1:
453 | return &v.sizeCache
454 | case 2:
455 | return &v.unknownFields
456 | default:
457 | return nil
458 | }
459 | }
460 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
461 | switch v := v.(*IsOnlineResponse); i {
462 | case 0:
463 | return &v.state
464 | case 1:
465 | return &v.sizeCache
466 | case 2:
467 | return &v.unknownFields
468 | default:
469 | return nil
470 | }
471 | }
472 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
473 | switch v := v.(*SendToClientIdRequest); i {
474 | case 0:
475 | return &v.state
476 | case 1:
477 | return &v.sizeCache
478 | case 2:
479 | return &v.unknownFields
480 | default:
481 | return nil
482 | }
483 | }
484 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
485 | switch v := v.(*SendToClientIdResponse); i {
486 | case 0:
487 | return &v.state
488 | case 1:
489 | return &v.sizeCache
490 | case 2:
491 | return &v.unknownFields
492 | default:
493 | return nil
494 | }
495 | }
496 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
497 | switch v := v.(*SendToClientIdsRequest); i {
498 | case 0:
499 | return &v.state
500 | case 1:
501 | return &v.sizeCache
502 | case 2:
503 | return &v.unknownFields
504 | default:
505 | return nil
506 | }
507 | }
508 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
509 | switch v := v.(*SendToClientIdsResponse); i {
510 | case 0:
511 | return &v.state
512 | case 1:
513 | return &v.sizeCache
514 | case 2:
515 | return &v.unknownFields
516 | default:
517 | return nil
518 | }
519 | }
520 | }
521 | type x struct{}
522 | out := protoimpl.TypeBuilder{
523 | File: protoimpl.DescBuilder{
524 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
525 | RawDescriptor: file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDesc,
526 | NumEnums: 0,
527 | NumMessages: 6,
528 | NumExtensions: 0,
529 | NumServices: 1,
530 | },
531 | GoTypes: file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_goTypes,
532 | DependencyIndexes: file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_depIdxs,
533 | MessageInfos: file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_msgTypes,
534 | }.Build()
535 | File_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto = out.File
536 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_rawDesc = nil
537 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_goTypes = nil
538 | file_grpcs_socket_cluster_gateway_proto_gateway_gateway_proto_depIdxs = nil
539 | }
540 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/proto/gateway/gateway.proto:
--------------------------------------------------------------------------------
1 |
2 | syntax = "proto3";
3 |
4 | package gateway;
5 |
6 | option go_package = "./gateway";
7 |
8 | message IsOnlineRequest{
9 | string client_id = 1;
10 |
11 | }
12 | message IsOnlineResponse{
13 | int64 code = 1;
14 | string msg = 2;
15 | bool is_online = 3;
16 |
17 | }
18 | message SendToClientIdRequest{
19 | string client_id = 1;
20 | bytes data = 2;
21 |
22 | }
23 | message SendToClientIdResponse{
24 | int64 code = 1;
25 | string msg = 2;
26 |
27 | }
28 | message SendToClientIdsRequest{
29 | repeated string client_ids = 1;
30 | bytes data = 2;
31 |
32 | }
33 | message SendToClientIdsResponse{
34 | int64 code = 1;
35 | string msg = 2;
36 |
37 | }
38 |
39 |
40 | service GatewayService{
41 | rpc IsOnline(IsOnlineRequest) returns (IsOnlineResponse); rpc SendToClientId(SendToClientIdRequest) returns (SendToClientIdResponse); rpc SendToClientIds(SendToClientIdsRequest) returns (SendToClientIdsResponse);
42 | }
43 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/proto/gateway/gateway_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc v3.14.0
5 | // source: grpcs/socket_cluster_gateway/proto/gateway/gateway.proto
6 |
7 | package gateway
8 |
9 | import (
10 | context "context"
11 |
12 | grpc "google.golang.org/grpc"
13 | codes "google.golang.org/grpc/codes"
14 | status "google.golang.org/grpc/status"
15 | )
16 |
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the grpc package it is being compiled against.
19 | // Requires gRPC-Go v1.32.0 or later.
20 | const _ = grpc.SupportPackageIsVersion7
21 |
22 | const (
23 | GatewayService_IsOnline_FullMethodName = "/gateway.GatewayService/IsOnline"
24 | GatewayService_SendToClientId_FullMethodName = "/gateway.GatewayService/SendToClientId"
25 | GatewayService_SendToClientIds_FullMethodName = "/gateway.GatewayService/SendToClientIds"
26 | )
27 |
28 | // GatewayServiceClient is the client API for GatewayService service.
29 | //
30 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
31 | type GatewayServiceClient interface {
32 | IsOnline(ctx context.Context, in *IsOnlineRequest, opts ...grpc.CallOption) (*IsOnlineResponse, error)
33 | SendToClientId(ctx context.Context, in *SendToClientIdRequest, opts ...grpc.CallOption) (*SendToClientIdResponse, error)
34 | SendToClientIds(ctx context.Context, in *SendToClientIdsRequest, opts ...grpc.CallOption) (*SendToClientIdsResponse, error)
35 | }
36 |
37 | type gatewayServiceClient struct {
38 | cc grpc.ClientConnInterface
39 | }
40 |
41 | func NewGatewayServiceClient(cc grpc.ClientConnInterface) GatewayServiceClient {
42 | return &gatewayServiceClient{cc}
43 | }
44 |
45 | func (c *gatewayServiceClient) IsOnline(ctx context.Context, in *IsOnlineRequest, opts ...grpc.CallOption) (*IsOnlineResponse, error) {
46 | out := new(IsOnlineResponse)
47 | err := c.cc.Invoke(ctx, GatewayService_IsOnline_FullMethodName, in, out, opts...)
48 | if err != nil {
49 | return nil, err
50 | }
51 | return out, nil
52 | }
53 |
54 | func (c *gatewayServiceClient) SendToClientId(ctx context.Context, in *SendToClientIdRequest, opts ...grpc.CallOption) (*SendToClientIdResponse, error) {
55 | out := new(SendToClientIdResponse)
56 | err := c.cc.Invoke(ctx, GatewayService_SendToClientId_FullMethodName, in, out, opts...)
57 | if err != nil {
58 | return nil, err
59 | }
60 | return out, nil
61 | }
62 |
63 | func (c *gatewayServiceClient) SendToClientIds(ctx context.Context, in *SendToClientIdsRequest, opts ...grpc.CallOption) (*SendToClientIdsResponse, error) {
64 | out := new(SendToClientIdsResponse)
65 | err := c.cc.Invoke(ctx, GatewayService_SendToClientIds_FullMethodName, in, out, opts...)
66 | if err != nil {
67 | return nil, err
68 | }
69 | return out, nil
70 | }
71 |
72 | // GatewayServiceServer is the server API for GatewayService service.
73 | // All implementations must embed UnimplementedGatewayServiceServer
74 | // for forward compatibility
75 | type GatewayServiceServer interface {
76 | IsOnline(context.Context, *IsOnlineRequest) (*IsOnlineResponse, error)
77 | SendToClientId(context.Context, *SendToClientIdRequest) (*SendToClientIdResponse, error)
78 | SendToClientIds(context.Context, *SendToClientIdsRequest) (*SendToClientIdsResponse, error)
79 | mustEmbedUnimplementedGatewayServiceServer()
80 | }
81 |
82 | // UnimplementedGatewayServiceServer must be embedded to have forward compatible implementations.
83 | type UnimplementedGatewayServiceServer struct {
84 | }
85 |
86 | func (UnimplementedGatewayServiceServer) IsOnline(context.Context, *IsOnlineRequest) (*IsOnlineResponse, error) {
87 | return nil, status.Errorf(codes.Unimplemented, "method IsOnline not implemented")
88 | }
89 | func (UnimplementedGatewayServiceServer) SendToClientId(context.Context, *SendToClientIdRequest) (*SendToClientIdResponse, error) {
90 | return nil, status.Errorf(codes.Unimplemented, "method SendToClientId not implemented")
91 | }
92 | func (UnimplementedGatewayServiceServer) SendToClientIds(context.Context, *SendToClientIdsRequest) (*SendToClientIdsResponse, error) {
93 | return nil, status.Errorf(codes.Unimplemented, "method SendToClientIds not implemented")
94 | }
95 | func (UnimplementedGatewayServiceServer) mustEmbedUnimplementedGatewayServiceServer() {}
96 |
97 | // UnsafeGatewayServiceServer may be embedded to opt out of forward compatibility for this service.
98 | // Use of this interface is not recommended, as added methods to GatewayServiceServer will
99 | // result in compilation errors.
100 | type UnsafeGatewayServiceServer interface {
101 | mustEmbedUnimplementedGatewayServiceServer()
102 | }
103 |
104 | func RegisterGatewayServiceServer(s grpc.ServiceRegistrar, srv GatewayServiceServer) {
105 | s.RegisterService(&GatewayService_ServiceDesc, srv)
106 | }
107 |
108 | func _GatewayService_IsOnline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
109 | in := new(IsOnlineRequest)
110 | if err := dec(in); err != nil {
111 | return nil, err
112 | }
113 | if interceptor == nil {
114 | return srv.(GatewayServiceServer).IsOnline(ctx, in)
115 | }
116 | info := &grpc.UnaryServerInfo{
117 | Server: srv,
118 | FullMethod: GatewayService_IsOnline_FullMethodName,
119 | }
120 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
121 | return srv.(GatewayServiceServer).IsOnline(ctx, req.(*IsOnlineRequest))
122 | }
123 | return interceptor(ctx, in, info, handler)
124 | }
125 |
126 | func _GatewayService_SendToClientId_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
127 | in := new(SendToClientIdRequest)
128 | if err := dec(in); err != nil {
129 | return nil, err
130 | }
131 | if interceptor == nil {
132 | return srv.(GatewayServiceServer).SendToClientId(ctx, in)
133 | }
134 | info := &grpc.UnaryServerInfo{
135 | Server: srv,
136 | FullMethod: GatewayService_SendToClientId_FullMethodName,
137 | }
138 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
139 | return srv.(GatewayServiceServer).SendToClientId(ctx, req.(*SendToClientIdRequest))
140 | }
141 | return interceptor(ctx, in, info, handler)
142 | }
143 |
144 | func _GatewayService_SendToClientIds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
145 | in := new(SendToClientIdsRequest)
146 | if err := dec(in); err != nil {
147 | return nil, err
148 | }
149 | if interceptor == nil {
150 | return srv.(GatewayServiceServer).SendToClientIds(ctx, in)
151 | }
152 | info := &grpc.UnaryServerInfo{
153 | Server: srv,
154 | FullMethod: GatewayService_SendToClientIds_FullMethodName,
155 | }
156 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
157 | return srv.(GatewayServiceServer).SendToClientIds(ctx, req.(*SendToClientIdsRequest))
158 | }
159 | return interceptor(ctx, in, info, handler)
160 | }
161 |
162 | // GatewayService_ServiceDesc is the grpc.ServiceDesc for GatewayService service.
163 | // It's only intended for direct use with grpc.RegisterService,
164 | // and not to be introspected or modified (even as a copy)
165 | var GatewayService_ServiceDesc = grpc.ServiceDesc{
166 | ServiceName: "gateway.GatewayService",
167 | HandlerType: (*GatewayServiceServer)(nil),
168 | Methods: []grpc.MethodDesc{
169 | {
170 | MethodName: "IsOnline",
171 | Handler: _GatewayService_IsOnline_Handler,
172 | },
173 | {
174 | MethodName: "SendToClientId",
175 | Handler: _GatewayService_SendToClientId_Handler,
176 | },
177 | {
178 | MethodName: "SendToClientIds",
179 | Handler: _GatewayService_SendToClientIds_Handler,
180 | },
181 | },
182 | Streams: []grpc.StreamDesc{},
183 | Metadata: "grpcs/socket_cluster_gateway/proto/gateway/gateway.proto",
184 | }
185 |
--------------------------------------------------------------------------------
/grpcs/socket_cluster_gateway/socket_cluster_gateway.go:
--------------------------------------------------------------------------------
1 | package socket_cluster_gateway
2 |
3 | import (
4 | "context"
5 | "log"
6 |
7 | "github.com/urfave/cli/v2"
8 |
9 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/handler"
10 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
11 | "github.com/weblazy/socket-cluster/node"
12 |
13 | "github.com/weblazy/easy/grpc/grpc_server"
14 | "github.com/weblazy/easy/grpc/grpc_server/grpc_server_config"
15 | )
16 |
17 | var Cmd = &cli.Command{
18 | // Name: "socket_cluster_gateway",
19 | // Aliases: []string{},
20 | // Usage: "socket_cluster_gateway start",
21 | // Subcommands: []*cli.Command{
22 | // {
23 | // Name: "start",
24 | // Usage: "start service",
25 | // Action: Run,
26 | // },
27 | // },
28 | }
29 |
30 | func Run(c context.Context, n node.Node, grpcConf *grpc_server_config.Config) error {
31 | // defer closes.Close()
32 | s := grpc_server.NewGrpcServer(grpcConf)
33 | gatewayService := handler.NewGatewayService(n)
34 |
35 | gateway.RegisterGatewayServiceServer(s, gatewayService)
36 | err := s.Init()
37 | if err != nil {
38 | log.Fatal(err)
39 | }
40 | err = s.Start()
41 | if err != nil {
42 | log.Fatal(err)
43 | }
44 |
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/logx/fileLogger.go:
--------------------------------------------------------------------------------
1 | package logx
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "time"
8 | )
9 |
10 | const defaultDir = "log"
11 |
12 | type fileLogger struct {
13 | dateFormat string // 时间格式 默认:2006-01-05 15:04:05.000
14 | flow string // 操作流 用于标记日志信息 不设置无输出
15 | level logLevel // 输出level 不设置默认全部输出
16 | output *os.File
17 | expiredTime int // 清理时间 <0 不清理 单位:天 默认:7
18 | }
19 |
20 | // 判断文件目录否存在
21 | func IsDirExists(path string) bool {
22 | fi, err := os.Stat(path)
23 |
24 | if err != nil {
25 | return os.IsExist(err)
26 | } else {
27 | return fi.IsDir()
28 | }
29 | }
30 |
31 | // 创建文件
32 | func MkdirFile(path string) error {
33 | err := os.Mkdir(path, os.ModePerm) //在当前目录下生成md目录
34 | if err != nil {
35 | return err
36 | }
37 | return nil
38 | }
39 |
40 | func NewFileLogger() *fileLogger {
41 | if !IsDirExists(defaultDir) {
42 | if mkdirerr := MkdirFile(defaultDir); mkdirerr != nil {
43 | fmt.Println(mkdirerr)
44 | }
45 | }
46 |
47 | file, err := makeLogFile()
48 | if err != nil {
49 | file = os.Stdout
50 | fmt.Println("file logger create failed, redirect to stdlogger.")
51 | }
52 | return &fileLogger{
53 | dateFormat: defaultTimeTemp,
54 | level: DEBUG | INFO | WARNING | ERROR,
55 | expiredTime: 7,
56 | output: file,
57 | }
58 | }
59 | func (l *fileLogger) SetDateFormat(temp string) *fileLogger {
60 | l.dateFormat = temp
61 | return l
62 | }
63 |
64 | func (l *fileLogger) SetFlow(flow string) *fileLogger {
65 | l.flow = flow
66 | return l
67 | }
68 |
69 | func (l *fileLogger) SetShowLevel(level logLevel) *fileLogger {
70 | l.level = level
71 | return l
72 | }
73 |
74 | func (l *fileLogger) SetExpiredTimeTime(expiredTime int) *fileLogger {
75 | l.expiredTime = expiredTime
76 | return l
77 | }
78 |
79 | func (l *fileLogger) Debug(v ...interface{}) {
80 | if l.level&DEBUG != 0 {
81 | content := makeContent(l.dateFormat, l.flow, DEBUG, v...)
82 | l.print(content)
83 | }
84 | }
85 |
86 | func (l *fileLogger) Debugf(format string, v ...interface{}) {
87 | if l.level&DEBUG != 0 {
88 | msg := makeMsg(l.dateFormat, l.flow, DEBUG, format, v...)
89 | l.print(msg)
90 | }
91 | }
92 |
93 | func (l *fileLogger) Info(v ...interface{}) {
94 | if l.level&INFO != 0 {
95 | content := makeContent(l.dateFormat, l.flow, INFO, v...)
96 | l.print(content)
97 | }
98 | }
99 |
100 | func (l *fileLogger) Infof(format string, v ...interface{}) {
101 | if l.level&INFO != 0 {
102 | msg := makeMsg(l.dateFormat, l.flow, INFO, format, v...)
103 | l.print(msg)
104 | }
105 | }
106 |
107 | func (l *fileLogger) Warning(v ...interface{}) {
108 | if l.level&WARNING != 0 {
109 | content := makeContent(l.dateFormat, l.flow, WARNING, v...)
110 | l.print(content)
111 | }
112 | }
113 |
114 | func (l *fileLogger) Warningf(format string, v ...interface{}) {
115 | if l.level&WARNING != 0 {
116 | msg := makeMsg(l.dateFormat, l.flow, WARNING, format, v...)
117 | l.print(msg)
118 | }
119 | }
120 |
121 | func (l *fileLogger) Error(v ...interface{}) {
122 | if l.level&ERROR != 0 {
123 | content := makeContent(l.dateFormat, l.flow, ERROR, v...)
124 | l.print(content)
125 | }
126 | }
127 |
128 | func (l *fileLogger) Errorf(format string, v ...interface{}) {
129 | if l.level&ERROR != 0 {
130 | msg := makeMsg(l.dateFormat, l.flow, ERROR, format, v...)
131 | l.print(msg)
132 | }
133 | }
134 |
135 | func makeLogFile() (*os.File, error) {
136 | now := time.Now().Format(defaultDateTemp)
137 | fileName := defaultDir + "/" + now + ".log"
138 | file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
139 | if err != nil {
140 | return nil, fmt.Errorf("log file open failed, err = %v", err)
141 | }
142 | return file, nil
143 | }
144 |
145 | func (s *fileLogger) print(log string) {
146 | if s.output == os.Stdout {
147 | fmt.Fprintln(s.output, log)
148 | } else {
149 | s.checkout()
150 | fmt.Fprintln(s.output, log)
151 | }
152 | }
153 |
154 | func (s *fileLogger) checkout() {
155 | now := time.Now().Format(defaultDateTemp)
156 | filename := s.output.Name()
157 | if filename[:10] != now {
158 | s.changeOutput()
159 | s.removeExpiredLogs()
160 | }
161 | }
162 |
163 | func (s *fileLogger) changeOutput() {
164 | s.output.Close()
165 | file, err := makeLogFile()
166 | if err == nil {
167 | s.output = file
168 | } else {
169 | fmt.Println("file logger create failed, redirect to stdlogger.")
170 | s.output = os.Stdout
171 | }
172 | }
173 |
174 | func (s *fileLogger) removeExpiredLogs() {
175 | files, err := filepath.Glob(defaultDir + "/*.log")
176 | if err != nil {
177 | fmt.Printf("failed to remove expired log files, error: %s", err)
178 | return
179 | }
180 | delTime := time.Now().AddDate(0, 0, -s.expiredTime).Format(defaultDateTemp)
181 | for _, file := range files {
182 | if file[:10] > delTime {
183 | continue
184 | }
185 | if err := os.Remove(file); err != nil {
186 | fmt.Printf("failed to remove expired log file: %s", file)
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/logx/filelogger_test.go:
--------------------------------------------------------------------------------
1 | package logx_test
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestFileLog(t *testing.T) {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/logx/log.go:
--------------------------------------------------------------------------------
1 | package logx
2 |
3 | var LogHandler = NewFileLogger()
4 |
--------------------------------------------------------------------------------
/logx/log/2023-08-19.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/logx/log/2023-08-19.log
--------------------------------------------------------------------------------
/logx/logx.go:
--------------------------------------------------------------------------------
1 | package logx
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "path"
7 | "runtime"
8 | "time"
9 | )
10 |
11 | const (
12 | DEBUG logLevel = 1 << iota
13 | INFO
14 | WARNING
15 | ERROR
16 |
17 | defaultTimeTemp = "2006-01-02 15:04:05"
18 | defaultDateTemp = "2006-01-02"
19 | )
20 |
21 | type (
22 | logLevel uint16
23 | Logx interface {
24 | Debug(v ...interface{})
25 | Debugf(format string, v ...interface{})
26 | Info(v ...interface{})
27 | Infof(format string, v ...interface{})
28 | Warning(v ...interface{})
29 | Warningf(format string, v ...interface{})
30 | Error(v ...interface{})
31 | Errorf(format string, v ...interface{})
32 | }
33 |
34 | record struct {
35 | TimeStamp string `json:"timestamp"` // 日志生成时间
36 | Level string `json:"level"` // 日志等级
37 | Flow string `json:"flow,omitempty"` // 操作流
38 | Loc string `json:"location"` // 发生位置
39 | Content []interface{} `json:"content,omitempty"` // 日志内容
40 | Msg string `json:"msg,omitempty"` // 日志内容
41 | }
42 | )
43 |
44 | func makeContent(dateFormat, flow string, level logLevel, v ...interface{}) string {
45 | levelStr := levelToStr(level)
46 | funcName, fileName, lineNo := getCaseLineInfo(3)
47 | record := record{
48 | TimeStamp: time.Now().Format(dateFormat),
49 | Flow: flow,
50 | Loc: fmt.Sprintf("%s:%d:%s", fileName, lineNo, funcName),
51 | Level: levelStr,
52 | Content: v,
53 | }
54 | bytes, _ := json.Marshal(record)
55 | return string(bytes)
56 | }
57 |
58 | func makeMsg(dateFormat, flow string, level logLevel, format string, v ...interface{}) string {
59 | levelStr := levelToStr(level)
60 | funcName, fileName, lineNo := getCaseLineInfo(3)
61 | record := record{
62 | TimeStamp: time.Now().Format(dateFormat),
63 | Flow: flow,
64 | Loc: fmt.Sprintf("%s:%d:%s", fileName, lineNo, funcName),
65 | Level: levelStr,
66 | Msg: fmt.Sprintf(format, v...),
67 | }
68 | bytes, _ := json.Marshal(record)
69 | return string(bytes)
70 | }
71 |
72 | func getCaseLineInfo(skip int) (funcName, fileName string, lineNo int) {
73 | pc, file, lineNo, ok := runtime.Caller(skip)
74 | if !ok {
75 | fmt.Println("runtime.Caller() failed")
76 | return
77 | }
78 | funcName = path.Base(runtime.FuncForPC(pc).Name())
79 | fileName = path.Base(file)
80 | return
81 | }
82 |
83 | func levelToStr(level logLevel) (levelStr string) {
84 | switch level {
85 | case DEBUG:
86 | levelStr = "debug"
87 | case INFO:
88 | levelStr = "info"
89 | case WARNING:
90 | levelStr = "warning"
91 | case ERROR:
92 | levelStr = "error"
93 | }
94 | return levelStr
95 | }
96 |
--------------------------------------------------------------------------------
/logx/stdlogger.go:
--------------------------------------------------------------------------------
1 | package logx
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | )
7 |
8 | type stdLogger struct {
9 | dateFormat string // 时间格式 默认:2006-01-05 15:04:05.000
10 | flow string // 操作流 用于标记日志信息 不设置无输出
11 | level logLevel // 输出level 不设置默认全部输出
12 | }
13 |
14 | func NewStdLogger() *stdLogger {
15 | return &stdLogger{
16 | dateFormat: defaultTimeTemp,
17 | level: DEBUG | INFO | WARNING | ERROR,
18 | }
19 | }
20 |
21 | func (l *stdLogger) SetDateFormat(temp string) *stdLogger {
22 | l.dateFormat = temp
23 | return l
24 | }
25 |
26 | func (l *stdLogger) SetFlow(flow string) *stdLogger {
27 | l.flow = flow
28 | return l
29 | }
30 |
31 | func (l *stdLogger) SetShowLevel(level logLevel) *stdLogger {
32 | l.level = level
33 | return l
34 | }
35 |
36 |
37 | func (l *stdLogger) Debug(v ...interface{}) {
38 | if l.level & DEBUG != 0 {
39 | content := makeContent(l.dateFormat, l.flow, DEBUG, v...)
40 | fmt.Fprintln(os.Stdout, content)
41 | }
42 | }
43 |
44 | func (l *stdLogger) Debugf(format string, v ...interface{}) {
45 | if l.level & DEBUG != 0 {
46 | msg := makeMsg(l.dateFormat, l.flow, DEBUG, format, v...)
47 | fmt.Fprintln(os.Stdout, msg)
48 | }
49 | }
50 |
51 | func (l *stdLogger) Info(v ...interface{}) {
52 | if l.level & INFO != 0 {
53 | content := makeContent(l.dateFormat, l.flow, INFO, v...)
54 | fmt.Fprintln(os.Stdout, content)
55 | }
56 | }
57 |
58 | func (l *stdLogger) Infof(format string, v ...interface{}) {
59 | if l.level & INFO != 0 {
60 | msg := makeMsg(l.dateFormat, l.flow, INFO, format, v...)
61 | fmt.Fprintln(os.Stdout, msg)
62 |
63 | }
64 | }
65 |
66 | func (l *stdLogger) Warning(v ...interface{}) {
67 | if l.level & WARNING != 0 {
68 | content := makeContent(l.dateFormat, l.flow, WARNING, v...)
69 | fmt.Fprintln(os.Stdout, content)
70 | }
71 | }
72 |
73 | func (l *stdLogger) Warningf(format string, v ...interface{}) {
74 | if l.level & WARNING != 0 {
75 | msg := makeMsg(l.dateFormat, l.flow, WARNING, format, v...)
76 | fmt.Fprintln(os.Stdout, msg)
77 | }
78 | }
79 |
80 | func (l *stdLogger) Error(v ...interface{}) {
81 | if l.level & ERROR != 0 {
82 | content := makeContent(l.dateFormat, l.flow, ERROR, v...)
83 | fmt.Fprintln(os.Stdout, content)
84 | }
85 | }
86 |
87 | func (l *stdLogger) Errorf(format string, v ...interface{}) {
88 | if l.level & ERROR != 0 {
89 | msg := makeMsg(l.dateFormat, l.flow, ERROR, format, v...)
90 | fmt.Fprintln(os.Stdout, msg)
91 | }
92 | }
--------------------------------------------------------------------------------
/logx/stdlogger_test.go:
--------------------------------------------------------------------------------
1 | package logx_test
2 |
3 | import (
4 | "github.com/weblazy/socket-cluster/logx"
5 | "testing"
6 | )
7 |
8 | func TestStdLogger(t *testing.T) {
9 | logger := logx.NewStdLogger().SetFlow("std").SetShowLevel(logx.INFO | logx.ERROR)
10 | logger.Info("info")
11 | logger.Debug("debug")
12 | logger.Warning("warning")
13 | logger.Error("error")
14 |
15 | logger.Infof("infof")
16 | logger.Debugf("debugf")
17 | logger.Warningf("warningf")
18 | logger.Errorf("errorf")
19 | }
20 |
--------------------------------------------------------------------------------
/node/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/node/.DS_Store
--------------------------------------------------------------------------------
/node/common.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "strings"
5 |
6 | uuid "github.com/satori/go.uuid"
7 | )
8 |
9 | // GetUUID return a uuid
10 | func GetUUID() string {
11 | uuId := uuid.NewV4().String()
12 | uuIdStr := strings.Replace(uuId, "-", "", -1)
13 | return uuIdStr
14 | }
15 |
--------------------------------------------------------------------------------
/node/conf.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: conf.proto
3 |
4 | //包名,通过protoc生成时go文件时
5 |
6 | package node
7 |
8 | import (
9 | fmt "fmt"
10 | proto "github.com/golang/protobuf/proto"
11 | math "math"
12 | )
13 |
14 | // Reference imports to suppress errors if they are not otherwise used.
15 | var _ = proto.Marshal
16 | var _ = fmt.Errorf
17 | var _ = math.Inf
18 |
19 | // This is a compile-time assertion to ensure that this generated file
20 | // is compatible with the proto package it is being compiled against.
21 | // A compilation error at this line likely means your copy of the
22 | // proto package needs to be updated.
23 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
24 |
25 | type NodeInfo struct {
26 | ClientAddress string `protobuf:"bytes,1,opt,name=client_address,json=clientAddress,proto3" json:"client_address,omitempty"`
27 | ClientCount int64 `protobuf:"varint,2,opt,name=client_count,json=clientCount,proto3" json:"client_count,omitempty"`
28 | Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
29 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
30 | XXX_unrecognized []byte `json:"-"`
31 | XXX_sizecache int32 `json:"-"`
32 | }
33 |
34 | func (m *NodeInfo) Reset() { *m = NodeInfo{} }
35 | func (m *NodeInfo) String() string { return proto.CompactTextString(m) }
36 | func (*NodeInfo) ProtoMessage() {}
37 | func (*NodeInfo) Descriptor() ([]byte, []int) {
38 | return fileDescriptor_0b6ecbfc68e85c65, []int{0}
39 | }
40 |
41 | func (m *NodeInfo) XXX_Unmarshal(b []byte) error {
42 | return xxx_messageInfo_NodeInfo.Unmarshal(m, b)
43 | }
44 | func (m *NodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
45 | return xxx_messageInfo_NodeInfo.Marshal(b, m, deterministic)
46 | }
47 | func (m *NodeInfo) XXX_Merge(src proto.Message) {
48 | xxx_messageInfo_NodeInfo.Merge(m, src)
49 | }
50 | func (m *NodeInfo) XXX_Size() int {
51 | return xxx_messageInfo_NodeInfo.Size(m)
52 | }
53 | func (m *NodeInfo) XXX_DiscardUnknown() {
54 | xxx_messageInfo_NodeInfo.DiscardUnknown(m)
55 | }
56 |
57 | var xxx_messageInfo_NodeInfo proto.InternalMessageInfo
58 |
59 | func (m *NodeInfo) GetClientAddress() string {
60 | if m != nil {
61 | return m.ClientAddress
62 | }
63 | return ""
64 | }
65 |
66 | func (m *NodeInfo) GetClientCount() int64 {
67 | if m != nil {
68 | return m.ClientCount
69 | }
70 | return 0
71 | }
72 |
73 | func (m *NodeInfo) GetTimestamp() int64 {
74 | if m != nil {
75 | return m.Timestamp
76 | }
77 | return 0
78 | }
79 |
80 | func init() {
81 | proto.RegisterType((*NodeInfo)(nil), "proto.NodeInfo")
82 | }
83 |
84 | func init() { proto.RegisterFile("conf.proto", fileDescriptor_0b6ecbfc68e85c65) }
85 |
86 | var fileDescriptor_0b6ecbfc68e85c65 = []byte{
87 | // 130 bytes of a gzipped FileDescriptorProto
88 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0xce, 0xcf, 0x4b,
89 | 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0x45, 0x5c, 0x1c, 0x7e, 0xf9,
90 | 0x29, 0xa9, 0x9e, 0x79, 0x69, 0xf9, 0x42, 0xaa, 0x5c, 0x7c, 0xc9, 0x39, 0x99, 0xa9, 0x79, 0x25,
91 | 0xf1, 0x89, 0x29, 0x29, 0x45, 0xa9, 0xc5, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0xbc,
92 | 0x10, 0x51, 0x47, 0x88, 0xa0, 0x90, 0x22, 0x17, 0x0f, 0x54, 0x59, 0x72, 0x7e, 0x69, 0x5e, 0x89,
93 | 0x04, 0x93, 0x02, 0xa3, 0x06, 0x73, 0x10, 0x37, 0x44, 0xcc, 0x19, 0x24, 0x24, 0x24, 0xc3, 0xc5,
94 | 0x59, 0x92, 0x99, 0x9b, 0x5a, 0x5c, 0x92, 0x98, 0x5b, 0x20, 0xc1, 0x0c, 0x96, 0x47, 0x08, 0x24,
95 | 0xb1, 0x81, 0xad, 0x36, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe7, 0x42, 0x43, 0xa2, 0x8f, 0x00,
96 | 0x00, 0x00,
97 | }
98 |
--------------------------------------------------------------------------------
/node/conf.proto:
--------------------------------------------------------------------------------
1 | // protoc --go_out=. msg.proto
2 | syntax = "proto3";
3 |
4 | package node;
5 | // nodeInfo
6 | message NodeInfo {
7 | string client_address = 1;
8 | int64 client_count = 2;
9 | int64 timestamp = 3;
10 | }
--------------------------------------------------------------------------------
/node/const.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "time"
5 |
6 | proto "github.com/golang/protobuf/proto"
7 | )
8 |
9 | const (
10 | clientPrefix = "client#"
11 | DefaultPassword = "password"
12 | defaultClientPingInterval = 120
13 | DefaultNodePingInterval = 10
14 | defaultInternalPort = 9528
15 | defaultPort = 9527
16 | defaultAddr = "127.0.0.1:9528"
17 | NodeAddress = "node_address"
18 | authTime = 10 * time.Second
19 | )
20 |
21 | const (
22 | AuthNodeMsgType int32 = 1
23 | ClientMsgType int32 = 2
24 | PingMsgType int32 = 3
25 | AuthBusinessClientMsgType int32 = 4
26 | BindNodeIdMsgType int32 = 5
27 | )
28 |
29 | var PingMsg []byte
30 |
31 | func init() {
32 | msg := Msg{MsgType: PingMsgType}
33 | PingMsg, _ = proto.Marshal(&msg)
34 | }
35 |
--------------------------------------------------------------------------------
/node/msg.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: msg.proto
3 |
4 | package node
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 |
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the proto package it is being compiled against.
19 | // A compilation error at this line likely means your copy of the
20 | // proto package needs to be updated.
21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22 |
23 | type Msg struct {
24 | MsgType int32 `protobuf:"varint,1,opt,name=msg_type,json=msgType,proto3" json:"msg_type,omitempty"`
25 | Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
26 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
27 | XXX_unrecognized []byte `json:"-"`
28 | XXX_sizecache int32 `json:"-"`
29 | }
30 |
31 | func (m *Msg) Reset() { *m = Msg{} }
32 | func (m *Msg) String() string { return proto.CompactTextString(m) }
33 | func (*Msg) ProtoMessage() {}
34 | func (*Msg) Descriptor() ([]byte, []int) {
35 | return fileDescriptor_c06e4cca6c2cc899, []int{0}
36 | }
37 |
38 | func (m *Msg) XXX_Unmarshal(b []byte) error {
39 | return xxx_messageInfo_Msg.Unmarshal(m, b)
40 | }
41 | func (m *Msg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
42 | return xxx_messageInfo_Msg.Marshal(b, m, deterministic)
43 | }
44 | func (m *Msg) XXX_Merge(src proto.Message) {
45 | xxx_messageInfo_Msg.Merge(m, src)
46 | }
47 | func (m *Msg) XXX_Size() int {
48 | return xxx_messageInfo_Msg.Size(m)
49 | }
50 | func (m *Msg) XXX_DiscardUnknown() {
51 | xxx_messageInfo_Msg.DiscardUnknown(m)
52 | }
53 |
54 | var xxx_messageInfo_Msg proto.InternalMessageInfo
55 |
56 | func (m *Msg) GetMsgType() int32 {
57 | if m != nil {
58 | return m.MsgType
59 | }
60 | return 0
61 | }
62 |
63 | func (m *Msg) GetData() []byte {
64 | if m != nil {
65 | return m.Data
66 | }
67 | return nil
68 | }
69 |
70 | type ClientsMsg struct {
71 | ReceiveClientIds []string `protobuf:"bytes,1,rep,name=receive_client_ids,json=receiveClientIds,proto3" json:"receive_client_ids,omitempty"`
72 | Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
73 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
74 | XXX_unrecognized []byte `json:"-"`
75 | XXX_sizecache int32 `json:"-"`
76 | }
77 |
78 | func (m *ClientsMsg) Reset() { *m = ClientsMsg{} }
79 | func (m *ClientsMsg) String() string { return proto.CompactTextString(m) }
80 | func (*ClientsMsg) ProtoMessage() {}
81 | func (*ClientsMsg) Descriptor() ([]byte, []int) {
82 | return fileDescriptor_c06e4cca6c2cc899, []int{1}
83 | }
84 |
85 | func (m *ClientsMsg) XXX_Unmarshal(b []byte) error {
86 | return xxx_messageInfo_ClientsMsg.Unmarshal(m, b)
87 | }
88 | func (m *ClientsMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
89 | return xxx_messageInfo_ClientsMsg.Marshal(b, m, deterministic)
90 | }
91 | func (m *ClientsMsg) XXX_Merge(src proto.Message) {
92 | xxx_messageInfo_ClientsMsg.Merge(m, src)
93 | }
94 | func (m *ClientsMsg) XXX_Size() int {
95 | return xxx_messageInfo_ClientsMsg.Size(m)
96 | }
97 | func (m *ClientsMsg) XXX_DiscardUnknown() {
98 | xxx_messageInfo_ClientsMsg.DiscardUnknown(m)
99 | }
100 |
101 | var xxx_messageInfo_ClientsMsg proto.InternalMessageInfo
102 |
103 | func (m *ClientsMsg) GetReceiveClientIds() []string {
104 | if m != nil {
105 | return m.ReceiveClientIds
106 | }
107 | return nil
108 | }
109 |
110 | func (m *ClientsMsg) GetData() []byte {
111 | if m != nil {
112 | return m.Data
113 | }
114 | return nil
115 | }
116 |
117 | type AuthMsg struct {
118 | NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`
119 | Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
120 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
121 | XXX_unrecognized []byte `json:"-"`
122 | XXX_sizecache int32 `json:"-"`
123 | }
124 |
125 | func (m *AuthMsg) Reset() { *m = AuthMsg{} }
126 | func (m *AuthMsg) String() string { return proto.CompactTextString(m) }
127 | func (*AuthMsg) ProtoMessage() {}
128 | func (*AuthMsg) Descriptor() ([]byte, []int) {
129 | return fileDescriptor_c06e4cca6c2cc899, []int{2}
130 | }
131 |
132 | func (m *AuthMsg) XXX_Unmarshal(b []byte) error {
133 | return xxx_messageInfo_AuthMsg.Unmarshal(m, b)
134 | }
135 | func (m *AuthMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
136 | return xxx_messageInfo_AuthMsg.Marshal(b, m, deterministic)
137 | }
138 | func (m *AuthMsg) XXX_Merge(src proto.Message) {
139 | xxx_messageInfo_AuthMsg.Merge(m, src)
140 | }
141 | func (m *AuthMsg) XXX_Size() int {
142 | return xxx_messageInfo_AuthMsg.Size(m)
143 | }
144 | func (m *AuthMsg) XXX_DiscardUnknown() {
145 | xxx_messageInfo_AuthMsg.DiscardUnknown(m)
146 | }
147 |
148 | var xxx_messageInfo_AuthMsg proto.InternalMessageInfo
149 |
150 | func (m *AuthMsg) GetNodeId() string {
151 | if m != nil {
152 | return m.NodeId
153 | }
154 | return ""
155 | }
156 |
157 | func (m *AuthMsg) GetPassword() string {
158 | if m != nil {
159 | return m.Password
160 | }
161 | return ""
162 | }
163 |
164 | type BindNodeIdMsg struct {
165 | NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`
166 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
167 | XXX_unrecognized []byte `json:"-"`
168 | XXX_sizecache int32 `json:"-"`
169 | }
170 |
171 | func (m *BindNodeIdMsg) Reset() { *m = BindNodeIdMsg{} }
172 | func (m *BindNodeIdMsg) String() string { return proto.CompactTextString(m) }
173 | func (*BindNodeIdMsg) ProtoMessage() {}
174 | func (*BindNodeIdMsg) Descriptor() ([]byte, []int) {
175 | return fileDescriptor_c06e4cca6c2cc899, []int{3}
176 | }
177 |
178 | func (m *BindNodeIdMsg) XXX_Unmarshal(b []byte) error {
179 | return xxx_messageInfo_BindNodeIdMsg.Unmarshal(m, b)
180 | }
181 | func (m *BindNodeIdMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
182 | return xxx_messageInfo_BindNodeIdMsg.Marshal(b, m, deterministic)
183 | }
184 | func (m *BindNodeIdMsg) XXX_Merge(src proto.Message) {
185 | xxx_messageInfo_BindNodeIdMsg.Merge(m, src)
186 | }
187 | func (m *BindNodeIdMsg) XXX_Size() int {
188 | return xxx_messageInfo_BindNodeIdMsg.Size(m)
189 | }
190 | func (m *BindNodeIdMsg) XXX_DiscardUnknown() {
191 | xxx_messageInfo_BindNodeIdMsg.DiscardUnknown(m)
192 | }
193 |
194 | var xxx_messageInfo_BindNodeIdMsg proto.InternalMessageInfo
195 |
196 | func (m *BindNodeIdMsg) GetNodeId() string {
197 | if m != nil {
198 | return m.NodeId
199 | }
200 | return ""
201 | }
202 |
203 | func init() {
204 | proto.RegisterType((*Msg)(nil), "node.Msg")
205 | proto.RegisterType((*ClientsMsg)(nil), "node.ClientsMsg")
206 | proto.RegisterType((*AuthMsg)(nil), "node.AuthMsg")
207 | proto.RegisterType((*BindNodeIdMsg)(nil), "node.BindNodeIdMsg")
208 | }
209 |
210 | func init() { proto.RegisterFile("msg.proto", fileDescriptor_c06e4cca6c2cc899) }
211 |
212 | var fileDescriptor_c06e4cca6c2cc899 = []byte{
213 | // 202 bytes of a gzipped FileDescriptorProto
214 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x8f, 0xb1, 0x4b, 0xc6, 0x30,
215 | 0x10, 0xc5, 0x89, 0xad, 0x6d, 0x73, 0x28, 0x48, 0x16, 0xab, 0x53, 0xe9, 0x94, 0x41, 0x5c, 0x74,
216 | 0x16, 0xd4, 0xa9, 0x83, 0x1d, 0x82, 0x7b, 0xa8, 0xbd, 0x10, 0x03, 0xb6, 0x09, 0xbd, 0xa8, 0xf4,
217 | 0xbf, 0x97, 0x44, 0x71, 0xfa, 0xf8, 0xb6, 0x7b, 0x77, 0xef, 0xfd, 0x78, 0x07, 0x7c, 0x21, 0x7b,
218 | 0x1b, 0x36, 0x1f, 0xbd, 0x28, 0x57, 0x8f, 0xa6, 0xbf, 0x87, 0xe2, 0x85, 0xac, 0xb8, 0x82, 0x66,
219 | 0x21, 0xab, 0xe3, 0x1e, 0x4c, 0xcb, 0x3a, 0x26, 0x4f, 0x55, 0xbd, 0x90, 0x7d, 0xdd, 0x83, 0x11,
220 | 0x02, 0x4a, 0x9c, 0xe2, 0xd4, 0x9e, 0x74, 0x4c, 0x9e, 0xa9, 0x3c, 0xf7, 0x23, 0xc0, 0xf3, 0x87,
221 | 0x33, 0x6b, 0xa4, 0x14, 0xbe, 0x01, 0xb1, 0x99, 0xd9, 0xb8, 0x2f, 0xa3, 0xe7, 0xbc, 0xd5, 0x0e,
222 | 0xa9, 0x65, 0x5d, 0x21, 0xb9, 0xba, 0xf8, 0xbb, 0xfc, 0xda, 0x07, 0xa4, 0x83, 0xbc, 0x07, 0xa8,
223 | 0x1f, 0x3f, 0xe3, 0x7b, 0x82, 0x5d, 0x42, 0x9d, 0x8a, 0x69, 0x87, 0xb9, 0x08, 0x57, 0x55, 0x92,
224 | 0x03, 0x8a, 0x6b, 0x68, 0xc2, 0x44, 0xf4, 0xed, 0x37, 0xcc, 0x59, 0xae, 0xfe, 0x75, 0x2f, 0xe1,
225 | 0xfc, 0xc9, 0xad, 0x38, 0x66, 0xe7, 0x31, 0xca, 0x5b, 0x95, 0x9f, 0xbf, 0xfb, 0x09, 0x00, 0x00,
226 | 0xff, 0xff, 0x5a, 0x43, 0x07, 0x78, 0x09, 0x01, 0x00, 0x00,
227 | }
228 |
--------------------------------------------------------------------------------
/node/msg.proto:
--------------------------------------------------------------------------------
1 | // protoc --go_out=. msg.proto
2 | syntax = "proto3";
3 |
4 | package node;
5 |
6 | message Msg {
7 | int32 msg_type = 1;
8 | bytes data = 2;
9 | }
10 |
11 | message ClientsMsg {
12 | repeated string receive_client_ids = 1;
13 | bytes data = 2;
14 | }
15 |
16 | message AuthMsg {
17 | string node_id = 1;
18 | string password = 2;
19 | }
20 |
21 | message BindNodeIdMsg {
22 | string node_id = 1;
23 | }
--------------------------------------------------------------------------------
/node/node.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "time"
8 |
9 | "github.com/weblazy/core/mapreduce"
10 | "github.com/weblazy/easy/econfig"
11 | "github.com/weblazy/easy/elog"
12 | "github.com/weblazy/easy/syncx"
13 | "github.com/weblazy/goutil"
14 | "github.com/weblazy/socket-cluster/logx"
15 | "github.com/weblazy/socket-cluster/protocol"
16 | "go.uber.org/zap"
17 | )
18 |
19 | type (
20 | Node interface {
21 | // SetClientIdOnline binds the client ID to the connection
22 | SetClientIdOnline(conn protocol.Connection, clientId string) error
23 | // OnClientPing updates the client heartbeat status
24 | OnClientPing(clientId string) error
25 | // IsOnline gets the online status of the clientId
26 | IsOnline(clientId string) bool
27 | // SendToClientId sends a message to a clientId
28 | SendToClientId(clientId string, req []byte) error
29 | // SendToClientId sends a message to multiple clientIds
30 | SendToClientIds(clientIds []string, req []byte) error
31 | }
32 |
33 | // Node communication node
34 | node struct {
35 | Node
36 | nodeConf *NodeConf
37 | // clientIdSessions map[key1]map[key2]value
38 | // Key1: clientId
39 | // Key2: socket address
40 | // Value: *Session
41 | clientIdSessions *syncx.ConcurrentDoubleMap
42 | // Key: socket address
43 | // Value: *Session
44 | clientConns goutil.Map
45 | // timer *timingwheel.TimingWheel // Timingwheel Close connects that timeout without authentication
46 | startTime time.Time // Start time
47 | // Key: socket address
48 | // Value: *Session
49 | businessClients goutil.Map // Send the message to client
50 | // Key: nodeId
51 | // Value: *Session
52 | // transClients goutil.Map // Forward the message to another node
53 | // Key: socket address
54 | // Value: protocol.Connection
55 | // transServices goutil.Map // Receive messages forwarded by other nodes
56 | nodeTimeout int64 // Node heartbeat timeout time
57 | clientTimeout int64 // Client heartbeat timeout time
58 | }
59 | )
60 |
61 | // NewNode return a Node
62 | func NewNode(cfg *NodeConf) (Node, error) {
63 | // timer, err := timingwheel.NewTimingWheel(time.Second, 30, func(k, v interface{}) {
64 | // // TODO Count the number of unauthenticated connections
65 | // logx.LogHandler.Infof("%s auth timeout", k)
66 | // if v.(protocol.Connection) != nil {
67 | // err := v.(protocol.Connection).Close()
68 | // if err != nil {
69 | // logx.LogHandler.Error(err)
70 | // }
71 | // }
72 | // })
73 |
74 | // if err != nil {
75 | // return nil, err
76 | // }
77 | nodeObj := &node{
78 | nodeConf: cfg,
79 | clientIdSessions: syncx.NewConcurrentDoubleMap(32),
80 | startTime: time.Now(),
81 | // timer: timer,
82 | clientConns: goutil.AtomicMap(),
83 | businessClients: goutil.AtomicMap(),
84 | nodeTimeout: cfg.nodePingInterval * 3, // The timeout is three times as long as the interval between heartbeats
85 | clientTimeout: cfg.clientPingInterval * 3, // The timeout is three times as long as the interval between heartbeats
86 | }
87 | nodeObj.nodeConf.discoveryHandler.SetNodeAddr(cfg.addr)
88 | // cfg.internalProtocolHandler.ListenAndServe(cfg.internalPort, nodeObj.onTransConnect)
89 |
90 | cfg.protocolHandler.ListenAndServe(cfg.port, nodeObj.onClientConnect)
91 | nodeObj.sendPing()
92 | nodeObj.register()
93 | return nodeObj, nil
94 | }
95 |
96 | // SetOnline sets the clientId to online
97 | func (this *node) SetClientIdOnline(conn protocol.Connection, clientId string) error {
98 | addr := conn.Addr()
99 | // if conn.ConnProtocol() != ws_protocol.WsConnProtocol {
100 | // this.timer.RemoveTimer(addr) // Cancel timeingwheel task
101 | // }
102 |
103 | session := &Session{Conn: conn, ClientId: clientId}
104 | this.clientConns.Store(addr, session)
105 | this.clientIdSessions.StoreWithPlugin(clientId, addr, session, func() {
106 | oldClientId := session.CasClientId(clientId)
107 | if oldClientId != "" && oldClientId != clientId {
108 | this.clientIdSessions.DeleteWithoutLock(oldClientId, addr)
109 | }
110 | })
111 | return this.nodeConf.sessionStorageHandler.BindClientId(this.nodeConf.addr, clientId)
112 | }
113 |
114 | // OnClientPing updates the client heartbeat status
115 | func (this *node) OnClientPing(clientId string) error {
116 | return this.nodeConf.sessionStorageHandler.OnClientPing(this.nodeConf.addr, clientId)
117 | }
118 |
119 | // IsOnline determine if a clientId is online
120 | func (this *node) IsOnline(clientId string) bool {
121 | addrArr, err := this.nodeConf.sessionStorageHandler.GetIps(clientId)
122 | if err != nil {
123 | logx.LogHandler.Error(err)
124 | return false
125 | }
126 | if len(addrArr) > 0 {
127 | return true
128 | }
129 | return false
130 | }
131 |
132 | // SendToClientId Send message to a clientId
133 | func (this *node) SendToClientId(clientId string, req []byte) error {
134 | if req == nil {
135 | return fmt.Errorf("message is nil")
136 | }
137 | ipArr, err := this.nodeConf.sessionStorageHandler.GetIps(clientId)
138 | if econfig.GlobalViper.GetBool("BaseConfig.Debug") {
139 | elog.InfoCtx(context.Background(), "SendToClientId", zap.String("clientId", clientId), zap.String("req", string(req)), zap.Any("ipArr", ipArr))
140 | }
141 | if err == nil {
142 | mapreduce.MapVoid(func(source chan<- interface{}) {
143 | for key := range ipArr {
144 | source <- ipArr[key]
145 | }
146 | }, func(item interface{}) {
147 | // ip := item.(string)
148 | // if ip == this.nodeConf.nodeId {
149 | this.clientIdSessions.RangeNextMap(clientId, func(k1, k2 string, se interface{}) bool {
150 | err = se.(*Session).Conn.WriteMsg(req)
151 | if err != nil {
152 | elog.InfoCtx(context.Background(), "SendToClientId", zap.String("clientId", clientId), zap.String("req", string(req)), zap.Error(err))
153 | }
154 | return true
155 | })
156 | // } else {
157 | // connect, ok := this.transClients.Load(ip)
158 | // if ok {
159 | // conn, ok := connect.(protocol.Connection)
160 | // if !ok {
161 | // return
162 | // }
163 | // clientsMsg := ClientsMsg{
164 | // ReceiveClientIds: []string{clientId},
165 | // Data: req,
166 | // }
167 | // clientsMsgBytes, err := proto.Marshal(&clientsMsg)
168 | // if err != nil {
169 | // logx.LogHandler.Error(err)
170 | // }
171 | // transReq := Msg{
172 | // MsgType: ClientMsgType,
173 | // Data: clientsMsgBytes,
174 | // }
175 | // reqBytes, err := proto.Marshal(&transReq)
176 | // err = conn.WriteMsg(reqBytes)
177 | // if err != nil {
178 | // logx.LogHandler.Error(err)
179 | // }
180 | // } else {
181 | // logx.LogHandler.Errorf("node:%s not online", ip)
182 | // return
183 | // }
184 | // }
185 | })
186 | }
187 | return nil
188 | }
189 |
190 | type BatchData struct {
191 | ip string
192 | clientIds []string
193 | }
194 |
195 | // SendToClientIds Sending messages to multiple clients
196 | func (this *node) SendToClientIds(clientIds []string, req []byte) error {
197 | if req == nil {
198 | return fmt.Errorf("message is nil")
199 | }
200 | clientMap, err := this.nodeConf.sessionStorageHandler.GetClientsIps(clientIds)
201 | if err != nil {
202 | return err
203 | }
204 | localClientIds, _ := clientMap[this.nodeConf.addr]
205 | delete(clientMap, this.nodeConf.addr)
206 | // Concurrent sends to other nodes
207 | // mapreduce.MapVoid(func(source chan<- interface{}) {
208 | // for k1 := range clientMap {
209 |
210 | // source <- &BatchData{ip: k1, clientIds: clientMap[k1]}
211 | // }
212 | // }, func(item interface{}) {
213 | // batchData := item.(*BatchData)
214 | // connect, ok := this.transClients.Load(batchData.ip)
215 | // if ok {
216 | // conn, ok := connect.(protocol.Connection)
217 | // if !ok {
218 | // return
219 | // }
220 | // ids := make([]string, 0)
221 | // for k1 := range batchData.clientIds {
222 | // ids = append(ids, batchData.clientIds[k1])
223 | // }
224 | // clientsMsg := ClientsMsg{
225 | // ReceiveClientIds: ids,
226 | // Data: req,
227 | // }
228 | // clientsMsgBytes, err := proto.Marshal(&clientsMsg)
229 | // if err != nil {
230 | // logx.LogHandler.Error(err)
231 | // }
232 | // msg := Msg{
233 | // MsgType: ClientMsgType,
234 | // Data: clientsMsgBytes,
235 | // }
236 | // msgBytes, err := proto.Marshal(&msg)
237 | // if err != nil {
238 | // logx.LogHandler.Error(err)
239 | // }
240 | // err = conn.WriteMsg(msgBytes)
241 | // if err != nil {
242 | // logx.LogHandler.Error(err)
243 | // }
244 | // } else {
245 | // logx.LogHandler.Error("node:%s not online", batchData.ip)
246 | // return
247 | // }
248 |
249 | // })
250 | // Concurrent sends to clients
251 | mapreduce.MapVoid(func(source chan<- interface{}) {
252 | for k1 := range localClientIds {
253 | this.clientIdSessions.RangeNextMap(localClientIds[k1], func(key1 string, key2 string, value interface{}) bool {
254 | source <- value
255 | return true
256 | })
257 | }
258 | }, func(item interface{}) {
259 | se := item.(*Session)
260 | err := se.Conn.WriteMsg(req)
261 | if err != nil {
262 | logx.LogHandler.Error(err)
263 | }
264 | })
265 | return nil
266 | }
267 |
268 | func (this *node) onClientConnect(connect protocol.Connection) {
269 | defer func() {
270 | this.onClientClose(connect)
271 | if connect != nil {
272 | connect.Close()
273 | }
274 | }()
275 | this.nodeConf.plugin.OnConnect(connect)
276 | // if connect.ConnProtocol() != ws_protocol.WsConnProtocol {
277 | // // The timeout unbound clientId connect will be closed
278 | // this.timer.SetTimer(connect.Addr(), connect, authTime)
279 | // }
280 | for {
281 | msg, err := connect.ReadMsg()
282 | if err != nil {
283 | logx.LogHandler.Error(err)
284 | break
285 | }
286 | this.onClientMsg(connect, msg)
287 | }
288 | }
289 |
290 | // OnClientMsg deal client message
291 | func (this *node) onClientMsg(conn protocol.Connection, msg []byte) {
292 | addr := conn.Addr()
293 | session, ok := this.clientConns.Load(addr)
294 | clientId := ""
295 | if ok {
296 | clientId = session.(*Session).ClientId
297 | }
298 | this.nodeConf.onMsg(&Context{Conn: conn, Msg: msg, ClientId: clientId})
299 | }
300 |
301 | func (this *node) onClientClose(connect protocol.Connection) {
302 | this.nodeConf.plugin.OnClose(connect)
303 | addr := connect.Addr()
304 | v1, ok := this.clientConns.Load(addr)
305 | if ok {
306 | clientId := v1.(*Session).ClientId
307 | if clientId != "" {
308 | this.clientIdSessions.Delete(clientId, addr)
309 | }
310 | }
311 | this.clientConns.Delete(addr)
312 | }
313 |
314 | // func (this *node) onTransConnect(connect protocol.Connection) {
315 | // defer func() {
316 | // if connect != nil {
317 | // connect.Close()
318 | // }
319 | // }()
320 | // this.timer.SetTimer(connect.Addr(), connect, authTime)
321 | // for {
322 | // msg, err := connect.ReadMsg()
323 | // if err != nil {
324 | // logx.LogHandler.Error(err)
325 | // break
326 | // }
327 | // this.onTransClientMsg(connect, msg)
328 | // }
329 | // }
330 |
331 | // OnTransMsg handle internal communication node messages
332 | // func (this *node) onTransServerMsg(conn protocol.Connection, msg []byte) {
333 | // var transMsg Msg
334 | // err := proto.Unmarshal(msg, &transMsg)
335 | // if err != nil {
336 | // logx.LogHandler.Error(err, string(msg))
337 | // return
338 | // }
339 | // switch transMsg.MsgType {
340 |
341 | // case ClientMsgType: // Message forwarded to the client
342 | // var clientsMsg ClientsMsg
343 | // err = proto.Unmarshal(transMsg.Data, &clientsMsg)
344 | // if err != nil {
345 | // logx.LogHandler.Error(err)
346 | // }
347 | // for k1 := range clientsMsg.ReceiveClientIds {
348 | // receiveClientId := clientsMsg.ReceiveClientIds[k1]
349 | // this.clientIdSessions.RangeNextMap(receiveClientId, func(k1, k2 string, se interface{}) bool {
350 | // err = se.(*Session).Conn.WriteMsg(clientsMsg.Data)
351 | // if err != nil {
352 | // logx.LogHandler.Error(err)
353 | // }
354 | // return true
355 | // })
356 | // }
357 |
358 | // case PingMsgType: // The heartbeat message
359 |
360 | // default: // The unknow message
361 | // logx.LogHandler.Info(transMsg)
362 | // }
363 |
364 | // }
365 |
366 | // OnTransMsg handle internal communication node messages
367 | // func (this *node) onTransClientMsg(conn protocol.Connection, msg []byte) {
368 | // var transMsg Msg
369 | // err := proto.Unmarshal(msg, &transMsg)
370 | // if err != nil {
371 | // logx.LogHandler.Error(err, string(msg))
372 | // return
373 | // }
374 | // switch transMsg.MsgType {
375 | // case AuthNodeMsgType: // Authentication message
376 | // var authMsg AuthMsg
377 | // err = proto.Unmarshal(transMsg.Data, &authMsg)
378 | // if err != nil {
379 | // logx.LogHandler.Error(err)
380 | // }
381 | // err = this.authTrans(conn, &authMsg)
382 | // if err != nil {
383 | // logx.LogHandler.Error(err)
384 | // }
385 |
386 | // case PingMsgType: // The heartbeat message
387 | // case AuthBusinessClientMsgType: // Authentication message
388 | // var authMsg AuthMsg
389 | // err = proto.Unmarshal(transMsg.Data, &authMsg)
390 | // if err != nil {
391 | // logx.LogHandler.Error(err)
392 | // }
393 | // err = this.authBusinessClient(conn, &authMsg)
394 | // if err != nil {
395 | // logx.LogHandler.Error(err)
396 | // return
397 | // }
398 | // bindNodeIdMsg := BindNodeIdMsg{NodeId: this.nodeConf.nodeId}
399 |
400 | // bindNodeIdMsgBytes, err := proto.Marshal(&bindNodeIdMsg)
401 | // if err != nil {
402 | // logx.LogHandler.Error(err)
403 | // }
404 | // bindNodeIdReq := Msg{
405 | // MsgType: BindNodeIdMsgType,
406 | // Data: bindNodeIdMsgBytes,
407 | // }
408 | // reqBytes, err := proto.Marshal(&bindNodeIdReq)
409 |
410 | // err = conn.WriteMsg(reqBytes)
411 | // if err != nil {
412 | // logx.LogHandler.Error(err)
413 | // }
414 | // case ClientMsgType: // Message forwarded to the client
415 | // addr := conn.Addr()
416 | // _, ok := this.businessClients.Load(addr)
417 | // if !ok {
418 | // logx.LogHandler.Error(errors.New("un auth connect"))
419 | // return
420 | // }
421 | // var clientsMsg ClientsMsg
422 | // err = proto.Unmarshal(transMsg.Data, &clientsMsg)
423 | // if err != nil {
424 | // logx.LogHandler.Error(err)
425 | // }
426 | // for k1 := range clientsMsg.ReceiveClientIds {
427 | // receiveClientId := clientsMsg.ReceiveClientIds[k1]
428 | // this.clientIdSessions.RangeNextMap(receiveClientId, func(k1, k2 string, se interface{}) bool {
429 | // err = se.(*Session).Conn.WriteMsg(clientsMsg.Data)
430 | // if err != nil {
431 | // logx.LogHandler.Error(err)
432 | // }
433 | // return true
434 | // })
435 | // }
436 |
437 | // default: // The unknow message
438 | // logx.LogHandler.Info(transMsg)
439 | // }
440 |
441 | // }
442 |
443 | // AuthTrans Auth the node
444 | // func (this *node) authTrans(conn protocol.Connection, authMsg *AuthMsg) error {
445 | // nodeId := authMsg.NodeId
446 | // this.timer.RemoveTimer(conn.Addr()) // Cancel timeingwheel task
447 | // if authMsg.Password != this.nodeConf.password {
448 | // logx.LogHandler.Infof("Connect:%s,Wrong password:%s", nodeId, authMsg.Password)
449 | // conn.Close()
450 | // return fmt.Errorf("auth faild")
451 | // }
452 | // this.transClients.Store(nodeId, conn)
453 | // return nil
454 | // }
455 |
456 | // AuthTrans Auth the node
457 | // func (this *node) authBusinessClient(conn protocol.Connection, authMsg *AuthMsg) error {
458 | // addr := conn.Addr()
459 | // this.timer.RemoveTimer(addr) // Cancel timeingwheel task
460 | // if authMsg.Password != this.nodeConf.password {
461 | // logx.LogHandler.Infof("Connect:%s,Wrong password:%s", addr, authMsg.Password)
462 | // conn.Close()
463 | // return fmt.Errorf("auth faild")
464 | // }
465 | // this.businessClients.Store(addr, conn)
466 | // return nil
467 | // }
468 |
469 | // SendPing send node Heartbeat
470 | func (this *node) sendPing() {
471 | go func() {
472 | for {
473 | time.Sleep(time.Duration(this.nodeConf.nodePingInterval) * time.Second)
474 | // update node info
475 | nodeInfo := map[string]interface{}{
476 | "node_addr": this.nodeConf.addr,
477 | "client_count": this.clientConns.Len(),
478 | "timestamp": time.Now().Unix(),
479 | }
480 | nodeInfoByte, err := json.Marshal(nodeInfo)
481 | if err != nil {
482 | logx.LogHandler.Error(err)
483 | }
484 | err = this.nodeConf.discoveryHandler.UpdateInfo(nodeInfoByte)
485 | if err != nil {
486 | logx.LogHandler.Error(err)
487 | }
488 | // err = this.updateNodeList()
489 | if err != nil {
490 | logx.LogHandler.Error(err)
491 | }
492 | // send to other node
493 | // this.transServices.Range(func(k, v interface{}) bool {
494 | // conn, ok := v.(protocol.Connection)
495 | // if !ok {
496 | // return true
497 | // }
498 |
499 | // err := conn.WriteMsg(PingMsg)
500 | // if err != nil {
501 | // logx.LogHandler.Error(err)
502 | // }
503 | // return true
504 | // })
505 | }
506 | }()
507 | }
508 |
509 | // Register
510 | func (this *node) register() {
511 | this.nodeConf.discoveryHandler.Register()
512 | // watchChan := make(chan discovery.EventType, 1)
513 | // go this.nodeConf.discoveryHandler.WatchService(watchChan)
514 | // go func() {
515 | // for {
516 | // select {
517 | // case _, ok := <-watchChan:
518 | // if !ok {
519 | // logx.LogHandler.Infof("channel close\n")
520 | // return
521 | // }
522 | // err := this.updateNodeList()
523 | // if err != nil {
524 | // logx.LogHandler.Error(err)
525 | // }
526 | // }
527 | // }
528 | // }()
529 | // init
530 | // err := this.updateNodeList()
531 | // if err != nil {
532 | // logx.LogHandler.Error(err)
533 | // }
534 | }
535 |
536 | // UpdateNodeList Add handles addition request
537 | // func (this *node) updateNodeList() error {
538 | // nodeMap := make(map[string]int)
539 | // for k1 := range this.nodeConf.hostList {
540 | // nodeList, port, err := dns.DnsParse(this.nodeConf.hostList[k1])
541 | // if err != nil {
542 | // logx.LogHandler.Error(err)
543 | // return err
544 | // }
545 | // for k2 := range nodeList {
546 | // nodeMap[nodeList[k2]+":"+port] = 1
547 | // }
548 | // }
549 |
550 | // for k1 := range nodeMap {
551 | // addr := k1
552 | // if addr == this.nodeConf.nodeId {
553 | // continue
554 | // }
555 |
556 | // // Connection already exists
557 | // _, ok := this.transServices.LoadOrStore(addr, "")
558 | // if ok {
559 | // continue
560 | // }
561 | // conn, err := this.nodeConf.internalProtocolHandler.Dial(addr)
562 | // if err != nil {
563 | // logx.LogHandler.Errorf("dial:%s", err.Error())
564 | // this.transServices.Delete(addr)
565 | // continue
566 | // }
567 |
568 | // auth := AuthMsg{
569 | // Password: this.nodeConf.password,
570 | // NodeId: this.nodeConf.nodeId,
571 | // }
572 | // authBytes, err := proto.Marshal(&auth)
573 | // if err != nil {
574 | // logx.LogHandler.Error(err)
575 | // }
576 | // data := Msg{
577 | // MsgType: AuthNodeMsgType,
578 | // Data: authBytes,
579 | // }
580 | // reqBytes, err := proto.Marshal(&data)
581 | // if err != nil {
582 | // logx.LogHandler.Error(err)
583 | // }
584 | // err = conn.WriteMsg(reqBytes)
585 | // if err != nil {
586 | // logx.LogHandler.Info(err)
587 | // }
588 | // this.transServices.Store(addr, conn)
589 | // go func(addr string, conn protocol.Connection) {
590 | // defer func(addr string, conn protocol.Connection) {
591 | // this.transServices.Delete(addr)
592 | // if conn != nil {
593 | // conn.Close()
594 | // }
595 | // }(addr, conn)
596 | // for {
597 | // msg, err := conn.ReadMsg()
598 | // if err != nil {
599 | // break
600 | // }
601 | // this.onTransServerMsg(conn, msg)
602 | // }
603 | // }(addr, conn)
604 | // }
605 | // return nil
606 | // }
607 |
--------------------------------------------------------------------------------
/node/node_conf.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "github.com/go-redis/redis/v8"
5 | "github.com/weblazy/socket-cluster/discovery"
6 | "github.com/weblazy/socket-cluster/protocol"
7 | "github.com/weblazy/socket-cluster/session_storage"
8 | )
9 |
10 | type (
11 | // RedisNode the hash redis node config
12 | RedisNode struct {
13 | RedisConf *redis.Options
14 | Position uint32 // The position of hash
15 | }
16 | // NodeConf node config
17 | NodeConf struct {
18 | addr string // The ip or domain of the node
19 | port int64 // Node clientPort
20 | internalPort int64 // Node internalPort
21 | password string // Password for auth when connect to other node
22 | clientPingInterval int64 // Node with client heartbeat interval
23 | nodePingInterval int64 // Node with node Heartbeat interval
24 | onMsg func(context *Context) // Callback function when receive client message
25 | discoveryHandler discovery.ServiceDiscovery // Discover service
26 | protocolHandler protocol.Protocol // Direct protocol between node and client
27 | // internalProtocolHandler protocol.Protocol // Direct protocol between node and node
28 | sessionStorageHandler session_storage.SessionStorage // On-line state storage components
29 | plugin Plugin // The interface that the client connects to or closes
30 | }
31 | // Params of onMsg
32 | Context struct {
33 | Conn protocol.Connection
34 | ClientId string
35 | Msg []byte
36 | }
37 | )
38 |
39 | // NewNodeConf creates a new NodeConf.
40 | func NewNodeConf(addr string, protocolHandler protocol.Protocol, sessionStorageHandler session_storage.SessionStorage, discoveryHandler discovery.ServiceDiscovery, onMsg func(context *Context)) *NodeConf {
41 | return &NodeConf{
42 | addr: defaultAddr,
43 | port: defaultPort,
44 | password: DefaultPassword,
45 | clientPingInterval: defaultClientPingInterval,
46 | nodePingInterval: DefaultNodePingInterval,
47 | protocolHandler: protocolHandler,
48 | // internalProtocolHandler: &tcp_protocol.TcpProtocol{},
49 | internalPort: defaultInternalPort,
50 | sessionStorageHandler: sessionStorageHandler,
51 | discoveryHandler: discoveryHandler,
52 | onMsg: onMsg,
53 | plugin: defaultPlugin,
54 | }
55 |
56 | }
57 |
58 | // WithPassword sets the password for transport node
59 | func (conf *NodeConf) WithPassword(password string) *NodeConf {
60 | conf.password = password
61 | return conf
62 | }
63 |
64 | // WithPort sets the port
65 | func (conf *NodeConf) WithPort(port int64) *NodeConf {
66 | conf.port = port
67 | return conf
68 | }
69 |
70 | // WithInternalPort sets the port for internal protocol
71 | func (conf *NodeConf) WithInternalPort(port int64) *NodeConf {
72 | conf.internalPort = port
73 | return conf
74 | }
75 |
76 | // WithInternalProtocolHandler sets the internal protocol for node
77 | // func (conf *NodeConf) WithInternalProtocolHandler(internalProtocolHandler protocol.Protocol) *NodeConf {
78 | // conf.internalProtocolHandler = internalProtocolHandler
79 | // return conf
80 | // }
81 |
82 | // WithClientInterval sets the heartbeat interval
83 | func (conf *NodeConf) WithClientInterval(pingInterval int64) *NodeConf {
84 | conf.clientPingInterval = pingInterval
85 | return conf
86 | }
87 |
88 | // WithAddr sets the addr for node cluster
89 | func (conf *NodeConf) WithAddr(addr string) *NodeConf {
90 | conf.addr = addr
91 | return conf
92 | }
93 |
94 | // WithPlugin sets the plugin
95 | func (conf *NodeConf) WithPlugin(plugin Plugin) *NodeConf {
96 | conf.plugin = plugin
97 | return conf
98 | }
99 |
--------------------------------------------------------------------------------
/node/plugin.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import "github.com/weblazy/socket-cluster/protocol"
4 |
5 | type (
6 | Plugin interface {
7 | OnConnect(conn protocol.Connection)
8 | OnClose(conn protocol.Connection)
9 | }
10 | DefaultPlugin struct {
11 | }
12 | )
13 |
14 | var defaultPlugin = &DefaultPlugin{}
15 |
16 | func (this *DefaultPlugin) OnConnect(conn protocol.Connection) {
17 |
18 | }
19 | func (this *DefaultPlugin) OnClose(conn protocol.Connection) {
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/node/session.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "sync/atomic"
5 | "unsafe"
6 |
7 | "github.com/weblazy/socket-cluster/protocol"
8 | )
9 |
10 | type Session struct {
11 | Conn protocol.Connection
12 | ClientId string
13 | }
14 |
15 | // LoadClientId returns the session uid.
16 | func (s *Session) LoadClientId() string {
17 | pointer := unsafe.Pointer(&s.ClientId)
18 | return *(*string)(atomic.LoadPointer(&pointer))
19 | }
20 |
21 | // StoreClientId sets the session uid.
22 | func (s *Session) StoreClientId(newClientId string) {
23 | pointer := unsafe.Pointer(&s.ClientId)
24 | atomic.StorePointer(&pointer, unsafe.Pointer(&newClientId))
25 | }
26 |
27 | // CasClientId sets the session uid and return oldClientId
28 | func (s *Session) CasClientId(newClientId string) string {
29 | newValue := unsafe.Pointer(&newClientId)
30 | pointer := unsafe.Pointer(&s.ClientId)
31 | for {
32 | oldValue := atomic.LoadPointer(&pointer)
33 | if atomic.CompareAndSwapPointer(&pointer, oldValue, newValue) {
34 | return *(*string)(oldValue)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/pic/business_client.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/pic/business_client.png
--------------------------------------------------------------------------------
/pic/socket_cluster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/pic/socket_cluster.png
--------------------------------------------------------------------------------
/protocol/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weblazy/socket-cluster/63d8325ffa8a0ef84ba0f6bb7666a7fdc4cd66f1/protocol/.DS_Store
--------------------------------------------------------------------------------
/protocol/flow_proto.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "io"
8 | )
9 |
10 | const (
11 | HEAD_SIZE = 4
12 | HEADER = "socket-cluster"
13 | // The maximum length of each message (including headers), which can be set to 4G
14 | MAX_LENGTH = 1024 * 70
15 | )
16 |
17 | var ExceededErr = errors.New("The maximum length of the packet is exceeded")
18 | var HeaderErr = errors.New("The message header is incorrect")
19 |
20 | type FlowProtocol struct {
21 | Proto
22 | header string
23 | headLength int
24 | bufLength int
25 | start int
26 | end int
27 | buf []byte
28 | conn io.Reader
29 | }
30 |
31 | func NewFlowProtocol(header string, bufLength int, conn io.Reader) *FlowProtocol {
32 | return &FlowProtocol{
33 | header: header,
34 | headLength: len(header) + HEAD_SIZE,
35 | bufLength: bufLength,
36 | buf: make([]byte, bufLength),
37 | conn: conn,
38 | }
39 | }
40 |
41 | // Read Read and parse the received message
42 | func (this *FlowProtocol) ReadMsg() ([]byte, error) {
43 | // read header
44 | for this.end-this.start < this.headLength {
45 | // reset start
46 | if this.start > 0 {
47 | copy(this.buf, this.buf[this.start:this.end])
48 | this.end -= this.start
49 | this.start = 0
50 | }
51 | n, err := this.conn.Read(this.buf[this.end:])
52 | if err != nil {
53 | return nil, err
54 | }
55 | this.end += n
56 | }
57 |
58 | headBuf := this.buf[this.start : this.start+this.headLength]
59 | // Verify that the message header is correct
60 | if string(headBuf[:len(this.header)]) != this.header {
61 | return nil, HeaderErr
62 | }
63 |
64 | // read content
65 | contentSize := int(binary.BigEndian.Uint32(headBuf[len(this.header):]))
66 | totalLenth := this.headLength + contentSize
67 | if totalLenth > this.bufLength {
68 | return nil, ExceededErr
69 | }
70 | for this.end-this.start < totalLenth {
71 | n, err := this.conn.Read(this.buf[this.end:])
72 | if err != nil {
73 | return nil, err
74 | }
75 | this.end += n
76 | }
77 | this.start += this.headLength
78 | contentBuf := this.buf[this.start : this.start+contentSize]
79 | this.start += contentSize
80 | return contentBuf, nil
81 | }
82 |
83 | // Pack Package raw data
84 | func (this *FlowProtocol) Pack(data []byte) ([]byte, error) {
85 | headSize := len(data)
86 | if headSize+this.headLength > this.bufLength {
87 | return nil, ExceededErr
88 | }
89 | var headBytes = make([]byte, HEAD_SIZE)
90 | binary.BigEndian.PutUint32(headBytes, uint32(headSize))
91 | var buf bytes.Buffer
92 | buf.Write([]byte(this.header))
93 | buf.Write(headBytes)
94 | buf.Write(data)
95 | return buf.Bytes(), nil
96 | }
97 |
--------------------------------------------------------------------------------
/protocol/protocol.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | type (
4 | // Proto pack/read protocol scheme of socket message.
5 | Proto interface {
6 | // Pack writes the Message into the connection.
7 | // NOTE: Make sure to write only once or there will be package contamination!
8 | Pack([]byte) ([]byte, error)
9 | // Read bytes from the connection.
10 | ReadMsg() ([]byte, error)
11 | }
12 | )
13 |
14 | type Protocol interface {
15 | // ListenAndServe turns on the listening service.
16 | ListenAndServe(port int64, onConnect func(conn Connection)) error
17 | // Dial connects with the socket of the destination address.
18 | Dial(addr string) (Connection, error)
19 | }
20 |
--------------------------------------------------------------------------------
/protocol/quic_protocol/quic_connect.go:
--------------------------------------------------------------------------------
1 | package quic_protocol
2 |
3 | // import (
4 | // "sync"
5 |
6 | // "github.com/quic-go/quic-go"
7 | // "github.com/weblazy/socket-cluster/protocol"
8 | // )
9 |
10 | // type QuicConnection struct {
11 | // stream quic.Stream
12 | // session quic.Session
13 | // mutex sync.Mutex
14 | // flowProtocolHandler protocol.Proto
15 | // }
16 |
17 | // func NewQuicConnection(stream quic.Stream, session quic.Session) *QuicConnection {
18 | // return &QuicConnection{
19 | // session: session,
20 | // stream: stream,
21 | // flowProtocolHandler: protocol.NewFlowProtocol(protocol.HEADER, protocol.MAX_LENGTH, stream),
22 | // }
23 | // }
24 |
25 | // // WriteMsg sends byte array message
26 | // func (this *QuicConnection) WriteMsg(data []byte) error {
27 | // data, err := this.flowProtocolHandler.Pack(data)
28 | // if err != nil {
29 | // return err
30 | // }
31 | // this.mutex.Lock()
32 | // defer this.mutex.Unlock()
33 | // _, err = this.stream.Write(data)
34 | // return err
35 | // }
36 |
37 | // // ReadMsg reads byte array message
38 | // func (this *QuicConnection) ReadMsg() ([]byte, error) {
39 | // msg, err := this.flowProtocolHandler.ReadMsg()
40 | // if err != nil {
41 | // return nil, err
42 | // }
43 | // return msg, err
44 | // }
45 |
46 | // func (this *QuicConnection) Addr() string {
47 | // return this.session.RemoteAddr().String()
48 | // }
49 |
50 | // func (this *QuicConnection) Close() error {
51 | // if this.stream == nil {
52 | // return nil
53 | // }
54 | // return this.stream.Close()
55 | // }
56 |
57 | // func (this *QuicConnection) SetFlowProtocolHandler(flowProtocolHandler protocol.Proto) {
58 | // this.flowProtocolHandler = flowProtocolHandler
59 | // }
60 |
--------------------------------------------------------------------------------
/protocol/quic_protocol/quic_protocol.go:
--------------------------------------------------------------------------------
1 | package quic_protocol
2 |
3 | // import (
4 | // "fmt"
5 |
6 | // "github.com/quic-go/quic-go"
7 | // "github.com/weblazy/socket-cluster/logx"
8 | // "github.com/weblazy/socket-cluster/protocol"
9 | // )
10 |
11 | // type QuicProtocol struct {
12 | // }
13 |
14 | // func (this *QuicProtocol) ListenAndServe(port int64, onConnect func(conn protocol.Connection)) error {
15 | // // Setup a bare-bones TLS config for the server
16 | // tlsConf := protocol.GenerateTLSConfigForServer()
17 | // listener, err := quic.ListenAddr(fmt.Sprintf(":%d", port), tlsConf, nil)
18 | // if err != nil {
19 | // logx.LogHandler.Error(err)
20 | // }
21 | // for {
22 | // session, err := listener.Accept()
23 | // if err != nil {
24 | // logx.LogHandler.Error(err)
25 | // } else {
26 | // go func(session quic.Session) {
27 | // // Use only the first stream
28 | // stream, err := session.AcceptStream()
29 | // if err != nil {
30 | // panic(err)
31 | // }
32 | // conn := NewQuicConnection(stream, session)
33 | // onConnect(conn)
34 | // }(session)
35 | // }
36 | // }
37 | // }
38 |
39 | // func (this *QuicProtocol) Dial(addr string) (protocol.Connection, error) {
40 | // // Setup a bare-bones TLS config for the client
41 | // tlsConf := protocol.GenerateTLSConfigForClient()
42 | // session, err := quic.DialAddr(addr, tlsConf, nil)
43 | // if err != nil {
44 | // return nil, err
45 | // }
46 | // // Use only the first stream
47 | // stream, err := session.OpenStreamSync()
48 | // if err != nil {
49 | // return nil, err
50 | // }
51 | // return NewQuicConnection(stream, session), err
52 | // }
53 |
--------------------------------------------------------------------------------
/protocol/quic_protocol/quic_test.go:
--------------------------------------------------------------------------------
1 | package quic_protocol
2 |
3 | // func TestQuic(t *testing.T) {
4 | // quic := &QuicProtocol{}
5 | // wait := sync.WaitGroup{}
6 | // wait.Add(1)
7 | // go quic.ListenAndServe(80, func(connection protocol.Connection) {
8 | // for {
9 | // msg, err := connection.ReadMsg()
10 | // if err != nil {
11 | // break
12 | // }
13 | // fmt.Printf("%s\n", string(msg))
14 | // wait.Done()
15 | // }
16 | // })
17 | // connection, err := quic.Dial("127.0.0.1:80")
18 | // if err != nil {
19 | // fmt.Printf("%#v\n", err.Error())
20 | // }
21 | // err = connection.WriteMsg([]byte("你好"))
22 | // if err != nil {
23 | // fmt.Printf("%#v\n", err.Error())
24 | // }
25 | // wait.Wait()
26 | // }
27 |
--------------------------------------------------------------------------------
/protocol/session.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "sync/atomic"
5 | "unsafe"
6 | )
7 |
8 | type Connection interface {
9 | ReadMsg() ([]byte, error)
10 | WriteMsg(data []byte) error
11 | Close() error
12 | Addr() string
13 | ConnProtocol() string
14 | }
15 |
16 | type Session struct {
17 | Conn Connection
18 | ClientId string
19 | }
20 |
21 | // LoadClientId returns the session uid.
22 | func (s *Session) LoadClientId() string {
23 | pointer := unsafe.Pointer(&s.ClientId)
24 | return *(*string)(atomic.LoadPointer(&pointer))
25 | }
26 |
27 | // StoreClientId sets the session uid.
28 | func (s *Session) StoreClientId(newClientId string) {
29 | pointer := unsafe.Pointer(&s.ClientId)
30 | atomic.StorePointer(&pointer, unsafe.Pointer(&newClientId))
31 | }
32 |
33 | // CasClientId sets the session uid and return oldClientId
34 | func (s *Session) CasClientId(newClientId string) string {
35 | newValue := unsafe.Pointer(&newClientId)
36 | pointer := unsafe.Pointer(&s.ClientId)
37 | for {
38 | oldValue := atomic.LoadPointer(&pointer)
39 | if atomic.CompareAndSwapPointer(&pointer, oldValue, newValue) {
40 | return *(*string)(oldValue)
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/protocol/tcp_protocol/tcp_connection.go:
--------------------------------------------------------------------------------
1 | package tcp_protocol
2 |
3 | import (
4 | "net"
5 | "sync"
6 |
7 | "github.com/weblazy/socket-cluster/protocol"
8 | )
9 |
10 | type TcpConnection struct {
11 | Conn net.Conn
12 | Mutex sync.Mutex
13 | flowProtocolHandler protocol.Proto
14 | }
15 |
16 | const TcpConnProtocol = "tcp"
17 |
18 | func NewTcpConnection(conn net.Conn) *TcpConnection {
19 | return &TcpConnection{
20 | Conn: conn,
21 | flowProtocolHandler: protocol.NewFlowProtocol(protocol.HEADER, protocol.MAX_LENGTH, conn),
22 | }
23 | }
24 |
25 | // WriteMsg send byte array message
26 | func (this *TcpConnection) WriteMsg(data []byte) error {
27 | data, err := this.flowProtocolHandler.Pack(data)
28 | if err != nil {
29 | return err
30 | }
31 | this.Mutex.Lock()
32 | defer this.Mutex.Unlock()
33 | _, err = this.Conn.Write(data)
34 | return err
35 | }
36 |
37 | func (this *TcpConnection) ReadMsg() ([]byte, error) {
38 | msg, err := this.flowProtocolHandler.ReadMsg()
39 | if err != nil {
40 | return nil, err
41 | }
42 | return msg, err
43 | }
44 |
45 | func (this *TcpConnection) Addr() string {
46 | return this.Conn.RemoteAddr().String()
47 | }
48 |
49 | func (this *TcpConnection) Close() error {
50 | return this.Conn.Close()
51 | }
52 |
53 | func (this *TcpConnection) SetFlowProtocolHandler(flowProtocolHandler protocol.Proto) {
54 | this.flowProtocolHandler = flowProtocolHandler
55 | }
56 |
57 | func (this *TcpConnection) ConnProtocol() string {
58 | return TcpConnProtocol
59 | }
60 |
--------------------------------------------------------------------------------
/protocol/tcp_protocol/tcp_protocol.go:
--------------------------------------------------------------------------------
1 | package tcp_protocol
2 |
3 | import (
4 | "fmt"
5 | "net"
6 |
7 | "github.com/weblazy/socket-cluster/logx"
8 | "github.com/weblazy/socket-cluster/protocol"
9 | )
10 |
11 | type TcpProtocol struct {
12 | }
13 |
14 | func (this *TcpProtocol) ListenAndServe(port int64, onConnect func(conn protocol.Connection)) error {
15 | listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
16 | if err != nil {
17 | return err
18 | }
19 | go func() {
20 | for {
21 | connect, err := listener.Accept()
22 | if err != nil {
23 | logx.LogHandler.Error(err)
24 | break
25 | }
26 | go func(connect net.Conn) {
27 | conn := NewTcpConnection(connect)
28 | onConnect(conn)
29 | }(connect)
30 | }
31 | }()
32 | return nil
33 | }
34 |
35 | func (this *TcpProtocol) Dial(addr string) (protocol.Connection, error) {
36 | tcpAddr, err := net.ResolveTCPAddr("tcp4", addr)
37 | if err != nil {
38 | return nil, err
39 | }
40 | conn, err := net.DialTCP("tcp", nil, tcpAddr)
41 | if err != nil {
42 | return nil, err
43 | }
44 | return NewTcpConnection(conn), err
45 | }
46 |
--------------------------------------------------------------------------------
/protocol/tls.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "crypto/rand"
5 | "crypto/rsa"
6 | "crypto/tls"
7 | "crypto/x509"
8 | "encoding/pem"
9 | "math/big"
10 | )
11 |
12 | // NewTLSConfigFromFile creates a new TLS config.
13 | func NewTLSConfigFromFile(tlsCertFile, tlsKeyFile string, insecureSkipVerifyForClient ...bool) (*tls.Config, error) {
14 | cert, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
15 | if err != nil {
16 | return nil, err
17 | }
18 | return newTLSConfig(cert, insecureSkipVerifyForClient...), nil
19 | }
20 |
21 | // GenerateTLSConfigForClient setup a bare-bones(skip verify) TLS config for client.
22 | func GenerateTLSConfigForClient() *tls.Config {
23 | return &tls.Config{
24 | NextProtos: []string{"http/1.1", "h2"},
25 | InsecureSkipVerify: true,
26 | }
27 | }
28 |
29 | // GenerateTLSConfigForServer setup a bare-bones TLS config for server.
30 | func GenerateTLSConfigForServer() *tls.Config {
31 | key, err := rsa.GenerateKey(rand.Reader, 1024)
32 | if err != nil {
33 | panic(err)
34 | }
35 | template := x509.Certificate{SerialNumber: big.NewInt(1)}
36 | certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
37 | if err != nil {
38 | panic(err)
39 | }
40 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
41 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
42 |
43 | cert, err := tls.X509KeyPair(certPEM, keyPEM)
44 | if err != nil {
45 | panic(err)
46 | }
47 | return newTLSConfig(cert)
48 | }
49 |
50 | func newTLSConfig(cert tls.Certificate, insecureSkipVerifyForClient ...bool) *tls.Config {
51 | var insecureSkipVerify bool
52 | if len(insecureSkipVerifyForClient) > 0 {
53 | insecureSkipVerify = insecureSkipVerifyForClient[0]
54 | }
55 | return &tls.Config{
56 | InsecureSkipVerify: insecureSkipVerify,
57 | Certificates: []tls.Certificate{cert},
58 | NextProtos: []string{"http/1.1", "h2"},
59 | PreferServerCipherSuites: true,
60 | CurvePreferences: []tls.CurveID{
61 | tls.CurveP256,
62 | tls.X25519,
63 | },
64 | MinVersion: tls.VersionTLS12,
65 | CipherSuites: []uint16{
66 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
67 | tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
68 | tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
69 | tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
70 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
71 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
72 | tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
73 | tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
74 | },
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/protocol/ws_protocol/ws_connection.go:
--------------------------------------------------------------------------------
1 | package ws_protocol
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/gorilla/websocket"
7 | "github.com/labstack/echo/v4"
8 | "github.com/weblazy/socket-cluster/protocol"
9 | )
10 |
11 | const WsConnProtocol = "ws"
12 |
13 | type WsConnection struct {
14 | Conn *websocket.Conn
15 | Mutex sync.Mutex
16 | protocol.Connection
17 | }
18 |
19 | func NewWsConnection(conn *websocket.Conn) *WsConnection {
20 | return &WsConnection{
21 | Conn: conn,
22 | }
23 | }
24 |
25 | // WriteMsg send byte array message
26 | func (this *WsConnection) WriteMsg(data []byte) error {
27 | this.Mutex.Lock()
28 | defer this.Mutex.Unlock()
29 | return this.Conn.WriteMessage(websocket.TextMessage, data)
30 | }
31 |
32 | func (this *WsConnection) ReadMsg() ([]byte, error) {
33 | _, msg, err := this.Conn.ReadMessage()
34 | if err != nil {
35 | if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
36 |
37 | }
38 | return nil, err
39 | }
40 | return msg, err
41 | }
42 |
43 | func (this *WsConnection) Addr() string {
44 | return this.Conn.RemoteAddr().String()
45 | }
46 |
47 | func (this *WsConnection) Close() error {
48 | if this.Conn == nil {
49 | return nil
50 | }
51 | return this.Conn.Close()
52 | }
53 |
54 | func OptionHandler(c echo.Context) error {
55 | c.Response().Header().Set("Access-Control-Allow-Origin", "*")
56 | c.Response().Header().Set("Access-Control-Allow-Headers", "*")
57 | return c.String(200, "")
58 | }
59 |
60 | func OriginMiddlewareFunc(next echo.HandlerFunc) echo.HandlerFunc {
61 | return func(c echo.Context) error {
62 | c.Response().Header().Set("Access-Control-Allow-Origin", "*")
63 | c.Response().Header().Set("Access-Control-Allow-Headers", "*")
64 | return next(c)
65 | }
66 | }
67 |
68 | func (this *WsConnection) ConnProtocol() string {
69 | return WsConnProtocol
70 | }
71 |
--------------------------------------------------------------------------------
/protocol/ws_protocol/ws_protocol.go:
--------------------------------------------------------------------------------
1 | package ws_protocol
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | "github.com/gorilla/websocket"
8 | "github.com/labstack/echo/v4"
9 | "github.com/weblazy/socket-cluster/protocol"
10 | )
11 |
12 | var (
13 | upgrader = websocket.Upgrader{
14 | ReadBufferSize: 4096,
15 | WriteBufferSize: 4096,
16 | EnableCompression: true,
17 | CheckOrigin: func(r *http.Request) bool {
18 | return true
19 | },
20 | }
21 | )
22 |
23 | type WsProtocol struct {
24 | ClientPath string
25 | HandlerFunc func(conn protocol.Connection) echo.HandlerFunc
26 | }
27 |
28 | func NewWsProtocol(clientPath string, h func(conn protocol.Connection) echo.HandlerFunc) *WsProtocol {
29 | return &WsProtocol{
30 | ClientPath: clientPath,
31 | HandlerFunc: h,
32 | }
33 | }
34 |
35 | func (this *WsProtocol) ListenAndServe(port int64, onConnect func(conn protocol.Connection)) error {
36 | e := echo.New()
37 | e.GET(this.ClientPath, func(c echo.Context) error {
38 | connect, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
39 | if err != nil {
40 | if _, ok := err.(websocket.HandshakeError); !ok {
41 |
42 | }
43 | return err
44 | }
45 | conn := NewWsConnection(connect)
46 | handler := this.HandlerFunc(conn)
47 | err = handler(c)
48 | if err != nil {
49 | return err
50 | }
51 | onConnect(conn)
52 | return nil
53 | })
54 | go func() {
55 | err := e.Start(fmt.Sprintf(":%d", port))
56 | if err != nil {
57 | panic(err)
58 | }
59 | }()
60 | return nil
61 | }
62 |
63 | func (this *WsProtocol) Dial(addr string) (protocol.Connection, error) {
64 | connect, _, err := websocket.DefaultDialer.Dial(addr, nil)
65 | if err != nil {
66 | return nil, err
67 | }
68 | return NewWsConnection(connect), nil
69 | }
70 |
--------------------------------------------------------------------------------
/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/handler"
7 | "github.com/weblazy/socket-cluster/node"
8 | "go.uber.org/zap"
9 |
10 | "github.com/weblazy/easy/elog"
11 | "github.com/weblazy/easy/grpc/grpc_server"
12 | "github.com/weblazy/easy/grpc/grpc_server/grpc_server_config"
13 | "github.com/weblazy/socket-cluster/grpcs/socket_cluster_gateway/proto/gateway"
14 | )
15 |
16 | type Server struct {
17 | Node node.Node
18 | }
19 |
20 | func (s *Server) Run(ctx context.Context, nodeConf *node.NodeConf, grpcConf *grpc_server_config.Config) {
21 | serverNode, err := node.NewNode(nodeConf)
22 | if err != nil {
23 | elog.ErrorCtx(ctx, "msg", zap.Error(err))
24 | }
25 | s.Node = serverNode
26 | server := grpc_server.NewGrpcServer(grpcConf)
27 | gateway.RegisterGatewayServiceServer(server.Server, handler.NewGatewayService(serverNode))
28 | err = server.Init()
29 | if err != nil {
30 | elog.ErrorCtx(ctx, "server.Init", zap.Error(err))
31 | }
32 | err = server.Start()
33 | if err != nil {
34 | elog.ErrorCtx(ctx, "server.Start", zap.Error(err))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/session_storage/redis_storage/redis_storage.go:
--------------------------------------------------------------------------------
1 | package redis_storage
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/go-redis/redis/v8"
8 | "github.com/spf13/cast"
9 | "github.com/weblazy/easy/db/eredis"
10 | "github.com/weblazy/socket-cluster/logx"
11 | "github.com/weblazy/socket-cluster/session_storage"
12 | )
13 |
14 | type RedisStorage struct {
15 | session_storage.SessionStorage
16 | clientTimeout int64 // client heartbeat timeout time
17 | redisClient *eredis.RedisClient
18 | }
19 |
20 | type RedisNode struct {
21 | RedisConf *redis.Options
22 | Position int64 //the position of hash ring
23 | }
24 |
25 | // NewRedisStorage return a RedisStorage
26 | func NewRedisStorage(redisClient *eredis.RedisClient) *RedisStorage {
27 | return &RedisStorage{redisClient: redisClient, clientTimeout: 360}
28 | }
29 |
30 | // GetIps get ip list by clientId
31 | func (this *RedisStorage) GetIps(clientId string) ([]string, error) {
32 | now := time.Now().Unix()
33 | ipArr, err := this.redisClient.ZRangeByScore(context.Background(), session_storage.ClientPrefix+clientId, &redis.ZRangeBy{Min: cast.ToString(now - this.clientTimeout), Max: "+inf"}).Result()
34 | return ipArr, err
35 | }
36 |
37 | // BindClientId set online with clientId
38 | func (this *RedisStorage) BindClientId(nodeAddr string, clientId string) error {
39 | now := time.Now().Unix()
40 | err := this.redisClient.ZAdd(context.Background(), session_storage.ClientPrefix+clientId, &redis.Z{Score: cast.ToFloat64(now), Member: nodeAddr}).Err()
41 | if err != nil {
42 | return err
43 | }
44 |
45 | err = this.redisClient.Expire(context.Background(), session_storage.ClientPrefix+clientId, time.Duration(this.clientTimeout)*time.Second).Err()
46 | if err != nil {
47 | return err
48 | }
49 |
50 | return nil
51 | }
52 |
53 | // ClientIdsOnline Get online users in the group
54 | func (this *RedisStorage) ClientIdsOnline(clientIds []string) []string {
55 | onlineClientIds := make([]string, 0)
56 | rangeTime := cast.ToString(time.Now().Unix() - this.clientTimeout)
57 |
58 | pipe := this.redisClient.Pipeline()
59 | for k2 := range clientIds {
60 | pipe.ZRangeByScore(context.Background(), session_storage.ClientPrefix+clientIds[k2], &redis.ZRangeBy{Min: rangeTime, Max: "+inf"}).Result()
61 | }
62 | cmders, err := pipe.Exec(context.Background())
63 | if err != nil {
64 | logx.LogHandler.Error(err)
65 | }
66 | for k3, cmder := range cmders {
67 | cmd := cmder.(*redis.StringSliceCmd)
68 | err := cmd.Err()
69 | if err != nil {
70 | logx.LogHandler.Error(err)
71 | } else {
72 | onlineClientIds = append(onlineClientIds, clientIds[k3])
73 | }
74 | }
75 |
76 | return onlineClientIds
77 | }
78 |
79 | // ClientIdsOnlineWithLua Get online users in the group
80 | func (this *RedisStorage) ClientIdsOnlineWithLua(clientIds []string) []string {
81 | onlineClientIds := make([]string, 0)
82 | return onlineClientIds
83 | }
84 |
85 | // IsOnline determine if a clientId is online
86 | func (this *RedisStorage) IsOnline(clientId string) bool {
87 | now := time.Now().Unix()
88 | addrArr, err := this.redisClient.ZRangeByScore(context.Background(), session_storage.ClientPrefix+clientId, &redis.ZRangeBy{Min: cast.ToString(now - this.clientTimeout), Max: "+inf"}).Result()
89 | if err != nil {
90 | logx.LogHandler.Error(err)
91 | return false
92 | }
93 | if len(addrArr) > 0 {
94 | return true
95 | }
96 | return false
97 | }
98 |
99 | // OnClientPing receive client heartbeat
100 | func (this *RedisStorage) OnClientPing(nodeAddr string, clientId string) error {
101 | now := time.Now().Unix()
102 | err := this.redisClient.ZAdd(context.Background(), session_storage.ClientPrefix+clientId, &redis.Z{Score: cast.ToFloat64(now), Member: nodeAddr}).Err()
103 | if err != nil {
104 | return err
105 | }
106 | err = this.redisClient.Expire(context.Background(), session_storage.ClientPrefix+clientId, time.Duration(this.clientTimeout)*time.Second).Err()
107 | return err
108 | }
109 |
110 | // GetIps get ip list by clientId list
111 | func (this *RedisStorage) GetClientsIps(clientIds []string) (map[string][]string, error) {
112 | rangeTime := cast.ToString(time.Now().Unix() - this.clientTimeout)
113 |
114 | pipe := this.redisClient.Pipeline()
115 | for k2 := range clientIds {
116 | pipe.ZRangeByScore(context.Background(), session_storage.ClientPrefix+clientIds[k2], &redis.ZRangeBy{Min: rangeTime, Max: "+inf"}).Result()
117 | }
118 | cmders, err := pipe.Exec(context.Background())
119 | if err != nil {
120 | logx.LogHandler.Error(cmders, err)
121 | }
122 | otherMap := make(map[string][]string)
123 | for k3, cmder := range cmders {
124 | cmd := cmder.(*redis.StringSliceCmd)
125 | strMap, err := cmd.Result()
126 | if err != nil {
127 | logx.LogHandler.Error(err)
128 | } else {
129 | for k4 := range strMap {
130 | if _, ok := otherMap[strMap[k4]]; ok {
131 | otherMap[strMap[k4]] = append(otherMap[strMap[k4]], clientIds[k3])
132 | } else {
133 | otherMap[strMap[k4]] = []string{clientIds[k3]}
134 | }
135 |
136 | }
137 |
138 | }
139 | }
140 |
141 | return otherMap, nil
142 | }
143 |
--------------------------------------------------------------------------------
/session_storage/session_storage.go:
--------------------------------------------------------------------------------
1 | package session_storage
2 |
3 | const (
4 | ClientPrefix = "client#"
5 | )
6 |
7 | type SessionStorage interface {
8 | IsOnline(clientId string) bool
9 | BindClientId(nodeAddr string, clientId string) error
10 | GetIps(clientId string) ([]string, error)
11 | GetClientsIps(clientIds []string) (map[string][]string, error)
12 | ClientIdsOnline(clientIds []string) []string
13 | OnClientPing(nodeAddr string, clientId string) error
14 | }
15 |
--------------------------------------------------------------------------------
/unsafehash/segment_hash.go:
--------------------------------------------------------------------------------
1 | package unsafehash
2 |
3 | import (
4 | "sort"
5 |
6 | "github.com/spf13/cast"
7 | "github.com/weblazy/easy/sortx"
8 | )
9 |
10 | type Segment struct {
11 | Position int64
12 | List []interface{} // client list
13 | }
14 |
15 | func NewSegment(position int64, list []interface{}) *Segment {
16 | return &Segment{
17 | Position: position,
18 | List: list,
19 | }
20 | }
21 |
22 | type SegmentHash struct {
23 | segmentList *sortx.SortList
24 | }
25 |
26 | func NewSegmentHash(segmentList ...*Segment) *SegmentHash {
27 | list := sortx.NewSortList(sortx.ASC)
28 | for k1 := range segmentList {
29 | list.List = append(list.List, sortx.Sort{
30 | Obj: segmentList[k1],
31 | Sort: cast.ToFloat64(segmentList[k1].Position),
32 | })
33 | }
34 | sort.Sort(list)
35 | return &SegmentHash{
36 | segmentList: list,
37 | }
38 | }
39 |
40 | func (c *SegmentHash) Append(segmentList ...*Segment) {
41 | for k1 := range segmentList {
42 | c.segmentList.List = append(c.segmentList.List, sortx.Sort{
43 | Obj: segmentList[k1],
44 | Sort: cast.ToFloat64(segmentList[k1].Position),
45 | })
46 | }
47 | sort.Sort(c.segmentList)
48 | }
49 |
50 | func (c *SegmentHash) Get(key int64) interface{} {
51 | if len(c.segmentList.List) == 0 || cast.ToFloat64(key) > c.segmentList.List[len(c.segmentList.List)-1].Sort {
52 | // out of range
53 | return nil
54 | }
55 | index := c.search(key)
56 | i := key % int64(len(c.segmentList.List[index].Obj.(*Segment).List))
57 | return c.segmentList.List[index].Obj.(*Segment).List[i]
58 | }
59 |
60 | func (c *SegmentHash) search(key int64) int {
61 | n := len(c.segmentList.List)
62 | i := sort.Search(n, func(i int) bool { return c.segmentList.List[i].Sort >= cast.ToFloat64(key) })
63 | if i < n {
64 | return i
65 | } else {
66 | return 0
67 | }
68 | }
69 |
--------------------------------------------------------------------------------