通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。第一个使用这个服务的人得到的短地址是 http://xx.xx/0 第二个是 http://xx.xx/1 第11个是 http://xx.xx/a 第依次往后,相当于实现了一个62进制的自增字段即可。
常用的url压缩算法是短地址映射法。具体步骤是:
随机、轮询、最少使用、一致性哈希(除了一致性哈希外,都有加权)
常见6种负载均衡算法:轮询,随机,源地址哈希,加权轮询,加权随机,最小连接数。
nginx5种负载均衡算法:轮询,weight轮询,ip_hash,fair(响应时间),url_hash
dubbo负载均衡算法:随机,轮询,最少活跃调用数,一致性Hash
可以,因为dubbo在注册中心挂掉之后,会从原先的缓存中读取连接地址。
调用方:
消费方:
1.dubbo采用RPC的方式交互,SpringCloud采用Http,restful协议进行交互。
2.dubbo依赖zookeeper进行服务注册,Springloud自己拥有自己的服务注册中心。
3.dubbo需要强依赖,需要持有相同的类或者jar包,springcloud弱依赖,但需要通过接口文档进行约束。
4.C数据一致性,A服务可用性,P服务对网络分区故障的容错性,Zookeeper 保证的是CP,euraka保证的是AP。
setnx指令,设置锁的有效时间防止死锁。设置一个随机值来标识锁的持有人,利用这个随机值来释放锁。
设计一个数据结构,有用户id,当前秒数,调用次数。每次请求时对比当前秒数和该对象内的是否一致,一致的话累加调用次数。不一致的话,将当前秒数替换成新的,调用次数清0。
同域下的单点登录,只需共享session即可。
登录业务系统,跳转至SSO服务器,判断用户名密码正确,在sso域下种下cookie,在session中标记为登录,返回一个ticket,跳转到业务系统,业务系统再拿这个ticket跑去SSO服务器验证ticket是否有效,有效的话,在业务系统session中设置为已登录即可。
相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。这个过程,也就是单点登录的原理,用下图说明
单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁,用下面的图来说明
事务消息与普通消息的区别就在于消息生产环节,生产者首先预发送一条消息到MQ(这也被称为发送half消息)
MQ接受到消息后,先进行持久化,则存储中会新增一条状态为待发送的消息
然后返回ACK给消息生产者,此时MQ不会触发消息推送事件
生产者预发送消息成功后,执行本地事务
执行本地事务,执行完成后,发送执行结果给MQ
MQ会根据结果删除或者更新消息状态为可发送
如果消息状态更新为可发送,则MQ会push消息给消费者,后面消息的消费和普通消息是一样的
漏桶:水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求。
令牌桶算法:系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token,如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token就拒绝服务。
基于redis实现的限流:假设每分钟访问次数不能超过10次,在Redis中创建一个键,过期60秒,对此服务接口的访问就把键值加1,在60秒内增加到10的时候,禁止访问服务接口。
计数器,滑动窗口(假设窗口为10s,则建立一个大小为10的数组,然后每次让当前秒数除10,落到哪个格子就累加,每一时刻数组的和就是窗口的数值)
令牌桶可以应对突发的大流量
漏斗算法用于请求恒定速率通过
新建一个topic,partition是原来的10倍;然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue;接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息;
Kafka消息发送分同步(sync)、异步(async)两种方式。默认是使用同步方式,可通过producer.type属性进行配置;Kafka保证消息被安全生产,有三个选项分别是0,1,-1。
网络异常:
acks设置为0时,不和Kafka集群进行消息接受确认,当网络发生异常等情况时,存在消息丢失的可能;
客户端异常:
异步发送时,消息并没有直接发送至Kafka集群,而是在Client端按一定规则缓存并批量发送。在这期间,如果客户端发生死机等情况,都会导致消息的丢失;
缓冲区满了:
异步发送时,Client端缓存的消息超出了缓冲池的大小,也存在消息丢失的可能;
Leader副本异常:
acks设置为1时,Leader副本接收成功,Kafka集群就返回成功确认信息,而Follower副本可能还在同步。这时Leader副本突然出现异常,新Leader副本(原Follower副本)未能和其保持一致,就会出现消息丢失的情况;
以上就是消息丢失的几种情况,在日常应用中,我们需要结合自身的应用场景来选择不同的配置。
想要更高的吞吐量就设置:异步、ack=0;想要不丢失消息数据就选:同步、ack=-1策略
其实很简单主要是用二分查找算法,比如我们要查找一条offest=10000的文件,kafka首先会在对应分区下的log文件里采用二分查看定位到某个记录该offest
=10000这条消息的log,然后从相应的index文件定位其偏移量,然后拿着偏移量到log里面直接获取。这样就完成了一个消息的检索过程。
Broker NIO异步消息处理,实现了IO线程与业务线程分离;
磁盘顺序写;
零拷贝(跳过用户缓冲区的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到用户态缓冲区);
分区/分段(每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力);
批量发送 (可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去,大大减少服务端的I/O次数)
数据压缩
ZooKeeper 运行期间,集群中至少有过半的机器保存了最新数据。集群超过半数的机器能够正常工作,集群就能够对外提供服务。
zookeeper可以选出N台机器作主机,它可以实现M:N的备份;keepalive只能选出1台机器作主机,所以keepalive只能实现M:1的备份。
通常有以下两种部署方案:双机房部署(一个稳定性更好、设备更可靠的机房,这个机房就是主要机房,而另外一个机房则更加廉价一些,例如,对于一个由 7 台机器组成的 ZooKeeper 集群,通常在主要机房中部署 4 台机器,剩下的 3 台机器部署到另外一个机房中);三机房部署(无论哪个机房发生了故障,剩下两个机房的机器数量都超过半数。在三个机房中都部署若干个机器来组成一个 ZooKeeper 集群。假设机器总数为 N,各机房机器数:N1 = (N-1)/2 ,N2=1~(N-N1)/2 ,N3 = N - N1 - N2 )。
水平扩容就是向集群中添加更多机器,Zookeeper2种方式(不完美),一种是集群整体重启,另外一种是逐台进行服务器的重启。
对于大促时候的秒杀活动,一般运营会配置静态的活动页面,配置静态活动页面主要有两个目的一方面是为了便于在各种社交媒体分发,另一方面是因为秒杀活动页的流量是大促期间最大的,通过配置成静态页面可以将页面发布在公有云上动态的横向扩展;
将秒杀活动的静态页面提前刷新到CDN节点,通过CDN节点的页面缓存来缓解访问压力和公司网络带宽,CDN上缓存js、css和图片;
将活动H5页面部署在公有云的web server上,使用公有云最大的好处就是能够根据活动的火爆程度动态扩容而且成本较低,同时将访问压力隔离在公司系统外部;
在提供真正商品秒杀业务功能的app server上,需要进行交易限流、熔断控制,防止因为秒杀交易影响到其他正常服务的提供,我们在限流和熔断方面使用了hystrix,在核心交易的controller层通过hystrix进行交易并发限流控制,当交易流量超出我们设定的限流最大值时,会对新交易进行熔断处理固定返回静态失败报文。
服务降级处理,除了上面讲到的限流和熔断控制,我们还设定了降级开关,对于首页、购物车、订单查询、大数据等功能都会进行一定程度的服务降级,例如我们会对首页原先动态生成的大数据页面布局降级为所有人看到的是一样的页面、购物车也会降级为不在一级页面的tabbar上的购物车图标上显示商品数量、历史订单的查询也会提供时间周期较短的查询、大数据商品推荐也会提供一样的商品推荐,通过这样的降级处理能够很好的保证各个系统在大促期间能够正常的提供最基本的服务,保证用户能够正常下单完成付款。
上面介绍的都是如何保证能扛住高并发,下面介绍下整个方案中如何防止超卖现象的发生,我们日常的下单过程中防止超卖一般是通过在数据库上实施乐观锁来完成,使用乐观锁虽然比for update这种悲观锁方式性能要好很多,但是还是无法满足秒杀的上万并发需求,我们的方案其实也很简单实时库存的扣减在缓存中进行,异步扣减数据库中的库存,保证缓存中和数据库中库存的最终一致性。
(1)使用redis的set集合
(2)使用redis的bitmap(注意内存消耗)
前端js,控制按钮。前端放置令牌。
数据库唯一索引。redis看key是否存在。或者数据库字段状态。
利用堆快照,查看到底是哪些对象占用大量内存导致经常gc
DB层面,有可能是sql,索引,表过大,数据库压力。
缓存层面:有可能缓存命中率差,redis性能瓶颈,需要扩容
服务器压力:服务器处理瓶颈
Java层面:代码写法
前端层面:cdn压力,页面压力
情景题:如果一个外卖配送单子要发布,现在有200个骑手都想要接这一单,如何保证只有一个骑手接到单子?
分布式锁,或者幂等接口,CAS乐观锁
场景题:美团首页每天会从10000个商家里面推荐50个商家置顶,每个商家有一个权值,你如何来推荐?第二天怎么更新推荐的商家?
可以借鉴下stackoverflow,视频网站等等的推荐算法。
悲观锁,乐观锁,存储过程放在mysql数据库中。
全局队列,把1000任务放在一个队列里面,然后每个人都是取,完成任务。
分为10个队列,每个人分别到自己对应的队列中去取务。
调用可以实现跟踪系统,可以在业务日志中添加调用链ID,各个环节RPC均添加调用时延,QPS等。
非业务组件应该少加入业务代码,服务调用采用买点,也会采用配置采样率方式,买点即当前节点的上下文信息,包含TraceId,RPCId,开始结束时间,类型,协议,调用方IP,端口,服务名等,以及其他异常信息,报文等扩展,日志采用离线+实时的如flume结合kafka等,应按照TraceId汇总日志后按RPCId顺序整理。
监控( Monitoring ): Redis Sentinel 实时监控主服务器和从服务器运行状态;
自动故障转移:如果一个 master 不正常运行了,哨兵可以启动一个故障转移进程,将一个 slave 升级成为 master,其他的 slave 被重新配置使用新的 master,并且应用程序使用 Redis 服务端通知的新地址;
雪花算法:
利用哈希环进行一致性哈希
★如何使用redis和zookeeper实现分布式锁?有什么区别优缺点,会有什么问题,分别适用什么场景。(延伸:如果知道redlock,讲讲他的算法实现,争议在哪里)
Redis实现比较复杂,流程如下:
根据lockKey区进行setnx(set not exist,顾名思义,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没有获取锁。
如果没有获得锁,去Redis上拿到该key对应的值,在该key上我们存储一个时间戳(用毫秒表示,t1),为了避免死锁以及其他客户端占用该锁超过一定时间(5秒),使用该客户端当前时间戳,与存储的时间戳作比较。
如果没有超过该key的使用时限,返回false,表示其他人正在占用该key,不能强制使用;如果已经超过时限,那我们就可以进行解锁,使用我们的时间戳来代替该字段的值。
但是如果在setnx失败后,get该值却无法拿到该字段时,说明操作之前该锁已经被释放,这个时候,最好的办法就是重新执行一遍setnx方法来获取其值以获得该锁。
缺点:有可能master崩溃,导致多节点获取到锁。
从实现难度上来说,Zookeeper实现非常简单,实现分布式锁的基本逻辑:
区别:
Redis分布式锁,需要自己不断去尝试获取锁,比较消耗性能
ZooKeeper分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小
如果Redis获取锁的那个客户端挂了,那么只能等待超时时间之后才能释放锁
而对于ZooKeeper,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁
redlock算法实现:
假设有5个完全独立的redis主服务器
获取当前时间戳
client尝试按照顺序使用相同的key,value获取所有redis服务的锁,在获取锁的过程中的获取时间比锁过期时间短很多,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁
client通过获取所有能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才算真正的获取锁成功
如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);
如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例;因为可能已经获取了小于3个锁,必须释放,否则影响其他client获取锁
redlock的争议点:(fgc导致的问题) 对于提升效率的场景下,RedLock 太重。 对于对正确性要求极高的场景下,RedLock 并不能保证正确性。
3pc 将2pc中的一阶段拆为 canCommit和prepareCommit
二阶段提交有几个缺点:
同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
3pc比2pc 减少事务阻塞范围 。3pc在超时后会自动提交。相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。
paxos算法的推导, 如果只有一个人投票的话,那么每个人必须接受第一个提议。 这样又会导致三个人分别投不同的票,形不成大多数。 推导出可以选多次票,但多次票有可能投自己又投别人,形成多个大多数,所以又加了限制条件,只能同意之前同意过的。 再推导出当两个机子重连的话,机子必须接受第一个发给他的提案,这样就违背了之前选好的。 所以当服务器重连的时候,必须发给他之前同意好的提案。 https://blog.51cto.com/12615191/2086264
zab协议:
原子广播:
ZAB 协议的消息广播过程使用的是一个原子广播协议,类似一个 二阶段提交过程。对于客户端发送的写请求,全部由 Leader 接收,Leader 将请求封装成一个事务 Proposal,将其发送给所有 Follwer ,然后,根据所有 Follwer 的反馈,如果超过半数成功响应,则执行 commit 操作(先提交自己,再发送 commit 给所有 Follwer)。
崩溃恢复:
针对这些问题,ZAB 定义了 2 个原则:
ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
如果让 Leader 选举算法能够保证新选举出来的 Leader 服务器拥有集群总所有机器编号(即 ZXID 最大)的事务,那么就能够保证这个新选举出来的 Leader 一定具有所有已经提交的提案。
当 Follower 链接上 Leader 之后,Leader 服务器会根据自己服务器上最后被提交的 ZXID 和 Follower 上的 ZXID 进行比对,比对结果要么回滚,要么和 Leader 同步。
★Dubbo的原理,有看过源码么,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现,重试转发,快速失败的策略是怎样的 。
MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
(另外一种硬件层面的解决是总线锁)
用途:类似文件系统的分布式协调服务。
选举原理:
zookeeper,判断某个节点下的子节点到达一定数目后,则执行,否则等待。
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
发送消息后和接收消息后 确认机制 加上持久化
唯一主键,或者使用redis做id,
避免阻塞
https://www.cnblogs.com/leaves1024/p/11073191.html
https://blog.csdn.net/chizizhixin/article/details/78563595
https://blog.csdn.net/lsh2366254/article/details/84910011使用同一个queue
实时队列采用双队列模式,生产者将行为记录写入Queue1,worker服务从Queue1消费新鲜数据,如果异常则写入Queue2(主要保存异常数据),RetryWorker会监听Queue2,消费异常数据,如果还未处理成功按照一定的策略等待或者将异常数据再写入Queue2,如果数据发生积压可以调整worker的消费游标,从最新数据重新开始消费,保证了最新data得到处理,中间未处理的一段则可以启动backupWorker指定起止游标在消费完指定区间的数据后,backupWorker会自动停止。
DB降级开关后,可直接写入redis(storm),同时将数据写入一份到Retry队列,在开启DB降级开关后消费Retry队列中的数据,从而把数据写入到mysql中,达到最终一致性。MYSQL切分为分片为2的N次方,例如原来分为两个库d0和d1均放在s0服务器上,s0同时有备机s1,扩容只要几步骤:确保s0到s1服务器同步顺利,没有明显延迟;s0暂时关闭读写权限;确保s1已经完全同步到s0更新;s1开放读写权限;d1的dns由s0切换到s1;s0开放读写权限。
转自我的github