3838import com .tencent .trpc .proto .http .common .RpcServerContextWithHttp ;
3939import com .tencent .trpc .proto .http .common .TrpcServletRequestWrapper ;
4040import com .tencent .trpc .proto .http .common .TrpcServletResponseWrapper ;
41+ import java .io .IOException ;
4142import java .lang .reflect .Type ;
4243import java .net .InetSocketAddress ;
4344import java .nio .charset .StandardCharsets ;
4445import java .util .Enumeration ;
4546import java .util .Map ;
47+ import java .util .concurrent .CompletableFuture ;
48+ import java .util .concurrent .TimeoutException ;
4649import java .util .concurrent .atomic .AtomicBoolean ;
4750import java .util .concurrent .CompletionStage ;
4851import java .util .concurrent .CountDownLatch ;
@@ -66,36 +69,33 @@ public abstract class AbstractHttpExecutor {
6669
6770 protected void execute (HttpServletRequest request , HttpServletResponse response ,
6871 RpcMethodInfoAndInvoker methodInfoAndInvoker ) {
69-
70- AtomicBoolean isTimeout = new AtomicBoolean (false );
72+ AtomicBoolean responded = new AtomicBoolean (false );
7173 try {
72-
7374 DefRequest rpcRequest = buildDefRequest (request , response , methodInfoAndInvoker );
74-
75- CountDownLatch countDownLatch = new CountDownLatch (1 );
76-
75+ CompletableFuture <Void > completionFuture = new CompletableFuture <>();
7776 // use a thread pool for asynchronous processing
78- invokeRpcRequest (methodInfoAndInvoker .getInvoker (), rpcRequest , countDownLatch , isTimeout );
79-
80- // If the request carries a timeout, use this timeout to wait for the request to be processed.
81- // If not carried, use the default timeout.
77+ invokeRpcRequest (methodInfoAndInvoker .getInvoker (), rpcRequest , completionFuture , responded );
8278 long requestTimeout = rpcRequest .getMeta ().getTimeout ();
8379 if (requestTimeout <= 0 ) {
8480 requestTimeout = methodInfoAndInvoker .getInvoker ().getConfig ().getRequestTimeout ();
8581 }
86- if (requestTimeout > 0 && !countDownLatch .await (requestTimeout , TimeUnit .MILLISECONDS )) {
87- isTimeout .set (true );
88- throw TRpcException .newFrameException (ErrorCode .TRPC_SERVER_TIMEOUT_ERR ,
89- "wait http request execute timeout" );
82+ if (requestTimeout > 0 ) {
83+ try {
84+ completionFuture .get (requestTimeout , TimeUnit .MILLISECONDS );
85+ } catch (TimeoutException ex ) {
86+ if (responded .compareAndSet (false , true )) {
87+ sendTimeoutResponse (request , response );
88+ }
89+ }
9090 } else {
91- countDownLatch . await ();
91+ completionFuture . get ();
9292 }
93-
9493 } catch (Exception ex ) {
9594 logger .error ("dispatch request [{}] error" , request , ex );
96- doErrorReply (request , response , ex );
95+ if (responded .compareAndSet (false , true )) {
96+ doErrorReply (request , response , ex );
97+ }
9798 }
98-
9999 }
100100
101101 /**
@@ -110,58 +110,91 @@ protected void execute(HttpServletRequest request, HttpServletResponse response,
110110 /**
111111 * Request processing
112112 *
113- * @param countDownLatch latch used to wait for the request processing
113+ * @param invoker the invoker
114+ * @param rpcRequest the rpc request
115+ * @param completionFuture the completion future
116+ * @param responded the responded flag
114117 */
115- private void invokeRpcRequest (ProviderInvoker <?> invoker , DefRequest rpcRequest , CountDownLatch countDownLatch ,
116- AtomicBoolean isTimeout ) {
118+ private void invokeRpcRequest (ProviderInvoker <?> invoker , DefRequest rpcRequest ,
119+ CompletableFuture <Void > completionFuture ,
120+ AtomicBoolean responded ) {
117121
118122 WorkerPool workerPool = invoker .getConfig ().getWorkerPoolObj ();
119-
120- if (null == workerPool ) {
121- logger .error ("dispatch rpcRequest [{}] error, workerPool is empty" , rpcRequest );
122- throw TRpcException .newFrameException (ErrorCode .TRPC_SERVER_NOSERVICE_ERR ,
123- "not found service, workerPool is empty" );
123+ if (workerPool == null ) {
124+ logger .error ("Worker pool is not available" );
125+ completionFuture .completeExceptionally (
126+ TRpcException .newFrameException (ErrorCode .TRPC_SERVER_NOSERVICE_ERR , "Worker pool not available" )
127+ );
128+ return ;
124129 }
125130
126131 workerPool .execute (() -> {
127-
128- // Get the original http response
129- HttpServletResponse response = getOriginalResponse (rpcRequest );
130-
131- // Invoke the routing implementation method to handle the request.
132- CompletionStage <Response > future = invoker .invoke (rpcRequest );
133- future .whenComplete ((result , t ) -> {
134- try {
135- if (isTimeout .get ()) {
136- return ;
132+ try {
133+ HttpServletResponse response = getOriginalResponse (rpcRequest );
134+
135+ CompletionStage <Response > rpcFuture = invoker .invoke (rpcRequest );
136+
137+ rpcFuture .whenComplete ((result , throwable ) -> {
138+ try {
139+ if (responded .get ()) {
140+ return ;
141+ }
142+
143+ if (throwable != null ) {
144+ throw throwable ;
145+ }
146+
147+ if (result .getException () != null ) {
148+ throw result .getException ();
149+ }
150+
151+ if (responded .compareAndSet (false , true )) {
152+ response .setStatus (HttpStatus .SC_OK );
153+ httpCodec .writeHttpResponse (response , result );
154+ response .flushBuffer ();
155+ }
156+
157+ completionFuture .complete (null );
158+ } catch (Throwable t ) {
159+ handleError (t , rpcRequest , response , responded , completionFuture );
137160 }
161+ });
138162
139- // Throw the call exception, which will be handled uniformly by the exception handling program.
140- if (t != null ) {
141- throw t ;
142- }
163+ } catch (Exception e ) {
164+ handleError (e , rpcRequest , getOriginalResponse (rpcRequest ), responded , completionFuture );
165+ }
166+ });
167+ }
143168
144- // Throw a business logic exception, which will be handled uniformly
145- // by the exception handling program.
146- Throwable ex = result .getException ();
147- if (ex != null ) {
148- throw ex ;
149- }
169+ /**
170+ * Handle error
171+ */
172+ private void handleError (Throwable t , DefRequest rpcRequest , HttpServletResponse response ,
173+ AtomicBoolean responded , CompletableFuture <Void > completionFuture ) {
174+ try {
175+ if (responded .compareAndSet (false , true )) {
176+ HttpServletRequest request = getOriginalRequest (rpcRequest );
177+ logger .warn ("Request processing failed: {}" , request .getRequestURI (), t );
178+ httpErrorReply (request , response ,
179+ ErrorResponse .create (request , HttpStatus .SC_INTERNAL_SERVER_ERROR , t ));
180+ }
181+ } finally {
182+ completionFuture .completeExceptionally (t );
183+ }
184+ }
150185
151- // normal response
152- response .setStatus (HttpStatus .SC_OK );
153- httpCodec .writeHttpResponse (response , result );
154- response .flushBuffer ();
155- } catch (Throwable e ) {
156- HttpServletRequest request = getOriginalRequest (rpcRequest );
157- logger .warn ("reply message error, channel: [{}], msg:[{}]" , request .getRemoteAddr (), request , e );
158- httpErrorReply (request , response ,
159- ErrorResponse .create (request , HttpStatus .SC_SERVICE_UNAVAILABLE , e ));
160- } finally {
161- countDownLatch .countDown ();
162- }
163- });
164- });
186+ /**
187+ * Send timeout response
188+ */
189+ private void sendTimeoutResponse (HttpServletRequest request , HttpServletResponse response ) {
190+ try {
191+ response .setStatus (HttpStatus .SC_GATEWAY_TIMEOUT );
192+ response .getWriter ().write ("Request Timeout" );
193+ response .flushBuffer ();
194+ logger .warn ("Request timeout: {} {}" , request .getMethod (), request .getRequestURI ());
195+ } catch (IOException e ) {
196+ logger .error ("Failed to send timeout response" , e );
197+ }
165198 }
166199
167200 /**
0 commit comments