@@ -38,7 +38,7 @@ import (
38
38
// Client of gnet.
39
39
type Client struct {
40
40
opts * Options
41
- el * eventloop
41
+ eng * engine
42
42
}
43
43
44
44
// NewClient creates an instance of Client.
@@ -59,28 +59,19 @@ func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) {
59
59
}
60
60
logging .SetDefaultLoggerAndFlusher (logger , logFlusher )
61
61
62
- var p * netpoll.Poller
63
- if p , err = netpoll .OpenPoller (); err != nil {
64
- return
65
- }
66
-
67
62
rootCtx , shutdown := context .WithCancel (context .Background ())
68
63
eg , ctx := errgroup .WithContext (rootCtx )
69
64
eng := engine {
70
65
listeners : make (map [int ]* listener ),
71
66
opts : options ,
72
67
turnOff : shutdown ,
73
68
eventHandler : eh ,
69
+ eventLoops : new (leastConnectionsLoadBalancer ),
74
70
concurrency : struct {
75
71
* errgroup.Group
76
72
ctx context.Context
77
73
}{eg , ctx },
78
74
}
79
- el := eventloop {
80
- listeners : eng .listeners ,
81
- engine : & eng ,
82
- poller : p ,
83
- }
84
75
85
76
if options .EdgeTriggeredIOChunk > 0 {
86
77
options .EdgeTriggeredIO = true
@@ -107,39 +98,82 @@ func NewClient(eh EventHandler, opts ...Option) (cli *Client, err error) {
107
98
default :
108
99
options .WriteBufferCap = math .CeilToPowerOfTwo (wbc )
109
100
}
110
-
111
- el .buffer = make ([]byte , options .ReadBufferCap )
112
- el .connections .init ()
113
- el .eventHandler = eh
114
- cli .el = & el
101
+ cli .eng = & eng
115
102
return
116
103
}
117
104
118
105
// Start starts the client event-loop, handing IO events.
119
106
func (cli * Client ) Start () error {
120
- logging .Infof ("Starting gnet client with 1 event-loop" )
121
- cli .el .eventHandler .OnBoot (Engine {cli .el .engine })
122
- cli .el .engine .concurrency .Go (cli .el .run )
107
+ numEventLoop := determineEventLoops (cli .opts )
108
+ logging .Infof ("Starting gnet client with %d event loops" , numEventLoop )
109
+
110
+ cli .eng .eventHandler .OnBoot (Engine {cli .eng })
111
+
112
+ var el0 * eventloop
113
+ for i := 0 ; i < numEventLoop ; i ++ {
114
+ p , err := netpoll .OpenPoller ()
115
+ if err != nil {
116
+ cli .eng .closeEventLoops ()
117
+ return err
118
+ }
119
+ el := eventloop {
120
+ listeners : cli .eng .listeners ,
121
+ engine : cli .eng ,
122
+ poller : p ,
123
+ buffer : make ([]byte , cli .opts .ReadBufferCap ),
124
+ eventHandler : cli .eng .eventHandler ,
125
+ }
126
+ el .connections .init ()
127
+ cli .eng .eventLoops .register (& el )
128
+ if cli .opts .Ticker && el .idx == 0 {
129
+ el0 = & el
130
+ }
131
+ }
132
+
133
+ cli .eng .eventLoops .iterate (func (_ int , el * eventloop ) bool {
134
+ cli .eng .concurrency .Go (el .run )
135
+ return true
136
+ })
137
+
123
138
// Start the ticker.
124
- if cli . opts . Ticker {
125
- ctx := cli .el . engine .concurrency .ctx
126
- cli .el . engine .concurrency .Go (func () error {
127
- cli . el .ticker (ctx )
139
+ if el0 != nil {
140
+ ctx := cli .eng .concurrency .ctx
141
+ cli .eng .concurrency .Go (func () error {
142
+ el0 .ticker (ctx )
128
143
return nil
129
144
})
130
145
}
146
+
131
147
logging .Debugf ("default logging level is %s" , logging .LogLevel ())
148
+
132
149
return nil
133
150
}
134
151
135
152
// Stop stops the client event-loop.
136
- func (cli * Client ) Stop () (err error ) {
137
- logging .Error (cli .el .poller .Trigger (queue .HighPriority , func (_ any ) error { return errorx .ErrEngineShutdown }, nil ))
138
- err = cli .el .engine .concurrency .Wait ()
139
- logging .Error (cli .el .poller .Close ())
140
- cli .el .eventHandler .OnShutdown (Engine {cli .el .engine })
153
+ func (cli * Client ) Stop () error {
154
+ cli .eng .shutdown (nil )
155
+
156
+ cli .eng .eventHandler .OnShutdown (Engine {cli .eng })
157
+
158
+ // Notify all event-loops to exit.
159
+ cli .eng .eventLoops .iterate (func (_ int , el * eventloop ) bool {
160
+ logging .Error (el .poller .Trigger (queue .HighPriority ,
161
+ func (_ any ) error { return errorx .ErrEngineShutdown }, nil ))
162
+ return true
163
+ })
164
+
165
+ // Wait for all event-loops to exit.
166
+ err := cli .eng .concurrency .Wait ()
167
+
168
+ cli .eng .closeEventLoops ()
169
+
170
+ // Put the engine into the shutdown state.
171
+ cli .eng .inShutdown .Store (true )
172
+
173
+ // Flush the logger.
141
174
logging .Cleanup ()
142
- return
175
+
176
+ return err
143
177
}
144
178
145
179
// Dial is like net.Dial().
@@ -156,7 +190,7 @@ func (cli *Client) DialContext(network, address string, ctx any) (Conn, error) {
156
190
return cli .EnrollContext (c , ctx )
157
191
}
158
192
159
- // Enroll converts a net.Conn to gnet.Conn and then adds it into Client.
193
+ // Enroll converts a net.Conn to gnet.Conn and then adds it into the Client.
160
194
func (cli * Client ) Enroll (c net.Conn ) (Conn , error ) {
161
195
return cli .EnrollContext (c , nil )
162
196
}
@@ -196,6 +230,7 @@ func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
196
230
}
197
231
}
198
232
233
+ el := cli .eng .eventLoops .next (nil )
199
234
var (
200
235
sockAddr unix.Sockaddr
201
236
gc * conn
@@ -208,29 +243,34 @@ func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
208
243
}
209
244
ua := c .LocalAddr ().(* net.UnixAddr )
210
245
ua .Name = c .RemoteAddr ().String () + "." + strconv .Itoa (dupFD )
211
- gc = newTCPConn ( dupFD , cli . el , sockAddr , c .LocalAddr (), c .RemoteAddr ())
246
+ gc = newStreamConn ( "unix" , dupFD , el , sockAddr , c .LocalAddr (), c .RemoteAddr ())
212
247
case * net.TCPConn :
213
248
if cli .opts .TCPNoDelay == TCPNoDelay {
214
249
if err = socket .SetNoDelay (dupFD , 1 ); err != nil {
215
250
return nil , err
216
251
}
217
252
}
218
253
if cli .opts .TCPKeepAlive > 0 {
219
- if err = socket .SetKeepAlivePeriod (dupFD , int (cli .opts .TCPKeepAlive .Seconds ())); err != nil {
254
+ if err = setKeepAlive (
255
+ dupFD ,
256
+ true ,
257
+ cli .opts .TCPKeepAlive ,
258
+ cli .opts .TCPKeepInterval ,
259
+ cli .opts .TCPKeepCount ); err != nil {
220
260
return nil , err
221
261
}
222
262
}
223
263
sockAddr , _ , _ , _ , err = socket .GetTCPSockAddr (c .RemoteAddr ().Network (), c .RemoteAddr ().String ())
224
264
if err != nil {
225
265
return nil , err
226
266
}
227
- gc = newTCPConn ( dupFD , cli . el , sockAddr , c .LocalAddr (), c .RemoteAddr ())
267
+ gc = newStreamConn ( "tcp" , dupFD , el , sockAddr , c .LocalAddr (), c .RemoteAddr ())
228
268
case * net.UDPConn :
229
269
sockAddr , _ , _ , _ , err = socket .GetUDPSockAddr (c .RemoteAddr ().Network (), c .RemoteAddr ().String ())
230
270
if err != nil {
231
271
return nil , err
232
272
}
233
- gc = newUDPConn (dupFD , cli . el , c .LocalAddr (), sockAddr , true )
273
+ gc = newUDPConn (dupFD , el , c .LocalAddr (), sockAddr , true )
234
274
default :
235
275
return nil , errorx .ErrUnsupportedProtocol
236
276
}
@@ -240,12 +280,12 @@ func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
240
280
ccb := & connWithCallback {c : gc , cb : func () {
241
281
close (connOpened )
242
282
}}
243
- err = cli . el .poller .Trigger (queue .HighPriority , cli . el .register , ccb )
283
+ err = el .poller .Trigger (queue .HighPriority , el .register , ccb )
244
284
if err != nil {
245
285
gc .Close () //nolint:errcheck
246
286
return nil , err
247
287
}
248
-
249
288
<- connOpened
289
+
250
290
return gc , nil
251
291
}
0 commit comments