服务端开发:技术、方法与实用解决方案
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 服务端开发技术栈

正如前文提及的那样,现阶段的服务端开发早已不再是简单地围绕着数据库编排CRUD服务。很多时候,服务端应用的第一行代码尚未写就,工程师便需要与产品、运营、法务等人员及网络、中间件、操作系统、搜索、数据、算法、运维、安全等技术体系打交道,互联网企业对工程师的要求越来越高。

单从技术栈来看,服务端开发涉及编程语言、开发工具、开发框架、数据库与数据存储、中间件、操作系统、应用部署、运维监控等知识体系。

1.2.1 编程语言

对服务端开发来说,编程是最基本的能力。目前,全球已经投入使用的编程语言超过50种,其中多数可用于服务端开发,但术业有专攻,不同语言的流行度和学习成本不一样,各自的特性也有较大差异。在选择开发语言时,可以参考TIOBE排行榜。TIOBE排行榜是基于业界资深软件工程师、课程和第三方厂商的数量,使用搜索引擎和第三方数据统计出的排名,每月更新一次。排名反映的是编程语言的热度。工程师可以用它来评估自身编程技能是否跟上了趋势,也可以通过它来了解世界范围内开发语言的走势。

1.选择编程语言的一般原则

在实践中,如何选择服务端编程语言呢?易学习、功能强大、生态丰富是朴素而直接的判断要素,但与此同时也充满争议,因为这些要素缺乏具备公信力的评判标准,感性评估则往往因人而异。在软件工程师圈子里谈及编程语言时,假设笔者说一句“PHP是世界上最好的语言”,则必然会引起工程师们的激烈讨论,众多Java、Python、C/C++、Go等语言的爱好者会一起“群殴”笔者的观点。那么,关于编程语言的选择难道就没有可遵循的原则吗?当然不是,通常有4个选择依据。

(1)编程语言须服务于系统架构

合适的编程语言应根据具体的场景来选择,而不是基于工程师自身的偏好。简而言之,即根据场景选择语言,而不是以语言去应付场景。例如引擎层是以C/C++为主,而算法层则是以Python为主。在一些特定的领域,Lisp、Scala等冷门语言亦有应用。

(2)尽量复用前人积累

软件工程一旦发展到比较庞大的规模,即使采用了先进的编程语言,如果不能很好地复用前人的积累,而不得不重新造轮子,也必然会导致效率降低。

(3)尽量避免冷门语言

一门编程语言的兴起通常都是从一个典型的软件产品开始的。比如日本人设计的Ruby语言,它的代表作是Gitlab。商业公司如果选用它,就不得不面对冷门语言工程师招聘困难的问题,即便招聘到了资深的冷门语言专家,软件的开发、维护,以及工程师的发展也将是问题。

(4)尽量选择生态丰富的语言

微服务开始起步后,给更多小众语言提供了更好的生存环境。Service Mesh技术让小众语言也可以通过接口或者系统调用获得更多中间件体系提供的便利,但当前尚处于前沿,还远未到完全成熟应用的阶段。在微服务之下的SOA(Service-Oriented Architecture,面向服务的架构),大多数还是建立在传统语言之上。比如阿里坚持深耕Java体系多年,一个重要的原因是其他语言短期内难以比拟Java的生态优势。事实上,不少语言在编程效率上优于Java,但一旦用来做大型工程,在达到一定规模时通常会陷入自洽性矛盾:要么放弃语言的灵活性,要么放弃工程的可持续性。

2.互联网企业常用的服务端编程语言

可用于服务端开发的编程语言很多,企业在选择编程语言时会根据自身的实际情况权衡,分出主次,即以1种编程语言为主要开发语言,1~3种编程语言为辅。这不难理解,选择一种编程语言为主,持续深耕,对于软件开发、维护,技术积累、传承,都是非常有利的,但同时,一种编程语言很难满足众多复杂业务场景的需要,因此需要选择几种辅助编程语言。在此,笔者列举了部分互联网企业服务端开发的主流编程语言,如表1-1所示,供读者参考。

表1-1 部分互联网企业服务端开发的主流编程语言

1.2.2 开发工具

工欲善其事,必先利其器。从事服务端开发工作,选择称手的工具很重要。常用的开发工具包括集成开发环境、代码管理工具及建模工具。

1.集成开发环境

集成开发环境(Integrated Development Environment,IDE)是指用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。它是集成了代码编写功能、分析功能、编译功能、调试功能等于一体的开发软件服务套件,所有具备这一特性的软件或者软件套(组)都可以叫集成开发环境。如微软的Visual Studio系列、JetBrains的IDE系列等。IDE可以独立运行,也可以和其他程序并用。

IDE众多,如何选择呢?针对服务端主流编程语言,笔者推荐两个系列的IDE给大家。

(1)VSCode

VSCode(Visual Studio Code)是由微软开发的一款功能强大的现代化轻量级IDE,社区版免费。VSCode具有强大的插件扩展能力,几乎支持所有主流语言(C++、Java、Go、Python等)的项目开发。该IDE支持语法高亮、代码自动补全(又称IntelliSense)、代码重构等功能,并且内置了命令行工具和Git版本控制系统。用户可以更改主题和键盘快捷方式实现个性化设置,也可以通过内置的扩展程序商店安装扩展以拓展软件功能。

(2)JetBrains系列

JetBrains是一家捷克的软件开发公司,该公司出品了支持Java、C++、Python、Go等主流编程语言的系列知名IDE,堪称IDE界的集大成者。其中最具代表性的是IntelliJIDEA,在业界被公认为最好的JavaIDE,它在智能代码助手、代码自动提示、重构、JavaEE支持、版本工具(Git、SVN等)、JUnit、CVS整合、代码分析、GUI设计等方面表现优异,深受Java工程师认可。此外,JetBrains为Go语言提供了GoLand,为Python语言提供了PyCharm,这两款IDE也广受工程师赞誉,长期位于最受欢迎的IDE榜单前列。

2.代码管理工具Git

在实践中,一个软件项目通常由多名工程师协作完成。工程师各自开发自己所负责部分的同时,还需兼顾整个项目。由几个人协同开发的小项目尚可通过人力管理来应对,但几十人、几百人协作的项目呢?如果没有一个强大的工具支撑,那将无疑是人力“黑洞”。著名的版本控制软件Git便是在这种背景下诞生的。

Git最初是Linus Torvalds为了帮助管理Linux内核而开发的一个开放源码的版本控制软件。它是一个开源的分布式版本控制系统,可用于敏捷、高效地处理任何或小或大的项目。通过Git,工程师可以方便地创建代码仓、创建开发分支、合并代码、提交代码、解决冲突、查看提交记录等。

3.建模工具Visual Paradigm

服务端开发工程师的日常工作并不止于“编写运行于服务器端的程序”。根据笔者的经验,编程占据工作时间的比例通常不到30%,那么,其他时间在做什么呢?主要是做设计。设计的本质其实就是“与自己的沟通”“与合作伙伴的沟通”。通过沟通帮助自己厘清思路,同时让合作者理解你的思路。在实际工作中,笔者将至少40%的时间用在沟通上,比如业务对焦、需求评审、服务端方案评审、客户端方案评审、系统重构、风险评估等,沟通对象包括产品、运营、技术、质量、视觉、交互等。在跨团队合作、跨部门合作甚至对外合作的场景中,“文山会海”往往使得沟通更加低效。

为了提升设计环节的沟通效率,在较大的项目中,业界一般采用建模利器Visual Paradigm辅助。Visual Paradigm是一款UML建模工具,可以支持多种图表类型,如类图、业务用例图、时序图、状态机图、动态图、组件图、部署图、对象图、场景交互图、领域模型图、业务框架图、组件关系图等。

通过Visual Paradigm将需求分析、抽象建模、系统设计等环节有机地链接起来,循序渐进,从具体的业务用例到抽象的领域模型,再到技术实现方案,便于不同角色理解,提升沟通效率的同时提升设计质量、减少后续维护成本。Visual Paradigm在服务端开发生命周期中的应用如图1-3所示。

图1-3 Visual Paradigm在服务端开发生命周期中的应用

1.2.3 开发框架

1.什么是框架

在软件工程中,框架(Framework)的定义为:整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法。一个框架是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,为构件复用提供了上下文关系。

框架是实现了某应用领域通用功能的底层服务,软件开发者可以在通用功能已经实现的基础上开始具体的系统开发。框架提供了所有应用期望的默认行为的类集合,具体的应用通过重写子类或组装对象来支持应用专用的行为。通俗地说,框架是完成了某种应用的半成品,它可以提供一些常用的工具类和基础通用化的组件,基于此,软件开发者可以专注于自身业务的开发。

2.为什么要使用框架

软件系统发展到今天已经很复杂了,特别是服务器端软件,它涉及资源、网络、中间件、离线计算、搜索引擎、实时计算、运维、测试、安全等一系列技术体系。因此,为开发出完善、健壮的软件,对工程师的要求将会非常高。如果采用成熟、稳健的框架,那么一些基础的通用工作(如事务处理、安全性、数据流控制等)就可以交给框架处理,工程师只需要专注于完成具体的业务逻辑设计,就可大大降低开发难度。从软件工程师的角度看,使用框架最明显的好处是复用,包括设计复用和分析复用。

❑设计复用:框架提供可复用的抽象算法及高层设计,并能将大系统分解成更小的构件,而且能描述构件间的内部接口。这些标准接口使得在已有构件的基础上通过组装建立各种各样的系统成为可能。只要符合接口定义,新的构件就能插入框架中,构件设计者就能复用构架的设计。

❑分析复用:框架的使用者若按照框架的思想和规范来分析、描述、设计软件工程,就可以将软件工程划分为符合一定标准的构件,那么使用同一框架的工程师之间就能进行高效的沟通。简言之,统一了语言之后,使用同一框架的工程师就可采用统一的工程语言来沟通。

一种技术的终极目标是为业务发展而服务。从业务的角度来讲,首先,框架服务于企业的业务发展和战略规划;其次,框架可提高企业的竞争力,包括降低研发成本、提高质量、缩短周期等;最后,框架实现上述目标的方式是进行有效的知识积累。软件开发是一种知识活动,因此知识的高效积累至关重要。框架能够采用一种结构化的方式对某个特定的业务领域进行描述,也就是将这个领域相关的技术以代码、文档、模型等方式固化下来。综合来看,合理地使用框架可以带来以下诸多好处。

❑显著提升代码的可复用性,从而提高软件生产效率和质量。

❑软件代码结构规范化,从而降低工程师之间的沟通成本和后期维护成本。

❑提高知识积累效率,通过设计框架和领域构件使得知识得以体系化积累。

❑降低软件研发难度,工程师可更专注于业务领域,使需求分析更充分。

❑提高协同工作效率,基于统一的规范和原型,有助于多人协同工作。

❑大粒度地复用可降低平均研发费用,缩短开发周期,减少开发人员,降低维护成本。

3.框架的分类

框架与类库是不同的,框架强调的是软件的设计重用性和系统的可扩充性,以缩短大型应用软件系统的开发周期,提高开发质量。相较于传统的基于类库的面向对象重用技术,应用框架更注重于面向专业领域的软件重用。应用框架具有领域相关性,构件根据框架进行复合而生成可运行的系统。框架的粒度越大,其中包含的领域知识就越完整。考虑到面向的领域和编码实现,软件开发框架一般可分为如下3类。

1)基础类库。基础类库是一种最基础的框架,涵盖多数项目所需要的基础类。例如,Java集合框架主要包括两种类型的容器:一种是集合(Collection),存储一个元素集合;另一种是图(Map),存储键/值对映射。Collection接口又分List、Set和Queue三种子类型,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap等。

2)基础框架。基础框架一般是针对于某特定领域,实现特定领域所需要的常用功能。例如Hibernate,它是一个对象关系映射框架,支持几乎所有主流的关系型数据库,如MySQL、Oracle、SQL Server和DB2等,开发者只须在配置文件中指定好当前使用的数据库,而不必关注不同数据库之间的差异。Hibernate提供了一系列数据访问接口,工程师可以通过这些接口轻松地使用面向对象思想对数据库进行操作。

3)平台框架。平台框架一般需整合或实现某种编程语言开发所需要的常用功能。例如Spring,它是一个综合型框架,致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的解决方案。一些场景下,Spring可以作为应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。Spring功能强大,可提供IOC、AOP及Web MVC等功能。Spring可以单独应用于构建应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用。

4.常用框架举例

针对部分主流服务端编程语言,笔者列举了对应的常用框架,如表1-2所示。

表1-2 部分编程语言的常用框架举例

1.2.4 数据库与数据存储

信息时代,数据已悄然成为企业的核心资产。由于数据库是数据唯一的持久层,几乎所有的业务流程最终都依赖数据库中的数据,因此作为服务端开发工程师,掌握数据库及数据存储技术尤为重要。本节将着重介绍数据与数据库的基本概念、分类以及常用数据库的特点和选型指标等。关于数据库及数据存储实战,笔者将在后文中专门介绍。

1.数据与数据库

数据是数据库中存储的基本对象,是按一定顺序排列组合的物理符号。数据有多种表现形式,可以是数字、文字、图像,甚至是音频或视频,它们都可以经过数字化后存入计算机。数据库是数据的集合,具有统一的结构形式并存放于统一的存储介质内,是多种应用数据的集成,并可被各个应用程序所共享。

数据库是数据管理的有效技术,是由一批数据构成的有序集合,被存放在结构化的数据表里。数据表之间相互关联,反映客观事物间的本质联系。数据库能有效地帮助一个组织或企业科学地管理各类信息资源。

2.数据分类

对于用户而言,在通过个人计算机、手机等设备使用软件应用时,接触到的都是图形化界面,无法直接接触到数据库。信息时代,数据库无处不在,生活的诸多方面都是建立在数据库的基础上的。例如,登录App时,用户的账号和密码都存储在服务器的数据库中;购买火车票、机票时,余票信息、个人购票记录也存储在数据库中;网上购物、订外卖、订酒店、订电影票等信息也都存储在数据库中。

数据库是存储数据的,没有数据的数据库,就不能称为数据库,个性推荐、运营分析等也需要大量的数据。那么数据从哪里来呢?在互联网领域,数据按来源大致可以分为以下两类。不同类型的数据,收集方式也存在差异。

(1)用户必要数据

比如电商平台的用户购物记录、收藏记录,这些数据需要存储到服务端的数据库中,当用户查看记录时,前端发起请求,经由服务端从数据库中获取对应数据,并显示出来。

(2)运营、分析数据

这类数据通常对用户不可见,而是企业为了实现个性推荐、运营分析所使用的数据。获取这类数据的方式主要是埋点,即在产品流程(用户与软件客户端交互流程)的关键环节植入相关统计代码,用来追踪用户的行为,并上传到服务器,通过数据库存储起来。比如,为了实现个性化推荐,电商平台会统计用户的浏览时间、点击商品等关键信息,基于这些数据来实现。

3.客户端、服务端和数据库

软件客户端和服务端的协作都需要有相应的服务器端程序提供服务。在简单的应用场景中,服务端主要围绕数据库编排CRUD服务。客户端、服务端和数据库的关系如图1-4所示。

图1-4 客户端、服务端和数据库的关系

4.数据库分类

数据库大致可以分为两大类,即SQL数据库和NoSQL数据库。SQL(Structured Query Language)数据库指关系型数据库,主要代表有SQL Server、Oracle、MySQL、PostgreSQL、SQLite。NoSQL(Not Only SQL)泛指非关系型数据库,主要代表有MongoDB、Redis、HBase、Memcached。SQL、NoSQL数据库在存储数据类型和存储方式上差异较大。

关系型数据库适合存储结构化数据,如用户的账号、积分、等级、注册时间等。这些数据通常需要做结构化查询,比如过滤出积分大于1000的所有用户,使用SQL查询就非常方便。在这类场景下,关系型数据库就略胜一筹。

随着互联网的发展,海量数据场景越来越多,如发微博、发微信、发评论等。一方面,这些数据规模大,增长速度难以预计;另一方面,这些数据类型比较复杂,可能同时包括文字、图片、音频、视频等,使用关系型数据库无法直接存储。关系型数据库在应对这些场景时显得有些力不从心,逐渐暴露出许多难以克服的难题,因此出现了针对大规模数据场景,以性能卓越和应用便捷为目标的数据库产品—NoSQL数据库。NoSQL数据库是“非关系实体模型”的数据库,NoSQL的意思是“不只是SQL”(Not only SQL),而不是“不是SQL”(No SQL)。显然,NoSQL数据库的出现并不是要完全否认或替代关系型数据库,而是作为传统关系型数据库的一个合理补充。

5.服务端常用数据库

(1)常用关系型数据库

1)Oracle。甲骨文公司开发的商业数据库,不开源,支持所有主流平台,性能好,功能强,稳定性好,安全性好,支持大数据量。在很长的一段时期,甲骨文凭借其在服务器、数据库软件、存储设备上的优势,几乎垄断了全球商用数据库系统的市场。但是,Oracle数据库非常复杂,收费高,以至于在互联网飞速发展、数据量爆炸式增长的背景下,这种昂贵、扩展性不足的商业数据库已不再是企业的优选方案。2009年年底,阿里开始“去IOE”,其中“O”指的便是Oracle数据库,即用MySQL和自研数据库替代Oracle。目前,头部互联网企业已经很少大规模使用Oracle。

2)SQL Server。SQL Server最初由Microsoft、Sybase和Ashton-Tate三家公司共同研发,后来由Microsoft独立研发。作为一款商业数据库,SQL Server具有易用性、可伸缩性,以及优秀的数据管理、分析功能,是一款高性价比的商业数据库。但是,它开放性较差,仅支持Windows平台。

3)MySQL。MySQL最初由瑞典的DataKonsultAB公司研发,该公司被Sun公司收购,后来Sun公司又被Oracle公司收购,因此MySQL目前属于Oracle旗下产品。MySQL软件采用了双授权政策,分为社区版和商业版。由于体积小、速度快、总体拥有成本低,MySQL通常是中小型网站的数据库首选。

4)PostgreSQL。PostgreSQL使用BSD协议的完全开源的、免费的关系型数据库管理系统,支持多种操作系统,功能强大,可以和多种开源工具配合。PostgreSQL不仅仅是关系型数据库,还同时支持JSON数据、全文检索以及其他扩展。

(2)常用非关系型数据库

1)Redis。Redis是现在最受欢迎的NoSQL数据库之一,具备如下特性:基于内存运行,性能高;支持分布式,理论上可以无限扩展;是键值对存储系统;使用ANSI C语言编写,遵守BSD协议,支持网络,是可持久化的日志型数据库,提供多种语言的API。

相比于其他数据库类型,Redis具备的优势是:读写速度极快、支持丰富的数据类型、操作具有原子性等。由于Redis性能出色,已被Twitter、阿里、百度等公司在开源版本的基础上继续深度开发与定制。

2)HBase。HBase(Hadoop Database)是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,它本质上是一个数据模型,可以提供快速随机访问海量结构化数据的能力。HBase的适用场景如下:

第一,写密集型应用。HBase支持每天写入量巨大,而读数量相对较小的应用,比如IM的历史消息、游戏的日志等。

第二,不需要复杂查询条件来查询数据的应用。HBase只支持基于rowkey的查询。对于HBase来说,单条记录或者小范围的查询是可以接受的,大范围的查询由于分布式的原因,在性能上可能有点影响,而对于像SQL的join等查询,HBase无法支持。

第三,对性能和可靠性要求非常高的应用。由于HBase本身没有单点故障,因此可用性非常高。

6.数据库特点及选型指标

前文已经提及不同数据库的特点和适用场景,此外简要概括一下关系型数据库和非关系型数据库的特点。

(1)关系型数据库的主要特点

❑表结构较严格,支持行列式存储结构化数据。

❑需要预定义数据类型。

❑部分支持事务特性,可保证较强的数据一致性。

❑支持SQL语言,增删改查功能强大,大都支持多表join操作。

❑较为通用,技术较成熟。

❑不适合处理大数据,当数据读写量较大时,通常需要分库分表。

❑高并发性能不足,扩展较为复杂。

(2)非关系型数据库的主要特点

❑表结构较灵活,如列存储、键值对存储、文档存储、图形存储;支持非结构化数据。

❑部分不需要预定义数据类型,甚至不需要预定义表。

❑不支持事务特性,数据一致性能力弱。

❑非SQL查询语言或类SQL查询语言,但功能都比较弱;通常不支持多表join操作,或有限支持。

❑支持大数据量,且多数支持分布式。

❑高并发性能较强,易扩展。

数据库选型一般需要考虑的指标有数据量、并发量、实时性、一致性要求、读写分布、读写类型、安全性、运维成本。当然,不同的应用场景需要关注的指标不同,例如“双11”大促活动,数据量和并发量是最重要的两个指标;而对于银行转账等金融业务,一致性要求是最关键的指标。

1.2.5 中间件

中间件(Middleware)是一种应用于分布式系统的基础软件。从纵向层次来看,中间件位于各类应用、服务与操作系统、数据库系统以及其他系统软件之间,主要解决分布式环境下数据传输、数据访问、应用调度、系统构建、系统集成和流程管理等问题。目前,中间件并没有很严格的定义,但业界普遍接受IDC的定义:中间件是一种独立的系统软件服务程序,管理计算资源和网络通信,帮助分布式应用软件在不同的技术之间共享资源。从这个意义上可以用一个等式来表示中间件:中间件=平台+通信。这也就限定了只有用于分布式系统中才能叫中间件,同时也把它与支撑软件和实用软件区分开来。

为了让读者直观地感受中间件与应用软件、支撑软件(如操作系统)的关系,我们来看一个例子,如图1-5所示:客户端请求传递到服务端,不是直接由应用承接,而是要经过负载均衡中间件(如Nginx)进行处理后,才由具体的应用进一步处理,而应用之间的协作一般也需要借助中间件;当然,中间件和应用都需要服务器的操作系统和硬件支撑。

图1-5 中间件与应用软件、支撑软件(如操作系统)的关系

中间件在过去的十年间大放异彩,庞大的中间件群体带来了巨大的效率提升。如果说开发框架使软件研发工程师能够通过代码掌控一切,那么,中间件则通过集中式服务来降低工程师的编程工作量。常用的中间件有消息中间件、事务中间件、数据中间件等。

1.消息中间件

消息中间件也称消息队列,是分布式系统中重要的组件,主要解决应用耦合、异步消息、流量削峰等问题。它可以实现高性能、高可用、可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件。消息队列在电商系统、消息通信、日志收集等应用中发挥着关键作用。以阿里为例,它研发的消息队列(RocketMQ)在历次天猫“双十一”活动中支撑了万亿级的数据洪峰,为大规模交易提供了有力保障。

作为提升应用性能的重要手段,分布式消息队列技术在互联网领域得到了越来越广泛的关注。常用的分布式消息队列开源软件有Kafka、ActiveMQ、RabbitMQ及RocketMQ。

为了便于读者理解消息中间件,我们以简化版电商架构为例进行具体介绍。如图1-6所示,在传统强耦合订单场景中,客户在电商网站下订单,订单系统接收到请求后,立即调用库存系统接口扣减库存。

图1-6 商品下单简化流程

上述模式存在如下巨大风险:

❑假如库存系统无法访问(升级、业务变更、故障等),则订单减库存将失败,从而导致订单失败;

❑短时间内大量的请求、频繁查询库存、修改库存等,库存系统负载极大。

我们引入消息中间件,解除强耦合性,处理流程又会怎样呢?如图1-7所示,订单系统中,用户下单后,订单系统完成持久化处理,将消息写入消息中间件,返回用户订单,此时客户可以认为下单成功。消息中间件提供异步的通信协议,消息的发送者将消息发送到消息中间件后可以立即返回,不用等待接收者的响应。消息会被保存在队列中,直到被接收者取出。库存系统中,从消息中间件中获取下单信息,库存系统根据下单信息进行操作。

图1-7 引入消息中间件后的商品下单简化流程

2.事务中间件

事务中间件又称事务处理管理程序,是当前用得最广泛的中间件之一,主要用于解决分布式环境下的事务一致性问题。在单机数据库下,维持事务的ACID(Atomicity、Consistency、Isolation、Durability)特性很容易,但在分布式系统中并不容易,而分布式事务中间件可以保证分布式系统中的分布式事务的ACID特性。

通常,分布式事务中间件可支持DRDS、RDS、MySQL等多种数据源,并兼容消息队列实现事务消息。通过各种组合,可以轻松实现分布式数据库事务、多库事务、消息事务、服务链路级事务等多种业务需求。常用的分布式事务中间件有GTS、TXC、Seata等。

3.数据中间件

数据中间件处于底层数据库和应用系统之间,主要用于屏蔽异构数据库的底层细节,是客户端与后台的数据库之间进行通信的桥梁。数据中间件一般用于解决海量请求下数据访问瓶颈及数据库的容灾问题,具备分布式数据库全生命周期的运维管控能力,支持分库、分表、平滑扩容、结果集合并、SQL解析、数据库容灾和分布式事务等特性。开源的数据中间件有Vitess、MyCat、Atlas、OneProxy等。

总体来看,目前开源的数据中间件比较少,广受认可的更是寥寥无几,大型互联网企业几乎都自研数据中间件,如阿里的DRDS、蚂蚁的Zdal、京东数科的CDS、美团点评的Zebra等。

1.2.6 操作系统

操作系统是管理计算机硬件与软件资源的计算机程序。操作系统需要管理和配置内存、决定系统资源供需的优先次序、控制输入设备与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。在计算机中,操作系统是最基本,也是最重要的基础性系统软件。

1.Linux系统的优势

对于服务端开发工程师,学习并掌握操作系统知识极为重要。通常,服务端程序几乎都是部署在Linux操作系统的服务器上的。为什么是Linux而不是常见的Windows或其他操作系统呢?主要有以下因素。

(1)开源

源代码开放使得企业可以获取整个操作系统的源码,并根据自己的需求对操作系统进行二次开发,甚至打造“定制”的操作系统。同时,源码开放使企业可以洞悉操作系统的实际运转情况,而诸如Windows、MacOS这种闭源商用操作系统,则很难掌控。

(2)免费

Linux开源意味着企业不用为操作系统支付任何费用,而Windows是商用操作系统,大规模使用成本高昂。很多时候,成本是企业考量的最重要的因素之一。毕竟企业要生存、发展,在满足需要的前提下节约成本,何乐不为呢?

(3)稳定

Linux系统以其稳定性而闻名,这也是企业非常注重的一个因素。企业里有很多服务器要求7×24小时不间断稳定运行,而这更是Linux最擅长的地方。Linux更新升级或者配置某一项操作的时候,只须重新启动对应的服务即可,无须重启服务器。

(4)生态

在Linux开源之后,一批技术专家迅速聚集起来,他们不求回报地为Linux提供代码、修复bug,提出新的想法,帮助Linux成长,直到如今形成了一个庞大的开源社区。现在开发者想要学习或者获取Linux的最新版本,都可以在开源社区上找到自己所需要的资料,在开发过程中遇到的问题也可以上社区和同行交流并寻求帮助。

2.Linux常用命令和操作

Linux系统相关的知识点非常多,市面上介绍Linux系统的书籍基本都是“大部头”,通篇学习实属不易,效果也很难保证。对于服务端开发工程师来说,可重点学习一些常用的命令和操作。

(1)Linux基础

如图1-8所示,Linux基础知识包括Linux版本和基础概念两个部分。Red Hat、Ubuntu、CentOS都是Linux的常见版本。

图1-8 Linux基础知识

(2)常用帮助命令

Linux系统的命令数量有上千个,每个命令又有若干个甚至数十个适配不同情景的参数,单纯通过记忆掌握这些命令是非常困难的。因此,Linux系统为使用者提供了帮助命令,只需要正确使用Linux的帮助命令,就能够快速地定位到自己想要的命令和参数。Linux系统常用的帮助命令有3个,即man、help、info,基础用法如表1-3所示。

表1-3 Linux系统常用帮助命令

(3)文件和目录管理

文件和目录管理命令是最基础的Linux命令,基于这两类命令,用户可以在Linux系统下创建文件和目录,也可以对已有文件和目录进行查看、删除、复制等操作。如表1-4所示,笔者列举了几个常用的文件和目录管理命令。

表1-4 Linux系统常用的文件和目录管理命令

(4)Vim文本编辑

与Windows操作系统不同,在安装Linux系统的服务器上,为了节省内存、提高效率等,通常不会安装图形界面,而只能通过命令行来进行各种操作。因此,当我们在命令行下新增文件、更改文件、编写脚本时,不可避免地要用到文本编辑器。目前,可供选择的文本编辑器有很多,如Vim、emacs、pico、nano等,作为服务端开发工程师,应熟悉至少一款Linux文本编辑器的用法。

就受工程师欢迎的程度来看,Vim可以说是无出其右。Vim是一个高效、功能强大、可扩展的编辑器。Vim有自己的脚本语言,称为Vim脚本(也称为VimScript或VimL),用户可以通过多种方式使用它来增强Vim,例如为其他编程语言启用语法高亮、自动化语法检查等。此外,Vim还具有高度可配置性,包含Vim核心全局设置(称为vimrc)的文件可以在各个Vim安装之间共享。

(5)文件系统与文件查找

作为服务端开发工程师,在日常定位问题时,最常用的手段是日志,因此掌握搜索、查看服务端运行日志的技能十分重要。在Linux系统上进行文件查找的命令主要有两个:grep和find。这两个命令的用法非常多,在此,笔者仅列举几个基础用法,如表1-5所示。

表1-5 grep和find命令的基础用法

除了上面列举的知识点外,系统管理、磁盘分区、逻辑卷、shell、文本操作等也有必要学习掌握。

1.2.7 应用部署

工程师开发的代码需要经过编译、打包等流程,并最终部署到服务器上,才能运行并对外提供服务。在2014年前,生产环境应用部署一般是通过工程师编写脚本实现的,而开发环境则基本是手动部署,效率普遍较低。经过多年的发展,目前一些头部互联网企业的研发平台和流程已经非常完善,不仅可以支持不同研发环境下自动编译、部署,而且能提供场景化分析、定制化质量和风险控制能力。研发平台化、流程标准化虽然使研发更加简单、高效、可靠,但对工程师屏蔽了背后的技术细节,某种程度上对初入职场的服务端开发工程师是不利的,应用部署不应该成为一个“黑盒”。鉴于此,在本节中,笔者将着重介绍一下互联网企业生产环境的应用部署。应用部署的发展历程大致可分为物理机部署、虚拟机部署和容器化部署3个阶段。

1.物理机部署

物理机部署,顾名思义,就是将应用直接部署在物理机器上,如图1-9所示。

在早期,物理机部署几乎是部署应用的唯一方式,这种部署方式存在一些弊端。

(1)硬件资源浪费

图1-9 物理机部署应用

当时服务器普遍采用高性能计算机,造价高昂。如果一台物理机只部署一个应用,则硬件资源难以被充分利用,造成资源浪费。此外,在部署异构系统(不同架构的两个或多个系统之间通常无法直接通信,也不能部署在同类服务器上)时须重新采购物理资源。

(2)进程间资源抢占

为了充分利用服务器资源,一种方案是将多个应用进程、数据库、缓存进程等都部署在同一台物理机上。这种部署方案固然能高效地利用昂贵的物理机,但有一个显著的缺点是进程间资源抢占。例如,如果某个进程占用了100%的CPU资源,那么其他进程将无法提供服务。

2.虚拟机部署

物理机部署存在进程间资源抢占问题,其根本解决方案是实现进程间硬件资源隔离。虚拟机技术的出现使得这一问题得到了很好的解决。虚拟机技术的本质是硬件虚拟化,即每台虚拟机事先从物理机分配好CPU核数、内存、磁盘等资源,每台虚拟机通常只部署一个应用,不同的进程在不同的虚拟机上运行,从而解决了进程间资源隔离的问题。如图1-10所示,一台物理机会部署多个虚拟机,物理机上的所有虚拟机则依靠虚拟机管理系统进行管理。

图1-10 虚拟机部署

虚拟机的出现使得用户在一台物理机上能够独立运行多个相互隔离的系统,通过对资源的抽象化使得主机资源能够被有效复用,这对于企业降低成本十分有益。然而,虚拟机也会带来一些问题。

(1)额外开销

虚拟机部署的主要目的之一是减少物理服务器的数量,但它通常会导致采用更多的虚拟服务器。随着虚拟机数量的增加,大量独立系统开始运行,从而带来许多额外开销。IT生态系统中的其他组件(如存储和网络)也将受到新增容量的影响。此外,每当运行新的虚拟机时,都需要重新配置一遍环境,与在物理机上的情况基本无差异。重复的环境配置操作则会消耗开发和运维人员的工作时间。

(2)资源争用

基于虚拟机管理系统,虽然可以对每个虚拟服务器进行资源分配调整,但如果其中一个虚拟机负担过重,则仍然会影响运行在同一物理服务器上的其他虚拟机。例如对CPU周期、内存和带宽等资源的争用可能会严重影响系统响应。即使有足够的资源,某些工作负载在虚拟机上的性能也可能不如在专用硬件服务器上运行时那么好。

(3)版本冲突

大应用集群的虚拟机第一次安装时基本可以保障软件的版本和库依赖统一。但随着时间的推移,开源软件需要逐步升级,这个过程中,批量升级可能出现遗漏或升级失败。此外,工程师可能会登录服务器修改软件的版本或配置,以满足特定的需求。一段时间后,一个应用集群的虚拟机的软件版本和配置就可能出现差异,导致线上故障。

3.容器化部署

为了解决虚拟机部署的不足,容器技术应运而生。容器化部署的本质是构建一个完整、独立的运行环境,包含3个关键因素:环境隔离、资源控制和文件系统。2013年发布的Docker便是容器化部署的佼佼者,目前已成为首屈一指的容器平台。它能提供轻量的虚拟化和一致性环境,允许将应用及其依赖的运行环境打包在一起,打包好的“集装箱”(镜像)能够被分发到任何节点上执行,无须再进行配置环境的部署。如此一来就解决了开发和部署应用时环境配置的问题,规范了应用交付和部署,降低了部署测试的复杂度以及开发运维的耦合度,极大提升了容器移植的便利性,便于构建自动化的部署交付流程。

容器和虚拟机都是资源虚拟化发展的产物,但二者在架构上又有区别。虚拟机通过虚拟机管理系统(如Hypervisor)虚拟化主机硬件资源,然后构建客户机操作系统,由宿主机进行程序管理。容器则直接运行于主机内核中,如图1-11所示,应用在主操作系统的用户空间上执行独立任务,不需要从操作系统开始构建环境,赋予了应用从交付到部署再到运维的独立性。

图1-11 容器化部署

相较于虚拟机部署,容器化部署有以下优点。

(1)资源隔离

容器的本质是宿主机上的一个进程。Docker通过namespace实现了资源隔离,通过cgroups实现了资源限制。其中cgroups是Control Groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如CPU、内存、I/O等)的机制。通过对资源进行隔离与限制,容器可以精确地为应用分配CPU、内存等资源,保证了应用间不会相互影响。

(2)高资源利用

相比于虚拟机,Docker不但启动速度更快,所需的计算开销更小,显著提高了服务器的效率,而且迁移时更加轻量,得益于分层文件系统,开发者共享代码更方便、快捷。容器没有管理程序的额外开销,它与底层共享操作系统,性能更加优异,系统资源负载更低,相比于传统虚拟机,在同等条件下可以运行更多的应用实例,而且可以更充分地利用系统资源。

(3)高研发效率

容器化部署将应用相关的代码与运行所需的全部环境、配置文件、依赖关系和库等打包在一起。基于容器提供的环境一致性和标准化,一旦出现故障,系统可以快速回滚。相比于虚拟机镜像,容器的压缩和备份速度更快,镜像可像普通进程一样快速启动。此外,工程师可以在一台机器上启动数百个相互隔离的容器,模拟现实场景进行测试。同时使用容器化部署,可以很方便地针对同一个项目生成多套不同的构建环境。

(4)版本控制

在应用集群部署时,每台机器首先会拉取指定版本的镜像文件,由于所有机器的镜像文件一样,因此容器的软件版本相同。即使开发或运维中途修改了容器的软件版本,但当容器销毁时,软件的改动也会随容器的销毁一起湮灭。当应用用已有的镜像文件重新部署时,生成的容器与修改之前的容器完全一样。容器如果要升级软件版本,那就修改镜像文件,这样部署时集群内所有的机器重新拉取新的镜像,软件也跟着一起升级。软件版本混乱的问题就得到了完美的解决。

(5)微服务架构基石

容器技术有助于将一个复杂的巨型应用拆分成一系列可以组合的松耦合服务。每个容器都可以被看作一个不同的微服务,可以独立升级,而不需要考虑它们的同步。基于微服务架构,工程师可专注于其所负责的微服务,而无须关注整个项目,降低了复杂度。与此同时,拆分开来的服务可重用,有助于提升研发效率,降低成本。此外,Docker支持多种编程语言,技术栈灵活,相对独立的微服务还可按需扩缩容、多节点部署。

(6)可移植与易管理

由于镜像不依赖主机的操作系统,因此具备良好的可移植性和跨平台运行能力。在迁移的时候,无须重复安装环境依赖。目前,越来越多的云平台开始支持容器,使得应用程序在公有云、私有云中进行混合部署成为可能。借助于容器编排平台如Kubernetes,容器化工作负载和服务的安装、更新、调试、扩展、监控与日志等流程都可以实现自动化。

1.2.8 运维监控

运维监控对应用稳定运行、业务效果感知十分重要。日常运维监控一方面有助于预警、定位问题,从而令问题得以快速解决,避免影响扩大化,另一方面还可以帮助研发人员洞悉业务效果和发展趋势,实现业务决策。

在互联网企业,对于一个上线运行的产品,运维监控在其整个生命周期将一直存在。当产品用户规模较小或业务场景较少时,运维监控通常由产品和研发兼任,甚至完全由研发负责;当产品的用户量增长到一定规模或业务场景增加时,运维监控通常会逐步独立出来,由专职人员负责。这类专职人员通常称为SRE(Site Reliability Engineer,网站可靠性工程师)。对于服务端开发工程师,在运维监控方面,一般需要关注以下几个方面。

(1)基础监控

基础监控即监控服务端应用部署的机器(通常是服务器集群)的系统指标,如CPU、负载、硬盘、内存、网络等。监控这些指标并设置预警阈值,可以帮助工程师直观地了解所负责应用系统的运行状态,及时发现瓶颈并解决。

(2)服务监控

服务监控即对核心服务进行监控,重点指标包括平均耗时、成功率、调用来源、错误码、调用量等。有了这些指标,一旦出现问题(如成功率下跌、耗时上涨等),工程师可以快速将问题排查范围缩小到服务(接口)维度,此外还可以指导技术优化方向(如减少耗时、提高成功率等)。

(3)业务监控

业务监控即监控业务相关流程的关键节点(如商品查看、下单、退单等)和指标(如下单成功率、订单总量等)。这类指标既有利于我们跟踪业务发展趋势,也有助于我们对业务进行相关分析并做出决策。

在实践中,如何开展运维监控呢?基础监控(CPU、负载、硬盘、内存、网络等)一般可借助专业的监控工具(如Anturis、SeaLion、Icinga、Munin等),并非服务端开发工程师专长。事实上,对于服务端开发,最重要的依托是日志,如图1-12所示。基于规范的日志可以快速构建、感知常用服务指标。站在业务角度,合理打印日志不仅能辅助定位问题原因,还可以多维度聚合分析各类场景。某种程度上,排查问题的过程,其本质就是日志结构化还原的过程。如果日志能结构化还原特定场景,那么该场景就能快速被定位。

图1-12 基于日志监控可感知的信息