[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,

  1. If you opened two server ports such as 80 and 443, you will have two boss threads.
  2. A boss thread accepts incoming connections until the port is unbound.
  3. 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

 

posted @ 2018-01-28 16:15  郝壹贰叁  阅读(239)  评论(0)    收藏  举报