Configure connection draining
When you remove a backend server or it fails a health check, its established connections are not immediately terminated. Abruptly closing these connections can disrupt in-flight requests and prevent services from shutting down gracefully. To avoid this issue, you can use the connection draining feature of Application Load Balancer (ALB). When a backend server is removed or fails a health check, connection draining keeps existing connections active for a configured period. After the timeout expires, ALB closes the connections to ensure a graceful shutdown.
Use cases
Connection draining applies to the following two scenarios.
Removing a backend server: Before you remove a backend server, set a longer connection draining timeout to allow in-flight requests to complete.
Health check failure: For a backend server that fails a health check, set a shorter timeout to quickly terminate faulty connections and prevent clients from receiving connection errors.
Because both scenarios share the same connection draining configuration, you must set a timeout that suits your business requirements.
Remove backend server
The following figure shows an example. When you remove the backend server ECS01, ALB stops sending new requests to it. ECS01 then processes only in-flight requests and does not accept new requests.
If connection draining is disabled, ECS01 closes the session only after all in-flight requests are processed.
If connection draining is enabled and a timeout is set:
If the removed backend server ECS01 has in-flight requests, ALB closes the existing sessions on ECS01 after the connection draining timeout expires.
If the removed backend server ECS01 has no in-flight requests or active connections, ALB immediately completes the removal process without waiting for the connection draining timeout to expire.
If the removed backend server ECS01 is processing a request when the removal process ends, the connection is terminated and the client receives a 500 error response. For example, if the connection draining timeout is set to 15 seconds but the request processing time on ECS01 is 30 seconds, the connection is terminated before ECS01 can send a response. In this case, the client receives a 500 error response.
NoteIf you re-add ECS01 to the server group, this does not affect any sessions that are already draining. ECS01 remains in the draining state—processing only in-flight requests—until the existing sessions are closed. ALB closes these sessions when the connection draining timeout expires.
The following figure shows the state transitions of ECS01 after it is removed from a server group with connection draining enabled.
Health check failure
When the backend server ECS01 fails a health check, ALB stops sending new requests to it. ECS01 then processes only in-flight requests and does not accept new requests.
If connection draining is disabled, ECS01 remains in this state until it passes the health check. After it passes, it starts to accept new requests.
If connection draining is enabled and a timeout is set:
ALB closes the existing sessions on ECS01 after the connection draining timeout expires.
If you update the server group (for example, by modifying the configuration of ECS01), its connection state does not change; it continues to process only in-flight requests. Even if ECS01 passes a health check after the update, ALB still closes its existing sessions when the connection draining timeout expires.
NoteAfter the connection draining timeout expires and ALB closes the sessions, ECS01 can accept new requests only if it passes the health check. If it continues to fail, it will not accept new requests.
Connection draining is triggered only by health check failures caused by backend service issues, not by failures resulting from configuration updates.
The following figure shows the state transitions of ECS01 after it fails a health check.
You can configure connection draining based on your business scenario. This topic uses Scenario 1: Remove a backend server as an example to demonstrate how to configure the termination of WebSocket and HTTP sessions.
Usage notes
Only Standard and WAF-enabled ALB instances support connection draining, while Basic ALB instances do not.
Function Compute server groups do not support connection draining.
Connection draining is effective for both WebSocket and HTTP sessions. For HTTP sessions, we recommend setting the connection draining timeout to a value greater than the ALB connection request timeout to prevent requests from being closed unexpectedly. The default timeout values already follow this recommendation. For information about how to set the connection request timeout, see Add an HTTP listener.
Prerequisites
A Standard or WAF-enabled ALB instance has been created, and a server group of the Server type has been created for the ALB instance. This topic uses a Standard ALB instance as an example. For more information, see Create an ALB instance and Create and manage a server group.
You have configured an HTTP listener on port
80for the ALB instance and associated it with the server group. For more information, see Add an HTTP listener.You have created two backend servers, ECS01 and ECS02. For more information, see Create an instance by using the wizard.
You have added ECS02 to the server group, and it is accessible from the client. For more information, see Use ALB to achieve load balancing for IPv4 services and Use ALB to achieve load balancing for IPv6 services.
NoteThis topic uses an Alibaba Cloud Linux 3.2104 64-bit operating system as the client. Make sure that Python is installed on both your client and the backend server ECS01. If Python is not installed, see the official Python website for instructions. This topic uses Python 3.x as an example.
In this topic, ECS02 is the backend server that serves production traffic. If you already have such a backend server, you do not need to create one.
Procedure
After you enable connection draining and set a timeout period, this topic demonstrates how ALB handles requests for WebSocket and HTTP sessions in different connection draining states.
WebSocket sessions
Step 1: Enable connection draining
A server group was created as a prerequisite. This step shows how to enable connection draining for an existing server group. You can also enable this feature when you create one.
Log on to the ALB console.
In the top navigation bar, select the region where the server group is deployed.
In the left-side navigation pane, choose ALB > Server Groups.
On the Server Groups page, find the target server group and click its ID.
On the Details tab, in the Basic Information section, click Modify Basic Information.
In the Modify Basic Information dialog box, click Advanced Settings and enable Connection Draining.
Set Timeout Period to 300 seconds and click Save.
Step 2: Verify the results
Configure the server
Log on to the ECS01 instance. For more information, see Connection methods.
Run the following commands to create and enter a WebSocket directory:
mkdir WebSocket cd WebSocketRun the following command to install dependencies:
pip install tornado pip install websocket-clientRun the following command to edit the server.py configuration file:
vim server.pyPress
ito enter edit mode and add the following configurations to start a WebSocket service:#!/usr/bin/env python3 # encoding=utf-8 import tornado.websocket import tornado.ioloop import tornado.web from datetime import datetime # WebSocket handler class WebSocketHandler(tornado.websocket.WebSocketHandler): def open(self): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "WebSocket connection opened") def on_message(self, message): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "Received message:", message) self.write_message("Server received your message: " + message) def on_close(self): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "WebSocket connection closed") # Routing application = tornado.web.Application([ (r"/websocket", WebSocketHandler), ]) if __name__ == "__main__": print("WebSocket Server Start on 8080 ...") application.listen(8080) tornado.ioloop.IOLoop.current().start()After you modify the configuration file, press the
Esckey, enter:wq, and then press Enter to save and close the file.
Go to the directory where server.py is stored and run the following command to start the WebSocket service:
python3 server.pyThe following response indicates that the WebSocket backend service is started.
Websocket Server Start on 8080 ...
Add ECS01 to the server group
Log on to the ALB console.
In the top navigation bar, select the region where the server group is deployed.
In the left-side navigation pane, choose ALB > Server Groups.
On the Server Groups page, find the target server group, and click Modify Backend Server in the Actions column.
On the Backend Servers tab, click Add Backend Server. In the Add Backend Server panel, select ECS01 and click Next.
In the Ports/Weights step, select the ECS01 instance, set the port to
8080, and then click OK.
Configure the client
Log on to the client and open the command-line interface (CLI). Run the following commands to create and enter a WebSocket directory:
mkdir WebSocket cd WebSocketRun the following command to install dependencies:
pip install websocket-clientRun the following command to edit the client.py file:
vim client.pyPress
ito enter edit mode and add the following configurations to start a WebSocket client to access the service:#!/usr/bin/env python3 # encoding=utf-8 import websocket import time from datetime import datetime def on_message(ws, message): print("Received message from server:", message) if __name__ == "__main__": ws = websocket.WebSocket() ws.connect("ws://<domain_name>:80/websocket") # Replace <domain_name> with your actual domain name. print("WebSocket connection opened") try: while True: current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Sending time:", formatted_time) ws.send("Hello, Server!") result = ws.recv() on_message(ws, result) time.sleep(1) except Exception: print("WebSocket connection closed")After you modify the configuration file, press the
Esckey, enter:wq, and then press Enter to save and close the file.
Go to the directory where client.py is stored and run the following command to access the ECS01 backend server:
python3 client.pyThe following response indicates that the access is successful.
WebSocket connection opened Sending time: 2024-04-28 17:00:53 Received message from server: Server received your message: Hello, Server! Sending time: 2024-04-28 17:00:54 Received message from server: Server received your message: Hello, Server!The ECS01 backend server returns the following response.
WebSocket Server Start on 8080 ... Time: 2024-04-28 17:00:53 WebSocket connection opened Time: 2024-04-28 17:00:53 Received message: Hello, Server! Time: 2024-04-28 17:00:54 Received message: Hello, Server!
Remove the backend server
Set the connection draining timeout before you remove the backend server.
Log on to the ALB console.
In the top navigation bar, select the region where the server group is deployed.
In the left-side navigation pane, choose ALB > Server Groups.
Find the target server group and click its ID.
Click the Backend Servers tab, find the target backend server ECS01, and then click Remove in the Actions column.
In the Remove dialog box, click OK.
Wait for connection draining
The Timeout Period is set to 300 seconds. The test results show that ALB terminates the session about 300 seconds after ECS01 is removed.
In the test results, the time difference between when the WebSocket connection on the ECS01 server is opened and closed is 330 seconds. The connection draining timeout period, which is the duration from when the backend server ECS01 is removed to when the WebSocket connection is closed, is about 300 seconds.
The client returns the following response.
Sending time: 2024-04-28 17:06:23 Received message from server: Server received your message: Hello, Server! Sending time: 2024-04-28 17:06:24 WebSocket connection closedThe ECS01 server instance returns the following response.
Time: 2024-04-28 17:06:22 Received message: Hello, Server! Time: 2024-04-28 17:06:23 Received message: Hello, Server! Time: 2024-04-28 17:06:23 WebSocket connection closed
HTTP sessions
In HTTP scenarios, the response that the client receives varies based on the settings of connection draining timeout, connection request timeout, and backend server processing time.
If the connection draining timeout is shorter than the backend server processing time, the connection is interrupted before ECS01 sends a response. In this case, the client receives a 500 error response.
If the backend server processing time is longer than the connection request timeout, the request to ECS01 times out. In this case, the client receives a 504 error response.
This topic uses a connection draining timeout of 15 seconds and a backend server processing time of 30 seconds as an example. In this scenario, the connection is interrupted before ECS01 finishes sending the response, and the client receives a 500 error response.
A 504 error is not returned because the connection request timeout (60 seconds, set in Step 2) is longer than the backend server processing time (30 seconds). Instead, a 500 error occurs because the connection draining timeout (15 seconds) is shorter than the server's processing time.
This topic uses the
time.sleepfunction in the Python code to simulate the backend server processing time.
Step 1: Enable connection draining
A server group was created as a prerequisite. This step shows how to enable connection draining for an existing server group. You can also enable this feature when you create one.
Log on to the ALB console.
In the top navigation bar, select the region where the server group is deployed.
In the left-side navigation pane, choose ALB > Server Groups.
On the Server Groups page, find the target server group and click its ID.
On the Details tab, in the Basic Information section, click Modify Basic Information.
In the Modify Basic Information dialog box, click Advanced Settings and enable Connection Draining.
Set Timeout Period to 15 seconds and click Save.
Step 2: Set ALB request timeout
Log on to the ALB console.
In the top navigation bar, select the region where the ALB instance is deployed.
On the Instances page, find the target ALB instance and click its ID.
Click the Listener tab, find the target HTTP listener, and then click its ID.
In the Basic Information area, click Modify Listener.
In the Modify Listener dialog box, click Modify next to Advanced Settings.
Set Connection Request Timeout to 60 seconds (the default value) and click Save.
Step 3: Configure DNS resolution
In a production environment, use your own domain name and point it to the DNS name of the ALB instance by creating a CNAME record.
-
In the left navigation pane, choose .
-
On the Instances page, copy the DNS name of your ALB instance.
-
Add a CNAME record to map your domain name to the DNS name of the ALB instance.
NoteIf your domain name is not registered with Alibaba Cloud, you must first add it to the Alibaba Cloud DNS console before you can configure DNS records. For more information, see Domain Name Management. If your domain name is registered with Alibaba Cloud, you can proceed to the next steps.
-
Log on to the Alibaba Cloud DNS console.
-
On the Authoritative DNS Resolution page, find your domain name and click Settings in the Operations column.
-
On the Settings page, click Add Record.
-
In the Add Record panel, configure the following parameters to create the CNAME record, and then click OK.
Configuration
Description
Record Type
Select CNAME from the drop-down list.
Hostname
The prefix for your domain name. This tutorial uses @.
NoteTo use the root domain, set the host to
@.Query Source
Select Default.
Record Value
Paste the copied DNS name of the ALB instance.
TTL
The Time to Live (TTL) is the amount of time the record is cached on a DNS server. This tutorial uses the default value.
-
Step 4: Verify the results
Configure the server
Log on to the ECS01 instance. For more information, see Connection methods.
Run the following commands to create and enter an HTTP directory:
mkdir http cd httpRun the following command to edit the http_server.py configuration file:
vim http_server.pyPress
ito enter edit mode and add the following configurations to start an HTTP server service:#!/usr/bin/env python3 # encoding=utf-8 from http.server import SimpleHTTPRequestHandler, HTTPServer from datetime import datetime import time class DelayedHTTPRequestHandler(SimpleHTTPRequestHandler): def do_GET(self): current_time = datetime.now() formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") print("Time:", formatted_time, "GET request received. Responding after a 30-second delay....") time.sleep(30) # Use the time.sleep function to simulate the processing time of the backend server. SimpleHTTPRequestHandler.do_GET(self) PORT = 8080 server = HTTPServer(("", PORT), DelayedHTTPRequestHandler) print(f"Serving HTTP on 0.0.0.0 port {PORT} (http://0.0.0.0:{PORT}/) ...") server.serve_forever()After you modify the configuration file, press the
Esckey, enter:wq, and then press Enter to save and close the file.
Go to the directory where http_server.py is stored and run the following command to start the HTTP server service:
python3 http_server.pyThe following response indicates that the HTTP server backend service is started.
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
Add ECS01 to the server group
Log on to the ALB console.
In the top navigation bar, select the region where the server group is deployed.
On the Server Groups page, find the target server group, and click Modify Backend Server in the Actions column.
On the Backend Servers tab, click Add Backend Server. In the Add Backend Server panel, select ECS01 and click Next.
In the Ports/Weights step, select the ECS01 instance, set the port to
8080, and then click OK.
Configure the client
Log on to the client and open the CLI. Run the following command to access the backend server ECS01:
curl http://<domain_name>:80/ -vThe following response indicates that ALB can access the backend service.
* About to connect() to www.example.com port 80 (#0) * Trying 10.X.X.225... * Connected to www.example.com (10.X.X.225) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.29.0 > Host: www.example.com > Accept: */*The server returns the following response.
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ... Time: 2024-02-07 13:57:33 Received a GET request. Responding after a 30-second delay....
Remove the backend server
Set the connection draining timeout before you remove the backend server.
Log on to the ALB console.
In the top navigation bar, select the region where the server group is deployed.
In the left-side navigation pane, choose ALB > Server Groups.
Find the target server group and click its ID.
Click the Backend Servers tab, find the target backend server ECS01, and then click Remove in the Actions column.
In the Remove dialog box, click OK.
Wait for connection draining
The test results show that the client receives a 500 error response when the connection draining timeout set for ALB is shorter than the backend server processing time.
* About to connect() to www.example.com port 80 (#0)
* Trying 10.X.X.224...
* Connected to www.example.com (10.XX.XX.224) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.example.com
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
< Date: Wed, 07 Feb 2024 06:02:24 GMT
< Content-Type: text/html
< Content-Length: 186
< Connection: close
< Via: HTTP/1.1 SLB.87
<
<html>
<head><title>500 Internal Server Error</title></head>
<body bgcolor="white">
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Closing connection 0Related documentation
To enable connection draining when you create a server group, see Create and manage a server group.
To gracefully roll out services, you can enable the slow start feature. For more information, see Configure slow start.
To learn more about using the WebSocket and HTTP protocols with ALB, see Add an HTTP listener and Use the WebSocket protocol with ALB to push real-time information.