如何设计和实现自适应的负载均衡

本文是第五届中间件性能挑战赛的赛题解析,参与比赛,赢取最高10万元奖金。 在现代分布式应用中,服务请求是由物理机或虚拟机组成的 server 池进行处理的。 通常,server 池规模巨大且服务容量各不相同,受网络、内存、CPU、下游服务等各种因素影响,一个 server 的服务容量始终处于动态变动和趋于稳定的状态,如何设计和实现这种系统的负载均衡算法是一个极具挑战的难题。 阿里巴巴中间件公众号对话框发送“挑战赛”,获取上一届优秀选手的解题思路,点击这里。 自适应负载均衡的需求背景负载均衡有两个主要目标: 保持较短的请求响应时间和较小的请求阻塞概率;负载均衡算法的 overhead 在可控级别,不占用过多的 CPU 、网络等资源。自适应负载均衡是指无论系统处于空闲、稳定还是繁忙状态,负载均衡算法都会自动评估系统的服务能力,进行合理的流量分配,使整个系统始终保持较好的性能,不产生饥饿或者过载、宕机。 这种算法对于现在的电商系统、数据中心、云计算等领域都很有必要,使用自适应负载均衡能够更合理的利用资源,提高性能。例如,在双十一零点,用户集中下单支付,整个电商系统的请求速率到达峰值。如果将这些请求流量只分配给少部分 server,这些机器接收到的请求速率会远超过处理速率,新来的任务来不及处理,产生请求任务堆积。 对用户而言,一旦产生任务堆积,请求会变慢甚至超时,体验严重下降,甚至导致服务不可用。而处理请求的机器也会由于堆积的任务越来越多而发生严重过载,直到被打垮。剩余的尚未宕机的其它机器会逐渐重复这个过程,直至整个应用不可用,发生系统故障。 为了避免这种情况发生,我们可能会想到一种常用的办法:在服务上线前提前进行压测,使用压测的容量作为限流值,当线上服务的请求速率大于限流值的时候,服务拒绝新到的服务,从而保障服务始终可用。但是这种方式也存在问题:压测时测试的容量进行限流通常会趋于保守,不能充分发挥异构系统的全部性能;也无法智能地应对由于网络、下游服务变化而导致的容量下降等问题,系统仍然存在宕机风险。 因此,我们需要具备自适应能力的负载均衡算法,来更好的进行流量分配调度以及稳定性保障,追求极致性能,挑战大促等场景下的流量洪峰。 结合中间件性能挑战赛的赛题 我们结合「第五届中间件性能挑战赛」中的初赛场景,来一起探讨一下设计和实现一个自适应的负载均衡的基本思路。 本次挑战赛的场景由施压程序(阿里云性能测试PTS)、服务调用方(Consumer)和三个规格不同的服务提供方(Provider) 组成。在评测过程中,每个程序都部署在不同的物理机上,以避免因 CPU、网络资源的竞争,导致评测程序抖动,影响最终评测成绩。 Becnhmarker 负责请求 Consumer, Consumer 收到请求后,从三台物理规格不同、服务响应时间和最大并发都不同的 Provider 中选择一个进行调用并返回结果。选择哪一个 Provider 进行调用的流程就是本次挑战赛需要实现的负载均衡算法。 为了简化环境部署和提升性能,本次挑战赛没有使用服务注册和发现机制。三个 Provider 对应的 URL 都已经被直接配置在了 Consumer 中,选手在开发和测试时可直接通过 Provider-small 等 hostname 访问相应的 Provider。 赛题分析题目描述很简单,不考虑 Consumer 直接拒绝的情况下,场景可以简化为 3 选 1 的问题,但如何进行这个决策则是本次挑战赛考察的难点和重点。 官方题目组提供了Random算法作为默认实现:从 3 个 Provider 中随机取任意一个。对于单 dispatcher (在本次赛题中是 Consumer) 同构系统的场景,Random可以达到渐近负载均衡, 每个 Provider 接收到的总请求数接近。但是对于多 dispatcher 或异构系统而言,Random 算法由于缺少全局状态,无法保证全局随机,极端条件下,多个 dispatcher 可能将请求同时分配到一台 Provider 上,导致系统存在服务过载和宕机的风险;异构系统中,不同 Provider 服务容量实际是不同的,即使每个 Provider 请求速率相同也会产生空闲、稳定、过载等不同的服务状态,无法实现最优流量分配,更不能做到响应时间最小。显而易见,Random并不是符合赛题要求的自适应算法。 ...

June 14, 2019 · 1 min · jiezi

关于分布式集群负载均衡微服务的关系说明

https://www.cnblogs.com/wmqia...

June 13, 2019 · 1 min · jiezi

如何运用PHPREDIS解决负载均衡后的session共享问题

一、为什么要使用Session共享?稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名、密码在整个网站的各个模块中都是可以登录使用的。各个服务器共享用户数据是比较容易实现的,只需要在后端放个数据库服务器,各个服务器通过统一接口对用户数据进行访问即可。但还存在一个问题,就是用户在这个服务器登录之后,进入另一个服务器的别的模块时,仍然需要重新登录,这就是一次登录,全部通行的问题,映射到技术上,其实就是各个服务器之间如何实现共享 SESSION 数据的问题。 二、了解session工作原理在解决问题之前,先来了解一下 PHP SESSION 的工作原理。在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ID(此 ID 可通过函数 session_id() 获取/设置)。SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。这里我们主要以 COOKIE 方式进行说明,因为应用比较广泛。 服务端通过客户端传递的session_id区分用户,用来标记用户的登录状态。 用户再次发送请求的时候,把服务端返回的session_id通过cookie[或者URL传参]的形式传递到服务端,这样服务端就可以区分出来具体操作的用户。 三、如何解决负载均衡之后的session共享问题?1.不使用session,换作cookie 把session改成cookie,就能避开session的一些弊端。【安全性较低】 2.数据库记录下session信息 使用数据库记录session信息,session的使用频率比较高,如果存在数据库中,频繁的读取会对数据库产生较大的压力,网站性能瓶颈一般都存在数据库. 3.负载均衡的时候使用ip_hash算法进行分发 使用ip_hash可能会导致某一台服务器负载较大。如果某段时间内服务器进入了很多固定IP代理的请求 [翻墙,代理] ,如果代理IP的负载过高就会导致ip_hash对应的服务器负载压力过大,这样ip_hash就失去了负载均衡的作用了。 4.对session文件进行同步 使用同步工具对session文件进行同步,保证负载服务器的session文件都是一致的,这种做法虽然可以解决session共享的问题,同样的内容会存在多个服务器上,而且部分服务器存在的session文件可能从开始到结束完全没有使用到,浪费了服务器的资源。 【rsync,inotify-tools等】 5.使用memcache或者redis保存session信息 [建议] 相比文件取信息,从内存取数据速度要快很多,而且在多个服务器需要共用 session 时会比较方便,将这些服务器都配置成使用同一组 memcached 服务器就可以,减少了额外的工作量。其缺点是 session 数据都保存在 memory 中,一旦宕机,数据将会丢失。但对 session 数据来说并不是严重的问题。 ...

June 3, 2019 · 1 min · jiezi

分布式文件存储-FastDFS

一、概念简述FastDFS 是由淘宝开发平台部资深架构师余庆开发,是一个轻量级、高性能的开源分布式文件系统( Distributed File System ),用纯 C 语言开发,包括文件存储、文件同步、文件访问(上传、下载)、存取负载均衡、在线扩容、相同内容只存储一份等功能,适合有大容量存储需求的应用或系统。 它对文件进行管理,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标。解决了大容量存储和负载均衡的问题。特别适合以中小文件( 建议范围:4KB < file_size <500MB )为载体的在线服务,如相册网站、视频网站等等具有显著的效果。同类的分布式文件系统有谷歌的 GFS、HDFS(Hadoop)、TFS(淘宝)等。 FastDFS架构FastDFS 服务端有三个角色:客户端(client),跟踪器(tracker)和存储器(storage)构成。基本架构如下图所示 跟踪器(tracker)Tracker 是 FastDFS 的 协调者,负责管理所有的 storage server 和 group,每个 storage 在启动后会连接 Tracker,告知自己所属的 group 等信息,并保持周期性的心跳,tracker 根据 storage 的心跳信息,建立 group ==> [storage serverlist] 的映射表。 Tracker 需要管理的元信息很少,会全部存储在内存中;另外 tracker 上的元信息都是由 storage 汇报的信息生成的,本身不需要持久化任何数据,这样使得 tracker 非常容易扩展,直接增加 tracker 机器即可扩展为 tracker cluster 来服务,cluster 里每个 tracker 之间是完全对等的,所有的 tracker 都接受 stroage 的心跳信息,生成元数据信息来提供读写服务。 跟踪器在访问上起负载均衡的作用。可以随时增加或下线而不会影响线上服务。存储节点(storage)存储系统(Storage server)由以组 group(或卷 volume)为单位组成。一个 group 内包含多台 storage 机器,数据互为备份,存储空间以 group 内容量最小的 storage为准。 ...

May 30, 2019 · 1 min · jiezi

干货-三分钟带你挑选专属负载均衡

对于云厂商来说,在提高系统可用性、扩展系统服务能力方面,负载均衡可谓是重要一环。 负载均衡可将用户的业务请求按照一定策略自主分发给多台后端服务器处理,从而调整资源利用情况,消除由于单台后端服务器故障对系统的影响。 LB?ALB?NLB?DNLB?傻傻分不清楚 负载均衡的使用场景众多,无论你是传统行业,还是互联网行业,不知不觉中,你就会接触到负载均衡。如果还分不清其中的区别,不知道如何做选择,那就不是坑队友,是坑自己了!|剧透一下我们今天会重点比较ALBNLBDNLB欢迎围观! ···· 负载均衡,Load Balancer,简称为LB。京东云负载均衡产品包括应用负载均衡(Application Load Balancer,简称ALB)、网络负载均衡(Network Load Balancer,简称NLB)和分布式网络负载均衡(Distributed Network Load Balancer,简称DNLB)。 三款负载均衡在组网中的部署位置相同,且都可以为内网和外网业务提供负载均衡服务,只是服务的业务类型不同。下面,我们从多角度介绍三者的不同,为大家提供选择的依据。 No.1 应用场景 ALB☟工作在proxy模式的七层负载均衡,主要面向基于HTTP和HTTPS的WEB应用程序,其在请求级别运行,可以为应用层业务提供更加出色的服务。 NLB☟有状态四层负载均衡,专注于提供四层有状态负载均衡服务,主要面向基于TCP的四层有状态业务,可提供高性能、低延时、会话保持等四层应用服务能力。 DNLB☟基于SDN技术的无状态四层负载均衡,提供软件定义的全可用区分布式负载均衡服务。相比于兼具会话保持功能的ALB和NLB,DNLB将负载均衡功能与会话保持解耦,天然具有转发性能无瓶颈、全可用区高可用、低时延、自动弹缩和长期免费的优点,满足客户三高一低(高性能、高可用、高弹性和低时延)服务场景需求。 No.2 产品特性 相比较于ALB支持丰富的七层特性,NLB支持保持业务状态信息,DNLB作为一款轻量级负载均衡产品,提供纯粹的负载均衡服务,满足用户的高性能需求,简化用户配置。 No.3 资源占用情况 ALB和NLB实例有具体的实体,需要占用京东云的计算资源及用户私网IP地址,规划网络方案时需考虑ALB和NLB实例的弹性扩展情况,预留足够的私网IP地址。 DNLB实例是基于京东云SDN技术架构的逻辑实体,不占用任何计算资源,实例IP地址采用京东云预留的IP地址,不占用用户私网IP地址,且DNLB天然具有转发性能无瓶颈的特性,无需弹性扩展。 No.4 产品定价 目前三款负载均衡均免费,后续将采取不同的收费策略。三款相比????????????ALB>????????NLB>????DNLB DNLB作为一款为广大京东云用户提供实惠的重量级产品,长期免费哦!????????????作为传说中的不占用计算资源、全可用区高可用、免费、转发性能无瓶颈的负载均衡产品DNLB已经完全开放!邀请大家一起公测!!! Summary 最后,本着满足各位大佬业务需求、提供更好的性能体验、花费更低的原则,敲黑板划重点: 如果您的业务需要负载均衡产品提供七层负载均衡服务,请选择ALB;如果您的业务需要负载均衡产品提供四层有状态负载均衡服务,请选择NLB。相比于ALB,NLB可以提供更高的性能体验、更低的费用;如果您的业务需要负载均衡产品提供四层无状态负载均衡服务,请选择DNLB。相比于ALB和NLB,DNLB可以提供无瓶颈的性能体验、更简单的配置,且不收取任何费用,是成本相对敏感用户的不二选择。 最后的最后,分享两个负载均衡产品选择不合适的小case,仅供参考,不建议模仿!!! ????????????用户场景需提供基于TCP的四层负载均衡服务,且后端服务端需感知或者统计真实源端信息,用户选择使用ALB。 分析:ALB也支持提供四层负载均衡服务并获取客户端源IP,但配置复杂、费用相对较高。ALB工作在proxy模式,通过proxy protocol携带客户端源IP,后端服务器需额外配置支持解析proxy protocol相关字段。建议根据业务场景选择NLB或DNLB,二者都支持直接透传客户端源IP,无需后端服务器额外配置,且费用较低或免费。 ????????????目前三款负载均衡产品都是免费的,所以无论什么场景,都选择功能最丰富、最成熟的ALB。 分析:NLB和DNLB虽然是京东云陆续推出的新产品,但是这两款产品早已在京东云多个产品服务(例如:CFS、RDS、MongoDB、Redis等)的高可用架构中成熟应用,所以稳定性及可用性无需顾虑。况且,ALB和NLB即将收费,建议大家根据需求选择最合适的负载均衡产品。 点击京东云快来试试看,哪一款负载均衡最适合你吧!

May 30, 2019 · 1 min · jiezi

虎牙在全球-DNS-秒级生效上的实践

本文整理自虎牙中间件团队在 Nacos Meetup 的现场分享,阿里巴巴中间件受权发布。 这次分享的是全球 DNS 秒级生效在虎牙的实践,以及由此产生的一些思考,整体上,分为以下5各部分: 背景介绍;方案设计和对比;高可用;具体实践和落地;规划;背景介绍虎牙用到的基础技术很多,DNS 是其中比较重要的一个环节。 DNS 的解析过程很关键,例如上图中的 DNS 解析器通过一个定位解析追踪到我们的 DNS,再到本地域名服务器迭代解析,经过根域再到.com名,最后到huya.com的根域名,获取最终的解析结果。 在这个过程中, DNS解析是天然的分布式架构,每一层都会有缓存,上一层出现问题挂掉,下一层都会有缓存进行容灾。另外,整个 DNS 协议支持面广,包括手机和 PC,我们用的编程框架里也有 DNS 解析器,服务器也会配 DNS 解析引擎,因此,DNS 在虎牙的基础设施中是很重要的部分。 虎牙的 DNS 的应用现状虎牙当前主要是依赖于公共的 DNS,相信在座的小伙伴们或多或少都会遇到过下面这些问题: 依赖公共 localDNS,解析不稳定,延迟大。记录变更生效时间长,无法及时屏蔽线路和节点异常对业务的影响。例如,权威 DNS 全球各节点数据同步时间不可控,全局生效时间超过10分钟;localDNS 缓存过期时间不可控,部分 localDNS 不遵循TTL时间,缓存时间超过48小时。内部 DNS 功能缺失,无法解决内部服务调用面临挑战。例如,时延大、解析不准、支持多种调度策略。无法满足国外业务的快速发展,虽然一些海外云厂商提供了基于 DNS 的快速扩容方案,以及基于 DNS 的数据库切换方案。方案设计和对比基于以上的问题,我们开始重新规划 DNS 的设计。 名字服务架构 整个规划会分三个方面,核心是我们做了「名字服务」的中心点,基于此,可以满足我们的需求。 一方面通过 Nacos Sync,将现有多个注册中心的服务, 同步到「名字服务」中, 通过 DNS 实现不同框架之间的 Rest 服务方式的调用, 实现例如 Eureka,Consul,Taf等框架之间的服务调用。 另一方面,在全球负载均衡的场景下,由于虎牙是以音视频业务为主,而音视频业务对节点的延迟是非常敏感的,所以我们希望一旦出现节点延迟的情况,能立马做切换。 第三个是传统 DNS 的场景, 可以满足容器和物理机的 DNS 需求, 提供本机 Agent 和集群两种方案, 通过缓存和 prefect 大大提高 DNS 解析的可用性和加快生效时间。 ...

May 27, 2019 · 3 min · jiezi

Python爬虫入门教程-7100-蜂鸟网图片爬取之二

1. 蜂鸟网图片-简介今天玩点新鲜的,使用一个新库 aiohttp ,利用它提高咱爬虫的爬取速度。 安装模块常规套路 pip install aiohttp 运行之后等待,安装完毕,想要深造,那么官方文档必备 :https://aiohttp.readthedocs.io/en/stable/ 接下来就可以开始写代码了。 我们要爬取的页面,这一次选取的是 http://bbs.fengniao.com/forum/forum_101_1_lastpost.html打开页面,我们很容易就获取到了页码 好久没有这么方便的看到页码了。 尝试用 aiohttp 访问这个页面吧,模块的引入,没有什么特殊的,采用 import 即可如果我们需要 使用Asyncio + Aiohttp异步IO 编写爬虫,那么需要注意,你需要异步的方法前面加上async 接下来,先尝试去获取一下上面那个地址的网页源码。 代码中,先声明一个fetch_img_url的函数,同时携带一个参数,这个参数也可以直接写死。 with 上下文不在提示,自行搜索相关资料即可 (`・・´) aiohttp.ClientSession() as session: 创建一个session对象,然后用该session对象去打开网页。session可以进行多项操作,比如post, get, put等 代码中 await response.text() 等待网页数据返回 asyncio.get_event_loop创建线程,run_until_complete方法负责安排执行 tasks中的任务。tasks可以为单独的函数,也可以是列表。 import aiohttp import asyncio async def fetch_img_url(num): url = f'http://bbs.fengniao.com/forum/forum_101_{num}_lastpost.html' # 字符串拼接 # 或者直接写成 url = 'http://bbs.fengniao.com/forum/forum_101_1_lastpost.html' print(url) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6726.400 QQBrowser/10.2.2265.400', } async with aiohttp.ClientSession() as session: # 获取轮播图地址 async with session.get(url,headers=headers) as response: try: html = await response.text() # 获取到网页源码 print(html) except Exception as e: print("基本错误") print(e)# 这部分你可以直接临摹loop = asyncio.get_event_loop()tasks = asyncio.ensure_future(fetch_img_url(1))results = loop.run_until_complete(tasks)上面代码最后一部分也可以写成 ...

May 27, 2019 · 2 min · jiezi

IP应用加速技术详解如何提升动静混合站点的访问速率

全站加速(DCDN)-IPA是阿里云自主研发四层加速产品,它基于TCP/UDP的私有协议提供加速服务,包括解决跨运营商网络不稳定、单线源站、突发流量、网络拥塞等诸多因素导致的延迟高、服务不稳定的问题,提升传输性能和用户体验。 IP应用加速架构如下图所示,首先,全球用户就近接入边缘节点,通过阿里云的智能调度系统接入二级节点,中间采用传输协议优化和路由优化,选取最快、最优的路径。最后,二级节点到源站则选择相同运营商进行回源。 在架构中也会使用CDN一些比较成熟的组件,包括调度系统、管控平台、数据监控、IP地址库及日志采集等服务。 同时,在实际加速过程中,IP应用加速通过“就近接入、传输优化、智能路由”三大技术能力提供更极致的加速体验。其中就近接入与CDN加速原理一致,传统静态CDN的就近接入与缓存是实现加速的主要手段,将用户的访问就近解析到离用户最近的CDN节点,利用访问CDN节点上的缓存内容来实现加速的效果。但是对于动态加速来说,就近接入可以理解为一个就近上车的动作。传输优化在CDN场景中是一个基础的手段,包括单边加速和双边加速,传输优化可以为用户提供更稳定、高效的传输。而智能理由则是动态加速场景中是核心的技术。下面依次对这三个技术点进行解读。 就近接入下图是一个典型的CDN通过域名方式就近解析、就近接入的方式。在现有的CDN加速系统中,如果用户在CDN上注册一个域名,CDN会返回给用户一个CNAME地址,用户把CNAME地址加入到自己的DNS server之上,通过这样的方式,用户在请求的时候,就会递归查询到CDN的GLB上,GLB会根据用户的localDNS、IP、地理位置、运营商等信息,选择一个就近的CDN节点提供接入。这是通过DNS方式就近接入的典型例子,目前IP应用加速已经实现了标准了DNS-CNAME、HTTPS、HTTPDNS的接入。 在接入的过程中,就近接入想达到足够好的效果,IP地址库以及资源的优势是非常重要的。如果IP地址库不准,LocalDNS会被识别到错误的地方或者运营商,那访问就可能跨省跨运营商,达不到加速的目的。如果没有足够的资源覆盖,也很难做到稳定的调度,这样就近接入可能也难以达到理想效果。 传输优化TCP协议是典型的基于丢包或异常延迟来识别网络拥塞的传输协议,它的特征包括拥塞控制算法,如果发生了丢包,吞吐率会急剧下降,系统会认为它是拥塞,发送窗口减小(半),这是标准的TCP的行为。所以在客户端到源站之间一旦发生丢包,吞吐率会极大下降 如果在客户端和源站之间加入Proxy,问题就会缩小到局部,用户和服务器不会感受到发送数据的显著变化。同时,在长RTT的网络环境中,Proxy会使长链路分割成短链路,每个数据包的确认也会变得更短,拥塞窗口的恢复会变得更快,对于提升整体吞吐率也有帮助。 除了TCP Proxy外,在内部协议优化上也进行大量实践。比如通过改进拥塞控制,区分随机丢包还是拥塞丢包来使用更优的重传策略,来提升传输效率;使用多种传输协议,私有协议、多路传输技术以及冗余的传输手段,达到更高的传输速率和可靠性;另外内部传输也采用了更好的异常感知的技术,能够快速加速网络传输过程中的异常并在网络层面切换,实现对上层业务的透明。 下图是在持续丢包的场景下的测试数据,阿里云自研的TCP传输协议比现在最新的bbr算法效果更优。 智能路由如果真的链路上出现了拥塞,这时就需要采用智能路由技术。它的本质是有效预测网络中的拥塞,并且实时切换。另外备份的策略有次优的路线选择,以此保障对上层业务的影响最低。 实际上智能路由算法中,需要考虑的问题非常多。比如要考虑链路的质量、节点的复杂、相关服务器的能力、节点水位、负载均衡、成本等问题,比如在转发的过程中,需要根据现实情况来判断用DGP或多线路进行接入,来满足同运营商回源。在负载均衡方面,还要考虑地域、运营商、源站的优先级等策略。在实际网络质量的评估过程中,我们会用到多层次、多维度实施网络探测,避免源站探测风暴。同时,也要考虑在突发流量汇聚情况下如何实现过载保护。在众多约束条件下,选取最短最优路径有相当大的技术挑战。 IP应用加速-全栈加速从客户端到源站,IP应用加速目前实现了七层、四层、三层各层级的加速,其中七层加速是传统CDN、DCDN的加速产品,针对http(s)的加速,对于一些私有协议,可以使用四层加速。对于IP协议,可以使用IP隧道加速。这其中每一层都是独立组网,实现独立转发。如果本层某些特殊业务,本层不能很好支持,可以考虑在实施过程中考虑把相关加速服务通过转交给下层,利用下层技术能力实现更好的技术服务。 比如,在一个大文件上传的场景中,客户端到服务器端文件上传假设使用http,如果网络有波动或异常,可能会导致成功率较低。而在过程中,如果利用下沉到四层加速,利用多径传输,即可有效降低网络异常对于传输成功率的影响。 IP应用加速的功能访问控制:通过支持白名单、黑名单,对用户到边缘节点之间提供访问控制能力。透明切换:当数据包转发回源站的过程中,可能会出现转发不成功、网络异常、服务器异常等情况,透明切换可以实现内部链路切换无感知。分区回源:不同边缘服务器可以根据源站域名的分区解析的结果,回到最优的源站,适用于多源站情况下的使用。负载均衡:通过适配源站和路径内部的负载均衡,处理汇聚点的相关问题。 除了以上功能,IP应用加速也会陆续上线UDP加速、升级安全防护策略同时提供SDK接入方式。详细的产品信息,可以登录阿里云官网全站加速产品详情页以及IP应用加速文档进行了解。 相关阅读IP应用加速产品发布会直播回顾:https://yq.aliyun.com/live/1074IP应用加速详情:https://promotion.aliyun.com/ntms/dcdnipa.html参与聚能聊话题,赢取礼品:https://yq.aliyun.com/roundtable/497735阿里云全站加速DCDN全面支持WebSocket协议https://yq.aliyun.com/articles/686839 本文作者:樰篱阅读原文 本文为云栖社区原创内容,未经允许不得转载。

May 23, 2019 · 1 min · jiezi

微服务注册中心注册表与hashcode实现golang版

背景基于负载均衡的服务调用基于负载均衡的服务相互调用指的是通过基于Lvs、Haproxy、Nginx等负载均衡软件来构建一个负载均衡服务,所有的服务调用都通过负载均衡器 从负载均衡的这种模式下其实有两个主要的问题: 一是中心化,整个系统都基于负载均衡器,负载均衡就相当于整个业务的中心,虽然我们可以通过一些高可用手段来保证,但其实内部流量通常是巨大的,很容易出现性能瓶颈二是增加了一次TCP交互 当然也有很多好处,比如可以做一些负载均衡、长链接维护、分布式跟踪等,这不是本文重点 基于注册中心的服务调用所有的服务都启动后都通过注册中心来注册自己,同时把注册中心里面的服务信息拉回本地,后续调用,就直接检查本地的服务和节点信息来进行服务节点的调用 注册中心中的注册表每个服务节点都会来注册中心进行服务注册,那数据如何在服务端进行保存呢,其实就是注册表,其实等同于windows 里面的注册表,每个服务都来注册,把自己的信息上报上来,然后注册中心吧注册表,返回给client端,那服务之间就知道要调用服务的节点啦 注册中心事件队列微服务注册注册中心通常会大量的服务注册, 那不能每次客户端来请求的时候,服务端都返回全量的数据,在数据传输的设计中,通常会有一种增量同步,其实在注册中心中也类似注册中心通过将最近的服务变更事件保存在一个事件队列中,后续每次客户端拉取只返回增量数据,这样服务端的忘了压力就会小很多 注册中心hashcode增量数据有一个问题就是,如果客户端错过啦某些事件,比如事件队列满了,则客户端与注册中心的注册表就会不一致, 所以eureka里面引入了一个hashcode的概念,通过比对hashcode是否相同, 如果不同则客户端需要重新全量拉取 代码实现系统架构系统整体上分为两个端:客户端(Client)和注册中心(Server)Server: 提供服务注册和获取注册表的接口, 同时本地把保存服务和节点的对应信息,变更事件写入eventQueueClient: 调用server接口进行服务注册, 同时调用注册表拉取接口进行注册表拉取,保存懂啊LocalRegistry 应用与节点信息Server端的服务注册表里面的服务和节点的信息,我通过Application和lease来维护Application: 代表一个应用,里面会包含服务对应的节点信息Lease: 维护一个节点的信息,比如心跳信息 服务端注册表注册表结构体服务端注册表结构体Registry主要包含三部分信息: lock(读写锁)、apps(应用对应信息)、eventQueue(事件队列)Lock: 注册中心是典型的读多写少的应用,server端注册表可能同时提供给N个服务进行读取,所以这里采用读写锁apps: 保存应用对应的信息, 其实后面写完发现,没必要使用,只使用基础的map就可以搞定eventQueue: 每次注册表变更都写入事件到里面 // Registry 注册表type Registry struct { lock sync.RWMutex apps sync.Map duration time.Duration eventQueue *EventQueue}注册表服务注册注册流程主要分为下面几部分: 从注册表获取对应的应用Application调用Application的add接口添加节点为节点创建一个Lease保存节点信息到Application.Node里将事件写入到eventQueue// Registr 注册服务func (r *Registry) Registr(name, node string) bool { r.lock.Lock() defer r.lock.Unlock() app := r.getApp(name) if app == nil { app = NewApplication(name) r.apps.Store(name, app) } if lease, ok := app.add(node, r.duration); ok { r.eventQueue.Push(&Event{lease: lease, action: ADD}) return true } return false}注册表拉取全量拉取通过all接口拉取全量的返回的是服务对应的节点切片增量拉取通过details接口返回增量的变更事件和服务端注册表的hashcode ...

May 23, 2019 · 2 min · jiezi

13使用Docker-Compose-实现nginx负载均衡

以Docker的网络管理,容器的IP设置为基础知识实现Nginx负载均衡查看所有docker网络 docker network ls/*NETWORK ID NAME DRIVER SCOPEb832b168ca9a bridge bridge local373be82d3a6a composetest_default bridge locala360425082c4 host host local154f600f0e90 none null local*/// composetest_default 是上一篇介绍Compose时,docker-compose.yml文件所在的目录名,// 所以,用docker-compose创建的容器会默认创建一个以目录名为网络名的网络,并且是dridge(桥接)类型指定容器IP地址 官网文档地址:https://docs.docker.com/compo... 继续编写上一篇《12.使用Docker Compose容器编排工具》文章中的docker-compose.yml version: "3"services: web1: container_name: web1 image: "centos:httpd" ports: - "8080:80" privileged: true volumes: - "/app/www/web1/:/var/www/html/" command: ['/usr/sbin/init'] networks: nginx-lsb: ipv4_address: 192.169.0.3 web2: container_name: web2 image: "centos:httpd" ports: - "8081:80" privileged: true volumes: - "/app/www/web2/:/var/www/html/" command: ['/usr/sbin/init'] networks: nginx-lsb: ipv4_address: 192.169.0.2networks: nginx-lsb: driver: bridge ipam: config: - subnet: 192.169.0.0/16使用docker-compose启动容器 ...

May 22, 2019 · 2 min · jiezi

CENTOS7-高性能Linux集群-通过yum进行-haproxy配置-安装-使用HAProxy配置文件详解

照片参考:https://blog.csdn.net/weixin_... Haproxy配置 永久修改名字:便于区分虚拟机!Haproxy:hostnamectl set-hostname haproxyWeb1: hostnamectl set-hostname WEB1Web2:hostnamectl set-hostname WEB2Test:hostnamectl set-hostname test 1、haproxy安装并将过程截图yum list |grep haproxy:查看haproxyl列表,查询可能会花费较长时间! yum -y install haproxy:安装: [点击并拖拽以移动] 2、填写IP分配表:服务器角色 IP地址 Haproxy 192.168.1*8.128 Web1 192.168.1*8.137 Web2 192.168.1*8.138test 192.168.1*8.135 3、简答:说明haproxy配置文件中global、defaults、frontend、backend参数的作用?Global: 用于设置全局配置参数,属于进程级的配置,通常用操作系统配置相关 defaults: 默认参数的配置部分。在些部分设置的参数,默认会自动引用到下面的frontend, backend和listen部分 frontend: 用于设置接收用户请求的前端虚拟节点。frontend可以根据ACL规则直接指定要使用的后端backend backend: 用于设置集群后端服务集群的配置,也就是用来添加一组真实服务器,以处理前端用户的请求4、Haproxy配置并截图编辑/etc/haproxy/haproxy.cfg配置文件,配置内容如下:位置:/etc/haproxy/haproxy.cfgvim /etc/haproxy/haproxy.cfg global log 127.0.0.1 local2 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode http log global option httplog option dontlognull ...

May 22, 2019 · 2 min · jiezi

项目笔记1通过在线制图工具绘制阿里云部署图

title: 通过在线制图工具绘制阿里云部署图最近做一个项目是关于采集指纹的系统,先给大家简单介绍一下项目的主要功能: 该项目主要是做一个采集婴幼儿的手掌指纹和掌纹的客户端,并且通过服务端接口保存手掌指纹到阿里云oss存储中。同时后台提供管理功能,对采集人员,系统角色权限管理,同时提供婴幼儿的手指指纹图片的查看和分析功能。 系统分为三个子系统: 指纹采集客户端程序(client)指纹采集接口应用服务(通过springboot 框架开发的Restful Api方式 (client restful api)指纹采集后台管理应用前端页面 (Ant Design Vue Admin )指纹采集后台应用接口服务器 (通过Spring Boot框架开发,用到Redis, JWT,mybatis等技术)由于客户要求使用阿里云服务作为应用部署的基本设施,按照客户的要求绘制阿里云系统部署架构图。 最后讨论后的部署建议方案: 为了保证数据传输的安全建议采用https SSL证书方式。用户请求通过负载均衡SLB分发到阿里云的ESC上。后端web服务器采用多台ECS(至少2台)负载用户请求。数据库采用阿里云的Mysql版本,通过设置好数据库的备份和安全OSS存在被采集人指纹信息。使用redis作为数据缓存中心。短信接口建议采用阿里云平台短信网关服务。在此使用了一个网上在线制图网站Freedgo Design 其访问地址为: https://www.freedgo.com. freedgo Design 是一个多种类型图表的在线绘制软件,让您创建 阿里云架构图 腾讯云架构图 Oracle云架构图 AWS系统部署图 软件架构图, UML,BPMN,ERD,流程图,UX设计图,ANT DESIGN,思维导图,图表。可以做到注册用户免费使用。 具体绘制步骤如下:打开https://www.freedgo.com,先点...,Freedgo Design提供邮箱、微信、QQ、微博等多种注册方式。注册成功后,点击 开始制作 按钮,然后就进入制图工具页面进行绘制。选择菜单文件-> 从类型中新建 -> 云架构 -> 阿里云左侧图标库中选择所需要的web服务器,数据库,redis等等图标安装业务逻辑绘制在线架构图。最终的绘制效果如下图:

May 16, 2019 · 1 min · jiezi

WAFSLB负载不均衡案例分享

问题演变过程时间点1:高防+WAF+SLB+2台ECS时间点2:高防+WAF+SLB+4台ECS 问题描述在时间点1时,没有发现明显的负载不均衡的情况。在时间点2时,出现大部分请求都打到了其中一台ECS上。需要定位问题原因 问题梳理问题链路是SLB后端的ECS出现负载不均衡的请求,那么直接影响这个转发算法的,是WAF以及SLB。那么和高防没有关系了。 配置情况 SLB:TCP监听,WRR转发算法,开启会话保持WAF:无特殊配置,域名直接回源负载均衡IP问题点1:轮询算法+会话保持措施:尝试修改轮询算法为WLC,会话保持时间调短。然而这个优化措施效果并不明显,由于开启了会话保持,那原有负载不均衡的情况下,调整WRR算法到WLC的算法,没有实现预期的WLC。 但是从另外一个角度来说,如果源IP非常分散的场景下,即使有会话保持,理论上还是应该在经过一个较长的时间段之后,依然能够到达均衡。这里由于是使用WAF的回源地址进行访问,所以对负载均衡来说,客户端的公网IP地址是固定的,一直是固定的几个;从而调整WLC+会话保持的调整收效甚微。 问题点2:会话保持模板刷新问题措施:尝试关闭会话保持。 稍有成效:关闭会话保持后,经过一段时间的通信,4台ECS初步的开始均衡,但是到了一个固定值之后;没有继续均衡,一直保持着1:2的状态。 这里有2个知识点: 1、WLC算法的计数开始是从调整为这个算法的时间点开始的;那么如果历史开始就出现不均衡,那么开启后还是会不均衡的。2、由于WAF的回源地址与SLB的通信一直在,没有断过所以历史的会话保持的效果依然存在,已经会话保持的IP,依然会发给对应负载均衡的RS,导致不均衡。 推荐的解法为:使用负载均衡的权重功能,将连接数多的机器的权重调低,待4台机器的连接数基本均衡后,将RS的权重都调整为一致。 本文作者:枫凡阅读原文 本文为云栖社区原创内容,未经允许不得转载。

May 14, 2019 · 1 min · jiezi

Hive集群合并之应用端的负载均衡算法

0.背景有这么一个场景,我们有两个Hive集群,Hive集群1(后面成为1号集群)是一直专享于数据计算平台的,而Hive集群2(后面成为2号集群)是用于其他团队使用的,比如特征,广告等。而由此存在两个主要问题:a) 两个Hive集群共享了同一份MetaData,导致经常会出现在HUE(建立与2号集群上)上建表成功后,但是在计算平台上却无法查询到新建表信息;b) 让运维同学们同时维护两套集群,管理和资源分配调整起来的确是麻烦很多,毕竟也不利于资源的弹性分配。那么鉴于此,经过讨论,需要做这么一样工作:两个集群合二为一,由1号集群合并到2号集群上来。 1.集群合并前的思考与分析但是,集群合并是不可能一下子全部合并,需要逐步迁移合并(比如每次20个结点)到2号集群。但是这样存在一个问题,计算平台每天使用的计算资源是差不多固定的,而在迁移过程中,1号集群的资源在逐渐减少,显然是不满足计算需求的,所以我们也需要由得到迁移资源的2号集群分担一些压力。那么重点来了,这就需要我们任务调度器合理的分配任务到1号集群以及2号集群的某个队列。其实,所谓的任务分配也就是一种负载均衡算法,即任务来了,通过负载均衡算法调度到哪个集群去执行,但是使用哪种负载均衡算法就需要好好探究一下。 1.1负载均衡算法的选择Q:常用的负载均衡算法有哪些呢?A:随机算法,轮询,hash算法,加权随机算法,加权轮询算法,一致性hash算法。 随机算法该算法通过产生随机数的方式进行负载,可能会导致任务倾斜,比如大量任务调度到了1好集群,显然不可取,pass。 轮询该算法是通过一个接一个循环往复的方式进行调度,会保证任务分配很均衡,但是我们的1号集群资源是在不断减少的,2号集群资源是在不断增加的,如果均衡分配计算任务,显然也是不合理的,pass。 hash算法该算法是基于当前结点的ip的hashCode值来进行调度,那么只要结点ip不变,那么hashCode值就不会变,所有的任务岂不是都提交到一个结点了吗?不合理,pass。 加权随机算法同随机算法,只不过是对每个结点增加了权重,但是因为是随机调度,不可控的,直接pass。 加权轮询算法上面说到,轮询算法可以保证任务分配很均衡,但是无法保证随集群资源的调整进行任务分配的动态调整。此时,如果我们可以依次根据集群迁移情况,设置1号集群与2号集群的任务比重为:7:5 -> 3:2 -> 2:3 -> 完整切换。可行。 一致性hash算法该算法较为复杂,鉴于我们是为了进行集群合并以及保证任务尽量根据集群资源的调整进行合理调度,无需设计太复杂的算法进行处理,故也pass。 2.负载均衡算法的落地实现虽然我们最终方法选定为加权轮询算法,但是它起源于轮询算法,那么我们就从轮询算法说起。 首选,我们会有Hive集群对应的HS2的ip地址列表,然后我们通过某种算法(这里指的就是负载均衡算法),获取其中一个HS2的ip地址进行任务提交(这就是任务调度)。 2.1轮询算法的实现我们先定义一个算法抽象接口,它只有一个select方法。 import java.util.List;/** * @author buildupchao * @date: 2019/5/12 21:51 * @since JDK 1.8 */public interface ClusterStrategy { /** * 负载均衡算法接口 * @param ipList ip地址列表 * @return 通过负载均衡算法选中的ip地址 */ String select(List<String> ipList);}轮询算法实现: import org.apache.commons.lang3.StringUtils;import java.util.Arrays;import java.util.List;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.atomic.AtomicInteger;/** * @author buildupchao * @date: 2019/5/12 21:57 * @since JDK 1.8 */public class PollingClusterStrategyImpl implements ClusterStrategy { private AtomicInteger counter = new AtomicInteger(0); @Override public String select(List<String> ipList) { String selectedIp = null; try { int size = ipList.size(); if (counter.get() >= size) { counter.set(0); } selectedIp = ipList.get(counter.get()); counter.incrementAndGet(); } catch (Exception ex) { ex.printStackTrace(); } if (StringUtils.isBlank(selectedIp)) { selectedIp = ipList.get(0); } return selectedIp; } public static void main(String[] args) { List<String> ipList = Arrays.asList("172.31.0.191", "172.31.0.192"); PollingClusterStrategyImpl strategy = new PollingClusterStrategyImpl(); ExecutorService executorService = Executors.newFixedThreadPool(100); for (int i = 0; i < 100; i++) { executorService.execute(() -> { System.out.println(Thread.currentThread().getName() + ":" + strategy.select(ipList)); }); } }}运行上述代码,你会发现,线程号为奇数的轮询到的是'172.31.0.191'这个ip,偶数是‘172.31.0.192’这个ip。至于打印出来的日志乱序,那是并发打印返回的ip的问题,并不是获取ip进行任务调度的问题。 ...

May 12, 2019 · 2 min · jiezi

亚马逊又挂了只是因为半价清仓活动

昨天亚马逊又挂了,为什么是又呢,因为每年亚马逊都要挂几次。 昨天是什么日子让亚马逊又挂了呢?不就是因为清仓促销吗……你的骄傲呢,高可用呢,负载均衡呢,分布式呢,三驾马车怎么一驾都不管用了呢? 不就是在国内弄个促销么……不就是被羊毛党盯上了么……至于么你,一挂就是几个小时,我反正在下午2点看看是上不去了,下午4点多还是不行。你再看看我们的淘宝,差距啊,知道自己为什么清仓了么。 行业里有句话是这么形容的:亚马逊的黑五就是淘宝的日常。 有一说一,淘宝的1111真的是全球独一档,后面的技术栈我不是阿里人就不瞎吹了,但是技术能力绝对是第一档。唯一。 这就不得不吹一波mongodb了,我最爱的数据库,没有之一。高可用、分布式样样有,样样精。关键时候就能看出一个数据库的健壮程度了。 从技术角度出发,电商领域一定会有非常多的关系型、非关系型、结构化、半结构化的数据,那么这些数据在大并发上来的时候如何有效的去做复杂场景的兼容,就要看程序员们的了。 当今互联网的宠儿,时代下应运而生的代名词:大数据,你们一定多少听过点。那请问,你们都知道有哪些大数据时代下的产物吗? Hadoop系列?Python?Dashboard?如果你能说出这些,说明你和其他人都差不多吗,能再多说点吗? 给大家说一个新名词:数据中台。什么是数据中台,是可以做数据实时汇聚的平台。这才是大数据时代下,每家机构单位都应该上的大数据产品!而小胖反观一圈市面上的所有大数据产品,别看那么多花里胡哨的包装介绍,最后脱掉衣服看本质,就是个hadoop改造啊。 那Hadoop系列最大的问题是什么知道吗?就是离线计算,我们行话叫T+1计算,在当今这个大数据时代下,数据就是价值,现在就看哪家企业能够把手里的数据变现,变现的方式有很多。但是针对不同的业务场景,去实现起来,代价可不小。 而目前市面上就有那么一款真正的数据中台产品,它可以做到数据的实时采集,而更令我惊喜的是在整个采集的过程中,他竟然支持各种关系型、非关系型数据源,多表关联,数据质量校验,数据建模,数据清洗,数据过滤等功能。 也就是说,当数据从源端落地到目标端的时候,数据已经按照既定的规则全部汇聚好了。这得省多少功夫啊。 那你肯定要杠我了,说这不就是个ETL么,你知道ETL的效率吗?我用下来kettle的效率在几百OPS反正,而同样的机器,这款产品的OPS可以达到2-3w!这还是普通配置的情况下,根据官网给出的数据,7-8w 的OPS是可以保证的。 说了那么多,也不给大家卖关子了,这个产品的名字叫:Tapdata,为了方便大家工作,贴个官网:http://www.tapdata.io 我是通过他们免费的云版了解到的,虽然是个阉割版,但是数据采集功能真的很吸引我,帮我解决了不少困难。也贴个福利给大家:https://cloud.tapdata.io Tapdata和普通大数据产品的区别是什么呢?我从他们架构师那里要来一张图,给大家分享下,你看了就明白了: 如果你对数据中台感兴趣的话,可以直接去联系这个架构师,或者他们的产品经理,这个人就是:我

April 30, 2019 · 1 min · jiezi

docker进阶nginx部署的几个重要点详解以及开发流程持续更新

部署基础知识url:协议://网站地址:端口(/)路径地址?参数eg: http://www.baidu.com:80/abc/dd/ www.baidu.com找服务器 80端口:找服务器上提供服务的应用 nginx uri:/abc/dd/ nginx的pid文件是可变化的等号两边不能空格,否则会报错tail -f 1.txt 实时监控文件变化 1.1 部署基础1.11.1 项目生命周期 传统项目生命周期 阶段 调研阶段 找方向点 设计阶段 方向点可视化 产品:产品需求文档、项目里程表 开发阶段 产品阶段功能实现 测试阶段 保证产品的阶段功能 运营阶段 项目部署 + 运营维护 关键点: 阶段间是有前后关系依赖的 阶段间项目的推进是有文档来主导 理想化的生命周期和开发模型1.2新型项目周期 软件项目: 一个产品被拆分成了非常多的子功能 团队组织:高效协作,沟通 图的理解: 四个圈串在一起 --- 子功能的完整周期 单个圈 --- 岗位的工作内容 图片: https://uploader.shimo.im/f/hRwe365QitwNgyS7.png 团队组织间的高效协作是很重要的 流程: 1 根据需求文档来梳理网站的目标架构 2 分析产品需求文档的功能,来梳理部署结点 部署节点示意图:图片: https://uploader.shimo.im/f/U... 3 根据部署结点去互联网上梳理各种解决方案(根据业务需求 4 整合所有的解决方案,-- 初版部署方案 5 根据实际的公司业务情况,对初版部署方案进行优化调整1.3 部署环境: ...

April 29, 2019 · 8 min · jiezi

一篇读懂分布式架构下的负载均衡技术分类原理算法常见方案等

1、引言关于“负载均衡”的解释,百度词条里:负载均衡,英文叫Load Balance,意思就是将请求或者数据分摊到多个操作单元上进行执行,共同完成工作任务。 负载均衡(Load Balance)建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 负载均衡有两方面的含义: 1)首先,大量的并发访问或数据流量分担到多台节点设备上分别处理,减少用户等待响应的时间; 2)其次,单个重负载的运算分担到多台节点设备上做并行处理,每个节点设备处理结束后,将结果汇总,返回给用户,系统处理能力得到大幅度提高。 简单来说就是: 1)其一是将大量的并发处理转发给后端多个节点处理,减少工作响应时间; 2)其二是将单个繁重的工作转发给后端多个节点处理,处理完再返回给负载均衡中心,再返回给用户。 目前负载均衡技术大多数是用于提高诸如在Web服务器、FTP服务器和其它关键任务服务器上的Internet服务器程序的可用性和可伸缩性。 总之,它的目的就通过调度集群,达到最佳化资源使用,最大化吞吐率,最小化响应时间,避免单点过载的问题。 内容概述:本文将从负载均衡技术的分类、技术原理、常见实现算法、常用方案等入手,为您详细讲解负载均衡技术的方方面面。这其中,四层和七层负载均衡技术最为常用,它们也是本文介绍的重点。 内容点评:对于IM或消息推送应用的开发者来说,本文所介绍的传统负载均衡技术,可能对于IM等即时通讯分布式场景来说,没有办法直接套用。原因是IM这类socket长连接场景,所处的网络通信层级比较低,而且即时通讯相关的技术实现跟具体的业务逻辑紧密相关,因而无法像HTTP短连接这样基于标准化的负载均衡方法来实现。但本文所介绍的负载均衡原理、算法和一些方案实现,仍然可以为IM或消息推送应用的开发者带来一些借鉴和参考意义,值得深 入一读。 补充:另一篇《快速理解高性能HTTP服务端的负载均衡技术原理》,也讲述了负载均衡方面的知识,有兴趣也可以阅读之。 (本文同步发布于:http://www.52im.net/thread-24...) 2、相关文章深入阅读以下文章,有助于您更好地理解本篇内容: 《网络编程懒人入门(一):快速理解网络通信协议(上篇)》 《网络编程懒人入门(二):快速理解网络通信协议(下篇)》 《网络编程懒人入门(三):快速理解TCP协议一篇就够》 《腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面》 《快速理解高性能HTTP服务端的负载均衡技术原理》 《新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践》 《通俗易懂:基于集群的移动端IM接入层负载均衡方案分享》 《IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理》 3、负载均衡分类TCP/IP协议的OSI模型: (本图高清版,请从《计算机网络通讯协议关系图(中文珍藏版)[附件下载]》一文中下载之) 根据OSI模型可将负载均衡分为: 1)二层负载均衡(一般是用虚拟mac地址方式,外部对虚拟MAC地址请求,负载均衡接收后分配后端实际的MAC地址响应); 2)三层负载均衡(一般采用虚拟IP地址方式,外部对虚拟的ip地址请求,负载均衡接收后分配后端实际的IP地址响应); 3)四层负载均衡(在三次负载均衡的基础上,用 ip+port 接收请求,再转发到对应的机器); 4)七层负载均衡(根据虚拟的url或是IP,主机名接收请求,再转向相应的处理服务器)。 这其中,最常见的是四层和七层负载均衡,也是本文接下来介绍的重点。 当客户端发起请求,会经过层层的封装,发给服务器,服务器收到请求后经过层层的解析,获取到对应的内容。 下图是一个典型的HTTP请求分层传递原理: 4、二层负载均衡二层负债均衡是基于数据链路层的负债均衡,即让负债均衡服务器和业务服务器绑定同一个虚拟IP(即VIP),客户端直接通过这个VIP进行请求。 那么如何区分相同IP下的不同机器呢?没错,通过MAC物理地址,每台机器的MAC物理地址都不一样,当负载均衡服务器接收到请求之后,通过改写HTTP报文中以太网首部的MAC地址,按照某种算法将请求转发到目标机器上,实现负载均衡。 这种方式负载方式虽然控制粒度比较粗,但是优点是负载均衡服务器的压力会比较小,负载均衡服务器只负责请求的进入,不负责请求的响应(响应是有后端业务服务器直接响应给客户端),吞吐量会比较高。 5、三层负载均衡三层负载均衡是基于网络层的负载均衡,通俗的说就是按照不同机器不同IP地址进行转发请求到不同的机器上。 这种方式虽然比二层负载多了一层,但从控制的颗粒度上看,并没有比二层负载均衡更有优势,并且,由于请求的进出都要经过负载均衡服务器,会对其造成比较大的压力,性能也比二层负载均衡要差。 6、四层负载均衡四层的负载均衡就是基于IP+端口的负载均衡:在三层负载均衡的基础上,通过发布三层的IP地址(VIP),然后加四层的端口号,来决定哪些流量需要做负载均衡,对需要处理的流量进行NAT处理,转发至后台服务器,并记录下这个TCP或者UDP的流量是由哪台服务器处理的,后续这个连接的所有流量都同样转发到同一台服务器处理。 对应的负载均衡器称为四层交换机(L4 switch),主要分析IP层及TCP/UDP层,实现四层负载均衡。 此种负载均衡器不理解应用协议(如HTTP/FTP/MySQL等等),常见例子有:LVS,F5。 7、七层负载均衡七层的负载均衡就是基于虚拟的URL或主机IP的负载均衡:在四层负载均衡的基础上(没有四层是绝对不可能有七层的),再考虑应用层的特征,比如同一个Web服务器的负载均衡,除了根据VIP加80端口辨别是否需要处理的流量,还可根据七层的URL、浏览器类别、语言来决定是否要进行负载均衡。 举个例子,如果你的Web服务器分成两组,一组是中文语言的,一组是英文语言的,那么七层负载均衡就可以当用户来访问你的域名时,自动辨别用户语言,然后选择对应的语言服务器组进行负载均衡处理。 对应的负载均衡器称为七层交换机(L7 switch),除了支持四层负载均衡以外,还有分析应用层的信息,如HTTP协议URI或Cookie信息,实现七层负载均衡。此种负载均衡器能理解应用协议,常见例子有: haproxy,MySQL Proxy。 8、四层负载均衡和七层负载均衡的区别8.1 技术原理上的区别 所谓四层负载均衡,也就是主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。 以常见的TCP为例,负载均衡设备在接收到第一个来自客户端的SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中目标IP地址进行修改(改为后端服务器IP),直接转发给该服务器。TCP的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。 所谓七层负载均衡,也称为“内容交换”,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。 以常见的TCP为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(三次握手)后,才可能接受到客户端发送的真正应用层内容的报文,然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。负载均衡和前端的客户端以及后端的服务器会分别建立TCP连接。所以从这个技术原理上来看,七层负载均衡明显的对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。 8.2 应用场景的需求 七层应用负载的好处,是使得整个网络更"智能化"。参考这篇《利用负载均衡优化和加速HTTP应用》,就可以基本上了解这种方式的优势所在。 ...

April 29, 2019 · 2 min · jiezi

企业如何应付网站被攻击呢

发起DDoS的方法很多种,最基本的一种是:攻击者伪装出一个与攻击目标相同的IP地址,然后向预先选定的DNS服务器发送一个伪装的DNS请求,当DNS服务器接收到DNS请求时,服务器会检查其数据库,回复一个表示没有包含欺骗性IP地址的DNS记录的响应消息,因此无法追踪到攻击者来源。企业该如何应付这种攻击呢?群英教你几招。1.与一些DDoS防御供应商合作,如Arbor、CloudFlare、Akamai、Prolexic等,这是应对最严重攻击的一个可行方法; 2.如果没有足够资源购买第三方产品和服务的话,可以采取一些措施,尽量减小DDoS攻击的危害:①了解网站的互联网连接,定期报告统计信息;②部署防御机制,检查所有到达的DNS响应;③更多地将基础架构部署到云上。 因此,在防御和应对DDoS攻击时,保持警惕至关重要。管理员要了解互联网系统的最新攻击趋势、策略和流程,提前做好防御准备。

April 25, 2019 · 1 min · jiezi

服务器租用哪家比较好如何更好的选择

说不上哪家最好,只有选择最合适自己的才是最好的,群英建议企业可通过以下几点来分析是否符合自己的需求。1.所选机房的位置在哪里,可根据网站目标群体来选择国内或海外机房,国内又分南方和北方机房;2.了解机房环境设施是否齐全,完善的软硬件设施能使网站运行更安全和稳定;3.根据网站的访问量来选择合适的宽带大小,太大了浪费,太小了服务器可能随时因受到攻击而瘫痪;4.宽带的线路有电信和联通,一般来说南方以电信为主,北方以联通为佳;5.在售后服务这块最好选能提供7*24小时人工服务的服务商,而不是在线机器人服务,这样网站更有保障;6.最后选一家资质齐全、正规、口碑佳,运营时间比较长的公司比较好,老品牌公司运营体系相对较完善。

April 23, 2019 · 1 min · jiezi

主流微服务注册中心浅析和对比

开源产品受开发者热捧,是因为其代码透明、可以参与共建、有社区进行交流和学习,当然更重要的是开源产品的接入成本低。个人开发者或者中小型公司往往会将开源产品作为选型首选。 开发者通过阅读源代码,理解产品的功能设计和架构设计,同时也可以通过本地部署来测试性能,随之而来的是对各类开源产品的对比,用以选型。不过当前关于微服务注册中心的对比,大多聚焦在功能上的对比,对架构或者性能的深入探讨,比较少见。 另一方面,作为默默支持的产品,服务注册中心往往隐藏在服务框架背后。优秀的服务框架往往会支持多种配置中心,但是注册中心的选择依然与服务框架强关联,普遍的情况是一种服务框架会带一个默认的服务注册中心。这样虽然免去了用户在选型上的烦恼,但是单个注册中心的局限性,导致用户使用多个服务框架时,必须部署多套完全不同的注册中心,这些注册中心之间的数据协同是一个问题。 本文来自Nacos社区,作者是 Nacos PMC 朱鹏飞,作者力求公正和客观的去看待主流微服务注册中心的各个维度。本文不仅仅包含常见服务注册中心产品的对比,也试图从Nacos的经验和调研中总结并阐述服务注册中心产品设计上应该去遵循和考虑的要点,文章篇幅较长,若您有不同的看法,欢迎在文末留言,或到Nacos @GitHub 提issue。 前言服务发现是一个古老的话题,当应用开始脱离单机运行和访问时,服务发现就诞生了。目前的网络架构是每个主机都有一个独立的IP地址,那么服务发现基本上都是通过某种方式获取到服务所部署的IP地址。DNS协议是最早将一个网络名称翻译为网络IP的协议,在最初的架构选型中,DNS+LVS+Nginx基本可以满足所有的RESTful服务的发现,此时服务的IP列表通常配置在Nginx或者LVS。后来出现了RPC服务,服务的上下线更加频繁,人们开始寻求一种能够支持动态上下线并且推送IP列表变化的注册中心产品。 ZooKeeper是一款经典的服务注册中心产品(虽然它最初的定位并不在于此),在很长一段时间里,它是国人在提起RPC服务注册中心时心里想到的唯一选择,这很大程度上与Dubbo在中国的普及程度有关。Consul和Eureka都出现于2014年,Consul在设计上把很多分布式服务治理上要用到的功能都包含在内,可以支持服务注册、健康检查、配置管理、Service Mesh等。而Eureka则借着微服务概念的流行,与SpringCloud生态的深度结合,也获取了大量的用户。去年开源的Nacos,则携带着阿里巴巴大规模服务生产经验,试图在服务注册和配置管理这个市场上,提供给用户一个新的选择。 当市面上有多种相似功能的产品出现时,人们往往希望知道这些产品相比较的优劣。产品本身的定位会决定它包含了哪些功能,而产品架构的设计,则会影响产品的性能和可用性等。开源产品的一个优势是开发人员可以去阅读源代码,理解产品的功能设计和架构设计,同时也可以通过本地部署来测试性能,随之而来的是各种产品的对比文章。不过当前关于注册中心的对比,往往停留在表面的功能对比上,对架构或者性能并没有非常深入的探讨。 另一个现象是服务注册中心往往隐藏在服务框架背后,作为默默支持的产品。优秀的服务框架往往会支持多种配置中心,但是注册中心的选择依然强关联与服务框架,一种普遍的情况是一种服务框架会带一个默认的服务注册中心。这样虽然免去了用户在选型上的烦恼,但是单个注册中心的局限性,导致用户使用多个服务框架时,必须部署多套完全不同的注册中心,这些注册中心之间的数据协同也是一个问题。 本文是一篇来自Nacos项目组的文章,虽然是来自Nacos,我们依然力求公正和客观的去看待服务发现所有产品的各个维度。本文不仅仅包含常见服务注册中心产品的对比,还试图从我们的经验和调研中总结和阐述服务注册中心产品设计上应该去遵循和考虑的要点。 数据模型注册中心的核心数据是服务的名字和它对应的网络地址,当服务注册了多个实例时,我们需要对不健康的实例进行过滤或者针对实例的一些特征进行流量的分配,那么就需要在实例上存储一些例如健康状态、权重等属性。随着服务规模的扩大,渐渐的又需要在整个服务级别设定一些权限规则、以及对所有实例都生效的一些开关,于是在服务级别又会设立一些属性。再往后,我们又发现单个服务的实例又会有划分为多个子集的需求,例如一个服务是多机房部署的,那么可能需要对每个机房的实例做不同的配置,这样又需要在服务和实例之间再设定一个数据级别。 Zookeeper没有针对服务发现设计数据模型,它的数据是以一种更加抽象的树形K-V组织的,因此理论上可以存储任何语义的数据。而Eureka或者Consul都是做到了实例级别的数据扩展,这可以满足大部分的场景,不过无法满足大规模和多环境的服务数据存储。Nacos在经过内部多年生产经验后提炼出的数据模型,则是一种服务-集群-实例的三层模型。如上文所说,这样基本可以满足服务在所有场景下的数据存储和管理。 Nacos的数据模型虽然相对复杂,但是它并不强制你使用它里面的所有数据,在大多数场景下,你可以选择忽略这些数据属性,此时可以降维成和Eureka和Consul一样的数据模型。 另外一个需要考虑的是数据的隔离模型,作为一个共享服务型的组件,需要能够在多个用户或者业务方使用的情况下,保证数据的隔离和安全,这在稍微大一点的业务场景中非常常见。另一方面服务注册中心往往会支持云上部署,此时就要求服务注册中心的数据模型能够适配云上的通用模型。Zookeeper、Consul和Eureka在开源层面都没有很明确的针对服务隔离的模型,Nacos则在一开始就考虑到如何让用户能够以多种维度进行数据隔离,同时能够平滑的迁移到阿里云上对应的商业化产品。 Nacos提供了四层的数据逻辑隔离模型,用户账号对应的可能是一个企业或者独立的个体,这个数据一般情况下不会透传到服务注册中心。一个用户账号可以新建多个命名空间,每个命名空间对应一个客户端实例,这个命名空间对应的注册中心物理集群是可以根据规则进行路由的,这样可以让注册中心内部的升级和迁移对用户是无感知的,同时可以根据用户的级别,为用户提供不同服务级别的物理集群。再往下是服务分组和服务名组成的二维服务标识,可以满足接口级别的服务隔离。 Nacos 1.0.0介绍的另外一个新特性是:临时实例和持久化实例。在定义上区分临时实例和持久化实例的关键是健康检查的方式。临时实例使用客户端上报模式,而持久化实例使用服务端反向探测模式。临时实例需要能够自动摘除不健康实例,而且无需持久化存储实例,那么这种实例就适用于类Gossip的协议。右边的持久化实例使用服务端探测的健康检查方式,因为客户端不会上报心跳,那么自然就不能去自动摘除下线的实例。 在大中型的公司里,这两种类型的服务往往都有。一些基础的组件例如数据库、缓存等,这些往往不能上报心跳,这种类型的服务在注册时,就需要作为持久化实例注册。而上层的业务服务,例如微服务或者Dubbo服务,服务的Provider端支持添加汇报心跳的逻辑,此时就可以使用动态服务的注册方式。 数据一致性数据一致性是分布式系统永恒的话题,Paxos协议的艰深更让数据一致性成为程序员大牛们吹水的常见话题。不过从协议层面上看,一致性的选型已经很长时间没有新的成员加入了。目前来看基本可以归为两家:一种是基于Leader的非对等部署的单点写一致性,一种是对等部署的多写一致性。当我们选用服务注册中心的时候,并没有一种协议能够覆盖所有场景,例如当注册的服务节点不会定时发送心跳到注册中心时,强一致协议看起来是唯一的选择,因为无法通过心跳来进行数据的补偿注册,第一次注册就必须保证数据不会丢失。而当客户端会定时发送心跳来汇报健康状态时,第一次的注册的成功率并不是非常关键(当然也很关键,只是相对来说我们容忍数据的少量写失败),因为后续还可以通过心跳再把数据补偿上来,此时Paxos协议的单点瓶颈就会不太划算了,这也是Eureka为什么不采用Paxos协议而采用自定义的Renew机制的原因。 这两种数据一致性协议有各自的使用场景,对服务注册的需求不同,就会导致使用不同的协议。在这里可以发现,Zookeeper在Dubbo体系下表现出的行为,其实采用Eureka的Renew机制更加合适,因为Dubbo服务往Zookeeper注册的就是临时节点,需要定时发心跳到Zookeeper来续约节点,并允许服务下线时,将Zookeeper上相应的节点摘除。Zookeeper使用ZAB协议虽然保证了数据的强一致,但是它的机房容灾能力的缺乏,无法适应一些大型场景。 Nacos因为要支持多种服务类型的注册,并能够具有机房容灾、集群扩展等必不可少的能力,在1.0.0正式支持AP和CP两种一致性协议并存。1.0.0重构了数据的读写和同步逻辑,将与业务相关的CRUD与底层的一致性同步逻辑进行了分层隔离。然后将业务的读写(主要是写,因为读会直接使用业务层的缓存)抽象为Nacos定义的数据类型,调用一致性服务进行数据同步。在决定使用CP还是AP一致性时,使用一个代理,通过可控制的规则进行转发。 目前的一致性协议实现,一个是基于简化的Raft的CP一致性,一个是基于自研协议Distro的AP一致性。Raft协议不必多言,基于Leader进行写入,其CP也并不是严格的,只是能保证一半所见一致,以及数据的丢失概率较小。Distro协议则是参考了内部ConfigServer和开源Eureka,在不借助第三方存储的情况下,实现基本大同小异。Distro重点是做了一些逻辑的优化和性能的调优。 负载均衡负载均衡严格的来说,并不算是传统注册中心的功能。一般来说服务发现的完整流程应该是先从注册中心获取到服务的实例列表,然后再根据自身的需求,来选择其中的部分实例或者按照一定的流量分配机制来访问不同的服务提供者,因此注册中心本身一般不限定服务消费者的访问策略。Eureka、Zookeeper包括Consul,本身都没有去实现可配置及可扩展的负载均衡机制,Eureka的负载均衡是由ribbon来完成的,而Consul则是由Fabio做负载均衡。 在阿里巴巴集团内部,却是使用的相反的思路。服务消费者往往并不关心所访问的服务提供者的负载均衡,它们只关心以最高效和正确的访问服务提供者的服务。而服务提供者,则非常关注自身被访问的流量的调配,这其中的第一个原因是,阿里巴巴集团内部服务访问流量巨大,稍有不慎就会导致流量异常压垮服务提供者的服务。因此服务提供者需要能够完全掌控服务的流量调配,并可以动态调整。 服务端的负载均衡,给服务提供者更强的流量控制权,但是无法满足不同的消费者希望使用不同负载均衡策略的需求。而不同负载均衡策略的场景,确实是存在的。而客户端的负载均衡则提供了这种灵活性,并对用户扩展提供更加友好的支持。但是客户端负载均衡策略如果配置不当,可能会导致服务提供者出现热点,或者压根就拿不到任何服务提供者。 抛开负载均衡到底是在服务提供者实现还是在服务消费者实现,我们看到目前的负载均衡有基于权重、服务提供者负载、响应时间、标签等策略。其中Ribbon设计的客户端负载均衡机制,主要是选择合适现有的IRule、ServerListFilter等接口实现,或者自己继承这些接口,实现自己的过滤逻辑。这里Ribbon采用的是两步负载均衡,第一步是先过滤掉不会采用的服务提供者实例,第二步是在过滤后的服务提供者实例里,实施负载均衡策略。Ribbon内置的几种负载均衡策略功能还是比较强大的,同时又因为允许用户去扩展,这可以说是一种比较好的设计。 基于标签的负载均衡策略可以做到非常灵活,Kubernetes和Fabio都已经将标签运用到了对资源的过滤中,使用标签几乎可以实现任意比例和权重的服务流量调配。但是标签本身需要单独的存储以及读写功能,不管是放在注册中心本身或者对接第三方的CMDB。 在Nacos 0.7.0版本中,我们除了提供基于健康检查和权重的负载均衡方式外,还新提供了基于第三方CMDB的标签负载均衡器,具体可以参考CMDB功能介绍文章。使用基于标签的负载均衡器,目前可以实现同标签优先访问的流量调度策略,实际的应用场景中,可以用来实现服务的就近访问,当您的服务部署在多个地域时,这非常有用。使用这个标签负载均衡器,可以支持非常多的场景,这不是本文要详细介绍的。虽然目前Nacos里支持的标签表达式并不丰富,不过我们会逐步扩展它支持的语法。除此以外,Nacos定义了Selector,作为负载均衡的统一抽象。关于Selector,由于篇幅关系,我们会有单独的文章进行介绍。 理想的负载均衡实现应该是什么样的呢?不同的人会有不同的答案。Nacos试图做的是将服务端负载均衡与客户端负载均衡通过某种机制结合起来,提供用户扩展性,并给予用户充分的自主选择权和轻便的使用方式。负载均衡是一个很大的话题,当我们在关注注册中心提供的负载均衡策略时,需要注意该注册中心是否有我需要的负载均衡方式,使用方式是否复杂。如果没有,那么是否允许我方便的扩展来实现我需求的负载均衡策略。 健康检查Zookeeper和Eureka都实现了一种TTL的机制,就是如果客户端在一定时间内没有向注册中心发送心跳,则会将这个客户端摘除。Eureka做的更好的一点在于它允许在注册服务的时候,自定义检查自身状态的健康检查方法。这在服务实例能够保持心跳上报的场景下,是一种比较好的体验,在Dubbo和SpringCloud这两大体系内,也被培养成用户心智上的默认行为。Nacos也支持这种TTL机制,不过这与ConfigServer在阿里巴巴内部的机制又有一些区别。Nacos目前支持临时实例使用心跳上报方式维持活性,发送心跳的周期默认是5秒,Nacos服务端会在15秒没收到心跳后将实例设置为不健康,在30秒没收到心跳时将这个临时实例摘除。 不过正如前文所说,有一些服务无法上报心跳,但是可以提供一个检测接口,由外部去探测。这样的服务也是广泛存在的,而且以我们的经验,这些服务对服务发现和负载均衡的需求同样强烈。服务端健康检查最常见的方式是TCP端口探测和HTTP接口返回码探测,这两种探测方式因为其协议的通用性可以支持绝大多数的健康检查场景。在其他一些特殊的场景中,可能还需要执行特殊的接口才能判断服务是否可用。例如部署了数据库的主备,数据库的主备可能会在某些情况下切换,需要通过服务名对外提供访问,保证当前访问的库是主库。此时的健康检查接口,可能就是一个检查数据库是否是主库的MYSQL命令了。 客户端健康检查和服务端健康检查有一些不同的关注点。客户端健康检查主要关注客户端上报心跳的方式、服务端摘除不健康客户端的机制。而服务端健康检查,则关注探测客户端的方式、灵敏度及设置客户端健康状态的机制。从实现复杂性来说,服务端探测肯定是要更加复杂的,因为需要服务端根据注册服务配置的健康检查方式,去执行相应的接口,判断相应的返回结果,并做好重试机制和线程池的管理。这与客户端探测,只需要等待心跳,然后刷新TTL是不一样的。同时服务端健康检查无法摘除不健康实例,这意味着只要注册过的服务实例,如果不调用接口主动注销,这些服务实例都需要去维持健康检查的探测任务,而客户端则可以随时摘除不健康实例,减轻服务端的压力。 Nacos既支持客户端的健康检查,也支持服务端的健康检查,同一个服务可以切换健康检查模式。我们认为这种健康检查方式的多样性非常重要,这样可以支持各种类型的服务,让这些服务都可以使用到Nacos的负载均衡能力。Nacos下一步要做的是实现健康检查方式的用户扩展机制,不管是服务端探测还是客户端探测。这样可以支持用户传入一条业务语义的请求,然后由Nacos去执行,做到健康检查的定制。 性能与容量虽然大部分用户用到的性能不高,但是他们仍然希望选用的产品的性能越高越好。影响读写性能的因素很多:一致性协议、机器的配置、集群的规模、存量数据的规模、数据结构及读写逻辑的设计等等。在服务发现的场景中,我们认为读写性能都是非常关键的,但是并非性能越高就越好,因为追求性能往往需要其他方面做出牺牲。Zookeeper在写性能上似乎能达到上万的TPS,这得益于Zookeeper精巧的设计,不过这显然是因为有一系列的前提存在。首先Zookeeper的写逻辑就是进行K-V的写入,内部没有聚合;其次Zookeeper舍弃了服务发现的基本功能如健康检查、友好的查询接口,它在支持这些功能的时候,显然需要增加一些逻辑,甚至弃用现有的数据结构;最后,Paxos协议本身就限制了Zookeeper集群的规模,3、5个节点是不能应对大规模的服务订阅和查询的。 在对容量的评估时,不仅要针对企业现有的服务规模进行评估,也要对未来3到5年的扩展规模进行预测。阿里巴巴的中间件在内部支撑着集团百万级别服务实例,在容量上遇到的挑战可以说不会小于任何互联网公司。这个容量不仅仅意味着整体注册的实例数,也同时包含单个服务的实例数、整体的订阅者的数目以及查询的QPS等。Nacos在内部淘汰Zookeeper和Eureka的过程中,容量是一个非常重要的因素。 Zookeeper的容量,从存储节点数来说,可以达到百万级别。不过如上面所说,这并不代表容量的全部,当大量的实例上下线时,Zookeeper的表现并不稳定,同时在推送机制上的缺陷,会引起客户端的资源占用上升,从而性能急剧下降。 Eureka在服务实例规模在5000左右的时候,就已经出现服务不可用的问题,甚至在压测的过程中,如果并发的线程数过高,就会造成Eureka crash。不过如果服务规模在1000上下,几乎目前所有的注册中心都可以满足。毕竟我们看到Eureka作为SpringCloud的注册中心,在国内也没有看到很广泛的对于容量或者性能的问题报告。 Nacos在开源版本中,服务实例注册的支撑量约为100万,服务的数量可以达到10万以上。在实际的部署环境中,这个数字还会因为机器、网络的配置与JVM参数的不同,可能会有所差别。图9展示了Nacos在使用1.0.0版本进行压力测试后的结果总结,针对容量、并发、扩展性和延时等进行了测试和统计。 完整的测试报告可以参考Nacos官网:https://nacos.io/en-us/docs/nacos-naming-benchmark.htmlhttps://nacos.io/en-us/docs/nacos-config-benchmark.html 易用性易用性也是用户比较关注的一块内容。产品虽然可以在功能特性或者性能上做到非常先进,但是如果用户的使用成本极高,也会让用户望而却步。易用性包括多方面的工作,例如API和客户端的接入是否简单,文档是否齐全易懂,控制台界面是否完善等。对于开源产品来说,还有一块是社区是否活跃。在比较Nacos、Eureka和Zookeeper在易用性上的表现时,我们诚邀社区的用户进行全方位的反馈,因为毕竟在阿里巴巴集团内部,我们对Eureka、Zookeeper的使用场景是有限的。从我们使用的经验和调研来看,Zookeeper的易用性是比较差的,Zookeeper的客户端使用比较复杂,没有针对服务发现的模型设计以及相应的API封装,需要依赖方自己处理。对多语言的支持也不太好,同时没有比较好用的控制台进行运维管理。 Eureka和Nacos相比较Zookeeper而言,已经改善很多,这两个产品有针对服务注册与发现的客户端,也有基于SpringCloud体系的starter,帮助用户以非常低的成本无感知的做到服务注册与发现。同时还暴露标准的HTTP接口,支持多语言和跨平台访问。Eureka和Nacos都提供官方的控制台来查询服务注册情况。不过随着Eureka 2.0宣布停止开发,Eureka在针对用户使用上的优化后续应该不会再有比较大的投入,而Nacos目前依然在建设中,除了目前支持的易用性特性以外,后续还会继续增强控制台的能力,增加控制台登录和权限的管控,监控体系和Metrics的暴露,持续通过官网等渠道完善使用文档,多语言SDK的开发等。 从社区活跃度的角度来看,目前由于Zookeeper和Eureka的存量用户较多,很多教程以及问题排查都可以在社区搜索到,这方面新开源的Nacos还需要随着时间继续沉淀。 集群扩展性集群扩展性和集群容量以及读写性能关系紧密。当使用一个比较小的集群规模就可以支撑远高于现有数量的服务注册及访问时,集群的扩展能力暂时就不会那么重要。从协议的层面上来说,Zookeeper使用的ZAB协议,由于是单点写,在集群扩展性上不具备优势。Eureka在协议上来说理论上可以扩展到很大规模,因为都是点对点的数据同步,但是从我们对Eureka的运维经验来看,Eureka集群在扩容之后,性能上有很大问题。 集群扩展性的另一个方面是多地域部署和容灾的支持。当讲究集群的高可用和稳定性以及网络上的跨地域延迟要求能够在每个地域都部署集群的时候,我们现有的方案有多机房容灾、异地多活、多数据中心等。 首先是双机房容灾,基于Leader写的协议不做改造是无法支持的,这意味着Zookeeper不能在没有人工干预的情况下做到双机房容灾。在单机房断网情况下,使机房内服务可用并不难,难的是如何在断网恢复后做数据聚合,Zookeeper的单点写模式就会有断网恢复后的数据对账问题。Eureka的部署模式天然支持多机房容灾,因为Eureka采用的是纯临时实例的注册模式:不持久化、所有数据都可以通过客户端心跳上报进行补偿。上面说到,临时实例和持久化实例都有它的应用场景,为了能够兼容这两种场景,Nacos支持两种模式的部署,一种是和Eureka一样的AP协议的部署,这种模式只支持临时实例,可以完美替代当前的Zookeeper、Eureka,并支持机房容灾。另一种是支持持久化实例的CP模式,这种情况下不支持双机房容灾。 在谈到异地多活时,很巧的是,很多业务组件的异地多活正是依靠服务注册中心和配置中心来实现的,这其中包含流量的调度和集群的访问规则的修改等。机房容灾是异地多活的一部分,但是要让业务能够在访问服务注册中心时,动态调整访问的集群节点,这需要第三方的组件来做路由。异地多活往往是一个包含所有产品线的总体方案,很难说单个产品是否支持异地多活。 多数据中心其实也算是异地多活的一部分。从单个产品的维度上,Zookeeper和Eureka没有给出官方的多数据中心方案。Nacos基于阿里巴巴内部的使用经验,提供的解决方案是才有Nacos-Sync组件来做数据中心之间的数据同步,这意味着每个数据中心的Nacos集群都会有多个数据中心的全量数据。Nacos-Sync是Nacos生态组件里的重要一环,不仅会承担Nacos集群与Nacos集群之间的数据同步,也会承担Nacos集群与Eureka、Zookeeper、Kubernetes及Consul之间的数据同步。 ...

April 22, 2019 · 1 min · jiezi

Nginx失败重试中的HTTP协议幂等问题: non_idempotent

Nginx通过反向代理做负载均衡时,如果被代理的其中一个服务发生错误或者超时的时候,通常希望Nginx自动重试其他的服务,从而实现服务的高可用性。实际上Nginx本身默认会有错误重试机制,并且可以通过proxy_next_upstream来自定义配置。 如果不了解HTTP协议以及Nginx的机制,就可能在使用过程中遇到各种各样的坑。例如服务出现了错误或超时却未重试,或者一些例如创建订单或发送短信这类的HTTP接口,客户端只发送一次请求,后台却由于Nginx重试导致创建了多个订单,或者收到多条短信,导致一些业务上的问题。 proxy_next_upstream在Nginx配置文件中,proxy_next_upstream用于指定在什么情况下Nginx会将请求转移到其他服务器上。其默认值是proxy_next_upstream error timeout,即发生网络错误以及超时,才会重试其他服务器。默认情况下服务返回500状态码是不会重试的,如果想在响应500状态码时也进行重试,可以配置: proxy_next_upstream error timeout http_500;当然还有http_502、http_503、http_404等可以指定在出现哪些状态码的情况下需要重试。具体配置项可以参考官方文档: http://nginx.org/en/docs/http... 。 用一个最简单的例子来测试一下该特性,例如下面是Spring Boot写了一个简单的HTTP接口,返回500状态码: @SpringBootApplicationpublic class NginxRetryApplication { public static void main(String[] args) { SpringApplication.run(NginxRetryApplication.class, args); }}@RestControllerclass TestController { @RequestMapping("/") public String test() { System.out.println("收到一个请求"); // 打印日志 throw new RuntimeException(); // 抛出异常, 返回500状态码 }}分别使用9030和9031两个端口号启动该Spring Boot服务,然后Nginx配置好负载均衡: upstream nginxretry { server 127.0.0.1:9030 max_fails=0; server 127.0.0.1:9031 max_fails=0;}server { listen 9039; location / { proxy_pass http://nginxretry; proxy_next_upstream error timeout http_500; }}注意:以上配置中max_fails=0是为了更方便的测试Nginx错误重试机制。max_fails默认值是1,用于指定一个server在一段时间内(默认10s)发生错误次数达到多少次,Nginx就会自动将该服务器下线。这里设置为0是禁用这个特性,防止在测试过程中服务器被踢下线不好测试。线上环境下一般不会设置max_fails=0。 配置完成后重启Nginx,使用GET方式请求 http://localhost:9039/ ,再分别查看9030和9031两个端口号对应的服务日志,可以发现两个服务都收到请求,也就是Nginx在访问其中一个服务收到500错误状态码后,又尝试去访问另一个服务。 ...

April 21, 2019 · 1 min · jiezi

聊聊 Nginx 的反向代理

本文来自于我的慕课网手记:聊聊 Nginx 的反向代理,转载请保留链接 ;)背景最近在优化服务基础设施这块,正好有时间写一下Nginx的体会。相信大家都听说过反向代理,一提到反向代理一定会想到Nginx。什么你没听过Nginx?那么你一定听说过Apache吧!Apache是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一。Apache的发展时期很长,而且是毫无争议的世界第一大服务器。它有着很多优点:稳定、开源、跨平台等等。它出现的时间太长了,它兴起的年代,互联网产业远远比不上现在。所以它被设计为一个重量级的。它不支持高并发的服务器。在Apache上运行数以万计的并发访问,会导致服务器消耗大量内存。操作系统对其进行进程或线程间的切换也消耗了大量的CPU资源,导致HTTP请求的平均响应速度降低。所以这些都决定了Apache不可能成为高性能WEB服务器,轻量级高并发服务器Nginx就应运而生了。什么是Nginx俄罗斯的工程师Igor Sysoev,他在为Rambler Media工作期间,使用C语言开发了Nginx。Nginx作为WEB服务器一直为Rambler Media提供出色而又稳定的服务。然后呢,Igor Sysoev将Nginx代码开源,并且赋予自由软件许可证。由于:Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器(这是个开源的时代啊~)Nginx是一个跨平台服务器,可以运行在Linux,Windows,FreeBSD,Solaris, AIX,Mac OS等操作系统上。可以作为反向代理进行负载均衡的实现,带来的极大的稳定性。Nginx这样做的目的主要是将数据的承载量分摊到多个服务器上进行执行,这只是在服务基础设施上提高性能的优化手段之一。从下面图就可以看出:什么是反向代理?看了网上很多篇文章,下面这段话是我个人觉得介绍的最清楚的。反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。所以反向代理服务器是引用在服务端。决定哪台服务器提供服务。那么我就模拟了一个基本的反向代理和负载均衡配置实现,下面是我的操作步骤:反向代理的模拟首先创建多个 tomcat 服务器,我这里创建了三个:然后修改每个 tomcat 运行在不同的端口号上,每个 server.xml配置文件需要修改三处端口:分别在 8080,8081,8082 端口配置 8006,8081,8010 端口,分别进行启动测试,但是注意防火墙配置问题。测试结果如下:接着配置 nginx 的配置文件,进行反向代理:最后不断访问刚配置的 test.tomcats.com 域名,发现三台 tomcat 成功运行的页面都有出现,比例大致 1:1:1:恭喜你,配置成功了!配置负载均衡其实很简单,就是在刚刚配置的 nginx 文件中 的 upstream tomcats 中的 server 后面添加一个 weight, 即可代表权重。权重越高,分派请求的数量就越多。默认权重是 1。

April 18, 2019 · 1 min · jiezi

IP应用加速 – DCDN迈入全栈新篇章

4月11日,第七届"亚太内容分发大会"暨CDN峰会国际论坛中,阿里云资深技术专家姚伟斌发布了DCDN子产品IP应用加速(IPA)。IPA是基于阿里云CDN本身的资源优化,对传输层(TCP&UDP)协议进行全栈能力提升,同时利用就近接入、智能路由、传输协议优化,以及多种负载均衡技术,实现更高效、更安全、更快速、更便捷的动态加速产品。通常全球内容传输会存在一些痛点,比如:偏远用户、长尾用户访问质量差;对跨运营商,跨国的覆盖要求;游戏、RTC等行业对可靠性、实时性要求很高;电商、金融等行业对安全性要求更高;失败率高等等,IP应用加速的出现,能很好的解决这些问题。优质的内容分发加速最基本的指标是可靠性,阿里云通过覆盖全球的2500个节点,利用超过99%精度的IP数据,并支持TCP和UDP自适应接入实现智能调度同时自研协议栈、多路径传输、动态选路、DDoS防御和全链路加密提供安全、可靠的传输链路。IPA功能优势及架构覆盖更多场景:除了七层外,还对四层、三层网络协议进行支持,全协议栈加速通道,满足更多用户需求和业务场景。更高可靠性:有效解决偏远地区、跨境传输的互联互通问题,降低错误率提供更低延时加速体验降低成本:大幅度减少机器及专线成本,节省预算。安全防护:抗攻击、流量清洗,满足高安全性的业务场景需求以下为整体架构图,包括边缘智能就近接入,节点间实时探测,动态选路,节点间高性能自研协议栈,源站自动选择。针对更高性能的传输需求,阿里云自研的高性能私有传输协议,实现数据多路径传输,改造TCP协议栈提升整个传输效率,加速整个数据传输的可靠性和实时性。同时对内部链路,也支持秒级智能路由切换功能,极大降低数据传输时候应对丢包的可能性。下图是线上实测的数据,相对于TCP协议来说,传输的效率会提升20%以上。IPA适用场景直播互动:弹幕分发、礼物分发、登录连接、评论分享、聊天室消息…游戏行业:游戏互动指令、游戏语音传输、TCP数据接口、UDP内容交互…在线教育:师生音视频互动、文件分享、即时消息、屏幕共享…办公系统:SSL VPN、远程桌面、移动办公APP、邮箱系统、OA…云服务同步:数据中心同步、云ERP、云CRM、远程登录系统…金融行业:手机银行、网银支付、在线交易、股票买卖…在接入方式上,IPA只需要用户提交一个域名即可接入,仅仅1分钟一键就可以开启四层加速的功能,有效降低原来开专线、买服务器等业务加速的复杂度,缩短业务上线时间。应用案例办公通讯软件这个场景下的最大痛点是偏远地区消息不稳定以及海外通信质量差,会出现失败率高的情况,IPA通过下图中的优化架构,从HTTPDNS接入,在节点内部通过ACL有效保证端口的高可靠性,全球访问速度提升超过50%。互动直播应用互动直播中发弹幕、送礼物的场景,对交流的实时性要求非常高,经常会出现登录与加速缓慢,互动体验差等问题。阿里云IPA架构支持客户端与中心服务器之间更有效的交流,同时覆盖了东南亚以及全国各地的地区,最终实现评论、分享延时降低20%以上。办公ERP系统办公ERP系统的痛点是数据传输延迟高,不稳定,表单查询成功率低等问题,导致协同办公效率低下。通过IPA对于超大文件的传输优化,实现传输速率提升30%以上,可用性提升50%。金融行业金融交易的展示不实时,用户的交易就无法保障,针对海外交易延时偏高,价格变动频繁,交易过程失败率高,对于安全性要求更高等问题,因此,阿里云针对针对链路传输加密,对源站保障负载均衡,最终整体延迟降低超过了80%。谈到IP应用加速的未来规划,姚伟斌说:未来IPA会提供三层、四层、七层全栈加速能力,支持数十万海量边缘节点接入,真正实现端-边-云的Overlay边缘传输网络,并尝试与生态硬件集成,为5G时代的边缘智能网络提供基础。本文作者:樰篱阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 12, 2019 · 1 min · jiezi

MySQL 复制 - 性能与扩展性的基石 3:常见问题及解决方案

主备复制过程中有很大可能会出现各种问题,接下来我们就讨论一些比较普遍的问题,以及当遇到这些问题时,如何解决或者预防问题发生。1 数据损坏或丢失问题描述:服务器崩溃、断电、磁盘损坏、内存或网络错误等问题,导致数据损坏或丢失。问题原因:非正常关机导致没有把数据及时的写入硬盘。这种问题,一般可以分为几种情况导致:1.1 主库意外关闭问题未发生,避免方案:设置主库的 sync_binlog 选项为 1。此选项表示 MySQL 是否控制 binlog 的刷新。当设置为 1 时,表示每次事务提交,MySQL 都会把 binlog 刷下去,是最安全,性能损耗也最大的设置。问题已发生,解决方案:指定备库从下一个二进制日志的开头重新读日志。但是一些日志事件将永久性丢失。可以使用 Percona Toolkit 中的 pt-table-checksum 工具来检查主备一致性,以便于修复。1.2 备库意外关闭备库意外关闭重启时,会去读 master.info 文件以找到上次停止复制的位置。但是在意外关闭的情况下,这个文件存储的信息可能是错误的。此外,备库也可能会尝试重新执行一些二进制文件,这可能会导致唯一索引错误。我们可以通过 Percona Toolkit 中的 pt-slave-restart 工具,帮助备库重新执行日志文件。如果使用的是 InnoDB 表,可以在重启后观察 MySQL 的错误日志。InnoDB 在恢复过程中会打印出恢复点的二进制日志坐标,可以使用这个值来决定备库指向主库的偏移量。1.3 主库二进制日志损坏如果主库上的二进制日志损坏,除了忽略损坏的位置外,别无选择。在忽略存货位置后,我们可以通过 FLUSH LOGS 命令在主库开始一个新的日志文件,然后将备库指向该文件的开始位置。1.4 备库中继日志损坏如果主库上的日志是完好的,有两种解决方案:1) 手工处理。找到 master binlog 日志的 pos 点,然后重新同步。2) 自动处理。mysql5.5 考虑到 slave 宕机中继日志损坏这一问题,只要在 slave 的的配置文件 my.cnf 里增加一个参数 relay_log_recovery=1 即可。1.5 二进制日志与 InnoDB 事务日志不同步由于各种各样的原因,MySQL 的复制碰到服务器崩溃、断电、磁盘损坏、内存或网络错误时,很难恢复当时丢失的数据。几乎都需要从某个点开始重启复制。2 未定义的服务器 ID如果没有再 my.cnf 里定义服务器 ID,虽然可以通过 CHANGE MASTER TO 来设置备库,但在启动复制时会遇到:mysql> START SLAVE;ERROR 1200 (HY000): The server us bit configured as slave; fix in config file or with CHANGE MASTER TO这个报错可能会让人困惑。因为我们可能已经通过 CHANGE MASTER TO 设置了备库,并且通过 SHOW MASTER STATUS 也确认了,为什么还会有这样的报错呢?我们通过 SELECT @@server_id 可以获得一个值,要注意的是,这个值只是默认值,我们必须为备库显式地设置服务器 ID。也就是在 my.cnf 里显示的设置服务器 ID。3 对未复制数据的依赖性如果在主库上有备库上不存在的数据库或数据表,复制就很容易中断,反之亦然。对于前者,假设在主库上有一个 single_master 表,备库没有。在主库上对此表进行操作后,备库在尝试回放这些操作时就会出现问题,导致复制中断。对于后者,假设备库上有一个 single_slave 表,主库没有。在主库上执行创建 single_slave 表的语句时,备库在回放该建表语句时就会出现问题。对于此问题,我们能做的就是做好预防:主备切换时,尽量在切换后对比数据,查清楚是否有不一致的表或库。一定不要在备库执行写操作。4 丢失的临时表临时表和基于语句的复制方式不相容。如果备库崩溃或者正常关闭,任何复制线程拥有的临时表都会丢失。重启备库后,所有依赖于该临时表的语句都会失败。复制时出现找不到临时表的异常时,可以做:直接跳过错误,或者手动地创建一个名字和结构相同的表来代替消失的的临时表。临时表的特性:只对创建临时表的连接可见。不会和其他拥有相同名字的临时表的连接起冲突;随着连接关闭而消失,无须显式的移除它们。4.1 更好使用临时表的方式保留一个专用的数据库,在其中创建持久表,把它们作为伪临时表,以模拟临时表特性。只需要通过 CONNETCTION_ID() 的返回值,给临时表创建唯一的名字。伪临时表的优劣势优势:更容易调试应用程序。可以通过别的连接来查看应用正在维护的数据;劣势:比临时表多一些开销。创建较慢伪临时表会较慢,因为表的 .frm 文件需要刷新到磁盘。5 InnoDB 加锁读导致主备数据不一致使用共享锁,串行化更新,保证备库复制时数据一致。某些情况下,加锁读可以防止混乱。假设有两张表:tab1 没有数据,tab2 只有一行数据,值为 99。此时,有两个事务更新数据。事务 1 将 tab2 的数据插入到 tab1,事务 2 更新 tab2。事务 1 使用获取 tab2 数据时,加入共享锁,并插入 tab1;同时,事务 2 更新 tab2 数据时,由于写操作的排它锁机制,无法获取 tab2 的锁,等待;事务 1 插入数据后,删除共享锁,提交事务,写入 binlog(此时 tab1 和 tab2 的记录值 都是 99);事务 2 获取到锁,更新数据,提交事务,写入 binlog(此时 tab1 的记录值为 99,tab2 的记录值为 100)。上述过程中,第二步非常重要。事务 2 尝试去更新 tab2 表,这需要在更新的行上加排他锁(写锁)。排他锁与其他锁不相容,包括事务 1 在行记录上加的共享锁。因此事务 2 需要等待事务 1 完成。备库在根据 binlog 进行复制时,会按同样的顺序先执行事务 1,再执行事务 2。主备数据一致。同样的过程,如果事务 1 在第一步时没有加共享锁,流程就变成:事务 1 无锁读取 tab2 数据,并插入 tab1(此时 tab1 和 tab2 的记录值 都是 99);同时,事务 2 更新 tab2 数据,先与事务 1 提交事务,写入 binlog(此时 tab1 的记录值为 99,tab2 的记录值为 100);事务 1 提交事务,写入 binlog(此时记录值无变化);mysqldump –single-transaction –all-databases –master-data=1 –host=server1 | mysql –host=server2要注意的是,上述过程中,事务 2 先提交,先写入 binlog。在备库复制时,同样先执行事务 2,将 tab2 的记录值更新为 100。然后执行事务 1,读取 tab2 数据,插入 tab1,所以最终的结果是,tab1 的记录值和 tab2 的记录值都是 100。很明显,数据和主库有差异。建议在大多数情况下将 innodb_unsafe_for_binlog 的值设置为 0。基于行的复制由于记录了数据的变化而非语句,因此不会存在这个问题。6 复制延迟过大产生延迟的两种方式突然产生延迟,然后再跟上;稳定的延迟增大前者通常是由于一条执行时间过长的 SQL 导致,而后者即使在没有慢语句也会出现。对于前者,我们可以通过备库上的慢查询日志来进行优化。在备库上开启 log_slow_slave_statement 选项,可以在慢查询日志中记录复制线程执行的语句。而对于后者,没有针对性的解决方案,只能通过各种方式提高备库的复制效率。而当我们想去对备库做优化时,会发现,除了购买更快的磁盘和 CPU,并没有太多的调优空间。只能通过 MySQL 选项禁止某些额外的工作以减少备库的复制。可以通过下面几种方式:使用 InnoDB 引擎时,设置 innodb_flush_log_at_trx_commit 值为 2,来使备库不要频繁的刷新磁盘,以提高事务提交效率。禁止二进制日志记录。把 innodb_locks_unsafe_for_binlog 设置为 1,并把 MyISAM 的 delay_key_write 设置为 ALL。要注意的是,这些设置是以安全换取速度,在将备库提升为主库时,记得把这些选项设置回安全的值。拆分效率较低的复制 SQL,分离复杂语句中的 SELECT 和 UPDATE 语句,降低复制消耗,提高效率。总结复制问题要分清楚是 master 的问题,还是 slave 的问题。master 问题找 binlog,slave 问题找 relaylog。 ...

April 8, 2019 · 2 min · jiezi

使用“微服务+云架构”轻松应对系统扩容!

不知道大家打开本文,有没有留意文章所在的分类节点:云计算。其实我的本意,是要将微服务跟云架构归类在一起。因为他们都有着一个相同的存在目的:方便扩容! 扩容。对于遇到过系统瓶颈,需要扩容的系统,恭喜你,你的系统一定是快速发展,遇到了访问量上升的情况!【云架构,系统扩容案例】先说下我个人的经历:我是做GPS防盗器系统的,硬件需要给后台服务器回发数据,所以硬件产品销售的越好,我的系统就需要面对越来越多的压力挑战。感谢经历了这样的一个过程,让我深刻意识到了系统扩容架构设计的巨大价值。我的项目里,经历过这么三个阶段:第一阶段:单机阶段单机应用,单进程应用,事实证明只能承载几百设备并发。通过改造多线程,IOCP设计模型,可以承载20000以上的并发瓶颈点:难以突破单机应用的并发能力,每次遇到难点都得重构。在我的案例里,就是可以增加到30000负载,增加不到50000万负载!第二阶段:手动拆分多服务器阶段手动分布式分离设计,网站,socket接收程序,缓存,数据库,使用自建机房独立运行。事实证明,可以承载几十万设备并发瓶颈点:自建机房防火墙设备有并发数限制,CISCO ASA 5515防火墙最大允许25万连接。第三阶段:云架构阶段云架构设计,通过修改系统,实现自动扩容。这个时候,客户端设备数再多也没事,因为阿里云的 复杂均衡SLB之后的ECS服务器数量可以随时添加和减少,目前已经达到了100多万的设备并发连接无压力。瓶颈点:仅限于我,将来数据库压力还需要进一步优化,但是目前并发设备数上百万毫无压力,不过阿里云的分布式数据库DRDS似乎也能解决我的难点。【微服务,模块化应用案例】我的案例下,重点解释了云架构的作用,没有重点介绍微服务的作用。但是实际上,在几次改造过程中,已经使用了一点点微服务的功能:模块化功能,刚才我的案例都是基于整体系统拆分,实际上还有个优化空间就是改造成微服务。“微服务应用”举例:登录系统功能:目前同时登陆用户最多也就几百人。登陆功能代码跟着网站整体发布,负载均衡下需要一下子维护起来一下子更新几十台web机器,显然太多余。如果登陆功能这个“微服务”组件单独发布,那么只用2台web机器(“登录功能专用服务器”)专门负载登陆功能戳戳有余。将来这部分系统压力增加,只需要增加一台“登陆服务器”即可。查询定位功能:每个人的定位页面都在高频率刷新访问,虽然只有几百人登陆,但是造成的访问次数却高达上万次。怎么办?专门拿出十几台web服务器,用于“定位查询服务器”。这样,如果监控到定位功能有问题,只需要从这十几台“定位查询服务器”中排查问题!结论:微服务的目的在于软件开发层面的功能化拆分。对于使用微服务:小项目用起来费力,大项目用起来省心。所以导致现在观点多种:没接触过大项目的人觉得微服务就是个累赘;接触过大项目的人承认微服务的价值,却不建议小项目使用微服务进行“高射炮打蚊子”一直做大流量项目的人,提倡使用微服务。【结论】微服务的价值:在于将来访问量上升时,精准调控某一个瓶颈点的功能,主要属于开发层面的储备云架构的价值:在于访问量上升时,直接增加服务器数量扩大系统承载阈值,主要属于运维层面的储备微服务+云架构:大型系统的重要组合!原文地址: https://www.opengps.cn/Blog/View.aspx?id=279 文章的更新编辑依此链接为准。欢迎关注源站原创文章!

March 30, 2019 · 1 min · jiezi

一篇读懂分布式架构下的负载均衡

一篇读懂分布式架构下的负载均衡微信公众号:IT一刻钟大型现实非严肃主义现场一刻钟与你分享优质技术架构与见闻,做一个有剧情的程序员关注可第一时间了解更多精彩内容,定期有福利相送哟。什么是负载均衡?百度词条里的解释是:负载均衡,英文叫Load Balance,意思就是将请求或者数据分摊到多个操作单元上进行执行,共同完成工作任务。它的目的就通过调度集群,达到最佳化资源使用,最大化吞吐率,最小化响应时间,避免单点过载的问题。负载均衡分类负载均衡可以根据网络协议的层数进行分类,我们这里以ISO模型为准,从下到上分为:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。当客户端发起请求,会经过层层的封装,发给服务器,服务器收到请求后经过层层的解析,获取到对应的内容。二层负载均衡二层负债均衡是基于数据链路层的负债均衡,即让负债均衡服务器和业务服务器绑定同一个虚拟IP(即VIP),客户端直接通过这个VIP进行请求,那么如何区分相同IP下的不同机器呢?没错,通过MAC物理地址,每台机器的MAC物理地址都不一样,当负载均衡服务器接收到请求之后,通过改写HTTP报文中以太网首部的MAC地址,按照某种算法将请求转发到目标机器上,实现负载均衡。这种方式负载方式虽然控制粒度比较粗,但是优点是负载均衡服务器的压力会比较小,负载均衡服务器只负责请求的进入,不负责请求的响应(响应是有后端业务服务器直接响应给客户端),吞吐量会比较高。三层负载均衡三层负载均衡是基于网络层的负载均衡,通俗的说就是按照不同机器不同IP地址进行转发请求到不同的机器上。这种方式虽然比二层负载多了一层,但从控制的颗粒度上看,并没有比二层负载均衡更有优势,并且,由于请求的进出都要经过负载均衡服务器,会对其造成比较大的压力,性能也比二层负载均衡要差。四层负载均衡四层负载均衡是基于传输层的负载均衡,传输层的代表协议就是TCP/UDP协议,除了包含IP之外,还有区分了端口号,通俗的说就是基于IP+端口号进行请求的转发。相对于上面两种,控制力度缩小到了端口,可以针对同一机器上的不用服务进行负载。这一层以LVS为代表。七层负载均衡七层负载均衡是基于应用层的负载均衡,应用层的代表协议有HTTP,DNS等,可以根据请求的url进行转发负载,比起四层负载,会更加的灵活,所控制到的粒度也是最细的,使得整个网络更"智能化"。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。可以说功能是非常强大的负载。这一层以Nginx为代表。在普通的应用架构中,使用Nginx完全可以满足需求,对于一些大型应用,一般会采用DNS+LVS+Nginx的方式进行多层次负债均衡,以上这些说明都是基于软件层面的负载均衡,在一些超大型的应用中,还会在前面多加一层物理负载均衡,比如知名的F5。负载均衡算法负载均衡算法分为两类:一种是静态负载均衡,一种是动态负载均衡。静态均衡算法:1、轮询法将请求按顺序轮流地分配到每个节点上,不关心每个节点实际的连接数和当前的系统负载。优点:简单高效,易于水平扩展,每个节点满足字面意义上的均衡;缺点:没有考虑机器的性能问题,根据木桶最短木板理论,集群性能瓶颈更多的会受性能差的服务器影响。2、随机法将请求随机分配到各个节点。由概率统计理论得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配,也就是轮询的结果。优缺点和轮询相似。3、源地址哈希法源地址哈希的思想是根据客户端的IP地址,通过哈希函数计算得到一个数值,用该数值对服务器节点数进行取模,得到的结果便是要访问节点序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会落到到同一台服务器进行访问。优点:相同的IP每次落在同一个节点,可以人为干预客户端请求方向,例如灰度发布;缺点:如果某个节点出现故障,会导致这个节点上的客户端无法使用,无法保证高可用。当某一用户成为热点用户,那么会有巨大的流量涌向这个节点,导致冷热分布不均衡,无法有效利用起集群的性能。所以当热点事件出现时,一般会将源地址哈希法切换成轮询法。4、加权轮询法不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。加权轮询算法要生成一个服务器序列,该序列中包含n个服务器。n是所有服务器的权重之和。在该序列中,每个服务器的出现的次数,等于其权重值。并且,生成的序列中,服务器的分布应该尽可能的均匀。比如序列{a, a, a, a, a, b, c}中,前五个请求都会分配给服务器a,这就是一种不均匀的分配方法,更好的序列应该是:{a, a, b, a, c, a, a}。优点:可以将不同机器的性能问题纳入到考量范围,集群性能最优最大化;缺点:生产环境复杂多变,服务器抗压能力也无法精确估算,静态算法导致无法实时动态调整节点权重,只能粗糙优化。5、加权随机法与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。6、键值范围法根据键的范围进行负债,比如0到10万的用户请求走第一个节点服务器,10万到20万的用户请求走第二个节点服务器……以此类推。优点:容易水平扩展,随着用户量增加,可以增加节点而不影响旧数据;缺点:容易负债不均衡,比如新注册的用户活跃度高,旧用户活跃度低,那么压力就全在新增的服务节点上,旧服务节点性能浪费。而且也容易单点故障,无法满足高可用。(注:以上所提到的单点故障,都可以用主从方式来解决,从节点监听主节点心跳,当发现主节点死亡,从节点切换成主节点顶替上去。这里可以思考一个问题,怎么设计集群主从可以最大程度上降低成本)动态负债均衡算法:1、最小连接数法根据每个节点当前的连接情况,动态地选取其中当前积压连接数最少的一个节点处理当前请求,尽可能地提高后端服务的利用效率,将请求合理地分流到每一台服务器。俗称闲的人不能闲着,大家一起动起来。优点:动态,根据节点状况实时变化;缺点:提高了复杂度,每次连接断开需要进行计数;实现:将连接数的倒数当权重值。2、最快响应速度法根据请求的响应时间,来动态调整每个节点的权重,将响应速度快的服务节点分配更多的请求,响应速度慢的服务节点分配更少的请求,俗称能者多劳,扶贫救弱。优点:动态,实时变化,控制的粒度更细,跟灵敏;缺点:复杂度更高,每次需要计算请求的响应速度;实现:可以根据响应时间进行打分,计算权重。3、观察模式法观察者模式是综合了最小连接数和最快响应度,同时考量这两个指标数,进行一个权重的分配。说在后面话还有哪些负载均衡的算法,或者有更好的想法或问题,欢迎留言交流!

March 29, 2019 · 1 min · jiezi

kong 配置负载均衡

负载均衡route根据paths转发给相应的service根据host(upstream的name)转发给 upstream负载均衡至targets,这就是kong的负载均衡执行流程,下面通过restApi分别配置upstream,service,route。upstreams创建名为upstream.api的upstreamcurl -X POST localhost:8001/upstreams-d “name=upstream.api”// reponse{ “created_at”: 1553661443, “hash_on”: “none”, “id”: “04c9c36c-eea8-4d58-8668-3bfa117c34fd”, “name”: “upstream.api”, …}为upstream.api添加后端服务器curl -X POST localhost:8001/upstreams/upstream.api/targets -d “target=192.168.20.6:8888” -d “weight=100”// reponse{ “created_at”: 1553663185.86, “upstream”: { “id”: “04c9c36c-eea8-4d58-8668-3bfa117c34fd” }, “id”: “3386af25-8643-4c9c-aff5-bd30451ae24b”, “target”: “192.168.20.6:8888”, “weight”: 100}curl -X POST localhost:8001/upstreams/upstream.api/targets -d “target=192.168.20.6:9999” -d “weight=100”// reponse{ “created_at”: 1553663185.86, “upstream”: { “id”: “04c9c36c-eea8-4d58-8668-3bfa117c34fd” }, “id”: “3386af25-8643-4c9c-aff5-bd30451ae24b”, “target”: “192.168.20.6:9999”, “weight”: 100}等同于创建了如下配置:upstream upstream.api { server 192.168.20.6:8888 weight=100; server 192.168.20.6:9999 weight=100;}services创建名为service.api的服务,并通过host绑定相应的后端服务upstream.api。curl -X POST localhost:8001/services/service.api-d “name=service.api”-d “host=upstream.api”//{ “host”: “upstream.api”,//绑定的upstream “created_at”: 1553663485, “connect_timeout”: 60000, “id”: “5b93eda7-7ba5-4acc-a536-cf12f58a1144”,//service.id “protocol”: “http”, “name”: “service.api”, “read_timeout”: 60000, “port”: 80, “path”: “/api/v1”, “updated_at”: 1553663485, “retries”: 5, “write_timeout”: 60000}等同于http { server { listen 8000; location waiting-for-define { proxy_pass http://upstream.api; } }}routes为服务service.api绑定路由。需要理解,route并非一条url,它是kong的路由服务,可以为某个kong服务管理管理一套路由集合,route就相当于 http > server 中的 location 规则集合。#为service.api添加路由集合curl -X POST localhost:8001/routes -d “name=route.api” -d “paths[]=/api/v1” -d “paths[]=/api/v2” -d “paths[]=/api/v3” -d “hosts[]=api.service.com” -d “hosts[]=service.com” -d “service.id=5b93eda7-7ba5-4acc-a536-cf12f58a1144”#或者通过 services 的接口curl -X POST localhost:8001/services/service.api/routes -d “name=route.api” -d “paths[]=/api/v1” -d “paths[]=/api/v2” -d “paths[]=/api/v3” -d “hosts[]=localhost” -d “hosts[]=api.service.com” -d “hosts[]=service.com” \我们还同时指定了hosts,相当于server_name,顺手把虚拟主机也做了。大致等同于如下配置:http { server { listen 8000; server_name localhost api.service.com service.com; location /api/v1 { proxy_pass http://upstream.api; } location /api/v2 { proxy_pass http://upstream.api; } location /api/v3 { proxy_pass http://upstream.api; } }}这样我们可以通过localhost:8000/api/v1api.service.com:8000/api/v2service.com:8000/api/v3来访问 service.api服务,此服务后端有两台服务器做 LB。 ...

March 27, 2019 · 1 min · jiezi

在阿里云上单机使用Nginx负载均衡发布网站

恕我见识短浅,不知道Nginx有Windows版本,可以运行在Windows系统下,当初遇到Socket并发压力增长,学习负载均衡的时候,找的是LVS的4层负载均衡,没有考虑7层负载均衡,所以一直不了解Nginx的Windows版本。今天写此教程,就是为了告诉大家怎么在Windows下配置Nginx。首先说,Nginx正常用法应当是在 网站发布在多机器上,实现网站压力大的时候,增加网站的负载能力和提高可用性能。本文选择单机发布多站点,首先是为了学习下Nginx的配置,其次是可以这样来实现网站的“高可用”。对于正式业务,可以选购阿里云的负载均衡SLB产品,不应当使用这种单机多站点的发布方法。本文将使用阿里云的ECS作为测试站点,使用条件:ECS有公网IP,使用Windows系统并装有IIS服务,安全组和防火墙提前放行80端口。【准备工作】首先在IIS里,建立几个的80之外的端口的临时网站(不要占用80端口,因为一会要把80端口给Nginx用):只有一个首页,内容分别是编号 web01、web02、web03……发布端口分别设置为81、82、83……在阿里云ECS内部使用浏览器检查下(http://127.0.0.1:81、http://127.0.0.1:82、http://127.0.0.1:83……),能分别看到各自的内容表示演示站点搭建无误【下载Nginx 1.15.9(windows版本)】官网地址:http://nginx.org/download/ngi… (需要其他版本请访问 http://nginx.org/en/download…. 自行寻找)【配置过程】使用远程桌面连接登录到阿里云服务器上,将Nginx文件下载到在服务器上解压缩,我的路径是 C:webnginx-1.15.9找到配置文件 C:webnginx-1.15.9conf 目录下的 nginx.conf 使用记事本(推荐notepad++)打开,这里就是配置文件,需要对新手说明一下的是,前面带有#的表示注释。1,添加配置,把前面准备好的几个站点放进去这里是自己添加的,本文单机演示,同ip不同端口,实际应用环境应该是不同内网ip,相同端口upstream linuxidc {server 127.0.0.1:81; server 127.0.0.1:82; server 127.0.0.1:83; #如果还有其他站点(机器),在这里添加即可}2,找到 location 配置节点,添加 proxy_pass 节点,内容配置为 http://linuxidc; (linuxidc是upstream的节点名称)location / {root html;index index.html index.htm;#添加转发配置,这里的 linuxidc是upstream的节点名称proxy_pass http://linuxidc;}然后访问下,在远程桌面里,打开系统自带的浏览器,反复刷新访问下Nginx的端口 127.0.0.1:80 ,会看到不同的站点的内容,说明Nginx配置成功3,最后一步,在自己其他的设备上,使用浏览器访问阿里云ECS的公网IP,看下是否跟第二步的结果相同,能正常打开表示网站发布完成!【写在最后】本文只做了最简单的入门教程,教大家使用Nginx实现网站的负载均衡最基础的部分,通过配置文件,可以看到其他配置信息:设置错误页面,设置文件访问权限,绑定SSL证书,配置不同节点的权重等等,这些还需要进一步试验学习原文地址: https://www.opengps.cn/Blog/V… 文章的更新编辑依此链接为准。欢迎关注源站原创文章!

March 25, 2019 · 1 min · jiezi

负载均衡算法 — 平滑加权轮询

首发于 樊浩柏科学院在 负载均衡算法 — 轮询 一文中,我们就指出了加权轮询算法一个明显的缺陷。即在某些特殊的权重下,加权轮询调度会生成不均匀的实例序列,这种不平滑的负载可能会使某些实例出现瞬时高负载的现象,导致系统存在宕机的风险。为了解决这个调度缺陷,就提出了 平滑加权轮询 调度算法。待解决的问题为了说明平滑加权轮询调度的平滑性,使用以下 3 个特殊的权重实例来演示调度过程。服务实例权重值192.168.10.1:22025192.168.10.2:22021192.168.10.3:22021我们已经知道通过 加权轮询 算法调度后,会生成如下不均匀的调度序列。请求选中的实例1192.168.10.1:22022192.168.10.1:22023192.168.10.1:22024192.168.10.1:22025192.168.10.1:22026192.168.10.2:22027192.168.10.3:2202接下来,我们就使用平滑加权轮询算法调度上述实例,看看生成的实例序列如何?算法描述假设有 N 台实例 S = {S1, S2, …, Sn},配置权重 W = {W1, W2, …, Wn},有效权重 CW = {CW1, CW2, …, CWn}。每个实例 i 除了存在一个配置权重 Wi 外,还存在一个当前有效权重 CWi,且 CWi 初始化为 Wi;指示变量 currentPos 表示当前选择的实例 ID,初始化为 -1;所有实例的配置权重和为 weightSum;那么,调度算法可以描述为:1、初始每个实例 i 的 当前有效权重 CWi 为 配置权重 Wi,并求得配置权重和 weightSum;2、选出 当前有效权重 最大 的实例,将 当前有效权重 CWi 减去所有实例的 权重和 weightSum,且变量 currentPos 指向此位置;3、将每个实例 i 的 当前有效权重 CWi 都加上 配置权重 Wi;4、此时变量 currentPos 指向的实例就是需调度的实例;5、每次调度重复上述步骤 2、3、4;上述 3 个服务,配置权重和 weightSum 为 7,其调度过程如下:请求选中前的当前权重currentPos选中的实例选中后的当前权重1{5, 1, 1}0192.168.10.1:2202{-2, 1, 1}2{3, 2, 2}0192.168.10.1:2202{-4, 2, 2}3{1, 3, 3}1192.168.10.2:2202{1, -4, 3}4{6, -3, 4}0192.168.10.1:2202{-1, -3, 4}5{4, -2, 5}2192.168.10.3:2202{4, -2, -2}6{9, -1, -1}0192.168.10.1:2202{2, -1, -1}7{7, 0, 0}0192.168.10.1:2202{0, 0, 0}8{5, 1, 1}0192.168.10.1:2202{-2, 1, 1}可以看出上述调度序列分散是非常均匀的,且第 8 次调度时当前有效权重值又回到 {0, 0, 0},实例的状态同初始状态一致,所以后续可以一直重复调度操作。此轮询调度算法思路首先被 Nginx 开发者提出,见 phusion/nginx 部分。代码实现这里使用 PHP 来实现,源码见 fan-haobai/load-balance 部分。class SmoothWeightedRobin implements RobinInterface{ private $services = array(); private $total; private $currentPos = -1; public function init(array $services) { foreach ($services as $ip => $weight) { $this->services[] = [ ‘ip’ => $ip, ‘weight’ => $weight, ‘current_weight’ => $weight, ]; } $this->total = count($this->services); } public function next() { // 获取最大当前有效权重实例的位置 $this->currentPos = $this->getMaxCurrentWeightPos(); // 当前权重减去权重和 $currentWeight = $this->getCurrentWeight($this->currentPos) - $this->getSumWeight(); $this->setCurrentWeight($this->currentPos, $currentWeight); // 每个实例的当前有效权重加上配置权重 $this->recoverCurrentWeight(); return $this->services[$this->currentPos][‘ip’]; }}其中,getSumWeight()为所有实例的配置权重和;getCurrentWeight()和 setCurrentWeight()分别用于获取和设置指定实例的当前有效权重;getMaxCurrentWeightPos()求得最大当前有效权重的实例位置,实现如下:public function getMaxCurrentWeightPos(){ $currentWeight = $pos = 0; foreach ($this->services as $index => $service) { if ($service[‘current_weight’] > $currentWeight) { $currentWeight = $service[‘current_weight’]; $pos = $index; } } return $pos;}recoverCurrentWeight()用于调整每个实例的当前有效权重,即加上配置权重,实现如下:public function recoverCurrentWeight(){ foreach ($this->services as $index => &$service) { $service[‘current_weight’] += $service[‘weight’]; }}需要注意的是,在配置services服务列表时,同样需要指定其权重:$services = [ ‘192.168.10.1:2202’ => 5, ‘192.168.10.2:2202’ => 1, ‘192.168.10.3:2202’ => 1,];数学证明可惜的是,关于此调度算法严谨的数学证明少之又少,不过网友 tenfy 给出的 安大神 证明过程,非常值得参考和学习。证明权重合理性假如有 n 个结点,记第 i 个结点的权重是 $x_i$,设总权重为 $S = x_1 + x_2 + … + x_n$。选择分两步:1、为每个节点加上它的权重值;2、选择最大的节点减去总的权重值;n 个节点的初始化值为 [0, 0, …, 0],数组长度为 n,值都为 0。第一轮选择的第 1 步执行后,数组的值为 $[x_1, x_2, …, x_n]$。假设第 1 步后,最大的节点为 j,则第 j 个节点减去 S。所以第 2 步的数组为 $[x_1, x_2, …, x_j-S, …, x_n]$。 执行完第 2 步后,数组的和为:$x_1 + x_2 + … + x_j-S + … + x_n => x_1 + x_2 + … + x_n - S = S - S = 0$由此可见,每轮选择第 1 步操作都是数组的总和加上 S,第 2 步总和再减去 S,所以每轮选择完后的数组总和都为 0。假设总共执行 S 轮选择,记第 i 个结点选择 $m_i$ 次。第 i 个结点的当前权重为 $w_i$。 假设节点 j 在第 t 轮(t < S)之前,已经被选择了 $x_j$ 次,记此时第 j 个结点的当前权重为 $w_j = t * x_j - x_j * S = (t - S) * x_j < 0$, 因为 t 恒小于 S,所以 $w_j < 0$。前面假设总共执行 S 轮选择,则剩下 S-t 轮 j 都不会被选中,上面的公式 $w_j = (t - S) * x_j + (S - t) * x_j = 0$。 所以在剩下的选择中,$w_j$ 永远小于等于 0,由于上面已经证明任何一轮选择后,数组总和都为 0,则必定存在一个节点 k 使得 $w_k > 0$,永远不会再选中节点 j。由此可以得出,第 i 个结点最多被选中 $x_i$ 次,即 $m_i <= x_i$。因为 $S = m_1 + m_2 + … + m_n$ 且 $S = x_1 + x_2 + … + x_n$。 所以,可以得出 $m_i == x_i$。证明平滑性证明平滑性,只要证明不要一直都是连续选择那一个节点即可。跟上面一样,假设总权重为 S,假如某个节点 i 连续选择了 t($t < x_i$) 次,只要存在下一次选择的不是节点 i,即可证明是平滑的。假设 $t = x_i - 1$,此时第 i 个结点的当前权重为 $w_i = t * x_i - t * S = (x_i - 1) * x_i - (x_i - 1) * S$。证明下一轮的第 1 步执行完的值 $w_i + x_i$ 不是最大的即可。$w_i + x_i => (x_i - 1) * x_i - (x_i - 1) * S + x_i =>$$x_i^2 - x_i * S + S => (x_i - 1) * (x_i - S) + x_i$因为 $x_i$ 恒小于 S,所以 $x_i - S <= -1$。 所以上面:$(x_i - 1) * (x_i - S) + x_i <= (x_i - 1) * -1 + x_i = -x_i + 1 + x_i = 1$所以第 t 轮后,再执行完第 1 步的值 $w_i + x_i <= 1$。如果这 t 轮刚好是最开始的 t 轮,则必定存在另一个结点 j 的值为 $x_j * t$,所以有 $w_i + x_i <= 1 < 1 * t < x_j * t$。所以下一轮肯定不会选中 i。总结尽管,平滑加权轮询算法改善了加权轮询算法调度的缺陷,即调度序列分散的不均匀,避免了实例负载突然加重的可能,但是仍然不能动态感知每个实例的负载。若由于实例权重配置不合理,或者一些其他原因加重系统负载的情况,平滑加权轮询都无法实现每个实例的负载均衡,这时就需要 有状态 的调度算法来完成。相关文章 »负载均衡算法 — 轮询(2018-11-29) ...

March 25, 2019 · 3 min · jiezi

架构设计脑图总结

xmind源件下载

March 20, 2019 · 1 min · jiezi

Kubernetes Ingress 日志分析与监控的最佳实践

摘要: Ingress主要提供HTTP层(7层)路由功能,是目前K8s中HTTP/HTTPS服务的主流暴露方式。为简化广大用户对于Ingress日志分析与监控的门槛,阿里云容器服务和日志服务将Ingress日志打通,只需要应用一个yaml资源即可完成日志采集、分析、可视化等一整套Ingress日志方案的部署。前言目前Kubernetes(K8s)已经真正地占领了容器编排市场,是默认的云无关计算抽象,越来越多的企业开始将服务构建在K8s集群上。在K8s中,组件通过Service对外暴露服务,常见的包括NodePort、LoadBalancer、Ingress等。其中Ingress主要提供HTTP层(7层)路由功能,相比TCP(4层)的负载均衡具备非常多的优势(路由规则更加灵活、支持金丝雀、蓝绿、A/B Test发布模式、SSL支持、日志、监控、支持自定义扩展等),是目前K8s中HTTP/HTTPS服务的主流暴露方式。Ingress简介K8s中Ingress只是一种API资源的声明,具体的实现需要安装对应的Ingress Controller,由Ingress Controller接管Ingress定义,将流量转发到对应的Service。目前Ingress Controller的实现有非常多种(具体可以参考Ingress Controller官方文档),比较流行的有Nginx、Traefik、Istio、Kong等,在国内接受度最高的是Nginx Ingress Controller。日志与监控日志和监控是所有Ingress Controller都会提供的基础功能,日志一般包括访问日志(Access Log)、控制日志(Controller Log)和错误日志(Error Log),监控主要从日志以及Controller中提取部分Metric信息。这些数据中访问日志的量级最大、信息最多、价值也最高,一般7层的访问日志包括:URL、源IP、UserAgent、状态码、入流量、出流量、响应时间等,对于Ingress Controller这种转发型的日志,还包括转发的Service名、Service响应时间等额外信息。从这些信息中,我们能够分析出非常多的信息,例如:网站访问的PV、UV;访问的地域分布、设备端分布;网站访问的错误比例;后端服务的响应延迟;不同URL访问分布。我们的开发、运维、运营、安全等人员可以基于这些信息完成各自的需求,例如:新老版本发布前后的数据指标对比;网站质量监控、集群状态监控;恶意攻击检测、反作弊;网站访问量统计、广告转化率统计。然而手动搭建、运维一整套的Ingress日志分析与监控系统非常复杂,系统所需要的模块有:部署日志采集Agent并配置采集、解析规则;由于K8s集群中,访问量相对较大,因此需要搭建一个缓冲队列,例如Redis、Kafka等;部署实时数据分析引擎,例如Elastic Search、clickhouse等;部署可视化组件并搭建报表,例如grafana、kibana等;部署告警模块并配置告警规则,例如ElastAlert、alertmanager等。阿里云日志服务Ingress解决方案为简化广大用户对于Ingress日志分析与监控的门槛,阿里云容器服务和日志服务将Ingress日志打通(官方文档),只需要应用一个yaml资源即可完成日志采集、分析、可视化等一整套Ingress日志方案的部署。Ingress可视化分析日志服务默认为Ingress创建5个报表,分别是:Ingress概览、Ingress访问中心、Ingress监控中心、Ingress蓝绿发布监控中心、Ingress异常检测中心。不同角色的人员可根据需求使用不同的报表,同时每个报表均提供筛选框用于筛选特定的Service、URL、状态码等。所有的报表均基于日志服务提供的基础可视化组件实现,可根据公司实际场景进行定制化调整。Ingress概览Ingress概览报表主要展示当前Ingress的整体状态,主要包括以下几类信息:整体架构状态(1天),包括:PV、UV、流量、响应延迟、移动端占比、错误比例等;网站实时状态(1分钟),包括:PV、UV、成功率、5XX比例、平均延迟、P95/P99延迟等;用户请求类信息(1天),包括:1天/7天访问PV对比、访问地域分布、TOP访问省份/城市、移动端占比、Android/IOS占比等;TOPURL统计(1小时),包括:访问TOP10、延迟TOP10、5XX错误TOP10、404错误TOP10。Ingress访问中心Ingress访问中心主要侧重于用于访问请求相关的统计信息,一般用于运营分析,包括:当日UV/PV、UV/PV分布、UV/PV趋势、TOP访问省份/城市、TOP访问浏览器、TOP访问IP、移动端占比、Android/IOS占比等。Ingress监控中心Ingress监控中心主要侧重于网站实时监控数据,一般用于实时监控与告警,包括:请求成功率、错误比例、5XX比例、请求未转发比例、平均延迟、P95/P99/P9999延迟、状态码分布、Ingress压力分布、Service访问TOP10、Service错误TOP10、Service延迟TOP10、Service流量TOP10等。Ingress蓝绿发布监控中心Ingress蓝绿发布监控中心主要用于版本发布时的实时监控与对比(版本前后对比以及蓝绿版本当前对比),以便在服务发布时快速检测异常并进行回滚。在该报表中需要选择进行对比的蓝绿版本(ServiceA和ServiceB),报表将根据选择动态显示蓝绿版本相关指标,包括:PV、5XX比例、成功率、平均延迟、P95/P99/P9999延迟、流量等。Ingress异常检测中心Ingress异常检测中心基于日志服务提供的机器学习算法,通过多种时序分析算法从Ingress的指标中自动检测异常点,提高问题发现的效率。实时监控与告警Ingress作为K8s网站请求的主要入口,实时监控与告警是必不可少的Ops手段之一。在日志服务上,基于上述的报表,只需3个简单的步骤即可完成告警的创建。下述示例为Ingress配置5XX比例的告警,告警每5分钟执行一次,当5XX比例超过1%时触发。除了通用的告警功能外,日志服务还额外支持:多维度数据关联,即通过多组SQL结果交叉判断进行告警,增加告警准确度;除支持短信、语音、通知中心、email外,还支持钉钉机器人通知、自定义WebHook扩展;告警的记录也以日志的形式记录,可以实现对告警失败进行告警的双保险。订阅报告日志服务除支持通过告警方式通知外,还支持报表订阅功能,可使用该功能将报表定期渲染成图片并通过邮件、钉钉群等方式发送。例如每天早上10点向运营群中发送昨日网站访问情况、每周发送报告到邮件组中存档、新版本发布时每5分钟发送一次监控报表…自定义分析如果容器服务Kubernetes版提供的默认报表无法满足你的分析需求,可以直接使用日志服务SQL、仪表盘等功能进行自定义的分析和可视化。尝鲜为了让大家可以体验Kubernetes审计日志功能,我们特别开通了体验中心,大家可以通过 https://promotion.aliyun.com/ntms/act/logdoclist.html 进入,该页面提供了非常多和Kubernetes相关的报表。参考文档阿里云日志服务阿里云容器服务Kubernetes版Ingress日志分析与监控告警配置订阅报表Ingress官方文档Ingress Controller官方文档一站式开发者服务,海量学习资源0元起!阿里热门开源项目、机器学习干货、开发者课程/工具、小微项目、移动研发等海量资源;更有开发者福利Kindle、技术图书幸运抽奖,100%中–》https://www.aliyun.com/acts/product-section-2019/developer?utm_content=g_1000047140本文作者:元乙阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 19, 2019 · 1 min · jiezi

时速云中标华数传媒基于容器的自动化运维平台项目

继时速云中标华数数字电视传媒集团有限公司(简称华数传媒)容器云平台项目之后,2019年2月,时速云再次中标华数传媒基于容器的自动化运维平台项目。中国新媒体产业发展第一方阵华数传媒是大型国有文化传媒产业集团,位居中国新媒体产业发展第一方阵。华数传媒紧抓国家推进传统媒体与新兴媒体融合发展的战略契机,以“新网络+应用”、“新媒体+内容”、“大数据+开发”三大发展战略为指导,以打造“智慧华数、融合媒体”为目标,积极构建新网络、新媒体、云计算和大数据、原创内容以及智慧化等产业板块,全面加快向智慧广电综合运营商和数字经济发展主体转型。目前,华数集团拥有有线网络业务、手机电视与互联网电视等全国新媒体业务以及宽带网络业务方面的运营牌照,覆盖海量传统媒体和新媒体用户,包括3000万有线电视家庭用户、1亿互联网电视覆盖用户、5600万华数手机电视用户。华数传媒客户群体庞大,拥有大量的基础设施,存在管理难度大,开发和集成速度慢,难以维护和发展,升级服务时启动慢,无感知升级困难,升级时环境易被破坏等问题。为减少运维人员、开发人员、测试人员工作量,减少反复冗杂的开发、测试、升级流程,华数传媒需要基于容器技术,打造新一代自动化运维平台,并于2019年年初对该项目进行公开招标。华数传媒基于容器的自动化运维平台在功能及性能方面需满足以下要求:功能要求:1、集成开发环境,实行自动化测试,灰度发布,正式发布2、减少升级、配置文件修改等操作时间,提升版本发布速率3、对于应用版本镜像的管理4、实现故障自动化隔离、自动化恢复5、服务器压力阈值自动化扩容性能要求:1、整体服务性能提升10-50%2、安装完成所需插件(Docker)后,服务器整体资源占用率小于5%再次中标,时速云在华数传媒基于容器的自动化运维平台项目招标中脱颖而出时速云是国内领先的容器云计算服务提供商,业务涵盖容器 PaaS 平台、DevOps、微服务治理、AIOps 等领域。拥有金融、能源、运营商、制造、广电、汽车等领域的诸多大型企业及世界500强客户。2018年,时速云曾中标华数传媒容器云平台项目,对基于 Docker 容器技术的 PaaS 平台进行开发部署,以便承载华数互动电视及新媒体业务系统,同时提供快速弹性伸缩、应用管理、监控运行等能力。此次,时速云再次凭借过硬的技术实力、高品质的服务能力以及丰富的实践经验,在华数传媒基于容器的自动化运维平台项目的招标中脱颖而出。通过容器云计算助力企业数字化转型是时速云的核心使命。目前,时速云已经为国家电网、新奥集团、戴姆勒奔驰、铂涛等众多知名企业成功交付了容器云 PaaS 平台及相关产品。再次中标华数传媒基于容器的自动化运维平台项目,是客户对时速云技术、产品及服务的又一次认可和肯定。此后,时速云将不断加大研发投入,把握行业发展机遇,为企业数字化、智能化转型提供更高品质的产品和服务,为企业创新、可持续发展保驾护航。

March 12, 2019 · 1 min · jiezi

Nginx快速上手

Nginx快速上手根据实际的应用需要,学习要用到的Nginx的知识,以快速上手、理解并掌握Nginx一:Nginx简介包括:Nginx是什么、能干什么、特点二:Nginx安装和基本使用包括:源码安装、安装配置选项、基本使用三:Nginx基本配置包括:结合示例的配置文件,熟悉Nginx的基本配置四:学习核心模块、日志模块和事件模块的常用指令五:学习Http模块的常用配置和指令Nginx简介Nginx是什么Nginx是一款轻量级的Web服务器,也是一款轻量级的反向代理服务器。Nginx能干什么Nginx能干的事情很多,这里简要罗列一些:1:直接支持Rails和PHP的程序2:作为HTTP反向代理服务器3:作为负载均衡服务器4:作为邮件代理服务器5:帮助实现前端动静分离……Nginx特点高稳定、高性能、资源占用少、功能丰富、模块化结构、支持热部署Nginx安装源码安装演示环境:CentOS6.51:需要gcc,系统自带了,没有的话,需要先安装2:需要pcre,安装的命令示例如下: yum install pcre3:需要zlib,安装的命令示例如下:yum install zlib zlib-devel4:如果需要支持ssl的话,安装OpenSSL,安装的命令示例如下:yum install openssl openssl-devel5:上http://nginx.org/去下载源码包,然后进行解压安装,示例如下:(1)先解压源码包,然后进入到这个包里面(2)安装命令示例如下:第一步:./configure –prefix=/usr/common/nginx –withhttp_stub_status_module –with-http_ssl_module如果提示确少啥,就加上相应的选项,比如缺少pcre的话,就加上–with-pcre=/usr/common/temp/pcre-8.34 (当然我们这里是不缺东西的)第二步:配置后就依次make , make install常见的Nginx安装配置选项-1编译参数可能会根据版本的不同进行变化,./configure –help查看编译参数列表,常见的选项如下:–prefix=<path> - 安装路径,如果没有指定,默认为/usr/local/nginx。–sbin-path=<path> - nginx可执行命令的文件,如果没有指定,默认为<prefix>/sbin/nginx。–conf-path=<path> - 在没有使用-c参数指定的情况下nginx.conf的默认位置,如果没有指定,默认为<prefix>/conf/nginx.conf。–pid-path=<path> - nginx.pid的路径,如果没有在nginx.conf中通过“pid”指令指定,默认为<prefix>/logs/nginx.pid。–lock-path=<path> - nginx.lock文件路径,如果没有指定,默认为<prefix>/logs/nginx.lock。–error-log-path=<path> - 当没有在nginx.conf中使用“error_log”指令指定时的错误日志位置,如果没有指定,默认为<prefix>/logs/error.log。–http-log-path=<path> - 当没有在nginx.conf中使用“access_log”指令指定时的访问日志位置,如果没有指定,默认为<prefix>/logs/access.log。–user=<user> - 当没有在nginx.conf中使用“user”指令指定时nginx运行的用户,如果没有指定,默认为“nobody”。–group=<group> - 当没有在nginx.conf中使用“user”指令指定时nginx运行的组,如果没有指定,默认为“nobody”。–builddir=DIR - 设置构建目录。–with-rtsig_module - 启用rtsig模块。常见的Nginx安装配置选项-2–with-select_module –without-select_module - 如果在configure的时候没有发现kqueue, epoll,rtsig或/dev/poll其中之一,select模块始终为启用状态。–with-poll_module –without-poll_module - 如果在configure的时候没有发现kqueue, epoll,rtsig或/dev/poll其中之一,poll模块始终为启用状态。–with-http_ssl_module - 启用ngx_http_ssl_module,启用SSL支持并且能够处理HTTPS请求。需要OpenSSL,在Debian系统中,对应的包为libssl-dev。–with-http_realip_module - 启用ngx_http_realip_module–with-http_addition_module - 启用ngx_http_addition_module–with-http_sub_module - 启用ngx_http_sub_module–with-http_dav_module - 启用ngx_http_dav_module–with-http_flv_module - 启用ngx_http_flv_module–with-http_stub_status_module - 启用”server status”(服务状态)页–without-http_charset_module - 禁用ngx_http_charset_module–without-http_gzip_module - 禁用ngx_http_gzip_module,如果启用,需要zlib包。–without-http_ssi_module - 禁用ngx_http_ssi_module–without-http_userid_module - 禁用ngx_http_userid_module–without-http_access_module - 禁用ngx_http_access_module–without-http_auth_basic_module - 禁用ngx_http_auth_basic_module常见的Nginx安装配置选项-3–without-http_autoindex_module - 禁用ngx_http_autoindex_module–without-http_geo_module - 禁用ngx_http_geo_module–without-http_map_module - 禁用ngx_http_map_module–without-http_referer_module - 禁用ngx_http_referer_module–without-http_rewrite_module - 禁用ngx_http_rewrite_module。如果启用,需要PCRE包。–without-http_proxy_module - 禁用ngx_http_proxy_module–without-http_fastcgi_module - 禁用ngx_http_fastcgi_module–without-http_memcached_module - 禁用ngx_http_memcached_module–without-http_limit_zone_module - 禁用ngx_http_limit_zone_module–without-http_empty_gif_module - 禁用ngx_http_empty_gif_module–without-http_browser_module - 禁用ngx_http_browser_module–without-http_upstream_ip_hash_module - 禁用ngx_http_upstream_ip_hash_module–with-http_perl_module - 启用ngx_http_perl_module–with-perl_modules_path=PATH - 为perl模块设置路径–with-perl=PATH - 为perl库设置路径–http-client-body-temp-path=PATH - 为http连接的请求实体临时文件设置路径,如果没有指定,默认为<prefix>/client_body_temp常见的Nginx安装配置选项-4–http-proxy-temp-path=PATH - 为http代理临时文件设置路径,如果没有指定,默认为<prefix>/proxy_temp–http-fastcgi-temp-path=PATH - 为http fastcgi临时文件设置路径,如果没有指定,默认为<prefix>/fastcgi_temp–without-http - 禁用HTTP服务–with-mail - 启用IMAP4/POP3/SMTP代理模块–with-mail_ssl_module - 启用ngx_mail_ssl_module–with-cc=PATH - 设置C编译器路径–with-cpp=PATH - 设置C预处理器路径–with-cc-opt=OPTIONS - 变量CFLAGS中附加的参数,用于FreeBSD中的PCRE库,同样需要指定–withcc-opt=”-I /usr/local/include”,如果我们使用select()函数则需要同时增加文件描述符数量,可以通过–with-cc-opt=”-D FD_SETSIZE=2048”指定。–with-ld-opt=OPTIONS - 通过连接器的附加参数,用于FreeBSD中的PCRE库,同样需要指定–withld-opt=”-L /usr/local/lib”。–with-cpu-opt=CPU - 指定编译的CPU,可用的值为: pentium, pentiumpro, pentium3, pentium4,athlon, opteron, amd64, sparc32, sparc64, ppc64–without-pcre - 禁用PCRE库文件,同时将禁用HTTP rewrite 模块,如果要在”location”指令中使用正则表达式,同样需要PCRE库。常见的Nginx安装配置选项-5–with-pcre=DIR - 设置PCRE库源文件路径。–with-pcre-opt=OPTIONS - 在编译时为PCRE设置附加参数。–with-md5=DIR - 设置md5库源文件路径。–with-md5-opt=OPTIONS - 在编译时为md5设置附加参数。–with-md5-asm - 使用md5汇编源。–with-sha1=DIR - 设置sha1库源文件路径。–with-sha1-opt=OPTIONS - 在编译时为sha1设置附加参数。–with-sha1-asm - 使用sha1汇编源。–with-zlib=DIR - 设置zlib库源文件路径。–with-zlib-opt=OPTIONS - 在编译时为zlib设置附加参数。–with-zlib-asm=CPU - 为指定的CPU使用zlib汇编源进行优化,可用值为: pentium, pentiumpro。–with-openssl=DIR - 设置openssl库源文件路径。–with-openssl-opt=OPTIONS - 在编译时为openssl设置附加参数。–with-debug - 启用debug记录。–add-module=PATH - 增加一个在PATH中的第三方模块。Nginx的基本运行测试配置文件:安装路径下的/nginx/sbin/nginx -t启动:安装路径下的/nginx/sbin/nginx停止安装路径下的/nginx/sbin/nginx -s stop或者是: nginx -s quit重启安装路径下的/nginx/sbin/nginx -s reload查看进程ps -ef |grep nginx安装过后,如果从外面访问不了,多半是被防火墙挡住了,可以关闭掉防火墙:/sbin/service iptables stopNginx的基本配置-1默认启动Nginx时,使用的配置文件是: 安装路径/conf/nginx.conf 文件可以在启动nginx的时候,通过-c来指定要读取的配置文件常见的配置文件有如下几个:nginx.conf:应用程序的基本配置文件mime.types:MIME类型关联的扩展文件fastcgi.conf:与fastcgi相关的配置proxy.conf:与proxy相关的配置sites.conf:配置Nginx提供的网站,包括虚拟主机Nginx的进程结构启动Nginx的时候,会启动一个Master进程,这个进程不处理任何客户端的请求,主要用来产生worker进程,一个worker进程用来处理一个request。Nginx模块分为:核心模块、事件模块、标准Http模块、可选Http模块、邮件模块、第三方模块和补丁等Nginx的基本配置-2Nginx基本模块:所谓基本模块,指的是Nginx默认的功能模块,它们提供的指令,允许你使用定义Nginx基本功能的变量,在编译的时候不能被禁用,包括:核心模块:基本功能和指令,如进程管理和安全事件模块:在Nginx内配置网络使用的能力配置模块:提供包含机制常见的核心模块指令,大部分都是放置在配置文件的顶部具体的指令,请参看nginx的官方文档,非常详细,参见:http://nginx.org/en/docs/ngx_…还有下面这个网站,也是非常不错的:http://www.howtocn.org/nginx:directiveindex常见的events模块指令,大部分都是放置在配置文件的顶部具体的指令,在上面那个文档里面,命令的context为events的就是events模块指令,只能在events里面使用Nginx常用的核心模块指令核心模块指令,重点看看:error_log、include、pid、user、worker_cpu_affinity、worker_processeserror_log日志有6个级别:debug|info|notice|warn|error|critNginx支持将不同的虚拟主机的日志记录在不同的地方,如下示例:http{error_log logs/http_error.log error;server{server_name one;access_log logs/one_access.log;error_log logs/one_error.log error;}server{server_name two;access_log logs/two_access.log;error_log logs/two_error.log error;}}注意:error_log off不是禁用日志,而是创建一个名为off的日志,要禁用日志,可以这么写:error_log/dev/null crit;Nginx常用的日志模块、事件模块指令日志模块指令,几个都看看事件模块指令,重点看看:use和worker_connectionsNginx的HTTP基本配置Nginx的HTTP配置主要包括三个区块,结构如下:http { //这个是协议级别include mime.types;default_type application/octet-stream;keepalive_timeout 65;gzip on;server { //这个是服务器级别listen 80;server_name localhost;location / { //这个是请求级别root html;index index.html index.htm;}}}Nginx的HTTP核心模块,包括大量的指令和变量,大都很重要,具体参见:http://nginx.org/en/docs/http…Location区段-1Location区段,通过指定模式来与客户端请求的URI相匹配,基本语法如下:location [=|||^|@] pattern{……}1:没有修饰符表示:必须以指定模式开始,如:server {server_name sishuok.com;location /abc {……}}那么,如下是对的:http://sishuok.com/abchttp://sishuok.com/abc?p1=11&…http://sishuok.com/abc/http://sishuok.com/abcdeLocation区段-22:= 表示:必须与指定的模式精确匹配,如:server {server_name sishuok.com;location = /abc {……}}那么,如下是对的:http://sishuok.com/abchttp://sishuok.com/abc?p1=11&…如下是错的:http://sishuok.com/abc/http://sishuok.com/abcdeLocation区段-33: 表示:指定的正则表达式要区分大小写,如:server {server_name sishuok.com;location ~ ^/abc$ {……}}那么,如下是对的:http://sishuok.com/abchttp://sishuok.com/abc?p1=11&…如下是错的:http://sishuok.com/ABChttp://sishuok.com/abc/http://sishuok.com/abcdeLocation区段-44:* 表示:指定的正则表达式不区分大小写,如:server {server_name sishuok.com;location * ^/abc$ {……}}那么,如下是对的:http://sishuok.com/abchttp://sishuok.com/ABChttp://sishuok.com/abc?p1=11&…如下是错的:http://sishuok.com/abc/http://sishuok.com/abcdeLocation区段-55:^ 类似于无修饰符的行为,也是以指定模式开始,不同的是,如果模式匹配,那么就停止搜索其他模式了。6:@ :定义命名location区段,这些区段客户段不能访问,只可以由内部产生的请求来访问,如try_files或error_page等查找顺序和优先级1:带有“=“的精确匹配优先2:没有修饰符的精确匹配3:正则表达式按照他们在配置文件中定义的顺序4:带有“^”修饰符的,开头匹配5:带有“” 或“” 修饰符的,如果正则表达式与URI匹配6:没有修饰符的,如果指定字符串与URI开头匹配Location区段匹配示例location = / {只匹配/ 的查询.[ configuration A ]}location / {匹配任何以/ 开始的查询,但是正则表达式与一些较长的字符串将被首先匹配。[ configuration B ]}location ^~ /images/ {匹配任何以/images/ 开始的查询并且停止搜索,不检查正则表达式。[ configuration C ]}location ~ .(gif|jpg|jpeg)$ {匹配任何以gif, jpg, or jpeg结尾的文件,但是所有/images/ 目录的请求将在Configuration C中处理。[ configuration D ]}各请求的处理如下例:■/ → configuration A■/documents/document.html → configuration B■/images/1.gif → configuration C■/documents/1.jpg → configuration D ...

March 12, 2019 · 2 min · jiezi

前端开发者必备的Nginx知识

nginx在应用程序中的作用解决跨域请求过滤配置gzip负载均衡静态资源服务器nginx是一个高性能的HTTP和反向代理服务器,也是一个通用的TCP/UDP代理服务器,最初由俄罗斯人Igor Sysoev编写。nginx现在几乎是众多大型网站的必用技术,大多数情况下,我们不需要亲自去配置它,但是了解它在应用程序中所担任的角色,以及如何解决这些问题是非常必要的。下面我将从nginx在企业中的真实应用来解释nginx在应用程序中起到的作用。为了便于理解,首先先来了解一下一些基础知识,nginx是一个高性能的反向代理服务器那么什么是反向代理呢?正向代理与反向代理代理是在服务器和客户端之间假设的一层服务器,代理将接收客户端的请求并将它转发给服务器,然后将服务端的响应转发给客户端。不管是正向代理还是反向代理,实现的都是上面的功能。正向代理正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。正向代理是为我们服务的,即为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。正向代理对我们是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。反向代理 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。反向代理对服务端是透明的,对我们是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。基本配置配置结构下面是一个nginx配置文件的基本结构:events { }http { server { location path { … } location path { … } } server { … }}main:nginx的全局配置,对全局生效。events:配置影响nginx服务器或与用户的网络连接。http:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。server:配置虚拟主机的相关参数,一个http中可以有多个server。location:配置请求的路由,以及各种页面的处理情况。upstream:配置后端服务器具体地址,负载均衡配置不可或缺的部分。内置变量下面是nginx一些配置中常用的内置全局变量,你可以在配置的任何位置使用它们。| 变量名 | 功能 | | —— | —— | | $host| 请求信息中的Host,如果请求中没有Host行,则等于设置的服务器名 || $request_method | 客户端请求类型,如GET、POST| $remote_addr | 客户端的IP地址 ||$args | 请求中的参数 ||$content_length| 请求头中的Content-length字段 ||$http_user_agent | 客户端agent信息 ||$http_cookie | 客户端cookie信息 ||$remote_addr | 客户端的IP地址 ||$remote_port | 客户端的端口 ||$server_protocol | 请求使用的协议,如HTTP/1.0、·HTTP/1.1` ||$server_addr | 服务器地址 ||$server_name| 服务器名称||$server_port|服务器的端口号|解决跨域先追本溯源以下,跨域究竟是怎么回事。跨域的定义同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。通常不允许不同源间的读操作。同源的定义如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。nginx解决跨域的原理例如:前端server的域名为:fe.server.com后端服务的域名为:dev.server.com现在我在fe.server.com对dev.server.com发起请求一定会出现跨域。现在我们只需要启动一个nginx服务器,将server_name设置为fe.server.com,然后设置相应的location以拦截前端需要跨域的请求,最后将请求代理回dev.server.com。如下面的配置:server { listen 80; server_name fe.server.com; location / { proxy_pass dev.server.com; }}这样可以完美绕过浏览器的同源策略:fe.server.com访问nginx的fe.server.com属于同源访问,而nginx对服务端转发的请求不会触发浏览器的同源策略。请求过滤根据状态码过滤error_page 500 501 502 503 504 506 /50x.html; location = /50x.html { #将跟路径改编为存放html的路径。 root /root/static/html; }根据URL名称过滤,精准匹配URL,不匹配的URL全部重定向到主页。location / { rewrite ^.$ /index.html redirect;}根据请求类型过滤。if ( $request_method !~ ^(GET|POST|HEAD)$ ) { return 403; }配置gzipGZIP是规定的三种标准HTTP压缩格式之一。目前绝大多数的网站都在使用 GZIP 传输 HTML、CSS、JavaScript 等资源文件。对于文本文件,GZip 的效果非常明显,开启后传输所需流量大约会降至 1/4 ~ 1/3。并不是每个浏览器都支持gzip的,如何知道客户端是否支持gzip呢,请求头中的Accept-Encoding来标识对压缩的支持。启用gzip同时需要客户端和服务端的支持,如果客户端支持gzip的解析,那么只要服务端能够返回gzip的文件就可以启用gzip了,我们可以通过nginx的配置来让服务端支持gzip。下面的respone中content-encoding:gzip,指服务端开启了gzip的压缩方式。 gzip on; gzip_http_version 1.1; gzip_comp_level 5; gzip_min_length 1000; gzip_types text/csv text/xml text/css text/plain text/javascript application/javascript application/x-javascript application/json application/xml;gzip开启或者关闭gzip模块默认值为 off可配置为 on / offgzip_http_version启用 GZip 所需的 HTTP 最低版本默认值为 HTTP/1.1这里为什么默认版本不是1.0呢?HTTP 运行在 TCP 连接之上,自然也有着跟 TCP 一样的三次握手、慢启动等特性。启用持久连接情况下,服务器发出响应后让TCP连接继续打开着。同一对客户/服务器之间的后续请求和响应可以通过这个连接发送。为了尽可能的提高 HTTP 性能,使用持久连接就显得尤为重要了。HTTP/1.1 默认支持 TCP 持久连接,HTTP/1.0 也可以通过显式指定 Connection: keep-alive 来启用持久连接。对于 TCP 持久连接上的 HTTP 报文,客户端需要一种机制来准确判断结束位置,而在 HTTP/1.0 中,这种机制只有 Content-Length。而在HTTP/1.1 中新增的 Transfer-Encoding: chunked 所对应的分块传输机制可以完美解决这类问题。nginx同样有着配置chunked的属性chunked_transfer_encoding,这个属性是默认开启的。Nginx 在启用了GZip的情况下,不会等文件 GZip 完成再返回响应,而是边压缩边响应,这样可以显著提高 TTFB(Time To First Byte,首字节时间,WEB 性能优化重要指标)。这样唯一的问题是,Nginx 开始返回响应时,它无法知道将要传输的文件最终有多大,也就是无法给出 Content-Length 这个响应头部。所以,在HTTP1.0中如果利用Nginx 启用了GZip,是无法获得 Content-Length 的,这导致HTTP1.0中开启持久链接和使用GZip只能二选一,所以在这里gzip_http_version默认设置为1.1。gzip_comp_level压缩级别,级别越高压缩率越大,当然压缩时间也就越长(传输快但比较消耗cpu)。默认值为 1压缩级别取值为1-9gzip_min_length设置允许压缩的页面最小字节数,Content-Length小于该值的请求将不会被压缩默认值:0当设置的值较小时,压缩后的长度可能比原文件大,建议设置1000以上gzip_types要采用gzip压缩的文件类型(MIME类型)默认值:text/html(默认不压缩js/css)负载均衡什么是负载均衡如上面的图,前面是众多的服务窗口,下面有很多用户需要服务,我们需要一个工具或策略来帮助我们将如此多的用户分配到每个窗口,来达到资源的充分利用以及更少的排队时间。把前面的服务窗口想像成我们的后端服务器,而后面终端的人则是无数个客户端正在发起请求。负载均衡就是用来帮助我们将众多的客户端请求合理的分配到各个服务器,以达到服务端资源的充分利用和更少的请求时间。nginx如何实现负载均衡Upstream指定后端服务器地址列表upstream balanceServer { server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}在server中拦截响应请求,并将请求转发到Upstream中配置的服务器列表。 server { server_name fe.server.com; listen 80; location /api { proxy_pass http://balanceServer; } }上面的配置只是指定了nginx需要转发的服务端列表,并没有指定分配策略。nginx实现负载均衡的策略轮询策略默认情况下采用的策略,将所有客户端请求轮询分配给服务端。这种策略是可以正常工作的,但是如果其中某一台服务器压力太大,出现延迟,会影响所有分配在这台服务器下的用户。upstream balanceServer { server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}最小连接数策略将请求优先分配给压力较小的服务器,它可以平衡每个队列的长度,并避免向压力大的服务器添加更多的请求。upstream balanceServer { least_conn; server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}最快响应时间策略依赖于NGINX Plus,优先分配给响应时间最短的服务器。upstream balanceServer { fair; server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}客户端ip绑定来自同一个ip的请求永远只分配一台服务器,有效解决了动态网页存在的session共享问题。upstream balanceServer { ip_hash; server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}静态资源服务器location ~ .(png|gif|jpg|jpeg)$ { root /root/static/; autoindex on; access_log off; expires 10h;# 设置过期时间为10小时 }匹配以png|gif|jpg|jpeg为结尾的请求,并将请求转发到本地路径,root中指定的路径即nginx本地路径。同时也可以进行一些缓存的设置。小结nginx的功能非常强大,还有很多需要探索,上面的一些配置都是公司配置的真实应用(精简过了),如果您有什么意见或者建议,欢迎在下方留言… ...

March 11, 2019 · 2 min · jiezi

使用pm2部署node生产环境

一、PM2是什么是可以用于生产环境的Nodejs的进程管理工具,并且它内置一个负载均衡。它不仅可以保证服务不会中断一直在线,并且提供0秒reload功能,还有其他一系列进程管理、监控功能。并且使用起来非常简单。嗯嗯,最好的用处就是监控我们的生产环境下的node程序运行状态,让它给我们日以继日的处于工作状态。pm2官方文档二、为森么要使用pm2原始社会的我们开发node服务端程序一般过程:编写好node程序app.js,运行node app.js;或者写入script使用npm运行;打开浏览器访问;好像需要修改内容,浏览器对修改的内容没有显示出来?->node app.js->再次运行;浏览器忽然访问不到服务,好像出错啦?重启下->node app.js->再次运行;哎呀开了好多控制台窗口,一不小心关闭了,服务又访问不到了,继续打开控制台->node app.js->再次运行;好崩溃!好像有个工具nodemon;安装使用nodemon app.js;哇,可以自动监听文件修改变化自动重启,但是关闭控制台服务还是会被摧毁。通过这个很常用的场景,我们了解到要避免这些麻烦一个服务器至少需要有:后台运行和自动重启,这两个能力。再来看看使用pm2可拥有的能力:日志管理;两种日志,pm2系统日志与管理的进程日志,默认会把进程的控制台输出记录到日志中;负载均衡:PM2可以通过创建共享同一服务器端口的多个子进程来扩展您的应用程序。这样做还允许以零秒停机时间重新启动应用程序。终端监控:可以在终端中监控应用程序并检查应用程序运行状况(CPU使用率,使用的内存,请求/分钟等)。SSH部署:自动部署,避免逐个在所有服务器中进行ssh。静态服务:支持静态服务器功能支持开发调试模式,非后台运行,pm2-dev start <appName>;。。。。。太过强大!pm2常用命令启动服务pm2 start <script_file|config_file> [options] 启动指定应用pm2 start app.js //启动app.js应用pm2 start app.js –name app //启动应用并设置namepm2 start app.sh //脚本启动pm2 start app.js –watch //监听模式启动,当文件发生变化,自动重启//max 表示PM2将自动检测可用CPU的数量并运行尽可能多的进程//max可以自定义,如果是4核CPU,设置为2则占用2个pm2 start app.js -i max //启用群集模式(自动负载均衡)pm2-dev start … // 开发模式启动,即不启用后台运行查看启动列表pm2 list显示应用程序详细信息pm2 show <appName> [options] 显示指定应用详情pm2 show [Name] //根据name查看pm2 show [ID] //根据id查看停止指定应用pm2 stop <appName> [options] 停止指定应用pm2 stop all //停止所有应用pm2 stop [AppName] //根据应用名停止指定应用pm2 stop [ID] //根据应用id停止指定应用重启应用pm2 reload|restart <appName> [options] 重启指定应用pm2 restart app.js //同时杀死并重启所有进程,短时间内服务不可用,生成环境慎用pm2 reload app.js //重新启动所有进程,0秒重启,始终保持至少一个进程在运行pm2 gracefulReload all //以群集模式重新加载所有应用程序启动静态服务器pm2 serve ./dist 8080将目录dist作为静态服务器根目录,端口为8080删除应用pm2 delete <appName> [options] 删除指定应用;如果修改了应用配置行为,需要先删除应用,重新启动后方才会生效,如修改脚本入口文件;pm2 delete all //关闭并删除应用pm2 delete [AppName] //根据应用名关闭并删除应用pm2 delete [ID] //根据应用ID关闭并删除应用pm2 kill 杀掉pm2管理的所有进程;pm2 logs <appName> 查看指定应用的日志,即标准输出和标准错误pm2 logs //查看所有应用日志pm2 logs [Name] //根据指定应用名查看应用日志pm2 logs [ID] //根据指定应用ID查看应用日志pm2 monit 监控各个应用进程cpu和memory使用情况;PM2配置方式命令生产默认示例配置文件pm2 ecosystem或pm2 init,运行默认会生成ecosystem.config.js配置文件module.exports = { apps: [ { name: ‘back-Api’, //应用名 script: ‘./server/start.js’, //应用文件位置 env: { PM2_SERVE_PATH: “./apidoc”, //静态服务路径 PM2_SERVE_PORT: 8080, //静态服务器访问端口 NODE_ENV: ‘development’ //启动默认模式 }, env_production : { NODE_ENV: ‘production’ //使用production模式 pm2 start ecosystem.config.js –env production }, instances:“max”, //将应用程序分布在所有CPU核心上,可以是整数或负数 instance_var: “INSTANCE_ID”, exec_mode: “cluster”, watch:[ “server”, ], //监听模式,不能单纯的设置为true,易导致无限重启,因为日志文件在变化,需要排除对其的监听 merge_logs: true, //集群情况下,可以合并日志 } ], deploy: { production : { user: ’node’, //ssh 用户 host: ‘212.83.163.1’, //ssh 地址 ref: ‘origin/master’, //GIT远程/分支 repo: ‘git@github.com:repo.git’, //git地址 path: ‘/var/www/production’, //服务器文件路径 “post-deploy”: ’npm install && pm2 reload ecosystem.config.js –env production’ //部署后的动作 } }}; 自定义json配置文件如:processes.json;启动pm2 start processes.json { “apps”: [{ “name”: “app”, //名称 “script”: “./”, //程序入口 “cwd”: “./”, //根目录 “watch”:[ “views” ],//需要监控的目录 “error_file”:"./logs/err.log",//错误输出日志 “out_file”:"./logs/out.log", //日志 “log_date_format”:“YYYY-MM-DD HH:mm Z” //日期格式 }] }pm2常用配置项解析1. apps:json结构,apps是一个数组,每一个数组成员就是对应一个pm2中运行的应用2. name:应用程序名称"app"3. cwd:应用程序所在的目录"./“4. script:应用程序的脚本路径”./“5. log_date_format: 日志文件名输出日期格式"YYYY-MM-DD HH:mm Z"6. error_file:自定义应用程序的错误日志文件”./logs/app-err.log",7. out_file:自定义应用程序日志文件"./logs/app-out.log"8. instances: 应用启动实例个数,仅在cluster模式有效 默认为fork;或者 max9. min_uptime:最小运行时间,这里设置的是60s即如果应用程序在60s内退出,pm2会认为程序异常退出,此时触发重启max_restarts设置数量10. max_restarts:设置应用程序异常退出重启的次数,默认15次(从0开始计数)11. cron_restart:定时启动,解决重启能解决的问题12. watch:是否启用监控模式,默认是false。如果设置成true,当应用程序变动时,pm2会自动重载。这里也可以设置你要监控的文件。13. “ignore_watch”: [ // 不用监听的文件 “node_modules”, “logs” ],13. merge_logs:// 设置追加日志而不是新建日志14. exec_interpreter:应用程序的脚本类型,这里使用的shell,默认是nodejs15. exec_mode:应用程序启动模式,这里设置的是cluster_mode(集群),默认是fork16. autorestart:启用/禁用应用程序崩溃或退出时自动重启,默认为true, 发生异常的情况下自动重启17. vizion:启用/禁用vizion特性(版本控制)18. “args”: “”, // 传递给脚本的参数19. env: { PM2_SERVE_PATH: “./apidoc”, //静态服务路径 PM2_SERVE_PORT: 8080, //静态服务器访问端口 NODE_ENV: ‘development’ //启动默认模式 },20. env_production : { NODE_ENV: ‘production’ //使用production模式 pm2 start ecosystem.config.js –env production },pm2配合log4js处理日志pm2启动时通常会发现log4js记录不到日志信息;决解方案,安装pm2的pm2-intercom进程间通信模块在log4js的配置文件logger.js里添加如下命令:pm2: true, pm2InstanceVar: ‘INSTANCE_ID’ pm2配置文件中添加"instance_var": “INSTANCE_ID”, // 添加这一行 字段发现如果没有设置群集模式"exec_mode": “cluster”,也会记录不到;其他log4js日志配置使用详情Koa日志中间件封装开发(log4js)“积跬步、行千里”—— 持续更新中~,喜欢的话留下个赞和关注哦!往期经典好文:团队合作必备的Git操作谈谈Js前端模块化规范 ...

March 8, 2019 · 2 min · jiezi

云计算这条赛道上 腾讯云正在加速度超越

新一轮数字化和互联网技术正在深刻影响各个行业,互联网的形态逐渐从虚拟经济向实体经济不断渗透,云计算等新技术的影响力也从泛互联网行业延伸至制造、能源、医疗等传统领域。作为全球领先的互联网公司,腾讯云基于自身在数字化转型领域的积淀和经验,同时依托腾讯在C端积累的海量用户连接能力,目前已在泛互联网领域的视频、电商、出行、游戏、文娱资讯等细分行业均处于领先地位。根据品牌云活动代金券首发平台“尊托云数”(zuntop.cn)了解到,腾讯云在视频云流量市场占有率、交通出行市场占有率、游戏类公有云服务市场覆盖率以及电商类公有云服务市场占有率均为第一。细数腾讯云合作伙伴,可以看到在出行、物流、社交等领域,腾讯云早已与滴滴、摩拜单车、顺丰、货车帮、知乎、珍爱网等知名企业展开了深度合作。并且从2018年下半年开始,腾讯云的战略合作伙伴同程艺龙、蘑菇街、创梦天地、微盟、猫眼五家互联网“独角兽”企业相继完成上市,进入企业发展的新阶段。这一阵上市潮,证明了腾讯云不仅能够满足不同用户的多样的业务需求,同时能够助力用户创新商业模式,助力客户获得成功。凭借领先的技术和生态能力,腾讯云已经成为众多互联网企业的上云首选。腾讯云受到企业青睐,背后原因在于腾讯云在开放的战略下,以数字化助手的形式,在基础设施、技术以及资源领域给予合作伙伴全力支持,助力合作伙伴构筑数字化生态下的新型商业模式。基础设施层面,腾讯云相继开通香港、美东、美西、印度、泰国、莫斯科等数据中心,目前已经在全球 25 个地理区域,运营 53 个可用区。另外,腾讯云当前已经拥有1300+CDN全球加速节点,以及100T带宽储备,加速能力位居国内领先地位。快速的基础设施建设,帮助摩拜单车统一了全国的网络接入,搭建了更加强壮业务架构,腾讯云遍布全球的海外节点更是为企业出海奠定坚实基础。技术层面,经过多年的积累和发展,腾讯云目前已经积累了200+产品和应用,涵盖云服务器、存储、数据库、安全等领域,产品的种类能够满足各种商业场景。同时,这些基础产品的性能呈现出几何级增长。比如,云服务器产品CVM单台创建耗时从1分钟提升到10秒,并发吞吐能力提升了10倍;虚拟机产品和SSD云盘性能提升8倍;存储产品QPS每秒请求数提升幅度超过10倍;新一代自研云原生数据库CynosDB达到单节点130万QPS读性能,全面领先国内其他同类产品。内部资源层面,腾讯云正打通腾讯内部生态,联合微信、腾讯优图、腾讯安全等核心能力,为众多应用或小程序提供丰富的“互联网+”解决方案。以人工智能为例,腾讯云集结腾讯AI Lab、优图实验室、微信AI团队、音视频实验室等内部顶尖AI团队的能力,为多项应用提供人脸识别、人脸核身解决方案。腾讯云的人脸识别技术已经过进博会的检验。此外,腾讯云还联合外部生态合作伙伴,凭借多年服务海量用户的成功经验,为泛互联网企业的发展提供能力支持,探索出了许多新的应用场景和解决方案,助力产业新生态发展。在即将展开广泛应用的5G领域,腾讯云就与中国联通携手,积极探索云游戏、车联网、VR直播等领域。在众多云厂商摩拳擦掌纷纷抢占市场份额之时,腾讯云以互联网行业市场占有率第一的成绩抢得先机,并在进一步纵深发展的产业互联网竞争中积极布局,以云计算等新技术助力更多企业业务创新发展,构筑数字化时代的新型商业模式。面对高速发展的云计算市场,腾讯云大有可为。

March 4, 2019 · 1 min · jiezi

“独角兽”企业都爱选择腾讯云,背后原因值得考究

众多的互联网公司选择云计算作为自身新一代IT基础设施,腾讯云以其独特的技术和生态能力,正在成为众多互联网公司的“上云”首选。这一点在最近一波强势的上市潮当中体现的淋漓尽致。从2018年下半年开始,腾讯云战略合作伙伴同程艺龙、蘑菇街、创梦天地、微盟、猫眼相继上市,引领了中国互联网又一轮上市潮, 也引发了互联网行业的强烈关注。 11月26号,号称“OTA第一港股”和“小程序第一股”的同程艺龙在港交所正式挂牌;12月6日,中概时尚科技第一股蘑菇街在纽约证券交易所挂牌上市;同一天,乐逗游戏母公司创梦天地也正式在香港联交所主板挂牌交易;专注于移动社交领域的微信第三方服务提供商微盟集团也于上周通过了香港联交所上市聆讯,启动公开发售。大年三十,猫眼CEO郑志昊携猫眼管理团队在香港联合交易所内出席上市仪式敲响了开市锣。 抛开企业自身成熟的业务模式和正确的经营理念,这些互联网公司的成功上市和近年来不断利用前沿的云计算技术实现自身业务快速升级转型不无关系。腾讯云基于自身在数字化转型领域的积淀和经验,同时依托腾讯在C端积累的海量用户连接能力, 不仅能够满足不同用户的多样的业务需求,同时能够助力用户创新商业模式,助力客户获得成功。另外腾讯云扶持企业上云的优惠力度也是前所未有的,不管个人或企业用户都可以通过腾讯云代金券首发网站“尊托云数”(zuntop.cn)领取高达7500元的优惠代金券,直接抵现金用。 那么,为什么这么多互联网公司都选择腾讯云呢? 这和腾讯云在基础设施、技术以及资源领域给予合作伙伴的强大支持不无关系,而在开放战略下,腾讯也特别注重以数字化助手的形式,帮助合作伙伴激发业务创新,尤其是利用最新的云计算、大数据和人工智能等新型基础设施,助力合作伙伴构筑数字化生态下的新型商业模式。 基础设施层面,腾讯云过去一年的数据中心建设速度势如破竹,相继开通香港、美东、美西、印度、泰国、莫斯科等节点,目前已经在全球 25 个地理区域,运营 53 个可用区。另外,腾讯云当前已经拥有1300+CDN全球加速节点,以及100T带宽储备,加速能力位居国内领先地位。快速的基础设施建设,支撑腾讯云上众多的互联网客户快速拓展全球化业务。 技术层面,经过多年的积累和发展,腾讯云目前已经积累了200+产品和应用,涵盖云服务器、存储、数据库、安全等领域,产品的种类能够满足各种商业场景。同时,这些基础产品的性能呈现出几何级增长。比如,云服务器产品CVM单台创建耗时从1分钟提升到10秒,并发吞吐能力提升了10倍;虚拟机产品和SSD云盘性能提升8倍;存储产品QPS每秒请求数提升幅度超过10倍;新一代自研云原生数据库CynosDB达到单节点130万QPS读性能,全面领先国内其他同类产品。 内部资源层面,腾讯云正打通腾讯内部生态,联合微信、腾讯优图、腾讯安全等核心能力,为众多应用或小程序提供丰富的“互联网+”解决方案。比如,针对微信小程序,腾讯云联合微信团队联合开发的小程序·云开发解决方案,大幅降低了小程序的开发门槛,以小程序文件上传功能实现为例,传统开发模式下,通过后台、前端、运维,总耗时超过1142分钟;但是在小程序·云开发模式下,只需要1个前端,4分钟就搞定。目前,乐逗游戏、享物说、作业盒子、腾讯乘车码等都是基于该方案实现了小程序的开发与上线。 更值得一提的是,腾讯云联合外部生态合作伙伴,凭借多年服务海量用户的成功经验,为泛互联网企业的发展提供能力支持,并探索出了许多新的应用场景和解决方案,助力产业新生态发展。比如,在即将展开广泛应用的5G领域,腾讯云就与中国联通携手,积极探索云游戏、车联网、VR直播等领域。 总的来说,无论是基础设施、技术还是资源层面,腾讯云都能从各个维度给予云上企业全面的发展支持。互联网企业也通过与腾讯云的合作,快速获取支撑海量用户扩张的平台化服务能力,并且借助腾讯云对于互联网业务的深入理解和成熟的业务模式,帮助企业快速实现业务的创新与蝶变。可以预见,在未来,通过与腾讯云的合作,互联网行业会出现更多的“独角兽”企业。

February 28, 2019 · 1 min · jiezi

企业云服务器的选择与配置指南

现在企业信息化,使用云服务器的越来越普遍了,做为企业上云,在选择云服务器时,应该需要了解哪些方面呢?云服务器 的配置选择,和网站或应用的类型、访问量、数据量大小、程序质量等因素有关,建议和您的网站或应用的开发技术人员沟通,选择最适合您的配置。 如果您没有技术人员可提供建议,可以参考我们的建议进行配置选择。网站初始阶段访问量小,只需要一台低配置的服务器即可,应用程序、数据库、文件等所有资源均在一台服务器上。 云服务器具有强大的弹性扩展和快速开通能力。随着业务的增长,你可以随时在线增加服务器的CPU、内存、硬盘以及带宽等配置,或者增加服务器数量,无需担心低配服务器在业务突增时带来的资源不足问题,不让一个用户流失。 企业云服务器一般是用来放网站、APP、或者小程序之类的应用,网站也分很多种,不同网站对于云服务器的配置要求也不一样,下面我们从不同网站所需的云服务器配置来了解。 企业展示类网站 企业展示类网站一般只用来放企业介绍、产品展示、企业动态等这类静态内容,访问流量一般也不会很大,对于云服务器的配置要求不是很高,可以选择下面两种配置的云服务器: 1核CPU | 1G内存 | 1Mbps带宽 2核CPU | 4G内存 | 1Mbps带宽 企业功能型网站 企业功能型网站一般有在线购物功能、在线直播功能、论坛功能等等,这类网站因为程序本身所需的配置比较大,如果访问量大一点的话,对于云服务器的配置要求就比展示类网要高,推荐下面两种配置的云服务器: 4核CPU | 4G内存 | 3Mbps带宽 4核CPU | 8G内存 | 5Mbps带宽 对于一些大企业网站,如果访问量特别大的情况下,就需要8核CPU | 16G内存 | 10Mbps带宽或者以上配置的云服务器了,当然前期可以选择配置稍低一点,如果不够用再升级也是可以的。 服务器搭配负载均衡 当您的业务需求较高时,您可以选择较高配置的服务器,或者采用多台稍微低配的服务器,搭配负载均衡服务,通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 环境配置 云服务器购买好了之后,还需要配置网站或应用所需要的环境才能够正常运行,您可以根据自己的实际情况,选择以下几种方式进行服务器环境配置: 自主配置环境 您可以找技术人员进行环境配置,或者通过学习网上的环境配置教程,自己尝试进行配置(通过搜索php环境配置,java环境配置等关键词,可找到大量教程),现在有很多环境集成安装包,一个环境包就可以安装好所有的环境软件,如果目前使用很多的宝塔管理系统、WDCP管理系统等等。但我们不推荐非技术人员自己进行环境配置。 服务商代维 现在有很多云服务器代运维的专业服务商,可以找这样的服务商代配置环境,不过价格一般都比较贵,但可以省心很多 另外,在购买国内大品牌云服务器时(如:阿里云、腾讯云、华为云等),可以通过“尊托云数”网站(zuntop.cn)领取高达7500元的代金券,在付款时直接抵现金,也可以节省一笔不小的费用。

February 27, 2019 · 1 min · jiezi

正向代理与反向代理

什么是代理服务器(Proxy Serve)? 提供代理服务的电脑系统或其它类型的网络终端,代替网络用户去取得网络信息。为什么使用代理服务器?提高访问速度。由于目标主机返回的数据会存放在代理服务器的硬盘中,因此下一次客户再访问相同的站点数据时,会直接从代理服务器的硬盘中读取,起到了缓存的作用,尤其对于热门网站能明显提高访问速度。防火墙作用 由于所有的客户机请求都必须通过代理服务器访问远程站点,因此可以在代理服务器上设限,过滤掉某些不安全信息。同时正向代理中上网者可以隐藏自己的IP,免受攻击。突破访问限制 互联网上有许多开发的代理服务器,客户机在访问受限时,可通过不受限的代理服务器访问目标站点,通俗说,我们使用的翻墙浏览器就是利用了代理服务器,可以直接访问外网。正向代理 正向代理(forward proxy) ,一个位于客户端和原始服务器之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并制定目标(原始服务器),然后代理向原始服务器转发请求并将获得的内容返回给客户端,客户端才能使用正向代理。我们平时说的代理就是指正向代理。 简单一点:A向C借钱,由于一些情况不能直接向C借钱,于是A想了一个办法,他让B去向C借钱,这样B就代替A向C借钱,A就得到了C的钱,C并不知道A的存在,B就充当了A的代理人的角色。反向代理 反向代理(Reverse Proxy),以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求的客户端,此时代理服务器对外表现为一个反向代理服务器。 理解起来有些抽象,可以这么说:A向B借钱,B没有拿自己的钱,而是悄悄地向C借钱,拿到钱之后再交给A,A以为是B的钱,他并不知道C的存在。正向代理和反向代理的区别位置不同: 正向代理,架设在客户机和目标主机之间; 反向代理,架设在服务器端; 代理对象不同: 正向代理,代理客户端,服务端不知道实际发起请求的客户端; 反向代理,代理服务端,客户端不知道实际提供服务的服务端; 备注:正向代理–HTTP代理为多个人提供翻墙服务;反向代理–百度外卖为多个商户提供平台给某个用户提供外卖服务。 用途不同 正向代理,为在防火墙内的局域网客户端提供访问Internet的途径; 反向代理,将防火墙后面的服务器提供给Internet访问; 安全性不同 正向代理允许客户端通过它访问任意网站并且隐藏客户端自身,因此必须采取安全措施以确保仅为授权的客户端提供服务; 反向代理都对外都是透明的,访问者并不知道自己访问的是哪一个代理。正向代理的应用 1. 访问原来无法访问的资源 2. 用作缓存,加速访问速度 3. 对客户端访问授权,上网进行认证 4. 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息反向代理的应用 1. 保护内网安全 2. 负载均衡 3. 缓存,减少服务器的压力 Nginx作为最近较火的反向代理服务器,安装在目的主机端,主要用于转发客户机请求,后台有多个http服务器提供服务,nginx的功能就是把请求转发给后台的服务器,决定哪台目标主机来处理当前请求。总结 正向代理是从客户端的角度出发,服务于特定用户(比如说一个局域网内的客户)以访问非特定的服务;反向代理正好与此相反,从服务端的角度出发,服务于非特定用户(通常是所有用户),已访问特定的服务。

February 27, 2019 · 1 min · jiezi

几张系统图

)

February 25, 2019 · 1 min · jiezi

tcpdump查看Nginx长连接还是短连接

tcpdump用法-i eth0 表示网卡-A 表示转为ascii码-n 表示不要转域名,用ip就好host 后面加IP地址查看nginx是长连接还是短连接:[root@nginx01 ~]# tcpdump -i eth0 -A host 192.168.156.44 -n | grep HTTP tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes.oJ&…ePOST /v2/story/comic/xxx/top HTTP/1.1…}.oJ&HTTP/1.1 200 OK.oN…..POST /v1/story/xxx HTTP/1.1…..oN.HTTP/1.1 200 OK.oN….:POST /v1/story/xxx HTTP/1.1…[.oN.HTTP/1.1 200 OK.oR….BPOST /v2/story/comic/xxx/top HTTP/1.1.oS….MPOST /v1/story/xxx HTTP/1.1…`.oR.HTTP/1.1 200 OK结果:HTTP/1.1 实锤

February 23, 2019 · 1 min · jiezi

国内高防云服务器哪家好?选高防云服务器必看

国内高防云服务器哪家好?选高防云服务器必看为了网站安全,中小企业客户一般会租用能够抵御CC,DDOS等外来攻击的服务器,这些服务器称为高防服务器。因为具有防御功能,所以高防服务器租用价格要比普通的独立服务器贵一些。最近腾讯云、阿里云都推出了2折特惠争夺市场,活动介绍可进入:zuntop.com 了解详情。市场上提供高防服务器的主机商有很多,但性能差异较大,甚至有些不知名的品牌滥竽充数,把根本不具有高防的普通服务器以“高防服务器”的名义销售。那么高防服务器如何辨认呢?作为IT基础设施资源的新一代交付和使用模式,云服务器最近几年在国内风生水起,迅速赢得市场认可,越来越多的企业开始将关键业务迁移上云。高防服务器简单来说,就是能够帮助网站拒绝服务攻击,并且定时扫描现有的网络主节点,查找可能存在的安全漏洞的服务器类型,都可定义为高防服务器。高防服务器主要是指独立单个硬防防御50G[1]以上的服务器,可以为单个客户提供安全维护,总体来看属于服务器的一种,根据各个IDC机房的环境不同,有的提供有硬防,有使用软防。云主机是云计算在基础设施应用上的重要组成部分,位于云计算产业链金字塔底层,产品源自云计算平台。识别是不是高防云服务器,主要从以下几点来分辨:一、从数据存储方式识别真假真的云服务器,是部署在大量服务器集群构建的云端资源池中,是基于网络的分布式数据存储,无单点故障。假云,所有数据是存储在单台服务器的本地硬盘中,无法实现分布式部署,因此将始终面临机器硬件故障的风险,一旦发生硬件故障,无法自动快速恢复。二、从数据备份方式识别真假真正的云服务器,支持快照备份策略,可以实时写入多份数据,即使用户数据丢失或误删,也能在线快速调度备份恢复使用,这是传统备份方式无法比拟的。假云,难以提供实时快照备份功能,最常见的处理方式是,由管理员导出备份数据,修复后再重新导入。三、从物理架构支撑识别真假真正的云服务器,其底层是由规模级的服务器集群组成,在云端形成海量的计算、存储、网络资源池,用户需求多少则分配多少,如此形成资源的高效利用。而假云,则是由一台独立的物理服务器提供服务,发生硬件故障将无法自动修复。四、从热迁移能力识别真假真正的云服务器支持热迁移,即使某台底层服务器硬件故障,用户业务也能在线迁移至其他云服务器,且迁移过程不影响网站正常运行。这是假云无法办到的。假云,一旦出现硬盘损坏等硬件故障,只能将数据备份手动转移到其他云服务器上恢复工作,并且只有当你完成迁移工作之后,你的网站才能恢复正常访问。五、从故障恢复速度识别真假真正的云服务器,是支持异节点快速重建的,这意味着即使计算节点异常中断或损坏,也可以在极短时间内通过其他不同节点重建虚拟机,且不影响数据完整。假云,则无法提供这样的功能。除以上几种辨认真假高防服务器外,最直接的方法就是让服务商提供高防IP段亲自测试下就知道了,直接对IP进行流量测试,看其防御能力是否达到自己的要求。

February 19, 2019 · 1 min · jiezi

上线 HTTPS 服务 / 反向代理 LB / 隐藏业务IP

ssl 证书免费申请ssl for free 可以免费为我们提供三个月的 ssl 证书及续签服务,填写业务域名,上传验证文件到业务服务器,验证成功后便会生成相应的证书.crt和私钥.key,提供一次性下载,重新生成,销毁及续签服务。Nginx 配置 HTTPS / 负载均衡配置如下注意:在upstream中加入hash语句。server语句中不能写入weight等其他的參数,hash_method是使用的hash算法。upstream upstream_server_api { #ip_hash; 同一请求ip发往同一负载 #fair; 按后端服务器的响应时间来分配请求,响应时间短的优先分配 #–url_hash– 按訪问url的hash结果来分配请求,使每一个url定向到同一个后端服务器 #hash $request_uri; #hash_method crc32; #–url_hash– server 127.0.0.1:8081 weight=1 max_fails=3 fail_timeout=30s;#权重1 失败3此后暂停30s server 127.0.0.1:8082 weight=2 max_fails=3 fail_timeout=30s;#权重2 失败3此后暂停30s server 127.0.0.1:8083 backup;# 当其他服务器不可用或全忙时启用 server 127.0.0.1:8084 down;# 服务下线}server { listen 443 ssl default; server_name api.foo.com; index index.html index.htm index.php; root /home/wwwroot/web; … ssl on; ssl_certificate /opt/nginx_ssl/certificate.crt; ssl_certificate_key /opt/nginx_ssl/private.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; … # 如果是静态请求 nginx 负责处理 否则转发给后端服务器location 处理动态请求 # 你可以根据自己的业务定义相应的转发规则 location / { try_files $uri $uri/ @loc_server_api; } # 后端服务器location则反向代理给后端服务 location @loc_server_api { proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_pass http://upstream_server_api; } location ~ ..(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ ..(js|css)?$ { expires 12h; } location ~ /. { deny all; }}如果需要重定向 http 到 https 可使用如下配置server { listen 80 default; server_name api.foo.com; #重定向http至https return 301 https://api.foo.com;}检查配置后重启服务即可nginx -tsystemctl restart nginx.service利用阿里云 SCDN 全站加速隐藏业务服务器 IP全站加速 SCDN 并不能对抗 DDOS/CC 攻击,它只是在 CDN 的基础上同时加速上行请求:选用更为畅通和高速的网络通道传输客户端向服务器端发送的请求,适合动态类数据的请求(web service)。1、创建业务域名 api.foo.com 下的 SCDN 规则,获得加速域名。2、CNAME 解析业务域名到相应的 SCDN 规则提供的加速域名。3、配置回源方式(业务域名|源站域名|自定义域名),这里我们选业务域名,我们业务服务器也应一致监听此业务域名。4、配置 HTTPS 填写申请的证书及私钥开启 HTTPS 访问,可配置是否重定向 HTTP 请求至 HTTPS。5、保存生效如此业务域名将被解析至全站加速CDN,而后 SCDN 回源请求给隐藏在后方的业务服务器,攻击者无法发现真实的业务服务器IP。 ...

January 24, 2019 · 1 min · jiezi

SpringCloud灰度发布实践(整合Apollo配置中心)

代码git地址feature[x] 灰度服务[x] 配置中心[x] 动态路由前言上篇文章介绍了SpringCloud灰度的实现及流程,每次修改服务的元数据信息metadata-map值需要重新调用一次eureka的RestFul接口,不仅如此当服务重启后又会重新读最初的配置值,这样不仅麻烦而且还不可靠。在经过与SpringCloud Config 、Disconf、Apollo等配置中心作出对比后,发现被Apollo友好方便的管理端所深深吸引,再加上该配置中心支持配置文件的灰度发布简直不要太完美。Apollo灰度配置让多个实例共享一个配置文件,示例配置spring.application.name = provide-testserver.port = 7770eureka.client.service-url.defaultZone = http://localhost:1111/eureka/然后新起一个灰度配置,让对应的服务使用该配置。eureka.instance.metadata-map.version = v1事件监听监听Apollo事件,当发现配置文件中的eureka.instance.metadata-map.version值若发生改变,则调用eureka接口更改metadata-map元数据 @ApolloConfigChangeListener(“application”) private void someOnChange(ConfigChangeEvent changeEvent) { changeEvent.changedKeys().forEach(key -> { ConfigChange change = changeEvent.getChange(key); // 灰度配置 if(“eureka.instance.metadata-map.version”.equals(change.getPropertyName())) { String appname = eurekaInstanceConfig.getAppname(); String instanceId = eurekaInstanceConfig.getInstanceId(); String value = StringUtils.isEmpty(change.getNewValue()) ? "" : change.getNewValue(); //TODO 调用eureka更改元数据接口 } }); }这样一来,只需要通过修改配置文件然后就会触发监听事件从而自动触发请求eureka更改元数据的值。动态路由在网关zuul整合了动态路由功能,监听Apollo配置文件使其修改配置文件后可以马上生效。此处不对此功能做过多的介绍,详情见代码配置示例url.map.provide-test = /pt/**url.map.为固定写法,provide-test为服务名称,/pt/**为映射路径参考文章灰度使用在启动类添加注解@SpringBootApplication@EnableDiscoveryClient@EnableGrayConfigpublic class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); }}

January 22, 2019 · 1 min · jiezi

网关实现灰度发布

一、背景互联网产品开发有个非常特别的地方,就是不停的升级,升级,再升级。采用敏捷开发的方式,基本上保持每周或者每两周一次的发布频率,系统升级总是伴随着各种风险,新旧版本兼容的风险,用户使用习惯突然改变而造成用户流失的风险,系统宕机的风险,500错误服务不可用的风险等等。为了避免这些风险,很多产品都采用了灰度发布的策略,其主要思想就是把影响集中到一个点,然后再发散到一个面,出现意外情况后很容易就回退,即使影响也是可控的。任何脱离实际业务的技术工作都是耍流氓,技术需要服务于业务。因此,本文尽量淡化了业务方面的因素,聚焦于技术层面,建议在实际运用中还是要根据各自的业务场景去变化和调整。二、什么是灰度灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。互联网系统,灰度其实就是根据设定的规则将请求路由到我们的灰度版本(灰度机器)上来。比如对于API来说,一般有如下几个需求:特定用户(比如测试帐号)、 特定的App(比如测试app或者合作App)、特定的模块、接口(只有某些接口需要灰度,这种一般是API Container的修改,拿一些不是很重要的API做灰度测试)、特定的机器(某些请求IP转发到灰度机)等。三、灰度的优势1、 在发布过程中降低上线风险2、 降低影响范围,并且范围可控3、 降低对测试的依赖,减少线下自测的数据构造成本4、 特定的请求能够指向特定的服务器,方便集中监控日志,方便跟踪完整的调用链路5、 方便系统流量切入6、 便于随时回滚7、 指定特定人群,方便系统回访,方便产品需求收集,完善产品功能,提升产品质量8、 在无状态的情况下保障用户使用到的版本一致9、 避免宕机给用户带来不好的体验和使用四、目标1、 做到对现有业务系统无侵入性2、 能够发挥以上提到的灰度的优势3、 发布系统的灵活配置4、 发布系统和业务系统的松耦合5、 和网关系统结合,让操作平滑五、功能1、 路由策略管理/配置2、 灰度规则管理3、 开启/关闭开关六、系统设计需要设计的系统分为两种场景,一种是http方式接入,需要借助网关(gate-way)去实现流量的切换,和系统路由;另一种是rpc接入(目前为dubbo),需要借助dubbo提供的负载均衡策略来实现,结合自带的qos(dubbo的在线运维命令)实现服务启动/关闭。【说明】:服务内部执行线程监控待定,sentinel 、 pinpoint or other。1、http方式接入其中分为几个重要的部分:接入层网关,接入客户端请求,根据下发的配置将符合条件的请求转发到新旧系统上.配置管理后台,这个后台可以配置不同的转发策略给接入层网关.稳定和灰度两种处理客户端请求的业务服务器.http请求的入口都落在网关上,网关会根据管控平台(admin dashboard)的配置进行uri的选择。此时请求数据会判断当前应用是否已经开启灰度,再次判断是应用级别的灰度还是服务级别的灰度,然后根据管控平台配置的灰度策略进行灰度,可以支持白名单、权重、ip段、业务域等。管控平台会调用引擎管理执行相应的指令,进行关闭、开启、更新策略和白名单数据等,每次网关重新reload和重启时会从灰度管理系统调用接口读取配置应用的信息,加入缓存。为了提升性能,应用的基本信息、灰度策略、白名单等数据缓存在内存或者类redis这样的缓冲中,灵活的进行缓存数据的更新。实现功能:1、动态路由2、服务动态编排,实现流量的自由切换3、启服/停服4、服务自检2、rpc(dubbo)接入如果直接停机重启rpc service会有什么影响:服务发布时,直接重启Tomcat,导致节点正在处理的请求会受到影响,严重时会有数据异常。服务发布时如果节点正在作为task_tracker运行lts任务,会导致任务失败并retry。服务发布时如果节点正在消费RocketMQ中的消息,会导致消息消费异常,甚至进入retry或dlq队列。服务发布完成后没有即时验证机制,直接暴露给用户,如有异常影响面很广。线上无法同时存在新老版本的服务来用于长时间的验证。竟然有这么多问题,想想就可怕,泪崩~,因此必须想法优雅的实现服务的启停,因此引出dubbo 服务的持续发布:dubbo-consumer实现不同的负载均衡,在负载的时候进行白名单校验和策略选择。系统对灰度管控平台非强制依赖,管控平台出现问题不影响系统正常运行。负载动态路由,阻止后续流量进入,监控服务是否还有执行的线程,加入钩子offline服务或者接口,进行服务升级,自检,启动online,接入负载均衡。由于很多接口都有在Dubbo中进行注册,因此需要有办法能够对其Provider Service接口进行下线或屏蔽,使其不提供服务,即其它服务无法调用它的接口。Service接口下线后,此consumer机器自然无任何流量流入,因此也无流量返回,达到下线consumer机器的目的,然后即可部署代码。官方有提供Dubbo-Admin工具,用于对Dubbo中各APP及其Service接口进行管理,里面自然也包含有实现下线的功能,可以有3种方法:屏蔽,貌似一直没有效果(尴尬);禁用,可以成功禁用;权重调节,可以设置0-100的权重,设置为0时即不提供服务。经过权重调节方案,通过Dubbo-Admin对需要下线机器的APP应用接口权限设置为0。实现的功能:1、缓存负载策略在系统启动的时候要根据系统配置拉取灰度策略,并且保存在内存中,定时获取最新的负载策略,需要提供及时触发的策略更新接口。2、 负载均衡在系统上线之前选择运行时使用的负载均衡进行调用。3、系统配置系统在上线前需要录入管控平台,并且完成相应的配置,在启动的时候作为唯一标识能够拉取相应的配置。4、监控和统计系统在内存中缓存统计信息,定时上传管控平台,监控出现问题不影响系统正常使用(sentinel,or dubbo-amdin模块扩展)。5、qos运维工具系统的启动使用qos,停服采用延时关闭结合jvm钩子。七、检查机制为了平滑发布的顺利进行,检查确认机制不可或缺,即确保Dubbo/Http中的下线都已生效,并且无流量发生,我们从以下两个维度去检查:接口检查,调用Dubbo、Http的API接口,检查业务服务机器状态,是否为已经下线。当然,在做了下线功能的同时,我们也有检查功能和上线功能,可供调用。监控检查,调用监控平台(ELK)的API接口,检查业务服务机器的请求访问数和日志流量是否都已经为0,已经处于下线状态。经过上述改造后,我们新的发布流程如下,基本解决了平滑发布问题,发布时对业务的影响降到了最低;八、停服/启服后小范围验证1.灰度验证–不影响线上用户2.部分实例发布– 导部分流量到新实例(可通过网关路由规则:用户ID取模,区域限制等等)3.全部发布

January 15, 2019 · 1 min · jiezi

LNMP+HAProxy+Keepalived负载均衡(五)- 通过rsyncd实现文件的相互同步

上接前文,前文提到web服务器附件不同步的问题,这里补上文件同步的配置。安装同步软件# 安装同步服务所需的软件yum -y install rsync配置密码本mkdir -p /etc/rsyncfg/# 注意:下面的两个SyncPwd必须一致echo ‘SyncName:SyncPwd’ > /etc/rsyncfg/server.pwdecho ‘SyncPwd’ > /etc/rsyncfg/client.pwd# 密码文件配置权限chmod 600 /etc/rsyncfg/server.pwdchmod 600 /etc/rsyncfg/client.pwd配置防火墙端口# 开启防火墙端口firewall-cmd –zone=public –add-port=873/tcp –permanentfirewall-cmd –reload配置同步软件# 编辑配置文件vim /etc/rsyncd.conf# 配置文件内容uid = rootgid = rootuse chroot = yesmax connections = 4exclude = lost+found/transfer logging = yestimeout = 900ignore nonreadable = yesdont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2[uploads]path = /home/wwwroot/PublishPath/uploadscomment = Host:192.168.6.100 uploads filesignore errorsread only = yeswrite only = nolist = noauth users = syncersecrets file = /etc/rsyncfg/server.pwdhosts allow = 192.168.6.110重启服务service rsyncd restart && service rsyncd status执行同步操作echo ’’ > /etc/rsyncfg/sync.logecho ‘rsync -auv –password-file=/etc/rsyncfg/client.pwd SyncName@192.168.6.100::uploads /home/wwwroot/PublishPath/uploads/’ > /etc/rsyncfg/sync.shchmod +x /etc/rsyncfg/sync.shcp /etc/rsyncfg/sync.sh /usr/sbin/配置计划任务crontab -e# 添加任务# * * * * * /etc/rsyncfg/sync.sh # 取消前面的注释即可# 重启定时任务服务service crond restart && service crond status问题汇总ERROR: auth failed on module XXX@ERROR: auth failed on module XXXrsync error: error starting client-server protocol (code 5) at main.c(xxx) [Receiver=x.x.x]1、密码输入错误:请再次确认你登录用户的密码无误2、secrets file格式错误:secrets file的文件格式是 upload:123456表示upload用户的rsync密码是1234563、配置文件写错:最坑爹的一个,看看自己模块配置下面的auth users、secrets file有没写错4、secrets file权限问题服务端的secrets file权限必须是600,可以使用chmod 600 /home/user/test/rsync/etc/test.pass5、secrets file文件拥有者与rsync运行者服务端rsync服务是以什么用户运行,则必须保证secrets file文件拥有者必须是同一个假设root运行rsync –daemon,则secrets file的owner也必须是root6、如果是以–password-file=file的方式附带密码确保客户端密码文件格式无误,与服务端的密码文件不同,客户端的不用加上用户名,即直接是 123456rsync: failed to connect to X.X.X.X Connection timed out (110)rsync: failed to connect to 192.168.6.100 (192.168.6.100): Connection timed out (110)rsync error: error in socket IO (code 10) at clientserver.c(125) [Receiver=3.1.2]端口不通,开启防火墙的873端口:firewall-cmd –zone=public –add-port=873/tcp –permanentfirewall-cmd –reloadERROR: password file must not be other-accessibleERROR: password file must not be other-accessiblersync error: syntax or usage error (code 1) at authenticate.c(196) [Receiver=3.1.2]密码本权限不对:chmod 600 /etc/rsyncfg/server.pwdchmod 600 /etc/rsyncfg/client.pwd ...

January 14, 2019 · 1 min · jiezi

LNMP+HAProxy+Keepalived负载均衡(四)- MySQL双机互备及自动备份

前三篇已实现了最基本的负载均衡,但是还存在问题,如两个数据库不同步,上传的附件不同步,数据库没有制定备份计划,负载均衡参数还有待优化等问题。这里先把双机互备和自动备份的内容补齐。配置MySQL数据库的账户密码(以下命令仅用参考,这里的目的是要有一个可供内网其他服务器访问本机数据库的账户);# 停止数据库服务service mysql stop# 免验证启动数据库服务mysqld –user=mysql –skip-grant-tables –skip-networking# 登录mysqlmysql -u root mysql# 修改用户密码的几种方式mysql> UPDATE user SET authentication_string=PASSWORD(‘dbpwd’) where USER=‘dbadmin’;mysql> UPDATE user SET authentication_string=PASSWORD(‘dbpwd’) where USER=‘dbbaker’;mysql> UPDATE user SET Password=PASSWORD(‘dbpwd’) where USER=‘dbadmin’;mysql> alter user user() identified by “dbpwd”;# 使配置生效mysql> FLUSH PRIVILEGES;mysql> quit;# 重启mysql服务,注意,这里的mysql如果找不到服务,可以尝试mysqld,再不行就通过ps -ef | grep mysql找到进程ID(pid),然后通过kill -9 pid停止服务;service mysql restartmysql -udbadmin -p# Enter password: <输入新密码newpassword># 创建用户并配置权限:mysql> GRANT ALL ON . TO ‘dbadmin’@’%’ IDENTIFIED BY ‘dbpwd’ WITH GRANT OPTION;# 移除权限# REVOKE ALL ON . TO ‘dbadmin’@’%’;FLUSH PRIVILEGES;# 全局层级:.# 数据库层级:db_name.# 表层级:db_name.tbl_name# ALL是指分配所有权限,具体权限可以通过下面的查询语句查看# 查询用户权限select * from mysql.user \G;# 删除用户# delete from mysql.user where Host <> ‘%’;# 至此,数据库可以通过以上账户进行远程连接了。配置MySQL的双机互备 - 主数据库服务器(192.168.6.200 /etc/my.cnf的配置请参考LNMP+HAProxy+Keepalived负载均衡(三)- 配置文件汇总);# 在主数据库服务器上执行配置:vim /etc/my.cnf# 修改配置内容:server-id=6 # 数据库集群中唯一lower_case_table_names=1 # 新增行,数据表不区分大小写replicate_wild_do_table=sync_db_name.% # 只同步“sync_db_name”库下的表log-slave-updates=YES # 从服务器同步后记录日志# 保存配置后重启MySQL服务:service mysql restart# 进入MySQL命令行,查看主数据库状态信息,其中的“File”和“Position”值后面会用到:mysql> use sync_db_name;mysql> show master status;配置MySQL的双机互备 - 备数据库服务器(192.168.6.210);# 停止slave:mysql> stop slave;# 进入MySQL命令行,配置要同步的主库来源:mysql> CHANGE MASTER TO MASTER_HOST=‘192.168.6.200’,MASTER_PORT=10002,MASTER_USER=‘dbadmin’,MASTER_PASSWORD=‘dbpwd’,MASTER_LOG_FILE=‘mysql-bin.000016’,MASTER_LOG_POS=1285;# MASTER_HOST:主数据库的IP;# MASTER_PORT:主数据库的端口(int),默认3306;# MASTER_USER,MASTER_PASSWORD:主数据库账户、密码;# MASTER_LOG_FILE:主数据库中查询到的“File”值;# MASTER_LOG_POS:主数据库中查询到的“Position”值;# 启动slave:mysql> start slave;# 检查slave:mysql> show slave status \G;# 命令行汇总:show master status;stop slave;Slave_IO_Running、Slave_SQL_Running 均显示为 Yes 说明主备同步服务正常运行,如下图:同步数据库的初始状态; 在启动同步服务前,还需要手动同步下数据库的初始状态,之后可以通过修改其中主数据库中的表来查看从数据库是否同步变更;# 锁定要同步的数据库(192.168.6.200):msyql> use sync_db_name;msyql> flush tables with read lock;# 将要同步的数据库导出脚本:mysqldump -udbadmin -pdbpwd sync_db_name >/home/backup/sync_db_name.sql# 解锁前面锁定的数据库:msyql> unlock tables;# 将上方备份的sql拷贝到从数据库服务器(192.168.6.210),然后创建同名数据库,恢复数据:msyql> create database sync_db_name;msyql> use sync_db_name;msyql> source /home/backup/sync_db_name.sql;做双机互备或多机循环互备; 然后将主从数据库服务器反转(即将192.168.6.200和192.168.6.210的主备身份调换),然后再配置一次同步即可。即192.168.6.200变更会同步到192.168.6.210,反之亦然。简单讲就是: 双机互备:A主B从 + A从B主; 多机循环互备:A主B从 + B主C从 + C主N从 + N主A从(建议不要过多,尤其是数据库数据量大,且变更频繁的情况下,同步毕竟也是有延迟的);数据库的自动备份(前三步前面的文章都有提到);安装计划工具;yum -y install crontabs编辑MySQL的配置文件;vim /etc/my.cnf# 添加配置文件内容:[mysqldump]# 用于备份数据库user=dbbakerpassword=dbpwd重启数据库服务;service mysql restart & service mysql status准备备份脚本;mkdir -p /home/bakup/lgd_system# 编辑bakdb.sh的内容:echo ‘mysqldump sync_db_name | gzip > /home/backup/lgd_system/sync_db_name_$(date +%Y%m%d_%H%M%S).sql.gz’ > /home/bakup/bakdb.shchmod +x /home/bakcup/bakdb.sh# 解压缩指定的备份文件:gunzip sync_db_name_.gz添加备份计划;# 方式一:crontab -e# 方式二:vim /etc/crontab # 编辑计划:# Example of job definition:# .—————- minute (0 - 59) *表示每分钟# | .————- hour (0 - 23) *表示每小时# | | .———- day of month (1 - 31) *表示每天# | | | .——- month (1 - 12) OR jan,feb,mar,apr … *表示每月# | | | | .—- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat *表示每天# | | | | |# * * * * * user-name(用户名) command to be executed(脚本路径)# 30 10 * * * /home/backup/bakdb.sh # 表示每天10:30执行一次备份脚本(前方的注释去掉即可)计划任务常用操作命令;service crond start # 启动服务service crond stop # 关闭服务service crond restart # 重启服务service crond reload # 重新载入配置service crond status # 查看服务状态tail -f /var/log/cron # 查看执行日志备份文件解压缩gzip命令:选项参数:-c:将压缩后的数据显示到屏幕上,可以用于重定向;-d:解压缩的参数;-t:检验压缩的一致性,看是否有错误;-v:显示 源文件大小/压缩文件大小 的压缩比;-#:# 为数字的意思,代表压缩等級,-1 最快,但是压缩比最差、-9 最慢,但是压缩比最好!默认是 -6gzip -v => 压缩文件,-v查看压缩比gzip -d gunzip file.gz ...

January 14, 2019 · 2 min · jiezi

LNMP+HAProxy+Keepalived负载均衡(三)- 配置文件汇总

Nginx的操作命令vim /usr/local/nginx/conf/nginx.conf# 将端口由80修改为10001,修改内容如下:listen 10001 default_server;# 具体配置可参考下面的nginx配置文件# 重启Nginx,并查看其状态;service nginx restart & service nginx statusNginx的配置文件(Web服务器需要修改的配置,仅用参考)user www www;worker_processes auto;error_log /home/wwwlogs/nginx_error.log crit;pid /usr/local/nginx/logs/nginx.pid;#Specifies the value for maximum file descriptors that can be opened by this process.worker_rlimit_nofile 51200;events{ use epoll; worker_connections 51200; multi_accept on;}http{ include mime.types; default_type application/octet-stream; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 50m; sendfile on; tcp_nopush on; keepalive_timeout 60; tcp_nodelay on; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 256k; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 2; gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss; gzip_vary on; gzip_proxied expired no-cache no-store private auth; gzip_disable “MSIE [1-6].”; #limit_conn_zone $binary_remote_addr zone=perip:10m; ##If enable limit_conn_zone,add “limit_conn perip 10;” to server section. server_tokens off; access_log off; server { # 端口根据自己的情况修改 listen 10001 default_server; server_name _; index index.html index.htm index.php default.html default.htm default.php; # 站点根目录 root /home/wwwroot/publishPath; include rewrite/laravel.conf; #error_page 404 /404.html; # Deny access to PHP files in specific directory #location ~ /(wp-content|uploads|wp-includes|images)/..php$ { deny all; } include enable-php.conf; location ~ ..(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ ..(js|css)?$ { expires 12h; } location ~ /.well-known { allow all; } location ~ /. { deny all; } access_log off; } # 可以加载自己的配置文件,这里我把配置文件中的内容直接替换了原本的server节点配置; # include vhost/.conf;}MySQL的操作命令vim /etc/my.cnfservice mysql restart & service mysql statuslnmp restartMySQL的配置文件(DB服务器需要修改的配置,仅用参考)[client]port = 10002socket = /tmp/mysql.sock[mysqld]port = 10002socket = /tmp/mysql.sock# 数据库文件存放位置datadir = /home/lnmp/mysql/dataskip-external-lockingkey_buffer_size = 128Mmax_allowed_packet = 1Mtable_open_cache = 512sort_buffer_size = 2Mnet_buffer_length = 8Kread_buffer_size = 2Mread_rnd_buffer_size = 512Kmyisam_sort_buffer_size = 32Mthread_cache_size = 64query_cache_size = 64Mtmp_table_size = 64Mperformance_schema_max_table_instances = 4000explicit_defaults_for_timestamp = true#skip-networkingmax_connections = 500max_connect_errors = 100open_files_limit = 65535log-bin=mysql-binbinlog_format=mixedserver-id = 51lower_case_table_names = 1expire_logs_days = 10replicate_wild_do_table=lgd_system.%# relay_log=mysqld-relay-binlog-slave-updates=YESdefault_storage_engine = InnoDBinnodb_file_per_table = 1innodb_data_home_dir = /home/lnmp/mysql/datainnodb_data_file_path = ibdata1:10M:autoextendinnodb_log_group_home_dir = /home/lnmp/mysql/datainnodb_buffer_pool_size = 512Minnodb_log_file_size = 128Minnodb_log_buffer_size = 8Minnodb_flush_log_at_trx_commit = 1innodb_lock_wait_timeout = 50[mysqldump]# 数据库备份账户,自行创建并分配相应的权限user=bakuserpassword=ZXdfty^&quickmax_allowed_packet = 16M[mysql]no-auto-rehash[myisamchk]key_buffer_size = 128Msort_buffer_size = 2Mread_buffer = 2Mwrite_buffer = 2M[mysqlhotcopy]interactive-timeoutHAProxy的操作命令# 负载状态监测:# Web服务器HAProxy - http://192.168.6.111:8080/web# DB服务器HAProxy - http://192.168.6.211:8080/db# 如果需要通过外网访问,需要把8080端口映射到外网端口即可。# 常用命令:vim /etc/haproxy/haproxy.cfgservice haproxy restart & service haproxy statusHAProxy的配置文件(Web服务器)#———————————————————————# Global settings#———————————————————————global # 全局的日志配置,使用log关键字,指定使用127.0.0.1上的syslog服务中的local0日志设备,记录日志等级为info的日志 log 127.0.0.1 local3 # 软件工作目录 chroot /var/lib/haproxy # haproxy的pid存放路径,启动进程的用户必须有权限访问此文件 pidfile /usr/local/haproxy/haproxy.pid # 最大连接数,默认4000 maxconn 30000 # 所属用户 user haproxy # 所属组 group haproxy # 以守护进程方式运行haproxy daemon # turn on stats unix socket # stats socket /var/lib/haproxy/stats # socket路径#———————————————————————# common defaults that all the ’listen’ and ‘backend’ sections will# use if not designated in their block#———————————————————————defaults mode http # 默认的模式mode { tcp|http|health },tcp是4层,http是7层,health只会返回OK log global # 采用全局定义的日志 option httplog # 启用日志记录HTTP请求,默认haproxy日志记录是不记录HTTP请求日志 option dontlognull # 不记录健康检查的日志信息 option http-server-close # 每次请求完毕后主动关闭http通道 # 如果后端服务器需要获得客户端真实ip需要配置的参数,可以从Http Header中获得客户端ip option forwardfor except 127.0.0.0/8 option redispatch # serverId对应的服务器挂掉后,强制定向到其他健康的服务器 retries 3 # 3次连接失败就认为服务不可用,也可以通过后面设置 timeout http-request 10s # http请求超时时间 timeout queue 1m # 一个请求在队列里的超时时间 timeout connect 10s # 连接超时 timeout client 1m # 客户端连接超时 timeout server 1m # 服务器连接超时 timeout http-keep-alive 10s # 设置http-keep-alive的超时时间 timeout check 10s # 检测超时 maxconn 3000 # 最大连接数#———————————————————————# main frontend which proxys to the backends#———————————————————————# 前端配置frontend main *:80 acl url_static path_beg -i /static /images /javascript /stylesheets acl url_static path_end -i .jpg .gif .png .css .js use_backend static if url_static default_backend servers#———————————————————————# static backend for serving up images, stylesheets and such#———————————————————————# 后台静态文件服务配置backend static balance roundrobin server static1 192.168.6.100:10001 check inter 2000 fall 3 weight 50 server static2 192.168.6.110:10001 check inter 2000 fall 3 weight 50#———————————————————————# round robin balancing between the various backends#———————————————————————# 后台服务配置backend servers balance roundrobin # 添加cookie配置,将某客户端引导到之前为其服务过的后端服务器上,即和后端某服务器保持联系,防止登录验证失效 cookie app_cook insert nocache server app1 192.168.6.100:10001 check inter 2000 fall 3 weight 50 cookie server1 server app2 192.168.6.110:10001 check inter 2000 fall 3 weight 50 cookie server2# HAProxy状态监控服务配置listen stats # 绑定端口 bind *:8080 mode http # stats enable # 访问地址:192.168.6.100:8080/web 和 192.168.6.110:8080/web stats uri /web stats realm Global\ statistics # 管理员账户 stats auth hapadmin:1qazse$#2HAProxy的配置文件(DB服务器)#———————————————————————# Global settings#———————————————————————global pidfile /var/run/haproxy.pid maxconn 30000 user haproxy group haproxy daemon nbproc 1#———————————————————————# common defaults that all the ’listen’ and ‘backend’ sections will# use if not designated in their block#———————————————————————defaults mode tcp option redispatch retries 3 timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout check 10s maxconn 4096 option abortonclosefrontend main bind :3306 default_backend serversbackend servers server mysql1 192.168.6.200:10002 check inter 3000 fall 3 weight 50 server mysql2 192.168.6.210:10002 check inter 3000 fall 3 weight 50# 监控访问地址:192.168.6.210:8080/db 和 192.168.6.200:8080/dblisten stats mode http bind 0.0.0.0:8080 stats enable stats uri /db stats realm Global\ statistics stats auth dbadmin:1qazse$#2Keeplived的操作命令# 查看已安装的Keepalived的版本:keepalived -v# 查看配置:cat /etc/keepalived/keepalived.conf# 编辑配置文件:vim /etc/keepalived/keepalived.conf# 测试高可用的远程访问:mysql -h 远程数据库ip地址 -P 端口 -u 用户名 -pmysql -h 192.168.6.200 -P 3306 -u dbuser -p# 开通服务器间的 vrrp 协议通信,用于Keepalived通信:firewall-cmd –direct –permanent –add-rule ipv4 filter INPUT 0 –in-interface 网卡名称 –destination 224.0.0.18 –protocol vrrp -j ACCEPT;firewall-cmd –reload;# 服务器的网卡名称请根据自己的情况修改,# INPUT代表接收224.0.0.18的报文。# 在VIP服务器上测试VIP漂移:ip addr | grep 网卡名称# 停止VIP所在服务器的keepalived服务,并查看VIP是否移除,并查看备用服务器是否获取到VIP:service keepalived stop && service keepalived statusip addr | grep 网卡名称# 在之前停止keepalived服务的服务器上开启keepalived服务,查看VIP是否已取回:service keepalived start && service keepalived statusip addr | grep 网卡名称Keeplived的配置(Web服务器)Web主服务器的配置:# Master的配置内容:! Configuration File for keepalivedglobal_defs { notification_email { example@domain.com # 收邮件人,可以定义多个 } notification_email_from HaproxyMaster@web.haproxy # 发件人,可伪装 smtp_server 127.0.0.1 # 发送邮件的服务器地址 smtp_connect_timeout 30 # 连接超时时间 no_email_faults router_id WebMaster vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0}vrrp_script chk_haproxy { # HAProxy服务监测脚本 script ‘/etc/keepalived/check_haproxy.sh’ interval 2 weight 2}vrrp_instance VI_1 { # 每一个vrrp_instance就是定义一个虚拟路由器 state MASTER # 由初始状态状态转换为master状态 interface 网卡名称 # 网卡名称,如eth0,根据自己的情况修改 virtual_router_id 100 # 虚拟路由的id号,一般不能大于255的 priority 100 # 优先级,数字越大,优先级越高,主比次大 advert_int 1 # 初始化通告 authentication { # 认证机制 auth_type PASS auth_pass 666 # 密码,自行更改,主备一致即可 } track_script { chk_haproxy } virtual_ipaddress { # Web服务的虚拟ip地址:vip,前面提到的备用的虚拟IP。 #<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPT> label <LABEL> #192.168.200.18/24 dev eth2 label eth2:1 192.168.6.111 } notify_master ‘/etc/keepalived/clean_arp.sh 192.168.6.111’}Web备服务器的配置:# Backup的配置内容:! Configuration File for keepalivedglobal_defs { notification_email { example@domain.com # 收邮件人,可以定义多个 } notification_email_from HaproxyBackup@web.haproxy # 发件人,可伪装 smtp_server 127.0.0.1 # 发送邮件的服务器地址 smtp_connect_timeout 30 # 连接超时时间 no_email_faults router_id WebBackup vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0}vrrp_script chk_haproxy { # HAProxy服务监测脚本 script ‘/etc/keepalived/check_haproxy.sh’ interval 2 weight 2}vrrp_instance VI_1 { # 每一个vrrp_instance就是定义一个虚拟路由器 state BACKUP # 由初始状态状态转换为backup状态 interface 网卡名称 # 网卡名称,如eth0,根据自己的情况修改 virtual_router_id 100 # 虚拟路由的id号,一般不能大于255的 priority 90 # 优先级,数字越大,优先级越高,主比次大 advert_int 1 # 初始化通告 authentication { # 认证机制 auth_type PASS auth_pass 666 # 密码,自行更改,主备一致即可 } track_script { chk_haproxy } virtual_ipaddress { # Web服务的虚拟ip地址:vip,前面提到的备用的虚拟IP。 #<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPT> label <LABEL> #192.168.200.18/24 dev eth2 label eth2:1 192.168.6.111 } notify_master ‘/etc/keepalived/clean_arp.sh 192.168.6.111’}Keeplived的配置(DB服务器)DB主服务器的配置:# Master的配置内容:! Configuration File for keepalivedglobal_defs { notification_email { example@domain.com # 收邮件人,可以定义多个 } notification_email_from HaproxyMaster@db.haproxy # 发件人,可伪装 smtp_server 127.0.0.1 # 发送邮件的服务器地址 smtp_connect_timeout 30 # 连接超时时间 no_email_faults router_id DBMaster vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0}vrrp_script chk_haproxy { # HAProxy服务监测脚本 script ‘/etc/keepalived/check_haproxy.sh’ interval 2 weight 2}vrrp_instance VI_1 { # 每一个vrrp_instance就是定义一个虚拟路由器 state MASTER # 由初始状态状态转换为master状态 interface 网卡名称 # 网卡名称,如eth0,根据自己的情况修改 virtual_router_id 99 # 虚拟路由的id号,一般不能大于255的 priority 100 # 优先级,数字越大,优先级越高,主比次大 advert_int 1 # 初始化通告 authentication { # 认证机制 auth_type PASS auth_pass 666 # 密码,自行更改,主备一致即可 } track_script { chk_haproxy } virtual_ipaddress { # DB服务的虚拟ip地址:vip,前面提到的备用的虚拟IP。 #<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPT> label <LABEL> #192.168.200.18/24 dev eth2 label eth2:1 192.168.6.211 } notify_master ‘/etc/keepalived/clean_arp.sh 192.168.6.211’}DB备服务器的配置:# Backup的配置内容:! Configuration File for keepalivedglobal_defs { notification_email { example@domain.com # 收邮件人,可以定义多个 } notification_email_from HaproxyBackup@db.haproxy # 发件人,可伪装 smtp_server 127.0.0.1 # 发送邮件的服务器地址 smtp_connect_timeout 30 # 连接超时时间 no_email_faults router_id DBBackup vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0}vrrp_script chk_haproxy { # HAProxy服务监测脚本 script ‘/etc/keepalived/check_haproxy.sh’ interval 2 weight 2}vrrp_instance VI_1 { # 每一个vrrp_instance就是定义一个虚拟路由器 state BACKUP # 由初始状态状态转换为master状态 interface 网卡名称 # 网卡名称,如eth0,根据自己的情况修改 virtual_router_id 99 # 虚拟路由的id号,一般不能大于255的 priority 90 # 优先级,数字越大,优先级越高,主比次大 advert_int 1 # 初始化通告 authentication { # 认证机制 auth_type PASS auth_pass 666 # 密码,自行更改,主备一致即可 } track_script { chk_haproxy } virtual_ipaddress { # DB服务的虚拟ip地址:vip,前面提到的备用的虚拟IP。 #<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPT> label <LABEL> #192.168.200.18/24 dev eth2 label eth2:1 192.168.6.211 } notify_master ‘/etc/keepalived/clean_arp.sh 192.168.6.211’}创建Keepalived调用的脚本操作命令mkdir /etc/keepalived/echo ’’ > /etc/keepalived/check_haproxy.shecho ’’ > /etc/keepalived/clean_arp.shchmod +x /etc/keepalived/.sh# 然后编辑两个脚本的内容,如下/etc/keepalived/check_haproxy.sh#!/bin/bash# 判断haproxy是否已经启动if [ $(ps -C haproxy –no-header | wc -l) -eq 0 ]; then # 如果没有启动,则启动haproxy程序 haproxy -f /etc/haproxy/haproxy.cfgfi# 睡眠两秒钟,等待haproxy完全启动sleep 2# 判断haproxy是否已经启动if [ $(ps -C haproxy –no-header | wc -l) -eq 0 ]; then # 如果haproxy没有启动起来,则将keepalived停掉,则VIP自动漂移到另外一台haproxy机器,实现了对haproxy的高可用 service keepalived stop/etc/keepalived/clean_arp.sh#!/bin/shVIP=$1GATEWAY=192.168.6.255 # 本机的网卡网关地址/sbin/arping -I ens160 -c 5 -s $VIP $GATEWAY &>/dev/null发布文件的配置# 站点根目录赋权chmod -R 777 /home/wwwroot/publishPath# PHP环境配置vim /home/wwwroot/publishPath/.env# 编辑配置内容:APP_DEBUG=false# Web的内网VIP,如需外网访问,则需要将192.168.6.111映射到外网,然后将该处的IP改成外网IPAPP_URL=http://192.168.6.111DB_CONNECTION=mysql# DB的内网VIPDB_HOST=192.168.6.211# DB的内网端口DB_PORT=3306# 数据库名称DB_DATABASE=dbName# 数据库用户名DB_USERNAME=dbuser# 数据库密码DB_PASSWORD=dbpwd# 其他配置选项使用默认设置,这里省略。# 配置保存退出后重启php服务:service php-fpm restart关于IP的说明 以上说到的IP都是内网IP,所有的配置都使用内网IP即可。如需外网访问,只需要把两个虚拟IP和端口映射到外网即可(注意修改php配置的APP_URL)。 ...

January 14, 2019 · 6 min · jiezi

Python猫荐书系列之五:Python高性能编程

稍微关心编程语言的使用趋势的人都知道,最近几年,国内最火的两种语言非 Python 与 Go 莫属,于是,隔三差五就会有人问:这两种语言谁更厉害/好找工作/高工资……对于编程语言的争论,就是猿界的生理周期,每个月都要闹上一回。到了年末,各类榜单也是特别抓人眼球,闹得更凶。其实,它们各有对方所无法比拟的优势以及用武之地,很多争论都是没有必要的。身为一个正在努力学习 Python 的(准)中年程序员,我觉得吧,先把一门语言精进了再说。没有差劲的语言,只有差劲的程序员,等真的把语言学好了,必定是“山重水复疑无路,柳暗花明又一村”。铺垫已了,进入今天的正题,Python 猫荐书系列之五——<center>Python高性能编程</center> 本书适合已入门 Python、还想要进阶和提高的读者阅读。所有计算机语言说到底都是在硬件层面的数据操作,所以高性能编程的一个终极目标可以说是“高性能硬件编程”。然而,Python 是一门高度抽象的计算机语言,它的一大优势是开发团队的高效,不可否认地存在这样或那样的设计缺陷,以及由于开发者的水平而造成的人为的性能缺陷。本书的一大目的就是通过介绍各种模块和原理,来促成在快速开发 Python 的同时避免很多性能局限,既减低开发及维护成本,又收获系统的高效。1、性能分析是基础首先的一个关键就是性能分析,借此可以找到性能的瓶颈,使得性能调优做到事半功倍。性能调优能够让你的代码能够跑得“足够快”以及“足够瘦”。性能分析能够让你用最小的代价做出最实用的决定。书中介绍了几种性能分析的工具:(1)基本技术如 IPython 的 %timeit 魔法函数、time.time()、以及一个计时修饰器,使用这些技术来了解语句和函数的行为。(2)内置工具如 cProfile,了解代码中哪些函数耗时最长,并用 runsnake 进行可视化。(3)line_profiler 工具,对选定的函数进行逐行分析,其结果包含每行被调用的次数以及每行花费的时间百分比。(4)memory_profiler 工具,以图的形式展示RAM的使用情况随时间的变化,解释为什么某个函数占用了比预期更多的 RAM。(5)Guppy 项目的 heapy 工具,查看 Python 堆中对象的数量以及每个对象的大小,这对于消灭奇怪的内存泄漏特别有用。(6)dowser 工具,通过Web浏览器界面审查一个持续运行的进程中的实时对象。(7)dis 模块,查看 CPython 的字节码,了解基于栈的 Python 虚拟机如何运行。(8)单元测试,在性能分析时要避免由优化手段带来的破坏性后果。作者强调了性能分析的重要性,同时也对如何确保性能分析的成功提了醒,例如,将测试代码与主体代码分离、避免硬件条件的干扰(如在BIOS上禁用了TurboBoost、禁用了操作系统改写SpeedStep、只使用主电源等)、运行实验时禁用后台工具如备份和Dropbox、多次实验、重启并重跑实验来二次验证结果,等等。性能分析对于高性能编程的作用,就好比复杂度分析对于算法的作用,它本身不是高性能编程的一部分,但却是最终有效的一种评判标准。2、数据结构的影响高性能编程最重要的事情是了解数据结构所能提供的性能保证。高性能编程的很大一部分是了解你查询数据的方式,并选择一个能够迅速响应这个查询的数据结构。书中主要分析了 4 种数据结构:列表和元组就类似于其它编程语言的数组,主要用于存储具有内在次序的数据;而字典和集合就类似其它编程语言的哈希表/散列集,主要用于存储无序的数据。本书在介绍相关内容的时候很克制,所介绍的都是些影响“速度更快、开销更低”的内容,例如:内置的 Tim 排序算法、列表的 resize 操作带来的超额分配的开销、元组的内存滞留(intern机制)带来的资源优化、散列函数与嗅探函数的工作原理、散列碰撞带来的麻烦与应对、Python 命名空间的管理,等等。理解了这些内容,就能更加了解在什么情况下使用什么数据结构,以及如何优化这些数据结构的性能。另外,关于这 4 种数据结构,书中还得出了一些有趣的结论:对于一个拥有100 000 000个元素的大列表,实际分配的可能是112 500 007个元素;初始化一个列表比初始化一个元组慢5.1 倍;字典或集合默认的最小长度是8(也就是说,即使你只保存3个值,Python仍然会分配 8 个元素)、对于有限大小的字典不存在一个最佳的散列函数。3、矩阵和矢量计算矢量计算是计算机工作原理不可或缺的部分,也是在芯片层次上对程序进行加速所必须了解的部分。然而,原生 Python 并不支持矢量操作,因为 Python 列表存储的不是实际的数据,而是对实际数据的引用。在矢量和矩阵操作时,这种存储结构会造成极大的性能下降。比如,grid[5][2] 中的两个数字其实是索引值,程序需要根据索引值进行两次查找,才能获得实际的数据。同时,因为数据被分片存储,我们只能分别对每一片进行传输,而不是一次性传输整个块,因此,内存传输的开销也很大。减少瓶颈最好的方法是让代码知道如何分配我们的内存以及如何使用我们的数据进行计算。Numpy 能够将数据连续存储在内存中并支持数据的矢量操作,在数据处理方面,它是高性能编程的最佳解决方案之一。Numpy 带来性能提升的关键在于,它使用了高度优化且特殊构建的对象,取代了通用的列表结构来处理数组,由此减少了内存碎片;此外,自动矢量化的数学操作使得矩阵计算非常高效。Numpy 在矢量操作上的缺陷是一次只能处理一个操作。例如,当我们做 A B + C 这样的矢量操作时,先要等待 A B 操作完成,并保存数据在一个临时矢量中,然后再将这个新的矢量和 C 相加。Numexpr 模块可以将矢量表达式编译成非常高效的代码,可以将缓存失效以及临时变量的数量最小化。另外,它还能利用多核 CPU 以及 Intel 芯片专用的指令集来将速度最大化。书中尝试了多种优化方法的组合,通过详细的分析,展示了高性能编程所能带来的性能提升效果。4、编译器书中提出一个观点:让你的代码运行更快的最简单的办法就是让它做更少的工作。 编译器把代码编译成机器码,是提高性能的关键组成部分。不同的编译器有什么优势呢,它们对于性能提升会带来多少好处呢?书中主要介绍了如下编译工具:Cython ——这是编译成C最通用的工具,覆盖了Numpy和普通的Python代码(需要一些C语言的知识)。Shed Skin —— 一个用于非Numpy代码的,自动把Python转换成C的转换器。Numba —— 一个专用于Numpy代码的新编译器。Pythran —— 一个用于Numpy和非numpy代码的新编译器。PyPy —— 一个用于非Numpy代码的,取代常规Python可执行程序的稳定的即时编译器。书中分析了这几种编译器的工作原理、优化范围、以及适用场景等,是不错的入门介绍。此外,作者还提到了其它的编译工具,如Theano、Parakeet、PyViennaCL、ViennaCL、Nuitka 与 Pyston 等,它们各有取舍,在不同领域提供了支撑之力。5、密集型任务高性能编程的一个改进方向是提高密集型任务的处理效率,而这样的任务无非两大类:I/O 密集型与 CPU 密集型。I/O 密集型任务主要是磁盘读写与网络通信任务,占用较多 I/O 时间,而对 CPU 要求较少;CPU 密集型任务恰恰相反,它们要消耗较多的 CPU 时间,进行大量的复杂的计算,例如计算圆周率与解析视频等。改善 I/O 密集型任务的技术是异步编程 ,它使得程序在 I/O 阻塞时,并发执行其它任务,并通过“事件循环”机制来管理各项任务的运行时机,从而提升程序的执行效率。书中介绍了三种异步编程的库:Gevent、Tornado 和 Asyncio,对三种模块的区别做了较多分析。改善 CPU 密集型任务的主要方法是利用多核 CPU 进行多进程的运算。Multiprocessing 模块使用基于进程和基于线程的并行处理,在队列上共享任务,以及在进程间共享数据,是处理 CPU 密集型任务的重要技术。书中没有隐瞒它的局限性:Amdahl 定律揭示的优化限度、适应于单机多核而多机则有其它选择、全局解释锁 GIL 的束缚、以及进程间通信(同步数据和检查共享数据)的开销。针对进程间通信问题,书中还分析了多种解决方案,例如 Less Naïve Pool、Manager、Redis、RawValue、MMap 等。6、集群与现场教训集群是一种多服务器运行相同任务的结构,也就是说,集群中的各节点提供相同的服务,其优点是系统扩展容易、具备容灾恢复能力。集群需要克服的挑战有:机器间信息同步的延迟、机器间配置与性能的差异、机器的损耗与维护、其它难以预料的问题。书中列举了两个惨痛的教训:华尔街公司骑士资本由于软件升级引入的错误,损失4.62亿美元;Skype 公司 24 小时全球中断的严重事故。书中给我们重点介绍了三个集群化解决方案:Parallel Python、IPython Parallel 和 NSQ。引申也介绍了一些普遍使用的方案,如 Celery、Gearman、PyRes、SQS。关于现场教训,它们不仅仅是一些事故或者故事而已,由成功的公司所总结出来的经验更是来之不易的智慧。书中单独用一章内容分享了六篇文章,这些文章出自几个使用 Python 的公司/大型组织,像是Adaptive Lab、RadimRehurek、Smesh、PyPy 与 Lanyrd ,这些国外组织的一线实践经验,应该也能给国内的 Python 社区带来一些启示。7、写在最后众所周知,Python 应用前景大、简单易学、方便开发与部署,然而与其它编程语言相比,它的性能几乎总是落于下风。如何解决这个难题呢?本期荐书的书目就是一种回应。《Python高性能编程》全书从微观到宏观对高性能编程的方方面面做了讲解,主要包含以下主题:计算机内部结构的背景知识、列表和元组、字典和集合、迭代器和生成器、矩阵和矢量计算、编译器、并发、集群和工作队列等。这些内容为编写更快的 Python 指明了答案。本篇文章主要以梳理书中的内容要点为主,平均而兼顾地理清了全书脉络(PS:介绍得太面面俱到了,但愿不被指责为一篇流水账的读书笔记才好……)。我认为,鉴于书中谈及的这些话题,它就足以成为我们荐书栏目的一员了。除去某些句段的糟糕翻译、成书时间比较早(2014年)而造成的过时外,这本书总体质量不错,可称为是一份优秀的高性能编程的指引手册。关于荐书栏目,我最后多说几句。本栏目原计划两周左右出一篇,但由于其它系列文章花费了我不少时间,而要写好一篇荐书/书评也特别费劲,最后生生造成了现在两月一更的尴尬局面……这篇文章是个错误的示范,我不该试图全面通读与概括其内容的。因此,我决定今后选一些易读的书目,在写作上也尽量走短小精悍风,希望能持续地将本栏目运作下去。若你有什么建议(如书目推荐、书评推荐、写作建议、甚至是投稿),我随时欢迎,先行致谢啦。往期荐书回顾: 第一期:《编写高质量代码改善 Python 程序的 91 个建议》第二期:《Python最佳实践指南》第三期:《黑客与画家》第四期:《Python源码剖析》—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 13, 2019 · 1 min · jiezi

k8s1.12.3集群使用token访问api

1.开启相关参数KUBE_API_ARGS="–service-node-port-range=30000-32767 –enable-swagger-ui=true –apiserver-count=3 –audit-log-maxage=30 –audit-log-maxbackup=3 –audit-log-maxsize=100 –audit-log-path=/var/log/k8s/audit.log –event-ttl=1h"2.创建用户,给cluster—admin角色,执行(kubectl create -f “yaml文件名”)apiVersion: v1kind: ServiceAccountmetadata: name: admin namespace: kube-system—kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: ecdataapisubjects: - kind: ServiceAccount name: ecdataapi namespace: kube-systemroleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io3.获取tokenkubectl get secret -n kube-system|grep adminkubectl describe secret “上条命令执行结果(例:ecdataapi-token-9w7zj)” -n kube-system4. 拿到token后,postman(6.7.1)中file>settings>General>ssl certficate verification关闭5.获取服务地址kubectl cluster-info获取地址后复制地址到postman,将刚才生成的token复制到Authorization>type>bearer-Token6.访问目标地址

January 12, 2019 · 1 min · jiezi

LNMP+HAProxy+Keepalived负载均衡 - 基础服务准备

日志服务修改日志服务配置并重启日志服务;vim /etc/rsyslog.conf编辑系统日志配置,指定包含的配置文件路径和规则:$IncludeConfig /etc/rsyslog.d/.conf为haproxy创建一个独立的配置文件;vim /etc/rsyslog.d/haproxy.conf编辑配置文件的内容如下:$ModLoad imudp # 取消注释$UDPServerRun 514 # 取消注释# 与“/etc/haproxy/haproxy.cfg”中的配置“log 127.0.0.1 local3”对应local3. /var/log/haproxy.log# 如果不加 “&~”,则除了在/var/log/haproxy.log中写入日志外,也会写入message文件&~配置“rsyslog”的主配置文件,开启远程日志;vim /etc/sysconfig/rsyslog修改配置内容如下:SYSLOGD_OPTIONS="-c 2 -r -m 0"# -c 2 使用兼容模式,默认是 -c 5# -r 开启远程日志# -m 0 标记时间戳,单位是分钟,为0表示禁用该功能重启HAProxy和日志服务并查看各自服务状态:service haproxy restart & service haproxy statusservice rsyslog restart & service rsyslog status# 查看PHP的错误日志配置cat /usr/local/php/etc/php.ini | grep error_log防火墙服务开通端口(根据自身需求配置):firewall-cmd –zone=public –add-port=3306/tcp –permanentfirewall-cmd –zone=public –add-port=873/tcp –permanentfirewall-cmd –zone=public –add-port=10002/tcp –permanentfirewall-cmd –zone=public –add-port=10001/tcp –permanentfirewall-cmd –zone=public –add-port=80/tcp –permanentfirewall-cmd –zone=public –add-port=8080/tcp –permanent重启/重新加载防火墙服务并查看其状态:systemctl restart firewalld.serviceservice firewalld restart && service firewalld statusfirewall-cmd –reload测试端口:telnet ip port第三方防火墙 这里推荐semanage,优点自行百度,安装配置:# 安装端口管理工具semanage;yum -y install policycoreutils-python# 查看已开通端口;semanage port -l|grep http# 开通端口;semanage port -a -t http_port_t -p tcp port_number # 开放端口port_number,要开通的端口号semanage port -d -t http_port_t -p tcp port_number # 关闭端口port_number,http_port_t为端口组名其他命令# 查看服务的pid:ps -ef | grep ServiceName# 停止服务:kill -9 service_pid# 查看端口占用情况:lsof -i tcp:80# 列出所有端口:netstat -ntlp# 分区及挂载操作# 查看当前空间df -h# 查看可用磁盘fdisk -lfdisk /dev/sdb# 创建分区,多数操作可以默认Command (m for help): m# 根据提示进行操作:# 分区后格式化mkfs -t ext4 /dev/sdb1mkfs -t ext4 /dev/sdb2# 挂载到已有目录mount -w /dev/sdb1 /mnt/lnmpmount -w /dev/sdb2 /mnt/backupmount -o remount -w /dev/sdb2 /mnt/backup ...

January 12, 2019 · 1 min · jiezi

LNMP+HAProxy+Keepalived负载均衡 - LNMP基础环境准备

环境版本说明:服务器系统:CentOS 7.5:cat /etc/redhat-releaseCentOS Linux release 7.5.1804 (Core) # 输出结果服务器IP地址: 服务器A:192.168.6.100 服务器B:192.168.6.200LNMP版本: lnmp1.5 下载地址:http://soft.vpser.net/lnmp/ln…准备安装环境(两台服务器都需要执行):# 关闭selinux(如果是centos系统,默认会开启selinux,会引发很多权限问题)vim /etc/selinux/config# 把SELINUX=enforcing改为SELINUX=disabled# 保存退出,并执行下面的命令使配置立即生效:setenforce 0# 升级所有包,改变软件设置和系统设置,系统版本内核都升级# yum -y update# 升级所有包,不改变软件设置和系统设置,系统版本升级,内核不改变yum -y upgrade# 安装后面用到的软件yum -y install haproxy keepalived vim crontabs mlocate && updatedb# 创建文件夹,并将lnmp安装包下载到当前新创建的文件夹mkdir -p /home/soft && cd /home/soft && wget http://soft.vpser.net/lnmp/lnmp1.5-full.tar.gz# 解压安装包tar -xvf lnmp1.5-full.tar.gz安装lnmp:cd /home/soft/lnmp1.5-full./install.sh根据自己的需要选择MySQL、PHP等软件的版本,按提示操作,然后等待安装完成。我这里都选择最新版本。记好设置的相关密码,后面会用到。其他命令集合(仅用参考,无需执行):# 添加用户组和用户,并为其分配相关文件夹的最高权限:groupadd -r GroupName useradd -g UserName -M -s /sbin/nologin GroupNamechown -R GroupName:UserName /usr/local/haproxy# 工具版本查看mysql -uroot -pPwdStr # 登录后就可以看到mysql的版本nginx -v # nginx versionhaproxy -v # HA-Proxy versionkeepalived -v # keepalived 版本# 编辑配置文件集合:vim /etc/keepalived/keepalived.confvim /etc/rsyslog.conf # 编辑系统日志配置vim /etc/rsyslog.d/haproxy.conf # HAProxy的日志vim /etc/sysconfig/rsyslog # rsyslog的主配置vim /usr/local/nginx/conf/nginx.conf # Nginx的配置vim /etc/haproxy/haproxy.cfg # HAProxy的配置vim /etc/my.cnf # MySQL的配置# 将相关服务设置为开机启动:chkconfig nginx on # Web服务chkconfig mysql on # 数据库服务chkconfig haproxy on # 反向代理服务chkconfig keepalived on # 服务状态监测chkconfig crond on # 计划任务服务chkconfig rsyslog on # 日志服务# 重启各服务集合:service haproxy restart & service haproxy statusservice rsyslog restart & service rsyslog statusservice nginx restart & service nginx statusservice mysql restart & service mysql statusservice keepalived restart & service keepalived statusservice crond restart & service crond statuslnmp restart离线安装 如果要安装的服务器无法连接外网,安装就要麻烦很多,无法使用lnmp的一键安装包了。只能通过PC下载,然后远程上传到服务器,然后再编译安装。这里就不列举所有软件的安装。下载MySQL 点击官方下载 mysql-8.0.13-1.el7.x86_64.rpm-bundle.tar;安装MySQL# 卸载系统自带数据库:rpm -qa | grep MySQL-rpm -ev xxxrpm -e –nodeps mysqlyum -y remove mari*# 将下载的文件通过Xftp上传到服务器# 解压文件到当前目录:tar -xvf mysql-8.0.13-1.el7.x86_64.rpm-bundle.tar# 安装 MySQL:rpm -ivh MySQL_# 创建用户组和用户:groupadd -r mysqluseradd -g mysql mysql# 为MySQL的数据库文件夹授权:chown -R mysql:mysql /home/lnmp/mysql/data/# 相应的依赖 # 1. libaio # 2. net-tools # 3. perl# 安装perl./Configure -des -Dprefix=/usr/bin/perlmake && make testmake installperl -v# 只需要安装一下四个组件就可以了:# 因为具有依赖关系,所以需要按顺序执行:rpm -ivh mysql-community-common-.rpmrpm -ivh mysql-community-libs-.rpmrpm -ivh mysql-community-client-.rpmrpm -ivh mysql-community-server-*.rpm# 查看mysql是否启动service mysqld status# 启动mysqlservice mysqld start# 停止mysqlservice mysqld stop# 重启mysqlservice mysqld restart配置MySQL# 安装完成后,打印出的安装日志里面有一些有用的提示信息,如:# 查看临时密码:cat /root/.mysql_secret# /usr/bin/mysql_secure_installation# New default config file was created as /usr/my.cnf and# will be used by default by the server when you start it.# WARNING: Default config file /etc/my.cnf exists on the system# This file will be read by default by the MySQL server# If you do not want to use this, either remove it, or use the# –defaults-file argument to mysqld_safe when starting the server# 登录后修改密码:mysql> SET PASSWORD = PASSWORD(‘DBPwdStr’);# 为数据库创建访问账户,修改账户的限制IP,查询用户表:mysql> GRANT ALL ON . TO ‘username’@’%’ IDENTIFIED BY ‘DBPwdStr’ WITH GRANT OPTION;mysql> update mysql.user set host=’%’ where host=’::1’;mysql> delete from mysql.user where host<>’%’;mysql> select * from mysql.user \G;# 编辑MySQL的配置文件:vim /etc/my.cnf# 启动MySQL服务:service mysql restart && service mysql status# 启动错误# The server quit without updating PID file (/home/lnmp/mysql/data/localhost.localdomain.pid).# 1.可能是/usr/local/mysql/data/rekfan.pid文件没有写的权限# 解决方法 :给予权限,执行 “chown -R mysql:mysql /var/data” “chmod -R 755 /usr/local/mysql/data” 然后重新启动mysqld!# 2.可能进程里已经存在mysql进程# 解决方法:用命令“ps -ef|grep mysqld”查看是否有mysqld进程,如果有使用“kill -9 进程号”杀死,然后重新启动mysqld!# 3.可能是第二次在机器上安装mysql,有残余数据影响了服务的启动。# 解决方法:去mysql的数据目录/data看看,如果存在mysql-bin.index,将它删除。# 4.mysql在启动时没有指定配置文件时会使用/etc/my.cnf配置文件,查看该文件的[mysqld]下有没有指定的数据目录(datadir)。# 解决方法:请在[mysqld]下设置这一行:datadir = /usr/local/mysql/data安装计划任务管理工具 - crontabs 点击链接下载 crontabs-1.11-6.20121102git.el7.noarch.rpm,如需下载其他版本,请访问官网; 安装:rpm -ivh crontabs-1.11-6.20121102git.el7.noarch.rpm 添加自动备份任务,具体操作请参考MySQL的自动备份。下载(需要连接VPN)并安装HAProxy 点击链接下载 haproxy-1.5.19.tar.gz,如需下载其他版本请访问官网; 安装HAProxy:# 添加用户组和用户:groupadd -r haproxy useradd -g haproxy -M -s /sbin/nologin haproxy# 为安装文件夹授权:chown -R haproxy:haproxy /usr/local/haproxy# 查看内核:uname -r# 解压安装包tar -xvf haproxy-1.5.19.tar.gzcd haproxy-1.5.19# 根据内核版本进行编译(这里的版本对应的目标是linux310):make TARGET=linux310 ARCH=x86_64 PREFIX=/usr/local/haproxymake install PREFIX=/usr/local/# 将可执行文件拷贝到全局目录下:cp /usr/local/haproxy/sbin/haproxy /usr/sbin/haproxy配置HAProxy# 配置HAProxy;cat /etc/haproxy/haproxy.cfgvim /etc/haproxy/haproxy.cfg# 编辑配置文件内容,请参考HAProxy的负载均衡# 重启HAProxy服务;haproxy -f /etc/haproxy/haproxy.cfg# 测试HAProxy;ps -ef | grep haproxy# 访问HAProxy代理的地址和端口,分别停掉备服务器的Nginx服务后,继续访问正常则说明基本配置没问题;下载并安装Keepalived 点击链接下载Keepalived 1.4.5,下载其他版本请访问官网; 安装Keeplived;tar -xvf keepalived-1.4.5.tar.gzcd keepalived-1.4.5./configure –prefix=/usr/local/keepalivedmake && make installmkdir /etc/keepalivedcp /usr/local/keepalived/sbin/keepalived /usr/bin/keepalivedcp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/keepalived.confcp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/keepalived# 设置开机启动chkconfig keepalived on# 服务操作命令service keepalived startservice keepalived stopservice keepalived restartservice keepalived statusservice keepalived restart & service keepalived status配置Keepalived;# 编辑配置文件:vim /etc/keepalived/keepalived.conf# 主从服务器的配置略有差别,具体配置请参照Keepalived的配置; ...

January 10, 2019 · 3 min · jiezi

彻底弄懂session,cookie,token

session,cookie和token究竟是什么简述我在写之前看了很多篇session,cookie的文章,有的人说先有了cookie,后有了session。也有人说先有session,后有cookie。感觉都没有讲的很清楚,泛泛而谈。希望本篇文章对大家有所帮助注:本文需要读者有cookie,session,token的相关基础知识。http是一个无状态协议什么是无状态呢?就是说这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的。这种无状态的的好处是快速。坏处是假如我们想要把www.zhihu.com/login.html和www.zhihu.com/index.html关联起来,必须使用某些手段和工具cookie和session由于http的无状态性,为了使某个域名下的所有网页能够共享某些数据,session和cookie出现了。客户端访问服务器的流程如下首先,客户端会发送一个http请求到服务器端。服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下,具体请看Cookie详解Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie服务器接收请求,分解cookie,验证信息,核对成功后返回response给客户端注意cookie只是实现session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用cookie后还有其他方法存储,比如放在url中现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用用session只需要在客户端保存一个id,实际上大量数据都是保存在服务端。如果全部用cookie,数据量大的时候客户端是没有那么多空间的。如果只用cookie不用session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大小结简而言之, session 有如用户信息档案表, 里面包含了用户的认证信息和登录状态等信息. 而 cookie 就是用户通行证tokentoken 也称作令牌,由uid+time+sign[+固定参数]token 的认证方式类似于临时的证书签名, 并且是一种服务端无状态的认证方式, 非常适合于 REST API 的场景. 所谓无状态就是服务端并不会保存身份认证相关的数据。组成uid: 用户唯一身份标识time: 当前时间的时间戳sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查库存放token在客户端一般存放于localStorage,cookie,或sessionStorage中。在服务器一般存于数据库中token认证流程token 的认证流程与cookie很相似用户登录,成功后服务器返回Token给客户端。客户端收到数据后保存在客户端客户端再次访问服务器,将token放入headers中服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码token可以抵抗csrf,cookie+session不行假如用户正在登陆银行网页,该网页未对csrf攻击进行防护。攻击者就可以注入一张图片,该图片src为http://www.bank.com/api/transfer?count=1000&to=Tom。倘若是session+cookie,用户打开网页的时候就已经转给Tom1000元了。因为session一旦建立,当前域页面以及该页面路径以下所有页面都共享cookie。在img请求的瞬间,cookie会被浏览器自动添加到请求头中。但token不同,开发者在每次发起请求时手动将 Token 添加到请求中。即打开页面请求img时,该请求头中没有token分布式情况下的session和token我们已经知道session时有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,session就会面对负载均衡问题。负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享seesion。这个问题也可以将session存在一个服务器中来解决,但是就不能完全达到负载均衡的效果。当今的几种解决session负载均衡的方法。而token是无状态的,token字符串里就保存了所有的用户信息客户端登陆传递信息给服务端,服务端收到后把用户信息加密(token)传给客户端,客户端将token存放于localStroage等容器中。客户端每次访问都传递token,服务端解密token,就知道这个用户是谁了。通过cpu加解密,服务端就不需要存储session占用存储空间,就很好的解决负载均衡多服务器的问题了。这个方法叫做JWT(Json Web Token)总结session存储于服务器,可以理解为一个状态列表,拥有一个唯一识别符号sessionId,通常存放于cookie中。服务器收到cookie后解析出sessionId,再去session列表中查找,才能找到相应session。依赖cookiecookie类似一个令牌,装有sessionId,存储在客户端,浏览器通常会自动添加。token也类似一个令牌,无状态,用户信息都被加密到token中,服务器收到token后解密就可知道是哪个用户。需要开发者手动添加。jwt只是一个跨域认证的方案参考文章Cookie、Session、Token那点事儿cookie,token验证的区别有了cookie为什么需要sessionCSRF Token的设计是否有其必要性cookie,token,session三者的问题和解决方案负载均衡集群中的session解决方案JWT介绍Json Web Token 入门教程谢谢本文如有错误,欢迎留言讨论

January 9, 2019 · 1 min · jiezi

网络协议 20 - RPC 协议(上)- 基于XML的SOAP协议

【前五篇】系列文章传送门:网络协议 15 - P2P 协议:小种子大学问网络协议 16 - DNS 协议:网络世界的地址簿网络协议 17 - HTTPDNS:私人定制的 DNS 服务网络协议 18 - CDN:家门口的小卖铺网络协议 19 - RPC 协议综述:远在天边,近在眼前 上一节我们了解 RPC 的经典模型和设计要点,并用最早期的 ONC RPC 为例子,详述了具体的实现。而时代在进步,ONC RPC 逐渐因为各种问题被替代,SOAP 协议就是替代者之一。ONC RPC 存在的问题 ONC RPC 将客户端要发送的参数,以及服务端要发送的回复,都压缩为一个二进制串,这样固然能够解决双方的协议约定问题,但是存在一定的不方便。 首先,需要双方的压缩格式完全一致,一点都不能差。一旦有少许的差错,多一位,少一位或者错一位,都可能造成无法解压缩。当然,我们可以用传输层的可靠性以及加入校验值等方式,来减少传输过程中的差错。 其次,协议修改不灵活。如果不是传输过程中造成的差错,而是客户端因为业务逻辑的改变,添加或者删除了字段,或者服务端添加或者删除了字段,而双方没有及时通知,或者线上系统没有及时升级,就会造成解压缩不成功。 因而,当业务发生改变,需要多传输一些参数或者少传输一些参数的时候,都需要及时通知对方,并且根据约定好的协议文件重新生成双方的 Stub 程序。自然,这样灵活性比较差。 如果仅仅是沟通的问题也还好解决,其实更难弄的还有版本的问题。比如在服务端提供一个服务,参数的格式是版本一的,已经有 50 个客户端在线上调用了。现在有一个客户端有个需求,要加一个字段,怎么办呢?这可是一个大工程,所有的客户端都要适配这个,需要重新写程序,加上这个字段,但是传输值是 0,不需要这个字段的客户端很“冤”,本来没我啥事儿,为啥让我也忙活? 最后,ONC RPC 的设计明显是面向函数的,而非面向对象。而当前面向对象的业务逻辑设计与实现方式已经成为主流。 这一切的根源就在于压缩。这就像平时我们爱用缩略语。如果是篮球爱好者,你直接说 NBA,他马上就知道什么意思,但是如果你给一个大妈说 NBA,她可能就不知所云。 所以,这种 RPC 框架只能用于客户端和服务端全由一拨人开发的场景,或者至少客户端和服务端的开发人员要密切沟通,相互合作,有大量的共同语言,才能按照既定的协议顺畅地进行工作。XML 与 SOAP 但是,一般情况下,我们做一个服务,都是要提供给陌生人用的,你和客户不会经常沟通,也没有什么共同语言。就像你给别人介绍 NBA,你要说美国职业篮球赛,这样不管他是干啥的,都能听得懂。 放到我们的场景中,对应的就是用文本类的方式进行传输。无论哪个客户端获得这个文本,都能够知道它的意义。 一种常见的文本类格式是 XML。我们这里举个例子来看。<?xml version=“1.0” encoding=“UTF-8”?><cnblog:purchaseOrder xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xmlns:cnblog=“http://www.example.com”> <order> <date>2019-01-08</date> <className> 板栗焖鸡 </className> <price>58</price> </order></cnblog:purchaseOrder> 我这里不准备详细讲述 XML 的语法规则,但是你相信我,看完下面的内容,即便你没有学过 XML,也能一看就懂,这段 XML 描述的是什么,不像全面的二进制,你看到的都是 010101,不知所云。 有了这个,刚才我们说的那几个问题就都不是问题了。 首先,格式没必要完全一致。比如如果我们把 price 和 author 换个位置,并不影响客户端和服务端解析这个文本,也根本不会误会,说这个作者的名字叫 68。 如果有的客户端想增加一个字段,例如添加一个推荐人字段,只需要在上面的文件中加一行:<recommended> Gary </recommended> 对于不需要这个字段的客户端,只要不解析这一行就是了。只要用简单的处理,就不会出现错误。 另外,这种表述方式显然是描述一个订单对象的,是一种面向对象的、更加接近用户场景的表示方式。 既然 XML 这么好,接下来我们来看看怎么把它用在 RPC 中。传输协议问题 我们先解决第一个,传输协议的问题。 基于 XML 的最著名的通信协议就是SOAP了,全称简单对象访问协议(Simple Object Access Protocol)。它使用 XML 编写简单的请求和回复消息,并用 HTTP 协议进行传输。 SOAP 将请求和回复放在一个信封里面,就像传递一个邮件一样。信封里面的信分抬头和正文POST /purchaseOrder HTTP/1.1Host: www.cnblog.comContent-Type: application/soap+xml; charset=utf-8Content-Length: nnn<?xml version=“1.0”?><soap:Envelope xmlns:soap=“http://www.w3.org/2001/12/soap-envelope"soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> <soap:Header> <m:Trans xmlns:m=“http://www.w3schools.com/transaction/" soap:mustUnderstand=“1”>1234 </m:Trans> </soap:Header> <soap:Body xmlns:m=“http://www.cnblog.com/perchaseOrder"> <m:purchaseOrder”> <order> <date>2019-01-08</date> <className> 板栗焖鸡 </className> <price>88</price> </order> </m:purchaseOrder> </soap:Body></soap:Envelope> HTTP 协议我们学过,这个请求使用 POST 方法,发送一个格式为 application/soap + xml 的 XML 正文给 www.geektime.com ,从而下一个单,这个订单封装在 SOAP 的信封里面,并且表明这是一笔交易(transaction),而且订单的详情都已经写明了。协议约定问题 接下来我们解决第二个问题,就是双方的协议约定是什么样的? 因为服务开发出来是给陌生人用的,就像上面下单的那个 XML 文件,对于客户端来说,它如何知道应该拼装成上面的格式呢?这就需要对于服务进行描述,因为调用的人不认识你,所以没办法找到你,问你的服务应该如何调用。 当然你可以写文档,然后放在官方网站上,但是你的文档不一定更新得那么及时,而且你也写的文档也不一定那么严谨,所以常常会有调试不成功的情况。因而,我们需要一种相对比较严谨的Web 服务描述语言,WSDL(Web Service Description Languages)。它也是一个 XML 文件。 在这个文件中,要定义一个类型 order,与上面的 XML 对应起来。 <wsdl:types> <xsd:schema targetNamespace=“http://www.example.org/cnblog"> <xsd:complexType name=“order”> <xsd:element name=“date” type=“xsd:string”></xsd:element><xsd:element name=“className” type=“xsd:string”></xsd:element><xsd:element name=“Author” type=“xsd:string”></xsd:element> <xsd:element name=“price” type=“xsd:int”></xsd:element> </xsd:complexType> </xsd:schema> </wsdl:types> 接下来,需要定义一个 message 的结构。 <wsdl:message name=“purchase”> <wsdl:part name=“purchaseOrder” element=“tns:order”></wsdl:part> </wsdl:message> 接下来,应该暴露一个端口。 <wsdl:portType name=“PurchaseOrderService”> <wsdl:operation name=“purchase”> <wsdl:input message=“tns:purchase”></wsdl:input> <wsdl:output message=”……"></wsdl:output> </wsdl:operation> </wsdl:portType> 然后,我们来编写一个 binding,将上面定义的信息绑定到 SOAP 请求的 body 里面。 <wsdl:binding name=“purchaseOrderServiceSOAP” type=“tns:PurchaseOrderService”> <soap:binding style=“rpc” transport=“http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name=“purchase”> <wsdl:input> <soap:body use=“literal” /> </wsdl:input> <wsdl:output> <soap:body use=“literal” /> </wsdl:output> </wsdl:operation> </wsdl:binding> 最后,我们需要编写 service。 <wsdl:service name=“PurchaseOrderServiceImplService”> <wsdl:port binding=“tns:purchaseOrderServiceSOAP” name=“PurchaseOrderServiceImplPort”> <soap:address location=“http://www.cnblog.com:8080/purchaseOrder" /> </wsdl:port> </wsdl:service> WSDL 还是有些复杂的,不过好在有工具可以生成。 对于某个服务,哪怕是一个陌生人,都可以通过在服务地址后面加上“?wsdl”来获取到这个文件,但是这个文件还是比较复杂,比较难以看懂。不过好在也有工具可以根据 WSDL 生成客户端 Stub,让客户端通过 Stub 进行远程调用,就跟调用本地的方法一样。服务发现问题 最后解决第三个问题,服务发现问题。 这里有一个UDDI(Universal Description, Discovery, and Integration),也即统一描述、发现和集成协议。它其实是一个注册中心,服务提供方可以将上面的 WSDL 描述文件,发布到这个注册中心,注册完毕后,服务使用方可以查找到服务的描述,封装为本地的客户端进行调用。小结原来的二进制 RPC 有很多缺点,格式要求严格,修改过于复杂,不面向对象,于是产生了基于文本的调用方式——基于 XML 的 SOAP;SOAP 有三大要素:协议约定用 WSDL、传输协议用 HTTP、服务发现用 UDDL。 ...

January 9, 2019 · 2 min · jiezi

网络协议 19 - RPC 协议:远在天边近在眼前

【前五篇】系列文章传送门:网络协议 14 - 流媒体协议:要说爱你不容易网络协议 15 - P2P 协议:小种子大学问网络协议 16 - DNS 协议:网络世界的地址簿网络协议 17 - HTTPDNS:私人定制的 DNS 服务网络协议 18 - CDN:家门口的小卖铺 这几年微服务很火,想必各位博友或多或少的都接触过。微服务概念中,各服务间的相互调用是不可或缺的一环。你知道微服务之间是通过什么方式相互调用的吗? 你可能说,这还不简单,用 socket 呗。服务之间分调用方和被调用方,我们就建立一个 TCP 或者 UDP 连接进行通信就好了。 说着说着,你可能就会发现,这事儿没那么简单。 我们就拿最简单的场景:客户端调用一个加法函数,将两个整数加起来,返回它们的和。 如果放在本地调用,那是简单的不能再简单,但是一旦变成了远程调用,门槛一下子就上去了。 首先,你要会 socket 编程,至少要先了解咱们这个系列的所有协议 ,然后再看 N 本砖头厚的 socket 程序设计的书,学会咱们了解过的几种 socket 程序设计的模型。 这就使得本来大学毕业就能干的一项工作,变成了一件五年工作经验都不一定干好的工作,而且,搞定了 socket 程序设计,才是万里长征的第一步,后面还有很多问题呢。存在问题问题一:如何规定远程调用的语法? 客户端如何告诉服务端,我是一个加法,而另一个是减法。是用字符串 “add” 传给你,还是传给你一个整数,比如 1 表示加法,2 表示减法? 服务端又该如果告诉客户端,我这个是加法,目前只能加整数,不能加小数和字符串。而另一个加法 “add1”,它能实现小数和整数的混合加法,那返回值是什么?正确的时候返回什么,错误的时候又返回什么?问题二:如何传递参数? 是先传两个整数,后传一个操作数 “add”,还是先传操作符,再传两个整数? 另外,如果我们是用 UDP 传输,把参数放在一个报文里还好,但如果是 TCP,是一个流,在这个流里面如何区分前后两次调用?问题三:如何表示数据? 在我们的加法例子中,传递的就是一个固定长度的 int 值,这种情况还好,如果是变长的类型,是一个结构体,甚至是一个类,应该怎么办呢?即使是 int,在不同的平台上长度也不同,该怎么办呢?问题四:如何知道一个服务端都实现了哪些远程调用?从哪个端口可以访问这个远程调用? 假设服务端实现了多个远程调用,每个实现可能都不在一个进程中,监听的端口也不一样,而且由于服务端都是自己实现的,不可能使用一个大家都公认的端口,而且有可能多个进程部署在一台机器上,大家需要抢占端口,为了防止冲突,往往使用随机端口,那客户端如何找到这些监听的端口呢?问题五:发生了错误、重传、丢包、性能等问题怎么办? 本地调用没有这个问题,但是一旦到网络上,这些问题都需要处理,因为网络是不可靠的,虽然在同一个连接中,我们还可以通过 TCP 协议保证丢包、重传的问题,但是如果服务器崩溃了又重启,当前连接断开了,TCP 就保证不了了,需要应用自己进行重新调用,重新传输会不会同样的操作做两遍,远程调用性能会不会受影响呢?解决问题 看到这么多问题,是不是很头疼?还记得咱们了解 http 的时候,认识的协议三要素吗? 本地调用函数里很多问题,比如词法分析、语法分析、语义分析等待,这些问题编译器基本上都帮我们解决了,但是在远程调用中,这些问题我们都要自己考虑。协议约定问题 很多公司对于这个问题,是弄一个核心通信组,里面都是 socket 编程的大牛,实现一个统一的库,让其他业务组的人来调用,业务的人不需要知道中间传输的细节。 通信双方的语法、语义、格式、端口、错误处理等,都需要调用方和被调用方开会商量,双方达成一致。一旦有一方改变,要及时通知对方,否则就会出现问题。 但是,不是每个公司都能通过这种大牛团队解决问题的,而是使用已经实现好的框架。 有一个大牛(Bruce Jay Nelson)通过一篇论文,定义了 RPC 的调用标准。后面所有 RPC 框架都是按照这个标准模式来的。整个过程如下:客户端的应用想发起一个远程调用时,它实际上是通过本地调用方的 Stub。它负责将调用的接口、方法和参数,通过约定的协议规范进行编码,并通过本地 RPCRuntime 进行传输,将调用网络包发送到服务器;服务端的 RPCRuntime 收到请求后,交给提供方 Stub 进行编码,然后调用服务端的方法,获取结果,并将结果编码后,发送给客户端;客户端的 RPCRuntime 收到结果,发给调用方 Stub 解码得到结果,返回给客户端。 上面过程中分了三个层次:客户端、Stub 层、服务端。 对于客户端和服务端,都像是本地调用一样,专注于业务逻辑的处理就可以了。对于 Stub 层,处理双方约定好的语法、语义、封装、解封装。对于 RPCRuntime,主要处理高性能的传输,以及网络的错误和异常。 最早的 RPC 的一种实现方式称为 Sun RPC 或 ONC RPC。Sun 公司是第一个提供商业化 RPC 库和 RPC 编译器的公司。这个 RPC 框架是在 NFS 协议中使用的。 NFS(Network File System)就是网络文件系统。要使 NFS 成功运行,就要启动两个服务端,一个 mountd,用来挂载文件路径。另一个是 nfsd,用来读写文件。NFS 可以在本地 mount 一个远程的目录到本地目录,从而实现让本地用户在本地目录里面读写文件时,操作是是远程另一台机器上的文件。 远程操作和远程调用的思路是一样的,就像本地操作一样,所以 NFS 协议就是基于 RPC 实现的。当然,无论是什么 RPC,底层都是 socket 编程。 XDR(External Data Representation,外部数据表示法)是有一个标准的数据压缩格式,可以表示基本的数据类型,也可以表示结构体。 这里有几种基本的数据类型。 在 RPC 的调用过程中,所有的数据类型都要封装成类似的格式,而且 RPC 的调用和结果返回也有严格的格式。XID 唯一标识请求和回复。请求是 0,回复是 1;RPC 有版本号,两端要匹配 RPC 协议的版本号。如果不匹配,就会返回 Deny,原因是 RPC_MISMATCH;程序有编号。如果服务端找不到这个程序,就会返回 PROG_UNAVAIL;程序有版本号。如果程序的版本号不匹配,就会返回 PROG_MISMATCH;一个程序可以有多个方法,方法也有编号,如果找不到方法,就会返回 PROG_UNAVAIL;调用需要认证鉴权,如果不通过,返回 Deny;最后是参数列表,如果参数无法解析,返回 GABAGE_ARGS; 为了可以成功调用 RPC,在客户端和服务端实现 RPC 的时候,首先要定义一个双方都认可的程序、版本、方法、参数等。 对于上面的加法而言,双方约定为一个协议定义文件,同理,如果是 NFS、mount 和读写,也会有类似的定义。 有了协议定义文件,ONC RPC 会提供一个工具,根据这个文件生成客户端和服务器端的 Stub 程序。 最下层的是 XDR 文件,用于编码和解码参数。这个文件是客户端和服务端共享的,因为只有双方一致才能成功通信。 在客户端,会调用 clnt_create 创建一个连接,然后调用 add_1,这是一个 Stub 函数,感觉是在调用本地函数一样。其实是这个函数发起了一个 RPC 调用,通过调用 clnt_call 来调用 ONC RPC 的类库,来真正发送请求。调用的过程较为复杂,后续再进行专门的说明。 当然,服务端也有一个 Stub 程序,监听客户端的请求,当调用到达的时候,判断如果是 add,则调用真正的服务端逻辑,也就是将两个数加起来。 服务端将结果返回服务端的 Stub,Stub 程序发送结果给客户端 Stub,客户端 Stub 收到结果后就返回给客户端的应用程序,从而完成这个调用过。 有了这个 RPC 框架,前面五个问题中的 “如何规定远程调用的语法?”、“如何传递参数?” 以及 “如何表示数据?” 基本解决了,这三个问题我们统称为协议约定问题。传输问题 前三个问题解决了,但是错误、重传、丢包以及性能问题还没有解决,这些问题我们统称为传输问题。这个 Stub 层就无能为力了,而是由 ONC RPC 的类库来实现。 在这个类库中,为了解决传输问题,对于每一个客户端,都会创建一个传输管理层,而每一次 RPC 调用,都会是一个任务,在传输管理层,你可以看到熟悉的队列机制、拥塞窗口机制等。 由于在网络传输的时候,经常需要等待,而同步的方式往往效率比较低,因而也就有 socket 的异步模型。 为了能够异步处理,对于远程调用的处理,往往是通过状态机来实现的。只有当满足某个状态的时候,才进行下一步,如果不满足状态,不是在那里等待,而是将资源留出来,用来处理其他的 RPC 调用。 如上图,从图也可以看出,这个状态转换图还是很复杂的。 首先,进入起始状态,查看 RPC 的传输层队列中有没有空闲的位置,可以处理新的 RPC 任务,如果没有,说明太忙了,直接结束或重试。如果申请成功,就可以分配内存,获取服务端的端口号,然后连接服务器。 连接的过程要有一段时间,因而要等待连接结果,如果连接失败,直接结束或重试。如果连接成功,则开始发送 RPC 请,然后等待获取 RPC 结果。同样的,这个过程也需要时间,如果发送出错,就重新发送,如果连接断开,要重新连接,如果超时,要重新传输。如果获取到结果,就可以解码,正常结束。 这里处理了连接失败、重试、发送失败、超时、重试等场景,因而实现一个 RPC 框架,其实很有难度。服务发现问题 传输问题解决了,我们还遗留了一个 “如何找到 RPC 服务端的那个随机端口”,这个问题我们称为服务发现问题,在 ONC RPC 中,服务发现是通过 portmapper 实现的。 portmapper 会启动在一个众所周知的端口上,RPC 程序由于是用户自己写的,会监听在一个随机端口上,但是 RPC 程序启动的时候,会向 portmapper 注册。 客户端要访问 RPC 服务端这个程序的时候,首先查询 portmapper,获取 RPC 服务端程序的随机端口,然后向这个随机端口建立连接,开始 RPC 调用。从下图中可以看出,mount 命令的 RPC 调用就是这样实现的。小结远程调用看起来用 socket 编程就可以了,其实是很复杂的,要解决协议约定问题、传输问题和服务发现问题;ONC RPC 框架以及 NFS 的实现,给出了解决上述三大问题的示范性实现,也就是公用协议描述文件,并通过这个文件生成 Stub 程序。RPC 的传输一般需要一个状态机,需要另外一个进程专门做服务发现。参考:刘超-趣谈网络协议系列课;如何给老婆解释什么是RPC; ...

January 7, 2019 · 2 min · jiezi

centos7 安装nginx并配置代理

前言笔者在国外租了一个虚机,用来部署自己的博客应用,并申请了一个域名51think.net来指向这个虚机。随着部署的应用越来越多,而80端口只有一个,无法直接通过域名去访问不同的应用。由此而来,部署一个代理服务器势在必行。本文对nginx的安装和配置进行简单整理,希望对初学者有帮助。一、安装nginx安装有两种方式,即yum和wget。1、通过yum方式在线安装需要注意的一点是,nginx并不在yum的安装源中。什么是yum?你可以理解为一个rpm包管理器的前置(什么是rpm?自己百度吧。。),yum类似于maven的效果,给一个包名,就能将其所依赖的软件包全部下载下来。maven是有中央仓库的,即包的来源。yum也是同样的概念,它也需要一个包源,而且可以配置多个,这个源可以是本地的也可以是网络的,而nginx并不在它的源中,因此我们要把它加到yum的源中。执行如下命令:rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm这个操作并不是安装nginx,只是安装了一个nginx的源。执行完成之后,会在/etc/yum.repos.d目录中看到多了一个文件nginx.repo 。从这个文件的后缀我们可以感知到,.repo即repository,仓库配置。文件内容如下:核心要素也就是一个网址。即告知yum命令,可以从这个网址里找nginx下载并安装。现在开始真正的安装,执行命令yum install -y nginx即可。2、通过wget下载nginx的压缩包wget http://nginx.org/download/nginx-1.10.1.tar.gz解压tar -zxvf nginx-1.10.1.tar.gz,我本地的解压缩目录是/usr/local/,这时候我们可以启动一下nginx观察一下效果,到/usr/local/nginx/sbin目录,执行./nginx,然后在浏览器中访问http://localhost ,弹出以下页面则表示安装成功(确保80端口没有被占用):二、配置代理1、单点代理配置在虚机上找到nginx的安装目录,找到nginx.conf文件。笔者的文件路径是:/usr/local/nginx/conf/nginx.conf这个配置文件的内容很简单,结构类似于json,重点关注server领域的配置,其他配置项默认即可。笔者的配置如下,供参考: server { listen 80; server_name www.51think.net 51think.net www.ueasy.cc; #charset koi8-r; #access_log logs/host.access.log main; #blog location / { proxy_pass http://138.128.193.108:8080; } #mall location /wx { proxy_pass http://138.128.193.108:8081; } }listen表示监听的端口,http的是80,https的是443。server_name表示本配置项是为哪些域名准备的,即可以接受哪些域名的访问。location就是代理的配置了,/表示可以通过域名的根目录去访问http://138.128.193.108:8080的tomcat服务,/wx表示可以通过“域名+/wx”的形式去访问http://138.128.193.108:8081的tomcat服务。要注意的一点是,如果location作为tomcat服务的全局入口,那么location的路径需要和tomcat的contextPath保持一致,否则访问可能出现404错误。举例说明,如果我的博客网站tomcat的contextPath是/blog,即直接访问路径应该是http://138.128.193.108:8080/blog。如果在nginx层面将location配置成如下: location / { proxy_pass http://138.128.193.108:8080; } 则通过域名http://51think.net/这样访问时,将会被代理到http://138.128.193.108:8080/这个访问路径,显然这样是访问不通的。如果tomcat的contextPath是/则没问题。2、负载均衡代理配置负载均衡配置也比较简单,将上文location配置中的 IP+端口换成一个新的配置项,然后在新的配置项里加入我们要负载的节点和负载的策略。location / { #将ip和端口信息换成一个新的配置项manyserver(自由命名) proxy_pass http://manyserver; }配置manyserver:upstream manyserver{ server 138.128.193.108:8080; #tomcat server 138.128.193.108:8084; #tomcat server 138.128.193.108:8085; #tomcat }upstream manyserver配置项里,我们还可以制定负载均衡策略,比如iphash,权重,轮询等,在此不再赘述。以上就是nginx安装配置的全部内容,希望对初学者有所帮助。三、注意事项1、nginx.conf中可以配置多个server节点,nginx可以根据监听端口或者访问域名去定位到不同的server配置项。2、配置完成之后,记得要重启nginx。到nginx的安装目录/usr/local/nginx/sbin/,执行./nginx -s reload即可。

January 6, 2019 · 1 min · jiezi

网络协议 18 - CDN:家门口的小卖铺

【前五篇】系列文章传送门:网络协议 13 - HTTPS 协议:加密路上无尽头网络协议 14 - 流媒体协议:要说爱你不容易网络协议 15 - P2P 协议:小种子大学问网络协议 16 - DNS 协议:网络世界的地址簿网络协议 17 - HTTPDNS:私人定制的 DNS 服务 到现在为止,我们基本上已经了解了网络协议中的大部分常用协议,对于整个 HTTP 请求流程也较为熟悉了。从无到有后,我们就要考虑如何优化“有”这个过程,也就是我们常见的请求优化。而现在的技术栈中,CDN 是最常用的一种方式。 在了解 CDN 前,我们可以先了解下现代社会的物流配送。 例如我们去电商网站下单买东西,这个东西一定要从电商总部的中心仓库送过来吗?在电商刚兴起的时候,所有的配送都是从中心仓库发货,所以买家可能要很久才能收到货。但是后来电商网站的物流系统学聪明了,他们在全国各地建立了很多仓库,而不是只有总部的中心仓库才可以发货。 电商网站根据统计大概知道,北京、上海、广州、深圳、杭州等地,每天能够卖出去多少书籍、纸巾、包、电器等存放期较长的商品,就将这些商品分布存放在各地仓库中,客户一下单,就从临近的仓库发货,大大减少了运输时间,提高了用户体验。 同样的,互联网也借鉴了“就近配送”这个思路。CDN 就近配送 全球有那么多的数据中心,无论在哪里上网,临近不远的地方基本上都有数据中心。可以在每个数据中心里部署几台机器,形成一个缓存集群来缓存部分热数据,这样用户访问数据的是,就可以就近访问了。 这些分布在各个地方的各个数据中心的节点,我们一般称为边缘节点。 由于边缘节点数目比较多,但是每个集群规模比较小,不可能缓存下来所有东西,因而可能无法命中,这样就会在边缘节点之上,形成了区域节点。 区域节点规模较大,缓存的数据也较多,命中的概率也就更大。而在区域节点之上是中心节点,规模更大,缓存数据更多。 就这样,在这样一层层的节点中缓存数据,提高响应速度。但是所有的节点都没有缓存数据,就只有进行回源网站访问了。 如上图,就是 CDN 的分发系统的架构。CDN 系统的缓存,是一层层的,能不访问源数据,就不访问。这也是电商网站物流系统的思路,广州找不到,找华南局,华南局找不到,再找南方局。 有了这个分发系统之后,客户端如何找到相应的边缘节点进行访问呢? 还记得咱们之前了解的基于 DNS 的全局负载均衡吗?这个负载均衡主要用来选择一个就近的相同运营商的服务器进行访问。 同样的,CDN 分发网络也可以用相同的思路选择最合适的边缘节点。 如上图,CDN 的负载均衡流程图。1)没有 CDN 的情况(图中虚线部分)。用户向浏览器输入 www.web.com 这个域名,客户端访问本地 DNS 服务器的时候,如果本地 DNS 服务器有缓存,则返回网站的地址。如果没有,递归查询到网站的权威 DNS 服务器,这个权威 DNS 服务器是负责 web.com 的,它会返回网站的 IP 地址。本地 DNS 服务器缓存下 IP 地址,将 IP 地址返回,然后客户端直接访问这个 IP 地址,就访问到了网站。2)有 CDN 的情况(图中实线部分)。此时,在 web.com 这个权威 DNS 服务器上,会设置一个 CNAME 别名,指向另外一个域名 www.web.cdn.com,返回给本地 DNS 服务器。 当本地 DNS 服务器拿到这个新的域名时,需要继续解析这个新的域名。这个时候,再访问的就不是 web.com 这个权威 DNS 服务器了,而是 web.cdn.com 的权威 DNS 服务器,这是 CDN 自己的权威 DNS 服务器,在这个服务器上,还是会设置一个 CNAME,指向另外一个域名,也就是 CDN 网络的全局负载均衡器。 接下来,本地 DNS 服务器去请求 CDN 的全局负载均衡器解析域名。全局负载均衡器会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:根据用户 IP 地址,判断哪一台服务器距用户最近;用户所处的运营商;根据用户所请求的 URL 中携带的内容名词,判断哪一台服务器上有用户所需的内容;查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。 基于以上这些条件,进行综合分析之后,全局负载均衡器会返回一台缓存服务器的 IP 地址。 本地 DNS 服务器缓存这个 IP 地址,然后将 IP 返回给客户端,客户端去访问这个边缘节点,下载资源。 缓存服务器响应用户请求,将用户所需内容传送给用户。如果这台缓存服务器上没有用户想要的内容,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器,将内容拉到本地。CDN 缓存内容 保质期长的日用品因为不容易过期,因此比较容易缓存。同样的,互联网中的静态页面、图片等,几乎不怎么改变,所以也适合缓存。而像生鲜之类的保存时间较短的,对应互联网中的动态资源,就需要用到动态 CDN 。静态资源缓存 还记得上图这个接入层缓存的架构吗?在进入数据中心的时候,我们希望通过最外层接入层的缓存,将大部分静态资源的访问拦在边缘。而 CDN 则更进一步,将这些静态资源缓存到离用户更近的数据中心外。总体来说,就是缩短用户的“访问距离”。离客户越近,客户访问性能越好,时延越低。流媒体 CDN 在静态内容中,流媒体也大量使用了 CDN。 CDN 支持流媒体协议。例如前面讲过的 RTMP 协议。在很多情况下,这相当于一个代理,从上一级缓存读取内容,转发给用户。由于流媒体往往是连续的,因而可以进行预先缓存的策略,也可以预先推送到用户的客户端。 对于静态页面来讲,内容的分发往往采取拉取的方式。也即,当发现缓存未命中的是,再去上一级进行拉取。 这个方式对于流媒体就不合适了。流媒体数据量大,如果出现回源,压力会比较大,所以往往采取主动推送的模式,将热点数据主动推送到边缘节点。 对于流媒体来讲,很多 CDN 还提供预处理服务。也就是在文件分发之前,进行一定的处理。例如将视频转换成不同的码流,以适应不同的网络带宽的用户需求。再如对视频进行分片,降低存储压力,也使得客户端可以选择使用不同的码率加载不同的分片。这就是我们常见的,超清、标清、流畅等。 除此之外,流媒体 CDN 还有个关键的防盗链问题。因为视频要花大价钱买版权,如果流媒体被其他网站盗走,在其他网站的播放,那损失就大了。 对于防盗链问题,最常用也最简单的方法就是利用 HTTP 头的 refer 字段。当浏览器发送请求的时候,一般会带上 refer。告诉服务器是从哪个页面链接过来的,服务器基于此可以获得一些信息用于处理。如果 refer 信息不是来自本站,就阻止访问或者跳到其它链接。 refer 的机制相对比较容易破解,所以还需要其它的机制配合。 一种常用的机制是时间戳防盗链。使用 CDN 的管理员可以在配置界面上,和 CDN 厂商约定一个加密字符串。 客户端访问时,取出当前的时间戳、要访问的资源极其路径,联通加密字符串进行前面算法得到一个字符串,然后生成一个下载链接,带上这个前面字符串和截止时间戳去访问 CDN。 在服务端,取出过期时间,和当前 CDN 节点时间进行比较,确认请求是否过期。然后 CDN 服务端根据请求的资源及路径、时间戳、和约定的加密字符串进行签名。只有签名和客户端发送的一致,才会将资源返回给客户。动态资源缓存 对于动态资源,用到动态 CDN。动态 CDN 主要有两种模式:1)“生鲜超市模式”,也就是边缘计算模式。 既然数据是动态生成的,所以数据的逻辑计算和存储,也相应的放在边缘的节点。其中定时从源数据那里同步存储的数据,然后在边缘节点进行计算得到结果。 这种方式很像现在的生鲜超市。新鲜的海鲜大餐是动态的,很难事先做好缓存,因而将生鲜超市放在你家旁边,既能够送货上门,也能够现场烹饪。这就是边缘计算的一种体现。2)“冷链运输模式”,也就是路径优化模式。数据不是在边缘计算生成的,而是在源站生成的,但是数据的下发则可以通过 CDN 的网络,对路径进行优化。 因为 CDN 节点较多,能够找到离源站很近的边缘节点,也能找到离用户很近的边缘节点。中间的链路完全由 CDN 来规划,选择一个更加可靠的路径,使用类似专线的方式进行访问。 除此之外,这些资源进行传输的时候,由于 TCP 的流量控制和拥塞控制,可以在 CDN 加速网络中调整 TCP 的参数,使得 TC 可以更加激进的传输数据。 所有这些手段就像冷链传输,优化整个物流运输,全程冷冻高速运输。不管是生鲜从你家旁边超市送过去,还是从产地运送,保证到你家是新鲜的。小结CDN 和电商系统的分布式仓储系统一样,分为中心节点、区域节点、边缘节点,从而将数据缓存在离用户最近的位置。CDN 最擅长的缓存是缓存静态数据。除此之外还可以缓存流媒体数据。这时候要注意防盗链问题。它也支持动态数据的缓存,一种是边缘计算,另一种是链路优化。参考:What is a CDN?;[维基百科 - Content delivery network](https://en.wikipedia.org/wiki...;刘超 - 趣谈网络协议系列课; ...

January 2, 2019 · 1 min · jiezi

网络协议 17 - HTTPDNS:私人定制的 DNS 服务

【前五篇】系列文章传送门:网络协议 12 - HTTP 协议:常用而不简单网络协议 13 - HTTPS 协议:加密路上无尽头网络协议 14 - 流媒体协议:要说爱你不容易网络协议 15 - DNS 协议:网络世界的地址簿网络协议 16 - HTTPDNS:私人定制的 DNS 服务 全球统一的 DNS 是很权威,但是我们都知道“适合自己的,才是最好的”。很多时候,标准统一化的 DNS 并不能满足我们定制的需求,这个时候就需要 HTTPDNS 了。 上一节我们知道了 DNS 可以根据名称查地址,也可以针对多个地址做负载均衡。然而,我们信任的地址簿也会存在指错路的情况。明明离你 500 米就有个吃饭的地方,非要把你推荐到 5 公里外。为什么会出现这样的情况呢? 还记得吗?由我们发出请求解析 DNS 的时候,首先会连接到运营商本地的 DNS 服务器,由这个服务器帮我们去整棵 “DNS 树” 上进行解析,然后将解析的结果返回给客户端。但是本地的 DNS 服务器,作为一个本地导游,往往会有自己的“小心思”。传统 DNS 存在的问题1)域名缓存问题 它可以在本地做一个缓存。也就是说,不是每一个请求,它都会去访问权威 DNS 服务器,而是把访问过一次的结果缓存到本地,当其他人来问的时候,直接返回缓存的内容。 这就相当于导游去过一个饭店,自己记住了地址,当有一个游客问的时候,他就凭记忆回答了,不用再去查地址簿。这样会存在一个问题,游客问的那个饭店如果已经搬走了,然而因为导游没有刷新“记忆缓存”,导致游客白跑一趟。 另外,有的运营商会把一些静态页面,缓存到本运营商的服务器内,这样用户请求的时候,就不用跨运营商进行访问,既加快了速度,也减少了运营商直接流量计算的成本。也就是说,在域名解析的时候,不会将用户导向真正的网站,而是指向这个缓存的服务器。 缓存的问题,很多情况下是看不出问题的,但是当页面更新,用户访问到老的页面,问题就出来了。 再就是本地的缓存,往往使得全局负载均衡失败。上次进行缓存的时候,缓存中的地址不一定是客户此次访问离客户最近的地方,如果把这个地址返回给客户,就会让客户绕远路了。2)域名转发问题 还记得我们域名解析的过程吗?捂脸是本地域名解析,还是去权威 DNS 服务器中查找,都可以认为是一种外包形式。有了请求,直接转发给其他服务去解析。如果转发的是权威 DNS 服务器还好说,但是如果因为“偷懒”转发给了邻居服务器去解析,就容易产生跨运营商访问的问题。 这就好像,如果 A 运营商的客户,访问自己运营商的 DNS 服务器,A 运营商去权威 DNS 服务器查询的话,会查到客户的 A 运营商的,返回一个部署在 A 运营商的网站地址,这样针对相同运营商的访问,速度就会快很多。 但是如果 A 运营商偷懒,没有转发给权威 DNS ,而是转发给了 B 运营商,让 B 运营商再去权威 DNS 服务器查询,这样就会让权威服务器误认为客户是 B 运营商的,返回一个 B 运营商的服务器地址,导致客户每次都要跨运营商访问,访问速度就会慢下来。3)出口 NAT 问题 前面了解网关的时候,我们知道,出口的时候,很多机房都会配置 NAT,也就是网络地址转换,使得从这个网关出去的包,都换成新的 IP 地址。 这种情况下,权威 DNS 服务器就没办法通过请求 IP 来判断客户到底是哪个运营商的,很有可能误判运营商,导致跨运营商访问。4)域名更新问题 本地 DNS 服务器是由不同地区、不同运营商独立部署的。对域名解析缓存的处理上,实现策略也有区别。有的会偷懒,忽略域名解析结构的 TTL 时间限制,在权威 DNS 服务器解析变更的时候,解析结果在全网生效的周期非常漫长。但是有的场景,在 DNS 的切换中,对生效时间要求比较高。 例如双机房部署的是,跨机房的负载均衡和容灾多使用 DNS 来做。当一个机房出问题之后,需要修改权威 DNS,将域名指向新的 IP 地址。但是如果更新太慢,很多用户都会访问一次。5)解析延迟问题 从 DNS 的查询过程来看,DNS 的查询过程需要递归遍历多个 DNS 服务器,才能获得最终的解析结果,这带来一定的延时,甚至会解析超时。 上面总结了 DNS 的五个问题。问题有了,总得有解决办法,就像因为 HTTP 的安全问题,才火了 HTTPS 协议一样,对应的,也有 HTTPDNS 来解决上述 DNS 出现的问题。HTTPDNS什么是 HTTPDNS ?其实很简单:HTTPDNS 是基于 HTTP 协议和域名解析的流量调度解决方案。它不走传统的 DNS 解析,而是自己搭建基于 HTTP 协议的 DNS 服务器集群,分布在多个地点和多个运营商。当客户端需要 DNS 解析的时候,直接通过 HTTP 请求这个服务器集群,得到就近的地址。 这就相当于每家基于 HTTP 协议,自己实现自己的域名解析,做一个自己的地址簿,而不使用统一的地址簿。但是我们知道,域名解析默认都是走 DNS 的,因而使用 HTTPDNS 需要绕过默认的 DNS 路径,也就不能使用默认的客户端。**使用 HTTPDNS 的,往往是手机应用,需要在手机端嵌入支持 HTTPDNS 的客户端 SDK。HTTPDNS 的工作流程 接下来,我们一起来认识下 HTTPDNS 的工作流程。 HTTPDNS 会在客户端的 SDK 里动态请求服务端,获取 HTTPDNS 服务器的 IP 列表,缓存在本地。随着不断地解析域名,SDK 也会在本地缓存 DNS 域名解析的结果。 当手机应用要访问一个地址的时候,首先看是否有本地的缓存,如果有直接返回。这个缓存和本地 DNS 的缓存不一样的是,这个是手机应用自己做的,而非整个运营商统一做。如何更新以及何时更新缓存,手机应用的客户端可以和服务器协调来做这件事情。 如果本地没有,就需要请求 HTTPDNS 的服务器,在本地 HTTPDNS 服务器的 IP 列表中,选择一个发出 HTTP 请求,获取一个要访问的网站的 IP 列表。请求的方式是这样的:curl http://123.4.5.6/d?dn=c.m.cnb… 手机客户端之道手机在哪个运营商、哪个地址。由于是直接的 HTTP 通信,HTTPDNS 服务器能够准确知道这些信息,因而可以做精准的全局负载均衡。 上面五个问题,归结起来就两大问题。一是解析速度和更新速度的平衡问题,二是智能调度的问题。HTTPDNS 对应的解决方案是 HTTPDNS 的缓存设计和调度设计。HTTPDNS 的缓存设计 解析 DNS 过程复杂,通信此时多,对解析速度造成很大影响。为了加快解析,因而有了缓存,但是这又会产生缓存更新速度不及时的问题。最要命的是,这两个方面都掌握在别人手中,也就是本地 DNS 服务器手中,它不会为你定制,作为客户端干着急也没办法。 而 HTTPDNS 就是将解析速度和更新速度全部掌控在自己手中。 一方面,解析的过程,不需要本地 DNS 服务递归的调用一大圈,一个 HTTP 的请求直接搞定。要实时更新的时候,马上就能起作用。另一方面,为了提高解析速度,本地也有缓存,缓存是在客户端 SDK 维护的,过期时间、更新时间,都可以自己控制。HTTPDNS 的缓存设计策略也是咱们做应用架构中常用的缓存设计模式,也即分为客户端、缓存、数据源三层。对于应用架构来讲,就是应用、缓存、数据库。常见的是 Tomcat、Redis、Mysql;对于 HTTPDNS 来讲,就是手机客户端、DNS 缓存、HTTPDNS 服务器。只要是缓存模式,就存在缓存的过期、更新、不一致的问题,解决思路也是相似的。例如,DNS 缓存在内存中,也可以持久化到存储上,从而 APP 重启之后,能够尽快从存储中加载上次累积的经常访问的网站的解析结果,就不需要每次都全部解析一遍,再变成缓存。这有点像 Redis 是基于内存的缓存,但是同样提供持久化的能力,使得重启或者主备切换的时候,数据不会完全丢失。SDK 中的缓存会严格按照缓存过期时间,如果缓存没有命中,或者已经过期,而且客户端不允许使用过期的几率,则会发起一次解析,保证缓存记录是更新的。解析可以同步进行,也就是直接调用 HTTPDNS 的接口,返回最新的记录,更新缓存。也可以异步进行,添加一个解析任务到后台,由后台任务调用 HTTPDNS 的接口。同步更新的优点是实时性好,缺点是如果有多个请求都发现过期的时候,会同时请求 HTTPDNS 多次,造成资源浪费。同步更新的方式对应到应用架构缓存的 Cache-Aside 机制,也就是先读缓存,不命中读数据库,同时将结果写入缓存。异步更新的优点是,可以将多个请求都发现过期的情况,合并为一个对于 HTTPDNS 的请求任务,只执行一次,减少 HTTPDNS 的压力。同时,可以在即将过期的时候,就创建一个任务进行预加载,防止过期之后再刷新,称为预加载。它的缺点是,当前请求拿到过期数据的时候,如果客户端允许使用过期时间,需要冒一次风险。这次风险是指,如果过期的请求还能请求,就没问题,如果不能请求,就会失败一次,等下次缓存更新后,才能请求成功。异步更新的机制,对应到应用架构缓存的 Refresh-Ahead 机制,即业务仅仅访问缓存,当过期的时候定期刷新。在著名的应用缓存 Guava Cache 中,有个 RefreshAfterWrite 机制,对于并发情况下,多个缓存访问不命中从而引发并发回源的请求,可以采取只有一个请求回源的模式。在应用架构的缓存中,也常常用数据预热或者预加载的机制。HTTPDNS 的调度设计由于客户端嵌入了 SDK,因而就不会因为本地 DNS 的各种缓存、转发、NAT,让权威 DNS 服务器误会客户端所在的位置和运营商,从而可以拿到第一手资料。在客户端,可以知道手机是哪个国家、哪个运营商、哪个省、甚至是哪个市,HTTPDNS 服务端可以根据这些信息,选择最佳的服务节点返回。如果有多个节点,还会考虑错误率、请求时间、服务器压力、网络状态等,进行综合选择,而非仅仅考虑地理位置。当有一个节点宕机或者性能下降的时候,可以尽快进行切换。要做到这一点,需要客户端使用 HTTPDNS 返回的 IP 访问业务应用。客户端的 SDK 会收集网络请求数据,如错误率、请求时间等网络请求质量数据,并发送到统计后台,进行分析、聚合,以此查看不同 IP 的服务质量。在服务端,应用可以通过调用 HTTPDNS 的管理接口,配置不同服务质量的优先级、权重。HTTPDNS 会根据这些策略综合地理位置和线路状况算出一个排序,优先访问当前那些优质的、时延低的 IP 地址。HTTPDNS 通过智能调度之后返回的结果,也会缓存在客户端。为了不让缓存使得调度失真,客户端可以根据不同的移动网络运营商的 SSID 来分维度缓存。不同的运营商解析出来的结果会不同。小结传统 DNS 会因为缓存、转发、NAT 等问题导致客户端误会自己所在的位置和运营商,从而影响流量的调度;HTTPDNS 通过客户端 SDK 和服务端,通过 HTTP 直接调用解析 DNS 的方式,绕过了传统 DNS 的缺点,实现了智能的调度。参考:HTTPDNS 的原理;刘超 - 趣谈网络协议系列课; ...

December 31, 2018 · 2 min · jiezi

网络协议 16 - DNS 协议:网络世界的地址簿

为什么在地址栏输入域名,就能直接访问到对应服务器?全局负载均衡和内部负载均衡又是什么?这些都和 DNS 解析息息相关,让我们一起来解密 DNS 解析。 其实说起 DNS 解析,应该都知道它很像地址簿。就像我们去一家新开的沃尔玛超市,通过地址簿查出来沃尔玛在哪条路多少号,然后再去找。 在网络世界中,也是这样的。我们可以记住网站的名称,但是很难记住网站的 IP 地址,因此需要一个“地址簿”,帮我们将网站名称转换成 IP。这个“地址簿”就是 DNS 服务器。DNS 服务器 对于 DNS 服务器而言,全球每个人上网,都需要访问它。 而全球的网民数,据最新统计,已经有 40 亿,每个人都访问它,可想而知 DNS 服务器会有很大的访问流量压力(高并发)。 而且,它还非常重要,一旦出了故障,整个互联网都将瘫痪(高可用)。 此外,上网的人分布在全世界各地,如果大家都去同一个地方的某一台服务器,时延将会非常的(分布式)。 因此,DNS 服务器一定要具备高可用、高并发、分布式的特点。 基于此,DNS 服务器设计成树状的层次结构。如下图:根 DNS 服务器:返回顶级域 DNS 服务器的 IP 地址;顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址;权威 DNS 服务器:返回相应主机的 IP 地址。DNS 解析流程 上面说了 DNS 服务器面临大流量访问的压力,因此,为了提高 DNS 的解析性能,很多网站都会就近部署 DNS 缓存服务器。所以,我们常见的 DNS 解析流程就变成了:客户端发出 DNS 请求给本地域名服务器。我们访问博客园,客户端会问本地域名服务器, www.cnblogs.com 的 IP 是什么?(本地域名服务器,如果网络是通过 DHCP 配置,本地 DNS 是由你的网络服务商,如电信、联通等自动分配,它通常就在网络服务商的机房里);本地 DNS 收到来自客户端的请求,查找“地址簿”,返回 IP 或请求根域名服务器。我们可以理解为服务器上缓存了一张域名与 IP 对应的大表,如果能找到 www.cnblogs.com,就直接返回对应的 IP 地址。如果没有找到,本地 DNS 会去问它的根域名服务器;根 DNS 收到来自本地 DNS 的请求,返回 .com 对应的顶级域名服务器的地址。根域名服务器是最高层次的,全球共有 13 套,它不直接用于域名解析,而是指明怎样去查找对应 IP。它发现请求的域名后缀是 .com,就会返回 .com 对应的顶级域名服务器的地址;本地 DNS 服务器收到顶级 DNS 服务器地址,请求顶级 DNS 服务器查询域名 IP;顶级 DNS 服务器返回权威 DNS 服务器地址。顶级域名服务器就是大名鼎鼎的,负责 .com、.net、.org 这些二级域名,比如 cnblogs.com,它会返回对应的权威 DNS 服务器地址;本地 DNS 服务器收到权威 DNS 服务器地址,请求权威 DNS 服务器查询域名 IP。而 cnblogs.com 的权威 DNS 服务器就是域名解析结果的原出处;权威 DNS 服务器返回对应 IP。权威 DNS 服务器查询“地址簿”,获取到域名对应 IP 地址,返回给本地 DNS 服务器;本地 DNS 服务器收到 IP,返回给客户端;客户端与目标建立连接。 至此,我们完成了 DNS 的解析过程,整个过程如下图:负载均衡 站在客户端角度,上述过程是一次 DNS 递归查询过程。因为本地 DNS 全权为它代劳,它只要坐等结果就好了。在这个过程中,DNS 除了可以通过名称映射为 IP 地址外,它还可以做另外一件很重要的事 - 负载均衡。 还是拿我们逛沃尔玛超市为例。它可能在一个城市里会有多家店,我们要逛沃尔玛,可以就近找一家,而不用都去同一家,这就是负载均衡。DNS 做负载均衡也有花样可以玩。1)DNS 做内部负载均衡 所谓的内部负载均衡,其实很好理解。就像我们的应用访问数据库,在应用里配置的数据库地址。如果配置成 IP 地址,一旦数据库换到了另外一台机器,我们就要修改配置。如果我们有很多台应用同时连一个数据库,一换 IP,就需要将这些应用的配置全部修改一遍,是不是很麻烦?所以,我们可以将数据地址配置成域名。在更换数据库位置时,只要在 DNS 服务器里,将域名映射为新的 IP 地址就可以了。 在这个基础上,我们可以更进一步 。例如,某个应用要访问另外一个应用,如果配置另外一个应用的 IP 地址,那么这个访问就是一对一的。但是当被访问的应用因流量过大撑不住的时候,我们就需要部署多个应用。这时候,我们就不能直接配置成 IP,而是要配置域名了。只要在域名解析的时候,配置好策略,这次返回一个 IP,下次返回第二个 IP,就实现了负载均衡。2)DNS 做全局负载均衡 为了保证我们应用的高可用性,往往会将应用部署在多个机房,每个地方都会有自己的 IP 地址。当用户访问某个域名的时候,这个 IP 地址可以轮询访问多个数据中心。如果一个数据中心因为某种原因挂了,只要将这个 IP 地址从 DNS 服务器中删掉就可以了,用户不会访问到宕机的服务器,保证了应用的可用性。 另外,我们肯定希望用户能访问就近的数据中心。这样客户访问速度就会快很多,体验也会好很多,也就实现了全局负载均衡的概念。负载均衡示例 我们通过 NDS 访问数据中心对象存储上的静态资源为例,来看一看整个过程。 假设全国有多个数据中心,托管在多个运营商,每个数据中心有三个可用区。对象存储可以通过跨可用区部署,实现高可用性。在每个数据中心中,都至少部署两个内部负载均衡器,内部负载均衡器后面对接多个对象存储的前置服务器(Proxy-server)。那么,请求过程如下图:当一个客户端要访问 object.yourcompany.com 的时候,需要将域名转换为 IP 地址进行访问,所以它要请求本地 DNS 解析器;本地 DNS 解析器先查看本地的缓存是否有这个记录。如果有,就直接用,省略后续查询步骤,提高相应时间;如果本地无缓存,就需要请求本地的 DNS 服务器;本地 DNS 服务器一般部署在数据中心或者你所在的运营商网络中。本地 DNS 服务器也需要看本地是否有缓存,如果有,就直接返回;本地没有,通过第 5、6、7 步骤获取到 IP 地址,缓存到本地 DNS 解析器中,然后在返回给客户端。 对于不需要做全局负载均衡的简单应用来讲,yourcompany.com 的权威 DNS 服务器可以直接将 object.yourcompa.com 这个域名解析为一个或者多个 IP 地址,然后客户端可以通过多个 IP 地址,进行简单的轮询,实现简单的负载均衡。 但是对于复制的应用,尤其是跨地域跨运营商的大型应用,就需要更加复杂的全局负载均衡机制,因而需要专门的设备或者服务器来做这件事情,这就是全局负载均衡器(GSLB,Global Server Load Balance)。 在 yourcompany.com 的 DNS 服务器中,一般是通过配置 CNAME 的方式,给 object.yourcompany.com 起一个别名。例如 object.vip.yourcompany.com,然后告诉本地 DNS 服务器,让它请求 GSLB 解析这个域名,GSLB 就可以在解析这个域名的过程中,通过自己的策略实现负载均衡。上图中画了两层的 GSLB,是因为分运营商和地域。我们希望不同运营商的客户,可以访问对应运营商机房中的资源,这样不跨运营商访问,有利于提高吞吐量,减少时延。两层 GSLB 的过程如下:第一层 GSLB,通过查看请求它的本地 DNS 服务器所在的运营商,就知道用户所在的运营商。假设是移动,通过 CNAME 的方式,通过另一个别名 object.yd.yourcompany.com,告诉本地 DNS 服务器去请求第二层的 GSLB;第二层 GSLB,通过查看请求它的本地 DNS 服务器的地址,知道用户所在的地理位置,然后将距离用户位置比较近的一个 Region 的六个内部负载均衡的地址,返回给本地 DNS 服务器;本地 DNS 服务器将结果返回给本地 DNS 解析器;本地 DNS 解析器将结果缓存后,返回给客户端;客户端开始访问属于相同运营商的,且距离比较近的 Region1 中的对象存储。当然,客户端得到了六个 IP 地址,它可以通过负载均衡的方式,随机或者轮询选择一个可用区进行访问。对象存储一般会有三个备份,从而实现对存储读写的负载均衡。小结DNS 是网络世界的地址簿。可以通过域名查地址,因为域名服务器是按照树状结构组织的,因而域名查找是使用递归查询的方式,并通过缓存的方式加快效率;在域名和 IP 的映射中,给了应用基于域名做负载均衡的机会,可以是简单的负载均衡,也可以是根据地址和运营商做的全局负载均衡。参考:维基百科-域名系统 词条;知乎-域名解析;刘超 - 趣谈网络协议系列课; ...

December 26, 2018 · 2 min · jiezi

网络协议 14 - 流媒体协议:要说爱你不容易

【前五篇】系列文章传送门:网络协议 9 - TCP协议(下):聪明反被聪明误网络协议 10 - Socket 编程(上):实践是检验真理的唯一标准网络协议 11 - Socket 编程(下):眼见为实耳听为虚网络协议 12 - HTTP 协议:常用而不简单网络协议 13 - HTTPS 协议:加密路上无尽头 大家都会关注“在浏览器输入一个地址,然后回车,会发生什么”这样一个问题,但是有没有想过这样一个问题:主播开始直播,用户打开客户端观看,这个过程发生了什么? 随着技术的发展,直播技术对人们生活的渗透日益加深。从最开始的游戏直播,到前几天爆出来的教育直播,甚至现在都有直播招聘。 而我们喜欢的这些直播,他们用到的传输协议有一个通用名-流媒体传输协议。 要认识流媒体协议,就离不开下面的三大系列名词。三大系列名词系列一:AVI、MPEG、RMVB、MP4、MOV、FLV、WebM、WMV、ASF、MKV。是不是就 MP4 看着熟悉?系列二:H.261、H.262、H.263、H.264、H.265。能认出来几个?别着急,重点关注 H.264。系列三:MPEG-1、MPEG-2、MPEG-4、MPEG-7。是不是更懵逼了? 在解释上面的三大系列名词之前,咱们先来了解下,视频究竟是什么? 博主记得小时候,经常会玩一种叫动感画册的东西。一本很小的画册上,每一页都画了一幅图,用手快速的翻过每一页,就能看到一个很短的“动画片”。 没错,咱们看到的视频,本质上就是一连串快速播放的图片。 每一张图片,我们称为一帧。只要每秒钟的数据足够多,也就是播放速度足够快,人眼就看不出是一张张独立的图片。对于人眼而言,这个播放临界速度是每秒 30 帧,而这里的 30 也就是我们常说的帧率(FPS)。 每一张图片,都是由像素组成,而每个像素又是由 RGB 组成,每个 8 位,共 24 位。 我们假设一个视频中的所有图片的像素都是 1024*768,可以大概估算下视频的大小:每秒钟大小 = 30 帧 x 1024 x 768 x 24 = 566,231,010 Bits = 70,778,880 Bytes 按我们上面的估算,一分钟的视频大小就是 4,,246,732,800 Bytes,这里已经有 4 个 G 了。 是不是和我们日常接触到的视频大小明显不符?这是因为我们在传输的过程中,将视频压缩了。 为什么要压缩视频?按我们上面的估算,一个一小时的视频,就有 240G,这个数据量根本没办法存储和传输。因此,人们利用编码技术,给视频“瘦身”,用尽量少的 Bit 数保持视频,同时要保证播放的时候,画面仍然很清晰。实际上,编码就是压缩的过程。视频和图片的压缩特点我们之所以能够对视频流中的图片进行压缩,因为视频和图片有下列这些特点:空间冗余:图像的相邻像素之间有较强的相关性,一张图片相邻像素往往是渐变的,而不是突变的,没必要每个像素都完整的保存,可以隔几个保存一个,中间的用算法计算出来。时间冗余:视频序列的相邻图像之间内容相似。一个视频中连续出现的图片也不是突变的,可以根据已有的图片进行预测和推断。视觉冗余:人的视觉系统对某些细节不敏感,因此不会注意到每一个细节,可以允许丢失一些数据。编码冗余:不同像素值出现的概率不同,概率高的用的字节少,概率低的用的字节多,类似霍夫曼编码的思路。 从上面这些特点中可以看出,用于编码的算法非常复杂,而且多种多样。虽然算法多种,但编码过程实际上是类似的,如下图:视频编码的两大流派 视频编码的算法这么多,能不能形成一定的标准呢?当然能,这里咱们就来认识下视频编码的两大流派。流派一:ITU(International tELECOMMUNICATIONS Union)的 VCEG(Video Coding Experts Group),这个称为国际电联下的 VCEG。既然是电信,可想而知,他们最初是做视频编码,主要侧重传输。我们上面的系列名词二,就是这个组织制定的标准。流派二:ISO(International Standards Organization)的 MPEG(Moving Picture Experts Group),这个是 ISO 旗下的 MPEG。本来是做视频存储的,就像咱们场面常说的 VCD 和 DVD。后来也慢慢侧重视频传输了。系列名词三就是这个组织制定的标准。后来,ITU-T(国际电信联盟电信标准化部门)与 MPEG 联合制定了 H.264/MPEG-4 AVC,这也是我们重点关注的。直播数据传输 视频经过编码之后,生动活泼的一帧帧图像就变成了一串串让人看不懂的二进制。这个二进制可以放在一个文件里,然后按照一定的格式保存起来,这里的保存格式,就是系列名词一。 编码后的二进制文件就可以通过某种网络协议进行封装,放在互联网上传输,这个时候就可以进行网络直播了。 网络协议将编码好的视频流,从主播端推送到服务器,在服务器上有个运行了同样协议的服务端来接收这些网络数据包,从而得到里面的视频流,这个过程称为接流。 服务端接到视频流之后,可以滴视频流进行一定的处理,比如转码,也就是从一个编码格式转成另一种格式,这样才能适应各个观众使用的客户端,保证他们都能看到直播。 流处理完毕后,就可以等待观众的客户端来请求这些视频流。观众的客户端请求视频流的过程称为拉流。 如果有非常多的观众同时看一个视频直播,都从一个服务器上拉流,压力就非常大,因此需要一个视频的分发网络,将视频预先加载到就近的边缘节点,这样大部分观众就能通过边缘节点拉取视频,降低服务器的压力。 当观众将视频流拉下来后,就需要进行解码,也就是通过上述过程的逆过程,将一串串看不懂的二进制转变成一帧帧生动的图片,在客户端播放出来。 整个直播过程,可以用下图来描述: 接下来,我们依次来看一下每个过程:编码:将丰富多彩的图片变成二进制流 虽然我们说视频是一张张图片的序列,但如果每张图片都完整,就太大了,因而会将视频序列分成三种帧:I帧,也称关键帧。里面是完整的图片,只需要本帧数据,就可以完成解码。P帧,前向预测编码帧。P 帧表示的是这一帧跟之前一个关键帧(或 P 帧)的差别,解码时需要用之前缓存的画面,叠加上和本帧定义的差别,生成最终画面。B帧,双向预测内插编码帧。B 帧记录的是本帧与前后帧的差别。要解码 B 帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的数据与本帧数据的叠加,取得最终的画面。 可以看出,I 帧最完整,B 帧压缩率最高,而压缩后帧的序列,应该是 IBBP 间隔出现。这就是通过时序进行编码。 在一帧中,分成多个片,每个片中分成多个宏块,每个宏块分成多个子块,这样将一张大图分解成一个个小块,可以方便进行空间上的编码。如下图: 尽管时空非常立体的组成了一个序列,但总归还是要压缩成一个二进制流。这个流是有结构的,是一个个的网络提取层单元(NALU,Network Abstraction Layer Unit)。变成这种格式就是为了传输,因为网络上的传输,默认的是一个个的包,因而这里也就分成了一个个的单元。 如上图,每个 NALU 首先是一个起始标识符,用于标识 NALU 之间的间隔。然后是 NALU 的头,里面主要配置了 NALU 的类型。最后的 Payload 里面是 NALU 承载的数据。 在 NALU 头里面,主要的内容是类型 NAL Type,其中:0x07 表示 SPS,是序列参数集,包括一个图像序列的所有信息,如图像尺寸、视频格式等。0x08 表示 PPS,是图像参数集,包括一个图像的所有分片的所有相关信息,包括图像类型、序列号等。 在传输视频流之前,剥削要传输者两类参数,不然就无法解码。为了保证容错性,每一个 I 帧之前,都会传一遍这两个参数集合。 如果 NALU Header 里面的表示类型是 SPS 或 PPS,则 Payload 中就是真正的参数集的内容。 如果类型是帧,则 Payload 中是真正的视频数据。当然也是一帧帧保存的。前面说了,一帧的内容还是挺多的,因而每一个 NALU 里面保存的是一片。对于每一片,到底是 I 帧,还是 P 帧,亦或是 B 帧,在片结构里面也有 Header,这里面有个类型用来标识帧的类型,然后是片的内容。 这样,整个格式就出来了。一个视频,可以拆分成一系列的帧,每一帧拆分成一系列的片,每一片都放在一个 NALU 里面,NALU 之间都是通过特殊的起始标识符分隔,在每一个 I 帧的第一片前面,要插入单独保存 SPS 和 PPS 的 NALU,最终形成一个长长的 NALU 序列。推流:将数据流打包传输到对端 形成 NALU 序列后,还需要将这个二进制的流打包成网络包进行发送。这里我们以 RTMP 协议为例,进入第二个过程,推流。 RTMP 是基于 TCP 的,因而也需要双方建立一个 TCP 连接。在有 TCP 的连接的基础上,还需要建立一个 RTMP 连接,也就是在程序里面,我们调用 RTMP 类库的 Connet 函数,显式创建一个连接。 RTMP 为什么需要建立一个单独的连接呢? 因为通信双方需要商量一些事情,保证后续的传输能正常进行。其实主要就是两个事情:确定版本号。如果客户端、服务端的版本号不一致,就不能正常工作;确定时间戳。视频播放中,时间是很重要的一个元素,后面的数据流互通的时候,经常要带上时间戳的差值,因而一开始双方就要知道对方的时间戳。 沟通这些事情,需要发送 6 条消息:客户端发送 C0、C1、C2服务端发送 S0、S1、S2 首先,客户端发送 C0 表示自己的版本号,不必等对方回复,然后发送 C1 表示自己的时间戳。 服务器只有在收到 C0 的时候,才会返回 S0,表明自己的版本号,如果版本不匹配,可以断开连接。 服务器发送完 S0 后,也不用等待,就直接发送自己的时间戳 S1。 客户端收到 S1 时,发一个知道了最烦时间戳的 ACK C2。同理,服务器收到 C1 的时候,发一个知道了对方时间戳的 ACK S2。 于是,握手完成。 握手之后,双方需要互相传递一些控制信息,例如 Chunk 块的大小、窗口大小等。 真正传输数据的时候,还是需要创建一个流 Stream,然后通过这个 Stream 来推流。 推流的过程,就是讲 NALU 放在 Message 里面发送,这个也称为 RTMP Packet 包。其中,Message 的格式就像下图所示: 发送的时候,去掉 NALU 的起始标识符。因为这部分对于 RTMP 协议来讲没有用。接下来,将 SPS 和 PPS 参数集封装成一个 RTMP 包发送,然后发送一个个片的 NALU。 RTMP 在收发数据的时候并不是以 Message 为单位的,而是把 Message 拆分成 Chunk 发送,而且必须在一个 Chunk 发送完成之后,才能开始发送下一个 Chunk。每个 Chunk 中都带有 Message ID,表示属于哪个 Message,接收端也会按照这个 ID 将 Chunk 组装成 Message。 前面连接的时候,设置 Chunk 块大小就是指这个 Chunk。将大的消息变为小的块再发送,可以在低带宽的情况下,减少网络拥塞。 下面用一个分块的示例,来了解下 RTMP 是如何分块的。 假设一个视频的消息长度是 307,而 Chunk 大小约定为 128,那么消息就会被拆分为 3 个 Chunk。 第一个 Chunk 的 Type = 0,表示 Chunk 头是完整的。头里面 Timestamp 为 1000,总长度 Length 为 307,类型为 9,是个视频,Stream ID 为 12346,正文部分承担 128 个字节的 Data。 第二个 Chunk 也要发送 128 个字节,但是由于 Chunk 头和第一个一样,因此它的 Chunk Type = 3,表示头和第一个 Chunk 一样。 第三个 Chunk 要发送的 Data 的长度为 51 个字节,Chunk Type 还是用的 3。 就这样,数据源源不断的到达流媒体服务器,整个过程就像下图: 这个时候,大量观看直播的观众就可以通过 RTMP 协议从流媒体服务器上拉取。为了减轻服务器压力,我们会使用分发网络。 分发网络分为中心和边缘两层。边缘层服务器部署在全国各地及横跨各大运营商里,和用户距离很近。而中心层是流媒体服务集群,负责内容的转发。 智能负载均衡系统,根据用户的地理位置信息,就近选择边缘服务器,为用户提供推/拉流服务。中心层也负责转码服务。例如,将 RTMP 协议的码流转换成 HLS 码流。拉流:观众的客户端看到直播视频 接下来,我们再来看看观众通过 RTMP 拉流的过程。 先读到的是 H.264 的解码参数,例如 SPS 和 PPS,然后对收到的 NALU 组成一个个帧,进行解码,交给播放器播放,这样客户端就能看到直播视频了。小结视频名词比较多,编码两大流派达成了一致,都是通过时间、空间的各种算法来压缩数据;压缩好的数据,为了传输而组成一系列的 NALU,按照帧和片依次排列;排列好的 NALU,在网络传输的是,要按照 RTMP 包的格式进行包装,RTMP 的包会拆分成 Chunk 进行传输;推送到流媒体集群的视频流经过转码和分发,可以被客户端通过 RTMP 协议拉取,然后组合成 NALU,解码成视频格式进行播放。参考:RTMP 协议规范;刘超 - 趣谈网络协议系列课; ...

December 19, 2018 · 2 min · jiezi

SpringCloud灰度发布实践

服务实例eureka-serverzuul -serverApollo-configprovider-test启动两个服务实例,一个带有版本号信息,一个没有port:7770 version:无port: 7771 version:v1consumer-test启动两个服务实例,一个带有版本号信息,一个没有任何信息.分别为:port:8880 version:无port:8881 version: v1场景分析 公司采用的是SpringCloud微服务体系,注册中心为eureka。我们知道,对于eureka-server而言,其他服务都是eureka-client的存在,我们在业务服务中只需要引入@EnableDiscoveryClient即可实现注册. 比如我们想要调用order-service的服务,只需要通过resttemplate或者fegin的方式指定该服务的服务名即可完成调用。这是为什么呢?这一切还得从Ribbon这个背后的男人说起,因为ribbon会根据order-service的服务实例名获取该服务实例的列表,这些列表中包含了每个服务实例的IP和端口号,Ribbon会从中根据定义的负载均衡算法选取一个作为返回. 看到这里一切都有点拨云见雾了,那么意味着是不是只要能够让Ribbon用我们自定义的负载均衡算法就可以为所欲为了呢?显然,是的!简单分析下我们在调用过程中的集中情况:外部调用:请求==>zuul==>服务zuul在转发请求的时候,也会根据Ribbon从服务实例列表中选择一个对应的服务,然后选择转发.内部调用:服务==>Resttemplate==>服务服务==>Fegin==>服务无论是通过Resttemplate还是Fegin的方式进行服务间的调用,他们都会从Ribbon选择一个服务实例返回.上面几种调用方式应该涵盖了我们平时调用中的场景,无论是通过哪种方式调用(排除直接ip:port调用),最后都会通过Ribbon,然后返回服务实例.设计思路eureka预备知识 eureka元数据:标准元数据:主机名,IP地址,端口号,状态页健康检查等信息自定义元数据:通过eureka.instance.metadata-map配置更改元数据:源码地址:com.netflix.eureka.resources.InstanceResource.updateMetadata()接口地址: /eureka/apps/appID/instanceID/metadata?key=value调用方式:PUTE流程解析: 1.在需要灰度发布的服务实例配置中添加eureka.instance.metadata-map.version=v1,注册成功后该信息后保存在eureka中.配置如下:eureka.instance.metadata-map.version=v1 2.自定义zuul拦截器GrayFilter。当请求带着token经过zuul时,根据token得到userId,然后从分布式配置中心Apollo中获取灰度用户列表,并判断该用户是否在该列表中(Apollo非必要配置,由于管理端比较完善所以笔者这里选择采用). 若在列表中,则把version信息存在ThreadLocal中,从而使Ribbon中我们自定义的Rule能够拿到该信息;若不在,则不做任何处理。 但是我们知道hystrix是用线程池做隔离的,线程池中是无法拿到ThreadLocal中的信息的! 所以这个时候我们可以参考Sleuth做分布式链路追踪的思路或者使用阿里开源的TransmittableThreadLocal.为了方便继承,这里采用Sleuth的方式做处理。Sleuth能够完整记录一条跨服务调用请求的每个服务请求耗时和总的耗时,它有一个全局traceId和对每个服务的SpanId.利用Sleuth全局traceId的思路解决我们的跨线程调用,所以这里可以使用HystrixRequestVariableDefault实现跨线程池的线程变量传递效果. 3.zuul在转发之前会先到我们自定义的Rule中,默认Ribbon的Rule为ZoneAvoidanceRule.自定义编写的Rule只需要继承ZoneAvoidanceRule并且覆盖其父类的PredicateBasedRule#choose()方法即可.该方法是返回具体server,源码如下public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule { public abstract AbstractServerPredicate getPredicate(); @Override public Server choose(Object key) { ILoadBalancer lb = getLoadBalancer(); Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key); if (server.isPresent()) { return server.get(); } else { return null; } }}这里就完成了zuul转发到具体服务的灰度流程了,流程图如下4.上面完成了zuul–>服务的灰度过程,接下来就是解决服务与服务间的调用.服务与服务间的调用依然可以用Sleuth的方式解决,只需要把信息添加到header里面即可.在使用RestTemplate调用的时候,可以添加它的拦截器ClientHttpRequestInterceptor,这样可以在调用之前就把header信息加入到请求中.restTemplate.getInterceptors().add(YourHttpRequestInterceptor());5.到这里基本上整个请求流程就比较完整了,但是我们怎么使用自定义的Ribbon的Rule了?这里其实非常简单,只需要在properties文件中配置一下代码即可.yourServiceId.ribbon.NFLoadBalancerRuleClassName=自定义的负载均衡策略类但是这样配置需要指定服务名,意味着需要在每个服务的配置文件中这么配置一次,所以需要对此做一下扩展.打开源码org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration类,该类是Ribbon的默认配置类.可以清楚的发现该类注入了一个PropertiesFactory类型的属性,可以看到PropertiesFactory类的构造方法 public PropertiesFactory() { classToProperty.put(ILoadBalancer.class, “NFLoadBalancerClassName”); classToProperty.put(IPing.class, “NFLoadBalancerPingClassName”); classToProperty.put(IRule.class, “NFLoadBalancerRuleClassName”); classToProperty.put(ServerList.class, “NIWSServerListClassName”); classToProperty.put(ServerListFilter.class, “NIWSServerListFilterClassName”); }所以,我们可以继承该类从而实现我们的扩展,这样一来就不用配置具体的服务名了.至于Ribbon是如何工作的,这里有一篇方志明的文章(传送门)可以加强对Ribbon工作机制的理解6.这里就完成了灰度服务的正确路由,若灰度服务已经发布测试完毕想要把它变成正常服务,则只需要通过eureka的RestFul接口把它在eureka的metadata-map存储的version信息置空即可.7.到这里基本上整个请求流程就比较完整了,上述例子中是以用户ID作为灰度的维度,当然这里可以实现更多的灰度策略,比如IP等,基本上都可以基于此方式做扩展 ...

December 18, 2018 · 1 min · jiezi

网络协议 13 - HTTPS 协议:加密路上无尽头

系列文章传送门:网络协议 1 - 概述网络协议 2 - IP 是怎么来,又是怎么没的?网络协议 3 - 从物理层到 MAC 层网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校网络协议 5 - ICMP 与 ping:投石问路的侦察兵网络协议 6 - 路由协议:敢问路在何方?网络协议 7 - UDP 协议:性善碰到城会玩网络协议 8 - TCP 协议(上):性恶就要套路深网络协议 9 - TCP协议(下):聪明反被聪明误网络协议 10 - Socket 编程(上):实践是检验真理的唯一标准网络协议 11 - Socket 编程(下):眼见为实耳听为虚网络协议 12 - HTTP 协议:常用而不简单 之前说了 HTTP 协议的各种问题,但是它还是陪伴着互联网、陪伴着我们走过了将近二十年的风风雨雨。现在有很多新的协议尝试去取代它,来解决性能、效率等问题,但它还还能靠着“多年的情分”活的滋润。然而,近些年,因为致命的安全问题,它不得不升级成 HTTPS 了。 就拿我们叫外卖来说,我们点外卖的数据包被黑客截获,然后在服务器回复你之前给你回复一个假消息:“好啊,你该付款了,把银行卡号、密码拿来。”,这时如果你真的把卡号和密码发给他,那你的钱包就真的危险了。 为了解决这些问题,我们给 HTTP 引入了加密,变成了 HTTPS。大家千万不要以为 HTTPS 是个新的协议,它实际上就是:HTTPS = HTTP + SSL 层 这里的 SSL 层的主要工作就是加密。加密方式一般分为两种:对称加密和非对称加密。 这两种加密算法,对称加密要比非对称加密的效率要高很多,性能也好很多,所以交互的场景下多用对称加密。对称加密 在对称加密算法中,加密和解密的密钥是相同的。也就是说,加密和解密使用的是同一个密钥。因此,使用者一定要做好保密功能,不能让第三方知道。 假设叫外卖的你和第三方约定了一个密钥 A,你发送请求的时候用 A 进行加密,外卖网站也用 A 进行解密,这样就算黑客截获了你们的请求,但是没有正确的密钥,还是破解不了。 看起来很好的解决了黑客的问题。但是这里又引入了一个问题,你和外卖网站怎么来约定这个密钥呢?如果这个密钥在互联网上传输,就必须还得用 B 密钥来加密,否则被黑客获取到 A,你们的交互还是不安全,而且,你们又怎么约定 B 呢?所以,只能通过线下传输。 线下传输的话,看过《肖申克的救赎》的博友应该知道,安迪越狱前给瑞德约定了一个地点,让他去那里拿一个信封,里面写着他的住处。 那我们和外卖网站也可以用这样的骚操作,偷偷约定时间地点,它给你一个纸条,上面写着你们两个的密钥,然后就用这个密钥在互联网定外卖。 打住打住,上面这个操作想想都不可思议,如果最初的互联网是这样发展的话,那相信肯定活不久。 相信你也发现了,只有对称加密,就会陷入密钥安全问题的死循环里,这时候,就需要非对称加密了。非对称加密 在非对称加密中 ,加密和解密过程中使用两个不相同的密钥。一个是公开的公钥,另一个是谁都不给的私钥。公钥加密的信息,只有私钥才能解密,而私钥加密的信息,也只有公钥才能解密。 放到外面上面的叫外卖过程中,非对称加密的私钥由外卖网站保存,不会再网上传输,这样就保证了私钥的私密性。与之对应的公钥是可以在互联网上随意传播的,只要外卖网站把这个公钥给你,你们就可以安全的互通了。 还是来看我们点外卖的过程。我们用公钥加密,说“我要豆浆加油条”。黑客在中间截获了这个数据包,但是他没有私钥,没法解密数据,因此可以顺利到达外卖网站。而外卖网站用私钥把这个报文解出来,然后回复,“我知道了,你付款吧,给我卡号和密码”。 整个过程好像很安全,再也不怕黑客了。但是,先别太乐观,你的银行卡是安全了,但是外卖网站可还是有危险的。黑客有外卖网站的公钥,可以模拟发送“我要定外卖”这个信息。 为了解决这个问题,看来一对公钥私钥是不够的,客户端也需要有自己的公钥和私钥,并且客户端也要把自己的公钥给外卖网站。 这样,客户端给外卖网站发送信息的时候,用外卖网站的公钥加密,而外卖网站给客户端发送消息的时候,使用客户端的公钥。这样就算有黑客企图模拟客户端获取一些信息,或者半路截获回复信息,但是由于它没有私钥,这些信息它还是打不开。 说了那么多,相信你也发现了,非对称加密也会有同样的问题,如何将不对称加密的公钥给对方?这时有两种可行方式,一种是放在一个公网的地址上,让对方下载,另一种就是在建立连接的时候传给对方。 这两种方法也有相同的问题。作为普通网民,你怎么鉴别别人给你的公钥是对方的,而不是被黑客冒充的?要知道,每个人都是可以创建自己的公钥和私钥的,创建过程如下:# bash// 创建私钥:openssl genrsa -out httpsprivate.key 1024// 根据私钥获取公钥openssl rsa -in httpsprivate.key -pubout -out httpspublic.pemHTTPS 证书 可以看到,通过工具,我们可以很容易的创建公钥和私钥,那么黑客也是可以创建的,咱们怎么知道外卖网站传过来的公钥是不是真的就是外卖网站的呢?这时候,就需要第三方机构来当这个中间人了。 这就像我们的户口本一样,每个人都可以打印出来,说是真的户口本,但是去使用的时候,人家就只认有公安局盖章的户口本。这个由权威部门颁发的称为**证书(Certificate)。 HTTPS 证书里面应该有以下内容:公钥:这是最重要的;所有者:说明证书是属于谁的,就像户口本上的姓名和身份证号,来证明这个户口本是你的;证书发布机构:看看你的户口本上有没有某某公安局的字样?证书有效时间:这个和咱们身份证有效期是一个意思。 说完了证书的内容,就到了下一步,怎么获取证书?这就像家里添了个小公举,去哪里上户口呢?恐怕大家都知道去公安局。与之对应的,HTTPS 也有专门负责派发证书的机构,这个机构我们称为 CA(Certificate Authrity)。而证书则可以通过下面这个命令生成:openssl req -key httpsprivate.key -new -out httpscertificate.req 将这个请求发给 CA,CA 会给这个证书“盖”一个章,我们称为签名算法。这个签名用到 CA 的私钥进行签发,来保证签名不被伪造。 签名算法大概是这样工作的:一般是对信息做一个 Hash 计算,得到一个 Hash 值,这个过程是不可逆的,也就是说无法通过 Hash 值还原回原来的信息内容。再把信息发出时,把上面得到的 Hash 加密后,作为一个签名和信息一起发出去。CA 给整数签名的命令是:openssl x509 -req -in httpscertificate.req -CA cacertificate-pem -CAkey caprivate.key 这个命令会返回 Signature ok,而 httpscertificate.pem 就是签名过的整数。CA 用自己的私钥给外卖网站的公钥签名,这就相当于给外卖网站背书,形成了外卖网站的证书。我们可以通过下面这个命令查看证书内容:openssl x509 -in httpscertificate.pem -noout -text 证书会显示以下内容:lssuer:证书颁发者;Subject:证书颁发给谁;Validity:证书期限;Public-key:公钥内容;Sinature Algorithm:签名算法 通过这种方式,我们访问外卖网站时,得到的不再是一个公钥,而是一个整数。这个证书里有发布机构 CA,你只要通过这个 CA 的公钥去解密外卖网站证书的签名,解密成功,Hash 对的上,就说明外卖网站的公钥是真实可信的。 上述整个过程中,都有一个前提,CA 是可信的。但是,我们又怎么确定 CA 的公钥就是对的呢?这就像有的人在偏远农村搞了个假公安局一样(应该没人这么干吧),我们怎么知道公安局是不是假的呢?然后我们就会想到,我去县公安局确认下当地公安局的信息不就好了。没错,CA 也是这么干的。 CA 的公钥也需要更牛的 CA 给它签名,然后形成 CA 的公钥。要想知道某个 CA 的证书是否可靠,要看 CA 的上级证书的公钥能不能解开这个 CA 的签名。这样追根溯源,直到全球皆知的几大著名 CA,我们称为Root CA,做最后的背书。正是通过这种层层授信背书的形式,保证了非对称加密模式的争吵运转。 除此之外,还有一种证书, 称为Self-Signed Certificate,就是自己给自己签名。这个就给人一种“我就是我,不一样的烟火,你爱信不信”的感觉,有兴趣的博友可以自行搜索了解。HTTPS 的工作模式 上面说了对称加密和非对称加密的原理,我们知道了非对称加密在性能上远不如对称加密,那在 HTTP 中,能否将两者结合起来呢?例如,公钥私钥主要用于传输对称加密的密钥,而真正的双方大数据量的通信都是通过对称加密进行。 是的,HTTPS 协议的思路就是这样的。如下图: 图比较长,整个过程最后的目标是生成在后续通信过程中使用的对称密钥,以及约定使用的加密算法。整体过程如下:客户端明文发送 TLS 版本信息、加密套件候选列表、压缩算法候选列表等信息,另外还会发送一个随机数,在协商对称密钥的时候使用(你好,我想定外卖,但你要保密我点了什么。这是我的加密套路列表,还有一个随机数 A,你留着);服务器返回 Server Hello 消息,告诉客户端,服务器选择使用的协议版本、加密套件、压缩算法等,还有一个随机数 B,用于后续进行密钥协商(你好,保密没问题,就按套路 2 来吧,我也给你一个随机数 B,你留着);服务器给客户端证书;客户端从自己信任的 CA 仓库中,拿 CA 的证书里面的公钥去解密服务器传来的证书。解密成功,说明外卖网站是可信的。这个解密过程,客户端可能胡不断往上追溯 CA、CA 的 CA、CA 的 CA 的 CA,直到一个授信的 CA 为止;证书验证可信后,客户端会计算产生随机数字 Pre-master,发送Client Key Exchange,用证书中的公钥加密,再发给服务器; 到此时,无论是客户端还是服务端,都有了三个随机数,分别是:A、B、Pre-master。通过这三个随机数,客户端和服务端可以产生相同的对称密钥。 约定好对称密钥和加密算法,就可以用对称加密的形式进行加密通信了,后续的通信除了多了一步密钥校验的过程,HTTP 协议里的那些过程都不会少。 不过上面的过程中只包含了 HTTPS 的单向认证,也就是客户端验证服务端的证书,这也是最常见的场景。不过在更加严格要求通信安全的情况下,也可以启用双向认证,双方互相验证证书。 通过上面的整个过程,我们可以看出,HTTPS 协议并不是一个新的协议,它只是 HTTP 协议与一些加密算法的组合,用来保证通信的安全。 虽然上面介绍的非对称加密方式,在现在看来是完美不可解的,但未来谁知道呢?正所谓“道高一尺魔高一丈”,加密安全路上永无尽头。小结加密分对称加密和非对称加密。对称加密效率高,但存在密钥传输的问题;非对称加密可以解决密钥传输的问题,但效率较低。非对称加密需要通过证书和权威机构来验证公钥的合法性。HTTPS 是综合了对称加密和非对称加密算法的 HTTP 协议。既保证了传输安全,也保证了传输效率。参考:百度百科 - htps 词条;刘超 - 趣谈网络协议系列课; ...

December 17, 2018 · 2 min · jiezi

网络协议 12 - HTTP 协议:常用而不简单

系列文章传送门:网络协议 1 - 概述网络协议 2 - IP 是怎么来,又是怎么没的?网络协议 3 - 从物理层到 MAC 层网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校网络协议 5 - ICMP 与 ping:投石问路的侦察兵网络协议 6 - 路由协议:敢问路在何方?网络协议 7 - UDP 协议:性善碰到城会玩网络协议 8 - TCP 协议(上):性恶就要套路深网络协议 9 - TCP协议(下):聪明反被聪明误网络协议 10 - Socket 编程(上):实践是检验真理的唯一标准网络协议 11 - Socket 编程(下):眼见为实耳听为虚 网络协议五层通天路,咱们从物理层、到链路层、网络层、再到传输层,现在又进一步,来到了应用层。这也是我们五层协议里最上面的一层,关于应用层,有太多协议要了解。但要说最有名的,那肯定就是 HTTP 了。 HTTP 协议,几乎是每个人上网用的第一个协议,同时也是很容易被人忽略的协议。 就像 http://blog.muzixizao.com/,是个 URL,叫作统一资源定位符。之所以叫统一,是因为它是有规定格式的。HTTP 称为协议,blog.muzixizao.com 是一个域名,表示互联网的一个位置。有的 URL 会有更详细的位置标识,例如http://blog.muzixizao.com/?p=140 正是因为格式是统一的,所以当你把这样一个字符串输入到浏览器的框里的时候,浏览器才知道如何进行统一处理。HTTP 请求的准备 浏览器会将 blog.muzixizao.com 这个域名发送给 DNS 服务器,让它解析为 IP 地址。关于 DNS 解析的过程,较为复杂,后面会专门介绍。 域名解析成 IP 后,下一步是干嘛呢? 还记得吗?HTTP 是基于 TCP 协议的,所以接下来就是建立 TCP 连接了。具体的连接过程可点击这里查看。 目前使用的 HTTP 协议大部分都是 1.1.在 1.1 协议里面,默认开启了 Keep-Alive 的,这样建立的 TCP 连接,就可以在多次请求中复用。虽然 HTTP 用了各种方式来解决它存在的问题,但基于TCP 的它,每次建立连接的三次握手以及断开连接的四次挥手,这个过程还是挺费时的。如果好不容易建立了连接,然后做一点儿事情就结束了,未免太浪费了。HTTP 请求的构建 建立了连接以后,浏览器就要发送 HTTP 的请求。请求的格式如下图: 如图,HTTP 的报文大概分为请求行、首部、正文实体三部分。接下来,咱们就来一一认识。请求行 在请求行中,URL 就是 http://blog.muzixizao.com,版本为 HTTP 1.1。这里要说一下的,就是对应的请求方法。有以下几种类型:1)GET 请求 对于访问网页来讲,最常用的类型就是 GET。顾名思义,GET 就是去服务器获取一些资源。对于访问网页来讲,要获取的资源往往是一个页面。其实也有很多其他的格式,比如返回一个 JSON 字符串。当然,具体要返回什么,是由服务端决定的。 例如,在云计算中,如果我们的服务端要提供一个基于 HTTP 协议的 API,获取所有云主机的列表,就会使用 GET 方法请求,返回的可能是一个 JSON 字符串,字符串里面是一个列表,列表里面会有各个云主机的信息。2)POST 请求 另一种类型叫做 POST。它需要主动告诉服务端一些信息,而非获取。而要告诉服务端的信息,一般都放在正文里面。正文里有各种各样的格式,最常见的的就是 JSON了。 例如,我们平时的支付场景,客户端就需要把 “我是谁?我要支付多少?我要买什么?” 这样信息告诉服务器,这就需要 POST 方法。 再如,在云计算里,如果我们的服务器,要提供一个基于 HTTP 协议的创建云主机的 API,也会用到 POST 方法。这个时候往往需要将 “我要创建多大的云主机?多少 CPU 和多少内存?多大硬盘?” 这些信息放在 JSON 字符串里面,通过 POST 的方法告诉服务器。 除了上面常见的两种类型,还有一种 PUT 类型,这种类型就是向指定资源位置上传最新内容。但是 HTTP 的服务区往往是不允许上传文件的,所以 PUT 和 POST 就都变成了要传给服务器东西的方法。 在我们的实际使用过程中,PUT 和 POST 还是有区别的。POST 往往是用来创建一个资源,而 PUT 往往是用来更新一个资源。 例如,云主机已经创建好了,想对云主机打一个标签,说明这个云主机是生产环境的,另外一个云主机是测试环境的。我们修改标签的请求往往就是用 PUT 方法。 还有 DELETE 方法。这个是用来删除资源的。首部字段 请求行下面就是首部字段。首部是 key-value 格式,通过冒号分割。这里面,往往保存了一些非常重要的字段。Accpet-Charset:客户端可以接受的字符集。防止传过来的字符串客户端不支持,从而出现乱码;Content-Type:正文格式。我们进行 POST 请求时,如果正文是 JSON,我们就应该将这个值设置为 application/json;缓存字段 Cache-Control、If-Modified-Since。 这里重点认识下缓存字段。为什么要使用缓存呢?这是因为一个非常大的页面有很多东西。 例如,我们浏览一个商品的详情,里面有商品的价格、库存、展示图片、使用手册等待。 商品的展示图片会保持较长时间不变,而库存胡一根筋用户购买情况经常改变。如果图片非常大,而库存数非常小,如果我们每次要更新数据的时候都要刷新整个页面,对于服务器的压力也会很大。 对于这种高并发场景下的系统,在真正的业务逻辑之前,都需要有个接入层,将这些静态资源的请求拦在最外面。架构就像下图: 其中 DNS、CDN 会在后面的章节详细说明。这里咱们就先来了解下 Nginx 这一层。它是如果处理 HTTP 协议呢?对于静态资源,有 Vanish 缓存层,当缓存过期的时候,才会访问真正的 Tomcat 应用集群。 在 HTTP 头里面,Cache-Control 是用来控制缓存的。当客户端发送的请求中包含 max-age 指令时,如果判定缓存层中,资源的缓存时间数值比指定时间的数值校,那么客户端可以接受缓存的资源;当指定 max-age 值为 0,那么缓存层通常需要将请求转发给应用集群。 另外,If-Modified-Since 也是关于缓存的字段,这个字段是说,如果服务器的资源在某个时间之后更新了,那么客户端就应该下载最新的资源;如果没有更新,服务端会返回“304 Not Modified” 的响应,那客户端就不用下载了,也会节省带宽。 到此,我们拼凑起了 HTTP 请求的报文格式,接下来,浏览器会把它交给传输层。HTTP 请求的发送 HTTP 协议是基于 TCP 协议的,所以它是以面向连接的方式发送请求,通过 stream 二进制流的方式传给对方。当然,到了 TCP 层,它会把二进制流变成一个个的报文段发送给服务器。 在发送给每个报文段的时候,都需要对方有一个回应 ACK,来保证报文可靠地到达了地方。如果没有回应,那么 TCP 这一层会重新传输,直到可以到达。同一个包有可能被传了好多次,但是 HTTP 这一层不需要知道这一点,因为是 TCP 这一层在埋头苦干。而后续传输过程如下:TCP 层封装目标地址和源地址。TCP 层发送每一个报文的时候,都需要加上自己的地址和它想要去的地址,将这两个信息放到 IP 头里面,交给 IP 层进行传输。IP 层获取 MAC 头。IP 层需要查看目标地址和自己是否在同一个局域网。如果是,就发送 ARP 协议来请求这个目标地址对应的 MAC 地址,然后将源 MAC 和目标 MAC 放入 MAC 头,发送出去;如果不在同一个局域网,就需要发送到网关,这里也要通过 ARP 协议来获取网关的 MAC 地址,然后将源 MAC 和网关 MAC 放入 MAC 头,发送出去。网关转发。网关收到包发现 MAC 符合,取出目标 IP 地址,根据路由协议找到下一跳的路由器,获取下一跳路由器的 MAC 地址,将包发给下一跳路由器。数据包到达目标地址的局域网。通过 ARP 协议获取目标地址的 MAC 地址,将包发出去。目标地址检查信息,返回 ACK。目标机器发现数据包中的 MAC 地址及 IP 地址都和本机匹配,就根据 IP 头中的协议类型,知道是 TCP 协议,解析 TCP 的头,获取序列号。判断序列号是否是本机需要的,如果是,就放入缓存中然后返回一个 ACK,如果不是就丢弃。根据端口号将数据包发送到指定应用。TCP 头里面还有端口号,HTTP 的服务器正在监听这个端口号。于是,目标机器自然指定是 HTTP 服务器这个进程想要这个包,就把数据包发给 HTTP 服务器。HTTP 服务器处理请求。HTTP 服务器根据请求信息进行处理,并返回数据给客户端。HTTP 返回的构建 HTTP 的返回报文也是有一定格式的,如下图:状态行包含状态码和短语。状态码反应 HTTP 请求的结果。200 是大吉大利;404 则是我们最不想见到的,也就是服务端无法响应这个请求。短语中会说明出错原因。首部 key-value。这里常用的有以下字段:Retry-After:客户端应该在多长时间后再次尝试连接;Content-Type:返回数据格式 构造好了返回的 HTTP 报文,接下来就是把这个报文发送出去。当然,还是交给 Socket 去发送,交给 TCP,让 TCP 返回的 HTML 分成一个个小的数据段,并且保证每一段都安全到达。这些小的数据段会加上 TCP 头,然后交给 IP 层,沿着来时的路反向走一遍。虽然不一定是完全相同的路径,但是逻辑过程是一样的,一直到达客户端。 客户端取出数据后 ,会根据端口号交给指定的程序,这时候就是我们的浏览器出马的时候。 浏览器拿到了 HTTP 报文,发现返回 200,一切正常,就从正文中将 HTML 拿出来,展示出一个炫酷吊炸天的网页。 以上就是正常的 HTTP 请求与返回的完整过程。HTTP 2.0 上面提到了,现在用到 HTTP 大多是 1.1 版本,而 HTTP 2.0 在 1.1 的基础上进行了一些优化,以期解决一些问题。 HTTP 1.1 在应用层以纯文本的形式进行通信。每次通信都要带完整的 HTTP 头,而且不考虑 pipeline 模式的话,每次的过程都要像上面描述的那样一去一回。显然,在效率上会存在问题。 为了解决这些问题,HTTP 2.0 会对 HTTP 头进行一定的压缩,将原来每次都要携带的大量 key-value 对在两端建立一个索引表,对相同的头只发送索引表中的索引。 另外,HTTP 2.0 协议将一个 TCP 连接切分成多个流,每个流都有自己的 ID,而且流可以是客户端发给服务端,也可以是服务端发给客户端,它其实只是个虚拟的通道,除此之外,它还有优先级。 HTTP 2.0 将所有的传输信息分割成更小的消息和帧,并对它们采用二进制格式编码。常见的帧有 Header 帧,用于传输 Header 内容,并且会开启一个新的流。还有 Data 帧,用来传输正文实体,并且多个 Data 帧属于同个流。 通过这两种机制,HTTP 2.0 的客户端可以将多个请求分到不同的流中, 然后将请求内容拆分成帧,进行二进制传输。这些帧可以打散乱序发送,然后根据帧首部的流标识符重新组装,并且可以根据优先级,决定先处理哪个流的数据。 针对 HTTP 2.0,我们来看一个例子。 假设我们有一个页面要发送三个独立的请求,一个获取 CSS、一个获取 JS、一个获取图片 jsg。如果使用 HTTP 1.1,这三个请求就是串行的,但是如果使用 HTTP 2.0,就可以在一个连接里,客户端和服务端同时反思多个请求和回应,而且不用按照顺序一对一对应。 如上图。HTTP 2.0 其实是将三个请求变成三个流,将数据分成帧,乱序发送到一个 TCP 连接中。 HTTP 2.0 成功解决了 HTTP 1.1 的队首阻塞问题。同时,也不需要通过 HTTP 1.x 的 pipeline 机制用多条 TCP 连接来实现并行请求与响应,减少了 TCP 连接数对服务器性能的影响,加快页面组件的传输速度。 HTTP 2.0 虽然大大增加了并发性,但由于 TCP 协议的按序处理的特性,还是会出现阻塞的问题。 还记得咱们之前说过的 QUIC 协议吗?这时候就是它登场的时候了。 它用以下四个机制,解决了 TCP 存在的一些问题。机制一:自定义连接机制 我们知道,一条 TCP 连接是由四元组标识的。一旦一个元素发生变化,就需要端口重连。这在移动互联网的情况下,当我们切换网络或者信号不稳定时,都会导致重连,从而增加时延。 TCP 没办法解决上述问题,但是 QUCI 基于 UDP 协议,就可以在自己的逻辑里面维护连接的机制,不再以四元组标识,而是以一个 64 位的随机数作为标识 ID,而且 UDP 是无连接的,只要 ID 不变,就不需要重新建立连接。机制二:自定义重传机制 TCP 为了保证可靠性,通过使用序号和应答机制,来解决顺序问题和丢包问题。 任何一个序号的包发出去,都要在一定时间内得到应答,否则就会超时重发。这个超时时间就是通过采样往返时间 RTT 不断调整的。其实,这个超时时间的采样是不太准确的。 如上图。发送一个包,序号为 100,超时后,再发送一个 100。然后收到了一个 ACK101。这个时候客户端知道服务器已经收到了 100,但是往返时间怎么计算呢?是 ACK 到达时间减去后一个 100 发送的时间,还是减去前一个 100 发送的时间呢?前者把时间算短了,后者把时间算长了。 QUIC 也有一个序列号,是完全递增的。任何一个包发送一次后,下一次序列号就要加一。像我们上面的例子,在 QUIC 协议中,100 的包没有返回,再次发送时,序号就是 101 了,如果返回是 ACK100,就是对第一个包的响应,如果返回 ACK101,就是对第二个包的响应,RTT 时间计算相对准确,过程如下图: 上面的过程中,有的童鞋可能会问了,两个序号不一样的包,服务器怎么知道是同样的内容呢?没错,这确实是个问题。为了解决这个问题,QUIC 协议定义了一个 Offset 的概念。 QUIC 既然是面向连接的,也就像 TCP 一样,是一个数据流。,发送的数据在这个流里面都有个偏移量 Offset,可以通过 Offset 查看数据发送到了那里,这样只要这个 Offset 的包没有来,就要重发。如果来了,就按照 Offset 拼接成一个流。机制三:无阻塞的多路复用 有了自定义的连接和重传机制,我们就可以解决上面 HTTP 2.0 的多路复用问题。 同 HTTP 2.0 一样,同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求。更棒的是,QUIC 是基于 UDP 的,一个连接上的多个 stream 之间没有依赖。这样,假如 stream2 丢了一个 UDP 包,后面跟着 stream3 的一个 UDP 包,虽然 stream2 的那个包需要重传,但是 stream3 的包无需等待,就可以发给用户。机制四:自定义流量控制 TCP 的流量控制是通过滑动窗口协议。QUIC 的流量控制也是通过 window_update,来告诉对端它可以接受的字节数。但是 QUIC 的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个 stream 控制窗口。 还记得吗?在 TCP 协议中,接收端的窗口的起始点是下一个要接收并且 ACK 的包,即便后来的包都到了,放在缓存里面,窗口也不能右移,因为 TCP 的 ACK 机制是基于序列号的累计应答,一旦 ACK 一个序列号,就说明前面的都到了,所以只要前面的没到,后面的即使到了也不能 ACK,就会导致后面的到了,也有可能超时重传,浪费带宽。 QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空档会等待到来或者重发即可,而窗口的起始位置为当前收到的最大 offset,从这个 offset 到当前的 stream 所能容纳的最大缓存,是真正的窗口大小,显然,这样更加准确。 另外,还有整个连接的窗口,需要对于所有的 stream 的窗口做一个统计。小结HTTP 协议虽然很常用,也很复杂,我们只需要重点记住 GET、POST、PUT、DELETE 这几个方法,以及重要的首部字段;HTTP 2.0 通过头压缩、分帧、二进制编码、多路复用等技术提升性能;QUIC 协议通过基于 UDP 自定义的类似 TCP 的连接、重试、多路复用、流量控制技术,进一步提升性能。参考:The TCP/IP Guide;百度百科 - HTTP 词条;刘超 - 趣谈网络协议系列课; ...

December 13, 2018 · 3 min · jiezi

盘点:2018年双11背后的蚂蚁核心技术

摘要: 一起来探索蚂蚁双11的神秘技术之旅吧!小蚂蚁说:你们都很关心的 “OB双11大促实战分享” 专题来啦!本系列将为你系统性的介绍OceanBase支撑蚂蚁双11背后的技术原理和实战分享。从平台到架构,再到实现,一起来探索蚂蚁双11这场神秘的技术之旅吧!2018年的双11十周年,最终成交额以2135亿元创纪录收官,支付宝系统在这场“商业奥运会”中再次经受住了考验。这也是OceanBase顺利支撑蚂蚁双11的第五年。从五年前,只有10%流量切到OceanBase上,到如今OceanBase 2.0版本成功支撑2018年双11的支付宝核心链路。每年不变的是一如既往的表现平稳,丝般顺滑,变化的是技术能力的不断升级和迭代。今年的双11,OceanBase 2.0扛起了大梁,性能比去年提升了50%,真正实现了“零成本”支撑大促。一、2018双11大促使用了哪些核心技术?今年的双11,OceanBase致力于通过底层架构及平台能力的提升,来实现双11稳定性、成本优化、性能及效率方面的全方位的提升。相较以往始终如一“丝般顺滑”的大促能力外,2018年的双11,OceanBase更加注重长久技术能力的沉淀:OceanBase2.0版本首次上线支付宝的核心链路,包括交易、支付系统,为“峰值百万支付能力”的三年战略沉淀了通用的“极致弹性”的分布式数据库能力,夯实了百万支付的底层基座。在底层存储介质方面,OceanBase 2.0核心链路首次100%运行在容器上,同时存储计算分离架构上线,大幅降低资源成本的同时夯实统一存储基座。在智能化运维的实践方面,OCP(OceanBase云平台)着眼于SQL优化诊断、故障根因分析和智能容量规划等数据库关键场景,将数据库专家的经验与AI算法/机器学习相结合,提供智能化的数据库服务。在平台能力的沉淀上,OCP引入Orchestration理念,通过编排/复用原子变更任务灵活,实现大促快速弹出/弹回的流程,同时平台内置变更免疫及变更三板斧能力(可监控/可灰度/可回滚),极大的提升了大促整体的稳定性和效率;在整个大促期间,OCP自动执行40000+变更,最终实现全程零故障。在商业产品化方面:智能化运维及平台能力抽象出大促及对外商业化场景,建设通用能力来覆盖蚂蚁内外场景。二、OceanBase 2.0 & 百万支付每年双11的压力在不断创造新高,支付系统需要具备百万每秒的支付能力,那么一个亟待解决的问题是:如何解决最小数据分片的峰值能力超过单机性能的问题。OceanBase 2.0应运而生,其目标是在应用无感知的情况下对数据分片进一步拆分,将数据sharding到无限多的机器上,实现极致弹性能力优雅支撑百万支付峰值。1.百万支付架构如下图的百万支付架构所示,传统数据库的弹性架构,将数据进行物理拆分到不同机器,业务在数据访问、研发、后期维护及数据配套设施上都非常繁琐;同时拆分后资源很难快速回收,且数据拆分及聚合无法实现业务无损。相比于传统数据库的弹性架构,OceanBase 2.0架构完全不侵入业务,内部通过分区实现数据分片的自组织及负载均衡,通过生成列及分区规则实现自动路由,通过分区聚合(partition_group)消除分布式事务性能开销以提升性能,从而实现无损线性伸缩。另外,数据分片间share_nothing及多版本的架构,实现分片故障隔离及单点故障消除的高可用架构。2.性能提升为实现“零成本大促”,OceanBase 2.0花了非常多的精力致力于性能的提升。相比OceanBase1.0,2.0在分布式架构上全面升级,如原生sharding/分布式事务优化/优化事务提交日志开销。OceanBase作为底层基础软件,任何微小的性能提升都会为业务节省大量资源,秉承持续优化的匠心,OceanBase 2.0在数据库底层架构、系统实现层面及数据库运行环境全方位进行优化。最终,OceanBase 2.0相比1.0提升了50%的性能,实现今年双11大促的零机器增加。三、OceanBase 容器化 & 存储计算分离双11峰值需要大量的资源支撑,而峰值后资源处于低水位状态,如何快速申请/释放这部分资源?双11当天非支付链路资源空闲,大促是否可以抢占这批资源?大促不同活动时间错峰,不同链路的资源可否实现快速腾挪?类似的资源问题不一而足。大家可以发现以上问题的本质在于:如何最大化程度降低双11当天的资源成本?这是大促技术要实现的一个核心价值。双11大促资源成本与两个因素相关,一个是大促资源的总数目,另一个是持有时长。我们可以通过系统优化提升单机性能,来降低大促资源的总数目(如前章节提到的OceanBase 2.0的性能优化)。那么如何降低持有时长呢?我们统一的思路是:用“高峰期抢占/低峰值释放资源”的方式来大幅降低持有时长;其两个关键前提技术就是容器化和存储计算分离。1.OceanBase容器化OceanBase容器化的核心思想是“资源调度”,大促目标就是“OceanBase能够被快速调度到各种资源载体上(如离线资源、云资源、峰值无压力的数据库其他集群)”;容器化屏蔽了底层资源载体的差异化,具备弹性部署高效的优点,是资源调度的前提条件。OceanBase打造自身调度能力,深入结合副本、租户的概念,精细化资源画像,使得OB容器化部署快速实现分时复用、资源抢占及混部。2.存储计算分离存储计算分离,顾名思义,将数据库运行依赖的计算资源和存储资源部署到不同的资源载体上,从而实现数据库的弱状态化,使得数据库可分别对存储和计算资源进行弹性伸缩。其好处是显而易见的。典型场景:大促态——CPU资源需求激增,而存储资源增幅很小,那么我们可以针对性对计算资源的机型进行扩容,从而降低资源成本且提升扩容效率;日常态——OB LSM架构将离散IO转化成顺序IO,因此存储的IO能力不是瓶颈,更多的是存储空间上的需求;存储计算分离后,多集群间可降低存储碎片,共享整体存储资源池,提升资源利用率。四、平台智能化随着业务规模的快速增长,系统稳定性SLA预发严峻和OceanBase部署的多样化,传统平台已无法满足我们的需求,可以预见不久的将来,运维将成为业务扩展的瓶颈。因此,OceanBase平台正在逐步走向智能化道路实现智能运维。OCP着眼于SQL优化诊断、故障根因分析和智能容量等大促关键场景,目标是将运维专家的技术经验和AI算法/机器学习技术相结合,分解运维关键技术,开发成一系列的智能运维模型,应用于大规模运维系统中。众所周知,SQL plan的正确性对数据库运行至关重要。OCP针对风险场景SQL,在千万峰值压力下,实时进行plan正确性比对,并对可能存在性能变坏隐患的SQL进行分钟级修正。容量水位是大促至关重要的一环,OCP通过数据建模/智能水位预测对集群/租户/docker进行容量画像,结合OceanBase内置Tenant Group能力,实现容器/集群/租户等多个维度的自动扩缩容,同时计算容量plan在集群/租户维度混部,实现最佳负载均衡部署【 深度部署资源利用率达到(n-1)/n 】,大幅节省了机器资源。OCP作为OceanBase的“智能大脑”,实时监控数据库运行状态,小至单条SQL plan,大至数千台机器容量,真正做到了生产环境智能化全覆盖。未来,OCP还将不断创新数据库智能化的运维之路,打造更加完善的数据库自治体系。五、生态与连接蚂蚁金服与金融机构最早建立的连接是基于支付业务的合作,后来又逐渐扩展了很多其他普惠金融类的业务,比如网商银行的同业合作,借呗/花呗等。如今随着在蚂蚁金服内部多年积累的技术能力与产品能力,OceanBase也将全面走向外部,对所有行业开放,通过科技作为新的连接纽带助力企业的数字化转型。过去金融业IT系统的基础架构建设基本都来自国外,如IBM、甲骨文、EMC这些公司构建底层架构,其中门槛最高的就是数据库的整体平滑替换。OceanBase团队从成立之初就肩负着使命,即我们要做一款通用数据库真正的去推动整个社会的进步,能够让整个社会的生产力发生变化。从2016年底,OceanBase就开始准备走出去,用技术改变业务形态;用技术创造新的业务模式,与更多企业建立更为紧密的连接关系。近两年对外服务的过程中,通过与ISV的深度合作与赋能,不仅提供OceanBase内核的能力,也不断丰富周边配套产品生态,涵盖使用数据库过程中的方方面面。未来,我们将继续致力于提供高可用、高性能、低成本的数据库服务,相信通过科技的连接助力更多企业,让科技的产出变成可以量化的业务价值。本文作者:平生栗子阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 4, 2018 · 1 min · jiezi

基于node开发的web应用,负载均衡的简单实践

集群(cluster)是一组相互独立的、通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理。一个客户与集群相互作用时,集群像是一个独立的服务器。负载均衡(Load Balance),其意思就是分摊到多个操作单元上进行执行阿里云负载均衡架构文档负载均衡好处节省成本,一个服务器性能再好也是有瓶颈的,而且性能越高的服务器成本也越大。极大的提高了并发量和响应速度。实践例子学无止境网该web应用,由两个服务器一起提供的服务实现负载均衡遇到的问题nginx负载均衡策略多台服务器代码同步多台服务器数据库同步node服务,代码更新后,服务重启源的代码更新问题和数据升级用户上传的图片等静态资源同步Nginx反向代理及负载均衡轮询权重ip_hashurl_hash等等…这里使用最简单的轮询机制,session存放在数据库,解决了session服务器之间不同步的问题。upstream tianshengjie{ server ip地址; server ip地址 max_fails=2 fail_timeout=10s;}server { listen 80 default_server; server_name 47.99.90.167 www.tianshengjie.cn tianshengjie.cn; location / { proxy_pass http://tianshengjie; proxy_cache_key $http_range$uri$is_args$args; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}服务部署forever start -c nodemon app.js –exitcrashforeverA simple CLI tool for ensuring that a given script runs continuously守护node进程程序nodemon自动监听文件变化,重启node服务exitcrash,当node服务奔溃后,重启代码同步使用shell脚本,自动更新代码,一键同步更新#!/bin/bashcd git仓库git pull;yarn install –production;rsync -av –exclude-from=/opt/ssh/blog_exclude.list git仓库 代码发布地址rsync -avz -e ssh /var/www/blog/ root@负载均衡服务器ip:负载均衡服务器发布代码目录cd 代码发布地址 ;forever stop app.js;npm run start;echo “发布成功"将git仓库和正式应用的代码地址分离更新git仓库地址下载程序依赖将git仓库更新后的代码复制到正式发布目录将代码同步更新到负载均衡服务器重启服务数据库同步阿里 云数据库文档地址性能最高,有备份有容灾,功能强大,但是收费mysqlmysql远程连接配置配置相对简单,数据库会有性能瓶颈,免费分布式数据库研究中静态资源同步当用户通过负载均衡,被定位到了不同的服务器。这时候,上传文件时,将会把文件上传到不同的服务器中。当用户被分配到了其他服务器时,就会找不到这个文件了。所以我们需要同步负载均衡的服务器的文件。方案一:自己实现统一文件上传管理系统,所有用户文件统一上传到一个地方。方案二:使用阿里云的NAS文件系统管理方案三:使用NFS系统阿里云 NAS文件系统管理阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例、HPC 和 Docker 等计算节点的文件存储服务,提供标准的文件访问协议,您无需对现有应用做任何修改,即可使用具备无限容量及性能扩展、单一命名空间、多共享、高可靠和高可用等特性的分布式文件系统。配置挂载缺点缺点:收费优点配置相对简单弹性伸缩,按量收费阿里出品NFS (Network FileSystem)配置文档缺点配置相对复杂server宕机了所有客户端都不能访问在高并发下NFS效率/性能有限数据是通过明文传送,安全性一般对数据完整性不做验证多台机器挂载NFS服务器时,连接管理维护麻烦优点免费,免费的就是好节省存储空间实现了多台服务器共享文件原文地址:https://tianshengjie.cn/artic… ...

November 5, 2018 · 1 min · jiezi

【整理总结】负载均衡浅析

运营研发团队 施洪宝一. 基础知识1.1 基础什么是负载均衡?当单机提供的并发量不能满足需求时,我们需要多台服务器同时服务。当客户请求到达时,如何为客户选择最合适的服务器?这个问题就是负载均衡问题。负载均衡主要需要解决的问题是哪些?从客户端的角度上看,客户需要最快速的得到服务器的相应,负载均衡时需要找出能最快相应客户需求的服务器进行服务。从服务端来看如何使得每台服务器都能达到较高的利用率,最大限制的为用户提供快速、可靠的服务是服务端需要考虑的主要问题。1.2 负载均衡分类硬件F5软件dns负载均衡LVS负载均衡(4层)nginx, haproxy(7层)二. F5负载均衡F5是一家美国的公司,该公司生产一些硬件设备可以作为负载均衡器使用(例如:big-ip), 本文后续部分所说的F5是指其负载均衡器产品。不同的产品实现的功能不一致,具体情况需要根据产品说明书。F5可以在4-7层内做负载均衡,用户可以根据需求进行配置。由于F5可以做7层负载均衡,故而可以实现会话管理,http处理等。2.1 数据转发模式standard类型, 这种模式下,客户端与F5服务器建立连接,F5服务器与真实服务器建立连接,F5服务器将客户需求转发给真实服务器,并将真实服务器的相应转发给客户端,此时F5可以查看请求和相应的所有信息。四层转发模式(performance L4), 这种模式下,F5只处理4层以下的数据。客户端将数据发送给F5, F5仅将数据转发给真实服务器,包括TCP的握手数据包以及挥手数据包,真实服务器需要先将数据发送给F5服务器,F5将其转发给客户端。路由模式, 这种模式与LVS的DR模式类似。…2.2 负载均衡算法轮询,加权轮询。源地址哈希…2.3 小结F5的优势在于功能强大,并发量高,能满足客户的大多数需求,但其成本较高,一般大型国企可能会使用。2.4 参考https://f5.com/zhhttps://www.jianshu.com/p/2b5…https://wenku.baidu.com/view/…三. dns负载均衡dns负载均衡由dns服务提供厂商提供。最初的dns负载均衡提供简单轮询,不能根据客户端或者服务端状态进行选择。目前,有些dns服务厂商可以提供智能dns服务,用户可以设置负载均衡方案,例如:根据客户端ip地址,选择就近的服务器。对于目前大多数的公司而言,为了更好的服务用户,通常会使用dns负载均衡,将用户按照就近原则,分配到某个集群服务器上。之后,集群内再采用其他的负载均衡方案。四. Linux Virtual Server(LVS)LVS通过修改数据包Ip地址,Mac地址实现负载均衡。LVS由ipvs(内核中), ipvsadm(用户态)组成。LVS需要理解tcp,ip头部。当tcp握手信号,SYN数据包达到时,ipvs选择一个后端服务器,将数据包进行转发。在此之后,所有包含相同的ip,tcp头部的数据包都会被转发到之前选择的服务器上。很明显,ipvs无法感知数据包内容。4.1 分类LVS-NATLVS-DRLVS-TUN4.2 基本原理4.2.1 LVS-DRLVS-DR模式的基本原理如下图所示:4.2.2 LVS-NATLVS-NAT模式的基本原理如下图所示:4.3 负载均衡算法4.3.1 静态算法轮询(Round Robin, RR)加权轮询(Weight Round Robin, WRR)源地址Hash(Source Hash, SH)目的地址Hash(Destination Hash, DH), 可以设置多个VIP4.3.2 动态算法最少连接(Least Connections, LC),找出当前连接数最小的服务器加权最少连接(Weighted Least Connections, WLC)最短期望延迟(Shortest Expected Delay Scheduling, SED) 基于WLC。例如: 现有A, B, C三台服务器,权重分别为100,200,300,当前的连接数分别为1,2,3,下一个连接到达时,通过计算期望时延选择服务器(1+1)/100, (2+1)/200, (3+1)/300, 故而选择C服务器。永不排队(Never Queue Scheduling, NQ), 改进的sed, 如果某台服务器连接数为0,直接连接过去,不在进行sed计算。基于局部性的最少连接(locality-Based Least Connections, LBLC),根据目标ip, 找出目标ip最近使用的服务器,如果服务器存在并且负载没有大于一个阈值,则将新的连接分配到这个服务器上,否则按照最少连接找出一个服务器处理该请求。带复制的基于局部性最少连接(Locality-Based Least Connections with Replication, LBLCR),根据目标ip,维护一个服务器组,每次从组中挑选服务器,如果服务器不可以处理,则从所有服务器中按照最少连接挑选出一台服务器,并将其加入到目标ip的处理组服务器中。4.3 参考https://liangshuang.name/2017…五. Nginx Load Balancenginx负载均衡工作在7层,它会与client、upstream分别建立tcp连接,nginx需要维护这两个连接的状态。nginx的stream模块可以用于4层负载均衡,但一般很少使用。5.1 基本原理nginx做7层负载均衡的基本原理如下图所示:5.2 负载均衡算法轮询(默认)加权轮询源ip哈希响应时间url 哈希 ...

November 2, 2018 · 1 min · jiezi

eureka分布式框架demo(springboot、springcloud、Feign、zuul、Mybatis)

eureka分布式框架,内含以下模块:eureka server、zuul 网关、commons 工具包、pojos 实体类、base服务提供者、order 服务提供者、web 服务消费者eureka_demo项目介绍eureka分布式框架,内含以下模块:eureka serverzuul 网关commons 工具包pojos 实体类base 服务提供者(集成Mybatis)order 服务提供者(集成Mybatis)web 服务消费者软件架构eureka分布式框架、集成sringboot、springcloud、Mybatis使用说明创建库表,在项目doc目录下有对应的sql文件然后修改server_base和server_order中的数据库链接order服务中创建了两个启动文件,端口不一样,记得配置两个启动即可测试负载 (Intellij Idea)。启动顺序:eureka -> zuul -> base -> order -> web启动完成打开页面访问:http://localhost:7070/ 查看eureka管理界面,查看服务是否已经注册进来如下图所示:具体的请求方式,在web模块controller中都有注释!模块示例图因资料太多,后台关注我主页领取免费的学习资源(有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等)

October 26, 2018 · 1 min · jiezi

JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式

JVM 在遇到OOM(OutOfMemoryError)时生成Dump文件的三种方式,以及如何使用Eclips MemoryAnalyzer(MAT)插件进行堆内存分析。方法一:jmap -dump:format=b,file=文件名 [pid]例如:jmap -dump:format=b,file=/usr/local/base/02.hprof 12942方法二:让JVM在遇到OOM(OutOfMemoryError)时生成Dump文件,需要配置一些信息-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base比如:我用eclipse配置一下。如下图所示:方法三:使用 jcmd 命令生成 dump 文件jcmd <pid> GC.heap_dump d:dumpheap.hprof此方法没有经过博主的测试。分析:dump文件可以通过MemoryAnalyzer(MAT)分析查看,可以查看dump时对象数量,内存占用,线程情况等。我们现在来安装一下eclipse MAT插件打开Help -> new install software名字可以随便起,插件地址:http://archive.eclipse.org/ma…剩下的就不做多的介绍了,安装完成后,我们来使用方法二来营造一个内存溢出的例子:比如我写的Java内存溢出程序是:import java.util.ArrayList;import java.util.List;public class OOM {public static void main(String[] args) { List<Object> list = new ArrayList<>(); // 创建n个1M大小的数组,耗尽内存 for (int i = 0; i < 10000; i++) { list.add(new byte[1024 * 1024]); } } }控制台打印:java.lang.OutOfMemoryError: Java heap spaceDumping heap to /Users/sun/Documents/java_pid31782.hprof …Heap dump file created [1943907998 bytes in 5.035 secs]下边我们切换eclipse视图到 Memory Analysis,点击File -> Open Heap Dump,选择生成的hprof文件,如下图所示打开完成后,如下所示:圈起来的这三个是常用的分析方式现在我们点击 Leak Suspects 来看一下点击 Histogram 来看一下byte 数组创建的很大,溢出的问题一目了然 ...

October 25, 2018 · 1 min · jiezi

天天写业务代码,如何成为架构技术大牛?

不管是开发、测试、运维,每个技术人员心理多多少少都有一个成为技术大牛的梦,毕竟“梦想总是要有的,万一实现了呢”!正是对技术梦的追求,促使我们不断地努力和提升自己。然而……然而“梦想是美好的,现实却是残酷的”,很多同学在实际工作后就会发现,梦想是成为大牛,但做的事情看起来跟大牛都不沾边,例如,程序员说“天天写业务代码还加班,如何才能成为技术大牛”,测试说“每天都有执行不完的测试用例”,运维说“扛机器接网线敲shell命令,这不是我想要的运维人生”……一:常见模式与工具学习Java技术体系,设计模式,流行的框架与组件——常见的设计模式,编码必备,Spring5,做应用必不可少的最新框架,MyBatis,玩数据库必不可少的组件……二:分布式架构高并发,高可用,海量数据,没有分布式的架构知识肯定是玩不转的:分布式架构原理分布式架构策略分布式中间件分布式架构实战三:微服务架构业务越来越复杂,服务分层,微服务架构是架构升级的必由之路,Java技术体系,和微服务相关的技术有哪些呢?微服务框架Spring CloudDocker与虚拟化微服务架构四:性能优化任何脱离细节的ppt架构师都是耍流氓,向上能运筹帷幄,向下能解决一线性能问题,Java技术体系,需要了解:性能指标体系JVM调优Tomcat调优MySQL调优五:底层知识从架构设计,到应用层调优,再深入了解底层原理,扎实的Java基本功才能让自己变为扫地神僧:内存模型并发模式线程模型锁细节以上为今天的分享内容,谢谢大家!因资料太多,关注我主页 里面会分享资深架构师录制的视频录像(有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构)

October 22, 2018 · 1 min · jiezi

Java高并发架构设计

序言高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等。为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适合自己业务场景的高并发处理方案。在电商相关产品开发的这些年,我有幸的遇到了并发下的各种坑,这一路摸爬滚打过来有着不少的血泪史,这里进行的总结,作为自己的归档记录,同时分享给大家。一丶服务器架构业务从发展的初期到逐渐成熟,服务器架构也是从相对单一到集群,再到分布式服务。一个可以支持高并发的服务少不了好的服务器架构,需要有均衡负载,数据库需要主从集群,nosql缓存需要主从集群,静态文件需要上传cdn,这些都是能让业务程序流畅运行的强大后盾。服务器这块多是需要运维人员来配合搭建,具体我就不多说了,点到为止。大致需要用到的服务器架构如下:服务器均衡负载(如:nginx,阿里云SLB)资源监控分布式数据库主从分离,集群DBA 表优化,索引优化,等分布式nosql主从分离,集群主从分离,集群主从分离,集群redismongodbmemcachecdnhtmlcssjsimage并发测试高并发相关的业务,需要进行并发的测试,通过大量的数据分析评估出整个架构可以支撑的并发量。测试高并发可以使用第三方服务器或者自己测试服务器,利用测试工具进行并发请求测试,分析测试数据得到可以支撑并发数量的评估,这个可以作为一个预警参考,俗话说知己自彼百战不殆。第三方服务:阿里云性能测试并发测试工具:Apache JMeterVisual Studio性能负载测试Microsoft Web Application Stress Tool实战方案通用方案日用户流量大,但是比较分散,偶尔会有用户高聚的情况;场景: 用户签到,用户中心,用户订单,等服务器架构图:说明:场景中的这些业务基本是用户进入APP后会操作到的,除了活动日(618,双11,等),这些业务的用户量都不会高聚集,同时这些业务相关的表都是大数据表,业务多是查询操作,所以我们需要减少用户直接命中DB的查询;优先查询缓存,如果缓存不存在,再进行DB查询,将查询结果缓存起来。更新用户相关缓存需要分布式存储,比如使用用户ID进行hash分组,把用户分布到不同的缓存中,这样一个缓存集合的总量不会很大,不会影响查询效率。方案如:用户签到获取积分计算出用户分布的key,redis hash中查找用户今日签到信息如果查询到签到信息,返回签到信息如果没有查询到,DB查询今日是否签到过,如果有签到过,就把签到信息同步redis缓存。如果DB中也没有查询到今日的签到记录,就进行签到逻辑,操作DB添加今日签到记录,添加签到积分(这整个DB操作是一个事务)缓存签到信息到redis,返回签到信息注意这里会有并发情况下的逻辑问题,如:一天签到多次,发放多次积分给用户。我的博文[大话程序猿眼里的高并发]有相关的处理方案。用户订单这里我们只缓存用户第一页的订单信息,一页40条数据,用户一般也只会看第一页的订单数据用户访问订单列表,如果是第一页读缓存,如果不是读DB计算出用户分布的key,redis hash中查找用户订单信息如果查询到用户订单信息,返回订单信息如果不存在就进行DB查询第一页的订单数据,然后缓存redis,返回订单信息用户中心计算出用户分布的key,redis hash中查找用户订单信息如果查询到用户信息,返回用户信息如果不存在进行用户DB查询,然后缓存redis,返回用户信息其他业务以上例子是一个相对简单的高并发架构,并发量不是很高的情况可以很好的支撑,但是随着业务的壮大,用户并发量增加,我们的架构也会进行不断的优化和演变,比如对业务进行服务化,每个服务有自己的并发架构,自己的均衡服务器,分布式数据库,nosql主从集群,如:用户服务、订单服务;消息队列秒杀、秒抢等活动业务,用户在瞬间涌入产生高并发请求场景:定时领取红包,等服务器架构图:说明:场景中的定时领取是一个高并发的业务,像秒杀活动用户会在到点的时间涌入,DB瞬间就接受到一记暴击,hold不住就会宕机,然后影响整个业务;像这种不是只有查询的操作并且会有高并发的插入或者更新数据的业务,前面提到的通用方案就无法支撑,并发的时候都是直接命中DB;设计这块业务的时候就会使用消息队列的,可以将参与用户的信息添加到消息队列中,然后再写个多线程程序去消耗队列,给队列中的用户发放红包;方案如:定时领取红包一般习惯使用 redis的 list当用户参与活动,将用户参与信息push到队列中然后写个多线程程序去pop数据,进行发放红包的业务这样可以支持高并发下的用户可以正常的参与活动,并且避免数据库服务器宕 机的危险一级缓存高并发请求连接缓存服务器超出服务器能够接收的请求连接量,部分用户出现建立连接超时无法读取到数据的问题;因此需要有个方案当高并发时候时候可以减少命中缓存服务器;这时候就出现了一级缓存的方案,一级缓存就是使用站点服务器缓存去存储数据,注意只存储部分请求量大的数据,并且缓存的数据量要控制,不能过分的使用站点服务器的内存而影响了站点应用程序的正常运行,一级缓存需要设置秒单位的过期时间,具体时间根据业务场景设定,目的是当有高并发请求的时候可以让数据的获取命中到一级缓存,而不用连接缓存nosql数据服务器,减少nosql数据服务器的压力比如APP首屏商品数据接口,这些数据是公共的不会针对用户自定义,而且这些数据不会频繁的更新,像这种接口的请求量比较大就可以加入一级缓存;服务器架构图:合理的规范和使用nosql缓存数据库,根据业务拆分缓存数据库的集群,这样基本可以很好支持业务,一级缓存毕竟是使用站点服务器缓存所以还是要善用。静态化数据高并发请求数据不变化的情况下如果可以不请求自己的服务器获取数据那就可以减少服务器的资源压力。对于更新频繁度不高,并且数据允许短时间内的延迟,可以通过数据静态化成JSON,XML,HTML等数据文件上传CDN,在拉取数据的时候优先到CDN拉取,如果没有获取到数据再从缓存,数据库中获取,当管理人员操作后台编辑数据再重新生成静态文件上传同步到CDN,这样在高并发的时候可以使数据的获取命中在CDN服务器上。CDN节点同步有一定的延迟性,所以找一个靠谱的CDN服务器商也很重要。因资料太多,请加架构资源QQ群:897889510 领取免费的学习资源!里面会分享资深架构师录制的视频录像(有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构)更多分享Java高级资料视频等你来!希望大家能成为一名更优秀的Java程序员,走向架构师的人生巅峰!

October 21, 2018 · 1 min · jiezi

深入理解高并发下分布式事务的解决方案

1、什么是分布式事务分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。2、分布式事务的产生的原因2.1、数据库分库分表当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表,具体分库分表的原理在此不做解释,以后有空详细说,简单的说就是原来的一个数据库变成了多个数据库。这时候,如果一个操作既访问01库,又访问02库,而且要保证数据的一致性,那么就要用到分布式事务。2.2、应用SOA化所谓的SOA化,就是业务的服务化。比如原来单机支撑了整个电商网站,现在对整个网站进行拆解,分离出了订单中心、用户中心、库存中心。对于订单中心,有专门的数据库存储订单信息,用户中心也有专门的数据库存储用户信息,库存中心也会有专门的数据库存储库存信息。这时候如果要同时对订单和库存进行操作,那么就会涉及到订单数据库和库存数据库,为了保证数据一致性,就需要用到分布式事务。以上两种情况表象不同,但是本质相同,都是因为要操作的数据库变多了!3、事务的ACID特性3.1、原子性(A)所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。3.2、一致性(C)事务的执行必须保证系统的一致性,就拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定是450元,B账户一定是350元。3.3、隔离性(I)所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。3.4、持久性(D)所谓的持久性,就是说一单事务完成了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。4、分布式事务的应用场景4.1、支付最经典的场景就是支付了,一笔支付,是对买家账户进行扣款,同时对卖家账户进行加钱,这些操作必须在一个事务里执行,要么全部成功,要么全部失败。而对于买家账户属于买家中心,对应的是买家数据库,而卖家账户属于卖家中心,对应的是卖家数据库,对不同数据库的操作必然需要引入分布式事务。4.2、在线下单买家在电商平台下单,往往会涉及到两个动作,一个是扣库存,第二个是更新订单状态,库存和订单一般属于不同的数据库,需要使用分布式事务保证数据一致性。5、常见的分布式事务解决方案5.1、基于XA协议的两阶段提交XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA实现分布式事务的原理如下:总的来说,XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,那就是性能不理想,特别是在交易下单链路,往往并发量很高,XA无法满足高并发场景。XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换回导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。5.2、消息事务+最终一致性所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性,具体原理如下:1、A系统向消息中间件发送一条预备消息2、消息中间件保存预备消息并返回成功3、A执行本地事务4、A发送提交消息给消息中间件通过以上4步完成了一个消息事务。对于以上的4个步骤,每个步骤都可能产生错误,下面一一分析:步骤一出错,则整个事务失败,不会执行A的本地操作步骤二出错,则整个事务失败,不会执行A的本地操作步骤三出错,这时候需要回滚预备消息,怎么回滚?答案是A系统实现一个消息中间件的回调接口,消息中间件会去不断执行回调接口,检查A事务执行是否执行成功,如果失败则回滚预备消息步骤四出错,这时候A的本地事务是成功的,那么消息中间件要回滚A吗?答案是不需要,其实通过回调接口,消息中间件能够检查到A执行成功了,这时候其实不需要A发提交消息了,消息中间件可以自己对消息进行提交,从而完成整个消息事务基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。原理如下:虽然上面的方案能够完成A和B的操作,但是A和B并不是严格一致的,而是最终一致的,我们在这里牺牲了一致性,换来了性能的大幅度提升。当然,这种玩法也是有风险的,如果B一直执行不成功,那么一致性会被破坏,具体要不要玩,还是得看业务能够承担多少风险。5.3、TCC编程模式所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。在此我向大家推荐一个架构学习交流群。交流学习群号:478030634 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多6、总结分布式事务,本质上是对多个数据库的事务进行统一控制,按照控制力度可以分为:不控制、部分控制和完全控制。不控制就是不引入分布式事务,部分控制就是各种变种的两阶段提交,包括上面提到的消息事务+最终一致性、TCC模式,而完全控制就是完全实现两阶段提交。部分控制的好处是并发量和性能很好,缺点是数据一致性减弱了,完全控制则是牺牲了性能,保障了一致性,具体用哪种方式,最终还是取决于业务场景。作为技术人员,一定不能忘了技术是为业务服务的,不要为了技术而技术,针对不同业务进行技术选型也是一种很重要的能力

October 10, 2018 · 1 min · jiezi

Ubuntu下配置NFS

什么是NFSNFS(Network FileSystem)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源。在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件,就像访问本地文件一样。NFS组成NFS有一个服务端和若干个客户端组成NFS作用可以将一些文件放到远程,节省本地存储空间在其他服务器上也能访问相同的文件,可解决负载均衡中,文件同步问题文件便于集中管理,备份服务端安装及配置// 安装服务端sudo apt install nfs-kernel-server// 修改配置sudo vi /etc/exports// 重启sudo service nfs-kernel-server restart配置说明// ip不限/var/data *(rw,sync,no_root_squash)//目录 ip(权限) ip(权限)/var/data 127.0.0(rw,sync,no_root_squash) 127.0.0(rw,sync,no_root_squash)rw 可读写的权限ro 只读的权限no_root_squash 登入NFS主机,使用该共享目录时相当于该目录的拥有者,如果是root的话,那么对于这个共享的目录来说,他就具有root的权限,这个参数『极不安全』,不建议使用all_squash 不论登入NFS的使用者身份为何,他的身份都会被压缩成为匿名使用者,通常也就是nobodyanonuid 可以自行设定这个UID的值,这个UID必需要存在于你的/etc/passwd当中anongid 同anonuid,但是变成groupID就是了sync 资料同步写入到内存与硬盘当中 async 资料会先暂存于内存当中,而非直接写入硬盘 insecure 允许从这台机器过来的非授权访问配置生效,无需重启sudo exportfs -rvexportfs命令常用选项-a 全部挂载或者全部卸载-r 重新挂载-u 卸载某一个目录-v 显示共享目录showmount// 显示NFS服务器上所有的共享目录showmount -e// 仅显示已被NFS客户端加载的目录showmount -a原文地址 https://www.unix.com/man-page…固定mountd端口系统 RPC服务在 nfs服务启动时默认会为 mountd动态选取一个随机端口(32768–65535)来进行通讯,我们可以通过编辑/etc/services 文件为 mountd指定一个固定端口# vi /etc/services在末尾添加 mountd 端口号/udpmountd 端口号/tcpNFS的守护进程rpc.nfsd:它是基本的NFS守护进程,主要功能是管理客户端是否能够登录服务器rpc.mountd:它是RPC安装守护进程,主要功能是管理NFS的文件系统。当客户端顺利通过rpc.nfsd登录NFS服务后,在使用NFS服务所提供的文凭前,还必须通过文件使用权限的验证。它会读取NFS的配置文件/etc/exports来对比客户端权限。portmap:portmap的主要功能是进行端口映射工作。当客户端尝试连接并使用RPC服务器提供的服务(如NFS服务)时,portmap会将所管理的与服务对应的端口提供给客户端,从而使客户可以通过该端口向服务器请求服务。查看 rpc进程rpcinfo -p 防火墙配置阿里云有虚拟防火墙,需要配置安全组,如图客户端配置安装apt-get install nfs-common挂载showmount -e 服务端ip// 查看服务端共享目录,若超时了,可能防火墙有问题,需要开放相应端口mount -t nfs 服务端ip:共享目录 挂载目录挂载命令详细说明查看所有挂载df -h

October 7, 2018 · 1 min · jiezi

优秀架构师必须掌握的架构思维

一、抽象思维如果要问软件研发/系统架构中最重要的能力是什么,我会毫不犹豫回答是抽象能力。抽象(abstraction)这个词大家经常听到,但是真正理解和能讲清楚什么是抽象的人少之又少。抽象其实是这样定义的:对某种事物进行简化表示或描述的过程,抽象让我们关注要素,隐藏额外细节。举一个例子,见下图:你看到什么?你看到的是一扇门,对不对?你看到的不是木头,也不是碳原子,这个门就是抽象,而木头或者碳原子是细节。另外你可以看到门上有个门把手,你看到的不是铁,也不是铁原子,门把手就是抽象,铁和铁原子是细节。在系统架构和设计中,抽象帮助我们从大处着眼(get our mind about big picture),隐藏细节(temporarily hide details)。抽象能力的强弱,直接决定我们所能解决问题的复杂性和规模大小。下图是我们小时候玩的积木,我发现小时候喜欢玩搭积木的,并且搭得快和好的小朋友,一般抽象能力都比较强。上图右边的积木城堡就是抽象,这个城堡如果你细看的话,它其实还是由若干个子模块组成,这些模块是子抽象单元,左边的各种形状的积木是细节。搭积木的时候,小朋友脑袋里头先有一个城堡的大图(抽象),然后他/她大脑里头会有一个初步的子模块分解(潜意识中完成),然用利用积木搭建每一个子模块,最终拼装出最后的城堡。这里头有一个自顶向下的分治设计,然后自底向上的组合过程,这个分治思维非常重要,我们后面会讲。我认为软件系统架构设计和小朋友搭积木无本质差异,只是解决的问题域和规模不同罢了。架构师先要在大脑中形成抽象概念,然后是子模块分解,然后是依次实现子模块,最后将子模块拼装组合起来,形成最后系统。所以我常说编程和架构设计就是搭积木,优秀的架构师受职业习惯影响,眼睛里看到的世界都是模块化拼装组合式的。抽象能力不仅对软件系统架构设计重要,对建筑、商业、管理等人类其它领域活动同样非常重要。其实可以这样认为,我们生存的世界都是在抽象的基础上构建起来的,离开抽象人类将寸步难行。这里顺便提一下抽象层次跳跃问题,这个在开发中是蛮普遍的。有经验的程序员写代码会保持抽象层次的一致性,代码读起来像讲故事,比较清晰易于理解;而没有经验的程序员会有明显的抽象层次跳跃问题,代码读起来就比较累,这个是抽象能力不足造成。举个例子:一个电商网站在处理订单时,一般会走这样一个流程:1.更新库存(InventoryUpdate)2.打折计算(Discounting)3.支付卡校验(PaycardVerification)4.支付(Pay)5.送货(Shipping)上述流程中的抽象是在同一个层次上的,比较清晰易于理解,但是没有经验的程序员在实现这个流程的时候,代码层次会跳,比方说主流程到支付卡校验一块,他的代码会突然跳出一行某银行API远程调用,这个就是抽象跳跃,银行API调用是细节,应该封装在PaycardVerification这个抽象里头。二、分层思维除了抽象,分层也是我们应对和管理复杂性的基本思维武器,如下图,为了构建一套复杂系统,我们把整个系统划分成若干个层次,每一层专注解决某个领域的问题,并向上提供服务。有些层次是纵向的,它贯穿所有其它层次,称为共享层。分层也可以认为是抽象的一种方式,将系统抽象分解成若干层次化的模块。分层架构的案例很多,一个中小型的Spring Web应用程序,我们一般会设计成三层架构:操作系统是经典的分层架构,如下图:TCP/IP协议栈也是经典的分层架构,如下图:如果你关注人类文明演化史,你会发现今天的人类世界也是以分层方式一层层搭建和演化出来的。今天的互联网系统可以认为是现代文明的一个层次,其上是基于互联网的现代商业,其下是现代电子工业基础设施,诸如此类。三、分治思维分而治之(divide and combine或者split and merge)也是应对和管理复杂性的一般性方法,下图展示一个分治的思维流程:对于一个无法一次解决的大问题,我们会先把大问题分解成若干个子问题,如果子问题还无法直接解决,则继续分解成子子问题,直到可以直接解决的程度,这个是分解(divide)的过程;然后将子子问题的解组合拼装成子问题的解,再将子问题的解组合拼装成原问题的解,这个是组合(combine)的过程。面试时为了考察候选人的分治思维,我经常会面一个分治题:给你一台8G内存/500G磁盘空间的普通电脑,如何对一个100G的大文件进行排序?假定文件中都是字符串记录,一行约100个字符。这是一个典型的分治问题,100G的大文件肯定无法一次加载到内存直接排序,所以需要先切分成若干小问题来解决。那么8G内存的计算机一次大概能排多大的数据量,可以在有限的时间内排完呢?也就是100G的大文件要怎么切法,切成多少份比较合适?这个是考察候选人的时间空间复杂度估算能力,需要一定的计算机组织和算法功底,也需要一定实战经验和sense。实际上8G内存的话,操作系统要用掉一部分,如果用Java开发排序程序,大致JVM可用24G内存,基于一般的经验值,一次排1G左右的数据应该没有问题(我实际在计算机上干过1G数据的排序,是OK的)。所以100G的文件需要先切分成100份,每份1G,这样每个子文件可以直接加载到内存进行排序。对于1G数据量的字符串排序,采用Java里头提供的快速排序算法是比较合适的。好,经过有限时间的排序(取决于计算机性能,快的一天内能排完),假定100个1G的文件都已经排好了,相当于现在硬盘上有100个已经排好序的文件,但是我们最终需要的是一个排好序的文件,下面该怎么做?这个时候我们需要把已经解决的子问题组合起来,合并成我们需要的最终结果文件。这个时候该采用什么算法呢?这里考察候选人对外排序和归并排序算法的掌握程度,我们可以将100个排好序的文件进行两两归并排序,这样不断重复,我们就会得到50个排好序的文件,每个大小是2G。然后再两两归并,不断重复,直到最后两个文件归并成目标文件,这个文件就是100G并且是排好序的。因为是外排序+归并排序,每次只需要读取当前索引指向的文件记录到内存,进行比较,小的那个输出到目标文件,内存占用极少。另外,上面的算法是两路归并,也可以采用多路归并,甚至是采用堆排序进行优化,但是总体分治思路没有变化。在此我向大家推荐一个架构学习交流群。交流学习群号:897889510 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多总体上这是一个非常好的面试题,除了考察候选人的分治思维之外,还考察对各种排序算法(快排,外排序,归并排序,堆排序)的理解,计算的时间空间复杂度估算,计算机的内外存特性和组织,文件操作等等。实际上能完全回答清楚这个问题的候选人极少,如果有幸被我面到一个,我会如获至宝,因为这个人有成长为优秀架构师的潜质。另外,递归也是一种特殊的分治技术,掌握递归技术的开发人员,相当于掌握了一种强大的编程武器,可以解决一些一般开发人员无法解决的问题。比方说最近我的团队在研发一款新的服务框架,其中包括契约解析器(parser),代码生产器(code generator),序列化器(serializer)等组件,里头大量需要用到递归的思维和技术,没有这个思维的开发人员就干不了这个事情。所以我在面试候选人的时候,一般都会出递归相关的编程题,考察候选人的递归思维。大自然中递归结构比比皆是,如下图,大家有兴趣不妨思考,大自然通过递归给我们人类何种启示?四、演化思维社区里头经常有人在讨论:架构是设计出来的?还是演化出验认为,架构既是设计出来的,同时也是演化出来的,对于互联网系统,基本上可以说是三分设计,七分演化,而且是在设计中演化,在演化中设计,一个不断迭代的过程。在互联网软件系统的整个生命周期过程中,前期的设计和开发大致只占三分,在后面的七分时间里,架构师需要根据用户的反馈对架构进行不断的调整。我认为架构师除了要利用自身的架构设计能力,同时也要学会借助用户反馈和进化的力量,推动架构的持续演进,这个就是演化式架构思维。当然一开始的架构设计非常重要,架构定系统基本就成型了,不容马虎。同时,优秀的架构师深知,能够不断应对环境变化的系统,才是有生命力的系统,架构的好坏,很大部分取决于它应对变化的灵活性。所以具有演化式思维的架构师,能够在一开始设计时就考虑到后续架构的演化特性,并且将灵活应对变化的能力作为架构设计的主要考量。当前,社区正在兴起一种新的架构方法学演化式架构,微服务架构就是一种典型的演化式架构,它能够快速响应市场用户需求的变化,而单块架构就缺乏这种灵活性。马丁·福乐曾经在其博客上给出过一张微服务架构的演化路线图[附录8.2],可以用来解释设计式思维和演化式思维的差异,如下图所示:上面的路线是一开始就直奔微服务架构,其实背后体现的是设计式架构的思维,认为架构师可以完全设计整个系统和它的演化方向。马丁认为这种做法风险非常高,一个是成本高昂,另外一个是刚开始架构师对业务域理解不深,无法清晰划分领域边界,开发出来的系统很可能无法满足用户需求。下面的路线是从单块架构开始,随着架构师对业务域理解的不断深入,也随着业务和团队规模的不断扩大,渐进式地把单块架构拆分成微服务架构的思路,这就是演化式架构的思维。如果你观察现实世界中一些互联网公司(例如eBay,阿里,Netflix等等)的系统架构,大部分走得都是演化式架构的路线。下图是建筑的演化史,在每个阶段,你可以看到设计的影子,但如果时间线拉得足够长,演化的特性就出来了。五、如何培养架构设计思维良好的架构设计思维的培养,离不开工作中大量高质量项目的实战锻炼,然后是平时的学习、思考和提炼总结。另外,基本的架构设计思维,其实在我们大学计算机课程(比如数据结构和算法)中可以找到影子,只不过当时以学习为主,问题域比较小和理想化。所以大学教育其实非常重要,基本的架构设计思维在那个时候就已经埋下种子,后面工程实践中进一步消化和应用,随着经验的积累,我们能够解决的问题域复杂性和规模逐渐变大,但基本的武器还是抽象、分层和分治等思维。我认为一个架构师的成长高度和他大学期间的思维习惯的养成关系密切。我所知道世界一流的互联网公司,例如谷歌等,招聘工程师新人时,对数据结构和算法的要求可以用苛刻来形容,这个可以理解,谷歌级别公司要解决的问题都是超级复杂的,基本思维功底薄弱根本无法应对。对于工作经验<5年的工程师新手,如果你大学时代是属于荒废型的,建议工作之余把相关课程再好好自学一把。个人推荐参考美国Berkeley大学的数据结构课程CS61B[附录8.1]进行学习,对建立抽象编程思维非常有帮助,我本人在研究生阶段自学过这门课程,现在回想起来确实受益匪浅,注意该课程中的所有Lab/Homework/Project都要实际动手做一遍,才有好的效果。我当年自学的是CS61B 2006秋季版的课程,上图是课程Logo对于演化设计思维,当前的大学教育其实培养很少,相反,当前大学教育大都采用脱离现实场景的简化理想模型,有些还是固定答案的应试教学,这种方式会造成学生思维确定化,不利于培养演化式设计思维。我个人的体会,演化式设计思维更多在实际工作中通过实战锻炼和培养。结论1.架构的本质是管理复杂性,抽象、分层、分治和演化思维是架构师征服复杂性的四种根本性武器。2.掌握了抽象、分层、分治和演化这四种基本的武器,你可以设计小到一个类,一个模块,一个子系统,或者一个中型的系统,也可以大到一个公司的基础平台架构,微服务架构,技术体系架构,甚至是组织架构,业务架构等等。3.架构设计不是静态的,而是动态演化的。只有能够不断应对环境变化的系统,才是有生命力的系统。所以即使你掌握了抽象、分层和分治这三种基本思维,仍然需要演化式思维,在设计的同时,借助反馈和进化的力量推动架构的持续演进。4.架构师在关注技术,开发应用的同时,需要定期梳理自己的架构设计思维,积累时间长了,你看待世界事物的方式会发生根本性变化,你会发现我们生活其中的世界,其实也是在抽象、分层、分治和演化的基础上构建起来的。另外架构设计思维的形成,会对你的系统架构设计能力产生重大影响。可以说对抽象、分层、分治和演化掌握的深度和灵活应用的水平,直接决定架构师所能解决问题域的复杂性和规模大小,是区分普通应用型架构师和平台型/系统型架构师的一个分水岭。

October 1, 2018 · 1 min · jiezi

基于 Spring Cloud 完整的微服务架构实战

技术栈Spring boot - 微服务的入门级微框架,用来简化 Spring 应用的初始搭建以及开发过程。Eureka - 云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。Spring Cloud Config - 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git 以及 Subversion。Hystrix - 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Zuul - Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Spring Cloud Bus - 事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与 Spring Cloud Config 联合实现热部署。Spring Cloud Sleuth - 日志收集工具包,封装了 Dapper 和 log-based 追踪以及 Zipkin 和 HTrace 操作,为 SpringCloud 应用实现了一种分布式追踪解决方案。Ribbon - 提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。Turbine - Turbine 是聚合服务器发送事件流数据的一个工具,用来监控集群下 hystrix 的 metrics 情况。Spring Cloud Stream - Spring 数据流操作开发包,封装了与 Redis、Rabbit、Kafka 等发送接收消息。Feign - Feign 是一种声明式、模板化的 HTTP 客户端。Spring Cloud OAuth2 - 基于 Spring Security 和 OAuth2 的安全工具包,为你的应用程序添加安全控制。应用架构该项目包含 8 个服务registry - 服务注册与发现config - 外部配置monitor - 监控zipkin - 分布式跟踪gateway - 代理所有微服务的接口网关auth-service - OAuth2 认证服务svca-service - 业务服务Asvcb-service - 业务服务B体系架构技术栈Spring boot - 微服务的入门级微框架,用来简化 Spring 应用的初始搭建以及开发过程。Eureka - 云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。Spring Cloud Config - 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git 以及 Subversion。Hystrix - 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Zuul - Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Spring Cloud Bus - 事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与 Spring Cloud Config 联合实现热部署。Spring Cloud Sleuth - 日志收集工具包,封装了 Dapper 和 log-based 追踪以及 Zipkin 和 HTrace 操作,为 SpringCloud 应用实现了一种分布式追踪解决方案。Ribbon - 提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。Turbine - Turbine 是聚合服务器发送事件流数据的一个工具,用来监控集群下 hystrix 的 metrics 情况。Spring Cloud Stream - Spring 数据流操作开发包,封装了与 Redis、Rabbit、Kafka 等发送接收消息。Feign - Feign 是一种声明式、模板化的 HTTP 客户端。Spring Cloud OAuth2 - 基于 Spring Security 和 OAuth2 的安全工具包,为你的应用程序添加安全控制。应用架构该项目包含 8 个服务registry - 服务注册与发现config - 外部配置monitor - 监控zipkin - 分布式跟踪gateway - 代理所有微服务的接口网关auth-service - OAuth2 认证服务svca-service - 业务服务Asvcb-service - 业务服务B体系架构技术栈Spring boot - 微服务的入门级微框架,用来简化 Spring 应用的初始搭建以及开发过程。Eureka - 云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。Spring Cloud Config - 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git 以及 Subversion。Hystrix - 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Zuul - Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Spring Cloud Bus - 事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与 Spring Cloud Config 联合实现热部署。Spring Cloud Sleuth - 日志收集工具包,封装了 Dapper 和 log-based 追踪以及 Zipkin 和 HTrace 操作,为 SpringCloud 应用实现了一种分布式追踪解决方案。Ribbon - 提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。Turbine - Turbine 是聚合服务器发送事件流数据的一个工具,用来监控集群下 hystrix 的 metrics 情况。Spring Cloud Stream - Spring 数据流操作开发包,封装了与 Redis、Rabbit、Kafka 等发送接收消息。Feign - Feign 是一种声明式、模板化的 HTTP 客户端。Spring Cloud OAuth2 - 基于 Spring Security 和 OAuth2 的安全工具包,为你的应用程序添加安全控制。应用架构该项目包含 8 个服务registry - 服务注册与发现config - 外部配置monitor - 监控zipkin - 分布式跟踪gateway - 代理所有微服务的接口网关auth-service - OAuth2 认证服务svca-service - 业务服务Asvcb-service - 业务服务B体系架构技术栈Spring boot - 微服务的入门级微框架,用来简化 Spring 应用的初始搭建以及开发过程。Eureka - 云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。Spring Cloud Config - 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git 以及 Subversion。Hystrix - 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Zuul - Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。Spring Cloud Bus - 事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与 Spring Cloud Config 联合实现热部署。Spring Cloud Sleuth - 日志收集工具包,封装了 Dapper 和 log-based 追踪以及 Zipkin 和 HTrace 操作,为 SpringCloud 应用实现了一种分布式追踪解决方案。Ribbon - 提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。Turbine - Turbine 是聚合服务器发送事件流数据的一个工具,用来监控集群下 hystrix 的 metrics 情况。Spring Cloud Stream - Spring 数据流操作开发包,封装了与 Redis、Rabbit、Kafka 等发送接收消息。Feign - Feign 是一种声明式、模板化的 HTTP 客户端。Spring Cloud OAuth2 - 基于 Spring Security 和 OAuth2 的安全工具包,为你的应用程序添加安全控制。应用架构该项目包含 8 个服务registry - 服务注册与发现config - 外部配置monitor - 监控zipkin - 分布式跟踪gateway - 代理所有微服务的接口网关auth-service - OAuth2 认证服务svca-service - 业务服务Asvcb-service - 业务服务B体系架构应用组件启动项目使用 Docker 快速启动配置 Docker 环境mvn clean package 打包项目及 Docker 镜像在项目根目录下执行 docker-compose up -d 启动所有项目本地手动启动配置 rabbitmq修改 hosts 将主机名指向到本地127.0.0.1 registry config monitor rabbitmq auth-service或者修改各服务配置文件中的相应主机名为本地 ip启动 registry、config、monitor、zipkin启动 gateway、auth-service、svca-service、svcb-service项目预览注册中心访问 http://localhost:8761/ 默认账号 user,密码 password监控访问 http://localhost:8040/ 默认账号 admin,密码 admin控制面板bp.jpg应用注册历史Turbine Hystrix面板应用信息、健康状况、垃圾回收等详情计数器查看和修改环境变量管理 Logback 日志级别查看并使用 JMX查看线程认证历史查看 Http 请求轨迹Hystrix 面板链路跟踪访问 http://localhost:9411/ 默认账号 admin,密码 admin控制面板链路跟踪明细服务依赖关系RabbitMQ 监控Docker 启动访问 http://localhost:15673/ 默认账号 guest,密码 guest(本地 rabbit 管理系统默认端口15672)接口测试在此我向大家推荐一个架构学习交流群。交流学习群号:897889510 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多接口测试1.获取 Tokencurl -X POST -vu client:secret http://localhost:8060/uaa/oauth/token -H “Accept: application/json” -d “password=password&username=anil&grant_type=password&scope=read%20write"返回如下格式数据:{“access_token”: “eac56504-c4f0-4706-b72e-3dc3acdf45e9”,“token_type”: “bearer”,“refresh_token”: “da1007dc-683c-4309-965d-370b15aa4aeb”,“expires_in”: 3599,“scope”: “read write”}2.使用 access token 访问 service a 接口curl -i -H “Authorization: Bearer eac56504-c4f0-4706-b72e-3dc3acdf45e9” http://localhost:8060/svca 返回如下数据:svca-service (172.18.0.8:8080)===>name:zhangxdsvcb-service (172.18.0.2:8070)===>Say Hello 3.使用 access token 访问 service b 接口curl -i -H “Authorization: Bearer eac56504-c4f0-4706-b72e-3dc3acdf45e9” http://localhost:8060/svcb 返回如下数据:svcb-service (172.18.0.2:8070)===>Say Hello 4.使用 refresh token 刷新 tokencurl -X POST -vu client:secret http://localhost:8060/uaa/oauth/token -H “Accept: application/json” -d “grant_type=refresh_token&refresh_token=da1007dc-683c-4309-965d-370b15aa4aeb” 返回更新后的 Token:{“access_token”: “63ff57ce-f140-482e-ba7e-b6f29df35c88”,“token_type”: “bearer”,“refresh_token”: “da1007dc-683c-4309-965d-370b15aa4aeb”,“expires_in”: 3599,“scope”: “read write”} 5.刷新配置curl -X POST -vu user:password http://localhost:8888/bus/refresh ...

September 30, 2018 · 3 min · jiezi

分布式的系统核心是什么——日志

什么是日志?日志就是按照时间顺序追加的、完全有序的记录序列,其实就是一种特殊的文件格式,文件是一个字节数组,而这里日志是一个记录数据,只是相对于文件来说,这里每条记录都是按照时间的相对顺序排列的,可以说日志是最简单的一种存储模型,读取一般都是从左到右,例如消息队列,一般是线性写入log文件,消费者顺序从offset开始读取。由于日志本身固有的特性,记录从左向右开始顺序插入,也就意味着左边的记录相较于右边的记录“更老”, 也就是说我们可以不用依赖于系统时钟,这个特性对于分布式系统来说相当重要。日志的应用日志在数据库中的应用日志是什么时候出现已经无从得知,可能是概念上来讲太简单。在数据库领域中日志更多的是用于在系统crash的时候同步数据以及索引等,例如MySQL中的redo log,redo log是一种基于磁盘的数据结构,用于在系统挂掉的时候保证数据的正确性、完整性,也叫预写日志,例如在一个事物的执行过程中,首先会写redo log,然后才会应用实际的更改,这样当系统crash后恢复时就能够根据redo log进行重放从而恢复数据(在初始化的过程中,这个时候不会还没有客户端的连接)。日志也可以用于数据库主从之间的同步,因为本质上,数据库所有的操作记录都已经写入到了日志中,我们只要将日志同步到slave,并在slave重放就能够实现主从同步,这里也可以实现很多其他需要的组件,我们可以通过订阅redo log 从而拿到数据库所有的变更,从而实现个性化的业务逻辑,例如审计、缓存同步等等。日志在分布式系统中的应用分布式系统服务本质上就是关于状态的变更,这里可以理解为状态机,两个独立的进程(不依赖于外部环境,例如系统时钟、外部接口等)给定一致的输入将会产生一致的输出并最终保持一致的状态,而日志由于其固有的顺序性并不依赖系统时钟,正好可以用来解决变更有序性的问题。我们利用这个特性实现解决分布式系统中遇到的很多问题。例如RocketMQ中的备节点,主broker接收客户端的请求,并记录日志,然后实时同步到salve中,slave在本地重放,当master挂掉的时候,slave可以继续处理请求,例如拒绝写请求并继续处理读请求。日志中不仅仅可以记录数据,也可以直接记录操作,例如SQL语句。日志是解决一致性问题的关键数据结构,日志就像是操作序列,每一条记录代表一条指令,例如应用广泛的Paxos、Raft协议,都是基于日志构建起来的一致性协议。日志在Message Queue中的应用日志可以很方便的用于处理数据之间的流入流出,每一个数据源都可以产生自己的日志,这里数据源可以来自各个方面,例如某个事件流(页面点击、缓存刷新提醒、数据库binlog变更),我们可以将日志集中存储到一个集群中,订阅者可以根据offset来读取日志的每条记录,根据每条记录中的数据、操作应用自己的变更。在此我向大家推荐一个架构学习交流群。交流学习群号:897889510 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多这里的日志可以理解为消息队列,消息队列可以起到异步解耦、限流的作用。为什么说解耦呢?因为对于消费者、生产者来说,两个角色的职责都很清晰,就负责生产消息、消费消息,而不用关心下游、上游是谁,不管是来数据库的变更日志、某个事件也好,对于某一方来说我根本不需要关心,我只需要关注自己感兴趣的日志以及日志中的每条记录。我们知道数据库的QPS是一定的,而上层应用一般可以横向扩容,这个时候如果到了双11这种请求突然的场景,数据库会吃不消,那么我们就可以引入消息队列,将每个队数据库的操作写到日志中,由另外一个应用专门负责消费这些日志记录并应用到数据库中,而且就算数据库挂了,当恢复的时候也可以从上次消息的位置继续处理(RocketMQ和Kafka都支持Exactly Once语义),这里即使生产者的速度异于消费者的速度也不会有影响,日志在这里起到了缓冲的作用,它可以将所有的记录存储到日志中,并定时同步到slave节点,这样消息的积压能力能够得到很好的提升,因为写日志都是有master节点处理,读请求这里分为两种,一种是tail-read,就是说消费速度能够跟得上写入速度的,这种读可以直接走缓存,而另一种也就是落后于写入请求的消费者,这种可以从slave节点读取,这样通过IO隔离以及操作系统自带的一些文件策略,例如pagecache、缓存预读等,性能可以得到很大的提升。在此我向大家推荐一个架构学习交流群。交流学习群号:575745314 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多。分布式系统中可横向扩展是一个相当重要的特性,加机器能解决的问题都不是问题。那么如何实现一个能够实现横向扩展的消息队列呢? 假如我们有一个单机的消息队列,随着topic数目的上升,IO、CPU、带宽等都会逐渐成为瓶颈,性能会慢慢下降,那么这里如何进行性能优化呢?1.topic/日志分片,本质上topic写入的消息就是日志的记录,那么随着写入的数量越多,单机会慢慢的成为瓶颈,这个时候我们可以将单个topic分为多个子topic,并将每个topic分配到不同的机器上,通过这种方式,对于那些消息量极大的topic就可以通过加机器解决,而对于一些消息量较少的可以分到到同一台机器或不进行分区2.group commit,例如Kafka的producer客户端,写入消息的时候,是先写入一个本地内存队列,然后将消息按照每个分区、节点汇总,进行批量提交,对于服务器端或者broker端,也可以利用这种方式,先写入pagecache,再定时刷盘,刷盘的方式可以根据业务决定,例如金融业务可能会采取同步刷盘的方式。3.规避无用的数据拷贝4.IO隔离结语日志在分布式系统中扮演了很重要的角色,是理解分布式系统各个组件的关键,随着理解的深入,我们发现很多分布式中间件都是基于日志进行构建的,例如Zookeeper、HDFS、Kafka、RocketMQ、Google Spanner等等,甚至于数据库,例如Redis、MySQL等等,其master-slave都是基于日志同步的方式,依赖共享的日志系统,我们可以实现很多系统: 节点间数据同步、并发更新数据顺序问题(一致性问题)、持久性(系统crash时能够通过其他节点继续提供服务)、分布式锁服务等等,相信慢慢的通过实践、以及大量的论文阅读之后,一定会有更深层次的理解。

September 30, 2018 · 1 min · jiezi

dubbo负载均衡策略及对应源码分析

在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。我们还可以扩展自己的负责均衡策略,前提是你已经从一个小白变成了大牛,嘻嘻1、Random LoadBalance1.1 随机,按权重设置随机概率。1.2 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。1.3 源码分析 package com.alibaba.dubbo.rpc.cluster.loadbalance; import java.util.List; import java.util.Random; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; /** * random load balance. * * @author qianlei * @author william.liangf / public class RandomLoadBalance extends AbstractLoadBalance { public static final String NAME = “random”; private final Random random = new Random(); protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { int length = invokers.size(); // 总个数 int totalWeight = 0; // 总权重 boolean sameWeight = true; // 权重是否都一样 for (int i = 0; i < length; i++) { int weight = getWeight(invokers.get(i), invocation); totalWeight += weight; // 累计总权重 if (sameWeight && i > 0 && weight != getWeight(invokers.get(i - 1), invocation)) { sameWeight = false; // 计算所有权重是否一样 } } if (totalWeight > 0 && ! sameWeight) { // 如果权重不相同且权重大于0则按总权重数随机 int offset = random.nextInt(totalWeight); // 并确定随机值落在哪个片断上 for (int i = 0; i < length; i++) { offset -= getWeight(invokers.get(i), invocation); if (offset < 0) { return invokers.get(i); } } } // 如果权重相同或权重为0则均等随机 return invokers.get(random.nextInt(length));}}说明:从源码可以看出随机负载均衡的策略分为两种情况a. 如果总权重大于0并且权重不相同,就生成一个1totalWeight(总权重数)的随机数,然后再把随机数和所有的权重值一一相减得到一个新的随机数,直到随机 数小于0,那么此时访问的服务器就是使得随机数小于0的权重所在的机器b. 如果权重相同或者总权重数为0,就生成一个1length(权重的总个数)的随机数,此时所访问的机器就是这个随机数对应的权重所在的机器2、RoundRobin LoadBalance2.1 轮循,按公约后的权重设置轮循比率。2.2 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。2.3 源码分析 package com.alibaba.dubbo.rpc.cluster.loadbalance; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.utils.AtomicPositiveInteger; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; /* * Round robin load balance. * * @author qian.lei * @author william.liangf / public class RoundRobinLoadBalance extends AbstractLoadBalance {public static final String NAME = “roundrobin”; private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();private final ConcurrentMap<String, AtomicPositiveInteger> weightSequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { String key = invokers.get(0).getUrl().getServiceKey() + “.” + invocation.getMethodName(); int length = invokers.size(); // 总个数 int maxWeight = 0; // 最大权重 int minWeight = Integer.MAX_VALUE; // 最小权重 for (int i = 0; i < length; i++) { int weight = getWeight(invokers.get(i), invocation); maxWeight = Math.max(maxWeight, weight); // 累计最大权重 minWeight = Math.min(minWeight, weight); // 累计最小权重 } if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样 AtomicPositiveInteger weightSequence = weightSequences.get(key); if (weightSequence == null) { weightSequences.putIfAbsent(key, new AtomicPositiveInteger()); weightSequence = weightSequences.get(key); } int currentWeight = weightSequence.getAndIncrement() % maxWeight; List<Invoker<T>> weightInvokers = new ArrayList<Invoker<T>>(); for (Invoker<T> invoker : invokers) { // 筛选权重大于当前权重基数的Invoker if (getWeight(invoker, invocation) > currentWeight) { weightInvokers.add(invoker); } } int weightLength = weightInvokers.size(); if (weightLength == 1) { return weightInvokers.get(0); } else if (weightLength > 1) { invokers = weightInvokers; length = invokers.size(); } } AtomicPositiveInteger sequence = sequences.get(key); if (sequence == null) { sequences.putIfAbsent(key, new AtomicPositiveInteger()); sequence = sequences.get(key); } // 取模轮循 return invokers.get(sequence.getAndIncrement() % length);}}说明:从源码可以看出轮循负载均衡的算法是:a. 如果权重不一样时,获取一个当前的权重基数,然后从权重集合中筛选权重大于当前权重基数的集合,如果筛选出的集合的长度为1,此时所访问的机 器就是集合里面的权重对应的机器b. 如果权重一样时就取模轮循3、LeastActive LoadBalance3.1 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差(调用前的时刻减去响应后的时刻的值)。3.2 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大3.3 对应的源码package com.alibaba.dubbo.rpc.cluster.loadbalance;import java.util.List;import java.util.Random;import com.alibaba.dubbo.common.Constants;import com.alibaba.dubbo.common.URL;import com.alibaba.dubbo.rpc.Invocation;import com.alibaba.dubbo.rpc.Invoker;import com.alibaba.dubbo.rpc.RpcStatus;/** LeastActiveLoadBalance* * @author william.liangf*/public class LeastActiveLoadBalance extends AbstractLoadBalance {public static final String NAME = “leastactive”;private final Random random = new Random();protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { int length = invokers.size(); // 总个数 int leastActive = -1; // 最小的活跃数 int leastCount = 0; // 相同最小活跃数的个数 int[] leastIndexs = new int[length]; // 相同最小活跃数的下标 int totalWeight = 0; // 总权重 int firstWeight = 0; // 第一个权重,用于于计算是否相同 boolean sameWeight = true; // 是否所有权重相同 for (int i = 0; i < length; i++) { Invoker<T> invoker = invokers.get(i); int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // 活跃数 int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); // 权重 if (leastActive == -1 || active < leastActive) { // 发现更小的活跃数,重新开始 leastActive = active; // 记录最小活跃数 leastCount = 1; // 重新统计相同最小活跃数的个数 leastIndexs[0] = i; // 重新记录最小活跃数下标 totalWeight = weight; // 重新累计总权重 firstWeight = weight; // 记录第一个权重 sameWeight = true; // 还原权重相同标识 } else if (active == leastActive) { // 累计相同最小的活跃数 leastIndexs[leastCount ++] = i; // 累计相同最小活跃数下标 totalWeight += weight; // 累计总权重 // 判断所有权重是否一样 if (sameWeight && i > 0 && weight != firstWeight) { sameWeight = false; } } } // assert(leastCount > 0) if (leastCount == 1) { // 如果只有一个最小则直接返回 return invokers.get(leastIndexs[0]); } if (! sameWeight && totalWeight > 0) { // 如果权重不相同且权重大于0则按总权重数随机 int offsetWeight = random.nextInt(totalWeight); // 并确定随机值落在哪个片断上 for (int i = 0; i < leastCount; i++) { int leastIndex = leastIndexs[i]; offsetWeight -= getWeight(invokers.get(leastIndex), invocation); if (offsetWeight <= 0) return invokers.get(leastIndex); } } // 如果权重相同或权重为0则均等随机 return invokers.get(leastIndexs[random.nextInt(leastCount)]);}}说明:源码里面的注释已经很清晰了,大致的意思就是活跃数越小的的机器分配到的请求越多4、ConsistentHash LoadBalance4.1 一致性 Hash,相同参数的请求总是发到同一提供者。4.2 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。4.3 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key=“hash.arguments” value=“0,1” />4.4 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes” value=“320” />4.5 源码分析暂时还没有弄懂,后面弄懂了再补充进来,有兴趣的小伙伴可以自己去看一下源码,然后一起交流一下心得5、dubbo官方的文档的负载均衡配置示例服务端服务级别 <dubbo:service interface="…" loadbalance=“roundrobin” /> 客户端服务级别 <dubbo:reference interface="…" loadbalance=“roundrobin” /> 服务端方法级别 <dubbo:service interface="…"> <dubbo:method name="…" loadbalance=“roundrobin”/> </dubbo:service> 客户端方法级别 <dubbo:reference interface="…"> <dubbo:method name="…" loadbalance=“roundrobin”/> </dubbo:reference> 大家觉得文章对你还是有一点点帮助的,大家可以点击下方二维码进行关注。 《乐趣区》 公众号聊的不仅仅是Java技术知识,还有面试等干货,后期还有大量架构干货。大家一起关注吧!关注烂猪皮,你会了解的更多………….. 原文连接:https://www.cnblogs.com/leeSm… ...

September 29, 2018 · 4 min · jiezi

分布式系统消息中间件——RibbitMQ的使用基础篇

前言我是在解决分布式事务的一致性问题时了解到RabbitMQ的,当时主要是要基于RabbitMQ来实现我们分布式系统之间对有事务可靠性要求的系统间通信的。关于分布式事务一致性问题及其常见的解决方案,可以看我另一篇博客。提到RabbitMQ,不难想到的几个关键字:消息中间件、消息队列。而消息队列不由让我想到,当时在大学学习操作系统这门课,消息队列不难想到生产者消费者模式。(PS:操作系统这门课程真的很好也很重要,其中的一些思想在我工作的很长一段一时间内给了我很大帮助和启发,给我提供了许多解决问题的思路。强烈建议每一个程序员都去学一学操作系统!)一 消息中间件1.1 简介消息中间件也可以称消息队列,是指用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息队列模型,可以在分布式环境下扩展进程的通信。当下主流的消息中间件有RabbitMQ、Kafka、ActiveMQ、RocketMQ等。其能在不同平台之间进行通信,常用来屏蔽各种平台协议之间的特性,实现应用程序之间的协同。其优点在于能够在客户端和服务器之间进行同步和异步的连接,并且在任何时刻都可以将消息进行传送和转发。是分布式系统中非常重要的组件,主要用来解决应用耦合、异步通信、流量削峰等问题。1.2 作用消息中间件几大主要作用如下:解耦冗余(存储)扩展性削峰可恢复性顺序保证缓冲异步通信1.3 消息中间件的两种模式1.3.1 P2P模式P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。P2P的特点:每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行它不会影响到消息被发送到队列接收者在成功接收消息之后需向队列应答成功如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式1.3.2 Pub/Sub模式Pub/Sub模式包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。Pub/Sub的特点每个消息可以有多个消费者发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。为了消费消息,订阅者必须保持运行的状态。如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。1.4 常用中间件介绍与对比Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache定级项目。Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。RocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。RabbitMQ比Kafka可靠,kafka更适合IO高吞吐的处理,一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用,比如ELK日志收集。二 RabbitMQ了解2.1 简介RabbitMQ是流行的开源消息队列系统。RabbitMQ是AMQP(高级消息队列协议)的标准实现。支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX,持久化。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。是使用Erlang编写的一个开源的消息队列,本身支持很多的协议:AMQP,XMPP, SMTP, STOMP,也正是如此,使的它变的非常重量级,更适合于企业级的开发。同时实现了一个Broker构架,这意味着消息在发送给客户端时先在中心队列排队。对路由(Routing),负载均衡(Load balance)或者数据持久化都有很好的支持。其主要特点如下:可靠性灵活的路由扩展性高可用性多种协议多语言客户端管理界面插件机制2.2 概念RabbitMQ从整体上来看是一个典型的生产者消费者模型,主要负责接收、存储和转发消息。其整体模型架构如下图所示:我们先来看一个RabbitMQ的运转流程,稍后会对这个流程中所涉及到的一些概念进行详细的解释。生产者:(1)生产者连接到RabbitMQ Broker,建立一个连接( Connection)开启一个信道(Channel)(2)生产者声明一个交换器,并设置相关属性,比如交换机类型、是否持久化等(3)生产者声明一个队列井设置相关属性,比如是否排他、是否持久化、是否自动删除等(4)生产者通过路由键将交换器和队列绑定起来(5)生产者发送消息至RabbitMQ Broker,其中包含路由键、交换器等信息。(6)相应的交换器根据接收到的路由键查找相匹配的队列。(7)如果找到,则将从生产者发送过来的消息存入相应的队列中。(8)如果没有找到,则根据生产者配置的属性选择丢弃还是回退给生产者(9)关闭信道。(10)关闭连接。消费者:(1)消费者连接到RabbitMQ Broker ,建立一个连接(Connection),开启一个信道(Channel) 。(2)消费者向RabbitMQ Broker 请求消费相应队列中的消息,可能会设置相应的回调函数,(3)等待RabbitMQ Broker 回应并投递相应队列中的消息,消费者接收消息。(4)消费者确认(ack) 接收到的消息。(5)RabbitMQ 从队列中删除相应己经被确认的消息。(6)关闭信道。(7)关闭连接。2.2.1 信道这里我们主要讨论两个问题:为何要有信道?主要原因还是在于TCP连接的"昂贵"性。无论是生产者还是消费者,都需要和RabbitMQ Broker 建立连接,这个连接就是一条TCP 连接。而操作系统对于TCP连接的创建于销毁是非常昂贵的开销。假设消费者要消费消息,并根据服务需求合理调度线程,若只进行TCP连接,那么当高并发的时候,每秒可能都有成千上万的TCP连接,不仅仅是对TCP连接的浪费,也很快会超过操作系统每秒所能建立连接的数量。如果能在一条TCP连接上操作,又能保证各个线程之间的私密性就完美了,于是信道的概念出现了。信道为何?信道是建立在Connection 之上的虚拟连接。当应用程序与Rabbit Broker建立TCP连接的时候,客户端紧接着可以创建一个AMQP 信道(Channel) ,每个信道都会被指派一个唯一的D。RabbitMQ 处理的每条AMQP 指令都是通过信道完成的。信道就像电缆里的光纤束。一条电缆内含有许多光纤束,允许所有的连接通过多条光线束进行传输和接收。2.2.2 生产者消费者关于生产者消费者我们需要了解几个概念:Producer:生产者,即消息投递者一方。消息:消息一般分两个部分:消息体(payload)和标签。标签用来描述这条消息,如:一个交换器的名称或者一个路由Key,Rabbit通过解析标签来确定消息的去向,payload是消息内容可以使一个json,数组等等。Consumer:消费者,就是接收消息的一方。消费者订阅RabbitMQ的队列,当消费者消费一条消息时,只是消费消息的消息体。在消息路由的过程中,会丢弃标签,存入到队列中的只有消息体。Broker:消息中间件的服务节点。2.2.3 队列、交换器、路由key、绑定从RabbitMQ的运转流程我们可以知道生产者的消息是发布到交换器上的。而消费者则是从队列上获取消息的。那么消息到底是如何从交换器到队列的呢?我们先具体了解一下这几个概念。Queue:队列,是RabbitMQ的内部对象,用于存储消息。RabbitMQ中消息只能存储在队列中。生产者投递消息到队列,消费者从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(轮询)给多个消费者进行消费,而不是每个消费者都收到所有的消息进行消费。(注意:RabbitMQ不支持队列层面的广播消费,如果需要广播消费,可以采用一个交换器通过路由Key绑定多个队列,由多个消费者来订阅这些队列的方式。)Exchange:交换器。在RabbitMQ中,生产者并非直接将消息投递到队列中。真实情况是,生产者将消息发送到Exchange(交换器),由交换器将消息路由到一个或多个队列中。如果路由不到,或返回给生产者,或直接丢弃,或做其它处理。RoutingKey:路由Key。生产者将消息发送给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则。这个路由Key需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。在交换器类型和绑定键固定的情况下,生产者可以在发送消息给交换器时通过指定RoutingKey来决定消息流向哪里。Binding:RabbitMQ通过绑定将交换器和队列关联起来,在绑定的时候一般会指定一个绑定键,这样RabbitMQ就可以指定如何正确的路由到队列了。从这里我们可以看到在RabbitMQ中交换器和队列实际上可以是一对多,也可以是多对多关系。交换器和队列就像我们关系数据库中的两张表。他们同归BindingKey做关联(多对多关系表)。在我们投递消息时,可以通过Exchange和RoutingKey(对应BindingKey)就可以找到相对应的队列。RabbitMQ主要有四种类型的交换器:fanout:扇形交换器,它会把发送到该交换器的消息路由到所有与该交换器绑定的队列中。如果使用扇形交换器,则不会匹配路由Key。direct:direct交换器,会把消息路由到RoutingKey与BindingKey完全匹配的队列中。topic:完全匹配BindingKey和RoutingKey的direct交换器 有些时候并不能满足实际业务的需求。topic 类型的交换器在匹配规则上进行了扩展,它与direct 类型的交换器相似,也是将消息路由到BindingKey 和RoutingKey相匹配的队列中,但这里的匹配规则有些不同,它约定:1:RoutingKey 为一个点号".“分隔的字符串(被点号”.“分隔开的每一段独立的字符串称为一个单词),如"hs.rabbitmq.client”,“com.rabbit.client"等。2:BindingKey 和RoutingKey 一样也是点号”.“分隔的字符串;3:BindingKey 中可以存在两种特殊字符串"“和”#",用于做模糊匹配,其中"“用于匹配一个单词,”#“用于匹配多规格单词(可以是零个)。如图: · 路由键为” apple.rabbit.client” 的消息会同时路由到Queuel 和Queue2; · 路由键为" orange.mq.client" 的消息只会路由到Queue2 中: · 路由键为" apple.mq.demo" 的消息只会路由到Queue2 中: · 路由键为" banana.rabbit.demo" 的消息只会路由到Queuel 中: · 路由键为" orange.apple.banana" 的消息将会被丢弃或者返回给生产者因为它没有匹配任何路由键。header:headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中 的headers属性进行匹配。在绑定队列和交换器时制定一组键值对, 当发送消息到交换器时, RabbitMQ 会获取到该消息的headers(也是一个键值对的形式) ,对比其中的键值对是否完全 匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。(注:该交换器类型性能较差且不实用,因此一般不会用到)。了解了上面的概念,我们再来思考消息是如何从交换器到队列的。首先Rabbit在接收到消息时,会解析消息的标签从而得到消息的交换器与路由key信息。然后根据交换器的类型、路由key以及该交换器和队列的绑定关系来决定消息最终投递到哪个队列里面。三 RabbitMQ使用3.1 RabbitMQ安装这里我们基于docker来安装。3.1.1 拉取镜像docker pull rabbitmq:management3.1.2 启动容器docker run -d –name rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management3.2 RabbitMQ 客户端开发使用这里我们以dotnet平台下RabbitMQ.Client3.6.9(可以从nuget中下载)为示例,简单介绍dotnet平台下对RabbitMQ的简单操作。更详细的内容可以从nuget中下载源码和文档进行查看。3.2.1 连接Rabbit ConnectionFactory factory = new ConnectionFactory(); factory.UserName = “admin”;//用户名 factory.Password = “admin”;//密码 factory.HostName = “192.168.17.205”;//主机名 factory.VirtualHost = “”;//虚拟主机(这个暂时不需要,稍后的文章里会介绍虚拟主机 的概念) factory.Port = 15672;//端口 IConnection conn = factory.CreateConnection();//创建连接 3.2.2 创建信道IModel channel = conn.CreateModel();说明:Connection 可以用来创建多个Channel 实例,但是Channel 实例不能在线程问共享,应用程序应该为每一个线程开辟一个Channel 。某些情况下Channel 的操作可以并发运行,但是在其他情况下会导致在网络上出现错误的通信帧交错,同时也会影响友送方确认( publisherconfrrm)机制的运行,所以多线程问共享Channel实例是非线程安全的。3.2.3 交换器、队列和绑定 channel.ExchangeDeclare(“exchangeName”, “direct”, true); String queueName = channel.QueueDeclare().QueueName; channel.QueueBind(queueName, “exchangeName”, “routingKey”);如上创建了一个持久化的、非自动删除的、绑定类型为direct 的交换器,同时也创建了一个非持久化的、排他的、自动删除的队列(此队列的名称由RabbitMQ 自动生成)。这里的交换器和队列也都没有设置特殊的参数。上面的代码也展示了如何使用路由键将队列和交换器绑定起来。上面声明的队列具备如下特性: 只对当前应用中同一个Connection 层面可用,同一个Connection 的不同Channel可共用,并且也会在应用连接断开时自动删除。上述方法根据参数不同,可以有不同的重载形式,根据自身的需要进行调用。ExchangeDeclare方法详解:ExchangeDeclare有多个重载方法,这些重载方法都是由下面这个方法中缺省的某些参数构成的。void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary<string, object> arguments);exchange : 交换器的名称。type : 交换器的类型,常见的如fanout、direct 、topicdurable: 设置是否持久化。durab l e 设置为true 表示持久化, 反之是非持久化。持久化可以将交换器存盘,在服务器重启的时候不会丢失相关信息。autoDelete : 设置是否自动删除。autoDelete 设置为true 则表示自动删除。自动删除的前提是至少有一个队列或者交换器与这个交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑。注意不能错误地把这个参数理解为:“当与此交换器连接的客户端都断开时,RabbitMQ 会自动删除本交换器”。internal : 设置是否是内置的。如果设置为true,则表示是内置的交换器,客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。argument : 其他一些结构化参数,比如alternate - exchange。QueueDeclare方法详解:QueueDeclare只有两个重载。QueueDeclareOk QueueDeclare();QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object> arguments);不带任何参数的queueDeclare 方法默认创建一个由RabbitMQ 命名的(类似这种amq.gen-LhQzlgv3GhDOv8PIDabOXA 名称,这种队列也称之为匿名队列〉、排他的、自动删除的、非持久化的队列。queue : 队列的名称。durable: 设置是否持久化。为true 则设置队列为持久化。持久化的队列会存盘,在服务器重启的时候可以保证不丢失相关信息。exclusive : 设置是否排他。为true 则设置队列为排他的。如果一个队列被声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意三点:排他队列是基于连接(Connection) 可见的,同一个连接的不同信道(Channel)是可以同时访问同一连接创建的排他队列;“首次"是指如果一个连接己经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同:即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除,这种队列适用于一个客户端同时发送和读取消息的应用场景。autoDelete: 设置是否自动删除。为true则设置队列为自动删除。自动删除的前提是:至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自动删除。不能把这个参数错误地理解为:当连接到此队列的所有客户端断开时,这个队列自动删除”,因为生产者客户端创建这个队列,或者没有消费者客户端与这个队列连接时,都不会自动删除这个队列。argurnents:设置队列的其他一些参数,如x-rnessage-ttl、x-expires、x-rnax-length、x-rnax-length-bytes、x-dead-letter-exchange、x-deadletter-routing-key,x-rnax-priority等。注意:生产者和消费者都能够使用queueDeclare来声明一个队列,但是如果消费者在同一个信道上订阅了另一个队列,就无法再声明队列了。必须先取消订阅,然后将信道直为"传输"模式,之后才能声明队列。在此我向大家推荐一个架构学习交流群。交流学习群号:478030634 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多QueueBind 方法详解:将队列和交换器绑定的方法如下:void QueueBind(string queue, string exchange, string routingKey, IDictionary<string, object> arguments);queue: 队列名称:exchange: 交换器的名称:routingKey: 用来绑定队列和交换器的路由键;argument: 定义绑定的一些参数。将队列与交换器解绑的方法如下: QueueUnbind(string queue, string exchange, string routingKey, IDictionary<string, object> arguments);其参数与绑定意义相同。注:除队列可以绑定交换器外,交换器同样可以绑定队列。即:ExchangeBind方法,其使用方式与队列绑定相似。3.2.4 发送消息发送消息可以使用BasicPublish方法。void BasicPublish(string exchange, string routingKey, bool mandatory,IBasicProperties basicProperties, byte[] body);exchange: 交换器的名称,指明消息需要发送到哪个交换器中。如果设置为空字符串,则消息会被发送到RabbitMQ 默认的交换器中。routingKey : 路由键,交换器根据路由键将消息存储到相应的队列之中。basicProperties: 消息的基本属性集。body : 消息体( pay1oad ),真正需要发送的消息。mandatory: 是否将消息返回给生产者(会在后续的文章中介绍这个参数).3.2.5 消费消息RabbitMQ 的消费模式分两种: 推(Push)模式和拉(Pull)模式。推模式采用BasicConsume进行消费,而拉模式则是调用BasicGet进行消费。推模式: EventingBasicConsumer consumer = new EventingBasicConsumer(channel);//定义消费者 对象consumer.Received += (model, ea) => { //do someting; channel.BasicAck(ea.DeliveryTag, multiple: false);//确认 }; channel.BasicConsume(queue: “queueName”, noAck: false, consumer: consumer);//订阅消息 string BasicConsume(string queue, bool noAck, string consumerTag, bool noLocal, bool exclusive, IDictionary<string, object> arguments, IBasicConsumer consumer);queue : 队列的名称:noAck : 设置是否需要确认,false为需要确认。consumerTag: 消费者标签,用来区分多个消费者:noLocal : 设置为true 则表示不能将同一个Connection中生产者发送的消息传送给这个Connection中的消费者:exclusive : 设置是否排他arguments : 设置消费者的其他参数consumer: 指定处理消息的消费者对象。拉模式BasicGetResult result = channel.BasicGet(“queueName”, noAck: false);//获取消息channel.BasicAck(result.DeliveryTag, multiple: false);//确认3.2.6 关闭连接在应用程序使用完之后,需要关闭连接,释放资源:channel.close();conn.close() ;显式地关闭Channel 是个好习惯,但这不是必须的,在Connection 关闭的时候,Channel 也会自动关闭。结束语以上简单介绍了分布式系统中消息中间件的概念与作用,以及RabbitMQ的一些基本概念与简单使用。下一篇文章将继续针对RabbitMQ进行总结。主要内容包括何时创建队列、RabbitMQ的确认机制、过期时间的使用、死信队列、以及利用RabbitMQ实现延迟队列……大家觉得文章对你还是有一点点帮助的,大家可以点击下方二维码进行关注。 《乐趣区》 公众号聊的不仅仅是Java技术知识,还有面试等干货,后期还有大量架构干货。大家一起关注吧!关注烂猪皮,你会了解的更多………….. 原文连接:https://www.cnblogs.com/hunte… ...

September 26, 2018 · 2 min · jiezi