To develop a high-performance tcpserver, it is important to familiarize yourself with the threading model of the framework you are using. MINA, Netty, and twisted are themselves high-performance network frameworks, assuming they are coupled with high-efficiency code. Ability to achieve a tall server.
But assume that you don't understand their threading model. It is very difficult to write high-performance code. The framework itself is much more efficient. If the program is poorly written, the overall performance of the server is not too high. Like a computer, the CPU is better. Memory small hard drive slow thermal poor, the overall performance is not too high.
The alumni who have played Android development know that there is a very important thread in the Android app: the UI thread (the main thread).
The UI thread is responsible for displaying the interface of an Android and interacting with the user. Some methods of activity. For example, OnCreate, OnStop, and OnDestroy are all running in the UI thread.
However, there is a need to be very careful when writing activity code. It is absolutely impossible to write blocked or time-consuming tasks in these methods, assuming that they are written in these methods. The UI thread is blocked, causing the user interface to be unresponsive. The experience is very poor. So in Android development, time-consuming or clogged tasks can be done in separate threads.
Same in Mina, Netty, twisted, there is also a very important thread:IO thread .
Traditional bio implementation of the TCPServer, especially for TCP long connection, usually to open a thread for each connection, the thread is also a resource of the operating system, so it is very difficult to achieve high-performance concurrency.
Asynchronous IO implementations are tcpserver because IO operations are asynchronous and can handle a large number of connected IO operations with a single thread or a small number of threads. Therefore, a small number of IO threads is required to enable high concurrency of the server.
In the process of network programming, there are usually some business logic that is more time-consuming and clogged, such as database operation, assuming bad network, and poor database performance. SQL is not optimized enough, the amount of data is large, and a SQL may run for very long.
Because the number of IO threads itself is not large, there is usually only one or a few, and it is assumed that such a time-consuming block of code runs in the IO thread. Other things like the IO thread, such as network Read and write. cannot be done, it affects IO performance and the performance of the entire server.
So, whether it's using Mina, Netty, Twisted, suppose there's a time-consuming task. It is absolutely not possible to run in an IO thread, but to open the thread to handle it.
MINA:
In Mina, there are three very important threads: acceptor thread, Connector thread, I/O processor thread.
The following is an introduction to the official documentation:
in MINA, there is three kinds of I/O worker threads in the NIO socket implementation.
acceptor Thread accepts incoming connections, and forwards the connection to the I/O processor thread For read and write operations.
Each socketacceptor creates one acceptor thread. You can ' t configure the number of the acceptor threads.
Connector Thread attempts connections to a remote peer, and forwards the succeeded connection to the I/O processor thread for read and write operations.
Each socketconnector creates one connector thread. You can ' t configure the number of the connector threads, either.
I/O processor thread performs the actual read and write operation until the connection is closed.
Each socketacceptor or socketconnector creates its own I/O processor thread (s). You can configure the number of the I/O processor threads. The default maximum number of the I/O processor threads is the number of the CPU cores + 1.
Acceptor Thread:
This thread is used by TCPServer to receive new connections. The connection is assigned to the I/O processor thread, and the IO operation is handled by the I/O processor thread. Each niosocketacceptor creates a acceptor thread, and the number of threads is not configurable.
Connector Thread:
Used to process the TcpClient connection to the server and assign the connection to the I/O processor thread. The IO operation is handled by the I/O processor thread. Each niosocketconnector creates a connector thread, and the number of threads is not configurable.
I/O processor thread:
An I/O operation to handle a TCP connection. such as read, write. The number of threads for I/O processor thread can be configured by Niosocketacceptor or Niosocketconnector construction method, which defaults to +1 of CPU cores.
Because this article mainly introduces TCPServer threading model, so there is no connector thread what matter. The following is the process of acceptor thread and I/O processor thread handling TCP connections:
Mina's tcpserver includes a acceptor thread and multiple I/O processor thread, when a new client is connected to the server, the connection is first acquired by the acceptor thread. At the same time assigning this connection to a thread in multiple I/O processor thread, when the client sends data to the server, the corresponding I/O processor thread is responsible for reading this data. and run the Iofilter and Iohandle in Iofilterchain.
Because the I/O processor thread itself is limited in number, it is usually a few, but it also handles thousands of connected IO operations, including read, write, encoding and decoding of the Protocol, various filter and business logic in Iohandle, especially business logic, For example Iohandle's messagereceived. Assuming that there are time-consuming, blocking tasks, such as querying the database, the I/O processor thread is blocked, resulting in the inability to handle other IO events in a timely manner and server performance degradation.
In response to this problem, Mina provides a executorfilterfor putting the business logic that would have to run for a very long time to clog the I/O processor thread into another thread. This does not clog the I/O processor thread and does not affect IO operations. The executorfilter includes a thread pool, which, by default, is Orderedthreadpoolexecutor, which guarantees that multiple events of the same connection run sequentially, It is also possible to use Unorderedthreadpoolexecutor, which does not guarantee the order in which events of the same connection are run and may run concurrently.
Between the two can be selected according to need.
public class TCPServer {public static void main (string[] args) throws IOException {Ioacceptor acceptor = new Niosocketacce Ptor (4); Configure the I/O processor thread number Acceptor.getfilterchain (). AddLast ("Codec", New Protocolcodecfilter (new Textlinecodecfactory ())); Acceptor.getfilterchain (). AddLast ("executor", New Executorfilter ()); Run Acceptor.sethandler (new Tcpserverhandle ()) in the thread pool where the business logic in Tcpserverhandle gets Executorfilter, and Acceptor.bind (new Inetsocketaddress (8080));}} Class Tcpserverhandle extends Iohandleradapter {@Overridepublic void messagereceived (iosession session, Object message) Throws Exception {//Suppose there's a perverted SQL to run for 3 seconds Thread.Sleep (3000);}}
Netty:
Netty when the TCPServer is started. Two nioeventloopgroup will be created. A boss, a worker:
Eventloopgroup Bossgroup = new Nioeventloopgroup (); Eventloopgroup Workergroup = new Nioeventloopgroup ();
Nioeventloopgroup is actually a thread group. Ability to set the number of threads by constructing methods. The CPU core number is the default.
The boss is used by the server to receive a new TCP connection, and the boss thread receives a new connection and joins the enrollment to the worker thread. The worker thread is used to process IO operations, such as read, write.
The boss thread in Netty is similar to the acceptor thread,work thread of Mina and Mina I/O processor threads.
The difference is that Mina's acceptor thread is a single thread, and Netty's boss is a thread group. In fact, Netty Serverbootstrap can listen to multiple port numbers, assuming that only a single port number, then only need a boss thread, it is recommended to set the number of bossgroup threads to 1.
Eventloopgroup Bossgroup = new Nioeventloopgroup (1);
When a new tcpclient is connected to the server, the connection is received by the boss thread. The connection is then registered to the worker thread. When the client sends the data to the server. The worker thread is responsible for receiving data and running Channelhandler in Channelpipeline.
Similar to Mina I/O processor thread. The number of worker threads in Netty is not large. and to handle IO events in real time, it is assumed that time-consuming business logic blocks the worker thread, such as running a time-consuming database query in Channelread, which causes the IO operation to fail. Overall server performance will be degraded.
In Netty 3. There is a executionhandler, which is an implementation class for Channelhandler that handles time-consuming business logic, similar to Mina's executorfilter. But it was removed in Netty 4. So there is no more introduction to Executionhandler.
Netty 4 can use Eventexecutorgroup to handle time-consuming business logic:
public class TCPServer {public static void main (string[] args) throws Interruptedexception {Eventloopgroup Bossgroup = new Nioeventloopgroup (1); Server listens on a port number, the boss thread number is recommended to set to 1EventLoopGroup Workergroup = new Nioeventloopgroup (4); The number of worker threads is set to 4try {serverbootstrap b = new Serverbootstrap (); B.group (Bossgroup, Workergroup). Channel ( Nioserversocketchannel.class). Childhandler (New channelinitializer<socketchannel> () {// Create a thread group of 16 threads to handle time-consuming business logic private Eventexecutorgroup group = new Defaulteventexecutorgroup; @Overridepublic void Initchannel (Socketchannel ch) throws Exception {Channelpipeline pipeline = Ch.pipeline ();p ipeline.addlast (new Linebasedframedecoder);p ipeline.addlast (New Stringdecoder (Charsetutil.utf_8));// Put the business logic in Tcpserverhandler into the Eventexecutorgroup thread group to run Pipeline.addlast (group, New Tcpserverhandler ());}); Channelfuture f = b.bind (8080). sync (); F.channel (). Closefuture (). sync (); finally {workergroup.shutdowngracefully (); bossgroup.shutdowngracefully ();}}} Class TcpserverhAndler extends Channelinboundhandleradapter {@Overridepublic void Channelread (Channelhandlercontext ctx, Object msg) Throws Interruptedexception {//Suppose there's a perverted SQL to run for 3 seconds Thread.Sleep (3000);}}
Twisted:
The threading model of twisted is the simplest and most brutal: single threaded, or reactor thread . That is, all IO operations, encoding and decoding, business logic, and so on, are all running in one thread.
In fact, even a single thread. Its performance is also very high. Able to handle a large number of connections at the same time. Programming in a single-threaded environment. There is no need to consider thread safety issues.
It's just that. A single thread brings a problem, that is, time-consuming business logic, assuming that the reactor thread is running, then other things, such as network IO, will wait until the reactor thread spare to continue, affecting the performance of the server.
The following code, which runs time-consuming business logic into a separate thread pool through reactor.callinthread , does not run in the reactor thread. This will not affect the network IO of the reactor thread.
Ability to set the number of threads for this thread pool through reactor.suggestthreadpoolsize .
#-*-Coding:utf-8–*-import timefrom twisted.internet.protocol import protocolfrom twisted.internet.protocol Import Factoryfrom twisted.internet Import reactor# time-consuming, clogged business logic def logic (data): print Data time.sleep (3) # Suppose there's a perverted SQL to run for 3 seconds class Tcpserverhandle (Protocol): def datareceived (self, data): Reactor.callinthread (Logic, data) # runs logic (data) time-consuming tasks in a thread pool. Not running Reactor.suggestthreadpoolsize (8) # in a reactor thread the number of threads that set the thread pool is 8factory = Factory () Factory.protocol = TCPSERVERHANDLEREACTOR.LISTENTCP (8080, Factory) Reactor.run ()
Because of the twisted's reactor single-threaded design. Many of its code is not thread-safe.
So code that runs in a non-reactor thread needs to be aware of thread safety issues.
For example, Transport.write is not thread-safe. It is only possible to invoke the Reactor.callfromthread method in a non-reactor thread, which, instead of callinthread, runs a function from another thread into the reactor thread. Just keep in mind that the function called by Reactor.callfromthread is running in the reactor thread. Assume that the operation is time consuming. The same will clog the reactor thread, affecting IO.
#-*-Coding:utf-8–*-import timefrom twisted.internet.protocol import protocolfrom twisted.internet.protocol Import Factoryfrom twisted.internet Import reactor# Non-thread-safe Code def notthreadsafe (): print "Notthreadsafe" # time-consuming, clogged business logic def Logic (data): print Data time.sleep (3) # Suppose there's a perverted SQL to run 3 seconds Reactor.callfromthread (notthreadsafe) # Run Notthreadsafe () class Tcpserverhandle (Protocol) in the reactor thread: def datareceived (self, data): Reactor.callinthread (logic, data) # Running logic (data) time-consuming task in a thread pool, not running Reactor.suggestthreadpoolsize (8) in Reactor threads # The number of threads that set the thread pool is 8factory = Factory () Factory.protocol = tcpserverhandlereactor.listentcp (8080, Factory) Reactor.run ()
In addition Many of the most convenient functions are available in the twisted.internet.threads. Like what
Threads.defertothreadUsed to run a time-consuming task in a thread pool. Unlike the Reactor.callinthread, it is. Its return value is the deferred type, capable of processing the result (return value) of the time-consuming task by adding a callback function.
#-*-Coding:utf-8–*-import timefrom twisted.internet.protocol import protocolfrom twisted.internet.protocol Import Factoryfrom twisted.internet Import reactor, threads# time-consuming, clogged business logic def logic (data): print Data time.sleep (3) # Let's say there's a pervert. SQL to run 3 seconds return "Success" # callback function def logicsuccess (result): # result is the return value of the logic function, which is "success" Print ResultClass Tcpserverhandle (Protocol): def datareceived (self, data): D = threads.defertothread (logic, Data) # Put the time-consuming business logic (data) into the thread pool to run. Defertothread return value type is deferred d.addcallback (logicsuccess) # Join callback function Reactor.suggestthreadpoolsize (8) # The number of threads that set the thread pool is 8factory = Factory () Factory.protocol = tcpserverhandlereactor.listentcp (8080, Factory) Reactor.run ()
Fork Brother reproduced please indicate the source: http://blog.csdn.net/xiao__gui/article/details/40146351
MINA, Netty, twisted together study series
MINA, Netty, twisted learn Together (i): Achieve a simple tcpserver
mina, Netty, twisted Together (ii): TCP message boundary problems and cut messages by row
mina, Netty, Twisted learn Together (iii): TCP message fixed-size prefix (Header)
mina, Netty, Twisted learn Together (iv): Customize your own protocol
mina, Netty, twisted Study Together (v): Integration Protobuf
mina, Netty, twisted study Together (VI): Session
mina, Netty, twisted Together (vii): Announcement/Subscription (Publish/subscribe)
mina, Netty, Twisted learn together (eight): Httpserver
mina, Netty, Twisted learn Together (ix): Asynchronous IO and callback functions
MINA, Netty, Twisted learn together (10): Threading model
MINA, Netty, Twisted learn together (11): SSL/TLS
MINA, Netty, Twisted learn together (12): HTTPS
Source
https://github.com/wucao/mina-netty-twisted
Mina, Netty, Twisted learn together (10): Threading model