We could use a traditional way to implement a server, let’s call it BioServer.
public class BioServer extends AbstractServer implements Server {
private ServerSocket serverSocket;
private final String host;
private final int port;
private final CommandReader commandReader;
private final AtomicBoolean stop;
private final RequestMeasureCollector requestMeasureCollector;
/**
* Init server with given host, port, and process.
*
* @param host host
* @param port port
* @param requestProcessor request processor
* @param type database type
*/
public BioServer(String host, int port, RequestProcessor requestProcessor, DatabaseType type) {
super(type, requestProcessor);
this.host = host;
this.port = port;
this.stop = new AtomicBoolean(false);
this.commandReader = new ClientCommandReader();
this.requestMeasureCollector = new RequestMeasureCollector("BIO");
}
/**
* Start the server.
* Process client request one by one.
*/
public void start() {
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(host, port));
System.out.println("Jimds is ready to accept requests on port " + port);
int times = 0;
while (!stop.get()) {
Socket socket = serverSocket.accept();
process(socket);
times++;
if (times % 100 == 0) {
times = 0;
requestMeasureCollector.print();
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
closeQuietly(serverSocket);
}
}
}
The process method is executed in current thread.
public class BioServer extends AbstractServer implements Server {
protected void process(Socket socket) {
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
MeasureData measureData = new MeasureData();
try {
measureData.add(RequestMeasureCollector.FIELD_NAME_CONNECTED);
inputStream = new BufferedInputStream(socket.getInputStream());
outputStream = new BufferedOutputStream(socket.getOutputStream());
DataReader dataReader = new StreamDataReader(inputStream, bufSize, MAX_COMMAND_LENGTH);
List<ByteWord> words = commandReader.read(dataReader);
measureData.add(RequestMeasureCollector.FIELD_NAME_READ);
Command command = CommandFactory.parseCommand(words);
measureData.add(RequestMeasureCollector.FIELD_NAME_PARSE);
NetworkData data = requestProcessor.process(database, command);
measureData.add(RequestMeasureCollector.FIELD_NAME_PROCESS);
data.write(new StreamDataWriter(outputStream));
outputStream.flush();
measureData.add(RequestMeasureCollector.FIELD_NAME_SEND);
} catch (JimdsException e) {
measureData.add(RequestMeasureCollector.FIELD_NAME_ERROR);
// internal exception, write to output
NetworkData data = new NetworkError(e.getMessage());
try {
data.write(new StreamDataWriter(outputStream));
} catch (IOException ex) {
e.printStackTrace();
}
} catch (Throwable e) {
measureData.add(RequestMeasureCollector.FIELD_NAME_ERROR);
// should only be thrown when writing data to output
// if input throws IOException, an error message will be written to output.
// throw new RuntimeException(e);
// ignore the message, and close socket
e.printStackTrace();
} finally {
requestMeasureCollector.add(measureData);
closeQuietly(inputStream);
closeQuietly(outputStream);
closeQuietly(socket);
}
}
}
In single thread mode, BioServer also has a good performance.
Here is the benchmark result for simple string command, small size refers to 1024 bytes, and testStringGetSet uses 10K values.
Benchmark Mode Cnt Score Error Units
BenchmarkTest.testStringGetSet thrpt 5 1804.792 ± 185.597 ops/s
BenchmarkTest.testStringGetSetSmallSize thrpt 5 4830.196 ± 98.344 ops/s
BenchmarkTest.testStringGetSet avgt 5 0.002 ± 0.001 s/op
BenchmarkTest.testStringGetSetSmallSize avgt 5 0.001 ± 0.001 s/op
BenchmarkTest.testStringGetSet sample 73744 0.003 ± 0.001 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.00 sample 0.002 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.50 sample 0.003 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.90 sample 0.003 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.95 sample 0.004 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.99 sample 0.005 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.999 sample 0.014 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p0.9999 sample 0.031 s/op
BenchmarkTest.testStringGetSet:testStringGetSet·p1.00 sample 0.073 s/op
BenchmarkTest.testStringGetSetSmallSize sample 203342 0.001 ± 0.001 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.00 sample 0.001 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.50 sample 0.001 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.90 sample 0.001 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.95 sample 0.002 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.99 sample 0.002 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.999 sample 0.003 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p0.9999 sample 0.021 s/op
BenchmarkTest.testStringGetSetSmallSize:testStringGetSetSmallSize·p1.00 sample 0.056 s/op
BenchmarkTest.testStringGetSet ss 0.016 s/op
BenchmarkTest.testStringGetSetSmallSize ss 0.006 s/op