作为一家初创公司,构建软件必须具有创新性、吸引力和竞争力。因为市场在不断变化,新的需求也在不断涌现。从软件的角度来看,保持这种优势意味着文档和开发阶段必须尽可能短。当然,保持软件的灵活性也很重要,提供卓越的服务是Algolia的重要目标之一。我们有很多高端用户,搜索功能对业务有非常重要的影响,所以宕机是不能接受的,尤其是黑色星期五等特殊时期。因此,开发人员必须在软件灵活性和创新之间找到适当的平衡点。这两个方面是齐头并进的:使软件具有弹性需要进行详尽的测试,这会耗费大量的精力和时间来进行创新。因此,更好的折衷方案是在生产环境中进行测试。为什么要在生产环境中测试?生产环境测试是将新代码发布到生产环境,直接使用真实的生产数据和流量进行“测试”的过程。这与运行一组全面的测试用例形成对比。这个风险非常高,开发者的第一直觉是肯定不会这么做的。但是随着软件规模的发展,你会发现穷举测试变得越来越不可能。让我们看看Algolia引擎。我们有超过两打查询参数。假设它们都是布尔类型,要运行的案例总数将是一百万,二十个参数,每个参数都有两个可能的值,即2^20种可能的场景。当谈到测试相关工作所消耗的时间时,需要考虑三个方面:编写测试用例的时间维护测试用例的时间运行测试用例的时间编写一百万个测试用例已经是一个惊人的工作量。一旦编写完成,它们就成为项目内容的一部分。就像维护其他源代码一样,维护它们需要付出努力,因此每个软件迭代都需要做更多的事情。假设你的团队足够大,有足够的人力来编写和维护这些测试用例,但是运行它们也需要大量的时间。假设运行每个测试用例只需要10毫秒,则运行全部需要2小时45分钟。无论代码有何更新,验证都需要2小时45分钟。客户购买我们的产品不仅是因为他们目前拥有的功能,还因为未来将要发布的功能。他们希望我们定期发布新功能,以帮助他们成长并变得更容易创新和更有弹性。因此,我们必须提高我们的效率。在开发新功能时,我们只编写简单的用例来验证功能并测试明显的边界条件。为了充分验证功能,我们将采用灰度发布的方式,直接在生产环境中进行测试。这样即使代码有缺陷,也能把对客户的影响控制到尽可能小。这样,我们就可以按时发布新功能。真实示例举一个我们最近重写的核心Algolia功能的具体示例。要对其进行全面测试,需要测试这两个参数与所有可能的Unicode字符(超过一百万)的组合的一百万个案例。加起来超过十亿倍。假设运行一个测试需要10毫秒,那么完成整个测试内容需要11天。我们必须找到更好的解决方案。所以我们放弃了十亿次测试。由于这个原因无法显着减慢我们的发布过程,我们决定在生产中进行测试。我们最大的担忧是对用户可能产生的影响,因此我们定义了部署它必须要做的事情:增量发布过程在良好基础设施上的重试方法主动??问题检测通过我们网站发送到Algolia的所有请求最终都由一个集群提供服务,该集群包括的三个节点。每个节点包含100%的数据,可以独立响应请求,从而提供健壮的部署场景。假设平均服务器可用性为95%,此解决方案可以提供99.987%的可用性。只有所有服务器都宕机了,你的服务才会真正宕机。所以概率是5%x5%x5%,即0.0125%的停机时间。但即使在这样的架构下,软件缺陷仍然可能导致服务中断。因此,我们采用了渐进的灰度发布计划。完成所有新软件发布需要三天时间。这样的部署策略给了我们足够的时间去发现问题。还有一点很重要,我们的客户端API会采用重试策略。如果它恰好将请求发给了有问题的节点,那么在请求失败后,它会自动寻找另一个节点重试,直到获得成功的响应。因此,最终用户并不知道这个问题。当我们部署新版本的突出显示功能时存在标准化问题。我们的目标是将所有文本转换为标准化格式,以便可以轻松比较不同的输入。一般来说,归一化后的内容长度不会比输入的原文长,这也是高亮功能的前提。结果,我们发现一些字符在归一化后长度增加了:字母?(德语)被归一化为“ss”。重写时,我们添加了运行时前提条件检查,以确保规范化后的长度小于或等于原始长度。此代码有效并暴露了问题。当我们将新版本的代码部署到第一个节点时,它立即停止响应规范化后长度会增加的请求。值得庆幸的是,我们的客户端API具有重试功能,因此客户端没有受到影响,也没有用户注意到。在后台,我们的监控系统发出了告警,所以我们立即回滚发布,以保持整个集群的稳定。通过这种方式,我们为自己争取了足够的时间来理解问题并完成代码修复,并进行相应的测试。合适的部署解决方案如果您还想在生产环境中进行测试,则需要三个关键的先决条件:可复制的基础架构;弹性软件;安全部署策略;在这个可复制的基础设施时代,配置可复制的基础设施非常容易:所有云服务提供商都可以在多个虚拟机前面提供负载平衡。就我们公司的架构而言,我们在整个集群层面复制了搜索引擎的数据,每个节点都有100%的全量数据。这样每个节点都可以独立完成对请求的响应。弹性软件这部分取决于您构建软件的方式。在Algolia引擎的代码中,有很多关于健康状态的检查,会检查函数的前置条件是否满足,是否处于预期的状态等,当运行到异常状态时,引擎会停止处理以避免返回有问题的数据。它强制API客户端透明地在其他节点上重试。安全部署策略这是最后提到的,但它也非常重要。这里所说的部署策略的主要目标是在逐步发布新版本软件的同时注意控制风险。Algolia的基础设施主要包括四个环境:测试环境、准生产环境、生产环境和安全环境。每个环境都有不同的SLA:测试环境仅包含供内部使用的集群。如果发生故障,只有公司内部的员工受到影响。暂存环境包含外部公共用户项目的搜索集群。生产环境包含我们客户的集群。安全环境包含我们最重要的SLA客户的集群。根据部署策略,我们通过使用多个不同的环境来降低风险。也就是说,我们会先部署在对客户影响最小的集群上。除了环境不同,我们还利用三副本的特点,制定了如下12步部署流程:首先部署到测试环境的所有三个节点;部署到准生产环境的第一个节点,然后部署到生产环境的第一个节点,再部署到安全环境的第一个节点;观察一天;部署到准生产环境的第二个节点,再部署到生产环境的第二个节点,再部署到安全环境的第二个节点;再观察一天;部署到准生产环境的第三个节点,再部署到生产环境的第三个节点,再部署到安全环境的第三个节点;这一步可以帮助我们发现分布式系统内部处理集群节点间交互的代码问题。接下来逐节点部署可以帮助我们确认集群是否可以同时支持两个不同的版本,以及代码是否足够稳定。为什么在部署节点的过程中需要观察一天两次?因为这让我们有充足的时间去发现性能问题,数据问题,或者需要长时间运行才能发现的问题。至此,我们已经解决了大部分问题。以下部署步骤只是帮助我们找到一些可能的未知错误。每当发现新问题,我们都会立即回滚新版本的代码。这样我们提供的服务就可以恢复到一个稳定的状态,我们也可以有充足的时间去修复问题,增加相应的测试用例。使用这种方法,我们的测试用例集就是客户真实的使用场景。这样效率非常高,而且我们每周都可以发布新版本来满足客户的需求。尽管我们的代码库很大,但我们还是这样做了。胜于完美创业公司的生态系统很艰难。小团队必须找到比大公司的大团队更有效的方法来构建更好的产品。定期发布新功能,其重要性不亚于拥有一款满足用户需求且用户体验良好的稳定产品。选择做足够多的测试还是选择有足够的测试覆盖率并且可以定期发布?对效率的需求迫使我们在两者之间找到一个平衡点。添加新的测试用例时必须非常小心,因为这会花费太多时间:编写、维护和运行都需要时间。那么你知道什么时候写测试吗?90-90规则适用于此:测试90%的功能简单直接,测试剩余的10%需要相同的时间。所以根据客户的使用情况来解决这剩下的10%,而不是追求完全覆盖是非常重要的。为降低风险,请花时间设计软件和基础架构,以便它们可以支持在生产环境中进行测试,同时对客户的影响最小。
