Netty學(xué)習(xí)筆記--1(使用Netty搭建一個(gè)基于Http協(xié)議的服務(wù))

0.what is Netty?

簡(jiǎn)單來(lái)說(shuō)啥箭,Netty是異步的谍珊,事務(wù)驅(qū)動(dòng)的,高性能的NIO框架
Netty官網(wǎng)
其中有詳細(xì)的netty介紹急侥,剛開始學(xué)習(xí)砌滞,有什么不對(duì),歡迎指出坏怪。

1.Netty中幾個(gè)比較重要的概念

1.0 Handler

Handler其實(shí)就是事件的處理器贝润,Netty通過(guò)Channel讀入請(qǐng)求內(nèi)容后會(huì)分配給Handler進(jìn)行事件處理,Handler能夠處理的事件包括:數(shù)據(jù)接收铝宵,異常處理打掘,數(shù)據(jù)轉(zhuǎn)換华畏,編碼解碼等問(wèn)題,其中包含兩個(gè)非常重要的接口ChannelInboundHandler尊蚁,ChannelOutboundHandler亡笑,前者負(fù)責(zé)處理客戶端發(fā)送到服務(wù)端的請(qǐng)求,后者反之横朋。關(guān)于Handler執(zhí)行順序的一些介紹可以看一看這篇文章: handler的執(zhí)行順序
在這個(gè)地方有一個(gè)值得注意的點(diǎn)仑乌,無(wú)論是InboundHandler還是OutboundHandler都不適合用于做耗時(shí)操作,官方要求耗時(shí)操作應(yīng)當(dāng)使用單獨(dú)的EventExcutorGroup+專門的Handler來(lái)進(jìn)行操作

static final EventExecutorGroup group = new DefaultEventExecutorGroup(16); 
... 
ChannelPipeline pipeline = ch.pipeline(); 
pipeline.addLast("decoder", new MyProtocolDecoder()); pipeline.addLast("encoder", new MyProtocolEncoder()); 
// Tell the pipeline to run MyBusinessLogicHandler's event handler methods 
// in a different thread than an I/O thread so that the I/O thread is not blocked by 
// a time-consuming task. 
// If your business logic is fully asynchronous or finished very quickly, you don't 
// need to specify a group. 
pipeline.addLast(group, "handler", new MyBusinessLogicHandler());

1.1 Channel

這里的Channel的概念和NIO中Channel的概念是一樣的琴锭,相當(dāng)于一個(gè)Socket連接

1.2 Bootstrap

Bootstrap其實(shí)就是Netty服務(wù)的啟動(dòng)器晰甚,服務(wù)端使用的是ServerBootstrap,客戶端使用的是Bootstrap,我們可以通過(guò)配置Bootstrap來(lái)配置Netty使用哪種的Channel决帖,Group厕九,Handler和Encoder,Decoder……

1.3 LoopGroup

一個(gè)LoopGroup可以包含多個(gè)EventLoop古瓤,我目前的理解是將其理解為一個(gè)線程池止剖,其中的EventLoop為其中的線程

1.4 ChannelFuture

這點(diǎn)在官方的Guide中也有提到,在Netty中落君,所有的處理都是異步的穿香,因此需要一個(gè)Future對(duì)象,可以注冊(cè)監(jiān)聽在異步線程處理完以后進(jìn)行一些處理

2.HelloWorld

2.0 Server端代碼

public class Server{

    public void start(int port) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        EventExecutorGroup bussinessGroup = new DefaultEventExecutorGroup(16);
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                     //使用哪一種Channel
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //添加http請(qǐng)求所需要的編碼器與解碼器
                            ch.pipeline().addLast(new HttpResponseEncoder());
                            ch.pipeline().addLast(new HttpRequestDecoder());
                            //ch.pipeline().addLast(new ServerOutboundHandler()); //如果存在OutboundHandler則必須在最后一個(gè)inbound前面
                            ch.pipeline().addLast(new ServerHandler());
                            //耗時(shí)操作使用的group和專門的handler
                            ch.pipeline().addLast(bussinessGroup, "handler", new BusinessHandler());
                        }
                    })
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture f = b.bind(port);
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Server server = new Server();
        server.start(8090);
    }

}

2.1 ServerHandler

public class ServerHandler extends ChannelInboundHandlerAdapter {

    private HttpRequest request;
    private HttpContent content;
    private FullHttpResponse response;
    private StringBuilder sb = new StringBuilder();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        if (msg instanceof HttpRequest) {
            request = (HttpRequest) msg;
            System.out.println(request.uri());
            System.out.println("request received success");
            sb.append("request received success \n");
        }
        if (msg instanceof HttpContent) {
            content = (HttpContent) msg;
            ByteBuf buf = content.content();
            String result = buf.toString(CharsetUtil.UTF_8);
            System.out.println(result);
            buf.release();
            System.out.println("content received success");
            sb.append("content received success");
            response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(sb.toString().getBytes()));
            ctx.writeAndFlush(response);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_GATEWAY);
        ctx.writeAndFlush(response);
        ctx.close();
    }
}

3. 請(qǐng)求服務(wù)器

3.0 使用postman來(lái)請(qǐng)求服務(wù)器

postman訪問(wèn)netty服務(wù)器

我使用post方式隨意發(fā)送了任意內(nèi)容到服務(wù)器绎速,能夠得到返回內(nèi)容皮获,并且idea控制臺(tái)輸出如下,表明可以獲得postman發(fā)送的數(shù)據(jù)

idea控制臺(tái)輸出內(nèi)容

3.1 使用NettyClient訪問(wèn)Netty服務(wù)器

3.1.0 Client

public class Client {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new HttpClientCodec());
                    ch.pipeline().addLast(new ClientHandler());
                }
            });
            ChannelFuture f = b.connect("127.0.0.1", 8090).sync();
            Channel ch = f.channel();
            FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://127.0.0.1:8090", Unpooled.wrappedBuffer("{ \"test\" : \"test\" }".getBytes()));  //發(fā)送請(qǐng)求道服務(wù)端
            request.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
            request.headers().set(HttpHeaderNames.CONTENT_LENGTH, 1024);  //必須設(shè)置Content length否則服務(wù)端收不到content
            System.out.println(request.content().toString(CharsetUtil.UTF_8));
            ch.writeAndFlush(request);
            //wait until server to close the connection
            ch.closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }

}

3.1.1 ClientHandler

public class ClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        HttpContent content;
        HttpResponse response;
        if (msg instanceof HttpResponse) {
            response = (HttpResponse) msg;
            System.out.println(response.status().toString());
        }

        if (msg instanceof HttpContent) {
            content = (HttpContent) msg;
            ByteBuf buf = content.content();
            String responseContent = buf.toString(CharsetUtil.UTF_8);
            System.out.println(responseContent);
            buf.release();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

以上代碼也能獲得和postman訪問(wèn)相同的效果

4.其中思考的問(wèn)題

  • Q:耗時(shí)的業(yè)務(wù)邏輯操作應(yīng)該放在哪里
    A:使用EventExcutorGroup和單獨(dú)的Handler來(lái)實(shí)現(xiàn)業(yè)務(wù)邏輯代碼(在上面的代碼中有提到)
  • Q :如何將InboundMessageHandler讀到的msg傳到其他Handler
    A:使用ctx.pipeline().channel().attr()可以設(shè)置屬性來(lái)在Handler傳遞屬性值
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纹冤,一起剝皮案震驚了整個(gè)濱河市洒宝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萌京,老刑警劉巖雁歌,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異知残,居然都是意外死亡靠瞎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門求妹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乏盐,“玉大人,你說(shuō)我怎么就攤上這事制恍「改埽” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵净神,是天一觀的道長(zhǎng)何吝。 經(jīng)常有香客問(wèn)我溉委,道長(zhǎng),這世上最難降的妖魔是什么岔霸? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任蝶念,我火速辦了婚禮呜袁,結(jié)果婚禮上迅矛,老公的妹妹穿的比我還像新娘臭挽。我一直安慰自己,他們只是感情好絮爷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布趴酣。 她就那樣靜靜地躺著,像睡著了一般坑夯。 火紅的嫁衣襯著肌膚如雪岖寞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天柜蜈,我揣著相機(jī)與錄音仗谆,去河邊找鬼。 笑死淑履,一個(gè)胖子當(dāng)著我的面吹牛隶垮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秘噪,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼狸吞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了指煎?” 一聲冷哼從身側(cè)響起蹋偏,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎至壤,沒(méi)想到半個(gè)月后威始,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡像街,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年字逗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宅广。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖些举,靈堂內(nèi)的尸體忽然破棺而出跟狱,到底是詐尸還是另有隱情,我是刑警寧澤户魏,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布驶臊,位于F島的核電站挪挤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏关翎。R本人自食惡果不足惜扛门,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纵寝。 院中可真熱鬧论寨,春花似錦、人聲如沸爽茴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)室奏。三九已至火焰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胧沫,已是汗流浹背昌简。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绒怨,地道東北人纯赎。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像窖逗,于是被迫代替她去往敵國(guó)和親址否。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理碎紊,服務(wù)發(fā)現(xiàn)佑附,斷路器,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • netty常用API學(xué)習(xí) netty簡(jiǎn)介 Netty是基于Java NIO的網(wǎng)絡(luò)應(yīng)用框架. Netty是一個(gè)NIO...
    花丶小偉閱讀 5,995評(píng)論 0 20
  • 本文是Netty文集中“Netty 源碼解析”系列的文章仗考。主要對(duì)Netty的重要流程以及類進(jìn)行源碼解析音同,以使得我們...
    tomas家的小撥浪鼓閱讀 5,863評(píng)論 9 12
  • 一場(chǎng)秋雨,將彌漫的塵霧一掃而空秃嗜。 碧空萬(wàn)里权均,書寫的不光是夏日的澄澈 行走在秋的環(huán)視中,是增添秋的美還是享受秋的吻锅锨?...
    沙漠里的胡楊v閱讀 302評(píng)論 0 2
  • 周末叽赊,早飯很遲。 不忍心叫醒正在熟睡的兒子必搞。 早已做好的早餐就只等兒子起來(lái)用餐了必指。餐桌上擺好了兒子喜歡吃的飯菜,牛...
    薛靜春閱讀 267評(píng)論 2 1