Netty、Redis、Zookeeper高并发实战(txt+pdf+epub+mobi电子书下载)

作者:尼恩

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Netty、Redis、Zookeeper高并发实战

Netty、Redis、Zookeeper高并发实战试读:

前言

第1章 高并发时代的必备技能1.1 Netty为何这么火1.1.1 Netty火热的程度1.1.2 Netty是面试的必杀器1.2 高并发利器Redis1.2.1 什么是Redis1.2.2 Redis成为缓存事实标准的原因1.3 分布式利器ZooKeeper1.3.1 什么是ZooKeeper1.3.2 ZooKeeper的优势1.4 高并发IM的综合实践1.4.1 高并发IM的学习价值1.4.2 庞大的应用场景1.5 Netty、Redis、ZooKeeper实践计划1.5.1 第1天:Java NIO实践1.5.2 第2天:Reactor反应器模式实践1.5.3 第3天:异步回调模式实践1.5.4 第4天:Netty基础实践1.5.5 第5天:解码器(Decoder)与编码器(Encoder)实

践1.5.6 第6天:JSON和ProtoBuf序列化实践1.5.7 第7~10天:基于Netty的单聊实战1.5.8 第11天:ZooKeeper实践计划1.5.9 第12天:Redis实践计划1.6 本章小结

第2章 高并发IO的底层原理2.1 IO读写的基础原理2.1.1 内核缓冲区与进程缓冲区2.1.2 详解典型的系统调用流程2.2 四种主要的IO模型2.2.1 同步阻塞IO(Blocking IO)2.2.2 同步非阻塞NIO(None Blocking IO)2.2.3 IO多路复用模型(IO Multiplexing)2.2.4 异步IO模型(Asynchronous IO)2.3 通过合理配置来支持百万级并发连接2.4 本章小结

第3章 Java NIO通信基础详解3.1 Java NIO简介3.1.1 NIO和OIO的对比3.1.2 通道(Channel)3.1.3 Selector选择器3.1.4 缓冲区(Buffer)3.2 详解NIO Buffer类及其属性3.2.1 Buffer类3.2.2 Buffer类的重要属性3.2.3 4个属性的小结3.3 详解NIO Buffer类的重要方法3.3.1 allocate()创建缓冲区3.3.2 put()写入到缓冲区3.3.3 flip()翻转3.3.4 get()从缓冲区读取3.3.5 rewind()倒带3.3.6 mark( )和reset( )3.3.7 clear( )清空缓冲区3.3.8 使用Buffer类的基本步骤3.4 详解NIO Channel(通道)类3.4.1 Channel(通道)的主要类型3.4.2 FileChannel文件通道3.4.3 使用FileChannel完成文件复制的实践案例3.4.4 SocketChannel套接字通道3.4.5 使用SocketChannel发送文件的实践案例3.4.6 DatagramChannel数据报通道3.4.7 使用DatagramChannel数据包通道发送数据的实践案

例3.5 详解NIO Selector选择器3.5.1 选择器以及注册3.5.2 SelectableChannel可选择通道3.5.3 SelectionKey选择键3.5.4 选择器使用流程3.5.5 使用NIO实现Discard服务器的实践案例3.5.6 使用SocketChannel在服务器端接收文件的实践案例3.6 本章小结

第4章 鼎鼎大名的Reactor反应器模式4.1 Reactor反应器模式为何如此重要4.1.1 为什么首先学习Reactor反应器模式4.1.2 Reactor反应器模式简介4.1.3 多线程OIO的致命缺陷4.2 单线程Reactor反应器模式4.2.1 什么是单线程Reactor反应器4.2.2 单线程Reactor反应器的参考代码4.2.3 一个Reactor反应器版本的EchoServer实践案例4.2.4 单线程Reactor反应器模式的缺点4.3 多线程的Reactor反应器模式4.3.1 多线程池Reactor反应器演进4.3.2 多线程Reactor反应器的实践案例4.3.3 多线程Handler处理器的实践案例4.4 Reactor反应器模式小结4.5 本章小结

第5章 并发基础中的Future异步回调模式5.1 从泡茶的案例说起5.2 join异步阻塞5.2.1 线程的join合并流程5.2.2 使用join实现异步泡茶喝的实践案例5.2.3 详解join合并方法5.3 FutureTask异步回调之重武器5.3.1 Callable接口5.3.2 初探FutureTask类5.3.3 Future接口5.3.4 再探FutureTask类5.3.5 使用FutureTask类实现异步泡茶喝的实践案例5.4 Guava的异步回调5.4.1 详解FutureCallback5.4.2 详解ListenableFuture5.4.3 ListenableFuture异步任务5.4.4 使用Guava实现泡茶喝的实践案例5.5 Netty的异步回调模式5.5.1 详解GenericFutureListener接口5.5.2 详解Netty的Future接口5.5.3 ChannelFuture的使用5.5.4 Netty的出站和入站异步回调5.6 本章小结

第6章 Netty原理与基础6.1 第一个Netty的实践案例DiscardServer6.1.1 创建第一个Netty项目6.1.2 第一个Netty服务器端程序6.1.3 业务处理器NettyDiscardHandler6.1.4 运行NettyDiscardServer6.2 解密Netty中的Reactor反应器模式6.2.1 回顾Reactor反应器模式中IO事件的处理流程6.2.2 Netty中的Channel通道组件6.2.3 Netty中的Reactor反应器6.2.4 Netty中的Handler处理器6.2.5 Netty的流水线(Pipeline)6.3 详解Bootstrap启动器类6.3.1 父子通道6.3.2 EventLoopGroup线程组6.3.3 Bootstrap的启动流程6.3.4 ChannelOption通道选项6.4 详解Channel通道6.4.1 Channel通道的主要成员和方法6.4.2 EmbeddedChannel嵌入式通道6.5 详解Handler业务处理器6.5.1 ChannelInboundHandler通道入站处理器6.5.2 ChannelOutboundHandler通道出站处理器6.5.3 ChannelInitializer通道初始化处理器6.5.4 ChannelInboundHandler的生命周期的实践案例6.6 详解Pipeline流水线6.6.1 Pipeline入站处理流程6.6.2 Pipeline出站处理流程6.6.3 ChannelHandlerContext上下文6.6.4 截断流水线的处理6.6.5 Handler业务处理器的热拔插6.7 详解ByteBuf缓冲区6.7.1 ByteBuf的优势6.7.2 ByteBuf的逻辑部分6.7.3 ByteBuf的重要属性6.7.4 ByteBuf的三组方法6.7.5 ByteBuf基本使用的实践案例6.7.6 ByteBuf的引用计数6.7.7 ByteBuf的Allocator分配器6.7.8 ByteBuf缓冲区的类型6.7.9 三类ByteBuf使用的实践案例6.7.10 ByteBuf的自动释放6.8 ByteBuf浅层复制的高级使用方式6.8.1 slice切片浅层复制6.8.2 duplicate整体浅层复制6.8.3 浅层复制的问题6.9 EchoServer回显服务器的实践案例6.9.1 NettyEchoServer回显服务器的服务器端6.9.2 共享NettyEchoServerHandler处理器6.9.3 NettyEchoClient客户端代码6.9.4 NettyEchoClientHandler处理器6.10 本章小结

第7章 Decoder与Encoder重要组件7.1 Decoder原理与实践7.1.1 ByteToMessageDecoder解码器7.1.2 自定义Byte2IntegerDecoder整数解码器的实践案例7.1.3 ReplayingDecoder解码器7.1.4 整数的分包解码器的实践案例7.1.5 字符串的分包解码器的实践案例7.1.6 MessageToMessageDecoder解码器7.2 开箱即用的Netty内置Decoder7.2.1 LineBasedFrameDecoder解码器7.2.2 DelimiterBasedFrameDecoder解码器7.2.3 LengthFieldBasedFrameDecoder解码器7.2.4 多字段Head-Content协议数据帧解析的实践案例7.3 Encoder原理与实践7.3.1 MessageToByteEncoder编码器7.3.2 MessageToMessageEncoder编码器7.4 解码器和编码器的结合7.4.1 ByteToMessageCodec编解码器7.4.2 CombinedChannelDuplexHandler组合器7.5 本章小结

第8章 JSON和ProtoBuf序列化8.1 详解粘包和拆包8.1.1 半包问题的实践案例8.1.2 什么是半包问题8.1.3 半包现象的原理8.2 JSON协议通信8.2.1 JSON序列化的通用类8.2.2 JSON序列化与反序列化的实践案例8.2.3 JSON传输的编码器和解码器之原理8.2.4 JSON传输之服务器端的实践案例8.2.5 JSON传输之客户端的实践案例8.3 Protobuf协议通信8.3.1 一个简单的proto文件的实践案例8.3.2 控制台命令生成POJO和Builder8.3.3 Maven插件生成POJO和Builder8.3.4 消息POJO和Builder的使用之实践案例8.4 Protobuf编解码的实践案例8.4.1 Protobuf编码器和解码器的原理8.4.2 Protobuf传输之服务器端的实践案例8.4.3 Protobuf传输之客户端的实践案例8.5 详解Protobuf协议语法8.5.1 proto的头部声明8.5.2 消息结构体与消息字段8.5.3 字段的数据类型8.5.4 其他的语法规范8.6 本章小结

第9章 基于Netty的单体IM系统的开发实践9.1 自定义ProtoBuf编解码器9.1.1 自定义Protobuf编码器9.1.2 自定义Protobuf解码器9.1.3 IM系统中Protobuf消息格式的设计9.2 概述IM的登录流程9.2.1 图解登录/响应流程的9个环节9.2.2 客户端涉及的主要模块9.2.3 服务器端涉及的主要模块9.3 客户端的登录处理的实践案例9.3.1 LoginConsoleCommand和User POJO9.3.2 LoginSender发送器9.3.3 ClientSession客户端会话9.3.4 LoginResponceHandler登录响应处理器9.3.5 客户端流水线的装配9.4 服务器端的登录响应的实践案例9.4.1 服务器流水线的装配9.4.2 LoginRequestHandler登录请求处理器9.4.3 LoginProcesser用户验证逻辑9.4.4 EventLoop线程和业务线程相互隔离9.5 详解ServerSession服务器会话9.5.1 通道的容器属性9.5.2 ServerSession服务器端会话类9.5.3 SessionMap会话管理器9.6 点对点单聊的实践案例9.6.1 简述单聊的端到端流程9.6.2 客户端的ChatConsoleCommand收集聊天内容9.6.3 客户端的CommandController发送POJO9.6.4 服务器端的ChatRedirectHandler消息转发9.6.5 服务器端的ChatRedirectProcesser异步处理9.6.6 客户端的ChatMsgHandler接收POJO9.7 详解心跳检测9.7.1 网络连接的假死现象9.7.2 服务器端的空闲检测9.7.3 客户端的心跳报文9.8 本章小结

第10章 ZooKeeper分布式协调10.1 ZooKeeper伪集群安装和配置10.1.1 创建数据目录和日志目录:10.1.2 创建myid文件10.1.3 创建和修改配置文件10.1.4 配置文件示例10.1.5 启动ZooKeeper伪集群10.2 使用ZooKeeper进行分布式存储10.2.1 详解ZooKeeper存储模型10.2.2 zkCli客户端命令清单10.3 ZooKeeper应用开发的实践10.3.1 ZkClient开源客户端介绍10.3.2 Curator开源客户端介绍10.3.3 Curator开发的环境准备10.3.4 Curator客户端实例的创建10.3.5 通过Curator创建ZNode节点10.3.6 在Curator中读取节点10.3.7 在Curator中更新节点10.3.8 在Curator中删除节点10.4 分布式命名服务的实践10.4.1 ID生成器10.4.2 ZooKeeper分布式ID生成器的实践案例10.4.3 集群节点的命名服务之实践案例10.4.4 使用ZK实现SnowFlakeID算法的实践案例10.5 分布式事件监听的重点10.5.1 Watcher标准的事件处理器10.5.2 NodeCache节点缓存的监听10.5.3 PathChildrenCache子节点监听10.5.4 Tree Cache节点树缓存10.6 分布式锁的原理与实践10.6.1 公平锁和可重入锁的原理10.6.2 ZooKeeper分布式锁的原理10.6.3 分布式锁的基本流程10.6.4 加锁的实现10.6.5 释放锁的实现10.6.6 分布式锁的使用10.6.7 Curator的InterProcessMutex可重入锁10.7 本章小结

第11章 分布式缓存Redis11.1 Redis入门11.1.1 Redis安装和配置11.1.2 Redis客户端命令11.1.3 Redis Key的命名规范11.2 Redis数据类型11.2.1 String字符串11.2.2 List列表11.2.3 Hash哈希表11.2.4 Set集合11.2.5 Zset有序集合11.3 Jedis基础编程的实践案例11.3.1 Jedis操作String字符串11.3.2 Jedis操作List列表11.3.3 Jedis操作Hash哈希表11.3.4 Jedis操作Set集合11.3.5 Jedis操作Zset有序集合11.4 JedisPool连接池的实践案例11.4.1 JedisPool的配置11.4.2 JedisPool创建和预热11.4.3 JedisPool的使用11.5 使用spring-data-redis完成11.5.1 CRUD中应用缓存的场景11.5.2 配置spring-redis.xml11.5.3 使用RedisTemplate模板API11.5.4 使用RedisTemplate模板API完成CRUD的实践案例11.5.5 使用RedisCallback回调完成CRUD的实践案例11.6 Spring的Redis缓存注解11.6.1 使用Spring缓存注解完成CRUD的实践案例11.6.2 spring-redis.xml中配置的调整11.6.3 详解@CachePut和@Cacheable注解11.6.4 详解@CacheEvict注解11.6.5 详解@Caching组合注解11.7 详解SpringEL(SpEL)11.7.1 SpEL运算符11.7.2 缓存注解中的SpringEL表达式11.8 本章小结

第12章 亿级高并发IM架构的开发实践12.1 如何支撑亿级流量的高并发IM架构的理论基础12.1.1 亿级流量的系统架构的开发实践12.1.2 高并发架构的技术选型12.1.3 详解IM消息的序列化协议选型12.1.4 详解长连接和短连接12.2 分布式IM的命名服务的实践案例12.2.1 IM节点的POJO类12.2.2 IM节点的ImWorker类12.3 Worker集群的负载均衡之实践案例12.3.1 ImLoadBalance负载均衡器12.3.2 与WebGate的整合12.4 即时通信消息的路由和转发的实践案例12.4.1 IM路由器WorkerRouter12.4.2 IM转发器WorkerReSender12.5 Feign短连接RESTful调用12.5.1 短连接API的接口准备12.5.2 声明远程接口的本地代理12.5.3 远程API的本地调用12.6 分布式的在线用户统计的实践案例12.6.1 Curator的分布式计数器12.6.2 用户上线和下线的统计12.7 本章小结前言

移动时代、5G时代、物联网时代的大幕已经开启,它们对于高性能、高并发的开发知识和技术的要求,抬升了Java工程师的学习台阶和面试门槛。

大公司的面试题从某个侧面映射出生产场景中对专项技术的要求。高并发的面试题以前基本是BAT等大公司的专利,现在几乎蔓延至与Java项目相关的整个行业。例如,与Java NIO、Reactor模式、高性能通信、分布式锁、分布式ID、分布式缓存、高并发架构等技术相关的面试题,从以前的加分题变成了现在的基础题,这也映射出开发Java项目所必需的技术栈:分布式Java框架、Redis缓存、分布式搜索ElasticSearch、分布式协调ZooKeeper、消息队列Kafka、高性能通信框架Netty。本书内容

本书的内容源于“疯狂创客圈社群”的博客,以及社群持续迭代的CrazyIM项目,虽然书中重在讲解Netty、Redis、ZooKeeper的使用方法,但是还有一个更大的价值,就是为大家打下Java高并发开发技术的坚实基础。

首先,本书从操作系统的底层原理开始讲解:浅显易懂地剖析高并发IO的底层原理,并介绍如何让单体Java应用支持百万级的高并发;从传统的阻塞式OIO开始,细致地解析Reactor高性能模式,介绍高性能网络开发的基础知识;从Java的线程Join和线程池开始,介绍Java Future和Guava ListenableFuture两种常用异步回调技术。这些原理方面的基础知识非常重要,是大家在日常开发Java后台应用时解决实际问题的金钥匙。

接着,重点讲解Netty。这是目前当之无愧的高性能通信框架皇冠上的明珠,是支撑其他众多著名的高并发、分布式、大数据框架底层的框架。这里有两大特色:一是从Reactor模式入手,以四两拨千斤的方式来学习Netty原理;二是通过Netty来解决网络编程中的重点难题,如ProtoBuf序列化问题、半包问题等。

然后,对ZooKeeper进行详细的介绍。除了全面地介绍使用Curator API操作ZooKeeper之外,还从实战的角度出发,介绍如何使用ZooKeeper来设计分布式ID生成器,并对重要的SnowFlake算法进行详细的介绍。另外,还通过图文并茂和结合小故事的方式浅显易懂地介绍分布式锁的基本原理,并完成一个ZooKeeper分布式锁的小实践案例。

接下来,从实践开发层面对Redis进行说明,详细介绍Redis的5种数据类型、客户端操作指令、Jedis Java API。另外,还通过spring-data-redis来完成两种方式的数据分布式缓存,并详尽地介绍Spring的缓存注解以及涉及的SpEL表达式语言。

最后,通过CrazyIM项目介绍一个亿级流量的高并发IM系统模型。这个高并发架构的系统模型不仅仅限于IM系统,通过简单的调整和适配,就可以应用于当前主流的Java后台系统。读者对象(1)对Java NIO、高性能IO、高并发编程感兴趣的大专院校学生。(2)需要学习Java高并发技术、高并发架构的初、中级Java工程师。(3)生产项目中需要用到Netty、Redis、ZooKeeper三大框架的架构师或者项目人员。本书源代码下载

本书的源代码可以从https://gitee.com/sfasdfasdfsdf/netty_redis_zookeeper_source_code.git下载。另外,还可以登录机械工业出版社华章公司网站(www.hzbook.com)下载,先搜索到本书,然后在页面上的“资料下载”模块下载即可。如果下载有问题,请发送电子邮件至booksaga@126.com,邮件主题为“求Netty、Redis、ZooKeeper高并发实战下载资源”勘误和支持

由于作者水平和能力有限,不妥之处在所难免,希望读者批评指正。本书的读者QQ群为104131248,目前群中已经包含了不少高质量的面试题以及开发技术难题,欢迎读者入群进行交流。致谢

写书,不仅仅是一项技术活,而且是一项工匠活,为了确保书中知识的全面性、系统性,我需要不断地思考与总结。为了保证书中的每一行程序都是正确的,我需要反复地编写LLT用例去进行验证。总之,一本优质的书,意味着需要牺牲陪伴家人的大量时间。感谢我的妻子、孩子们给我一贯的支持和帮助!

感谢卞诚君老师在我写书过程中给予的指导和帮助。没有他的提议,我不会想到将自己发布在“疯狂创客圈”社群中高并发方面的博客文章整理成一本书出版。另外,还让我感觉到写博客和写书不是一个层面的事情。博客里的很多内容是不全面、不严谨的,甚至是错误的。

感谢“疯狂创客圈”社群中的小伙伴们,虽然你们的很多技术难题,我不一定能给出最佳的解答方案,但正是因为一路同行,一直坦诚、纯粹的技术交流,大家相互启发了许多技术灵感,拓展了彼此的技术视野,最终提升了水平。欢迎大家来“砸”问题,也欢迎大家多多交流。尼恩中国武汉第1章 高并发时代的必备技能

高并发时代已然到来,Netty、Redis、ZooKeeper是高并发时代的必备工具。1.1 Netty为何这么火

Netty是JBOSS提供的一个Java开源框架,是基于NIO的客户端/服务器编程框架,它既能快速开发高并发、高可用、高可靠性的网络服务器程序,也能开发高可用、高可靠的客户端程序。

注:NOI是指非阻塞输入输出(Non-Blocking IO),也称非阻塞IO。另外,本书为了行文上的一致性,把输入输出的英文缩写统一为IO,而不用I/O。1.1.1 Netty火热的程度

Netty已经有了成百上千的分布式中间件、各种开源项目以及各种商业项目的应用。例如火爆的Kafka、RocketMQ等消息中间件、火热的ElasticSearch开源搜索引擎、大数据处理Hadoop的RPC框架Avro、主流的分布式通信框架Dubbo,它们都使用了Netty。总之,使用Netty开发的项目,已经有点数不过来了……

Netty之所以受青睐,是因为Netty提供异步的、事件驱动的网络应用程序框架和工具。作为一个异步框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便地主动获取或者通过通知机制获得IO操作结果。

与JDK原生NIO相比,Netty提供了相对十分简单易用的API,因而非常适合网络编程。Netty主要是基于NIO来实现的,在Netty中也可以提供阻塞IO的服务。

Netty之所以这么火,与它的巨大优点是密不可分的,大致可以总结如下:

·API使用简单,开发门槛低。

·功能强大,预置了多种编解码功能,支持多种主流协议。

·定制能力强,可以通过ChannelHandler对通信框架进行灵活扩展。

·性能高,与其他业界主流的NIO框架对比,Netty的综合性能最优。

·成熟、稳定,Netty修复了已经发现的所有JDK NIO中的BUG,业务开发人员不需要再为NIO的BUG而烦恼。

·社区活跃,版本迭代周期短,发现的BUG可以被及时修复。1.1.2 Netty是面试的必杀器

Netty是互联网中间件领域使用最广泛、最核心的网络通信框架之一。几乎所有互联网中间件或者大数据领域均离不开Netty,掌握Netty是作为一名初中级工程师迈向高级工程师重要的技能之一。

目前来说,主要的互联网公司,例如阿里、腾讯、美团、新浪、淘宝等,在高级工程师的面试过程中,就经常会问一些高性能通信框架方面的问题,还会问一些“你有没有读过什么著名框架的源代码?”等类似的问题。

如果掌握了Netty相关的技术问题,更进一步说,如果你能全面地阅读和掌握Netty源代码,相信面试大公司时,一定底气十足,成功在握。1.2 高并发利器Redis

任何高并发的系统,不可或缺的就是缓存。Redis缓存目前已经成为缓存的事实标准。1.2.1 什么是Redis

Redis是Remote Dictionary Server(远程字典服务器)的缩写,最初是作为数据库的工具来使用的。是目前使用广泛、高效的一款开源缓存。Redis使用C语言开发,将数据保存在内存中,可以看成是一款纯内存的数据库,所以它的数据存取速度非常快。一些经常用并且创建时间较长的内容,可以缓存到Redis中,而应用程序能以极快的速度存取这些内容。举例来说,如果某个页面经常会被访问到,而创建页面时需要多次访问数据库、造成网页内容的生成时间较长,那么就可以使用Redis将这个页面缓存起来,从而减轻了网站的负担,降低了网站的延迟。

Redis通过键-值对(Key-Value Pair)的形式来存储数据,类似于Java中的Map映射。Redis的Key键,只能是string字符串类型。Redis的Value值类型包括:string字符类型、map映射类型、list列表类型、set集合类型、sortedset有序集合类型。

Redis的主要应用场景:缓存(数据查询、短连接、新闻内容、商品内容等)、分布式会话(Session)、聊天室的在线好友列表、任务队列(秒杀、抢购、12306等)、应用排行榜、访问统计、数据过期处理(可以精确到毫秒)。1.2.2 Redis成为缓存事实标准的原因

相对于其他的键-值对(Key-Value)内存数据库(如Memcached)而言,Redis具有如下特点:(1)速度快 不需要等待磁盘的IO,在内存之间进行的数据存储和查询,速度非常快。当然,缓存的数据总量不能太大,因为受到物理内存空间大小的限制。(2)丰富的数据结构 除了string之外,还有list、hash、set、sortedset,一共五种类型。(3)单线程,避免了线程切换和锁机制的性能消耗。(4)可持久化 支持RDB与AOF两种方式,将内存中的数据写入外部的物理存储设备。(5)支持发布/订阅。(6)支持Lua脚本。(7)支持分布式锁 在分布式系统中,如果不同的节点需要访同到一个资源,往往需要通过互斥机制来防止彼此干扰,并且保证数据的一致性。在这种情况下,需要使用到分布式锁。分布式锁和Java的锁用于实现不同线程之间的同步访问,原理上是类似的。(8)支持原子操作和事务 Redis事务是一组命令的集合。一个事务中的命令要么都执行,要么都不执行。如果命令在运行期间出现错误,不会自动回滚。(9)支持主-从(Master-Slave)复制与高可用(Redis Sentinel)集群(3.0版本以上)(10)支持管道 Redis管道是指客户端可以将多个命令一次性发送到服务器,然后由服务器一次性返回所有结果。管道技术的优点是:在批量执行命令的应用场景中,可以大大减少网络传输的开销,提高性能。1.3 分布式利器ZooKeeper

突破了单体瓶颈之后的高并发,就必须靠集群了,而集群的分布式架构和协调,一定少不了可靠的分布式协调工具,ZooKeeper就是目前极为重要的分布式协调工具。1.3.1 什么是ZooKeeper

ZooKeeper最早起源于雅虎公司研究院的一个研究小组。在当时,研究人员发现,在雅虎内部很多大型的系统需要依赖一个类似的系统进行分布式协调,但是这些系统往往存在分布式单点问题。所以雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架。在项目初期给这个项目命名的时候,准备和很多项目一样,按照雅虎公司的惯例要用动物的名字来命名的(例如著名的Pig项目)。在进行探讨取什么名字的时候,研究院的首席科学家Raghu Ramakrishnan开玩笑说:再这样下去,我们这儿就变成动物园了。此话一出,大家纷纷表示就叫动物园管理员吧,于是,ZooKeeper的名字由此诞生了。当然,取名ZooKeeper,也绝非没有一点儿道理。ZooKeeper的功能,正好是用来协调分布式环境的,协调各个以动物命名的分布式组件,所以,ZooKeeper这个名字也就“名副其实”了。1.3.2 ZooKeeper的优势

ZooKeeper对不同系统环境的支持都很好,在绝大多数主流的操作系统上都能够正常运行,如:GNU/Linux、Sun Solaris、Win32以及MacOS等。需要注意的是,ZooKeeper官方文档中特别强调,由于FreeBSD系统的JVM(Java Virtual Machine,即Java虚拟机)对Java的NIO Selector选择器支持得不是很好,因此不建议在FreeBSD系统上部署生产环境的ZooKeeper服务器。

ZooKeeper的核心优势是,实现了分布式环境的数据一致性,简单地说:每时每刻我们访问ZooKeeper的树结构时,不同的节点返回的数据都是一致的。也就是说,对ZooKeeper进行数据访问时,无论是什么时间,都不会引起脏读、重复读。注:脏读是指在数据库存取中无效数据的读出。

ZooKeeper提供的功能都是分布式系统中非常底层且必不可少的基本功能,如果开发者自己来实现这些功能而且要达到高吞吐、低延迟同时的还要保持一致性和可用性,实际上是非常困难的。因此,借助ZooKeeper提供的这些功能,开发者就可以轻松在ZooKeeper之上构建自己的各种分布式系统。1.4 高并发IM的综合实践

为了方便交流和学习,笔者组织了一帮高性能发烧友,成立了一个高性能社群,叫作“疯狂创客圈”。同时,牵头组织社群的小伙伴们,应用Netty、Redis、ZooKeeper持续迭代一个高并发学习项目,叫作“CrazyIM”。1.4.1 高并发IM的学习价值

为什么要开始一个高并发IM(即时通信)的实践呢?

首先,通过实践完成一个分布式、高并发的IM系统,具有相当的技术挑战性。这一点,对于从事传统的企业级Web开发者来说,相当于进入了一片全新的天地。企业级Web,QPS(Query Per Second,每秒查询率)峰值可能在1000以内,甚至在100以内,没有多少技术挑战性和含金量,属于重复性的CRUD的体力活。注:CRUD是指Create(创建)、Retrieve(查询)、Update(更新)和Delete(删除)。而一个分布式、高并发的IM系统,面临的QPS峰值可能在十万、百万、千万,甚至上亿级别。对于此纵深层次化的、递进的高并发需求,将无极限地考验着系统的性能。需要不断地从通信协议、到系统的架构进行优化,对技术能力是一种非常极致的考验和训练。

其次,就具有不同QPS峰值规模的IM系统而言,它们所处的用户需求环境是不一样的。这就造成了不同用户规模的IM系统,各自具有一定的市场需求和实际需要,因而它们不一定都需要上亿级的高并发。但是,作为一个顶级的架构师,就应该具备全栈式的架构能力,对不同用户规模的、差异化的应用场景,提供和架构出与对应的应用场景相匹配的高并发IM系统。也就是说,IM系统综合性相对较强,相关的技术需要覆盖到满足各种不同应用场景的网络传输、分布式协调、分布式缓存、服务化架构等。

接下来具体看看高并发IM的应用场景吧。1.4.2 庞大的应用场景

一切高实时性通信、消息推送的应用场景,都需要高并发IM。随着移动互联网、AI的飞速发展,高性能、高并发IM,有着非常广泛的应用场景。

高并发IM典型的应用场景如下:私信、聊天、大规模推送、视频会议、弹幕、抽奖、互动游戏、基于位置的应用(Uber、滴滴司机位置)、在线教育、智能家居等,如图1-1所示。图1-1 高并发IM典型的应用场景

尤其是对于APP开发的小伙伴们来说,IM已经成为大多数APP的标配。在移动互联网时代,推送(Push)服务成为APP应用不可或缺的重要组成部分,推送服务可以提升用户的活跃度和留存率。我们的手机每天接收到各种各样的广告和提示消息等,它们大多数都是通过推送服务实现的。

随着5G时代物联网的发展,未来所有接入物联网的智能设备,都将是IM系统的客户端,这就意味着推送服务会在未来面临海量的设备和终端接入。为了支持这些千万级、上亿级的终端,一定是需要强悍的后台系统。

有这么多的应用场景,对于想成长为Java高手的小伙伴们,高并发IM都是绕不开的一个话题。

对于想在后台有所成就的小伙伴们来说,高并发IM实践,更是在与终极BOSS PK之前的一场不可避免的打怪练手。1.5 Netty、Redis、ZooKeeper实践计划

下面从最基础的NIO入手,列出一个大致12天的实践计划,帮助大家深入掌握Netty、Redis、ZooKeeper。1.5.1 第1天:Java NIO实践

实践一:使用FileChannel复制文件

通过使用Channel通道,完成复制文件。

本环节的目标是掌握以下知识:Java NIO中ByteBuffer、Channel两个重要组件的使用。

接着是升级实战的案例,使用文件Channel通道的transferFrom方法,完成高效率的文件复制。

实践二:使用SocketChannel传输文件

本环节的目标是掌握以下知识:

·非阻塞客户端在发起连接后,需要不断的自旋,检测连接是否完成的。

·SocketChannel传输通道的read读取方法、write写入方法。

·在SocketChannel传输通道关闭前,尽量发送一个输出结束标志到对方端。

实践三:使用DatagramChannel传输数据

客户端使用DatagramChannel发送数据,服务器端使用DatagramChannel接收数据。

本环节的目标是掌握以下知识:

·使用接收数据方法receive,使用发送数据方法send。

·DatagramChannel和SocketChannel两种通道,在发送、接收数据上的不同。

实战四:使用NIO实现Discard服务器

客户端功能:发送一个数据包到服务器端,然后关闭连接。服务器端也很简单,收到客户端的数据,直接丢弃。

本环节的目标是掌握以下知识:

·Selector选择器的注册,以及选择器的查询。

·SelectionKey选择键方法的使用。

·根据SelectionKey方法的四种IO事件类型,完成对应的IO处理。1.5.2 第2天:Reactor反应器模式实践

实践一:单线程Reactor反应器模式的实现

使用单线程Reactor反应器模式,设计和实现一个EchoServer回显服务器。功能很简单:服务器端读取客户端的输入,然后回显到客户端。

本环节的目标是掌握以下知识:

·单线程Reactor反应器模式两个重要角色——Reactor反应器、Handler处理器的编写。

·SelectionKey选择键两个重要的方法——attach和attachment方法的使用。

实践二:多线程Reactor反应器模式

使用多线程Reactor反应器模式,设计一个EchoServer回显服务器,主要的升级方式为:

·引入ThreadPool线程池,将负责IOHandler输入输出处理器的执行,放入独立的线程池中,与负责服务监听和IO事件查询的反应器线程相隔离。

·将反应器线程拆分为多个SubReactor子反应器线程,同时,引入多个Selector选择器,每一个子反应器线程负责一个选择器读取客户端的输入,回显到客户端。

本环节的目标是掌握以下知识:

·线程池的使用。

·多线程反应器模式的实现。1.5.3 第3天:异步回调模式实践

实践一:使用线程join方式,通过阻塞式异步调用的方式,实现泡茶喝的实例

为了在计算机中,实现华罗庚的课文《统筹方法》泡茶喝的流程,可以设计三条线程:主线程、清洗线程、烧水线程,主要介绍如下:

·主线程(MainThread)的工作是:启动清洗线程、启动烧水线程,等清洗、烧水的工作完成后,泡茶喝。

·清洗线程(WashThread)的工作是:洗茶壶、洗茶杯。

·烧水线程(HotWarterThread)的工作是:洗好水壶,灌上凉水,放在火上,一直等水烧开。

本环节的目标是掌握以下知识:

不同的版本的join方法的使用。

实践二:使用FutureTask类和Callable接口,启动阻塞式的异步调用,并且获取异步线程的结果。

还是实现华罗庚的课文《统筹方法》泡茶喝的流程,可以设计三条线程:主线程、清洗线程、烧水线程,主要改进如下:

·主线程(MainThread)的工作是:启动清洗线程、启动烧水线程,然后阻塞,等待异步线程的返回值,根据异步线程的返回值,决定后续的动作。

·清洗线程(WashThread)在异步执行完成之后,有返回值。

·烧水线程(HotWarterThread)在异步执行完成之后,有返回值。

本环节的目标是掌握以下知识:

·Callable(可调用)接口的使用;Callable接口和Runnable(可执行)接口的不同。

·FutureTask异步任务类的使用。

实践三:使用ListenableFuture类和FutureCallback接口,启动非阻塞异步调用,并且完成异步回调。

还是实现华罗庚的课文《统筹方法》泡茶喝的流程,可以设计三条线程:主线程、清洗线程、烧水线程,主要改进如下:

·主线程(MainThread)的工作是:启动清洗线程、启动烧水线程,并且设置异步完成后的回调方法,这里主线程不阻塞等待,而是去干其他事情,例如读报纸。

·清洗线程(WashThread)在异步执行完成之后,执行回调方法。

·烧水线程(HotWarterThread)在异步执行完成之后,执行回调方法。

本环节的目标是掌握以下知识:

·FutureCallback接口的使用;FutureCallback接口和Callable接口的区别和联系。

·ListenableFuture异步任务类的使用,以及为异步任务设置回调方法。1.5.4 第4天:Netty基础实践

实践一:Netty中Handler处理器的生命周期

操作步骤如下:定义一个非常简单的入站处理器——InHandlerDemo。这个类继承于ChannelInboundHandlerAdapter适配器,它实现了基类的所有的入站处理方法,并在每一个方法的实现中,都加上了必要的输出信息。编写一个单元测试代码:将这个处理器加入到一个EmbeddedChannel嵌入式通道的流水线中。通过writeInbound方法,向EmbeddedChannel写一个模拟的入站ByteBuf数据包。InHandlerDemo作为一个入站处理器,就会处理到该ByteBuf数据包。通过输出,可以观测到处理器的生命周期。

本环节的目标是掌握以下知识:

·Netty中Handler处理器的生命周期。

·EmbeddedChannel嵌入式通道的使用。

实践二:ByteBuf的基本使用

操作步骤如下:使用Netty的默认分配器,分配了一个初始容量为9个字节,最大上限为100个字节的ByteBuf缓冲区。向ByteBuf写数据,观测ByteBuf的属性变化。从ByteBuf读数据,观测ByteBuf的属性变化。

本环节的目标是掌握以下知识:

·ByteBuf三个重要属性:readerIndex(读指针)、writerIndex(写指针)、maxCapacity(最大容量)。

·ByteBuf读写过程中,以上三个重要属性的变化规律。

实践三:使用Netty,实现EchoServer回显服务器

前面实现过Java NIO版本的EchoServer回显服务器,学习了Netty后,设计和实现一个Netty版本的EchoServer回显服务器。功能很简单:服务器端读取客户端的输入,然后将数据包直接回显到客户端。

本环节的目标是掌握以下知识:

·服务器端ServerBootstrap的装配和使用。

·服务器端NettyEchoServerHandler入站处理器的channelRead入站处理方法的编写。

·服务器端实现Netty的ByteBuf缓冲区的读取、回显。

·客户端Bootstrap的装配和使用。

·客户端NettyEchoClientHandler入站处理器中接受回显的数据,并且释放内存。

·客户端实现多种方式释放ByteBuf,包括:自动释放、手动释放。1.5.5 第5天:解码器(Decoder)与编码器(Encoder)实践

实践一:整数解码实践

具体步骤如下:定义一个非常简单的整数解码器——Byte2IntegerDecoder。这个类继承于ByteToMessageDecoder字节码解码抽象类,并实现基类的decode抽象方法,将ByteBuf缓冲区中的数据,解码成以一个一个的Integer对象。定义一个非常简单的整数处理器——IntegerProcessHandler。读取上一站的入站数据,把它转换成整数,并且显示在Console控制台。编写一个整数解码实战的测试用例。在测试用例中,新建了一个EmbeddedChannel嵌入式的通道实例,将两个自己的入站处理器Byte2IntegerDecoder、IntegerProcessHandler加入到通道的流水线上。通过writeInbound方法,向EmbeddedChannel写入一个模拟的入站ByteBuf数据包。通过输出,可以观察整数解码器的解码结果。

本环节的目标是掌握以下知识:

·如何基于Netty的ByteToMessageDecoder字节码解码抽象类,实现自己的ByteBuf二进制字节到POJO对象的解码。

·使用ByteToMessageDecoder,如何管理ByteBuf的应用计数。

实践二:整数相加的解码器实践

具体步骤如下:继承ReplayingDecoder基础解码器,编写一个整数相加的解码器:一次解码两个整数,并把这两个数据相加之和,作为解码的结果。使用前面定义的整数处理器——IntegerProcessHandler。读取上一站的入站数据,把它转换成整数,并且显示在Console控制台。使用前面定义的测试类,测试整数相加的解码器,并且查看结果是否正确。

本环节的目标是掌握以下知识:

·如何基于ReplayingDecoder解码器抽象类,实现自己的ByteBuf二进制字节到POJO对象的解码。

·ReplayingDecoder的成员属性——state阶段属性的使用。

·ReplayingDecoder的重要方法——checkpoint(IntegerAddDecoder.Status)方法的使用。

实践三:基于Head-Content协议的字符串分包解码器

具体步骤如下:继承ReplayingDecoder基础解码器,编写一个字符串分包解码器StringReplayDecoder。

在StringReplayDecoder的decode方法中,分两步:第1步,解码出字符串的长度;第2步,按照第一个阶段的字符串长度,解码出字符串的内容。编写一个简单的业务处理器StringProcessHandler。其功能是:读取上一站的入站数据,把它转换成字符串,并且显示在Console控制台。新建了一个EmbeddedChannel嵌入式的通道实例,将两个自己的入站处理器StringReplayDecoder、StringProcessHandler加入到通道的流水线上。为了测试入站处理器,使用writeInbound方法,向嵌入式通道EmbeddedChannel写入了100个ByteBuf入站缓冲;每一个ByteBuf缓冲,仅仅包含一个字符串。EmbeddedChannel通道接收到入站数据后,pipeline流水线上的两个入站处理器,就能不断地处理这些入站数据:将接收到的二进制字节,解码成一个一个的字符串,然后逐个地显示在Console控制台上。

本环节的目标是掌握以下知识:

·如何基于ReplayingDecoder解码器抽象类,实现自己的ByteBuf二进制字节到字符串的解码。

·巩固ReplayingDecoder的成员属性——state阶段属性的使用。

·巩固ReplayingDecoder的重要方法——checkpoint(IntegerAddDecoder.Status)方法的使用。

实践四:多字段Head-Content协议数据包解析实践

具体步骤如下:使用LengthFieldBasedFrameDecoder解码器,解码复杂的Head-Content协议。例如协议中包含版本号、魔数等多个其他的数据字段。使用前面所编写那一个简单的业务处理器StringProcessHandler。其功能是:读取上一站的入站数据,把它转换成字符串,并且显示在Console控制台上。新建一个EmbeddedChannel嵌入式的通道实例,将第一步和第二步的两个入站处理器LengthFieldBasedFrameDecoder、StringProcessHandler加入到通道的流水线上。为了测试入站处理器,使用writeInbound方法,向嵌入式通道EmbeddedChannel写入100个ByteBuf入站缓冲;每一个ByteBuf缓冲,仅仅包含一个字符串。EmbeddedChannel通道接收到入站数据后,pipeline流水线上的两个入站处理器,就能不断地处理到这些入站数据:将接到的二进制字节,解码成一个一个的字符串,然后逐个地显示在控制台上。

本环节的目标是掌握以下知识:

·LengthFieldBasedFrameDecoder解码器的使用。

·LengthFieldBasedFrameDecoder解码器的长度的矫正公式,计算公式为:内容字段的偏移-长度字段的偏移-长度字段的长度。1.5.6 第6天:JSON和ProtoBuf序列化实践

实践一:JSON通信实践

客户端将POJO转成JSON字符串,编码后发送到服务器端。服务器端接收客户端的数据包,并解码成JSON,转成POJO。

具体步骤如下:客户端的编码过程:

先通过谷歌的Gson框架,将POJO序列化成JSON字符串;然后使用Netty内置的StringEncoder编码器,将JSON字符串编码成二进制字节数组;最后,使用LengthFieldPrepender编码器(Netty内置),将二进制字节数组编码成Head-Content格式的二进制数据包。服务器端的解码过程:

先使用LengthFieldBasedFrameDecoder(Netty内置的自定义长度数据包解码器),解码Head-Content二进制数据包,解码出Content字段的二进制内容。

然后,使用StringDecoder字符串解码器(Netty内置的解码器),将二进制内容解码成JSON字符串。

最后,使用自定义的JsonMsgDecoder解码器,将JSON字符串解码成POJO对象。编写一个JsonMsgDecoder自定义的JSON解码器。将JSON字符串,解码成特定的POJO对象。分别组装好服务器端、客户端的流水线,运行程序,查看两端的通信结果。

本环节的目标是掌握以下知识:

·LengthFieldPrepender编码器的使用:在发送端使用它加上Head-Content的头部长度。

·JsonMsgDecoder的编写。

·JSON传输时,客户端流水线编码器的组装,服务器端流水线解码器的组装。

实践二:ProtoBuf通信实践

设计一个简单的客户端/服务器端传输程序:客户端将ProtoBuf的POJO编码成二进制数据包,发送到服务器端;服务器端接收客户端的数据包,并解码成ProtoBuf的POJO。

具体步骤如下:设计好需要传输的ProtoBuf的“.proto”协议文件,并且生成ProtoBuf的POJO和Builder:

在“.proto”协议文件中,仅仅定义了一个消息结构体,并且该消息结构体也非常简单,只包含两个字段:消息ID、消息内容。

使用protobuf-maven-plugin插件,生成message的POJO类和Builder(构造者)类的Java代码。客户端的编码过程:

先使用Netty内置的ProtobufEncoder,将ProtobufPOJO对象编码成二进制的字节数组。

然后,使用Netty内置的ProtobufVarint32LengthFieldPrepender编码器,加上varint32格式的可变长度。

Netty会将完成了编码后的Length+Content格式的二进制字节码,发送到服务器端。服务器端的解码过程:

先使用Netty内置的ProtobufVarint32FrameDecoder,根据varint32格式的可变长度值,从入站数据包中,解码出二进制Protobuf字节码。

然后,可以使用Netty内置的ProtobufDecoder解码器,将Protobuf字节码解码成Protobuf POJO对象。

最后,自定义一个ProtobufBussinessDecoder解码器,处理ProtobufPOJO对象。

本环节的目标是掌握以下知识:

试读结束[说明:试读内容隐藏了图片]

下载完整电子书

若在网站上没有找合适的书籍,可联系网站客服获取,各类电子版图书资料皆有。

客服微信:xzh432

登入/注册
卧槽~你还有脸回来
没有账号? 忘记密码?