引起了高度的重视。因为这是修改之前几年前编写的几经易手、十分核心且之前没怎么敢改动的代码。 详细方案设计在别人写的代码上做修改,做详细设计时,第一步要做的是充分评估改动影响;第二步是画流程图梳理改动前后的调用链和数据流,列出修改点;第三步是定好测试关键案例,确保结果的正确性。 在很多方案设计中,往往没有将这一步规划到明确的流程中去,草率的实施,是日后出现问题的根源。 具体要怎么做呢?举个例子来说,之前做过很多http接口,常有需求说要在返回值里添加字段。 我也不建议他这样的保证。后来,我自己想了一下,如果用两个模板,两个append同时写一个日志文件,之前也没有这么用过,也有风险,所以还是按照他说的改了。 其实本质上我同事的意思就是:“我和你一起保证修改的正确性”。用心是非常好的。 最终提的7条每条我们都争论了,那是因为每一条我们两个都真正思考过。这种氛围我觉得是非常好的。
集群备份会话管理器 全节点复制模式存在的一个很大的问题就是用于备份的网络流量会随着节点数的增加而急速增加,这也就是无法构建较大规模集群的原因。为了解决这个问题,tomcat提出了集群备份会话管理器。 这样就可构建大规模的集群。 ? 同步组件 在上述无论是发送还是接收信息的过程中,使用到的组件主要有三个:Manager,Cluster,tribes。 简单来讲,Manager的作用是将操作的信息记录下来,然后序列化后交给Cluster,接着Cluster是依赖于tribes将信息发送出去的。 其余节点收到信息后,按照相反的流程一步步传到Manager,经过反序列化之后使该节点同步传递过来的操作信息。如图,假设我们访问的是中间的节点,该节点将信息同步出去。 信息是以Cluster Message对象发送的。
VOLATILE 只保证可见性,并不保证原子性 ? 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型。 类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤: ① 取出a和b ② 计算a+b ③ 将计算结果写入内存 Synchronized:保证可见性和原子性 Volatile:保证可见性,但不保证操作的原子性 Volatile实现内存可见性是通过store和load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中 Synchronized和Volatile的比较 1)Synchronized保证内存可见性和操作的原子性 2)Volatile只能保证内存可见性 3)Volatile不需要加锁,比Synchronized 所以volatile不能保证i++操作的原子性
,那么你会怎么设计这个架构。 这种衡量指标能在一定的程度上反应这段时间的系统性能,这也是很多程序员喜欢用的判定性能的方式,但是这种方式判定具有不确定性,感知能力较弱,假如这段时间内只有少数的请求慢,其最终的平均值是没什么大的变化的, 现在已经确定了系统性能的衡量指标了,那最终的性能是需要相关的编码进行实现的。 /O模型:阻塞、非阻塞、同步、异步 进程模型:单进程、多进程、多线程 相信到了这里,加上前面分析的指标数据,通过压测找到你系统当前单机的性能瓶颈,此时肯定定能知道怎么去优化你的代码。 IO密集型系统是指大部分操作是在等待IO(磁盘IO,网络IO)完成,像数据库系统、缓存系统、WEB系统等都属于IO密集型系统,那这类系统的瓶颈需要怎么发现优化呢 1,可以分析linux系统上的磁盘、文件系统
03 设计系统高可用延伸思路 上面介绍了我们在宏观方面怎么设计系统高可用,其实我们在编码的时候除了故障转移方案,同样需要考虑很多东西来保证系统的可用性,主要想体现在,超时机制、降级、限流等 超时机制 在我们系统中其实大部分会调用三方接口 所以这块我们需要对此进行设置合理的超时时间,来快速结束这些慢请求,来保证我们系统的可用性。 降级 降级也是互联网中老生常谈的话题,那么我们使用降级方案如何来保证系统的可用性呢,分析自己的业务,在面对流量剧增的时候,例如,秒杀,大促等这些剧增的大流量,可以将不影响本次业务的流程也砍掉,不去调用了 其实各大电商都是采取这种方案来保证系统的可用性的。 所以限流就是为了保证系统的高可用而限制住大流量的情况发生。
1.保证消息不会丢失 消息持久化;ACK确认机制;设置集群镜像模式;消息补偿机制 第一种:消息持久化 RabbitMQ的消息是默认放在内存的,如果不特别声明消息持久到磁盘,当节点关掉或者crash(碰撞 收到一半就没了,消费者就死掉了; 这个使用就要使用Message acknowledgment 机制,就是消费端消费完成要通知服务端,服务端才把消息从内存删除,一个消费者出了问题,没有同步消息给服务端,还有其他的消费端去消费 ,保证了消息不丢的case 第三种:设置集群镜像模式 RabbitMQ三种部署模式: 单点模式:最简单的模式,非集群模式,节点挂了,消息不可用了,业务瘫痪,只能等待; 普通模式:必须消息是持久的,默认是集群模式 ,某个节点挂了,消息不可用了,业务瘫痪了,此时只能等待节点恢复重启使用; 镜像模式:把队列做成镜像需要的队列,存放于多个节点, 属于RabbitMQ的HA方案; 队列的内容仅仅只存在于某一个节点,并不在所有的节点 第四种:消息补偿机制 消息补偿机制需要建立在消息要写入DB日志,发送日志,接受日志,两者的状态必须记录,然后根据DB日志记录check 消息发送消费是否成功,不成功,进行消息补偿措施,重新发送消息处理。
所谓主备延迟,就是同一个事务,在备库执行完成的时间和主库执行完成的时间之间的差值,也就是 T3-T1。 备库所在机器的性能要比主库所在的机器性能差。 但实际上,更新过程中也会触发大量的读操作。所以,当备库主机上的多个备库都在争抢资源的时候,就可能会导致主备延迟了. 当然,这种部署现在比较少了。 (删除表空间) 备库的并行复制能力 由于主备延迟的存在,所以在主备切换的时候,就相应的有不同的策略。 fs page cache②备库的压力大,产生的原因大量的查询操作在备库操作,耗费了大量的cpu,导致同步延迟,解决办法,使用一主多从,多个从减少备的查询压力③大事务,因为如果一个大的事务的dml操作导致执行时间过长 问题:发生主从切换的时候,主有的最新数据没同步到从,会出现这种情况吗,出现了会怎么样?
同时,也需要删除跳跃表中的该节点。这样的做法可以保证删除操作的正确性和性能,原因如下:使用有序集合可以确保跳跃表中的节点值唯一。 Redis的有序集合是使用跳跃表+字典的数据结构实现的,跳跃表保证了有序集合的有序性,字典用于存储节点值和节点的指针,以支持高效的查找和删除操作。 在插入新节点时,通过在有序集合中查找是否已经存在相同的节点值,可以避免插入重复的节点。这样可以保证跳跃表中不会存在重复节点的情况。在删除节点时,先在有序集合中查找到对应的节点,并删除该节点。 这样可以确保删除操作的正确性,并保持跳跃表和有序集合的一致性。 综上所述,通过使用有序集合来存储跳跃表节点的值和分值,并对插入和删除操作做相应的处理,可以有效地处理Redis的跳跃表中可能存在的重复节点,并保证删除操作的正确性和性能。
关于HTTPS的连接过程,也是老生常谈的话题了。 其中涉及到的数字证书、电子签名、SSL/TLS、对称加密、非对称加密的问题总是让人摸不清头脑,不知道怎么回答。 虽然数据加了密看似安全了,但是加密的密钥怎么管理呢?这是个大问题,保存在客户端?引入插件?感觉都不是什么比较好的办法,都还是有可能被破解。 兼容问题。 有人就会问了,这不还是和刚才说到的一样吗?这个密钥怎么管理呢? 这就需要在正式传输数据之前 想办法 把这个对称密钥告诉对方了。而这个办法就是——非对称加密。 怎么告诉对方这个对称密钥? 但是,这个用作签名的另外的私钥和另外的公钥怎么来的呢?这就需要强大的CA来验证了。 CA自己的私钥对这个证书的签名 然后服务器将这个证书在连接阶段传给客户端,客户端怎么验证呢?
前面分享了高并发系统(你们系统是怎么保证高并发的)以及高可用系统(你们系统是怎么保证高可用的)的解决方案,今天我们再来看另一个很重要的模块,可扩展系统,系统的可扩展性同样是架构所需要重点考虑的一个设计点 顾名思义,可扩展即是通过增加相应的机器来达到抗住系统的突然流量激增的目的。 所以,今天我们来看看该怎么设计一个可扩展的系统,目的是,在公司运营突然大促或者我们应用曝光量更火爆的时候,我们能够从容的端着咖啡去应对,而不是被产品逼着问服务怎么又停了。 比如我们酒店预订中就可以将运营和相关权益政策服务进行降级,不去请求他们,从而让我们整个系统能保证用户能搜到和订到酒店。 ? 总结,今天我分享了可扩展是架构必须要考虑的设计点,以及可扩展设计并不能一味的只考虑服务层的扩展,要全局的把控,同时后面讲到了我们通过拆分的方法论进行如何优雅的进行设计系统的可扩展。
前言 RabbitMQ相信大家都非常熟悉了,今天咱们来聊聊怎么保证RabbitMQ的可靠性。 那什么时候会出现问题呢? 第一种是生产端出现的问题。 消费端处理消息时如果出现异常,默认的解决方式是在重复消费多次,当次数超过阈值时直接删除消息,这也导致消息丢失。 接下来咱们就看看怎么应对以上三种问题。 不过ack的情况下消息未送达队列,会有相应的错误信息提醒。 nack就代表消息并未送达交换机。 那么,怎么才能知道消息发送情况呢? 可以设置callback来获取消息发送结果。 然后我们设置的转入队列中的消息数加一,这时候我们可以接收下该队列中的信息,存储到数据库中,方便维护人员手动进行处理。 总结 从生产端、RabbitMQ以及消费端三方面介绍了一下怎么保证RabbitMQ的可靠性,另外还有关于死信队列和延迟队列的内容在这篇博客中,大家有兴趣可以看一下。
1、键的过期删除策略 Redis中键到达设定的过期时间后并不会马上被删除,这是因为如果是一个存储多个数据的大列表,那么删除时是会阻塞命令执行线程的,这会造成其他命令无法执行,因此Redis采用的是定时删除和懒惰删除兼具的做法 定时删除:在一个给定时间批量删除过期键,如果键的过期时间基本一致就会带来跟即时删除一样的问题,因此建议设置过期时间时添加随机值。 Redis默认每秒会进行十次过期扫描,会从过期字典中随机取出二十个键,删除其中已经过期的,如果已过期的占总数的1/4那么就再进行一次扫描,以此循环,当然Redis默认设置了扫描的最长时间为25ms; 懒惰删除 ,但后面来的写请求就不能处理了; 这里需要注意,Redis中使用的是近似的LRU算法,在第一次采样是从数据中随机选出N个存入集合中,之后将这N个中lru字段最小的淘汰出去,如果缓存空间还是到达限制,就接着再次进行随机抽取数据 ,跟现有集合数据进行比较,淘汰出最小的那个。
binlog 的写入机制 其实,binlog 的写入逻辑比较简单:事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。 一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及到了 binlog cache 的保存问题。 图中的 write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。 图中的 fsync,才是将数据持久化到磁盘的操作。 write 和 fsync 的时机,是由参数 sync_binlog 控制的: sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync; sync_binlog=1 的时候 在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成 0,比较常见的是将其设置为 100~1000 中的某个数值。
学Java的都知道hashMap的底层是“链表散列”的数据结构也也可以说是hash表。在put的实话先根据key的hashcode重新计算hash值的,而我们又知道hash是一种算法。 所以哈希码并不是完全唯一的。 查看哈希码百科: ? 哈希表可以说就是数组链表,底层还是数组但是这个数组每一项就是一个链表 一:为什么说hashmap的put方法是根据key进行hashcode计算的呢? 查看源码: ? 在查看hash方法,如下: ?
问题描述 堆空间是线程共享的,那当多个线程同时申请堆内存空间,怎么保证线程安全 2. 解决办法 常见的解决办法就是CAS,失败重试,但是每次线程申请内存的时候都进行CAS,在并发高的情况下,会影响性能。 所以HotSpot虚拟机中采用TLAB的方法进行内存分配。 即: 每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块"私有"内存中分配,当这部分区域用完之后,再分配新的"私有"内存。 如何调整TLAB默认大小 -XX:TLABSize 通过该参数指定分配给每一个线程的TLAB空间的大小 总结一下TLAB: 需要TLAB的原因就是提高对象在堆上的分配效率而采用的一种手段,就是给每个线程分配一小块私有的堆空间 参考 JAVA | Java对象的内存分配过程是如何保证线程安全的? 关于栈上分配和TLAB的理解
系统A和系统B都是前后端分离的,比如前端框架用的 React / Vue / Angular,都是通过 NPM 编译后独立部署的,前后端完全通过HTTP接口的方式进行交互,也有可能前后端项目的域名都不一样 试想一下: 三个系统都是前后端分离的情况,流程图应该怎么调整? 三个系统都不是前后端分离的情况,流程图应该怎么调整? 对外接口 系统A和系统B:用户退出接口。 上图,表示的是从某一个系统退出的流程图。 退出,还可以从SSO认证中心退出,然后调取各个系统的用户退出接口。 当用户再进行操作的时候,就会跳转到SSO的登录界面。 扩展 SSO与OAuth的区别 谈到SSO很多人就想到OAuth,也有谈到OAuth想到SSO的,在这里我简单的说一下区别。 通俗的解释,SSO是处理一个公司内的不同应用系统之间的登录问题,比如阿里巴巴旗下有很多应用系统,我们只需要登录一个系统就可以实现不同系统之间的跳转。
这里不谈秒杀设计,不谈使用队列等使请求串行化,就谈下怎么用锁来保证数据正确,就是已经到减库存那一步了,在这一步中如果保证不超卖。 在这种隔离级别下,同一个事务中多次读取,返回的数据是一样的 同时,Spring声明式事务默认的传播特性REQUIRED ? Spring声明式事务是Spring AOP最好的例子,Spring是通过AOP代理的方式来实现事务的,也就是说在调用reduceStock()方法的之前就已经开启了事务。 S1已提交的数据,于是就会出现T1和T2读取到的值是一样的,即T2读取的是T1更新前的库存数据。 不仅要关注值,还要关注是不是原来的对象 ? 基于“值”的CAS乐观锁,可能导致ABA问题。CAS乐观锁,必须保证修改时的“此数据”就是“彼数据”,应该由“值”比对,优化为“版本号”比对。 ?
公司年会有个环节是现场线上线下互动,现场会通过二维码及其他方式迅速建起一个超过5000人的群。员工会在群里分享一年来的感人故事,业绩以及对公司的祝愿,老板们可能在群里发红包,表示对员工的感谢。 群里的信息会实时投影到现场的大屏幕上,同时也会推送到个人手机上。 这样一个群,短时间的消息并发量非常大,消息的延迟很难完全避免。普通消息延迟一点大家也就忍了,但是红包怎么办呢? 首先看看延迟发生在哪些环节 1、运营商基站 一些大型的聚会活动(如演唱会),我们往往能看到运营商架设的临时基站,架设基站的原因是解决固定基站容量和带宽不足的问题。 即使1个红包消息,也需要进行5000次推送,这个点上也是有先有后的。 延迟很难避免,我们在推送顺序上做一些策略来尽量保证红包的公平性。 尽管对于单个红包,某些成员的推送顺序有优势(优势也是随机的),但对于多个红包,能保证不会总是某些人有优势。 消息从服务端推送出去后,到底先到达谁的手机就不好说了(网络因素太复杂)。
上一章的时候不是已经介绍过了吗? 老王:上一章只是简单的介绍了一下CAS功能而已,但是关于unsafe的cas功能底层是怎么保证原子性的?在操作系统层面是怎么实现的? 这些东西我们还没有讲。 小陈:我记得CAS操作是可以保证原子性的,也就是同一个时间,同一个操作只允许一个CPU操作成功,它这个又是怎么保证的呢? 老王:这个啊,其实CAS底层的操作,还是会用到锁的!!! 小陈:哈哈,老王,你画的这个图太好了,我看到图就知道它是怎么操作的了,真牛啊...... 老王:好的,那这一张CAS底层加锁保证原子性的讨论我们就到这里了,我们明天继续... 怎么解决并发的可见性问题? JAVA并发专题《练气篇》 5.volatile怎么保证可见性? 6.什么是内存屏障?具有什么作用? 7.volatile怎么通过内存屏障保证可见性和有序性? 15.unsafe类的CAS是怎么保证原子性的?