Some time ago our game server development environment upgraded to Java8, these two days I again the server's threading model redesigned, with a lambda expression . Lambda expressions can really greatly simplify Java code, especially the ugliness of anonymous inner classes , and this article mainly wants to share this with you.
Threading model
Let's start with a brief introduction to the threading model of our game server, as shown in the following:
The netty thread pool only handles sending and receiving messages, and when Netty receives a message, it is handed over to the game logic thread . Since it is a single-threaded process for game logic, every message must be processed quickly, that is, there is no time-consuming operation like a database, or the logical thread is likely to get stuck. To not hold logical threads, database operations are handled by a separate thread pool. After the logical thread initiates the database operation, it returns to continue processing other messages, and then notifies the logical thread when the database thread pool has finished processing, thus achieving the effect of the asynchronous database operation.
Gameaction
Netty part of the network code, after the message is received, the corresponding action is found according to the message, and then executed. The specific code is omitted, the following is the simplified gameaction code:
Public abstract class Gameaction { /** * Processes messages in the logical line thread. * @param gs * @param req Request message * /public void execute (gamesession GS, Object req) { gamelogicexecutor.exec Ute ((), { Doexecute (GS, req); } Subclasses implement public abstract void Doexecute (gamesession gs, Object req); }
The Execute () method uses a lambda expression to implement the Runnable interface.
Gamelogicexecutor
Gamelogicexecutor is the game logic execution thread, and the code looks like this:
Import Java.util.concurrent.executor;import java.util.concurrent.executors;/** * game logic thread. */public class Gamelogicexecutor { //Todo limit queue Length private static final Executor Executor = Executors.newsinglethr Eadexecutor (); /** * Put the game logic in the queue. * @param gamelogic */public static void Execute (Runnable gamelogic) { executor.execute (gamelogic); } }
Getplayerlistaction
Here's a specific gameaction implementation, which returns the user-created list of players based on the user ID, with the following code:
Import Java.util.list;public class Getplayerlistaction extends Gameaction { @Override public void Doexecute ( Gamesession GS, Object req) { int userId = (Integer) req; Playerdao.getplayerlist (UserId, (list<player> players), { gs.write (players); }); }
Suppose the request parameter is the player Id,doexecute () method and does not wait for the database operation, but returns immediately. The callback object passed to DAO is a lambda expression, and in the callback method, the player list is written to the client via Gamesession (
This is just a demo, the actual response message may be protobuf or JSON)。
Playerdao
Import Java.util.arraylist;import Java.util.list;public class Playerdao {public static void getplayerlist (int UserId, dbopcallback<list<player>> CB) { Dbopexecutor.execute (), { try { list< Player> players = Getplayerlist (userId); Cb.ok (players); } catch (Exception e) { cb.fail (e); } }); Time-consuming database operation private static list<player> getplayerlist (int userId) { return new arraylist<> (); } }
The Getplayerlist () method receives two parameters, the first parameter is the user ID, the second parameter is a callback, and the callback is notified when the database operation is complete (success or failure). The internal use of DAO may be JDBC or mybatis, in short, a time-consuming operation, executed by the database thread pool.
Dbopexecutor
The code for Dbopexecutor is relatively simple, similar to Gamelogicexecutor, as follows:
Import Java.util.concurrent.executor;import java.util.concurrent.executors;/** * Database operation thread pool. */public class Dbopexecutor { //TODO determines the number of threads depending on the number of CPUs private static final Executor Executor = Executors.newfixedthr Eadpool (4); /** * Put database operations into the queue. * @param dbop * /public static void Execute (Runnable dbop) { executor.execute (Dbop); } }
Dbopcallback
Finally, take a look at the Dbopcallback code:
/** * Database Operation callback. * @param <T> * * @FunctionalInterfacepublic interface dbopcallback<t> { /** * Processing database returns results. * @param result * /void Handleresult (T result); /** * Database operation ended normally. * @param result * /default void ok (T result) { //thread processing result Gamelogicexecutor.execute (() in the game Logic line) c12/>try { Handleresult (result), } catch (Exception e) { //TODO handles exception } }); /** * Database operation exception occurred. * @param e * /default void fail (Exception e) { //TODO handles exception } }
@FunctionalInterface Note that this is a
function-type interface, simply put, is an interface with only an abstract method. interface of
Default MethodIs the new grammar of Java8, please refer to Java8 related information. The OK () method ensures that the result of the database operation is thread processing on the logical line.
Conclusion
The above code can be written with an anonymous inner class before Java8, but more verbose and ugly than a lambda expression. Also note that the above code is just the simplified example code , not the real code. If you want to apply the above code to your project, also note the handling of the exception.