乐趣区

关于java:netty系列之一个价值上亿的网站速度优化方案

简介

其实软件界最赚钱的不是写代码的,写代码的只能叫马龙,高级点的叫做程序员,都是苦力活。那么有没有高大上的职业呢?这个必须有,他们的名字就叫做咨询师。

咨询师就是去帮企业做计划、做架构、做优化的,有时候一个简略的代码改变、一个架构的调整都能够让软件或者流程更加高效的运行,从而为企业节俭上亿的开销。

明天除了要给大家介绍一下如何在 netty 中同时反对 http 和 https 协定之外,还给大家介绍一个价值上亿的网站数据优化计划,有了这个计划,年薪百万不是梦!

本文的指标

本文将会给大家介绍一下如何在一个 netty 服务中同时反对 http 和 http2 两种协定,在这两个服务器中,提供了对多图片的拜访反对,咱们介绍如何从服务器端返回多个图片。最初介绍一个价值上亿的速度优化计划,必定大家会受益匪浅。

反对多个图片服务

对于服务器端来说,是通过 ServerBootstrap 来启动服务的,ServerBootstrap 有一个 group 办法用来指定 acceptor 的 group 和 client 的 group。

    public ServerBootstrap group(EventLoopGroup group) 
    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) 

当然你能够指定两个不同的 group,也能够指定同一个 group,它提供了两个 group 办法,成果上没太大的区别。

这里咱们当初主服务器中创立一个 EventLoopGroup,而后将其传入到 ImageHttp1Server 和 ImageHttp2Server 中。而后别离在两个 server 中调用 group 办法,而后配置 handler 即可。

先看一下 ImageHttp1Server 的结构:

        ServerBootstrap b = new ServerBootstrap();
        b.option(ChannelOption.SO_BACKLOG, 1024);
        b.group(group).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO))
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch){ch.pipeline().addLast(new HttpRequestDecoder(),
                                      new HttpResponseEncoder(),
                                      new HttpObjectAggregator(MAX_CONTENT_LENGTH),
                                      new Http1RequestHandler());
            }
        });

咱们传入了 netty 自带的 HttpRequestDecoder、HttpResponseEncoder 和 HttpObjectAggregator。还有一个自定义的 Http1RequestHandler。

再看一下 ImageHttp2Server 的结构:

ServerBootstrap b = new ServerBootstrap();
        b.option(ChannelOption.SO_BACKLOG, 1024);
        b.group(group).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch)  {ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()), new CustProtocolNegotiationHandler());
            }
        });

为了简略起见,咱们默认如果从 http 来拜访的话,就应用 http1 服务,如果是从 https 来拜访的话,就应用 http2 服务。

所以在 http2 服务中,咱们只须要自定义 ProtocolNegotiationHandler 即可,而不必解决 clear text 降级的申请。

http2 处理器

在 TLS 环境中,咱们通过自定义 CustProtocolNegotiationHandler,继承自 ApplicationProtocolNegotiationHandler 来进行客户端和服务器端协定的交互。

对于 http2 协定来说,应用了 netty 自带的 InboundHttp2ToHttpAdapterBuilder 和 HttpToHttp2ConnectionHandlerBuilder 将 http2 frame 转换成为 http1 的 FullHttpRequest 对象。这样咱们间接解决 http1 格局的音讯即可。

转换过程如下:

DefaultHttp2Connection connection = new DefaultHttp2Connection(true);
        InboundHttp2ToHttpAdapter listener = new InboundHttp2ToHttpAdapterBuilder(connection)
                .propagateSettings(true).validateHttpHeaders(false)
                .maxContentLength(MAX_CONTENT_LENGTH).build();

        ctx.pipeline().addLast(new HttpToHttp2ConnectionHandlerBuilder()
                .frameListener(listener)
                .connection(connection).build());

        ctx.pipeline().addLast(new Http2RequestHandler());

转换转换的 http2 handler 和一般的 http1 的 handler 惟一不同的是须要额定设置一个 streamId 属性到申请头和响应头中。

并且不须要解决 http1 特有的 100-continue 和 KeepAlive。其余的和 http1 handler 没什么两样。

解决页面和图像

因为咱们应用转换器将 http2 的 frame 转换成了 http1 的一般对象,所以对申请相应的页面和图像来说,跟 http1 的解决没什么太大区别。

对于页面来说,咱们须要获取要返回的 html,而后设置 CONTENT_TYPE 为 ”text/html; charset=UTF-8″,返回即可:

    private void handlePage(ChannelHandlerContext ctx, String streamId,  FullHttpRequest request) throws IOException {ByteBuf content =ImagePage.getContent();
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
        response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
        sendResponse(ctx, streamId, response, request);
    }

对于图像来说,咱们获取到要返回的图像,将其转换成为 ByteBuf,而后设置 CONTENT_TYPE 为 ”image/jpeg”,返回即可:

    private void handleImage(String id, ChannelHandlerContext ctx, String streamId,
            FullHttpRequest request) {ByteBuf image = ImagePage.getImage(parseInt(id));
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, image);
        response.headers().set(CONTENT_TYPE, "image/jpeg");
        sendResponse(ctx, streamId, response, request);
    }

这样,咱们就可能在 netty 服务器端同时解决页面申请和图片申请了。

价值上亿的速度优化计划

终于要到本文中最精彩的局部了,价值上亿的速度优化计划是什么呢?

在讲这个计划之前,先给大家讲一个抗洪抢险的故事。有两个县都住在一条大河的旁边。这条大河很不安稳,常常会发洪灾,然而两个县的县长做法很不同。

A 县的县长认真负责,派人定期巡逻查看所属的河段,筑堤、种树、巡视,一刻都放松,在他的任期平平安安,没有产生任何洪水溃堤的状况。

B 县的县长从来不巡检,一道河水泛滥的时候,B 县长就组织人抗洪抢险,而后媒体全都报道的是 B 县长抗洪的汗马功劳,最初 B 县长因为政绩突出,升任市长。

好了,故事讲完了,接下来是咱们的优化。不论是用户申请页面还是图片,最终都须要调用 ctx.writeAndFlush(response) 办法进行响应回写。

如果将其放入一个定时工作中,来定时执行,如下所示:

ctx.executor().schedule(new Runnable() {
            @Override
            public void run() {ctx.writeAndFlush(response);
            }
        }, latency, TimeUnit.MILLISECONDS);

那么服务器在通过 latency 指定的毫秒之后,才会发送对应的响应。比方这里咱们设置 latency 的值为 5 秒。

当然 5 秒是不可能让人称心的,于是领导或者客户找到你,说让你给优化一下。你说这个性能问题是很难的,波及到了麦克斯韦方程组和热力学第三定律,须要一个月工夫。领导说好,撸起袖子加油干,下个月给你工资涨 50%。

一个月后,你把 latency 改成 2.5 秒,性能晋升了 100%,这个优化值不值几个亿?

总结

当然,上一节给大家开个玩笑,不过在 netty 响应中应用定时工作的技巧,大家也应该牢牢把握,起因你懂的!

本文的例子能够参考:learn-netty4

本文已收录于 http://www.flydean.com/34-netty-multiple-server/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

退出移动版