Start HttpServer

/**
* 启动 HttpServer
* multi instances 采用 synchronized防止线程安全问题
* addHandlers 方法是actor模式的实现(EventLoopPoolSize >= instances):
  * 1 instances : 1 verticle(actor) : 1 VertxThread(Eventloop)
*/
public synchronized HttpServer listen(int port, String host, Handler<AsyncResult<HttpServer>> listenHandler) {
//是否有配置requestHandler或webscoket
if (requestStream.handler() == null && wsStream.handler() == null) {
throw new IllegalStateException("Set request or websocket handler first");
}
if (listening) {
throw new IllegalStateException("Already listening");
}
listenContext = vertx.getOrCreateContext(); //根据currentThread 获取Context,获取null则create
serverOrigin = (options.isSsl() ? "https" : "http") + "://" + host + ":" + port;//判断是否启用ssl
List<HttpVersion> applicationProtocols = options.getAlpnVersions();//获取协议版本,默认支持1.1和2.0 if (listenContext.isWorkerContext()) {//是否使用 Worker Verticles ,不予许使用HTTP2.0
applicationProtocols = applicationProtocols.stream().filter(v -> v != HttpVersion.HTTP_2).collect(Collectors.toList());
}
sslHelper.setApplicationProtocols(applicationProtocols);//应用协议 synchronized (vertx.sharedHttpServers()) {// 监听多个不同网络接口(ip:port) Httpserver 防止并发
this.actualPort = port;
id = new ServerID(port, host);//生成服务id
HttpServerImpl shared = vertx.sharedHttpServers().get(id); if (shared == null || port == ) {// mutil instances 的情况,利用 mutli core cpu
/**
* frist instances
*/
serverChannelGroup = new DefaultChannelGroup("vertx-acceptor-channels", GlobalEventExecutor.INSTANCE);
ServerBootstrap bootstrap = new ServerBootstrap();
//定义两个线程组,accept size 1, 重写的VertxEventLoopGroup
bootstrap.group(vertx.getAcceptorEventLoopGroup(), availableWorkers); applyConnectionOptions(bootstrap);//添加Connection Accept之后的附属选项
sslHelper.validate(vertx);//验证ssl相关参数
bootstrap.childHandler(new ChannelInitializer<Channel>() { @Override
/**
* connection accept 调度切换线程后触发
*/
protected void initChannel(Channel ch) throws Exception {
 //限流策略,读大于写,导致内存无限扩大,最终 OOM
if (requestStream.isPaused() || wsStream.isPaused()) {
ch.close(); //超过服务承载能力,关闭连接
return;
}
ChannelPipeline pipeline = ch.pipeline();
if (sslHelper.isSSL()) {//是否启用ssl
io.netty.util.concurrent.Future<Channel> handshakeFuture;
if (options.isSni()) {//是否启用sni,单服务多证书情况
VertxSniHandler sniHandler = new VertxSniHandler(sslHelper, vertx);
pipeline.addLast(sniHandler);
handshakeFuture = sniHandler.handshakeFuture();
} else {
SslHandler handler = new SslHandler(sslHelper.createEngine(vertx));
pipeline.addLast("ssl", handler);
handshakeFuture = handler.handshakeFuture();
}
//侦听 TLS handshake
handshakeFuture.addListener(future -> {
if (future.isSuccess()) {// 握手成功
if (options.isUseAlpn()) {//是否启用alpn,协调使用的protocol
//获取使用的协议
SslHandler sslHandler = pipeline.get(SslHandler.class);
String protocol = sslHandler.applicationProtocol();
if ("h2".equals(protocol)) {//是否是http2.0
handleHttp2(ch);
} else {
handleHttp1(ch);
}
} else {
handleHttp1(ch);
}
} else {//握手失败
HandlerHolder<HttpHandlers> handler = httpHandlerMgr.chooseHandler(ch.eventLoop());
handler.context.executeFromIO(() -> handler.handler.exceptionHandler.handle(future.cause()));
}
});
} else {
//是否是启用http2,通过VM Options: -Dvertx.disableH2c 设置;默认false
if (DISABLE_H2C) {
handleHttp1(ch);
} else {
IdleStateHandler idle;
if (options.getIdleTimeout() > ) {//是否定义最大空闲时间
pipeline.addLast("idle", idle = new IdleStateHandler(, , options.getIdleTimeout()));
} else {
idle = null;
} /**直接使用明文的http2.0或1.1处理*/
pipeline.addLast(new Http1xOrH2CHandler() {
@Override
protected void configure(ChannelHandlerContext ctx, boolean h2c) {
if (idle != null) {
//移除idleHandler,重新添加,不用注意次序
pipeline.remove(idle);
}
if (h2c) {//判断协议,如果定义idle则会重新添加 idleHandler
handleHttp2(ctx.channel());
} else {
handleHttp1(ch);
}
} @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) {
ctx.close();
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
//根据eventloop选中对应的handler进行异常传播
HandlerHolder<HttpHandlers> handler = httpHandlerMgr.chooseHandler(ctx.channel().eventLoop());
handler.context.executeFromIO(() -> handler.handler.exceptionHandler.handle(cause));
}
});
}
}
}
}); addHandlers(this, listenContext);////添加一个instaces(verticle的HttpHandlers)到httpHandlerMgr中
try {
 //listen ip:port
bindFuture = AsyncResolveConnectHelper.doBind(vertx, SocketAddress.inetSocketAddress(port, host), bootstrap);
bindFuture.addListener(res -> {
if (res.failed()) {
vertx.sharedHttpServers().remove(id);
} else {
Channel serverChannel = res.result();
HttpServerImpl.this.actualPort = ((InetSocketAddress) serverChannel.localAddress()).getPort();
serverChannelGroup.add(serverChannel);//添加当前的ServerSocketChannel
//初始化metrcis指标
VertxMetrics metrics = vertx.metricsSPI();
this.metrics = metrics != null ? metrics.createMetrics(this, new SocketAddressImpl(port, host), options) : null;
}
});
} catch (final Throwable t) {
if (listenHandler != null) {
vertx.runOnContext(v -> listenHandler.handle(Future.failedFuture(t)));
} else {
log.error(t);
}
listening = false;
return this;
}
vertx.sharedHttpServers().put(id, this);//启动的HttpServer服务(verticle)添加到Vertx.sharedHttpMap中
actualServer = this;
} else {//other instances
actualServer = shared;
this.actualPort = shared.actualPort;
//在actualServer基础上添加一个instaces(verticle的HttpHandlers)到httpHandlerMgr中
addHandlers(actualServer, listenContext);
//初始化metrics
VertxMetrics metrics = vertx.metricsSPI();
this.metrics = metrics != null ? metrics.createMetrics(this, new SocketAddressImpl(port, host), options) : null;
}
//服务 bind 状态
actualServer.bindFuture.addListener(future -> {
if (listenHandler != null) {
final AsyncResult<HttpServer> res;
if (future.succeeded()) {
res = Future.succeededFuture(HttpServerImpl.this);
} else {
res = Future.failedFuture(future.cause());
listening = false;
}
listenContext.runOnContext((v) -> listenHandler.handle(res));//回调处理
} else if (future.failed()) {
listening = false;
log.error(future.cause());
}
});
}
return this;
}

如何实现隔离(actor模型)

/**
* 添加一个verticle instances handlers
* @param server First Actual Server(multi instances)
* mutil instances 情况下第一个instance启动成功,other instances 仅仅是
* 利用multi core cpu,所以以 first instances actual Server为主,后续在
* Current HttpServerImpl instance 添加handlers(verticle)
* @param context current Thread context
* multi instances 下EventLoopGroup.next 方法挑选(choose)出一个Eventloop
* 与Context 映射. netty EventExecutor调度DefaultEventExecutorChooserFactory类
* 两种实现:
* ①求余取模
* ②位运算取模(2的幂)
* 所以防止实例数量大于EventloopGroup数量,Default : 2 * CpuCoreSensor.availableProcessors()
* ,linux下以读取/proc/self/status 文件为主,而不是Runtime.getRuntime().availableProcessors()
*/
private void addHandlers(HttpServerImpl server, ContextImpl context) {
server.httpHandlerMgr.addHandler(
new HttpHandlers(
requestStream.handler(),
wsStream.handler(),
connectionHandler,
exceptionHandler == null ? DEFAULT_EXCEPTION_HANDLER : exceptionHandler)
, context);
} public class HttpHandlers {
final Handler<HttpServerRequest> requestHandler;
final Handler<ServerWebSocket> wsHandler;
final Handler<HttpConnection> connectionHandler;
final Handler<Throwable> exceptionHandler; /**
* @param requestHandler Http Request Handler
* @param wsHandler WebScoket Handler
* @param connectionHander TCP Connection Handler
* @param exceptionHander Exception Handlet
*/
public HttpHandlers(
Handler<HttpServerRequest> requestHandler,
Handler<ServerWebSocket> wsHandler,
Handler<HttpConnection> connectionHandler,
Handler<Throwable> exceptionHandler) {
this.requestHandler = requestHandler;
this.wsHandler = wsHandler;
this.connectionHandler = connectionHandler;
this.exceptionHandler = exceptionHandler;
}
} public class HandlerManager<T> {
public synchronized void addHandler(T handler, ContextImpl context) {
/**
* 添加一个eventloop(Thread)到 VertxEventLoopGroup 集合中.
* accept状态后的read/write事件,线程调度在VertxEventLoopGroup类的next方法,
* vertx重写choose策略
*/
EventLoop worker = context.nettyEventLoop();
availableWorkers.addWorker(worker);
/**
* 添加handlers,并且绑定handler和context映射关系.
* 注意部署的instances size不要超过EventLoopPoolSize,
* 否则出现 1 EventLoop : N handler(verticle)
* 导致一个eventloop上执行 N 个verticle
*/
Handlers<T> handlers = new Handlers<>();
Handlers<T> prev = handlerMap.putIfAbsent(worker, handlers);
if (prev != null) {
handlers = prev;
}
handlers.addHandler(new HandlerHolder<>(context, handler));
hasHandlers = true;
}
}

Connection scheduling process:

add handler to eventloop structure:

  1. an eventloop corresponds to a handlers
  2. an eventloop corresponds to multiple instances verticles(HandlerHolder)

HttpServer option

public class HttpServerOptions extends NetServerOptions {
//是否启用压缩,默认false
private boolean compressionSupported; //压缩级别越高cpu负荷越大,默认gzip
private int compressionLevel; //websocket最大的 Frame 大小,默认65536
private int maxWebsocketFrameSize; //websocket 最大消息大小,默认65536*4
private int maxWebsocketMessageSize; //处理WebSocket消息的约定的子协议
private String websocketSubProtocols; //是否自动处理100-Continue,默认false
private boolean handle100ContinueAutomatically; //分段传输chunk 大小,默认8192
private int maxChunkSize; //initial line 最大长度,默认 4096
private int maxInitialLineLength; //Header 最大大小,默认 8192
private int maxHeaderSize; //http2.0最大的并发流,默认100
private Http2Settings initialSettings; //支持alpn的版本,默认Http1.1和Http2.0
private List<HttpVersion> alpnVersions; //设置连接的窗口大小,默认无限制
private int http2ConnectionWindowSize; //是否启用压缩解码
private boolean decompressionSupported; //WebSocket Masked位为true。 PerformingUnMasking将是错误的,默认为false
private boolean acceptUnmaskedFrames; //默认HttpObjectDecoder的初始缓冲区大小,默认128
private int decoderInitialBufferSize;
}

备注

1.建立HttpServer,配置最大的idle时间,默认tcpkeepalive配置是false,
  网络故障等造成TCP挥手交互失败从而导致epoll的达到FileMax,阻止后续连接,导致
  服务器无法提供服务; 或者启用keepalive,依靠内核TCP模块去侦测(默认2小时一次).
  可用netstat工具查看应用当前网络状况
  
2.启用HTTP2,使用jetty开源apln-boot jar包,对JDK版本依赖关系强,需下载对应JDK版本的apln;
 或者使用openssl,当前服务环境都需安装,迁移服务麻烦,但是性能稍高. 3.具体 Route和其它HttpServer功能在 Web 模块中, core 模块只是实现Tcp相关、TLS、
  Choose vertcile.handlers Scheduling 和codec等.

vertx的HttpServer模块的更多相关文章

  1. 使用Node.js 搭建http服务器 http-server 模块

    1. 安装 http-server 模块 npm install http-server -g   全局安装 2.在需要的文件夹   启动 http-server  默认的端口是8080    可以使 ...

  2. vertx的NetServer模块

    启动 public synchronized void listen(Handler<NetSocket> handler, SocketAddress socketAddress, Ha ...

  3. tornado httpserver

    # coding:utf-8 import tornado.web import tornado.ioloop import tornado.httpserver # 新引入httpserver模块 ...

  4. Python全栈 项目(HTTPServer、PiP使用)

    pip是Python官方推荐的包管理工具   属于python的一部分            pip的使用    pip的安装             sudo apt-get install pyt ...

  5. 构建通用的 React 和 Node 应用

    这是一篇非常优秀的 React 教程,这篇文章对 React 组件.React Router 以及 Node 做了很好的梳理.我是 9 月份读的该文章,当时跟着教程做了一遍,收获很大.但是由于时间原因 ...

  6. angular单元测试与自动化UI测试实践

    关于本文:介绍通过karma与jsmine框架对angular开发的应用程序进行单元与E2E测试. angular单元测试与集成测试实践 先决条件 创建项目 webstorm中创建空白web项目 创建 ...

  7. AngularJS介绍

    AngularJS介绍–AngularJS的前世今生 AngularJS是什么 在Web开发领域,我们一般使用HTML作为前端页面元素的声明式语言,使用CSS技术作为展示样式的描述语言,JavaScr ...

  8. D3.js部署node环境开发

    总结一段D3.js部署node环境的安装过程 准备阶段: 首先电脑上要安装node环境,这个阶段过滤掉,如果node环境都不会装,那就别玩基于node环境搞的其他东西了. 搭建环境: 我在自己的F:系 ...

  9. vert.x学习(七),使用表单获取用户提交的数据

    在web开发中,用的最多的就是表单了,用户通过表单提交数据到系统后台,系统又可以通过表单传递的数据做业务分析.那么这章就学习在vert.x中怎么使用表单,获取表单的参数值. 编写一个表单模板代码res ...

随机推荐

  1. cetos6.8配置svn服务器

    一,安装步骤 1,  检查是否安装过svn rpm -qa subversion 2,  卸载旧版本svn yum remove subversion 3,  安装SVN,输入官网提供的命令 yum ...

  2. C语言博客作业05--指针

    1.本章学习总结 1.1 思维导图 1.2 本章学习体会及代码量学习体会 1.2.1 学习体会 1.2.2 代码累计 2.PTA总分 2.1截图PTA中函数题目集的排名得分 2.2 我的总分: 3.P ...

  3. 树莓派3B+(一)

    第一步:安装raspbian系统 介绍:Raspbian是为树莓派设计,基于Debian的操作系统,由一个小团队开发.其不隶属于树莓派基金会,但被列为官方支持的操作系统. 下载地址:https://w ...

  4. Verilog语言实现并行(循环冗余码)CRC校验

    1 前言 (1)    什么是CRC校验? CRC即循环冗余校验码:是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定.循环冗余检查(CRC)是一种数据传输检错功能, ...

  5. Java定义三个点Object...

    从Java 5开始,Java语言对方法参数支持一种新写法,叫 可变长度参数列表,其语法就是类型后跟...,表示此处接受的参数为0到多个Object类型的对象,或者是一个Object[]. public ...

  6. 【BZOJ4032】[HEOI2015]最短不公共子串(后缀自动机,序列自动机)

    [BZOJ4032][HEOI2015]最短不公共子串(后缀自动机,序列自动机) 题面 BZOJ 洛谷 题解 数据范围很小,直接暴力构建后缀自动机和序列自动机,然后直接在两个自动机上进行\(bfs\) ...

  7. pwn-ROP(2)

    通过int80系统只对静态编译有效,动态编译需要用其他方法 本题提供了一个地址输入端,输入函数地址会返回该函数的实际地址,我们用得到的实际地址-偏移地址=基地址,然后用基地址+任意函数的偏移地址就可以 ...

  8. JAVA 中的命名规则

    命名规则– 基本要求• 见名知意– 常见命名的规则 • 包 (其实就是文件夹,用于对类进行管理)– 全部小写, 多级包用点隔开.– com,com.itheima • 类– 一个单词首字母大写 Stu ...

  9. 011 Socket定义客户端

    引入命名空间: using System.Net; using System.Net.Sockets; using System.Threading;

  10. Bootstrap里的Modal框