高并发时代已经到来,Netty、Redis、ZooKeeper是高并发时代的必备工具
1、Netty为何这么火
Netty是JBOSS提供的一个Java开源框架,是居于NIO的客户端/服务器编程框架,它既能快速开发高并发、高可用、高可靠性的网络服务器程序,也能开发高可用、高可靠的客户端程序
注:NIO是指非阻塞输入输出(Non-Blocking IO),也称非阻塞IO
1.1 Netty火热的程度
火爆的Kafka、RocketMQ等消息中间件、火热的ElasticSearch开源搜索引擎、大数据处理Hadoop的RPC框架Avro、主流的分布式通信框架Dubbo,它们都使用了Netty
Netty之所以受青睐,是因为Netty提供异步的、事件驱动的网络应用程序框架和工具。作为一个异步框架,Net特有的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便地主动获取或者通过通知机制获得IO操作结果
与JDK原生NIO相比,Netty提供了相对十分简单易用的API,因为非常适合网络编程。Netty主要是基于NIO来实现的,在Netty中也可以提供阻塞IO的服务
优点:
- API使用简单,开发门槛低
- 功能强大,预置了多种编解码功能,支持多种主流协议
- 定制能力强,可以通过ChannelHandler对通信框架进行灵活扩展
- 性能高,与其他业界主流的NIO框架对比,Netty的总和性能最优
- 成熟、稳定,Netty修复了已经发现的所有JDK NIO中的BUG,业务开发人员不需要再为NIO的BUG而烦恼
- 社区活跃,版本迭代周期短,发现的BUG可以被即时修复
1.2 Netty是面试的必杀器
Netty是互联网中间件领域使用最广泛、最核心的网络通信框架之一
2、高并发利器Redis
任何高并发的系统,不可或缺的就是缓存。Redis缓存目前已经成为缓存的事实标准
2.1 什么是Redis
Redis是Remote Dictionary Server(远程字典服务器)的缩写,最初是作为数据库的工具来使用的。是目前使用广泛、高效的一款开源缓存。Redis使用C语言开发,将数据保存在内存中,可以看成是一款纯内存的数据库,所以它的数据存取速度非常快。一些经常用并且创建时间较长的内容,可以缓存到Redis中,而应用程序能以极快的速度存取这些内容
Redis通过键-值对(Key - Value Pair)的形式来存取数据,类似于Java中的Map映射。Redis的Key键,只能是String字符串类型。Redis的Value值类型包括:string字符类型、map映射类型、list列表类型、set集合类型、sortedset有序集合类型
Redis的主要应用场景:缓存(数据查询、短连接、新闻内容、商品内容等)、分布式会话(Session)、聊天室的在线好友列表、任务队列(秒杀、抢购、12306等)、应用排行榜、访问统计、数据过期处理(可以精确到毫秒)
2.2 Redis成为缓存事实标准的原因
相对于其他的键-值对(Key - Value)内存数据库(如Memcached)而言,Redis具有如下特点:
- 速度快
不需要等待磁盘的IO,在内存之间进行的数据存储和查询,速度非常快。当然,缓存的数据总量不能太大,因为受到物理内存空间大小的限制 - 丰富的数据结构
除了string之外,还有list、hash、set、sortedset,一共五种类型 - 单线程,避免了线程切换和锁机制的性能消耗
- 可持久化
支持RDB于AOF两种方式,将内存中的数据写入外部的物理存储设备 - 支持发布 / 订阅
- 支持Lua脚本
- 支持分布式锁
在分布式系统中,如果不同的节点需要访问到一个资源,往往需要通过互斥机制来防止彼此干扰,并且保证数据的一致性。在这种情况下,需要使用到分布式锁。分布式锁和Java的锁用于实现不同线程之间的同步访问,原理上是类似的 - 支持原子操作和事务
Redis事务是一组命令的集合。一个事务中的命令要么都执行,要么都不执行。如果命令在运行期间出现错误,不会自动回滚 - 支持住 - 从(Master-Slave)复制与高可用(Redis Sentinel)集群(3.0 版本以上)
- 支持管道
Redis管道是指客户端可以将多个命令一次性发送到服务器,然后由服务器一次性返回所有结果。管道技术的优点是:在批量执行命令的应用场景中,可以大大减少网络传输的开销,提高性能
3、分布式利器Zookeeper
突破了单体瓶颈之后的高并发,就必须靠集群了,而集群的分布式架构和协调,一定少不了可靠的分布式协调工具,Zookeeper就是目前结尾中的分布式协调工具
3.1 什么是Zookeeper
起源于雅虎。Zookeeper的功能,正好是用来协调分布式环境的,协调各个以动物命名的分布式组件
3.2 Zookeeper的优势
Zookeeper对不同系统环境的支持都很好
Zookeeper的核心优势是,实现了分布式环境的数据一致性,简单地说:每时每刻我们访问Zookeeper的树结构时,不同的节点返回的数据都是一致的。也就是说,对Zookeeper进行数据访问时,无论是什么时间,都不会引起脏读、重复读。注:脏读是指在数据库存取中无效数据的读出
Zookeeper提供的功能都是分布式系统中非常底层且必不可少的基本功能,如果开发者自己来实现这些功能而且要达到高吞吐、低延迟同时的还要保证一致性和可用性,实际上是非常困难的。因此,借助Zookeeper提供的这些功能,开发者就可以轻松在Zookeeper之上构建自己的各种分布式系统
4、高并发IM的综合实践
应用Netty、Redis、Zookeeper持续迭代一个高并发学习项目,叫做“CrazyIM”
4.1 高并发IM(即时通讯)的学习价值
一个分布式、高并发的IM系统,面临的QPS(Query Per Second,每秒查询率)峰值可能在十万、百万、千万,甚至上亿级别。对于此纵深层次化的、递进的高并发需求,将无极限地考研着系统的性能。需要不断地从通信协议、到系统的架构进行优化,对技术能力是一种非常极致的考验和训练
具有不同的QPS峰值规模的IM系统而言,它们所处的用户需求环境是不一样的。也就是说,IM系统综合性相对较强,相关的技术需要覆盖到满足各种不同应用场景的网络传输、分布式协调、分布式缓存、服务化架构等
4.2 庞大的应用场景
高并发IM典型的应用场景如下:私信、聊天、大规模推送、视频会议、弹幕、抽奖、互动游戏、基于位置的应用(Uber、滴滴司机位置)、在线教育、智能家居等,如下图所示
尤其是对于APP开发的小伙伴们来说,IM已经成为大多数APP的标配。在移动互联网时代,推送(Push)服务成为APP应用不可或缺的重要组成部分,推送服务可以提升用户的活跃度和留存率。我们的手机每天接收到各种各样的广告和提示信息等,它们大多数都是通过推送服务实现的
随着5G时代物联网的发展,未来所有接入物联网的智能设备,都将是IM系统的客户端,这就意味着推送服务会在未来面临海量的设备和终端接入。为了支持这些千万级、上亿级的终端,一定是需要强悍的后台系统
5、Netty、Redis、Zookeeper实践计划
从最基础的NIO入手,列出一个大致12天的实践计划,深入掌握Netty、Redis、Zookeeper
5.1 第1天:Java NIO实践
实践一:使用FileChannel复制文件
通过使用Channel通道,完成复制文件
本节掌握:Java NIO中ByteBuffer、Channel两个重要组件的使用
使用文件Channel通道的transferFrom方法,完成高效率的文件复制
实践二:使用SocketChannel传输文件
本节掌握以下知识:
- 非阻塞客户端在发起连接后,需要不断的自旋,检测连接是否完成的
- SocketChannel传输管道的reaf读取办法、write写入方法
- 在SocketChannel传输管道关闭前,尽量发送一个输出结束标志到对方端
实践三:使用DatagramChannel传输数据
客户端使用Datagramchannel发送数据,服务器端使用DatagramChannel接收数据
本节掌握以下知识:
- 使用接收数据方法receive,使用发送数据方法send
- DatagramChannel和SocketChannel两种通道,在发送、接收数据上的不同
实践四:使用NIO实现Discard服务器
客户端功能:发送一个数据包到服务器端,然后关闭连接。服务器端也很简单,收到客户端的数据,直接丢弃
本节掌握以下知识:
- Selector选择器的注册,以及选择器的查询
- SelectionKey选择键方法的使用
- 根据SelectionKey方法的四种IO事件类型,完成对应的IO的处理
5.2 第2天:Reactor反应器模式实践
实践一:单线程Reactor反应器模式的实现
使用单线程Reactor反应器模式,设计和实现一个EchoServer回显服务器。功能很简单:服务器端读取客户端的输入,然后回显到客户端
本节掌握以下知识:
- 单线程Reactor反应器模式两个重要角色——Reactor反应器、Handler处理器的编写
- SelectionKey选择键两个重要的方法——attach和attachment方法的使用
实践二:多线程Reactor反应器模式
使用多线程Reactor反应器模式,设计一个EchoServer回显服务器,主要的升级方式为:
- 引入ThreadPool线程池,将负责IOHandler输入输出处理器的执行,放入独立的线程池中,与负责服务监听和IO实践查询的反应器线程相隔离
- 将反应器线程拆分为多个SubReactor子反应器线程,同时,引入多个Selector选择器,每一个子反应器线程负责一个选择器读取客户端的输入,回显到客户端
本节掌握以下知识:
- 线程池的使用
- 多线程反应器模式的实现
5.3 第3天:异步回调模式实践
实践一:使用线程join方式,通过阻塞式异步调用的方式,实现泡茶喝的实例
泡茶喝的三条线程:主线程、清晰线程、烧水线程
- 主线程(Main Thread) 的工作是:启动清洗线程、启动烧水线程,等清洗、烧水的工作完成后,泡茶喝
- 清洗线程(WashThread) 的工作是:洗茶壶、洗茶杯
- 烧水线程(HotWater) 的工作是:洗好水壶,灌上凉水,放在火上,一直等水烧开
本节掌握以下知识:
- 不同的版本的join方法的使用
实践二:使用FutureTask类和Callable接口,启动阻塞式的异步调用,并且获取异步线程的结果
将实践一改进成如下:
- 主线程(MainThread) 的工作是:启动清洗线程、启动烧水线程,然后阻塞,等待异步线程的返回值,决定后续的动作
- 清洗线程(WashThread) 在异步执行完成之后,有返回值
- 烧水线程(HotWaterThread) 在异步执行完成之后,有返回值
本节掌握以下知识:
- Callable(可调用)接口的使用;Callable接口和Runnable(可执行)接口的不同
- FutureTash异步任务类的使用
实践三:使用ListenableFuture类和FutureCallback接口,启动非阻塞异步调用,并且完成异步回调
继续改进如下:
- 主线程(MainThread) 的工作是:启动清洗线程、启动烧水线程,并且设置异步完成后的回调方法,这里主线程不阻塞等待,而是去干其他事情,例如读报纸
- 清洗线程(WashThread) 在异步执行完成之后,执行回调方法
- 烧水线程(HotWaterThread) 在异步执行完成后,执行回调方法
本节掌握以下知识:
- FutureCallback接口的使用;FutureCallback接口和Callable接口的区别和联系
- ListenableFuture异步任务类的使用,以及为异步任务设置回调方法
5.4 第4天:Netty基础实践
实践一:Netty中Handler处理器的生命周期
操作步骤如下:
步骤01:
定义一个非常简单的入站处理器——InHandlerDemo。这个类继承于ChannellnboundHandlerAdapter适配器,它实现了基类的所有的入站处理方法,并在每一个方法的实现中,都加上了必要的输出信息
步骤02:
编写一个单元测试代码:将这个处理器加入到一个EmbeddedChannel嵌入式通道的流水线中
步骤03:
通过writeInbound方法,向EmbeddedChannel写一个模拟的入站ByteBuf数据包。InHandlerDemo作为一个入站处理器,就会处理到该ByteBuf数据包
步骤04:
通过输出,可以观测到处理器的生命周期
本节掌握以下知识:
- Netty中Handler处理器的生命周期
- EmbeddedChannel嵌入式通道的使用
实践二:ByteBuf的基本使用
操作步骤如下:
步骤01:
使用Netty的默认分配器,分配了一个初始容量为9个字节,最大上限为100个字节的ByteBuf缓冲区
步骤02:
向ByteBuf写数据,观测ByteBuf的属性变化
步骤03:
从ByteBuf读数据,观测ByteBuf的属性变化
本节掌握以下知识:
- ByteBuf三个重要属性:readerIndex(读指针)、writerIndex(写指针)、maxCapacity(最大容量)
- ByteBuf读写过程中,以上三个重要属性的变化规律
实践三:使用Netty,实现EchoServer回显服务器
服务器读取客户端的输入,然后将数据包直接回显到客户端
本节掌握以下知识:
- 服务器端ServerBootstrap的装配和使用
- 服务器端NettyEchoServerHandler入站处理器的channelRead入站处理方法的编写
- 服务器端实现Netty的ByteBuf缓冲区的读取、回显
- 客户端Bootstrap的装配和使用
- 客户端NettyEchoClientHandler入站处理器中接收回显的数据,并且释放内存
- 客户端实现多种方式释放ByteBuf,包括:自动释放、手动释放
5.5 第5天:解码器(Decoder)与编码器(Encoder)实践
实践一:整数解码实践
具体步骤如下:
步骤01:
定义一个非常简单的整数解码器——Byte2IntegerDecoder。这个类继承于ByteToMessageDecoder字节码解码抽象类,并实现基类的decode抽象方法,将ByteBuf缓冲区中的数据,解码成以一个一个的Integer对象
步骤02:
定义一个非常简单的整数处理器——IntegerProcessHandler。读取上一站的入站数据,把它转换成整数,并且显示在Console控制台
步骤03:
编写一个整数解码实战的测试用例。在测试用例中,新建了一个EmbeddedChannel嵌入式的通道实例,将两个自己的入站处理器Byte2IntegerDecoder、IntegerProcessHandler加入到通道的流水线上。通过writeInbound方法,向EmbeddedChannel写入一个模拟的入站ByteBuf数据包
步骤04:
通过输出,可以观察整数解码器的解码结果
本节掌握以下知识:
- 如何基于Netty的ByteToMessageDecoder字节码解码抽象类,实现自己的ByteBuf二进制字节到POJO对象的解码
- 使用ByteToMessageDecoder,如何管理ByteBuf的应用计数
实践二:整数相加的解码器实践
具体步骤如下:
步骤01:
继承ReplayingDecoder基础解码器,编写一个整数相加的解码器:一次解码两个整数,并把这两个数据相加之和,作为解码的结果
步骤02:
使用前面定义的整数处理器——IntegerProcessHandler。读取上一站的入站数据,把它转换成整数,并且显示在Console控制台
步骤03:
使用前面定义的测试类,测试整数相加的解码器,并且查看结果是否正确
本节掌握以下知识:
- 如何基于ReplayingDecoder解码器抽象类,实现自己的ByteBuf二进制字节到POJO对象的解码
- ReplayingDecoder的成员属性——state阶段属性的使用
- ReplayingDecoder的重要方法——checkpoint(IntegerAddDecoder.Status)方法的使用
实践三:基于Head-Content协议的字符串分包解码器
具体步骤如下:
步骤01:
继承ReplayingDecoder基础解码器,编写一个字符串分包解码器StringReplayDecoder
在StringReplayDecoder的decode方法中,分两步:第1步,解码出字节串的长度;第2步,按照第一个阶段的字节串长度,解码出字符串的内容
步骤02:
编写一个简单的业务处理器StringProcessHandler。其功能是:读取上一站的入站数据,把它转换成字符串,并且显示在Console控制台
步骤03:
新建了一个EmbeddedChannel嵌入式的通道实例,将两个自己的入站处理器StringReplayDecoder、StringProcessHandler加入到通道的流水线上。为了测试入站处理器,使用writeInbound方法,向嵌入式通道EmbeddedChannel写入了100个ByteBuf入站数据后,pipeline流水线上的两个入站处理器,就能不断地处理这些入站数据:将接收到的二进制字节,解码成一个一个的字符串,然后逐个地显示在Console控制台上
本节掌握以下知识:
- 如何基于ReplayingDecoder解码器抽象类,实现自己的ByteBuf二进制字节到字符串的解码
- 巩固ReplayingDecoder的成员属性——state阶段属性的使用
- 巩固ReplayingDecoder的重要方法——checkpoint(IntegerAddDecoder.Status)方法的使用
实践四:多字段Head-Content协议数据包解析实践
具体步骤如下
步骤01:
使用LengthFieldBasedFrameDecoder解码器,解码复杂的Head-Content协议。例如协议中包含版本号、魔术等多个其他的数据字段
步骤02:
使用前面所编写那一个简单的业务处理器StringProcessHandler。其功能是:读取上一站的入站数据,把它转换成字符串,并且显示在Console控制台上
步骤03:
新建一个EmbeddedChannel嵌入式的通道实例,将第一步和第二步的两个入站处理器LengthFieldBasedFrameDecoder、StringProcessHandler加入到通道的流水线上。为了测试入站处理器,使用writeInbound方法,想嵌入式通道EmbeddedChannel写入100个ByteBuf入站缓冲;每一个ByteBuf缓冲,仅仅包含一个字符串。EmbeddedChannel通道接收到入站数据后,pipeline流水线上的两个入站处理器,就能不断地处理到这些入站数据:将接到的二进制字节,解码成一个一个的字符串,然后逐个地显示在控制台上
本节掌握以下知识:
- LengthFieldBasedFrameDecoder解码器的使用
- LengthFiledBasedFrameDecoder解码器的长度的矫正公式,计算公式为:内容字段的偏移-长度字段的偏移-长度字段的长度
5.6 第6天:JSON和ProtoBuf序列化实践
实践一:JSON通信实践
客户端将POJO转成JSON字符串,编码后发送到服务器端。服务器端接收客户端的数据包,并解码成JSON,转成POJO
具体步骤如下:
步骤01:
客户端的编码过程:
先通过谷歌的Gson框架,将POJO序列化成JSON字符串;然后使用Netty内置的StringEncoder编码器,将JSON字符串编码成二进制字节数组;最后,使用LengthFieldPrepender编码器(Netty内置) ,将二进制字节数组编码成Head-Content格式的二进制数据包。
步骤02:
服务器端的解码过程:
先使用LengthFieldBasedFrameDecoder (Netty内置的自定义长度数据包解码器)解码Head-Content二进制数据包,解码出Content字段的二进制内容。
然后,使用StringDecoder字符串解码器(Netty内置的解码器) ,将二进制内容解码成JSON字符串。
最后,使用自定义的JsonMsgDecoder解码器,将JSON字符串解码成POJO对象。
步骤03:
编写一个JsonMsgDecoder自定义的JSON解码器。将JSON字符串,解码成特定的POJO对象。
步骤04
分别组装好服务器端、客户端的流水线,运行程序,查看两端的通信结果。
本环节的目标是掌握以下知识:
- LengthFieldPrepender编码器的使用:在发送端使用它加上Head-Content的头部长度。
- JsonMsgDecoder的编写。
- JSON传输时,客户端流水线编码器的组装,服务器端流水线解码器的组装。
实践二:ProtoBuf通信实践
设计一个简单的客户端/服务器端传输程序:客户端将ProtoBuf的POJO编码成二进制数据包,发送到服务器端;服务器端接收客户端的数据包,并解码成ProtoBuf的POJO
具体步骤如下:
步骤01:
设计好需要传输的ProtoBuf的".proto"协议文件,并且生成ProtoBuf的POJO和Builder:
在".proto"协议文件中,仅仅定义了一个消息结构体,并且该消息结构体也非常简单,只包含两个字段:消息ID、消息内容。
使用protobuf-maven-plugin插件,生成message的POJO类和Builder (构造者)类的Java代码。
步骤02:
客户端的编码过程:先使用Netty内置的ProtobufEncoder,将ProtobufPOJO对象编码成二进制的字节数组。
然后,使用Netty内置的ProtobufVarint32LengthFieldPrepender编码器,加上varint32格式的可变长度。
Netty会将完成了编码后的Length+Content格式的二进制字节码,发送到服务器端。
步骤03:
服务器端的解码过程:
先使用Netty内置的ProtobufVarint32FrameDecoder,根据varint32格式的可变长度值,从入站数据包中,解码出二进制Protobuf字节码。
然后,可以使用Netty内置的ProtobufDecoder解码器,将Protobuf字节码解码成Protobuf POJO对象。
最后, 自定义一个ProtobufBussinessDecoder解码器,处理ProtobufPOJO对象。
本环节的目标是掌握以下知识:
- proto"基础协议。
- Netty内置的ProtobufEncoder,ProtobufDecoder两个专用的传输Protobuf序列化数据的编码器/解码器的使用。
- Netty内置的两个ProtoBuf专用的可变长度Head-Content协议的半包编码、解码处理器:ProtobufVarint32LengthFieldPrepender编码器ProtobufVarint32FrameDecoderProtobufEncoder解码器的使用。
5.7 第7~10天:基于Netty的单聊实战
实践一:自定义ProtoBuf编码器/解码器
具体步骤如下
步骤01
为单聊系统,设计一套自定义的".proto"协议文件;然后通过maven插件生成Protobuf Builder和POJO
步骤02
继承Netty提供的MessageToByteEncoder编码器,编写一个自定义的Protobuf编码器,完成Head-Content协议的复杂数据包的编码。
通过自定义编码器,最终将ProtobufPOJO编码成Head-Content协议的二进制ByteBuf帧。
步骤03
继承Netty提供的ByteToMessageDecoder解码器,编写一个自定义的Protobuf解码器,完成Head-Content协议的复杂数据包的解码。
通过自定义的解码器,最终将Head-Content协议的复杂数据包,解码出ProtobufPOJO
步骤04
分别组装好服务器端、客户端的流水线,运行程序,查看两端的通信结果。
本环节的目标是掌握以下知识:
- 设计复杂的".proto"协议文件。
- 自定义的Protobuf编码器的编写。
- 自定义的Protobuf编码器的编写。
实践二:登录实践
业务逻辑:
- 客户端发送登录数据包。
- 服务器端进行用户信息验证。
- 服务器端创建Session会话。
- 服务器端将登录结果信息返回给客户端,包括成功标志、Session ID等。
具体步骤如下:
从客户端到服务器端再到客户端,大致有以下几个步骤。
步骤01
编写LoginConsoleCommand控制台命令类,从客户端收集用户ID和密
步骤02
编写客户端LoginSender发送器类,组装Protobuf数据包,通过客户端通道发送到服务器端。
步骤03
编写服务器端UserLoginHandler入站处理器,处理收到的登录消息,完成数据的校验后,将数据包交给业务处理器LoginMsgProcesser,进行异步的处理。
步骤04
编写服务器端LoginMsgProcesser (业务处理器) ,将处理结果,写入用户绑定的子通道。
步骤05
编写客户端业务处理器LoginResponceHandler,处理登录响应,例如设置登录的状态,保存会话的SessionID等。
本环节的目标是掌握以下知识:
- Netty知识的综合运用。
- Channel通道容器功能的使用。
实践三:单聊实践
单聊的业务逻辑:
- 当用户A登录成功之后,按照单聊的消息格式,发送所要的消息。
- 这里的消息格式设定为–userld:content,其中的userld,就是消息接收方目标用户B的userld;其中的content,表示聊天的内容
- 服务器端收到消息后,根据目标userld,进行消息帧的转发,发送到用户B所在的客户端。
- 客户端用户B收到用户A发来的消息,显示在自己的控制台上。
具体步骤如下:
从客户端到服务器端再到客户端,大致有5个步骤。
步骤01
当用户A登录成功之后,按照单聊的消息格式,发送所要的消息。
这里的消息格式设定为-userld:content,其中的userld,就是消息接收方目标用户B的userld,其中的content,表示聊天的内容。
步骤02
CommandController在完成聊天内容和目标用户的收集后,调用chatSender发送器实例,将聊天消息组装成Protobuf消息帧,通过客户端channel通道发往服务器端
步骤03
编写服务器端的消息转发处理器ChatRedirectHandler类,其功能是,对用户登录进行判断:如果没有登录,则不能发送消息;开启异步的消息转发,由负责转发的chatRedirectProcesser实例,完成消息转发。
步骤04
编写负责异步消息转发的ChatRedirectProcesser类,功能如下:根据目标用户ID,找出所有的服务器端的会话列表(Session List) ,然后对应每一个会话,发送一份消息
步骤05
编写客户端ChatMsgHandler处理器,主要的工作如下:对消息类型进行判断,判断是否为聊天请求Protobuf数据包。如果不是,直接将消息交给流水线的下一站;如果是聊天消息,则将聊天消息显示在控制台上。
本环节目标是掌握以下知识:
- Netty知识的综合运用。
- 服务器端会话(Session)的管理。
5.8 第11天:Zookeeper实践计划
实践一:分布式ID生成器
具体步骤如下:
步骤01
白定义一个分布式ID生成器-IDMaker类,通过创建ZK的临时顺序节点的方法,生成全局唯一的ID。
步骤02
基于自定义的IDMaker,编写单元测试的用例,生成ID
本环节的目标是掌握以下知识:
- 分布式ID生成器原理。
- ZooKeeper客户端的使用。
实践二:使用ZK实现SnowFlake ID算法
具体步骤如下:
步骤01
编写一个实现SnowFlake ID算法的ID生成器SnowflakeldGenerator类,生成全局唯一的ID
步骤02
基于自定义的SnowflakeldGenerator,编写单元测试的用例,生成ID
本环节的目标是掌握以下知识:
- SnowFlake ID算法原理。
- ZooKeeper客户端的使用。
5.9 第12天:Redis实践计划
实践一:使用RedisTemplate模板类完成Redis的缓存CRUD操作
具体步骤如下:
步骤01
将RedisTemplate模板类的大部分缓存操作,封装成一个自己的缓作Service服务,称之为CacheOperationService类。
步骤02
编写业务类UserServicelmplWith Template类,使用CacheOperationService类,完成User对象的缓存CRUD。
步骤03
编写测试用例,访问UserServicelmplWith Template类。观察在进User对象的查询时,能优先使用缓存数据,是否省去数据库访问的时间。
本环节目标是掌握以下知识:
- Redis Template模板类的使用。
- Jedis的客户端API
- Redis Template模板API
实践二:使用RedisTemplate模板类完成Redis的缓存CRUD操作
具体步骤如下:
步骤01
使用RedisCallback的dolnRedis回调方法,在dolnRedis回调方法中,直接使用实参RedisConnection连接类实例,完成Redis的操作。
步骤02
编写业务类UserServicelmpllnTemplate类,使用Redis Template模板实例去执行RedisCallback回调实例,完成User对象的缓存CRUD
步骤03
编写测试用例,访问UserServicelmplInTemplate类。观察在进行User对象的查询时,优先使用缓存数据,看看是否省去了数据库访问的时间。
本环节的目标是掌握以下知识:
- RedisCallback回调接口的使用。
- Jedis的客户端API
- RedisTemplate模板API
实践三:使用Spring缓存注解,完成Redis的缓存CRUD操作
具体步骤如下:
步骤01
编写业务类UserServicelmplWithAnn类,使用缓存注解,完成User对象的缓存CRUD。
步骤02
编写测试用例,访问UserServicelmplWithAnn类。观察在进行User对象的查询时,优先使用缓存数据,看看是否省去了数据库访问的时间。
本环节目标是掌握以下知识:
- Spring缓存注解的使用。
- Jedis的客户端API
- Spring缓存注解的配置。