日本最大菜谱网站Cookpad微服务经验总结

作者:吉川

来源:Cookpad公司技术博客

Cookpad是日本最大的在线菜谱分享公司,上市企业;这都不算啥,说到Rails,日本人没有不知道Cookpad的,可以说Cookpad将Rails用到了极致,相信他们在微服务方面的经验也能为我们带来帮助。


大家好,我是Cookpad技术部的吉川。

最近微服务这个概念很流行,其技术也逐渐变得系统化、结构化。另一方面就是关于微服务概念上或者抽象的内容比较多,很难让人对微服务有一个具体的印象。

Cookpad公司的技术博客在1年半之前已经对 Cookpad和微服务 进行了介绍。我们也将当时公司内部使用的工具Garage进行了 开源 ,现在Cookpad的系统已经比当时有了更大的发展。

所以,在前几天的 Microservices Casual Talks 上,我们也对Cookpad最近关于微服务的使用经验进行了分享。

本文并不打算对微服务一些抽象难懂的概念进行解说,而是更专注于具体实例。本文应该会对那些理解一些微服务的基本概念,而实际中又不知道如何应用的人有很大帮助,因此我们决定通过本博客再来进行一下介绍。

服务粒度

大家常说微服务就是职责单一的服务,但是单一职责又需要具体到什么地步才算单一呢?Cookpad的服务主要分为3种类型。

用户服务

根据产品关注点的分类。一个根本原则是微服务需要根据业务领域来划分。Cookpad提供了包括美味料理、料理教室、料理视频、Cookpad博客、大家的咖啡等在内的众多服务。

尽管大多数服务都采用了Rails进行开发(也有一部分服务只有手机客户端),但是每个团队的特点都不一样。比如ES6的话既使用了React.js,也使用了CoffeeScript;有的团队使用了Rubocop,有的则没有使用。

由于产品的不同,目标用户也不太一样,因此我们可以很灵活地选择所使用的技术。如果能够很方便地使用其他Domain的model,则很容易产生协同效应。换句话说,即使最终用户看起来服务千变万化,但是从公司内部的其他服务来看,使用的是一个统一的Model。

View服务

Domain model相同但是展现形式不一样。


从形式上很像 BFF(Backend for frontends) 模式,但是由于Cookpad需要兼容各种各样的设备,因此同一个domain model需要以不同版本(这里的版本不是指alpha、beta这样的版本号,而是类似个人版、商业版这样的概念 – 译者注)的形式来提供。OEM版就是一个例子,提供的东西是一样的,但是却以不同版本的UI或者用户系统来提供服务。

共通基础服务

指的是为各种服务提供功能的服务。比如下面这些都是共通服务:

  • OAuth provider

  • 结算

  • Push通知、邮件服务

  • 活动

  • 各种日志

  • 敏感(个人)信息存储

  • 视频

服务之间的集成

RESTful Hypermedia API

微服务之间如何通信一直以来都是一个值得讨论的话题。虽然我们认为一般都会通过HTTP使用REST API,但是应该也有人会选择Protocol Buffers或者Thrift这样基于RPC的方式。

Cookpad微服务之间的通信采用了 基于Garage的RESTful Hypermedia API 方案。在之前的博客中我们已经介绍过Garage了,这里就略过了。在这篇博客中,我们会对一些Garage解决不了的问题进行介绍。

并行处理和容错

服务之间的集成越来越多,带来了两个问题。第一个问题是性能开销。一体化应用的话只需要连接到数据库把数据取回来就可以了,但是如果只是将这个功能采用REST API来实现的话,性能开销会变大。为了提高吞吐量,需要做很多努力,就微服务架构来说,调用方在发出请求之后,将会进入单纯的IO等待状态,非常适合采用并行处理的方式。因此对服务进行合适粒度的拆分,各服务的责任更独立,更容易采取并行处理的方式。

第二个问题是容错。如果某一服务阻塞了,等待该服务超时的客户端也会被阻塞,进而导致阻塞的连锁反应。好不容易将服务拆分了,当然不想轻易地出现这样的故障影响正常运行。

或者将请求并行来处理,或者进行重试,在超过一定数量的错误发生后停止对该服务的请求。我们采用Expeditor 实现了该需求。熟悉这些需求的人可能已经想到了,这就是 Netflix/Hystrix 的Ruby版。当然现在还没有Hystrix那么多功能,但是一些基本功能,比如有依赖关系的Asynchronous Execution和Fallback、Retry、Circuit Breaker都已经实现了。

服务间的测试

REST API集成的问题之一就是API的兼容性。

通常在CI能够发挥作用的测试级别的测试中,发给其他服务的请求都是stub的形式,不能确认是否有不兼容该服务接口的变动混入。使用Protocol Buffers这样方式的话,由于需要共享proto文件,可能不会出什么问题。而使用REST API的方式进行集成的话,也有一种使用JSON Schema的方法。不过由于是Ruby + HTTP风格的文化,相比使用类型系统进行管理的开发风格,我们的工程师们更习惯于进行动态测试这种风格。这也是 Rack::VCR 产生的背景。

但是使用各服务自己的CI来保证兼容性的话,如果服务之间的构建频率不一致,在Cookpad这样发布非常频繁的公司,很可能会出现在发布之后才能发现API不兼容的问题(即发布之后API提供方的API才检测到调用方的调用有问题 – 译者注)。因此我们决定转向 Consumer-Driven Contract testing ,现在已经迁移到Pact了。


这样,如果客户端破坏了API的兼容性,会被提供方(provider)的CI会检测到,从而防止不兼容的修改被发布。

服务间日志的事务关联

服务分开之后,日志追踪会变得很困难。特别是错误日志,当一个服务发生错误时,在调用方发现了错误的话,如果在被调用方不能追查到发生了什么错误的话,就很难找到问题的根本原因。一般的解决方式是为请求设置一个ID,以此来对各服务中的调用进行分析。特别是Rails默认就支持X-Request-Id HTTP头,没有ID的话也会自动设置一个。而且我们的各服务通过使用 GarageClient 进行集成,GarageClient会在发起请求时设置X-Request-Id并将这个ID传递下去。

Cookpad错误日志管理采用了 Sentry ,错误日志通过将请求ID作为标签来进行管理。

服务的运行环境、配置关联

随着服务的增多,给基础设施也带来了很大的压力。Cookpad本来使用的配置管理工具是Puppet,随着模块的增多,操作也变得复杂,同样的应用程序,有时还得区分是作为在线应用服务使用还是作为批处理作业使用来分开管理,总之会出现各种复杂的场景。

现在除了一部分巨型Rails应用程序以外,包括测试环境和生产环境在内,Cookpad几乎所有的应用都跑在Docker中。

构建pipeline

各服务的修改合并代码之后,就会启动CI,测试通过后构建新的Docker镜像。这个新的Docker镜像会自动部署到staging环境中。同时生产环境使用的也是这个镜像,而且没有为批处理作业和在线服务准备不同的环境,只需要拉取最新的镜像运行即可。当然这个镜像也可以在开发者本地运行。在Scale out或者进行渗透测试需要创建独立的运行环境的时候,采用Docker可以快速部署。

部署

Cookpad的Docker运行环境是ECS,实际部署的时候会将容器和ELB结合起来一起配置、注入环境变量等操作。Hako 就是一个这样能将部署过程中各种步骤连接起来的一个工具。有了Hako,在第一次部署的时候可以自动创建ELB,使用Route53设置域名指向,而以前这些工作都需要基础设施工程师单独一个个的去设置。利用这种机制,在生产环境下,一个应用程序对应一个ELB,在测试环境下,一个ELB则可以对应多个应用程序,进行非常灵活的设置。

配置管理

即使是同一个镜像,DB连接信息、API key等也不同,都依赖于具体的环境。Hako支持环境变量注入功能,这在一定程度上可以进行环境变量管理工作,但是需要安全处理的信息则使用了 etcenv 和 etcvault 来进行管理。后端存储是etcd,不过由于etcd没有ACL管理功能,因此数据都采用了加密后存储的方式。etcenv 有一个UI工具 etcweb ,可以通过Web进行管理。

公司级别的支持

采用了微服务架构,团队也随着服务而独立。不同团队之间的协作方法,全体的决策方式等也逐渐发生着变化。

技术领域课题共有会

各团队的独立性越高,对其他不同团队的具体工作内容就越难以把握。各自业务固有的烦恼也会增加。由此会出现很多问题,比如有一些共通难题,如果团队内部认为只有自己才有这个烦恼的话,就不会将这个问题提到公共层面,而实际上隔壁的团队也许早已经解决了这个问题。因此Cookpad各团队的leader和基础设施、共通服务的工程师每个月都会聚到一起通过会议的方式来讨论一些共通话题。

在这个会议上各个团队会提出自己目前遇到的问题,如果一些问题已经有了解决方案,则将解决方案在公司范围内共享,如果没有解决方案,则会选择一个合适的团队专门负责来解决这个问题。此外对全局都可能带来影响的需求变更也会拿到会上讨论。这样就会发现很多问题,比如有些事自己认为不是问题,可是大家都在为此烦恼;或者自己认为大家都在使用的服务实际上并没有人用,自己却还努力在维护。

共通基础技术工程师

译者注:即公司内部开发公共服务的工程师

随着团队变得多样化,有的团队有擅长共通基础的成员,有的则没有;有的团队新产品较多,所以对公司内部最新的共通基础技术都很了解,而有的团队则对公司的共通基础技术认识不足,甚至还有重复制造轮子的现象。也有一些团队根本没有精力去对共通基础技术进行改善。

所以Cookpad将共通基础团队的成员分配给各个服务,作为各个团队的共通基础技术工程师。一个人对应多个服务,而不是专属于某一服务团队,所以他们可以称为是团队的外部共通基础技术工程师。共通基础技术工程师使用服务团队的协作(Chat)工具,谈论各种话题,有时候也全身心投入开发环境的准备工作中。

总结

本文我们对Cookpad近期在微服务方面的使用心得进行了介绍。Docker带来的可移植性给公司内部的开发风格带来了很大的改变,加速了微服务的落地。同时随着Pact等工具的出现以及公司层面的支持,Cookpad的开发风格在这一年有了显著的进步。

有了微服务各个团队也更能将精力集中在本职工作上。集中精力在产品上,也就是相当于集中精力在用户身上一样。根据产品的不同选择最合适的技术,团队成员对自己的产品负责,可以自己主导产品开发。

这里我们只是对Cookpad中微服务的概要进行了介绍,后面我想可能还会带来对Hako或者Pact等工具的介绍。

原文有一些链接,请点击 阅读原文 查看。

版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。

-END-

日本最大菜谱网站Cookpad微服务经验总结相关推荐

  1. Spotify编目微服务经验

    本文讲的是Spotify编目微服务经验[编者的话]Spotify作为一家大规模采用了微服务架构的公司,运行着成百上千的微服务,如何有效地治理微服务生态系统?本文从微服务编目的角度,给大家带来了一些可供 ...

  2. 某厂:有微服务经验者优先!

    随着 5G.云时代的来临,微服务,由于独立部署.选型灵活.易扩展等特点,在行业内变得备受关注.而容器.轻量级协议,代码管理.新集成方法与工具等技术的成熟发展,更是促使互联网企业纷纷走上微服务改造的道路 ...

  3. 实践出真知:博云微服务经验之避坑指南

    目前每个企业都想做微服务,但如何做好微服务?微服务改造过程中有哪些必须重视的问题?博云通过自己的实践,总结了一些经验之谈.日前InfoQ对博云高级解决方案架构师赵安全就此话题进行了专访,以兹各位对微服 ...

  4. 恕我直言,微服务挺好,但不适合你

    今天这篇文章我们继续说架构师大刘的故事. 故事纯属虚构,别对号入座哈. 前言 大刘日子最近还不错,经常午睡醒来,就继续拿着手机看小说摸鱼.大刘对当前所在的这家公司比较满意.大部分系统已经成熟稳定,用户 ...

  5. 几种常见的微服务架构方案简述——ZeroC IceGrid、Spring Cloud、基于消息队列

    微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果.虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合 ...

  6. 恕我直言,你可能误解了微服务

    刘超,网易云计算首席架构师,有10多年的云计算架构与开发经历,积累了丰富的企业级应用的微服务化,容器化实战经验.刘超将担任今年 5 月份 QCon 全球软件开发大会广州站「微服务实战」专题的出品人,为 ...

  7. 用户在电商网站中购买成功了,那么 TA 在微服务中经历了什么?

    题目:用户在电商网站中购买成功了,那么它在微服务中经历了什么? 当我傻啊,用户在电商网站购买成功,还在微服务中,那肯定就是有一套微服务架构的电商系统. 设计一套电商系统还不简单 简单想象一下,既然是一 ...

  8. 放弃Dubbo,选择最流行的Spring Cloud微服务架构实践与经验总结

    Spring Cloud 在国内中小型公司能用起来吗?从 2016 年初一直到现在,我们在这条路上已经走了一年多. 在使用 Spring Cloud 之前,我们对微服务实践是没有太多的体会和经验的.从 ...

  9. 魔鬼面试官:用户在电商网站中购买成功了,那么它在微服务中经历了什么?...

    点击上方"朱小厮的博客",选择"设为星标" 做积极的人,而不是积极废人 面试的时候,面试官问:用户在电商网站中购买成功了,那么它在微服务中经历了什么?你该如何作 ...

最新文章

  1. 证书格式pfx和cer的区别及转换
  2. html 在weblogic 上编译报错,HTTL在weblogic环境下,JDK版本1.7情况下。出现编译错误。...
  3. 映射文件xxx.hbm.xml下的各元素结构
  4. Go三种方式创建赋值map
  5. 什么是SAP CRM里的Sales Bundle?
  6. 数据可视化|实验三 分析特征内部数据分布于分散状况
  7. 华为android怎样隐藏软件,华为怎么打开隐藏应用功能
  8. conda安装pytorch1.10.1+paddlepaddle-gpu2.2.1+cuda10.2+cudnn7.6.5
  9. 小米8劲敌来了!同是骁龙845,它降价幅度更大
  10. RestExpress response中addHeader 导致stackOverflow
  11. String StringBuffer StringBuilder的异同
  12. url的地址循环怎么写_电子邮件地址怎么写
  13. 保研复习整理——信号与系统
  14. GoldenDict音标乱码
  15. 【EasyUI】如何根据条件控制可编辑表格某字段是否可以编辑;
  16. Ansible Jinjia2 模板
  17. 一分钟内搞定!熊猫杀毒软件研发提升扫描速度的技术
  18. CentOS8下载及设置安装源(最新设置)
  19. 数据结构 | 时间复杂度与空间复杂度
  20. Intel oneAPI Base Toolkit 安装教程(Linux)

热门文章

  1. 电脑键盘equals在哪个位置_常用标点符号和电脑键盘符号英语表示
  2. mysql截取中文字符_mysql 截取中文字符
  3. typeof 关键字的作用
  4. Java 多线程发展简史
  5. 记一次云之家签到抓包
  6. android飞行模式开启wifi,手机在飞行模式下怎么使用WiFi? 飞行模式下开启WiFi的方法图文教程详细介绍[多图]...
  7. 万字总结数据库Redis,值得大家收藏
  8. 8篇报告|马斯克称Chat GPT好得惊人
  9. javaScript使用递归循环遍历多层级对象方法(无限不定数层级)
  10. [附源码]JAVA毕业设计美容院业务管理系统(系统+LW)