目录:
- 将程序部署到服务器
- 服务下线
- 灰度发布
- 服务器扩容
参考/来源:
将程序部署到服务器
主要分为几个步骤
- Linux系统的安装和登录,配置好ip等网络设置
- 安装JDK
- 安装Mysql
- 上传打包成jar的工程文件到Linux
- 运行,设置开启自启动
详细可以参看:手把手教你将程序部署到服务器
服务下线
在生产环境中,如何保证在服务升级的时候,不影响用户的体验,这个是一个非常重要的问题。如果在我们升级服务的时候,会造成一段时间内的服务不可用,这就是不够优雅的。那什么是优雅的呢?主要就是指在服务升级的时候,不中断整个服务,让用户无感知,进而不会影响用户的体验,这就是优雅的。
这里以Spring Cloud作为例,如何进行服务下线
/service-registry
端点
在想下线应用的application.yml中添加配置,从而暴露/service-registry
端点:
management:
endpoints:
web:
exposure:
include: service-registry
发送 POST 请求到/actuator/service-registry
端点:
curl -X "POST" "http://localhost:8000/actuator/service-registry?status=DOWN" \
-H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"
实行后的效果类似如下图:
利用Eureka
在实际项目中,我们可以先使用/service-registry
端点,将服务标记为DOWN,然后监控服务的流量,当流量为 0 时,即可升级该服务。当然,这里假设我们部署了多个服务实例,当一个服务实例DOWN掉之后,其他服务实例仍然是可以提供服务的,如果就部署一台服务的话,那么讨论优不优雅就没那么重要了。
除了上述的下线方式之外,还有一种利用EurekaAutoServiceRegistration
对象达到优雅下线的目标。
- 执行
eurekaAutoServiceRegistration.start()
方法时,当前服务向 Eureka 注册中心注册服务; - 执行
eurekaAutoServiceRegistration.stop()
方法时,当前服务会向 Eureka 注册中心进行反注册,注册中心收到请求后,会将此服务从注册列表中删除。
示例代码如下:
@RestController
@RequestMapping(value = "/graceful/registry-service")
public class GracefulOffline {
@Autowired
private EurekaAutoServiceRegistration eurekaAutoServiceRegistration;
@RequestMapping("/online")
public String online() {
this.eurekaAutoServiceRegistration.start();
return "execute online method, online success.";
}
@RequestMapping("/offline")
public String offline() {
this.eurekaAutoServiceRegistration.stop();
return "execute offline method, offline success.";
}
}
到这里,我们已经介绍了两种相对优雅的下线方式了。
具体如何操作,我们可以根据实际上情况进行包装,或者利用自动化的脚本来实现更加优雅的下线方式。可以参考:https://mp.weixin.qq.com/s/vj05pLhT9-JEOJm7hy6UAg
灰度发布
蓝绿部署
蓝绿部署,英文名为 Blue Green Deployment,是一种可以保证系统在不间断提供服务的情况下上线的部署方式。
如何保证系统不间断提供服务呢?那就是同时部署两个集群,但仅对外提供一个集群的服务,当需要升级时,切换集群进行升级。蓝绿部署无需停机,并且风险较小。其大致步骤为:
- 部署集群 1 的应用(初始状态),将所有外部请求的流量都打到这个集群上
- 部署集群 2 的应用,集群 2 的代码与集群 1 不同,如新功能或者 Bug 修复等
- 将流量从集群 1 切换到集群 2
- 如集群 2 测试正常,就删除集群 1 正在使用的资源(例如实例),使用集群 2 对外提供服务
因为在使用蓝绿部署的方式时,我们需要控制流量,所以我们需要借助路由服务,如 Nginx 等。
滚动部署
滚动部署,英文名为 Rolling Update,同样是一种可以保证系统在不间断提供服务的情况下上线的部署方式。和蓝绿部署不同的是,滚动部署对外提供服务的版本并不是非此即彼,而是在更细的粒度下平滑完成版本的升级。
如何做到细粒度平滑升级版本呢?滚动部署只需要一个集群,集群下的不同节点可以独立进行版本升级。
比如在一个 12 节点的集群中,我们每次升级 4 个节点,并将升级后的节点重新投入使用,周而复始,直到集群中所有的节点都更新为新版本。
这种部署方式相对于蓝绿部署,更加节约资源,因为它不需要运行两个集群。但这种方式也有很多缺点,例如:
- 没有一个确定 OK 的环境。使用蓝绿部署,我们能够清晰地知道老版本是 OK 的,而使用滚动发布,我们无法确定。
- 修改了现有的环境。
- 如果需要回滚,很困难。举个例子,在某一次发布中,我们需要更新 100 个实例,每次更新 10 个实例,每次部署需要 5 分钟。当滚动发布到第 80 个实例时,发现了问题,需要回滚。这时,我们估计就要疯了。
- 有的时候,我们还可能对系统进行动态伸缩,如果部署期间,系统自动扩容/缩容了,我们还需判断到底哪个节点使用的是哪个代码。尽管有一些自动化的运维工具,但是依然令人心惊胆战。
并不是说滚动发布不好,滚动发布也有它非常合适的场景。
金丝雀部署
金丝雀部署又称灰度部署(或者,灰度发布),英文名为 Canary Deployment,是指在黑与白之间,能够平滑过渡的一种发布方式。
金丝雀的名称来源于「矿井中的金丝雀」,早在 17 世纪,英国矿井工人发现,金丝雀对瓦斯这种气体十分敏感,空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱;而当瓦斯含量超过一定限度时,虽然鲁钝的人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为“瓦斯检测指标”,以便在危险状况下紧急撤离。
我们来看一下金丝雀部署的步骤:
- 准备好部署各个阶段的工件,包括:构建工件,测试脚本,配置文件和部署清单文件
- 从负载均衡列表中移除掉“金丝雀”服务器
- 升级“金丝雀”应用(切断原有流量并进行部署)
- 对应用进行自动化测试
- 将“金丝雀”服务器重新添加到负载均衡列表中(连通性和健康检查)
- 如果“金丝雀”在线使用测试成功,升级剩余的其他服务器(否则就回滚)
在金丝雀部署中,常常按照用户量设置路由权重,例如 90% 的用户维持使用老版本,10% 的用户尝鲜新版本。不同版本应用共存,经常与 A/B 测试一起使用,用于测试选择多种方案。
金丝雀部署比较典型的例子,就是我们在使用某个应用的时候,该应用邀请我们进行“内测”或者“新版本体验”,如果我们同意了,那么我们就成了金丝雀。
服务器扩容
扩容策略可以分为两种,:
- 一种是对单机整体扩容,也就是机器内部包含CPU、内存、存储设备等
- 另一种是扩容对应的组件,例如扩内存、扩磁盘、扩CPU。
整机硬件
整机扩容的好处是,有很多专业的服务器硬件供应商,例如IBM、浪潮、DELL、HP等,专业的硬件供应商,他们组装以及搭配方面可能经验更加丰富,另外有些公司会对组件进行一些优化,从而服务器更加稳定,可以类比为买电脑,有的人可能选择买淘宝卖家已经组装好的台式,有的人可能自己买各种硬件自己回家组装,对于一般人而言,选择前者是较为靠谱的选择,因为你即使懂硬件的一些参数,也难保自己搭配的机器是否能发挥各个部件最大性能。
组件
对于一些技术能力强悍的公司,更多的是自己买各种组件组装,这样成本更低,因为节省了组装等费用,并且可以根据业务个性化定制,例如有的公司是计算密集型的,那么主要是更换更强的CPU,有的IO密集型,那么扩容的应该是内存等,有的公司需要存储大量的数据,那么可能扩容的是硬盘等存储设备。
组件包含:
cpu
Intel、Amd ,参考频率、线程数等
网卡
百兆->千兆 -> 万兆
内存
ECC校验
磁盘
SCSI HDD(机械)、HHD(混合)、SATA SSD、PCI-e SSD、 MVMe SSD
拆分扩容后存在的问题
随着业务的增长,系统变得越来越庞大, 根据系统功能拆分成独立而又互通的项目, 比如交易系统、财务系统、生产流程系统、物流系统、网站系统等等,但是分布式结构会存在很多问题。对于这些问题每一个都值得深入探讨
数据共享问题
所有的服务之间数据如何共享同步,这是一个需要考虑的问题,微服务架构中,数据不可能只有一份,没法避免机器损坏等原因造成的数据丢失,多份数据之间如何同步?目前可供参考的解决思路是建立数据中心、搭建数据库集群。
接口调用问题
不同的服务器之间进行调用遵循远程调用协议RPC
JAVA RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。
dubbo:提供了面向接口代理的高性能RPC调用
持久化数据雪崩问题
数据库分库分表 资源隔离 缓存设定数据持久化策略
高并发问题
缓存:诸如缓存击穿、穿透、雪崩等
数据闭环:为了便于理解,举个例子,对于淘宝而言,有网页版、IOS版、安卓版、还有什么一淘等等,虽然客户端不一样,但是展示的商品信息是相同的,也就是一件商品,无论是哪个端用的数据是一样的,需要一套方案来解决并发下根据相同数据在不同端进行不同展示的问题,这就叫数据闭环。
数据一致性问题
这是一个难点,大意就是多个服务器之间数据如何保证一致性,同样的商品在不同客户端服务端端价格应该是一样的, 通常使用分布式锁。
分布式ID
面对分布式ID,需要满足下面的要求:
- 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
- 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
- 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
- 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。
目前业界常用的ID生成策略有很多,例如UUID、雪花生成算法、Redis、Zookeeper等,这儿只简单讲讲UUID以及Snowflake。
UUID生成算法
UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000
,到目前为止业界一共有5种方式生成UUID。
优点:
- 性能非常高:本地生成,没有网络消耗。
缺点:
不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用:
① MySQL官方有明确的建议主键要尽量越短越好[4],36个字符长度的UUID不符合要求。
② 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变 动,严重影响性能。
雪花生成算法
这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示:
41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。
12个自增序列号可以表示212212个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。
这种方式的优缺点是:
优点:
- 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
- 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
- 可以根据自身业务特性分配bit位,非常灵活。
缺点:
- 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。