Singlethreaded Server trong Java
Singlethreaded server hay server đơn luồng là một kiểu thiết kế server chạy trên một luồng duy nhất. Để phân biệt các kiểu server này, các bạn có thể quay về bài viết trước tại đây. Bài viết này sẽ giới thiệu về cách hiện thực một server đơn luồng trong Java.
Singlethreaded server
Server đơn luồng tuy chưa phải là một thiết kế tốt nhất cho một server trong Java, nhưng nó minh hoạ cho ta một cách rõ ràng về cách server nhận và xử lý request như thế nào. Các kiểu server tối ưu hơn như multithreaded hay threadpool cũng dựa trên tư tưởng của singlethreađed.
Vòng đời của singlethreaded server
Có thể hình dung vòng đời hay vòng lặp của server qua 3 bước sau:
Waiting
: chờ request từ clientProcessing
: xử lý request từ client khi có request tớiRepeating
: lặp lại quá trình trên
Khá đơn giản đúng không nào!!
Quá trình 3 bước trên gần như giống với các kiểu server khác trong Java. Sự khác biệt giữa singlethreaded
và multithreaded
là singledthreaded sử dụng chính luồng nhận request để xử lý, trong khi multithreaded sử dụng các thread khác (worker thread) để xử lý. Việc xử lý request đến từ client trên cùng một thread sẽ không hiệu quả, vì khi đó client chỉ có thể connect khi thread đó đang ở trạng thái waiting, nếu không thì client connnection sẽ không được thiết lập. Multithreaded sử dụng các worker thread để xử lý, giúp client connection có thể thiết lập được.
Hiện thực một singlethreaded server
Dưới đây là một hiện thực đơn giản cho singlethreaded server, xem mã nguồn ví dụ tại đây.
/*
* License from https://github.com/wearenodev
*/
package wearenodev.java.examples.thread;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
* @author harisk
*/
public class SingleThreadedServer implements Runnable {
private ServerSocket serverSocket;
private final int serverPort;
private boolean isStopped = false;
public SingleThreadedServer(int port) {
this.serverPort = port;
}
private void processClientRequest(Socket clientSocket) throws Exception {
InputStream input = clientSocket.getInputStream();
OutputStream output = clientSocket.getOutputStream();
long time = System.currentTimeMillis();
byte[] responseDocument = ("<html><body>"
+ "SingleThreadedServer time: "
+ time
+ "</body></html>").getBytes("UTF-8");
byte[] responseHeader = ("HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html; charset=UTF-8\r\n"
+ "Content-Length: " + responseDocument.length
+ "\r\n\r\n").getBytes("UTF-8");
output.write(responseHeader);
output.write(responseDocument);
output.close();
input.close();
System.out.println("Request processed: " + time);
}
private synchronized boolean isStopped() {
return this.isStopped;
}
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
} catch (IOException e) {
throw new RuntimeException("Error on stop server", e);
}
}
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
System.out.println("Server is running on port " + this.serverPort);
} catch (IOException e) {
throw new RuntimeException("Cannot open port " + this.serverPort, e);
}
}
@Override
public void run() {
openServerSocket();
while (!isStopped()) {
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
} catch (IOException e) {
if (isStopped()) {
System.out.println("Server stopped");
return;
}
throw new RuntimeException("Error on accept client connection", e);
}
try {
processClientRequest(clientSocket);
} catch (Exception ex) {
ex.printStackTrace();
}
}
System.out.println("Server stopped");
}
public static void main(String[] args) {
SingleThreadedServer server = new SingleThreadedServer(8080);
/**
* Run server in single thread
*/
new Thread(server).start();
/**
* Waiting 20s before stopping server
*/
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
/**
* Stop server and exit processR
*/
server.stop();
System.exit(0);
}
}
Trong ví dụ ở trên, chúng ta tạo một class SingleThreadedServer
implement Runnable
để có thể thực thi trong một Thread
của Java (có thể hiểu Thread
trong Java khi thực thi cần một Runnable class
để chạy). Ta tạo một thread duy nhất trong process chính để thực thi server này như trong hàm main như sau:
/// ...
public static void main(String[] args) {
SingleThreadedServer server = new SingleThreadedServer(8080);
/**
* Run server in single thread
*/
new Thread(server).start();
/**
* Waiting 20s before stopping server
*/
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
/**
* Stop server and exit processR
*/
server.stop();
System.exit(0);
}
/// ...
Trong hàm main này, ta start server trên port 8080
và cho thực thi trong 20s
, sau đó thực hiện stop server và stop process chính.
Trong class SingleThreadedServer
được hiện thực, ta ghi đè (override
) method run()
của interface Runnable
như sau:
/// ...
@Override
public void run() {
openServerSocket();
while (!isStopped()) {
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
} catch (IOException e) {
if (isStopped()) {
System.out.println("Server stopped");
return;
}
throw new RuntimeException("Error on accept client connection", e);
}
try {
processClientRequest(clientSocket);
} catch (Exception ex) {
ex.printStackTrace();
}
}
System.out.println("Server stopped");
}
/// ...
Ban đầu, ta cần mở port 8080
cho server. Thực hiện vòng lặp kiểm tra xem cờ isStopped
của server. Trong từng bước lặp, ta chờ nhận socket từ client, khi có connection thì xử lý request đó.
Đây chỉ là một ví dụ đơn giản hiện thực một singlethreaded server. Trong thực tế chúng ta ít khi sử dụng kiểu thiết kế này vì những nhược điểm và hạn chế của nó. Việc sử dụng đơn luồng khiến server bị block khi đang xử lý một connection nào đó. Do đó multithreaded hay threadpool chính là những kiểu thiết kế tốt hơn, giải quyết vấn đề này của singlethreaded. Các bạn có thể tìm hiểu thêm về các kiểu thiết kế này trong chuỗi bài viết của mình.
Ngoài ra Java cũng có rất nhiều framework cho web server, các bạn cũng có thể tham khảo ở bài viết này.
Tìm hiểu thêm về: