mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
849 lines
46 KiB
HTML
849 lines
46 KiB
HTML
<!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 & 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 -> {
|
||
// This handler will be called every time an HTTP request is received at the server
|
||
request.response().end("hello world!");
|
||
});
|
||
</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 & 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("roy").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<Integer> list = new ArrayList();
|
||
for (int i = 0; i < 100; i++) {
|
||
list.add(i);
|
||
}
|
||
int result = list.stream().map(v -> v + 1).reduce(0, (v1, v2) -> 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("name", "roy").put("age", "30");
|
||
System.out.println(jsonObject.toString());
|
||
}
|
||
}
|
||
</code></pre>
|
||
<p>对于异步处理,大部分是通过设置 handler ,让 Vert.x 自己根据响应去调用,举例一个 request 的返回如下:</p>
|
||
<pre><code class="language-Java">vertx.createHttpServer().requestHandler(req -> {
|
||
req.response()
|
||
.putHeader("content-type", "text/plain")
|
||
.end("Hello from Vert.x!");
|
||
});
|
||
</code></pre>
|
||
<p>配合使用异步处理和链式 API 操作,我们既可以获得代码的简洁性,也可以得到分布式下的高性能。</p>
|
||
<h4>常用两个协议 ( HTTP & 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 -> {
|
||
req.response()
|
||
.putHeader("content-type", "text/plain")
|
||
.end("Hello from Vert.x!");
|
||
});
|
||
//指定监听端口
|
||
server.listen(8080, res -> {
|
||
if (res.succeeded()) {
|
||
System.out.println("Begin http server !");
|
||
} else {
|
||
System.out.println("Http server occured error " + 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 -> {
|
||
System.out.println("path is " + ws.path());
|
||
if (!ws.path().equals("/ws")) {
|
||
ws.reject();
|
||
}
|
||
ws.textMessageHandler(msg -> {
|
||
ws.writeTextMessage(msg);
|
||
});
|
||
})
|
||
//增加 http服务
|
||
.requestHandler(req -> {
|
||
req.response()
|
||
.putHeader("content-type", "text/plain")
|
||
.end("Hello from Vert.x!");
|
||
})
|
||
//指定监听端口
|
||
.listen(8080, res -> {
|
||
if (res.succeeded()) {
|
||
System.out.println("Begin http server !");
|
||
} else {
|
||
System.out.println("Http server occured error " + 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("Hello world");
|
||
});
|
||
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 -> {
|
||
JsonObject metrics = new JsonObject();
|
||
metrics.put("CPU", systemMBean.getProcessCpuLoad());
|
||
metrics.put("Mem", systemMBean.getTotalPhysicalMemorySize() - systemMBean.getFreePhysicalMemorySize());
|
||
vertx.eventBus().publish("metrics", 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("/dashboard").handler(ctx -> {
|
||
ctx.response()
|
||
.putHeader("Content-Type", "application/json")
|
||
.end(dashboard.encode());
|
||
});
|
||
vertx.eventBus().<JsonObject>consumer("metrics").handler(msg -> {
|
||
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>
|