热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

架构设计小心得

项目开发流程:image-20220303112748431image-20220303113042065短链接设计:方案一:设计:id    BIGINT,自增主键url    

项目开发流程:

image-20220303112748431image-20220303112748431
image-20220303113042065image-20220303113042065


短链接设计:


方案一:


设计:

id    BIGINT,自增主键
url    长地址,也就是需要跳转的原地址

缺点:

(1)如果数据比较大,比如几百亿,你的url地址依然过长
(2)你的数据具有规律性,别人用一个简单的脚本就可以遍历出你的跳转地址!

方案二:


设计:

id    BIGINT,自增主键
key    短串,需要加唯一索引
url    长地址,也就是需要跳转的原地址
有两种方法生成key,一个是id转62进制(每一个新url需要查询是否已发id,lru缓存),另一个是url进行hash(可能出现冲突,比如md5碰撞几率极小)
推荐:
    通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。
    如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。  key为长url

数据过长问题:

由于我们的短链接是由 a-z、A-Z 和 0-9 共 62 个字符可以选择。因此,我们可以讲十进制的数字id,转换为一个62进制的数,例如201314就可以转换为Qn0。

数据规律性:

(1)不希望反推出全局ID
    使用洗牌算法
(2)希望反推出全局ID
    固定位加减

细节优化:

(1)分库分表(增加并发)
    问题1:分片key?
        如果这个系统是放在公网,给大家使用的。建议上来就分库分表,数据量过1000万是很容易的。这里涉及到一个问题,拿全局发号器给的自增id做分片健,还是拿转换后的key做分片键。
        数据库存放id->长url的记录,所以用id
        key通过进制转换,拿到id后查询数据库位置,直接根据主键查询。
    问题2:如何保证发号器的大并发高可用?
        我们是否可以实现两个发号器,一个发单号,一个发双号,这样就变单点为多点了?依次类推,我们可以实现1000个逻辑发号器,分别发尾号为0999的号。
        每发一个号,每个发号器加1000,而不是加1。这些发号器独立工作,互不干扰即可。
        而且在实现上,也可以先是逻辑的,真的压力变大了,再拆分成独立的物理机器单元。
(2)读写分离
    这种系统显然,读远大于写。建议可以考虑做读写分离。
(3)引入缓存
    假设,我们在一个时间。给手机推送短信链接的短信后。显然,后面的一段时间内,对该短链接的请求量会大大提升。没有必要每次都去数据库查询,因此可以引入redis缓存。
(4)全局发号器用其他算法行不行
    可以。这里只是要一个全局唯一ID而已。自己要估算好,使用其他算法所带来的性能影响。以及采用其他算法,会不会造成生成的生成的ID过于规律。
(5)防攻击
    做好被恶意攻击的准备,防止自增ID的值,被全部耗光。
(6)跳转用301还是302 
    301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。同时对服务器压力也会有一定减少。
    但是如果使用了301,我们就无法统计到短地址被点击的次数了。而这个点击次数是一个非常有意思的大数据分析数据源。
    能够分析出的东西非常非常多。所以选择302虽然会增加服务器压力,但是我想是一个更好的选择。

分布式一致性:


CAP原理:

一个分布式系统最多只能同时满足 一致性(Consistency),可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

场景:

领导者选举(leader election):进程对leader达成一致;
互斥(mutual exclusion):进程对进入临界区的进程达成一致;
原子广播(atomic broadcast):进程对消息传递(delivery)顺序达成一致。

强一致性算法:

raft共识算法(consensus algorithm)
zab是对Paxos底层算法的工业实现

raft:

raft会先选举出leader,leader完全负责replicated log的管理。leader负责接受所有客户端更新请求,然后复制到follower节点,并在“安全”的时候执行这些请求。如果leader故障,followes会重新选举出新的leader。
状态: 
    leader
    follower
    candidate
    过程:flower->candidata->leader,leader会有任期term
日志提交:
    首先由leader即节点a接收到,并且节点a写入一条日志。由于这条日志还没被其他任何节点接收,所以它的状态是uncommitted。
    为了提交这条日志,Leader会将这条日志通过心跳消息复制给其他的Follower节点:
    一旦有大多数节点成功写入这条日志,那么Leader节点的这条日志状态就会更新为committed状态,并且值更新为5
选举超时:
    为了防止3个节点(假设集群由3个节点组成)同时发起投票,会给每个节点分配一个随机的选举超时时间(Election Timeout),即从Follower状态成为Candidate状态需要等待的时间。
    在这个时间内,节点必须等待,不能成为Candidate状态。如下图所示,节点C优先成为Candidate,而节点A和B还在等待中:
心跳超时
    leader节点会固定间隔时间向两个Follower节点A和C发送心跳消息,这个固定间隔时间被称为heartbeat timeout。
重新选举:
    选举过程中,如果Leader节点出现故障,就会触发重新选举。如下图所示,Leader节点B故障(灰色),这时候节点A和C就会等待一个随机时间(选举超时),谁等待的时候更短,谁就先成为Candidate,然后向其他节点发送投票请求:
网络分区:
    节点A和B在一个网络分区,节点C、D和E在另一个网络分区,如下图所示,且节点B和节点C分别是两个网络分区中的Leader节点
    一个客户端,并且往节点B上发送了一个SET 3,由于网络分区的原因,这个值不能被另一个网络分区中的Leader即节点C拿到,它最多只能被两个节点(节点B和C)感知到,所以它的状态是uncomitted(红色)
    另一个客户端准备执行SET 8的操作,由于可以被同一个分区下总计三个节点(节点C、D和E)感知到,3个节点已经符合大多数节点的条件。所以,这个值的状态就是committed
    假设网络恢复正常,如下图所示。节点B能感知到C节点这个Leader的存在,它就会从Leader状态退回到Follower状态,并且节点A和B会回滚之前没有提交的日志(SET 3产生的uncommitted日志)。
    同时,节点A和B会从新的Leader节点即C节点获取最新的日志(SET 8产生的日志),从而将它们的值更新为8。如此以来,整个集群的5个节点数据完全一致了

一致性Hash算法:


介绍:

对于大量随机的请求或调用,通过一定形式的Hash将他们均匀的散列,从而实现压力的平均化。

问题:

如果将Key作为缓存的Key,对应的Group储存该Key的Value,就可以实现一个分布式的缓存系统
但只要集群的数量N发生变化,之前的所有Hash映射就会全部失效。

解决:

一致性Hash通过构建环状的Hash空间代替线性Hash空间的方法解决了这个问题。
整个Hash空间被构建成一个首尾相接的环,使用一致性Hash时需要进行两次映射。
    第一次,给每个节点(集群)计算Hash,然后记录它们的Hash值,这就是它们在环上的位置。
    第二次,给每个Key计算Hash,然后沿着顺时针的方向找到环上的第一个节点,就是该Key储存对应的集群。
当节点被删除时,其余节点在环上的映射不会发生改变,只是原来打在对应节点上的Key现在会转移到顺时针方向的下一个节点上去。增加一个节点也是同样的,最终都只有少部分的Key发生了失效。
不过发生节点变动后,整体系统的压力已经不是均衡的了,下文中提到的方法将会解决这个问题。

数据倾斜:

如果节点的数量很少,而hash环空间很大(一般是 0 ~ 2^32),直接进行一致性hash上去,大部分情况下节点在环上的位置会很不均匀,挤在某个很小的区域。
最终对分布式缓存造成的影响就是,集群的每个实例上储存的缓存数据量不一致,会发生严重的数据倾斜。
缓存雪崩
如果每个节点在环上只有一个节点,那么可以想象,当某一集群从环中消失时,它原本所负责的任务将全部交由顺时针方向的下一个集群处理。例如,当group0退出时,它原本所负责的缓存将全部交给group1处理。
这就意味着group1的访问压力会瞬间增大。设想一下,如果group1因为压力过大而崩溃,那么更大的压力又会向group2压过去,最终服务压力就像滚雪球一样越滚越大,最终导致雪崩。
引入虚拟节点
解决上述两个问题最好的办法就是扩展整个环上的节点数量,因此我们引入了虚拟节点的概念。一个实际节点将会映射多个虚拟节点,这样Hash环上的空间分割就会变得均匀。
同时,引入虚拟节点还会使得节点在Hash环上的顺序随机化,这意味着当一个真实节点失效退出后,它原来所承载的压力将会均匀地分散到其他节点上去。

另一种解法:

将环上的空间均匀的映射到一个线性空间,每个节点占用一定的区间,那么数据的映射是均匀的。
问题是原来分配到一个节点的,现在由于区间变小,可能分配到另一个节点上。解决:线性空间失效的那部分平分到其他区间。

服务架构设计:


单机时代-单机模式:

单台服务器Web+DB
1.用户浏览器输入url
2.DNS服务器域名解析
3.web服务器架构:apache,数据读写用mysql
    数据库每日凌晨备份,保存到本地:
        主从复制
        定时:写一个linux定时脚本

单机时代-动静分离:

单台服务器Web+DB
Nginx+uwsgi实现动静分离
    nginx的location 写root
    静态资源开启压缩
    方案:
        方案一:
            最前端nginx做负载均衡,不动静分离,不用项目代码
            后面的多台nginx+uwsgi实现动静分离。
            优点:适用性强,访问日志可定制
        方案二:
            最前端nginx做负载均衡和动静分离,需项目代码
            后面的多台uwsgi。
            优点:架构简单
数据定时备份

单机时代-数据库分离:

web服务器和数据库服务器单独部署
    或者数据库服务器用来做从,备份,只读
        一定要set global readonly,以防用错库
        mysql主从备份不能限速
        日志log是否备份、保留多久:每天整库的备份
web服务器资源备份到数据库服务器
数据库每日凌晨备份,保存到web服务器

单机时代-组件分离:

静态资源专门搞一个服务器
    方法一:NFS文件挂载到这个服务器
    方法二:这个服务器部署nginx,只能访问静态资源
静态服务器使用一级独立域名www.shop-pic.com防止COOKIE提交
静态服务器可以使用多个二级域名w提高加载速度
静态资源作为NFS,存放在NFS上
数据库每日备份到NFS
优点:
    1.多个二级域名,增加浏览器下载速度,浏览器对每个域名请求有并发限制。
    2.拆分域名防止COOKIE提交。默认浏览器保留的COOKIE访问相同域名时会自动发送。占用带宽
    3. html的css文件js文件位置,提高浏览器加载速度
    4. 静态资源压缩
    5.多个域名需预解析,html5预获取dns,<link rel="dns-prefetch">

web集群-HTTP重定向:

1.使用HTTP重定向302和请求头Location(代码实现),把一部分数据转移到其他节点。
    多个下载节点选择
2.数据库使用复制技术做主从
3.NFS使用rsync进行定时备份
4.该架构常见于下载站点,web站点不常见

web集群-DNS轮询:

1.使用DNS轮询实现负载均衡集群,将域名解析为不同ip。
2.无法动态感知web服务器故障,无法快速故障转移
3.常用于和其他集群模式配合,以及智能DNS与其他集群模式配合

web集群-反向代理负载均衡:

1.使用Nginx作为反向代理,依靠proxy模块,实现负载均衡。
2.Nginx支持多种负载均衡算法和健康检测(web服务器是否返回,没返回剔除)    

web集群-负载均衡+高可用:

1.引入Keepalived,解决反向代理单点故障,多个Nginx配置一样。
2.DNS解析的公网IP使用VIP,即keepalivedVRRP竞选协议,心跳检测
3.会存在session问题

web集群-session解决:

1.Session解决三大方案:保持ip_hash、复制、共享。
2.使用redissession共享

web集群-haproxy+keepalived:

1.使用haproxy四层和七层负载均衡替代nginx。

web集群-LVS+keepalived:

1.使用LVS四层和二层负载均衡替代haproxy。
2.二层修改mac地址,四层修改ip和port

web集群-CDN:

1.引入CDN解决静态资源全球访问。
2.降低机房带宽、提高静态资源访问速度
3.一般购买cdn使用
4.调度、内容分发、内容管理、运营管理

web集群-分布式缓存:

1.引入分布式缓存,做数据库读缓存、写缓存(异步写,有高可用问题,用消息队列延迟写到数据库)。
2.使用持久化的Nosql解决大量K/V类型数据的读写问题

web集群-LVS+Haproxy:

1.LVS做四层负载均衡(七层负载性能扛不住)。
2.Haproxy/Nginx做统一七层调度(四层分发给七层、七层分发到集群),四层专注调度,七层专注URL处理、https证书等
3.如果业务扩展,可以使用多组负载均衡

web集群-SOA(量大到集群扛不住):

1.通过SOA进行业务解耦(服务注册中心、商品服务、购物车、结算中心。。。)。
2.使用消息队列作为SOA服务间的消息传递
3.使用注册中心用于服务注册,自动切换集群容量,进行扩容和缩容
4.SOA集群的负载均衡有内部负载均衡提供
5.分服务后分库比较简单

负载均衡:


keepalived是什么及作用?

基于vrrp协议的一款高可用软件
Keepalived的作用是检测服务器的状态,如果有一台web服务器宕机,或工作出现故障,Keepalived将检测到,
并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工作正常后Keepalived
自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务
器。
原理:
    master向backend定时(3秒)发IP多播包,告知自己在线
    竞选协议来竞选master
    VRRP模块做HA健康检测,配置master或backend等信息,多台服务器的keepalived的一台成为master路由器后,将请求转发给内部LVS
    LVS模块做NAT转发,配置virtual_server或组,里面包含多个real_server,real_server里设置健康检测方式和权重等

haproxy是什么以及作用?

一个使用C语言编写的自由及开放源代码软件
HAProxy是一个使用C语言编写的自由及开放源代码软件[1],其提供高可用性、负载均衡,以及基于TCP和HTTP
的应用程序代理。
HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理。HAProxy运行在当前
的硬件上,完全可以支持数以万计的并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构
中, 同时可以保护你的web服务器不被暴露到网络上。
HAProxy实现了一种事件驱动, 单一进程模型,此模型支持非常大的并发连接数。多进程或多线程模型受内存限制
、系统调度器限制以及无处不在的锁限制,很少能处理数千并发连接。事件驱动模型因为在有更好的资源和时间管
理的用户空间(User-Space) 实现所有这些任务,所以没有这些问题。此模型的弊端是,在多核系统上,这些程
序通常扩展性较差。这就是为什么他们必须进行优化以 使每个CPU时间片(Cycle)做更多的工作。
包括 GitHub、Bitbucket[3]、Stack Overflow[4]、Reddit、Tumblr、Twitter[5][6]和 Tuenti[7]在内的知
名网站,及亚马逊网络服务系统都使用了HAProxy。
作用:
    负载均衡
对比nginx:
    七层代理,L4-L7
    对session保持支持较多,如COOKIE
    负载均衡算法多,htp请求头、ip、最小连接数、COOKIE、url、轮询、权重轮询

四层负载均衡和七层的区别:

四层:转发,功能少,性能高
    修改报文源Ip、目标IP等,
七层:代理,功能多,能根据url,ip等http协议做定制操作,灵活。
    独立的TCP连接到服务器,服务器然后TCP握手到Server
方案:
    四层在前面,七层在后面

LVS:

四层 转发 内核级别 内存和CPU消耗不大,配置简单,能做数据库、web服务器
性能高
调度算法多
无健康检测

Nginx:

七层 代理 应用级别 正则表达式做路由分发    
可以负载均衡、web服务器
性能好
缺点:不支持自定义URL检测,对某个url做健康检查
不支持session保持,通过ip_hash解决

缓存:


用户层缓存:


DNS缓存:

本地DNS缓存
local缓存
服务器缓存
浏览器缓存 -- 预获取dns prefetching
# 问题:如果域名对应的ip要更改,要考虑运营商的缓存,做个反向代理

浏览器:

浏览器缓存页面内容
    基于最后修改时间的http缓存协商,比如静态页面,有最后一次修改时间,没有修改使用缓存,返回http304
    基于标签Etag,适合修改时间经常变动,内容却无变化的情况。浏览器将etag发给服务器,服务器判断etag是否改变
                如果无改变,服务器返回304
    基于过期时间,Expires、cache-control,为静态资源设置超时时间。
            Expires看客户端时间,时间不对会出现马上超时的情况
            cache-control给时间周期,根据浏览器本地时间来判断     
    # 刷新:
        回车 所有没有过期的缓存直接使用
        F5 刷新按钮 发起请求,没有过期的缓存返回304
        ctrl+F5\点击刷新 强制刷新 重新请求缓存
    # 问题:
        静态资源缓存了30天要刷新怎么办?
            改静态资源名字
            给js增加时间戳 code.js?2013355 对于不同的时间戳,浏览器会重新请求,类似cdn

代理层:

CDN反向代理缓存
    内容分发网络,解决带宽以及用户就近访问的问题,安全,用户请求会被cdn的dns解析器分发给就近、负载轻的节点
    调度  
    缓存
    性能优化
    安全

web层:

解释器
    opcache 操作码缓存 如高级语言的中间码缓存,下次直接执行,python的pyc
web服务器
    web服务器缓存,如Nginx的FastCGI缓存,Proxy cache

应用层:

应用服务
    local cache 应用本地缓存
    动态内容缓存 缓存动态内容输出,开发框架自带
    页面静态化   将不常变动的内容缓存,变化内容如价格动态定期获取更新,

数据层:

分布式缓存
    redis等,主要读缓存、写缓存也可以,持久化当数据库
数据库
    mysql的innodb缓存、myisam缓存

系统层:

操作系统
    CPU L1的数据缓存、指令缓存 L2 L3
内存缓存
    内存高速缓存、page cache

物理层:

硬盘 
    disk cache 磁盘缓存,一般关闭
硬件
    磁盘阵列缓存,带电源,突然断电也没事,一般开启


推荐阅读
  • OpenEdge已经更名为Baetyl。OpenEdge是百度云发布的国内首个开源边缘计算产品,可将云计算能力拓展至用户现场,提供临时离线、低延时的计算 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文总结了Java中日期格式化的常用方法,并给出了示例代码。通过使用SimpleDateFormat类和jstl fmt标签库,可以实现日期的格式化和显示。在页面中添加相应的标签库引用后,可以使用不同的日期格式化样式来显示当前年份和月份。该文提供了详细的代码示例和说明。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了求解gcdexgcd斐蜀定理的迭代法和递归法,并解释了exgcd的概念和应用。exgcd是指对于不完全为0的非负整数a和b,gcd(a,b)表示a和b的最大公约数,必然存在整数对x和y,使得gcd(a,b)=ax+by。此外,本文还给出了相应的代码示例。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • JetBrain有哪些产品可以用来编写C++和C代码?
    本文介绍了JetBrain的哪些产品可以用来编写C++和C代码,帮助读者选择适合自己的开发工具。 ... [详细]
  • BPM是什么软件?1、BPM是BusinessProcessManagement的简称,译为业务流程管理,它是一种以规范化的构造端到端的卓越业务流程为中心以持续的提高组织业务绩效为 ... [详细]
author-avatar
情之瞬
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有