Java
Spring
专业组件
TCP vs UDP
1、基于连接与无连接;
2、对系统资源的要求(TCP较多,UDP少);
3、UDP程序结构较简单;
4、流模式与数据报模式 ;
5、TCP保证数据正确性,UDP可能丢包;
6、TCP保证数据顺序,UDP不保证。
TCP 建立与断开连接的过程
为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
TCP流量控制-TCP拥塞控制
利用滑动窗口实现流量控制
如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。
利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。
-
拥塞:即对资源的需求超过了可用的资源。若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降。
流量控制和拥塞控制区别:拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。
流量控制:指点对点通信量的控制,是端到端正的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
RSDB vs Nosql
Credis vs Redis vs Jedis
Redis单机能支持10w/s的命令
6种数据类型的基本用法
redis底层数据结构
redis五大数据结构的实现原理
常用指令
type key,查看键值类型
OBJECT ENCODING key,查看对象编码
Redis对象
五个属性,
type, 类型:字符串对象,列表对象,哈希对象,集合对象,有序集合对象
encoding, 编码:INT,long类型的整数;EMBSTR,编码的简单动态字符串;RAW,简单动态字符串;HT,字典;LINKEDLIST,双端链表;ZIPLIST,压缩列表;INTSET,整数集合;SKIPLIST,跳跃表和字典
*ptr,指向底层数据结构的指针
refcount, 引用计数,用于:内存回收,内存共享(只用于整数值的字符对象)
lru,记录最后一次被程序访问的时间; 内存清理,对象空转时长
Redis的值支持哪些类型的对象
字符串对象:最大512M;编码可以是int(保存的是可以用long类型表示的整数值), embstr(长度小于44字节的字符串)或者raw(长度大于44字节);
embstr:redisobject和sds连续内存空间,创建时分配一次内存空间,删除时也少释放一次空间,以及对象的所有数据连在一起,寻找方便;如果字符串长度增加需要重新分配内存,因此redis中的embstr实现为只读;在对embstr对象进行修改时,都会先转化为raw再进行修改,因此,只要是修改embstr对象,修改后的对象一定是raw的,无论是否达到了44字节。
raw: 需要分配两次内存空间(redisobject和sds)。
使用场景-二进制安全的,可存图片,视频;可以用作计数器(INCR,DECR),比如分布式环境中统计在线人数,秒杀等。
列表对象:链表结构;编码可以是 ziplist(压缩列表)和 linkedlist(双端链表);使用ziplist的条件:元素个数小于512,每个元素长度小于64字节。
使用场景-可以实现简单的消息队列,另外可以利用lrange命令,做基于redis的分页功能
Hash对象:值是一个健值对集合;编码可以是ziplist和hashtable; 使用ziplist的条件同列表结构。
使用场景- value存放的是键值对,比如可以做单点登录存放用户信息
集合对象:无序集合,不通通过索引来操作元素,集合中的元素不能重复;编码可以是intset或者hashtable;编码转换条件:集合中的元素都是整数和元素个数小于512
使用场景-查找快,且不允许重复,利用这两个特性我们可以进行全局去重,比如在用户注册模块,判断用户是否注册;另外就是利用交集、并集,差集等操作,可以计算共同喜好,自己独有的喜好等功能。
有序集合对象:有序集合,每个元素设置一个分数作为排序依据;编码可以是ziplist 和skiplist。skiplist编码的有序集合对象使用zset做为底层实现,一个zset同时包含一个字典和一个跳跃表。编码转换:元素数量小于128和无素长度小于64字节
使用场景-可以做范围查找,排行榜应用,取TOP N操作。
为什么需要SDS
我们看上面对于 SDS 数据类型的定义:
1、len 保存了SDS保存字符串的长度
2、buf[] 数组用来保存字符串的每个元素
3、free j记录了 buf 数组中未使用的字节数量
上面的定义相对于 C 语言对于字符串的定义,多出了 len 属性以及 free 属性。为什么不使用C语言字符串实现,而是使用 SDS呢?这样实现有什么好处?
①、常数复杂度获取字符串长度
由于 len 属性的存在,我们获取 SDS 字符串的长度只需要读取 len 属性,时间复杂度为 O(1)。而对于 C 语言,获取字符串的长度通常是经过遍历计数来实现的,时间复杂度为 O(n)。通过 strlen key 命令可以获取 key 的字符串长度。
②、杜绝缓冲区溢出
我们知道在 C 语言中使用 strcat 函数来进行两个字符串的拼接,一旦没有分配足够长度的内存空间,就会造成缓冲区溢出。而对于 SDS 数据类型,在进行字符修改的时候,会首先根据记录的 len 属性检查内存空间是否满足需求,如果不满足,会进行相应的空间扩展,然后在进行修改操作,所以不会出现缓冲区溢出。
③、减少修改字符串的内存重新分配次数
C语言由于不记录字符串的长度,所以如果要修改字符串,必须要重新分配内存(先释放再申请),因为如果没有重新分配,字符串长度增大时会造成内存缓冲区溢出,字符串长度减小时会造成内存泄露。
而对于SDS,由于len属性和free属性的存在,对于修改字符串SDS实现了空间预分配和惰性空间释放两种策略:
1、空间预分配:对字符串进行空间扩展的时候,扩展的内存比实际需要的多,这样可以减少连续执行字符串增长操作所需的内存重分配次数。
2、惰性空间释放:对字符串进行缩短操作时,程序不立即使用内存重新分配来回收缩短后多余的字节,而是使用 free 属性将这些字节的数量记录下来,等待后续使用。(当然SDS也提供了相应的API,当我们有需要时,也可以手动释放这些未使用的空间。)
④、二进制安全
因为C字符串以空字符作为字符串结束的标识,而对于一些二进制文件(如图片等),内容可能包括空字符串,因此C字符串无法正确存取;而所有 SDS 的API 都是以处理二进制的方式来处理 buf 里面的元素,并且 SDS 不是以空字符串来判断是否结束,而是以 len 属性表示的长度来判断字符串是否结束。
⑤、兼容部分 C 字符串函数
虽然 SDS 是二进制安全的,但是一样遵从每个字符串都是以空字符串结尾的惯例,这样可以重用 C 语言库<string.h> 中的一部分函数。
Redis集群
为什么需要集群
- 并发量,10w/s命令不满足,如:离线计算
- 数据量,单机内存16~256G之间,容量超过单机容量范围
数据分区方式 - 顺序分区,适合键值业务;数据分散,但是容易造成访问倾斜;支持顺序访问;支持批量操作
- 哈希分布,特点:数据分散度高;键值分布与业务无关;不支持顺序访问;支持批量操作
- 一致性哈希
Redis过期删除策略
惰性删除和定期删除(默认10s一次)组合使用
Redis过期删除策略是采用惰性删除和定期删除这两种方式组合进行的,惰性删除能够保证过期的数据我们在获取时一定获取不到,而定期删除设置合适的频率,则可以保证无效的数据及时得到释放,而不会一直占用内存数据。
Redis内存淘汰策略
Redis是部署在物理机上的,内存不可能无限扩充的,当内存达到我们设定的界限后,便自动触发Redis内存淘汰策略,而具体的策略方式要根据实际业务情况进行选取。
当现有内存大于 maxmemory 时,便会触发redis主动淘汰内存方式,通过设置 maxmemory-policy ,有如下几种淘汰方式:
1)volatile-lru 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used ) 。
2)allkeys-lru 利用LRU算法移除任何key (和上一个相比,删除的key包括设置过期时间和不设置过期时间的)。通常使用该方式。
3)volatile-random 移除设置过过期时间的随机key 。
4)allkeys-random 无差别的随机移除。
5)volatile-ttl 移除即将过期的key(minor TTL)
6)noeviction 不移除任何key,只是返回一个写错误 ,默认选项,一般不会选用。
Redis持久化 RDB , AOF
RDB: 是把当前内存的数据集快照写入磁盘,也就是Snapshot。恢复时是将快照文件直接读到内存里;一定时间做一次备份,如果redis意外odwn掉的放太会丢失最后一次快照后的所有修改。
AOF: 是通过保存Redis服务器所执行的写命令来记录数据库状态。
Redis使用可能存在的问题
- 缓存穿透:对于缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到数据库,从而压垮数据库。
解决办法:a. 业务层校验;b. 不存在的数据设置短过期时间;c.布隆过滤器,一种数据结构,利用极小的内存,可以判断大量的数据“一定不存在或者可能存在”。布隆过滤器可以判断某个数据一定不存在,但是无法判断一定存在。
布隆过滤器优缺点
优点:优点很明显,二进制组成的数组,占用内存极少,并且插入和查询速度都足够快。
缺点:随着数据的增加,误判率会增加;还有无法判断数据一定存在;另外还有一个重要缺点,无法删除数据。 - 缓存击穿:Redis中一个热点key在失效的同时,大量的请求过来,从而会全部到达数据库,压垮数据库。
解决办法:a. 热点key永不过期;b. 定时更新;c.互斥锁,互斥锁简单来说就是在Redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则睡眠一段时间(比如100ms)后重试。 - 缓存雪崩:Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库。
解决办法:a. 设置有效期均匀分布,避免缓存设置相近的有效期,我们可以在设置有效期时增加随机值;或者统一规划有效期,使得过期时间均匀分布。b. 数据预热,对于即将来临的大量请求,我们可以提前走一遍系统,将数据提前缓存在Redis中,并设置不同的过期时间。c. 保证Redis服务高可用,Redis的哨兵模式和集群模式,为防止Redis集群单节点故障,可以通过这两种模式实现高可用。
Redis集群如何保证数据的一致性
Redis 集群有16384个哈希槽,每个要保存到Redis的键值对,都会先通过CRC16函数计算,然后再对16384取模,来决定这个键值对要被放置哪个槽。
集群的每个节点都会负责一部分哈希槽,举个例子,如果当前集群有3个节点M1、M2、M3,那么:
节点 M1可能 包含 0 到 5500号哈希槽
节点 M2可能 包含5501 到 11000 号哈希槽
节点 M3可能包含11001 到 16384号哈希槽
由于,Redis集群始终包含16384个哈希操作,因此,它很容易添加或者删除节点。比如,如果我想新添加个节点M4,那我只需要从节点 M1,M2,M3中得到部分槽,然后迁移到M4上即可。
Memcache 与 Redis 的区别都有哪些?
相同点-----Memcache和Redis都支持集群和单机模式
不同点:1。数据类型,Redis支持的数据类型丰富,而Memcache支持简单的数据类型,复杂的对象需要开发者自己处理
2.持久化:redis支持数据的持久化,可以将数据存储在系统磁盘中,重启的时候可以再次载入使用;Memcahe这个不支持数据持久化,
3.数据一致性:redis是单线程模式,保证了数据按照顺序提交;Memcahe是多核的,需要cas保证数据的一致性。
Hbase
qSchedule vs xxl-Job
调度模块(调度中心): 负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块; 支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover
执行模块(执行器): 负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效; 接收“调度中心”的执行请求、终止请求和日志请求等
- 任务执行器根据配置的调度中心的地址,自动注册到调度中心
- 达到任务触发条件,调度中心下发任务
- 执行器基于线程池执行任务,并把执行结果放入内存队列中、把执行日志写入日志文件中
- 执行器的回调线程消费内存队列中的执行结果,主动上报给调度中心
- 当用户在调度中心查看任务日志,调度中心请求任务执行器,任务执行器读取任务日志文件并返回日志详情
QMQ vs Rocket-MQ
使用场景
- 系统解耦
- 异步调用
- 流量削峰
缺点:
- 系统可用性降低,多引入了一个组件,如果mq挂了,系统就挂了;
- 系统复杂性提高,需要考虑消息失败,丢失,重复,乱序等问题;
- 一致性问题
顺序消息
为了保证消息的顺序消费,需要满足以下条件:
生产者必须将需要保持有序的消息按顺序发送到同一个主题下的同一个队列,避免使用多线程发送顺序消息;
对于消费者来说,一个队列的消息同时只能有一个线程消费。
事务消息原理
qConfig vs Apollo
Apollo客户端的实现原理:
- 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
- 客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
- 这是一个fallback机制,为了防止推送机制失效导致配置不更新
客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified - 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟。
- 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
- 客户端会把从服务端获取到的配置在本地文件系统缓存一份
- 在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
- 应用程序从Apollo客户端获取最新的配置、订阅配置更新通知
Http Long Polling实现的,具体而言:
- 客户端发起一个Http请求到服务端
- 服务端会保持住这个连接60秒
- 如果在60秒内有客户端关心的配置变化,被保持住的客户端请求会立即返回,并告知客户端有配置变化的namespace信息,客户端会据此拉取对应namespace的最新配置
- 如果在60秒内没有客户端关心的配置变化,那么会返回Http状态码304给客户端
- 客户端在收到服务端请求后会立即重新发起连接,回到第一步
- 用户在Portal操作配置发布
- Portal调用Admin Service的接口操作发布
- Admin Service发布配置后,发送ReleaseMessage给各个Config Service
- Config Service收到ReleaseMessage后,通知对应的客户端
业务领域
OTA
开放问题
谈谈遇到过的挑战
快速接手直连和分销业务系统
挑战点:
- 原产研团队均离职,没有人交接,而且文档不全
- 系统多,人手少,原团队8,9个人维护的系统;而我这里只有3个人;
工作开展
- 人员分工,按模块分别梳理业务流程
- 整理监控点和报警
- 输出业务流程文档并在组内给其他人讲解
- 也同时进行招骋
- 老带新,进行新人培养;
最近看哪些书
OKR, DDD