Netty:我的小故事

我的韩国欧巴,让我从 JDK 的混杂的环境中诞生。workaround 过不予修复“陷阱(BUG,BUG..)”默认失败地,跳过不可使用的 IP_TOS 选项,放弃 AIO 这个鸡肋。这一切让我变得如此与众不同,要说特别之处,我行异步,事件驱动之道;内有性能加持,外有呼风唤雨之术法;以三大形态互换,三大法相容相生,三几数笔达万丈之渊。现在的互联网,非复吴下阿蒙,一变不知样。以前尚且可以 CRUDER,现在只能去学习我,然后用上 Spring WebFlux 再与微服务搞上。此外,还得我来搞个前所未有的话术,提提性能冲点业绩,换点银子讨好老婆,养养儿子,爽歪歪(欧巴,我**你) 。在爽之余,要知道有我曾经有孪生兄弟他叫 5阿发,不过早早出家不问世事了(我会继承你的遗志的)。。。
那,我们就从 Echo 与 Http 开始,入门 Netty 吧! 首先,我的韩国欧巴把我放在 https://netty.io 上,如果要见我那你得去下载页面找到我,然后把我配置到 项目 中。不然你用 Maven 帮助你把我从另外一个地方出来帮你也行,别忘了我的版本号是3个点。 二话不说,先来个我需要的几个小家伙的介绍: 如果要搞个自定义协议的服务器端,要知道这几个 Bigman ,大人物 skr,skr,skr。
  • EventLoopGroup:任务分组,最多可以使用两组(是的,你没看错)
  • ServerBootstrap:Server 运作组织类,指挥部
  • SocketChannel:工作频道
  • ChannelHandler/ChannelInboundHandler:提供各种事件处理
ServerBootstrap 需要指定什么呢?
  • group():指定 事件循环 组
  • channel():指定 SocketChannel 类
  • handler:也是指定事件(对于 第一个 EventLoopGroup)
  • childHandler:指定事件处理
  • option():链接选项
  • childOption():链接选项
来看一段代码就明白了:
package com.codimiracle.practice;

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

public class EchoServer {
private int port;

private EchoServer(int port) {
this.port = port;
}

public static void main(String[] args) {
EchoServer server = new EchoServer(3400);
server.start();
}

private void start() {
NioEventLoopGroup majorEventLoopGroup = new NioEventLoopGroup();
NioEventLoopGroup seniorEventLoopGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap
.group(majorEventLoopGroup, seniorEventLoopGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new LoggingHandler(LogLevel.INFO))
.addLast(new EchoService());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture channelFuture = serverBootstrap.bind(this.port).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
majorEventLoopGroup.shutdownGracefully();
seniorEventLoopGroup.shutdownGracefully();
}
}
}
Handler 代码:
package com.codimiracle.practice;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class EchoService extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 内部缓存写入
ctx.write(msg);
// 把缓存发出
ctx.flush();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
  • EventLoopGroup:任务分组,最多一个
  • Bootstrap:连长
  • SocketChannel:是的还是它
  • ChannelHandler/ChannelInboundHandler:没错,也是它
如果要搞个自定义的客户端,需要知道这几个 小东西。 就换了指挥的人。
package com.codimiracle.practice;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class EchoClient {
private String host;
private int port;

public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void connect() {
NioEventLoopGroup clientEventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(clientEventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new LoggingHandler(LogLevel.INFO))
.addLast(new EchoServiceProxy());
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
clientEventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
EchoClient client = new EchoClient("localhost", 3400);
client.connect();
}
}
Client Handler 代码:
package com.codimiracle.practice;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class EchoServiceProxy extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.wrappedBuffer("Hello world\n".getBytes()));
}
}

我的三个模式

  • 排队打饭(排队模式 OIO/BIO,后来放弃了)
  • 点单自领(NIO模式)
  • 包厢等吃(AIO模式,后来移除了)
要更换模式,只需要 把 EventLoopGroup,ServerSocketChannel 的对象换成对应前缀的对象即可(如 OioEventLoopGroup,OioSocketChannel/OioServerSocketChannel) NIO 实现: EventLoopGroup:如果通过源码追溯进去,实际上是一个特殊化的 ExecutorService(线程池),特殊在那里呢? EventLoop:看一看代码,可以发现 EventLoop 处理了选择器当前(selectNow() 方法)的选择策略 SocketChannel:对应的事件频道

我的三种 Reactor

Reactor更像是一种事件的派发机制,但这些派发机制与响应的 IO 模式相关联。
  • ThreadPreConnection:使用 new NioEventLoopGroup(1) 创建,即可使用单 Reactor 单线程模式
  • Reactor:使用 new NioEventLoopGroup() 创建,即可使用单 Reactor 多线程模式
  • Proactor:使用两个 NioEventLoopGroup() 对象,即可创建主从 Reactor 多线程模式

毕业回顾

回想已经经过的时间,总是让人着迷和反思。每个选择的节点依然清晰无比,却又那么的扎心。每个人或许都觉得之前能够选择得更好,不过我至少现在的我看来这一切都是固定的。现在的你觉得之前可以做得更好是因为你得到了成长,那么接下来会是什么等待自己,重蹈覆辙还是踏出与之前完全不同的一步呢?
自出生尔来,每个人的际遇不同从而塑造了不一样的人格和性格。对于事物和道理的意识到和行动到,两者之间永远有差距。我于之前环境和周遭所赋予我的,暂且只是记录和要求自己改变,受现实的曲折,行赤子之心,结果云云。改变和成长是多么有趣的词语,让人生的体验更为完好,想改变吗?想,如同孤独形影相随。《那年那兔那些事儿》告诉我,理想从现在开始就会早一步实现。那么如何改变?以同理之心,上善若水,心诚而明理,不妄自菲薄,亦不增长自负之心。《认知的尺度》引用“最难的事情是认清自己”,告诉我了解了自己是最有意思的事情啊。 一时迷失,一时失去,一时怨恨,患得患失。这样的人又如何担起大任来呢?却也承受精神之涣散,肉体之酸痛,明力之未逮,失道以无能。矛盾啊,我本是平凡之人,却强迫自己如神仙下凡一般,动指而移山,挥手而洒雨,飞天而畅游,遁地如游戏。其种种,不如相信积累的力量,要说结果,总得畜够结果的养分啊。

计划篇

回顾所学,自我 Debug 之余,我想一个败笔就是没有好好地把写过的东西进行汇总和整理。现我有一点空余,不如整理、反思和改进。maven的 jira 账号已经注册了太久了是时候发起 issue 了。

Java Web 整理

概念: contract:指以特定的对象来实现特定的重复,这个重复具备稳定的约束。 包: basic-contract:基础约束,这个约束处理的是 controller 中一些重复使用的部分。(即将发布 Jar 包到中央仓库)
mybatis-contract:mybatis 约束,定制了自己常用的 关于 mybatis 的约束,如分页器的复用,Mapper 一些常常出现的方法。
content-middleware:企图把 内容作为一个单独的模块抽象出来。

Java 虚拟机 – 结构

数据类型

Java 虚拟机中有两种数据类型:原始类型和引用类型。这两种类型的值可以存进变量,作为参数传递,被方法返回,并通过原始值或者引用值进行操作。虚拟机不进行类型检查,所有的检查都在运行时之前处理完成。虚拟机通过虚拟机指令来确定参数类型如 iadd,ladd,fadd,dadd 都是两个数值操作类型返回一个数值结果的指令,分别对 integer, long, float, double 进行操作。
原始类型byteshortintlongchar
字节数816326416
默认值0000‘\u000000’
数值范围[-128,
127]
[-32768,
32767]

[-2147483648,
-2147483647]

[-9223372036854775808
, 9223372036854775807]
[0,
65535]
原始类型 float IEEE 754 double IEEE 754
字节数 32 64
默认值
+0+0
returnAddress 类型被虚拟机 jsr,ret,jsr_w 指令进行操作,returnAddress 的值指向一条操作码(opcode), returnAddress 不会作为 Java 语言中特定类型并且不能通过编程语言进行修改。 boolean 类型虽然在虚拟机中进行了定义,但实际中是使用 int 来表示的,虚拟机也是使用 byte 相关的指令对其进行操作,不存在 boolean 相关的操作指令, boolean 数组使用 newarray 指令创建,并使用 byte 数组指令 baload,bastore 进行访问。 引用类型有三种,分别是 class 类型,array 类型,interface 类型,null 没有具体的运行时类型,但可以强制转换为任意的引用类型。虚拟机规范没有规定 null 的具体值。

运行时数据区域