[Netty] Fossil - Program Design in Netty Server
本文记录一些服务器代码架构中可能会遇到的代码设计问题,重在思考。
第一步,main()
当然,start from:
public static void main(String[] args) throws Exception { ... }
第二步,ServerSocketChannelFactory
ChannelFactory is a factory which creates and manages Channels and its related resources. It processes all I/O requests and performs I/O to generate ChannelEvents.
Netty provides various ChannelFactory implementations,
Here NioServerSocketChannelFactory was used.
It does not create I/O threads by itself.
It is supposed to acquire threads from the thread pool specified in the constructor.
A ServerSocketChannelFactory which creates a server-side NIO-based ServerSocketChannel.
It utilizes the non-blocking I/O mode which was introduced with NIO to serve many number of concurrent connections efficiently.
There are two types of threads in a NioServerSocketChannelFactory;
Boss threads:
Each bound ServerSocketChannel has its own boss thread. For example,
- If you opened two server ports such as 80 and 443, you will have two boss threads.
- A boss thread accepts incoming connections until the port is unbound.
- Once a connection is accepted successfully, the boss thread passes the accepted Channel to one of the worker threads that the NioServerSocketChannelFactory manages.
Worker threads:
One NioServerSocketChannelFactory can have one or more worker threads.
A worker thread performs non-blocking read and write for one or more Channels in a non-blocking mode.
ChannelFactory factory = new NioServerSocketChannelFactory ( Executors.newCachedThreadPool(), //boss threads pool Executors.newCachedThreadPool());//worker threads pool // 一个helper class,用来帮助启动factory这个server ServerBootstrap bootstrap = new ServerBootstrap( factory ); bootstrap.setOption( "reuseAddress", true ); // New and configure the ChannelPipelineFactory.
// Whenever a new connection is accepted by the server, // a new ChannelPipeline will be created by the specified ChannelPipelineFactory bootstrap.setPipelineFactory( new ManGagaServerPipelineFactory() );
总之,server是来个连接,就配备个ChannelPipeline,并且是永久性的。
第三步,ChannelPipeline
在ChannelPipeline中handler的执行顺序:
An upstream event is handled by the upstream handlers in the bottom-up direction.
A downstream event is handled by the downstream handler in the top-down direction.
For example, let us assume that we created the following pipeline:
ChannelPipeline p = Channels.pipeline(); p.addLast("1", new UpstreamHandlerA()); p.addLast("2", new UpstreamHandlerB()); p.addLast("3", new DownstreamHandlerA()); p.addLast("4", new DownstreamHandlerB()); p.addLast("5", new UpstreamHandlerX());
When an event goes upstream, the handler evaluation order is 1, 2, 5;
When an event goes downstream, the order is 4, 3.
我们到底需要哪些构建哪些handler?
- 一个是Protobuf
- 一个是自定义的
p.addLast( "frameDecoder", new ProtobufVarint32FrameDecoder( ) ); // upstream handler p.addLast( "protobufDecoder", new ProtobufDecoder( ManGaGaProtocoRequest.MhRequest.getDefaultInstance( ) ) );// upstream handler p.addLast( "frameEncoder", new ProtobufVarint32LengthFieldPrepender( ) ); // downstream handler p.addLast( "protobufEncoder", new ProtobufEncoder( ) ); // downstream handler p.addLast( "handler", new ManGagaServerHandler( ) ); // upstream handler
第四步,ChannelPipeline中的自定义handler
@Override public void messageReceived( ChannelHandlerContext ctx, MessageEvent e ) { System.out.println( "ManGagaServerHandler: Message received" ); // 获取这个请求包 mggRequest request = ( mggRequest ) e.getMessage( ); mggServiceBase service = null; mggResponse response = null; // 查看服务器端是否有对应客户端的function的请求 if ( request.hasFunction( ) ) {// get service handler to handle this request service = mggServiceFactory.getServcie( request.getFunction( ) ); response = service.handleRequest( request ); } else { // unknwon request response = createUnknownResponse( ); } // 回应客户端 e.getChannel( ).write( response ); }
第五步,服务进程循环
// 与地址bind成功 Channel channel = bootstrap.bind(new InetSocketAddress( SERV_ADDR, PORT_NUM )); // 另外开启一个位置服务 new Thread( new ManGagaServerMulticastUserLocation( ) ).start( );
// 循环起来 channel.getCloseFuture( ).awaitUninterruptibly( );
接下来设计下干实事的部分。
Netty server接收一个request到执行sql,最后返回给client结果。
流程log:
ManGagaServerHandler: Message received Function name:1 handleSignIn sql:SELECT Id as user_id from Users where AccountName = "mmmm" Succeeded connecting to the Database! Succeeded connecting to the Database! procedure is {call dbCheckPassword(35,"123456")} columnName = user_id columnValue = 35 Succeeded connecting to the Database! procedure is {call dbIsSignedIn(35)} columnName = user_id columnValue = 35 sql:SELECT SignedIn.UserId as user_id, SignedIn.SecurityId as security_id, SignedIn.ClientIPAddress as client_addr FROM SignedIn WHERE SignedIn.UserId = 35 Succeeded connecting to the Database!
第一步:得到request,启动专门对应的service,准备“生成相应的handle,处理这个request“。
自定义service handler
public class ManGagaServerHandler extends SimpleChannelUpstreamHandler { @Override public void messageReceived( ChannelHandlerContext ctx, MessageEvent e ) { // get response from channel mggRequest request = ( mggRequest ) e.getMessage( ); mggServiceBase service = null; mggResponse response = null; if ( request.hasFunction( ) ) { service = mggServiceFactory.getServcie( request.getFunction( ) ); // --> 启动相应的服务 response = service.handleRequest( request );
第二步:开始“生成相应的handle,处理这个request“
account module
public class AccountService extends mggServiceBase { @Override public mggResponse handleRequest(mggRequest req) { FunctionHanlderBase handler = null; switch( req.getFunction() ){ ... case mggFunctions.FUN_SIGNIN_VALUE:{ handler = new FunctionHandlerSignin(); // --> break; }
... } if( handler != null ){ return handler.handle( req ); }
解析protobuf压缩格式,获取request中的参数:
account module:
public class FunctionHandlerSignin implements FunctionHanlderBase { ...
...
private DbAccountFun mDbAccountFun = new DbAccountFun();
private DbAccessFun mDbFun = new DbAccessFun();
private DbAccess mDb = new DbAccess();
private DbAccount mDbAccount = new DbAccount();
@Override public MhResponse handle( MhRequest req ) { HashMap<String, Object> map = null;
//protobuf解析 String username = req.getRequestDataSignin().getUsername(); String password = req.getRequestDataSignin().getPassword();
// database module: Wrapper of some sql execution frequently used long userid = DbUtility.userExisting( mDb, username ); // database module: Wrapper of some sql execution for account management,侧重检查 mDbAccountFun.signin( mDbFun, mUserId );
// database module: Wrapper of some sql execution for account management,侧重执行 map = mDbAccount.getSignedInInfo(mDb, mUserId);
第三步:执行db中的干实事的家伙,其中public class DbAccount 设计如下。
public boolean signin( DbAccess db, long userid ){ // 构造SQL语句 String sql = String.format( SQL_SIGNIN, userid, DEFAULT_SECURITY_ID, DEFAULT_CLIENT_IP ); System.out.println("sql:"+sql); // 执行返回结果 int rows = db.executeUpdate( sql );
// --> 下一级别包括数据库的连接检查,数据库的调用等等,可设计为public class DbAccess类
// 结果check if( rows == 1 ){ return true; } else { return false; } }
可见,过程中主要经历了db中的四个模块: DbUtility --> DbAccountFun --> DbAccount --> DbAccess

浙公网安备 33010602011771号