@@ -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,7 +243,7 @@ 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 = newStreamConn ("unix" , 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 {
@@ -229,13 +264,13 @@ func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
229
264
if err != nil {
230
265
return nil , err
231
266
}
232
- gc = newStreamConn ("tcp" , dupFD , cli . el , sockAddr , c .LocalAddr (), c .RemoteAddr ())
267
+ gc = newStreamConn ("tcp" , dupFD , el , sockAddr , c .LocalAddr (), c .RemoteAddr ())
233
268
case * net.UDPConn :
234
269
sockAddr , _ , _ , _ , err = socket .GetUDPSockAddr (c .RemoteAddr ().Network (), c .RemoteAddr ().String ())
235
270
if err != nil {
236
271
return nil , err
237
272
}
238
- gc = newUDPConn (dupFD , cli . el , c .LocalAddr (), sockAddr , true )
273
+ gc = newUDPConn (dupFD , el , c .LocalAddr (), sockAddr , true )
239
274
default :
240
275
return nil , errorx .ErrUnsupportedProtocol
241
276
}
@@ -245,12 +280,12 @@ func (cli *Client) EnrollContext(c net.Conn, ctx any) (Conn, error) {
245
280
ccb := & connWithCallback {c : gc , cb : func () {
246
281
close (connOpened )
247
282
}}
248
- err = cli . el .poller .Trigger (queue .HighPriority , cli . el .register , ccb )
283
+ err = el .poller .Trigger (queue .HighPriority , el .register , ccb )
249
284
if err != nil {
250
285
gc .Close () //nolint:errcheck
251
286
return nil , err
252
287
}
253
-
254
288
<- connOpened
289
+
255
290
return gc , nil
256
291
}
0 commit comments