learn.lianglianglee.com/文章/Vert.x 基础入门.md.html
2022-05-11 19:04:14 +08:00

849 lines
46 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>Vert.x 基础入门.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析.md.html</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析.md.html</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通.md.html</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析.md.html</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器.md.html</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点.md.html</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结.md.html</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析.md.html</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇).md.html</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决.md.html</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI.md.html</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal.md.html</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践.md.html</a>
</li>
<li>
<a href="/文章/Java魔法类Unsafe应用解析.md.html">Java魔法类Unsafe应用解析.md.html</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记.md.html</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log).md.html</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享.md.html</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集.md.html</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL.md.html</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?.md.html</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC.md.html</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL优化优化 select count().md.html">MySQL优化优化 select count().md.html</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁.md.html</a>
</li>
<li>
<a href="/文章/MySQL的MVCC多版本并发控制.md.html">MySQL的MVCC多版本并发控制.md.html</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践.md.html</a>
</li>
<li>
<a href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦.md.html</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter.md.html</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理.md.html</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘.md.html</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖.md.html</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式.md.html</a>
</li>
<li>
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition.md.html</a>
</li>
<li>
<a class="current-tab" href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门.md.html</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践.md.html</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事.md.html</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战.md.html</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用.md.html</a>
</li>
<li>
<a href="/文章/从SpringCloud开始聊微服务架构.md.html">从SpringCloud开始聊微服务架构.md.html</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft.md.html</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析.md.html</a>
</li>
<li>
<a href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计.md.html</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?.md.html</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践.md.html</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器.md.html</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?.md.html</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统.md.html</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型.md.html</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时我们在讨论些神马.md.html">当我们在讨论CQRS时我们在讨论些神马.md.html</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制.md.html</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答.md.html</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM).md.html</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践.md.html</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html">服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache.md.html</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现.md.html</a>
</li>
<li>
<a href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge.md.html</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索.md.html</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?.md.html</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?.md.html</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践.md.html</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践.md.html</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现.md.html</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考.md.html</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题.md.html</a>
</li>
<li>
<a href="/文章/进阶Dockerfile 高阶使用指南及镜像优化.md.html">进阶Dockerfile 高阶使用指南及镜像优化.md.html</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构.md.html</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>Vert.x 基础入门</h1>
<p>Vert.x 是一个轻量级、高性能、模块化的响应式编程技术,天生自带异步、分布式属性,简约而不简单,是目前实现微服务和 Serverless 架构的最热门框架之一。</p>
<p>2011 年,在 VMware 工作的 Tim Fox 开始开发 Vert.x。2014 年 5 月Vert.x 在 JAX 创新奖中荣获“最具创新性的 Java 技术”奖。经过 8 年的发展Vert.x 已经成长为一个功能全面的响应式编程框架,并且拥有了一个较为丰富的生态圈。通过使用 Vert.x 可以快速开发出高性能的 HTTP 服务和 WebSocket 服务,也可以作为底层网络通信框架减少多线程编程的复杂性。</p>
<p>工程师对于性能的追求是无止境的,而响应式编程是提升系统性能的一大利器。追求极致的你是否愿意跟我一起来一探究竟呢?</p>
<p>本文将带你一起入门 Vert.x ,通过 <strong>Vert.x 是什么</strong>来理解这个工具的框架,然后再通过<strong>主要功能探究</strong>来学习主要功能,最后通过一个小实战项目来巩固所学的知识。文中提到的代码都可以通过 https://github.com/roytrack/vertx-gitchat 来下载。下面我们开始吧~</p>
<h3>Vert.x 是什么</h3>
<p>在 Vert.x 的官网上 <a href="https://vertx.io/">Vert.x</a> 有这么一段话说明了它是什么。</p>
<blockquote>
<p><em>Eclipse Vert.x is a tool-kit for building reactive applications on the JVM.</em></p>
</blockquote>
<p>翻译过来就是Vert.x 是构建基于 JVM 的反应式应用的工具包。 首先,它是一个全家桶式的工具包,既可以用简单的代码来创建一个高性能的网络服务器,也就是可以作为类似于 Apache 的 httpclient 来使用,提供 HTTP client 、DNS client 等一系列工具,同时提供各类好用的功能集合:</p>
<ul>
<li>数据访问:提供 MongoDB、JDBC、Redis 等客户端;</li>
<li>集成其他框架邮件客户端、JCA 适配器、RabbitMQ 和 Kafka 客户端、Consul 客户端、MQTT 服务器;</li>
<li>事件总线桥接Camel 和 TCP 的事件总线</li>
<li>登录验证集成JDBC 验证、JWT 验证、Shiro 验证、MongoDB 验证、OAuth2 验证</li>
<li>反应式编程Vert.x Rx、反应式流处理、Vert.x Sync</li>
<li>微服务支持:服务发现、配置管理、熔断器</li>
<li>开发运维shell 和 docker 的支持、度量度量工具和包管理工具</li>
<li>异步测试支持Vert.x Unit</li>
<li>集群支持Hazelcast、Infinispan、Apache Ignite、Apache Zookeeper</li>
<li>云支持OpenShift Cartridge</li>
</ul>
<p>其次,它可以用来构建反应式应用的,在其官网右上角有<a href="https://www.reactivemanifesto.org/zh-CN">反应式宣言</a>,声明了反应式系统的特质:</p>
<ol>
<li>即时响应性</li>
<li>回弹性</li>
<li>弹性</li>
<li>消息驱动</li>
</ol>
<p>文中链接有对应宣言的翻译解释链接,限于篇幅,就不在此探讨了。</p>
<h4>框架定位</h4>
<p>Vert.x 的定位非常灵活。</p>
<p>简单的网络工具、复杂的 web 应用程序、 HTTP / REST 微服务、海量事件处理、完整的后端消息总线应用程序都可以用 Vert.x 来做。</p>
<p>Vert.x 并不是一个严格意义上的框架或者容器,框架开发者不会告诉你一条“正确”的道路去写一个应用,而是给你了很多强有力的工具,让你去按照自己希望的方式去构建应用。</p>
<p>例如很多人在学习 Vert.x 的时候认为 Verticle 必不可少,其实可以只去用它的 HTTP client而不用其他的这也是可以的。</p>
<p>另外一个体现就是不强制要求只用 Vert.x 提供的功能,而是可以与其他类库灵活的组合使用,例如 Spring 的 IOC 容器就可以灵活搭配起来。</p>
<h4>框架功能简介</h4>
<p>Vert.x 生态系统是由一系列模块组成的,一部分是官方维护,一部分是志愿者维护。具体如下图结构:</p>
<p><img src="assets/vertx_gitchat_eco.jpg" alt="eco" /></p>
<p>Vert.x Core 作为最核心部分,提供了以下功能:</p>
<ol>
<li>TCP 客户端和服务器;</li>
<li>HTTP 客户端和服务器,并且支持 WebSocket ,这部分我们会在下面<strong>常用两个协议 ( HTTP &amp; WebSocket )</strong> 进一步展开讲;</li>
<li>事件总线,用来进行信息传递,实质是一种基于 TCP 连接的消息队列,支持点对点和发布订阅两种机制;</li>
<li>共享数据,通过本地的 map 结构和集群级别的分布式 map</li>
<li>定时器和延迟操作,通过 vertx.setPeriodic 实现周期定时执行, vertx.setTimer 来实现一次性的调用(延时操作);</li>
<li>装载、卸载部署单元;</li>
<li>UDP 数据报文;</li>
<li>DNS 客户端;</li>
<li>访问文件系统;</li>
<li>高可用;</li>
<li>集群化。</li>
</ol>
<p>Vert.x Core 只包含一个非常轻量级的 jar 包,里面有以上的核心功能。</p>
<p>Vert.x Web 基于 Vert.x Core提供了一系列更丰富的功能以便更容易地开发实际的 Web 应用。它继承了 Vert.x 2.x 里的 <a href="https://pmlopes.github.io/yoke/">Yoke</a> 的特点,灵感来自于 Node.js 的框架 <a href="https://expressjs.com/">Express</a> 和 Ruby 的框架 <a href="http://www.sinatrarb.com/">Sinatra</a> 等等。Vert.x Web 的设计是强大的非侵入式的并且是完全可插拔的。Vert.x Web 非常适合编写 <strong>RESTful HTTP 微服务</strong></p>
<p>Vert.x Stack 包含一系列提供鉴权、网络、数据访问等的官方扩展 jar 包。</p>
<p>而 Vert.x Awesome 则包含了官方和非官方提供的各种扩展,例如各种书籍、构建工具、云支持、容器支持等。</p>
<p>下面放上一个一文全的地址: https://github.com/vert-x3/vertx-awesome ,可以体会一下 Vert.x 的生态规模。</p>
<h4>黄金法则</h4>
<p>Vert.x 中有一个黄金法则,就是不要阻塞 <em>Event Loop</em></p>
<p>这个黄金法则设立的原因我们来一起探索一下。</p>
<p>大部分 Vert.x 的 API 都是事件驱动的,通过事件来调用起对应的 handler,这样就可以解耦,进行异步操作。</p>
<p>例如下面的代码示例,当有 HTTP 请求过来的时候,调用这个 handler 来返回响应信息:</p>
<pre><code class="language-Java">server.requestHandler(request -&gt; {
// This handler will be called every time an HTTP request is received at the server
request.response().end(&quot;hello world!&quot;);
});
</code></pre>
<p>相比于以前的处理,就是不会再阻塞处理,而是通过事件驱动,让 Vert.x 自己主动调用。</p>
<p>这样引入的优势是Vert.x 可以通过少量的线程,处理大量的并发事件。如果这里面并发事件同时出现了与线程数相同的阻塞操作,例如读取文件,那么所有线程都被阻塞,整个程序就被挂起、拒绝服务了。</p>
<p>在大部分情况下Vert.x 在一个线程里调用对应的 handlers这个线程就叫做一个 <strong>Event Loop</strong></p>
<p><img src="assets/event-loop.png" alt="img" /></p>
<p>图片来自 Vert.x官网https://vertx.io/docs/guide-for-java-devs/</p>
<p>Vert.x 保证自己的 API 是非阻塞的,并且不会阻塞 <em>Event Loop</em>,但是但是无法控制你自己的代码是否是阻塞的,所以会对这部分进行监控,并通过日志警告你。我们来造一个阻塞操作看看:</p>
<pre><code class="language-Java">public class BlockWarningDemo extends AbstractVerticle {
@Override
public void start() throws Exception {
Thread.sleep(3000L);
}
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
BlockWarningDemo blockWarningDemo = new BlockWarningDemo();
vertx.deployVerticle(blockWarningDemo);
vertx.close();
}
}
</code></pre>
<p>运行上面的代码就可以得到一个阻塞被警告的日志:</p>
<pre><code class="language-Shell">三月 03, 2019 11:08:06 上午 io.vertx.core.impl.BlockedThreadChecker
警告: Thread Thread[vert.x-eventloop-thread-0,5,main] has been blocked for 2847 ms, time limit is 2000 ms
</code></pre>
<p>默认 <em>Event Loop</em> 的线程执行时间是 2 秒,上面的代码让线程睡了三秒就直接被检查出来了。与此类似的还有以下的阻塞操作:</p>
<ol>
<li>等待锁;</li>
<li>等待互斥锁或者同步代码块;</li>
<li>数据库长时间查询;</li>
<li>进行消耗很多时间的大量计算;</li>
<li>文件句柄的获取与读写。</li>
</ol>
<p>那么这些阻塞的操作我们怎么做呢?使用 vertx.executeBlocking 或者使用一类新的 Verticle 叫做 <strong>Worker Verticle</strong></p>
<p>根本上就是让这些阻塞操作在一组内部或者自定义的线程池上执行,而不要在处理事件的 Event Loop 操作。待处理完毕后,再通过 future 对应的 handler 来回调 <em>Event Loop</em> 进行后续处理。</p>
<p>通过这一部分的讲解,我们知道了 Vert.x 是构建基于 JVM 的反应式应用的工具包,提供了一批可用于 TCP 、 UDP 、 HTTP 、 DNS 的工具集,具有较好的开源生态,并且对其黄金法则进行了探索与实战。</p>
<h3>主要功能探究</h3>
<p>Vert.x 提供了很多功能,我们学习起来可能会有点选择困难症,下面我挑了几个比较核心的,能尽快形成生产力的功能来进行探索。</p>
<h4>部署单元 ( Verticle )</h4>
<p>在学习 Vert.x 的部署单元之前,我们先来理解一下 Actor 模型。</p>
<blockquote>
<p>你可以将 Actor 当作是一群人,他们互相之间不会面对面地交流,而只是通过邮件的方式进行沟通。</p>
</blockquote>
<p>Verticle 可以<strong>近似</strong>的看做 Actor 模型中的 actor (之所以说近似,因为各个 Verticle 还是可以通过共享 map 来通信。)。不同的 verticle 实体部署在一个 Vert.x 实例中,通过 <em>Event Bus</em> 来进行消息传递。</p>
<p>定义自己的 Verticle 需要扩展 AbstractVerticle ,逻辑可以写在重载的不带参数的 start 、 stop 方法中,表示在 Verticle 部署后,卸载后执行的逻辑。 如果重载带参数 Future 的 start 、 stop 方法,则表示在部署或者卸载 Verticle 前要做一些逻辑,做完后将 future 设置为成功或者失败,标志着 Verticle 相应动作成功或失败。</p>
<p>Verticle 分为三类:</p>
<ul>
<li>标准 Verticle ,在 <em>Event Loop</em> 上执行;</li>
<li>工作 Verticle ,在工作线程池运行。一个实例不会同时在多于一个线程上执行;</li>
<li>多线程工作 Verticle ,在工作线程池运行,是高阶特性,一个实例不会同时在多于一个线程上执行,但是实例自身可以开启多个线程。</li>
</ul>
<p>Verticle 最大的特色就是它的线程隔离性。在启动的时候, Verticle 就被分配给了创建和 start 方法调用的 <em>Event Loop</em> 了。当调用一个使用 core API 的 handler 的方法的时候, Vert.x 保证这些 handler 将在同一个 <em>Event Loop</em> 上执行的。</p>
<p>也就是说在 Verticle 实例的代码保证是在同一个 <em>Event Loop</em> 执行。</p>
<p>上文中的 <em>BlockWarningDemo</em> 就是一个标准 Verticle 。如果要转化为工作 Verticle ,在部署的时候进行以下配置:</p>
<pre><code class="language-Java">DeploymentOptions options = new DeploymentOptions().setWorker(true);
BlockWarningDemo blockWarningDemo = new BlockWarningDemo();
vertx.deployVerticle(blockWarningDemo, options);
</code></pre>
<p>这样这个 Verticle 就不在 <em>Event Loop</em> 上分配了,转而分配到 Vert.x 的内部工作线程池上。</p>
<p><img src="assets/event-bus.png" alt="img" /></p>
<p>图片来自 Vert.x官网https://vertx.io/docs/guide-for-java-devs/</p>
<h4>链式 API 与异步 ( Fluent API &amp; Asynchronous )</h4>
<p>链式操作和异步处理,是始终贯穿 Vert.x API 中的两个通用模式。我们在编写程序的时候更期望使用流畅的 API ,一路处理下来即可。例如使用 Builder 可以更方便的获取新的对象:</p>
<pre><code class="language-Java">package com.roytrack.fluent;
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class LombokDemo {
private final String userName;
private final Integer age;
public static void main(String[] args) {
LombokDemo demo = LombokDemo.builder().userName(&quot;roy&quot;).age(30).build();
System.out.println(demo);
}
}
</code></pre>
<p>亦或者是 Java 8 中引入的流处理 API ,更方便的进行流处理:</p>
<pre><code class="language-Java">package com.roytrack.fluent;
import java.util.ArrayList;
import java.util.List;
public class Java8Demo {
public static void main(String[] args) {
List&lt;Integer&gt; list = new ArrayList();
for (int i = 0; i &lt; 100; i++) {
list.add(i);
}
int result = list.stream().map(v -&gt; v + 1).reduce(0, (v1, v2) -&gt; v1 + v2);
System.out.println(result);
}
}
</code></pre>
<p>Vert.x 也大量的使用了类似的 API 设计,下面这个例子展示了它灵活的 Json 构造和处理:</p>
<pre><code class="language-Java">package com.roytrack.fluent;
import io.vertx.core.json.JsonObject;
public class VertxDemo {
public static void main(String[] args) {
JsonObject jsonObject = new JsonObject().put(&quot;name&quot;, &quot;roy&quot;).put(&quot;age&quot;, &quot;30&quot;);
System.out.println(jsonObject.toString());
}
}
</code></pre>
<p>对于异步处理,大部分是通过设置 handler ,让 Vert.x 自己根据响应去调用,举例一个 request 的返回如下:</p>
<pre><code class="language-Java">vertx.createHttpServer().requestHandler(req -&gt; {
req.response()
.putHeader(&quot;content-type&quot;, &quot;text/plain&quot;)
.end(&quot;Hello from Vert.x!&quot;);
});
</code></pre>
<p>配合使用异步处理和链式 API 操作,我们既可以获得代码的简洁性,也可以得到分布式下的高性能。</p>
<h4>常用两个协议 ( HTTP &amp; WebSocket )</h4>
<p>下面我们来写一个最小的 HTTP 服务:</p>
<pre><code class="language-Java">package com.roytrack.http;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
public class MinimalHttpServer {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
//创建 httpServer
HttpServer server = vertx.createHttpServer().requestHandler(req -&gt; {
req.response()
.putHeader(&quot;content-type&quot;, &quot;text/plain&quot;)
.end(&quot;Hello from Vert.x!&quot;);
});
//指定监听端口
server.listen(8080, res -&gt; {
if (res.succeeded()) {
System.out.println(&quot;Begin http server !&quot;);
} else {
System.out.println(&quot;Http server occured error &quot; + res.cause());
}
});
}
}
</code></pre>
<p>访问后获得结果:</p>
<p><img src="assets/http_back.png" alt="http response" /></p>
<p>如果想同时构建 HTTP 和 WebSocket 服务,处理起来也很简单,代码如下:</p>
<pre><code class="language-Java">package com.roytrack.http;
import io.vertx.core.Vertx;
public class HttpAndWsServer {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
//创建 httpServer
vertx.createHttpServer()
//增加 websocket服务
.websocketHandler(ws -&gt; {
System.out.println(&quot;path is &quot; + ws.path());
if (!ws.path().equals(&quot;/ws&quot;)) {
ws.reject();
}
ws.textMessageHandler(msg -&gt; {
ws.writeTextMessage(msg);
});
})
//增加 http服务
.requestHandler(req -&gt; {
req.response()
.putHeader(&quot;content-type&quot;, &quot;text/plain&quot;)
.end(&quot;Hello from Vert.x!&quot;);
})
//指定监听端口
.listen(8080, res -&gt; {
if (res.succeeded()) {
System.out.println(&quot;Begin http server !&quot;);
} else {
System.out.println(&quot;Http server occured error &quot; + res.cause());
}
});
}
}
</code></pre>
<p>通过访问 WebSocket 返回如下:</p>
<p><img src="assets/ws_back.png" alt="http response" /> 同时,访问 HTTP 也不受影响,因为 WebSocket 协议的升级包也是 HTTP 协议。</p>
<p>另外如果使用 Verticle 多实例部署,也可以共用一个端口,这样一个 Verticle 停用或者卸载了,也不影响其他 Verticle 的服务,体现了反应式宣言中的回弹性。</p>
<h4>多语言编程 ( Polyglot )</h4>
<p>Vert.x 不止支持 Java还官方支持 Ceylon 、 Grovvy 、 JavaScript 、Ruby 、 Scala 、 Kotlin ,贡献者支持 Python 、 TypeScript 。</p>
<p>最快体验多语言特性,莫过于安装一下 Vert.x 的命令行工具,下面是两种安装方式。</p>
<p>通过 Node 安装 vertx ,如果是 windows 系统,可以进入到对应 module 中执行 vertx.bat</p>
<pre><code class="language-Shell">npm install vertx3-full
</code></pre>
<p>或者MacOS 通过 Homebrew 安装 vertx </p>
<pre><code class="language-Shell">brew install vert.x
</code></pre>
<p>安装后就可以执行 vertx 命令了。</p>
<p>编写一个 js 脚本 my-verticle.js 如下:</p>
<pre><code class="language-JavaScript">var server = vertx.createHttpServer();
server.requestHandler(function (request) {
request.response().end(&quot;Hello world&quot;);
});
server.listen(8080);
</code></pre>
<p>通过执行以下命令,就可以启动一个 httpServer 返回 Hello world 了。</p>
<pre><code class="language-Shell">vertx run my-verticle.js
</code></pre>
<h3>项目实战 :构建一个资源管理器</h3>
<p>经过以上的学习,我们对 Vert.x 的主要功能有一些了解了,下面我们来实战一个项目。</p>
<p>需求:调用 MXBean 来获取 cpu 和内存,然后通过 eventbus 发送。另外一个接收 eventbus ,发送到页面进行展现。 最终效果如下图:</p>
<p><img src="assets/dashboard.png" alt="dashboard" /> 最终完成的项目地址为https://github.com/roytrack/vertx-gitchat</p>
<h4>构建 Agent</h4>
<p>com.roytrack.dashboard.Agent 核心代码如下:</p>
<pre><code class="language-Java"> @Override
public void start() {
OperatingSystemMXBean systemMBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
String pid = UUID.randomUUID().toString();
vertx.setPeriodic(1000, id -&gt; {
JsonObject metrics = new JsonObject();
metrics.put(&quot;CPU&quot;, systemMBean.getProcessCpuLoad());
metrics.put(&quot;Mem&quot;, systemMBean.getTotalPhysicalMemorySize() - systemMBean.getFreePhysicalMemorySize());
vertx.eventBus().publish(&quot;metrics&quot;, new JsonObject().put(pid, metrics));
});
}
</code></pre>
<p>获取系统的 MBean ,设置每秒执行一次,将相关参数放入到 JsonObject ,然后通过 EventBus 发送到 metrics 这个地址。</p>
<h4>构建 Server</h4>
<p>com.roytrack.dashboard.Server 核心代码如下:</p>
<pre><code class="language-Java"> @Override
public void start() {
Router router = Router.router(vertx);
// The web server handler
router.route().handler(StaticHandler.create().setCachingEnabled(false));
router.get(&quot;/dashboard&quot;).handler(ctx -&gt; {
ctx.response()
.putHeader(&quot;Content-Type&quot;, &quot;application/json&quot;)
.end(dashboard.encode());
});
vertx.eventBus().&lt;JsonObject&gt;consumer(&quot;metrics&quot;).handler(msg -&gt; {
JsonObject metrics = msg.body();
dashboard.mergeIn(metrics);
});
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(8080);
}
</code></pre>
<p>StaticHandler 会去找 webroot 的静态资源,直接对外提供服务。</p>
<p>/dashboard 路径每次返回最新的数据。</p>
<p>消费地址为 metrics 的 EventBus ,来更新 Json 对象 dashboard 的内容。</p>
<p>最后创建 httpServer监听 8080 端口即可。</p>
<p>项目代码下载下来的同学,会发现给出的项目代码还使用了 hazelcast 作为集群发现的方式,可以作为一个进阶增强进行自学,有不懂的地方我们在读者圈继续交流。</p>
<h3>学习资料</h3>
<ol>
<li>我们可以通过 Vert.x 的官方文档系统学习它地址为https://vertx.io/docs/</li>
<li>同时github上有人翻译了中文版https://vertxchina.github.io/vertx-translation-chinese/</li>
<li>官网同时有一本电子书《使用Java构建响应式微服务》大家可以学习参考https://developers.redhat.com/promotions/building-reactive-microservices-in-java/</li>
<li>Vert.x生态的各种信息和例子都可以从这个项目里找到https://github.com/vert-x3/vertx-awesome</li>
</ol>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099802c7fc68b66","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>