Netty构建一个简单的http服务

netty简介

  netty是一个异步的基于事件驱动的网络框架,用于快速开发可维护的高性能协议的服务器和客户端。
  netty是一个NIO客户端服务器框架,它使得网络框架的开发变得更加快速和简单,例如基于协议的服务端和客户端的开发,极大的简化了例如TCP和UDP socket服务等的网络编程。

运行环境

  • mac pro
  • Itellij Idea
  • gradle
  • jdk 8

程序所需jar包

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile (
            "junit:junit:4.12",
            "io.netty:netty-all:4.1.11.Final"
    )
}

Netty服务端开发一般流程

  1. 编写服务端main方法,其中定义两个事件驱动循环组对象 NioEventLoopGroup,然后定义一个服务端启动类ServerBootstrap对象,设置通道(NioServerSocketChannel.class)并且设置childHandler对象,也就是第二步定义的Initializer类对应的对象,绑定端口。
  2. 新建一个Initializer类,该类一般继承ChannelInitializer<SocketChannel>类。在initChannel方法里面添加对应的handler。
  3. 新建一个我们自己的handler,在channelRead0方法里面编写从客户端读取消息的处理逻辑。该方法的第二个参数就是从客户端读取的消息。

以上就是netty服务端开发的一般步骤。

简单Http服务的代码

main方法所在的启动类

package com.shengsiyuan.netty.firstexample;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * 实现一个http服务
 * @Author: zhouwen
 * @Date: 2017/6/9 21:49
 */
public class TestServer {

    public static void main(String[] args) throws Exception {
        //服务端一般都会定义两个循环事件组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //服务端启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new TestServerInitializer());

            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

Initializer编写

package com.shengsiyuan.netty.firstexample;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * http服务的initializer
 * @Author: zhouwen
 * @Date: 2017/6/9 21:55
 */
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {


    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        //http的编解码
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //处理http请求的handler
        pipeline.addLast("testHttpServerHandler", new TestHttpServerHandler());
    }


}

自定义的handler编写

package com.shengsiyuan.netty.firstexample;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import java.net.URI;

/**
 * http服务的的handler
 * @Author: zhouwen
 * @Date: 2017/6/9 21:59
 */
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

        System.out.println(msg.getClass().getName());

        if (msg instanceof HttpRequest) {
            HttpRequest httpRequest = (HttpRequest) msg;
            System.out.println("请求方法名: " + httpRequest.method().name());
            //对不同的路径进行处理
            URI uri = new URI(httpRequest.uri());
            if ("/favicon.ico".equals(uri.getPath())) {
                System.out.println("请求favicon.ico");
            }
            // 返回的内容
            ByteBuf content = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8);
            // http的响应
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
            //响应头设置
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
            // 把响应内容写回到客户端
            ctx.writeAndFlush(response);
        }

    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel active!");
        super.channelActive(ctx);
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel registered!");
        super.channelRegistered(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handler add!");
        super.handlerAdded(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel inactive!");
        super.channelInactive(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel unregistered!");
        super.channelUnregistered(ctx);
    }
}

启动服务后进行测试

curl 'http://localhost:8899'

只需在命令行工具里面使用curl命令即可发送一个http请求。返回的结果为:
这里写图片描述
这里与我们在代码里面的返回结果是符合的。再看看控制台的输出:
这里写图片描述
从控制台也可以看出handler里面定义的事件的执行流程。使用curl命令发送http请求,每次请求完成链接都会断开,所以控制台一定会输出channel inactive和channel unregistered。但是使用浏览器就不一样了,因为我们使用的是http 1.1版本,用浏览器不会立马就断开连接。

打开Google浏览器,输入http://localhost:8899,并且打开调试工具查看,如下图:
这里写图片描述
发现了浏览器发送了两个请求,这时控制台输出如下:
这里写图片描述
发现并没有输出channel inactive和channel unregistered。说明浏览器访问的时候,http请求不是立马就断开的,这个是根据keep-alive的一个规则来的。

以上全部,就是一个使用netty开发一个简单的http服务的过程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Netty编写一个RPC框架可以分为以下几个步骤: 1. 定义通信协议:首先需要定义客户端和服务端之间的通信协议,包括消息的格式、编码方式、数据传输方式等。可以选择使用自定义协议或者现有的协议,如HTTP、TCP等。 2. 设计服务接口:定义服务接口,包括方法名、参数列表、返回值等。可以使用Java接口或者其他IDL(接口描述语言)工具来定义服务接口。 3. 实现服务端:使用Netty构建服务端,监听指定的端口,接收客户端的请求。当有请求到达时,根据协议解析请求数据,并根据请求调用相应的业务逻辑处理方法,最后将结果封装成响应数据发送给客户端。 4. 实现客户端:使用Netty构建客户端,连接到服务端的IP和端口。当需要调用远程服务时,根据协议封装请求数据,并发送给服务端。然后等待服务端返回响应数据,解析响应数据并返回给调用方。 5. 进行序列化和反序列化:在客户端和服务端之间进行数据传输时,需要对请求参数和响应结果进行序列化和反序列化。可以使用现有的序列化框架,如Protobuf、JSON等。 6. 管理连接和负载均衡:在实际应用中,可能存在多个服务提供者和多个消费者,需要管理客户端和服务端之间的连接,以及实现负载均衡策略,选择合适的服务提供者进行调用。 7. 异常处理和容错机制:在RPC框架中,需要考虑异常处理和容错机制。当服务端出现异常或不可用时,需要进行相应的处理,如重试、降级、熔断等。 这些步骤只是一个大致的框架,具体的实现细节和代码编写会根据具体需求而有所不同。通过以上步骤,你可以使用Netty构建一个简单的RPC框架。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值