在兩個不同的連接埠上執行 Tomcat 伺服器
1. 概述
Apache Tomcat 是一個 Web 伺服器和 Servlet 容器,用於部署和執行 Java Web 應用程式。預設情況下,Tomcat 監聽單一 HTTP 連接埠(例如 8080)來處理所有傳入請求。然而,在許多情況下,我們希望在多個連接埠上執行同一個 Tomcat 實例。
這種設定支援藍綠部署或金絲雀部署、平滑的連接埠遷移,以及獨立的管理或監控端點,從而實現更好的控制。
在本快速教學中,我們將了解如何使用一個 JVM 進程和一個部署在多個連接埠上執行單一 Tomcat 實例。
2. 在server.xml中新增多個<Connector>
讓我們來看看 Tomcat 的server.xml文件,其中定義了多個連接器來處理傳入的請求。這兩個Connectors共享同一個應用程式和內容:
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8081" protocol="HTTP/1.1" />
<Connector port="7081" protocol="HTTP/1.1" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="/tmp/tomcat-dummy" unpackWARs="false" autoDeploy="false">
<Context path="" docBase="STATIC_DIR_PLACEHOLDER" reloadable="false" />
</Host>
</Engine>
</Service>
</Server>
現在,我們將創建一個可以透過這兩個連接埠存取的小型應用程式。我們將使用Catalina API 載入上面自訂的server.xml文件,並將佔位符STATIC_DIR_PLACEHOLDER替換為我們靜態目錄的絕對路徑。
public Catalina startServer() throws Exception {
URL staticUrl = getClass().getClassLoader().getResource("static");
if (staticUrl == null) {
throw new IllegalStateException("Static directory not found in classpath");
}
Path staticDir = Paths.get(staticUrl.toURI());
Path baseDir = Paths.get("target/tomcat-base").toAbsolutePath();
Files.createDirectories(baseDir);
String config;
try (InputStream serverXmlStream = getClass().getClassLoader().getResourceAsStream("server.xml")) {
if (serverXmlStream == null) {
throw new IllegalStateException("server.xml not found in classpath");
}
config = new String(serverXmlStream.readAllBytes()).replace( "STATIC_DIR_PLACEHOLDER", staticDir.toString());
}
Path configFile = baseDir.resolve("server.xml");
Files.writeString(configFile, config);
System.setProperty("catalina.base", baseDir.toString());
System.setProperty("catalina.home", baseDir.toString());
Catalina catalina = new Catalina();
catalina.load(new String[]{"-config", configFile.toString()});
catalina.start();
System.out.println("\nTomcat started with multiple connectors!");
System.out.println("http://localhost:8081");
System.out.println("http://localhost:7081");
return catalina;
}
我們設定了所需的 Catalina 系統屬性,建立了一個Catalina實例,並使用自訂設定檔載入它。如上所示,此設定檔包含兩個位於不同連接埠的 HTTP 連接器。
現在我們可以透過連線到http://localhost:8081或http://localhost:7081:
3. 新增多個具有嵌入式 Tomcat 的連接器
讓我們來看看如何在不建立server.xml檔案的情況下,直接在程式碼中新增多個連接器。首先,我們建立一個Tomcat實例,並將其主連接埠設定為 7080。然後,我們建立一個新的Connector對象,將其連接埠設定為 8080,並使用addConnector()方法將其新增至 Tomcat 服務中,以啟用第二個連接埠:
public static void main(String[] args) throws Exception {
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir(new File("tomcat-temp").getAbsolutePath());
tomcat.setPort(7080);
tomcat.getConnector();
Connector secondConnector = new Connector();
secondConnector.setPort(8080);
tomcat.getService().addConnector(secondConnector);
Context ctx = tomcat.addContext("", new File(".").getAbsolutePath());
Tomcat.addServlet(ctx, "portServlet", new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int port = req.getLocalPort();
resp.setContentType("text/plain");
resp.getWriter().write("Port: " + port + "\n");
}
});
ctx.addServletMappingDecoded("/", "portServlet");
tomcat.start();
System.out.println("Tomcat running on ports 8080 and 7080");
tomcat.getServer().await();
}
我們沒有提供靜態文件,而是使用了一個內聯 servlet 來顯示接收請求的連接埠。我們已將其新增至上下文中,並將其對應到根路徑/.接下來,我們將造訪http://localhost:7080和http://localhost:8080 ,servlet 將顯示接收請求的連接埠號碼。當我們訪問這些 URL 時,我們將看到:
Port: 7080
Port: 8080
4. 使用網路級連接埠轉發
與其配置 Tomcat 監聽多個端口,不如設定作業系統層級的連接埠轉發,將來自不同連接埠的流量路由到單一 Tomcat 容器。
例如,假設 Tomcat 在本地運行於 8080 端口,但我們也想透過 7080 端口存取它。我們可以使用如下的 Linux 命令:
sudo iptables -t nat -A PREROUTING -p tcp --dport 7080 -j REDIRECT --to-port 8080
我們已配置 Linux 防火牆規則,在核心層級將流量從一個端口重定向到另一個端口,甚至在應用程式接收到請求之前就完成了這項操作。當 TCP 流量到達 7080 連接埠時,核心會攔截它並將其重定向到 8080 連接埠。因此,應用程式監聽的是 8080 連接埠並接收流量,而用戶端則認為它連接的是 7080 連接埠。
我們可以透過執行以下 Docker 命令快速嘗試這個範例:
docker run -it --rm --cap-add=NET_ADMIN -p 8080:80 -p 7080:7080 ubuntu:22.04 sh -c \
"apt-get update -qq && apt-get install -y -qq iptables nginx && \
service nginx start && \
iptables -t nat -A PREROUTING -p tcp --dport 7080 -j REDIRECT --to-port 80 && \
echo 'Nginx started on port 80' && \
echo 'iptables rule: 7080 → 80' && \
iptables -t nat -L -n -v && \
tail -f /dev/null"
同時,在另一個終端機中,我們可以執行curl http://localhost:7080來查看以下輸出:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
5. 結論
在本教程中,我們討論了在其他連接埠上執行 Tomcat 伺服器的不同方法。多個Connectors只需修改一個檔案即可。配置以 XML 格式存儲,方便追蹤。此外,由於 Tomcat 內建了此功能,重新啟動 Tomcat 會自動恢復所有連接器。
網路層連接埠轉送方案簡潔明了,無需對 Tomcat 進行任何更改,是容器化或雲端環境的理想選擇。
和往常一樣,程式碼可以在 GitHub 上找到。