/nettyServer

一个基于netty5的HTTP和Websocket手脚架,将HTTP路由到指定的Action,并使用ehcache实现session

Primary LanguageJava

##A server baseed on netty add sping IOC without XML

netty同时处理HTTP和Websocket,并将HTTP请求路由到相应Action中;使用ehcache实现Session;spring IOC做管理容器,mybatis 做sql数据库ORM;spring data mongoDB做mongo的ORM;HikariCP做sql数据库连接池;Gson用于json解析和生成;logback日志处理。

####netty处理HTTP和websocket

  • smart.core.netty.HttpHandler:是一个自定义的ChannelHandler用于处理HTTP 和Websocket请求
  • Handler分别处理HTTP和Websocket
public void messageReceived(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof FullHttpRequest) {//如果是HTTP请求,进行HTTP操作
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {//如果是Websocket请求,则进行websocket操作
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }
  • 由于websocket也是基于HTTP的,需要判断是websocket后,将HTTP升级为Websocket
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        logger.warn("uri:" + req.uri());
        if (req.uri().startsWith("/ws/join")) {//如果urL开头为/ws/join则升级为websocket
            mac = wsBeforeHandler(ctx, req);
            if (mac == null || mac.length() < 1) {
                RespTools.paraErrorBack(ctx,req,null);
                return;
            }
            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                    getWebSocketLocation(req), null, true);
            handshaker = wsFactory.newHandshaker(req);
            if (handshaker == null) {
                WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
            } else {
                handshaker.handshake(ctx.channel(), req);
            }
        } else {//是HTTP请求则路由到Action
            RouteResult<Action> routeResult = rs.getRouter().route(req.method(), req.uri());
            Action action = routeResult.target();
            action.act(ctx, req);
        }
    }
  • websocket请求处理,这里是从websocket请求中获取客户端传来的json字符串,并将字符串转为javabean
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {

        // Check for closing frame
        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        if (frame instanceof PingWebSocketFrame) {
            ctx.write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if (frame instanceof TextWebSocketFrame) {
            devicePool.join(ctx.channel(), mac);
            String json = ((TextWebSocketFrame) frame).text();
            Logic.ReqRespType data= JsonTools.read(json,Logic.ReqRespType.class);
           //...
            return;
        }
    }
  • 如果是HTTP则需在RouterSetting中配置路由. 比如r.POST("api/get_verify_code", getVerifyCodeAct): 将url为"api/get_verify_code"的POST请求路由到LoginAct中
public class RouterSetting {
    @Autowired
    private Router<Action> router;
    @Autowired
    private GetVerifyCodeAct getVerifyCodeAct;//w
    @Autowired
    private LoginAct loginAct;
    @Autowired
    private RegisterAct registerAcc;

    public Router<Action> getRouter() {
        routerConfig(this.router);
        return this.router;
    }

    private void routerConfig(Router<Action> r) {
        r.POST("api/get_verify_code", getVerifyCodeAct);
        r.ANY("api/login", loginAct);
        r.GET("api/register", registerAcc);
    }
}
  • Action处理HTTP请求并返回
@Controller
public class LoginAct implements Action {
    private static final Logger logger = LoggerFactory.getLogger(LoginAct.class);
    @Override
    public void act(ChannelHandlerContext ctx, FullHttpRequest req) {
        String ip = HttpTools.getIp(req);
        String body = Convert.buf2Str(req.content());
        Get.Login get = JsonTools.read(body, Get.Login.class);//1.得到HTTP传来的json数据解析为javabean
        Sub.Register back;//构建返回给客户端的javabean的实例
        //...
        HttpTools.sendCorrectResp(ctx, req, back);//返回给客户端HTTP Response
    }
}
  • 添加Session(依靠ehcache)
private void addSession(long userId, String ip) {
        Logic.DeviceSession session = new Logic.DeviceSession(ip, "");
        Cache.add(userId + "", session, "6mn");//设置session的缓存时间为6分钟
        //debugSession(userId);
    }
  • 从HTTP请求中获取IP地址
String ip = HttpTools.getIp(req);

####netty参数设置

port=8090
netty.boss.thread.count=2
netty.worker.thread.count=1
netty.so.keepalive=true
netty.so.backlog=100

####项目依赖

 //---------------------单元测试----------------------------
    testCompile group: 'junit', name: 'junit', version: '4.11'
    //--------------------数据库驱动----------------------------
    compile 'org.mongodb:mongodb-driver:3.2.2'
    compile 'mysql:mysql-connector-java:5.1.38'
    //-------------------数据库连接池---------------------------
    compile 'com.zaxxer:HikariCP:2.4.5'
    //----------------------ORM------------------------------
    compile group: 'org.mybatis', name: 'mybatis', version:mybatisVersion
    compile group: 'org.mybatis', name: 'mybatis-spring', version:mybatisSpringVersion
    //-----------------------缓存----------------------------
    compile group: 'net.sf.ehcache', name: 'ehcache', version:ehcacheVersion
    //----------------------工具包----------------------------
    compile 'commons-httpclient:commons-httpclient:3.1-rc1'
    compile 'org.javassist:javassist:3.20.0-GA'
    //---------------------日志处理----------------------------
    compile 'org.slf4j:slf4j-api:1.7.21'
    compile 'ch.qos.logback:logback-core:1.1.7'
    compile 'ch.qos.logback:logback-classic:1.1.7'
    //---------------------json处理---------------------------
    compile 'com.google.code.gson:gson:2.6.2'
    //---------------------netty-----------------------------
    compile group: 'io.netty', name: 'netty-all', version:nettyVersion
    //---------------------spring----------------------------
    compile group: 'org.springframework', name: 'spring-test', version:springVersion
    compile group: 'org.springframework', name: 'spring-jdbc', version:springVersion
    compile(group: 'org.springframework', name: 'spring-context', version:springVersion) {
        exclude(module: 'commons-logging')
    }