1. 程式人生 > >netty原始碼解析

netty原始碼解析

背景

netty 是一個非同步事件驅動的網路通訊層框架,其官方文件的解釋為

Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.

我們在新美大訊息推送系統sailfish(日均推送訊息量50億),新美大移動端代理優化系統shark(日均吞吐量30億)中,均選擇了netty作為底層網路通訊框架。

既然兩大如此重要的系統底層都使用到了netty,所以必然要對netty的機制,甚至原始碼瞭若指掌,於是,便催生了netty原始碼系列文章。後面,我會通過一系列的主題把我從netty原始碼裡所學到的毫無保留地介紹給你,原始碼基於4.1.6.Final

為什麼使用netty

netty底層基於jdk的NIO,我們為什麼不直接基於jdk的nio或者其他nio框架?下面是我總結出來的原因

1.使用jdk自帶的nio需要了解太多的概念,程式設計複雜
2.netty底層IO模型隨意切換,而這一切只需要做微小的改動
3.netty自帶的拆包解包,異常檢測等機制讓你從nio的繁重細節中脫離出來,讓你只需要關心業務邏輯
4.netty解決了jdk的很多包括空輪訓在內的bug
5.netty底層對執行緒,selector做了很多細小的優化,精心設計的reactor執行緒做到非常高效的併發處理
6.自帶各種協議棧讓你處理任何一種通用協議都幾乎不用親自動手
7.netty社群活躍,遇到問題隨時郵件列表或者issue
8.netty已經歷各大rpc框架,訊息中介軟體,分散式通訊中介軟體線上的廣泛驗證,健壯性無比強大

解析netty

瞭解了這麼多,今天我們就從一個例子出來,開始我們的netty原始碼之旅。

本篇主要講述的是netty是如何繫結埠,啟動服務。啟動服務的過程中,你將會了解到netty各大核心元件,我先不會細講這些元件,而是會告訴你各大元件是怎麼串起來組成netty的核心

example

下面是一個非常簡單的服務端啟動程式碼

public final class SimpleServer {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1
); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new SimpleServerHandler()) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { } }); ChannelFuture f = b.bind(8888).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } private static class SimpleServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive"); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered"); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerAdded"); } } }

簡單的幾行程式碼就能開啟一個服務端,埠繫結在8888,使用nio模式,下面講下每一個步驟的處理細節

EventLoopGroup 說白了,就是一個死迴圈,不停地檢測IO事件,處理IO事件,執行任務

ServerBootstrap 是服務端的一個啟動輔助類,通過給他設定一系列引數來繫結埠啟動服務

group(bossGroup, workerGroup) 我們需要兩種型別的人幹活,一個是老闆,一個是工人,老闆負責從外面接活,接到的活分配給工人幹,放到這裡,bossGroup的作用就是不斷地accept到新的連線,將新的連線丟給workerGroup來處理

.channel(NioServerSocketChannel.class) 表示服務端啟動的是nio相關的channel,channel在netty裡面是一大核心概念,可以理解為一條channel就是一個連線或者一個服務端bind動作,後面會細說

.handler(new SimpleServerHandler() 表示伺服器啟動過程中,需要經過哪些流程,這裡SimpleServerHandler最終的頂層介面為ChannelHander,是netty的一大核心概念,表示資料流經過的處理器,可以理解為流水線上的每一道關卡

childHandler(new ChannelInitializer<SocketChannel>)...表示一條新的連線進來之後,該怎麼處理,也就是上面所說的,老闆如何給工人配活

ChannelFuture f = b.bind(8888).sync(); 這裡就是真正的啟動過程了,繫結8888埠,等待伺服器啟動完畢,才會進入下行程式碼

f.channel().closeFuture().sync(); 等待服務端關閉socket

bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); 關閉兩組死迴圈

上述程式碼可以很輕鬆地再本地跑起來,最終控制檯的輸出為:

handlerAdded
channelRegistered
channelActive

總結

netty啟動一個服務所經過的流程
1.設定啟動類引數,最重要的就是設定channel
2.建立server對應的channel,建立各大元件,包括ChannelConfig,ChannelId,ChannelPipeline,ChannelHandler,Unsafe等
3.初始化server對應的channel,設定一些attr,option,以及設定子channel的attr,option,給server的channel新增新channel接入器,並出發addHandler,register等事件
4.呼叫到jdk底層做埠繫結,並觸發active事件,active觸發的時候,真正做服務費埠繫結