1515 */
1616package org .jetlinks .community .network .http .server .vertx ;
1717
18+ import io .netty .handler .codec .http .websocketx .WebSocketCloseStatus ;
19+ import io .vertx .core .http .HttpClosedException ;
1820import lombok .*;
1921import lombok .extern .slf4j .Slf4j ;
22+ import org .hswebframework .web .exception .I18nSupportException ;
23+ import org .jetlinks .core .lang .SeparatedCharSequence ;
24+ import org .jetlinks .core .lang .SharedPathString ;
2025import org .jetlinks .core .topic .Topic ;
2126import org .jetlinks .community .network .DefaultNetworkType ;
2227import org .jetlinks .community .network .NetworkType ;
2328import org .jetlinks .community .network .http .server .HttpExchange ;
2429import org .jetlinks .community .network .http .server .HttpServer ;
2530import org .jetlinks .community .network .http .server .WebSocketExchange ;
31+ import org .jetlinks .core .utils .TopicUtils ;
32+ import org .springframework .http .HttpMethod ;
2633import org .springframework .http .HttpStatus ;
2734import reactor .core .Disposable ;
2835import reactor .core .Disposables ;
2936import reactor .core .publisher .Flux ;
3037import reactor .core .publisher .FluxSink ;
31- import reactor .core .publisher .Mono ;
3238
3339import java .net .InetSocketAddress ;
3440import java .util .Collection ;
41+ import java .util .Map ;
42+ import java .util .concurrent .ConcurrentHashMap ;
43+ import java .util .concurrent .atomic .AtomicLongFieldUpdater ;
3544import java .util .stream .Collectors ;
3645import java .util .stream .Stream ;
3746
4554@ Slf4j
4655public class VertxHttpServer implements HttpServer {
4756
57+ private static final SharedPathString WEBSOCKET_PREFIX = SharedPathString .of ("/ws" );
58+
59+ private static final Map <HttpMethod , SeparatedCharSequence > HTTP_PREFIX_CACHE = new ConcurrentHashMap <>();
60+
4861 private Collection <io .vertx .core .http .HttpServer > httpServers ;
4962
5063 private HttpServerConfig config ;
5164
5265 private String id ;
5366
67+ private static final AtomicLongFieldUpdater <VertxHttpServer > PENDING =
68+ AtomicLongFieldUpdater .newUpdater (VertxHttpServer .class , "pending" );
69+ private volatile long pending ;
70+
71+ private int maxConcurrency = -1 ;
72+
5473 private final Topic <FluxSink <HttpExchange >> route = Topic .createRoot ();
5574 private final Topic <FluxSink <WebSocketExchange >> websocketRoute = Topic .createRoot ();
5675
@@ -79,59 +98,151 @@ public void setHttpServers(Collection<io.vertx.core.http.HttpServer> httpServers
7998 for (io .vertx .core .http .HttpServer server : this .httpServers ) {
8099 server
81100 .webSocketHandler (socket -> {
82- socket .exceptionHandler (err -> {
83- log .error (err .getMessage (), err );
84- });
85-
86- String url = socket .path ();
87- if (url .endsWith ("/" )) {
88- url = url .substring (0 , url .length () - 1 );
101+ long pending = PENDING .incrementAndGet (this );
102+ if (maxConcurrency > 0 && pending >= maxConcurrency ) {
103+ PENDING .decrementAndGet (this );
104+ socket .close ((short ) WebSocketCloseStatus .TRY_AGAIN_LATER .code ());
105+ return ;
89106 }
90- VertxWebSocketExchange exchange = new VertxWebSocketExchange (socket );
91-
92- websocketRoute
93- .findTopic ("/ws" + url )
94- .flatMapIterable (Topic ::getSubscribers )
95- .doOnNext (sink -> sink .next (exchange ))
96- .switchIfEmpty (Mono .fromRunnable (() -> {
97107
98- log .warn ("http server no handler for:[{}://{}{}]" , socket .scheme (), socket .host (), socket .path ());
99- socket .reject (404 );
100-
101- }))
102- .subscribe ();
108+ socket .exceptionHandler (err -> {
109+ if (err instanceof HttpClosedException ){
110+ return ;
111+ }
112+ log .warn (err .getMessage (), err );
113+ });
114+ VertxWebSocketExchange exchange = new VertxWebSocketExchange (
115+ socket
116+ );
117+ exchange .closeHandler (() -> PENDING .decrementAndGet (this ));
118+ Topic .find (
119+ WEBSOCKET_PREFIX .append (parsePath (exchange .getPath ())),
120+ websocketRoute ,
121+ exchange ,
122+ socket ,
123+ null ,
124+ null ,
125+ (_exchange , _req , nil , nil2 , topic ) -> {
126+ for (FluxSink <WebSocketExchange > sink : topic .getSubscribers ()) {
127+ _exchange .mark ();
128+ sink .next (_exchange );
129+ }
130+ }, (_exchange , _socket , nil2 , nil ) -> {
131+ if (_exchange .handleCount () == 0 ) {
132+ if (log .isInfoEnabled ()) {
133+ log .info ("http server no handler for:[{}://{}{}] remote: {}" ,
134+ _socket .scheme (),
135+ _socket .authority (),
136+ _socket .path (),
137+ _socket .remoteAddress ());
138+ }
139+ _socket .close ((short ) WebSocketCloseStatus .ENDPOINT_UNAVAILABLE .code ());
140+ }
141+ });
103142
104143 })
105144 .requestHandler (request -> {
106- request .exceptionHandler (err -> {
107- log .error (err .getMessage (), err );
108- });
109145
110- VertxHttpExchange exchange = new VertxHttpExchange ( request , config );
146+ request . endHandler ( ignore -> PENDING . decrementAndGet ( this ) );
111147
112- String url = exchange .getUrl ();
113- if (url .endsWith ("/" )) {
114- url = url .substring (0 , url .length () - 1 );
148+ long pending = PENDING .incrementAndGet (this );
149+ if (maxConcurrency > 0 && pending >= maxConcurrency ) {
150+ request
151+ .response ()
152+ .setStatusCode (HttpStatus .SERVICE_UNAVAILABLE .value ())
153+ .end ();
154+ return ;
155+ }
156+ request .exceptionHandler (err -> {
157+ if (err instanceof HttpClosedException ){
158+ return ;
159+ }
160+ log .warn (err .getMessage (), err );
161+ });
162+ try {
163+ VertxHttpExchange exchange = new VertxHttpExchange (request , config );
164+ Topic .find (
165+ getHttpPrefix (exchange .request ().getMethod ())
166+ .append (parsePath (exchange .getPath ())),
167+ route ,
168+ exchange ,
169+ request ,
170+ null ,
171+ null ,
172+ //查找到订阅者
173+ (_exchange ,
174+ _req ,
175+ nil , nil2 ,
176+ topic ) -> {
177+ for (FluxSink <HttpExchange > sink : topic .getSubscribers ()) {
178+ _exchange .mark ();
179+ sink .next (_exchange );
180+ }
181+ },
182+ //全部查找结束
183+ (_exchange ,
184+ _req ,
185+ nil2 , nil ) -> {
186+ if (_exchange .handleCount () == 0 ) {
187+ if (log .isInfoEnabled ()) {
188+ log .info ("http server no handler for:[{} {}://{}:{}] remote: {}" ,
189+ _req .method (),
190+ _req .scheme (),
191+ _req .authority (),
192+ _req .path (),
193+ _req .remoteAddress ());
194+ }
195+ _req
196+ .response ()
197+ .setStatusCode (HttpStatus .NOT_FOUND .value ())
198+ .end ();
199+ }
200+ });
201+ } catch (Throwable e ) {
202+ request
203+ .response ()
204+ .setStatusCode (HttpStatus .BAD_GATEWAY .value ())
205+ .end ();
115206 }
116-
117- route .findTopic ("/" + exchange .request ().getMethod ().name ().toLowerCase () + url )
118- .flatMapIterable (Topic ::getSubscribers )
119- .doOnNext (sink -> sink .next (exchange ))
120- .switchIfEmpty (Mono .fromRunnable (() -> {
121-
122- log .warn ("http server no handler for:[{} {}://{}{}]" , request .method (), request .scheme (), request .host (), request .path ());
123- request .response ()
124- .setStatusCode (HttpStatus .NOT_FOUND .value ())
125- .end ();
126-
127- }))
128- .subscribe ();
129207
130208 });
131- server .exceptionHandler (err -> log .error (err .getMessage (), err ));
209+ server .exceptionHandler (err -> {
210+ // 忽略
211+ if (err instanceof HttpClosedException ) {
212+ return ;
213+ }
214+ log .warn ("http server [{}] error" , bindAddress , err );
215+ });
132216 }
133217 }
134218
219+ private SeparatedCharSequence parsePath (String url ) {
220+ while (url .charAt (url .length () - 1 ) == '/' ) {
221+ url = url .substring (0 , url .length () - 1 );
222+ }
223+ String [] split = TopicUtils .split (
224+ url ,
225+ '/' ,
226+ (i , p ) -> {
227+ //不支持通配符
228+ if ("*" .equals (p ) || "**" .equals (p )) {
229+ throw new I18nSupportException .NoStackTrace ("error.illegal_http_path" );
230+ }
231+ return p ;
232+ });
233+ return SharedPathString .of (split );
234+ }
235+
236+ private SeparatedCharSequence getHttpPrefix (HttpMethod method ) {
237+ return HTTP_PREFIX_CACHE .computeIfAbsent (
238+ method ,
239+ m -> {
240+ String prefix = m .name ().toLowerCase ();
241+ return SharedPathString .of ("/" + prefix );
242+ });
243+ }
244+
245+
135246 @ Override
136247 public Flux <HttpExchange > handleRequest () {
137248 return handleRequest ("*" , "/**" );
@@ -144,7 +255,7 @@ public Flux<WebSocketExchange> handleWebsocket(String urlPattern) {
144255
145256 @ Override
146257 public Flux <HttpExchange > handleRequest (String method , String ... urlPatterns ) {
147- return createRoute (route , method , urlPatterns );
258+ return createRoute (route , method . toLowerCase () , urlPatterns );
148259 }
149260
150261 private <T > Flux <T > createRoute (Topic <FluxSink <T >> root , String prefix , String ... urlPatterns ) {
0 commit comments