Some time ago our game server upgrade to the development environment JAVA8, these days, I again the server's threading model was designed again, consuming lambda emoticons .
Lambdajava code. In particular, the ugly anonymous inner class , this article mainly wants to share this with you.
Threading model
Let's start by introducing the threading model of our game server. Roughly for example with what is seen:
The netty thread pool only handles the sending and receiving of messages when Netty receives the message. will be handed to the game logic threading . Because it is a single thread that handles game logic, each message must be processed very quickly. That is, you cannot have time-consuming operations such as databases. Otherwise, logical threads are 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. After the database thread pool processing is complete. The logical thread is then notified, 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 based on the message and then run.
The detailed code is omitted, and 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 that runs the thread, as seen in the following code:
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 is a detailed gameaction implementation that returns the user-created list of players based on the user ID. The code is as follows:
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); }); }
If the request parameter is a player ID. The Doexecute () method does not wait for the database operation. Instead, they returned 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 of the parameters. The first parameter is the user ID, and the second parameter is a callback when the database operation is complete (success or failure). Will inform the callback. DAO may be used internally, such as JDBC or mybatis, in short, time-consuming operations, run by the database thread pool.
Dbopexecutor
Dbopexecutor's code is simpler. Similar to Gamelogicexecutor, as seen in the following:
Import Java.util.concurrent.executor;import java.util.concurrent.executors;/** * Database operation thread pool. */public class Dbopexecutor { //TODO determines the number of threads based on 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 simply an interface with an abstract method. interface of
Default MethodIs the new grammar of Java8, please refer to JAVA8 related information in detail. The OK () method ensures that the result of the database operation is thread processing on the logical line.
Conclusion
The code above. It is also possible to write with an anonymous inner class before Java8, just to be more verbose and ugly than a lambda expression. Also note that the above code is just the simplified Demo sample code , not the real code.
Fake the above code to envision their own projects, but also pay attention to the exception handling.
Copyright notice: This article Bo Master original articles, blogs, without consent may not be reproduced.
Java8 lambda expression application--single-threaded game server+ Asynchronous database operations