> “在所有的 System Design 里面,Surprise 都不是一个 Good Thing,Surprise 都是 Bad Thing…… 成功的一个 Engineer,设计的目标是减少 Surprise,是非常悲观的。Engineer 设计应该非常悲观,尽量考虑一些 Worst Case Scenario。”
```mermaid
flowchart LR
accTitle: Schema on Write vs Schema on Read
accDescr: Comparison between Schema-on-Write rejected early vs Schema-on-Read failing late with surprise.
subgraph Relational["Schema-on-Write (RDBMS)"]
W1["写入数据"] --> C1{"验证 Schema"}
C1 -->|不合规| R1["立即 Reject"]
C1 -->|合规| D1[("数据库")]
D1 --> Read1["读取:平稳无惊吓 (No Surprise)"]
end
subgraph Mongo["Schema-on-Read (NoSQL)"]
W2["写入数据"] --> D2[("直接入库 (Smooth)")]
D2 --> Read2["读取:需大量条件分支判空"]
Read2 -.-> S2["线上异常 (Big Surprise!)"]
end
```
为了在如此混乱的环境下维持系统的稳定运行,必须严格遵守两个核心兼容性定义:
- **Backward Compatibility(向后兼容)**:新代码能够正确读取由旧代码写出的旧数据(New code can read old data)。
- **Forward Compatibility(向前兼容)**:旧代码能够正确读取由新代码写出的新数据(Old code can read new data)。这要求工程师在最初写旧代码时就必须具有前瞻性,能够优雅地忽略或容忍未知的新字段,其实现难度往往远高于向后兼容。
```mermaid
flowchart LR
accTitle: In-Memory to Wire Encoding
accDescr: Process of transforming in-memory pointer based data structures into network transportable formats.
subgraph NodeA["Node A (发送端)"]
Mem["内存数据结构<br>(Heap/Pointers/Objects)"] -->|Encode / Serialize| Wire["网络传输格式<br>(JSON / Binary Stream)"]
end
Wire -->|TCP / Network| NodeB
subgraph NodeB["Node B (接收端)"]
NodeB_Wire["网络传输格式"] -->|Decode / Deserialize| NodeB_Mem["重建内存对象<br>(Language Objects)"]
end
```
```mermaid
flowchart LR
accTitle: RPC IDL Code Generation and Wire Protocol
accDescr: Shows how Thrift or Protobuf IDL files are compiled into different languages and talk across the wire using binary protocols.
subgraph Def["Schema Definition"]
IDL["IDL File\n.thrift /.proto"]
end
subgraph ClientSide["Client (e.g. Go)"]
CG["Generated Go Code"]
CD["Go Struct Data"]
end
subgraph Wire["Network Transfer"]
BP["Binary / Compact Protocol\n(Packed Bytes)"]
end
subgraph ServerSide["Server (e.g. Java)"]
SG["Generated Java Code"]
SD["Java Object / Data"]
end
IDL --> CG
IDL --> SG
CD --> CG
CG --> BP
BP --> SG
SG --> SD
```
这种机制保证了客户端和服务端能够 "speak the same language"(使用相同的语言交流)。即便客户端是用 Go 写的,服务端是用 Java 写的,Go 端生成的 Thrift 代码会把内存中的结构体塞进 Binary Protocol 或 Compact Protocol 中压缩成一串纯粹的二进制字节流发往远端;Java 服务端接收到这串字节流后再解码成 Java 的数据结构。在 Thrift 的二进制编码下,编码器直接去除了冗余的字段名字,取而代之的是用紧凑的数值标记(Field Tag)与类型(Field Type)来定位字段,例如用 001 标记第一个字段,后面附带长度与真实数据。
---
[29:45](https://www.youtube.com/watch?v=9R-R8ePV22s&t=1785s) Thrift Binary Protocol 的结构瓶颈与 Tag 坟墓法则
在这种编码体系下,真实的数据内容本身(例如具体的字符串 `"Martin"`、`"daydreaming"`、`"hiking"`)是绝对不可能被凭空省掉的;能优化的只有包裹它们的元数据(Metadata)。与此同时,既然网络数据包中已经完全抹掉了字段名,客户端与服务端之所以能正确理解彼此,完全仰仗于每个字段头部的 Field Tag。如果 Tag 是 1,双方都通过本地编译好的代码查表,认定 1 代表 `username`。
subgraph Approaches["Shared-Nothing 架构下的数据流动途径"]
direction LR
A["1. 共享数据库<br/>(Database)"]
B["2. 远程调用<br/>(REST API / RPC)"]
C["3. 异步消息<br/>(Message Passing)"]
end
A -.->|高风险/反模式| E["多服务直连共享 DB<br/>易致数据污染与丢失"]
B -->|最普遍/解耦| F["契约隔离<br/>独立演进部署"]
C -->|解耦/削峰| G["事件驱动<br/>异步解耦缓冲"]
```
通过共享数据库来流转数据被普遍视作一种架构反模式(Anti-pattern),在实际生产中极易产生问题。当两个独立的微服务(如 Service 1 与 Service 2)同时访问并修改底层的同一个数据库甚至同一张数据表时,两者所承载的业务逻辑假定与 ORM(对象关系映射)框架往往并不一致。例如,Service 1 在向支付表写入数据时,为了规范展示,通过自身业务逻辑将信用卡号每四位切分并添加连字符(Dash);而 Service 2 在读取或写入时并没有这种假设与处理,读出的数据格式便完全混乱损坏(Corrupted)。
## Notable quotes
> [05:00](https://www.youtube.com/watch?v=9R-R8ePV22s&t=300s) 在所有的 System Design 里面,Surprise 都不是一个 Good Thing,Surprise 都是 Bad Thing…… 成功的一个 Engineer,设计的目标是减少 Surprise,是非常悲观的。Engineer 设计应该非常悲观,尽量考虑一些 Worst Case Scenario。 — Scott Shi
> [05:00](https://www.youtube.com/watch?v=9R-R8ePV22s&t=300s) Data Outlives Code — Scott Shi
> [29:45](https://www.youtube.com/watch?v=9R-R8ePV22s&t=1785s) 为了表示 Lens 是 6,它整个这个数量都比这个 6 大了 — Scott Shi
> [29:45](https://www.youtube.com/watch?v=9R-R8ePV22s&t=1785s) 变成一个 tomb 了,就变成一个坟墓了,没人敢用这个 2 了 — Scott Shi
> [36:50](https://www.youtube.com/watch?v=9R-R8ePV22s&t=2210s) 我自己至少见过两三次了,就是这个一行 code 把系统 breakdown 了 — Scott Shi
> [01:00:00](https://www.youtube.com/watch?v=9R-R8ePV22s&t=3600s) 永远是 Data 大于一切,Data 永远比 Code 重要 — Scott Shi
> [01:14:00](https://www.youtube.com/watch?v=9R-R8ePV22s&t=4440s) 程序员永远不会丢饭碗 — Scott Shi
## Video description
Please subscribe to my channel!
in this session, Scott covered the session of encoding(protobuf, thrift or avro) and evolution
doc: https://docs.google.com/presentation/d/1D0OTKXtYvOxzGR0qBnStKWpOSxNGu1CzZ9fAfhnWOes/edit?usp=sharing
[5:00] Scott Shi: 在所有的 System Design 里面,Surprise 都不是一个 Good Thing,Surprise 都是 Bad Thing…… 成功的一个 Engineer,设计的目标是减少 Surprise,是非常悲观的。Engineer 设计应该非常悲观,尽量考虑一些 Worst Case Scenario。
[5:00] Scott Shi: Data Outlives Code
[29:45] Scott Shi: 为了表示 Lens 是 6,它整个这个数量都比这个 6 大了
[29:45] Scott Shi: 变成一个 tomb 了,就变成一个坟墓了,没人敢用这个 2 了
[36:50] Scott Shi: 我自己至少见过两三次了,就是这个一行 code 把系统 breakdown 了
[60:00] Scott Shi: 永远是 Data 大于一切,Data 永远比 Code 重要
[74:00] Scott Shi: 程序员永远不会丢饭碗
Original transcript
[0:00] 今天讲那个encoding and evolution这一张呢就是这是DDIA的那个Design Data Intensive Application第四张然后这张其实很多时候就是很多部分可以跳过的但是这张其实还是挺重要的一张因为它这个就是像我们Safety组前两天前前期的时间就有一个engineer一个new class然后就改了一行code然后就broke了很长的一段时间代码这是一个真实的例子然后就是和这块这个evolution就是schemaevolution这块没有理解和比较相关如果你用那个就是RPC的话这块作为一个new grids来说是最容易blow up的地方就是最容易new grids或者是比较没有经验的话其实不一定是new grids如果你是对RPC比较新的话也非常容易blow up的地方这个是基本上new grids都是有非常惨痛的教训的在这块常见的一个比较容易出事的地方,就是工作中。然后这张主要也是讲了两大部分吧,一大部分是这个encoding,encoding也叫Serialization和Desserialization,它其实是一种data transfer 的一种方式,你Data在Memory里面你是有Data structure,各种各样Data structure在Memory里面然后你是,比如说加Memore 设定里面的话,你有GVM里面,你有HIP,有Stack管理的那个层,然后你class就是incapture起来,然后你这样子但是你如果比如说你在两个不同的机器,像我们这话说的讨论都是Internet都是在intelign,share nothing architecture在你需要从一台node传到另一台node的时候那你就需要有一个encoding然后或者叫serialization把这个data能传过去所以是serialization和desserialization是一定要做的所以这块的话就很容易出事的地方常见的比如说java的话像python的话不是有个plpico你可以就pico dump然后pico load然后就可以做这个serialization和desserialization就是Serialization和Desserialization这块然后另一块的话讲了一下DataFlow几种不同的DataFlow的形式然后还是非常有趣的所以我们这块也会看到然后这块的话我们会讲一些REST API和SOAP的区别然后也讲一下Micro Service listProtobuf和这个Protobuf和Shift好,然后在我们这个Code中,我觉得有一点和就是不同的地方,比如说你在做学术的时候,做student做Project的时候有一点不同的是,你在做Project的时候,在学术的时候,一般只有一个VersionCode你比如说你在写一个version code然后它不work的话或者improve的话就有一个version code但是在所有的工作情况下是不一样的在工作情况下往往有多个version code你比如说你现在是在production中可能有多个version你可能是version 1,version 2并且在任何一个时间段在任何一个时间段你可能有多个version并行的在run假设你在有version 1假设你在有version 1然后你要更新你的code现在的话,现在所有的distribute system都讲究的是0 downtime,你的availability一定要高所以如果你想把version 1 upgrade到version 2的话version 1 upgrade到version 2的话,你不会是一下就整个把version 1全部bring offline,只有银行会这么干不会把所有version 1bring offline,然后再deploy,全部deploy version 2它现在大部分都是用一些比如说用kubernetes这种就是你update是incremental update就是比如说你version 1有10台machine你先bring down一台然后把9台变成version 1,1台变成version 2是吧然后在任何一个时间段你可能有多个version同时在run的所以说这个这种情况下的话你其实是在任何情况下你的任何一个service在production里面可能有多个version这个事情会变得很复杂如果你是像做Micro Service的话如果你做Micro Service的话你其实是有多个Service然后每个Service有多个Version那多个Version之间相互交互的时候你的Assumption可能就不一样了所以Assumption不一样就很容易出生Bug所以这也是为什么做Micro Service的时候你Service Level的Agreement一定要做得非常好就是说你这个Agreement什么是允许的什么是不允许的你整个这个Doc要做得非常好就是这样你跟其他组合作的时候它会有非常好的合作的一个pattern然后如果出问题的话你也可以说这个我们这个是这个agreement是哪一个组的fault是吧这个是非常重要的然后其次的话这个code是一方面如果做的经验多的话就是我觉得New Glass是非常在乎code的但是做的经验多是不在乎code是非常在乎data的
[5:00] 谢谢data是远远重于code的,为什么呢?因为有几种常见的方式,你的data一般都是outleave code的你的code可能在任何一个时间段只有version 1和version 2两个version,version 1是old version,version 2是新的version这是你的code 是吧,任何情况下就两个version,就是旧的version和新的version code,但是data的话,你data可能是有5年前输入的data,10年前输入的data和刚才输入的data每个data的format如果你没有专门整理过,你没有做data migration的话,没有把它统一整理过的话都是不一样的。所以这个data呢Version data是远远大于code的,任何时候的code产生了data,任何一个version产生了data,都会leave在那里。所以经常有一个常见的说法就是data outleave code。Data outlive code,这个也会反复提起,在今天的这个speak中也会反复提起。所以说做的经验多的话,就是Data往往是最重要的,比Code越远重要。所以这个,不管你是用什么algorithm实践的,但是如果你API这个实践好的话呢,你不管是algorithm A,algorithm B,它只有performance的不同。但是你Data,你五年前的Data,如果和现在Data不compatible的话,那就是很大的问题了,就非常非常大的问题了。和这个code schema version的进化是吧,version是怎么进化的,不同之间的version是怎么样compatible的。这个的话是非常重要的一章,就是它很多这个字里面讲的东西都是就是工作中比较学与类的教训。好的。所以我们看一下relational database,因为它是我们像之前大概两三周前讲过,relational database是schema on write,就是当你写进去的时候,你就根据你的data和relational database的table进行verify,一旦你不满足relational database的schema的话,你的write会被reject,但是所有的这个NoSQL database,比如说Mongo,你是Schema on RAID,就是什么意思?就是你写的时候可以任何Schema,Mongo可以把任何Json写到它的Database里面,只要它那个size,比如说它size没有超过什么16MB,你可以写任何Json。但是你读的时候,你需要有一些intelligence,是吧?你读的时候,你知道它哪些schema有,哪些schema没有。所以这个Mongo这个database,你经常写的时候是很smooth的,但是你读的时候有surprise,是吧?然后,记住,所有的system design里面,surprise都不是一个good thing,surprise都是bad thing,就是,surprise就是,所有的system design engineer,你设计的goal是减少surprise的,就是,成功的一个Engineer,是减少Surprise,是非常悲观的,所以Engineer设计应该非常悲观的,尽量考虑一些worst case scenario,减少Surprise所以你Mongo写进去的时候,你写的时候很开心,你读的时候有big surprise 是吧?但是你如果用那个relational database的话,你写的时候如果出问题的话,写的时候就reject了,但你读的时候很happy,不需要处理很多connor case所以,就是不同的 Schema on Read 和 Schema on Write然后其次的话,这块也是我之前谈的,就是当你在任何情况下的你server side的这个就是internet company,是吧?所有现在热门的都是internet company,热门的company都是internet company,你server side application的话,肯定是有多个version在轮,再结合你的Micro Service,Gbag Service再加几十个version,Gbag Service每个service再加两个version,那这个交互就非常interaction,非常非常复杂了。好好,其次的话其次的话,Client side的这个version就更复杂了,你比如说Apple的话这个还好一点,至少Apple的operating system是version是比较固定的是吧,就是它几个version是相对来说是比较少的,就是iOS,Android的那块就非常fragmented,operating system就非常fragmented,你application的这个version更为复杂。你真的发了三个request,还是我们这个version,这个code的问题,还是什么,确认不错,我们必须把data purge一下,是吧,把data clean up一下,然后我们还得强制这个client的端,update一下version,我知道iOS好像是可以有一个强制update,
[10:00] 就是你必须update以后才可以使用,但是这个其实是大杀器嘛,就是你不希望使用太多是吧然后这个就很难控制,所以Client application version就更多了。我们再反回一下,再想一下这个问题啊。你Client可能有几十个version,iOS有十几个version,你Android有几十个version application。然后靠到你这个server段,你server段有几百个micro service然后每个Micro Service的Cell多个version共同存在,你就可以看到,其实每个人使用的时候,他的Experience都是不一样的。这个又 depends on 它到底靠哪一个client version 是吧然后再看到哪一个server version然后这个server version又和另一个server的哪一个version进行进行交互了,所以这个Experience是非常不一样的。很多时候这个很容易出出bag,经常有人抱怨UberEats这个APP出bag,那你实际上它这个,你到底是哪一个version出bag,你的version出bag version,可能跟别人出bag version不一样,你简单相乘一下就有几百种可能。基本上千种可能的就是即使你用一个很简单的feature简单相当下基本上千种可能的哈然后这块我定义了两个非常重要的概念,一个叫backward compatibility就是backward compatibility,就是你写一个新的code,可以读旧的data新的code 可以读旧的data新的code 可以读旧的 data 好然后 forward compatibility 是什么意思呢 是你旧的code可以读旧的data同时也可以读新的data就是你旧的code设计的时候,你有前瞻性是吧,这个forward compatibility更难做,就是你需要有前瞻性,所以你这个旧的code不但能读旧的data,也可以读新的data,就是这两个compatibility的问题。好,然后我们再说这个程序的时候,同时你data大部分是有两种形式,一种是在memory的,比如说你在jvm,jama virtual machine 里面,你可能有HIP 是吧 就是有HIP stack 然后你code 其实是专门存在这个 GBM 有个code section 里面是专门存code 的然后你处理的时候呢 你这个data 实际上可能是 不管是structure, list, array, hash table, trace 这些可能dependent on language 是吧 有的是在stack上 有的是在HIP上 是吧但是这些总体来说呢 它是用pointer 来进行operation的然后它是被optimize过的,如果我们回想一下上周的那个类比是吧,就是这个访问的类比的话,你在memory中取一个数据就相当于就走200公里取一个数据还是很快的,到disk的话你可能就到土星那块了是吧,得走好几天,你如果按旅行者的话得走好几天,就到土星那块起一个出去就很慢了。但是当你想share的这个data和另一个node的时候,因为所有的都是shareNasi architecture,当你想share这个data和另一个node的时候,那么你必须把这个data通过network或者通过其他的方式,send 是吧,那你必须把这个data,in memory data structure,转化成一种可以接受的,可以transfer,从network transfer 的这个一种方式,那么的话你可能就需要,不管是把它变成一种Json, encode成一种Json,或者 encode成一种Binary format,你必须把它encode一下。这种的话,如果在上学的话,大家写的时候也经常见,比如说你这个写了一个python,然后把python这个program,或者把这个program加一些data,然后这个pico dump一下,然后pico load一下,这也是一样的。也是一种 Serialization and Deserialization,它就是两种 Presentation.OK然后它这个有多种叫法,叫Serialization/Desserialization,然后这本书里面叫Coding和Encoding,Coding和Encoding和Decoding,然后这个,像这个,Go里面还有什么叫Marshalling和Marshalling有不同的叫法,但是它其实只是同一个东西,就是说白了是把in-memory的一种Data structure,变成一种可以在这个Network之间传的Text Representation,或者是一种Binary Representation。然后最常见的一种就是language,比如说这个java的serialization,ruby的marshall,然后python的pico这个就是你用的时候非常简单,但是它有很多的问题,你是很难在production中,你是很难在工作中用到这些,你可以在学术中用到这些,可以在工作中用到这些。为什么呢?首先这个encoding呢,是和某个language相保定的,是吧?你如果是用python的pcode的话,你不可能用java的serializeboard来decode。然后其次的话,这个它有security问题,如果因为你作为一个server端,你要run这个的时候,你实际上是把,比如说你serialize,你其实run一个deserialize的code。在你desserilize code里面可以做任何code就像说白了这个问题就很像这个Lead Code,我们前两天讲了Design Lead Code的问题。你别人Send你一个Code,这个Code可以做很多事情。最常见的一个攻击
[15:00] 这个叫decompress bomb这个它把一个data一个data非常小的1k的data传给你然后做decompress然后直接就把你的整个memorymemory和disk直接crash掉了因为它1k的datadecompress可能变成两个t或多个t的data你整个这个系统就crash掉了所以你有security问题因为你要执行一段你不知道是什么的code然后其次的话有version的问题这个是完全没有version的是吧你dump这个data不管dumpdatadump code是完全没有version是完全没有做没法做不同version之间相互coordinate的你要update的系统假设你做这个你用这个作为一个productiondeploy到一个production系统你这个version你怎么样upgrade可能你是要把整个system什么go offline然后花几个小时update跟这个chase啊这种大银行做的一样其实非常awkward不应该有dontime然后最后的话efficiency是非常低的然后这块的话我想额外提一下就是Java像这种language很多这个非常好用的building library其实非常非常有很多坑的比如说这个还有另一个常见的就是Java hashcode你Java hashcode的理论上来说一个hashcode是给一个stringhash完以后的HashStream是仅跟你的那个InputStream相关的,但加上Hashcode不一样加上Hashcode是不reproduceable的,就是你这个ThreadHashcode和另一个ThreadHashcode可能是不一样的所以你其实它不是一个简单的跟这个Hashcode最后产生的HashStream不仅仅和你的Input有关还和你具体实践的时候是在哪一个side执行的和哪一个,我记得哪个side, 还是哪一个cpu执行的相关所以这个就完全没法 reuseable所以这个看起来有些很好用的东西其实不能用完你看hashcode的这个,如果你是为了能reproduce的话就是任何人都不应该用这个hashcode来加完的hashcode好,然后其次的话另一种方式就是另一种方式,这叫language specific encoding and decoding另一种常见的encoding和decoding方式就是用JSON,XML和CSVJSON近几年比较火,多半原因是因为这个JavaScript相对比较火一点所以JSON相对来说比较好用一点,并且像比如说Mongo也非常native的支持JSON,就直接start到database里面所以这个JSON这几年比较火的原因然后但是这几种方式呢,就是不管你把DataDOM成一个JSON还是DOCSV 还是 dump 成一个 XML它是有它的优势比刚才那个要好很多但是它有劣势,比如它劣势什么呢?在 XML 和 CSV 里面你不能区分它到底是一个 number 还是一个 string它可能比如说 15 你是不能区分它的 data type 是 number 还是 string 的然后 JSON 里面是不能区分很大的那个数和浮点数的所以在这个Json里面,Twitter有一个坑,就是Twitter我忘了它有个什么ID的,然后它那个数是非常大的一个数,非常大的一个big number,然后导致这个Json,它incrept那个数的时候,encoding和decoding那个数的时候会产生歧义,所以它区分不了它对应的到底是数A还是数B,所以它必须用另外一个data帮它去encoding,所以这个Json有很多坑,Twitter反正之前采过一种坑。然后其次的话这个JSON还是不管是JSON XML还是CSV它是不支持BinaryStream的如果你想把它变成Binary的话比如说你想把它变成BinaryStream那你能做的事情只能是把BinaryStream先变成Base64Encode一下然后传Base64它的Efficiency会减少很多他们说我记得他提了一个数据说efficiency会减少33%它浪费了一些efficiency其次的话有一些schema support对于Json和XML也不是太好的XML和Json其实设计的时候设计者是加了一个schema support但是没有人用没有人用Json和XMLschema support他其实加了很多很不错的东西但是它中间可能太复杂了,没有人用它比如说还加了一些这个Regular expression什么的,还是挺powerful的但是还是没有人用就是很尴尬的一点,就是你设计的时候你是这么想的,但你的user不一定这么使用然后最后一点的话就是csv其实是有很多counter case的比如说你这个csv是用comma来separate的话那你如果这个value里面content一个comma怎么办你可以skip string你可以skip string来就是skip这个comma但是它还有很多其他的countercase就导致很复杂这个是我是知道的因为这个我当时有个project是parse所有的conversation然后这个data parse出来是csv的然后导致中间有很多countercase非常复杂然后这个csv经常容易被mass up掉mass up掉的话就整个这个row就是你看到这个csv
[20:00] 所以这个肉就卖散不掉了,这个data就非常missaligned有很多 counter case 非常复杂 你要把所有 counter case cover 一遍 其实挺复杂的所以这个CSV 如果你Data Transfer 通过这个JSON,CSV,XML 是可以接受的一种模式大部分公司之间,就是B2B之间,Data sharing的话是通过这个方式来进行share的就是有的是通过API,有API当然好了,就比如说我是一个Uber,是个公司给这个假设给Lift,跟Lift share一些某些Data,那我给Lift一个API那Lift靠这个Uber API就可以拿到Data,这当然是最好的,但是很多时候很多公司之间相互交流的时候没有那么多Engineering,API毕竟authorization, authentication然后这个复杂度还是挺高的,但是你如果做CSV的话就是每个月我download的一遍CSV,每个月我去monstay把这个data,用CSV格式download了下来,然后把data出来发给你,然后你再把它处理一下,然后就可以了。这个其实很常见的,我们insurance,uber的insurance,包括所有的insurance公司都是这么搞的,就是他跟第三方insurance,不管是那个farmers,state farm,还是什么progressive,这种insurance公司,他就是从,我们自己写了一个什么fileserver,然后从这些公司里面把Data以CSV的格式download下来,然后放到我们fileserver上,然后我们很多insurance的accurate,就是那个精算师。还有一些data analyst就把那个CSV处理下来,然后upload到一个Google Excel里面,然后进行一大堆处理,然后运算,然后data validation,然后再把这个Month类的Report的Pause一遍这个其实他们基本上每月花一周时间做这个事情你可以看到这个CSV用的还是挺广泛的除了刚才我们说的一个是Language Encoding,Language就是Native Encoding,CSV和JSON的encoding,还有一个是Banary Encoding这块我们拿了一个Banner Encoding是用了一个Message Pack是一种Banner Encoding的一种方式这块举了一个例子这款像左边的话就是你要encoding的这个Json,还有一个username,有favorite number,还有interest然后我们注意到它encoding的时候,encode从右面这个数,右面这个data,就是binary data注意到这款encoding有一些细节还挺有趣的,它所有的data,比如说username,Username,然后Martin然后这个Ferrith number都是用ASKMA来表示的然后它那个然后它有一些metadata比如说这个object有多少个entread83表示这个object是有三个entread嗯object和三个entray这个8可能是表示object,3可能有三个enteryanyway,反正它就表示object是有三个entery的然后第一个entery是它表示string,然后它lance是8然后第2个就是 然后就是 第1个untray 是吧? 然后它那个K是Stream,然后Lens是8,它Value也是Stream,Lens是6,所以就是UserMartin,然后第2个untray它的K是什么Stream,然后Lens是14然后它value是一个on int是吧onsigned int,然后是16位的,然后是他给了1337,然后第三个的话的key是interest,是个A9是吧,然后他那个value的话是arrays,是两个的array,有两个entery是吧,9表是array二的话表示两个entray然后它是Type还是Stream,A表示TypeStream,然后Lens是B是吧Lens B表示11,16进制的话B表示11,A表示10B是11嘛,然后加上这个K是,就是,就是,就是,Dedreaming和这个hacking是吧然后这个的话实际上看的话,这个incony还是比较明显的,就是先表示多好个object,然后表示有type和lens然后就把它一个一个写过来,这个incondy还是比较明显的。比较简单的,它encoding了以后呢,它这个byte变成66byte,就是Bets减小了,整个数量是比之前的小一点这里面有几个东西我想说一下这个Message Pack和其他的后面几个encoding的不一样第一个不一样是它所有的Field name全部就也encode过来了,比如说usernameFavorite number, Interested,这个是Field name,也Encode过来了这个Fuel Name,如果你 client 端和 server 端 run 是同一个东西的话,你其实可以不 encode比如说第一个column约定书层的就是你client端和server端达层一致第二 column 就是 username 你可以不硬 code 是吧不英课的这个column name然后第二column就是fuel number,那你就不可以不incode这个column内,就没必要传送
[25:00] 我们可以思考一下如果是这个是个request 的非常frequent的endpoint的话,你前面这个全部都是重复的,你这个username,favorite number of interest,你要是 million request per second的话,你每秒钟你这个就duplicate了一个 million time所以这个全部是没必要的,你只要把后面的data传输过就行了。你这个前面这个schema,就是你到底column到底是什么fill name吗?你的FieldType其实没必要传送的,你其实两边client端和server端如果run同样代码的话是没必要去encode这方面的所以就是看了第二个encoding,就是我们Uber之前用过的叫Shift,Shift是有两种encoding的,我们先看它的banner encoding。这个是一个常见的Strift的一个Structure,就是有叫Structure of Person然后它有个username然后有数字数然后有interest,我们看这个就是跟刚才那个是完全一样的,但是它client端和server端,都是swift的这个code,所以它其实generate的时候这个schema是你看端端和色彩端是达成一致的,就是说第一个Field就是UserName,第二个Field就是FieldNumber,然后如果你没有用过Strift或者Protobuf的话,我这块简单的花一分钟解释一下Swift的Protobuf是RPC的一种一种 framework它提供两种东西,high level 说它提供两种,一种是你定义的时候,你定义一个叫IDL的东西,IDL叫哦一定要DataDescriptionLanguage 我忘了idea是什么了反正它提供一种data description language 你只描述你自己的data它是为了 因为RPC 你是为了transfer remote procedure call 你是为了靠remote一个function 靠一个remote function然后你需要把那个parameter 传进去 它可能要把data transfer回来所以你需要在这个shift和protobuf里面,你需要定义的data schema你需要定义Data Schema,然后你用Safe的时候它会帮你generate这个,根据你不同的language, generate一些code你比如说在这块的时候我们可以说这个service叫叫Lookup Person 是吧 你这个Service叫Lookup Person然后用Swift 不管用Swift的Protobuf不管是用thrift和protobuf,你实际上是把这个,你把这个,就是你定义的这个,.proto或者是.srfile把它 compile成,编译成, compile成不同language,比如说你用python的话,它可以compile成python,你用java的话可以compile成generate.java code,你用Going的话可以给你 generate 一大堆Go code但是你的Client端和Server端同时做这件事情也就是说假设我们有个Client有一个Server的话,当你把这个PersonStractorPersonStrike不管在Clend的Server端同时有的话,当你使用Swift的时候,当你使用Swift Compile的时候,它会把他所有支持的语言大概支持五六种语言,可能近十种语言,然后把所有的语言对于每一种语言generate成一个Clend。Client code 就是比如说对于go的话就generate从一个person的点go code generated code,然后你其他的code就可以直接使用它然后把这个这个data就是就是client和server端相互交流我不知道没有说清啊,就是你client端和server端是帮你generate code的,然后generate code之间保证你client的server端是speak same language,这个language就是swift的binary protocol或者swift的那个compact protocol,然后所以你不管你client端用什么code,比如说你client端用的是go,你server端用的是java,那它会把go靠你go generate the script file,然后把你的那个data呢放到这个go,就是把这个data呢放到这个compact,就是把这个data呢放到这个compact,变成一个Binary的Data,然后通过这个Swift Binary Protocol或Swift Compact Protocol然后转到那个server端,然后server端因为你用的是java,所以你decode的时候你decode的时候会把它变成java的一种Java的那种表现,就是Java的那种Data structure,然后读出来,然后用Java的形式使用。然后这个IDL是Interactive Data Language, 我忘了前程是什么了,就是Interactive Data Language。好,然后这边的话,所以这个Binary encoding的时候,其实我们看到一下这个swift其实和刚才那个没有本质的区别。我们看的话就是它唯一的不同就是什么呢?它去掉了这个FieldName,你没有这个FieldName是UserName,还是FieldNumber,还是Interest,但它Encode的时候是一致的它第一个就首先是这个FieldType 比如说是String然后它FieldTag是第几个Field 是吧这块Field1还是Field2然后它是Field1的话就是说是001所以它这个支持Field是大概支持2的4次方 它后面可以extended应该是2^4层次所以他这个还是挺好的
[30:00] 然后它后面还可以extended是吧应该是256个还是挺大的后面还可以extended然后它每个field比如说field1的话它会定义一下它lens是多少然后它这块像这种情况下username.lens.16然后它后面跟上具体的data然后第二个field的话因为它是i64的就是64个的integer然后它给上具体的这个数是什么然后这个第三个field的话它就说的这个是list是吧 type是list然后field type是3然后这个stream,type是stream,然后有多少个list item,是两个list item,然后第一个lens是多少,然后给上daystreaming,第二个lens是7,给上hacking。所以这个里面我们看一下,这个其实它是59个bats,是比刚才66个bats要小的。它省略的,其实大部分省的地方就是这个fuel name,你这个fuel name不管是username,favorite number和interest就省掉了。你增加的地方是type和lens特别它这个lens用起来是非常不高效的你比如说这个lens是6这个lens是6的情况下它这块其实用了几个bit呢用了几个这个应该是几个byte呢用1 2 3 4 5 6 7 8个byte所以这个效率非常低并且它这个一个应该是两个byte因为它是16进制的所以实际上是16个byte这个效率是非常非常低的效率是非常非常低的所以它有进一步优化的空间这是第一点是吧就是它这个比刚才那个好一点的原因是它省略了field name但是它这个明显还是非常低效的因为它这个lance使用的空间太多了它有进一步优化的空间我们注意到这里面我们看到这两个encoding也可以看到有一部分是没办法进一步优化的比如说这个marting比如说这个daydreaming比如说这个hiking这个具体的data是没有办法进一步优化的是吧但是它这个metadata是可以优化的所以这也是它们优化的方向优化这个metadata然后具体的data是没法优化的然后最后一点注意点是你这个data transfer的时候因为你没有transfer这个data field所以怎么样保证client端和server讲的是同一个language呢实际上是根据这个 field tag来的所以这个 field tag是1的时候那我client端假设就是usernameclient端我读啊我是usernameserver端读的也是username是吧所以它是根据user tag来的如果你说我不想换user field了是吧我这个user这个person这个datafield tag是1的时候,我不想是UserName,我想用它是叫Credit Card那你Client端,你发给Server端的时候你Client端以为它是Credit Card,Server端接受的时候以为是UserName,就出现大问题了所以不管是在Shrift,还是在Protobuf里面你这个Field是没法从用的你这个Field是没法从用的假设你这块有个field,你deprecated了,那你也不应该用那你这个field,这个field tag,就是比如说这个r,这个fielit number,觉得以后没有用这个field变成deprecated,那这个field number要放在这里,变成一个tom了,就变成一个坟墓了,没人敢用这个r了因为你如果再用r的话,比如说你在新的version里面把它变成这个不是fielit number了,是变成一个比如说是full number,是吧这两个都是integer,是可以共用的,它之间是没有冲突的但是,你client端以为它是favorite number,但server端以为是full number,这是会出问题的,是吧?特别是你在以后的version里面,你不知道你的client端使用什么version,client端可能几年没有更新过protobuf和sifth的client了,所以它以为是favorite number,但你server端一直assume它是full number,那就出了大问题了,所以在这个protobuf 和 Swift 里面是永远不要用重用 永远不要 reuse tag 的所以你的 tag 是 growing 的如果这个 field deprecated 的话你这个是一直 growing 的问题就是不停在 grow好 我们看到刚才那个叫 Swift Binary Protocol我们现在看一个 compact protocol就是还是一样的 field 还是一样的 field但是我们看到这块进行了几个优化第一个优化就是他把刚才的16个bytes就是二乘以八16个bytes变成了多少呢变成四个bytes变成四个bytes就优化了大概优化了很多是吧就优化了很多刚才16个bytes那个Lens就变成了四个bytes然后其次的话他这个我们可以看一下他这个Field tag是吧他这个Field tag呢就是第一个表示是他第一个Field表示Field tag是1它后面的field是表示和第一个之间的差值因为这个field定义的时候你一般都是连续定义第一个field 第二个数是第三个field所以第一个field是1的话第二个field1表示在上面的基础上加1第三个field1表示在上面的基础上加1所以field变成123然后第二个这个byte data表示什么呢是表示它type 是吧
[35:00] 如果是String的话就表示是80表示String, 6的话表示是int64位的,然后这个9的话表示List,这样的话我们可以看一下就是这个其实省了很多很多的空间,省了大概压缩了四倍嘛,大概感觉。然后这个Lens也是压缩了很多的,像我们刚才看这个Lens就非常,这前一页的这个Lens就非常,为了表示Lens是6,它整个这个数量都比这个6大了是吧,这个Lens的利用率很低,这块的话就直接减制成6,如果它这个Lens比这个6强的话,它会有其他的方式来就是把它,增加这个Lens 是吧,就是其他方式增加这个Lens,所以它压缩的比更高一点是吧。所以之前的话是59个Bet,所以现在压缩了一下,就是Compact Protocol压缩成34个Bet。所以它这个focus就是这个压缩呢就是在datatype上和lance上进行压缩我们看一下DataType和Lens上进行了压缩我们再回顾一下Message Pack是有Full Name的所以它这个Lens比较长是66个byteBinary Protocol是把Full Name取掉了,所以它这个Lens 从66减到59但是它这个metadata占用很多空间,特别这个lens占用很多空间,所以它只有减少了7个byte,减少了fuel name,减少了7个byte。这个里面不仅把FillName取消了,就是这个Compact Protocol,不仅把FillName取消掉了,并且把这个MetaData也取消了很多。这就是额外的减了40%多的这个40%多的这个bites 是吧 就变成34个bites了然后我们看一下ProBuff,ProBuff是JRPC的一个就是用来做这个transfer的protocol 是吧,serialized or deserialized protocol。它实际上也是做跟那个compact protocol是比较相似的。也是做了这个就是也是就是对MetaZeta进行优化,然后没有Fuel Name谢谢大家我们看一下这块我这块简单列了几个OK和NotOK的大家就是思考一下,这个也是一个血和泪的教型啊,就是我觉得NewBuy是最常见的犯的就是你去公司最常见的犯的一个code,就是一行code,然后把整个系统breakdown,这是最常见的,我自己至少见过两三次了,就是这个一行code把fieldbreakdown了好所以为了你保证你这个backward compatible和forward compatible,什么更改是允许的?比如说你改field name是允许的,就是因为它我们想一下这个Protobuf和Strift的进行DataTransfer的时候,它不用Field,它不用Field name,它是根据tag来的,就是你到底是在第几个Field,第一个Field还是第二个Field还是第三个Field,它是根据这个Offset来的,所以你Field name是可以随便更改的。这个是不会出现问题的,但是你不能reuse field,我们之前也讲过了是不能reuse field的,因为reuse field,你不知道你有哪一个client是几年前的版本,你以为是field number,结果人家sign的是fold number,那你整个data就mass up了,所以你是不能reuse field的,因为你不知道你client用的什么version,是吧?然后,change type是也许ok,但一般情况下,大部分情况下是不ok的。比如说你这个type之前是个array 是吧,你要把它变成一个string,多半是不ok的。你之前是个int,你要把它变成string,多半也是不ok的。所以change type的话你要非常非常小心,多半是不ok的。这个问题会很大,就是你比如说你这块定义的client端定义的favorite number,然后server端想的是string,然后你传过去的时候这个就不compatible,很有问题的。然后最后的话是加requireField是非常非常不ok的,这也是最常见的你broker系统的一个方式,就是我们可以思考一下,这个protobuf还是这个,不管是protobuf还是shift,他做的时候就是把这个field就是把这个dataCompress,就是Encode和Decode以后,它会check这个field在不在是吧,你如果是定义required field,它接受这个data的时候,如果你定义required field,如果它那个field不存在,或者那个data没有,那就会zero exception,就是你的invalid request,是吧?就server段会说exception invalid request,或者client段会说exception,我不知道你server段返回什么,反正我这块没有。是吧?如果你有一个,加了一个necessary field,如果你加了一个NancyField,我们可以思考一下,假设你client端是version1,是有两个field都是optionField,你 server 端突然加了一个 necessary field 就是 field 3 叫 necessary field任何client端用的旧的client发给server的request全部是invalid的因为你只有两个field,field 1和field 2 which is optional,当你,当你,但你server don't expect,你有第三个field,是required field没有这个 field 那你过去的时候就会fill 掉那我们反过来想一下
[40:00] 假设你remove了一个field 是吧在你server端 remove了一个field你client端是有field1 field2和field3的其中 field 3 是 required field然后你server端是remove了一个field是吧然后你send a request过去,然后他给你返回数的时候返回数的时候,他最后只有Fu1和Fu2,没有Fu3那你也可以是被rejected 然后或者是你这个另一种情况是什么呢Client的端是有Field1,Field2,和第三个Client端是有Field1Field2的然后呃是是是Field 3是Required Field 是一个最是old version 是吧Server 端也是Field 1,Field 2和Field 3,然后Field 3是Required Field,也是old version,但你看端更新那个新的version,把Field 3给remove掉了,which is required field,那你这样的话算过去的时候,server的话还是不认是吧,因为你缺少一个required field。这个forward compatible和backward compatible中间有很多,你可以把很多这个列举一下,眉举一下,你就会发现整个这个required field就是一个坑。所以现在很多时候这个一个非常好的这个习惯就是不要加required field。除非你确定这个Field加上去以后永远能存在并且你只在第一个version加required field,是吧,你在version 0或version 1的时候加required field,以后就不加required field,这个是比较好的一个practice,就是如果你加required field,你可以在第一个version加以后就不更改它了。不管是增加required field 还是remove required field 都不应该有然后如果你想有一个field变成就是你希望在application level希望有一个field是required的话,那你可以在application level加logic,就是说这个field应该是required的,如果不是required的话,我可以log一下是吧,我可以systemlog一下说这个field应该required的,你这个client有问题,然后呢,然后你再返回一个你sugar过的一个,你专门处理过的这个response。所以这个backward和forward compatibility的点,大家可以,就是有很多文章写的挺不错的,这款我只是一个非常简化版的version,你可以搜一下这个比如说thrift和protobuf backward compatible有很多有很多业界的同僚写的那个总结文章写的非常不错特别是你改SRIFT的时候要非常小心读一遍这个文章大概有个概念然后最基本的雷不要踩是吧不要突然加一个required field这种最基本的雷不要踩是吧reuse field这种雷不要踩是吧这个还是很重要的不过,NewGuy采来也正常,因为这个是最常见的这个一行扣的整个系统到达最常见的空好然后我们就看一下最后一种是Avro encoding,这个Avro encoding和之前的encoding不一样,我们看之前的两种encoding,不管是Protobuf还是Drift,它更多的是为了QPS,就是Transactional Request,是吧?它实际上是,你实际上就是有点像,比如说我们之前讲的这个Transactional和Analytic,就是嗯上周次讲的Transactional Analytics,它是425这个User Traffic所以,用者是有所在的但是Averill这个protocol不一样,Averill这个protocol更多是为了data transfer的在Uber 里面我们也用这个Avro Protocol,在Avro Protocol用的什么呢?是把这个Data从这个Transactional Database然后Pot到这个Analytic Hadoop上,Hadoop File System上,就是用这个File System是吧,它实际上是用一个Uber有一个kafka的一个service叫watch tower,就是你所有的analytic transaction它会同时会把它变成一个就是那个event driven的一个kafka message,然后这个kafka呢然后卡夫卡这个schema是用Avro来定义的然后他是用Avzor来transfer的,他是为了database先transferdata的。我这块具体怎么样compress,encode和decode我就跳过了。因为Avro设计的时候是数据的数据,比如说数据的数据,所以它这个Full name很重要,Full tag就不重要了。这个是不一样的,因为这个database transfer,你fuel name是非常重要的,但是你fuel tag就不是很重要。好,所以这个Avro呢,它优点是什么呢?就是你用来,比如说你slot file有很多records啊,然后或者是你Send records over network connection,或者是你这个database dynamic generate schema,比如说你这个不管是sift还是put a buffer,它是费了transactional needs需求的,所以它没有这个needs说我这块有个database,那块有个database,我把这个database从这个database传到那个database是没有的但是这个Avro是这样定义的它会dynamically根据你的database的generate schema然后dump你的那个data然后在另一个schema
[45:00] 接受就是Avril设计的这个这个优点是吧那我们看到之前这个Binary Coding和这个CSVJSON还有更早的这个Language Encoding有什么区别呢其实它的区别是这样子,首先它更Compact我们可以看到这个Protobuf不管是Protobuf的这个encoding还是Swift的Compact encoding它可以使这个data使用率增加一倍,进一倍是吧?并且你这个metadata越多你的使用率就越高我们这个data使用率是非常高的这个这个特别是可以减少 emit field name假设你这个request是一个 million request per second,如果你不用这个的话,你用的是比如说是这个JSON来传的话,那你这个Field可能要重复一个million time 是吧,你说每个request里面都要fill name,但是用这个swift或者protobuf的话,你这fill name就省略了,每秒就省略了一个million time的fill name的传输。其次的话这个schema也是非常valuable的,你不管是protobuf还是swift,我们可以看一下你所有的这个protobuf和swift,你其实都是作为code来check-in的你作为Code来Check-in,就可以用到Git了,GitHub这种CodeVersion Control System,是吧,CodeVersion Control Management System,就是不管是这个Git还是这个SVN是吧,它实际上就把你的所有的Version全部列出来,这样的话你就可以看到你这个Protobuf这个Schema是怎么样involved的了,是之前比如说两年前什么Version,然后几个月前是什么version,现在是什么version是吧?你说的version其实都全部index了,就是通过这个GitHub,还有这个当你check in code的时候就相当于帮你保存了。你想lookup旧的version,你可以看Git的这个history,你可以看Gitlog就知道它version是怎么样演变的。但是如果是比如说你用这个CSV或JSON,那你这个是完全没有的是吧,包括你用Language的这个,Language的这个encoding的话更没有了,因为Language encoding根本没有考虑version的问题,所以你用Protobuf和Swift,因为它是code的一部分,你在写code,你写Protobuf,写Swift,写code的一部分,所以它相当于是用Github就帮你version过了。好其次的话,因为你这个schema是相对于你有一个database, backward forward,你可以check的是吧?所以在你任何deploy,比如说你server端想把这个version从V1 bump到V2,那你可以看,根据把说的historical client version看一下,这个V2会不会有breaking effort 是吧?会不会你deploy V2了以后,之前有个版本的V1,这个Client就没法抗你这个V2的server了,因为你有说过去的history,所以这个schema evolvability会更好一点。然后最后一点的话就是你Strong Type的就是Statistical Type的Language,比如说Go,比如说Java,你Generate Code的这个Portograph和Swift的Generate Code是可以帮你发现一些type mismatch,在Compile的时候就可以发现一些type checking,做一些type checking的。如果大家写过Compiler的话,Type Checking和Type Reference是非常重要的一部分。啊一般来说,稍微有一点经验的公司或大公司都会用一些strong type language,这也是为什么Facebook花了那么长时间,把那个PHP改成了它那个非常特殊的一个PHP,Facebook的PHP是Tapped PHP,因为PHP本身是没法没有Tapped的,它会造成很多这种非常trivial的Error,然后就没有被人发现,然后就造成outage了。所以Facebook花了很长时间写了一个HHVMApplicationDeveloper以为他写的是一个PHP,但是实际上他会把这PHP这个type的php 转换成C++ code,然后在productions上跑到C++ code,他专门写了一个php,就是typephp.c++的一个compiler也是因为这个type code其实会减少很多这个 unnecessary headache unnecessary outage好,encoding这块和decoding这块就讲完了,大家有什么问题吗?有问题的话回复一下我觉得可以直接问如果有问题的话可以直接提问谢谢大家谢谢大家谢谢大家谢谢大家谢谢大家没有话我们就到下一个小章节啊,就是这个Data Flow,Data Flow的不同方式。我们讲过之前
[50:00] 我们所有的这个system design都是一个互联网环境下的 是吧?不是你不是design一个比如说autonomous driving自动驾驶这个其实不是一个互联网的环境,因为它这个数据量太大了,autonomous driving你要是有一个128LiDAR每秒钟的数据量高达几百个Mbps180MB/s 你这个是没法用互联网来搞定的 并且就autonomous driving 任何通过互联网 我们之前讲那个在第一次讲的互联网的DarkSizeTruth, Knowledge and the Lives in the Internet 的时候,就是讲这个这个SharonArcy Architecture 的时候讲过。这个Network 它的 performance 是非常不稀疏的。你很多时候一个request之前可能要花5毫秒就解决了,但是你下一次的话,因为多个用户同时在使用,本来是5毫秒的request,你突然就变成了两分钟都没有回应,一两分钟都没有回应。所以你autonomous driving,如果是几秒都没有回应,可能就出车祸了。所以它那个环境下,它是不能用这个,就是不能用这个,就是它不是一个互联网公司,它不能用这种server端的这个来处理。但是我们大部分就是现在的比较热门的工作都是互联网公司,他这个其实面对的问题不要,所以都是用share nothing architecture。然后这方面我也想给大家一个找工作的建议,如果你找工作的话,我觉得第一点就是你先看他是不是一个server,就是互联网公司。Client Server不是那种非常传统的那种像特别早的什么比如说这个Adobe啊,特别早的这个Microsoft words啊,是吧,不要这种工资,它这个迭代数据很慢,学到东西也很少。互联网公司的话它讲就是fast situation,它整个这个工作模式和这个工作环境和这个都是不一样的,因为我两个公司都工作过,我之前在那个EA做过那个游戏,不管是开发模式什么都不一样,然后所以找互联网公司就是这个就是它不仅是给的钱多啊,它更多的是对你职业发贷有好处,其次的话,如果你是在一个互联网公司工作的话,它基本上都是share nothing architecture所以它就是我们之前讲的说的design是围绕这个Sharnazi architecture来的所以这个share nursing architecture 里面几种 data flow 的话一般是有三种,一种是 data flow 通过 database 来share第二种是通过 API 来share 的, 第三种的话是通过 message 来share 的, 所以我们一个一个看。从 database 来share 的话, 首先这是一个错的模式, 这是一个非常不 work 的模式。为什么不work呢?就是你现在是用database来扩你这个database的时候,访问的时候可能是来自于两个service 是吧?可能是来自于Service也可能是来自于service1,也可能是来自于service2,同时对database进行修改那你这个service 两个service 之间你可能中间的 business logic 处理是不一样的比如Service 1的时候,它可能会通过Logic Business Code 1来处理,然后它可能是这个ORM可能是用的ORM Framework 1是吧,然后把它写进去那你另一个Service 2可能用了不同的Business logic然后用了不同的ORM写进去,你写进去是同一个database,同一个table,但你读出来的时候,你不一定compatible,就是你series2读series1的data是不一定compatible的,因为它可能出现了一个,就是它可能用的logic不一样,是吧,它可能是一种,就是你assume 的assumption 不一样,是吧也可能他处理的不一样是不compatible的是吧即使你现在compatible以后也不一定compatible是吧你比如说servicee说我现在是在这个payment里面写一个新的roll,我要去payment里面写一个新的roll,我为了保证我这个数据更好一点,我会把payment里面这个credit card进行处理一下,把每四位poss一下,就是四位加一个dash,四位加一个dash这种举个例子。但是你service 2可能没有这么做,所以你service 2读的时候,你四位加一个dash,这个data读出来就非常crofted了。所以这样的话,这个DataFlow通过Database是非常run的方式,那因为你这个,首先你有可能有不同的version在写入,其次,你可能有不同的Service,首先你可能有不同的Service在写入同一个Database里面,其次不同的Service有不同的Version,这就变得非常不maintainable然后再其次,你写的时候和读的时候不一定是同一个service,也不一定是同一个version这个就整个乱层一船了就完全没法用了好好,然后这块的话就给了一个例子,这块给了一个例子就是这个还是,就是这个是很常见的一个例子,就这也不完全是跟这个database
[55:00] 就是Data Pass from Database相关的比如说这个例子里面在你这个写的时候你是用了一个新的version写的这是一个link in 的例子不一定是linky嘛 就是一个profile例子你写的时候是用一个新的version code写的所以你有username, favorite number, interest,还有个photo url 是吧也是四个field然后你写进去这个database的时候当你做这个ORM,Object Relationship Mapping的时候,就是Encode和Decode的时候你就把它变成了什么,就是ORM变成了什么,就变成了这个这个old version 因为它是old version 所以它没有photo.uil所以他要把photo url discarded了是吧,我们看一下这个,这个code就是在你写code的时候,它是old version code,Client是新的version你的server端是old version,所以拿到这个时候就把photo url discarded你写进database的时候,这个photo.us就彻底丢失了这就是Data Loss了,看一下就是你写的时候就是你data written的时候是new version code有一个photo url你就是你把它decode的时候,read decode的时候,你code是old version你是不认识这个photo URL的,是Java code的,然后你写进database的时候,这个photo URL就彻底丢失了,所以这样就落死我data了。好,这个是可以通过carefully coding能解决的,这个是可以仔细的coding解决的,但是这个是,所以说在Coding比较在Syrnaz新architecture,coding比较难的部分就是你想一下,每次都要想一下你的新的version和旧的version是否compatible的。如果你这个Service和Service ABCD都有interaction的话,那你当前的Service新的Version和旧的ABCD是不是也是compatible的?就是都需要想一遍的,所以如果Micro Service有一点不好的地方就是如果你这个Service跟20个Service做interaction,你做update的时候,你需要想一下你的新的version是否和旧的这20个Service全部 compatible,是不是全部 backward compatible这是非常重要的一点谢谢大家并且 service 比如说你这个 service A B C D E他的Client的version,他可能用一个非常旧的Client version,那如果你这个Compatible不只是跟上一个版本Compatible,你可能要跟非常旧的版本也要Compatible这个就非常非常复杂了,所以这个你可能到时候跟另一个组是吧,比如说Service ABC是用的是特别旧的version,你还得跟它单独聊说,你必须把这个你的那个version bump到20以后,否则的话我们这个data就crafted了,你首先得check in,说保证它这个version bump到20以后,然后你才能把你的version再check in,是吧,这个rollout是非常非常复杂的。业界上关于这个miserable story非常非常多的,这个Misterable Story,这个version,upgradeMisterable Story非常非常多的我记得我去年失业的时候参加了一个Kafka Conference它这个 Kafka,这个topic,不是有version吗?它这个 Kafka Conference就是他这个version也是讲了很多这种非常miserable的story,就是这个version不compatible,然后deploy 出问题了然后我前两天读了一件Hacker News的文章,就是卡夫卡是把root paper 给remove掉了Kafka之前是在用Rootkeeper 来管它那个replication 和这个Partition, 我接下来会讲replication, partition, 这一块, 卡夫卡用这个rootkeeper 来管它replication和partition的, 它最新的版本是它起了一个非常酷的名字叫, 卡夫卡need a nodekeeperCafcar要把这些Rulekeeper给remove掉,dependency remove掉,然后它那个version update很有趣,就是它有一个过渡的version,就是说如果你用Cafcar过渡version的话,那你这个它同时支持有Rulekeeper和没有Rulekeeper的如果你想 update 到最新的没有 rulekeeper 的 version,他推荐你先 update 到其中, upgrade 到其中一个过渡的 version 上。就是在intermedium,interim version上,然后呢,通过设计一个configuration通过设置这个configuration,把rootkeeper的dependency取消掉,那你就用没有rootkeeper的过渡vership然后如果没有问题的话,你再可以bump到最新的version,最新的version是完全没有rootkeeper,就不支持有rootkeeper的version了。它是通过这个方式来管这个version的,它花了很久的篇幅就介绍一下怎么样怎么upgrade,这也可以看到这个业界里面整个version,不仅是client server 端的version,不仅是swift 之间的version,不仅是data 之间的version,你 software upgrade 的时候,你这个 library update 的时候,version也是非常头疼的一件事情,有很多 miserable story
[60:00] 好最后的话就是我看应该没有几个slides了就是Data Outview Code,这个我之前也讲过一遍了,就是你Code可能总体来说就几个version是吧,如果你这个可能server 端一般就一到两个version,client端如果你没有要求你的client upgrade version的话,可能要多个version比较复杂一点,client端毕竟不是你直接用的,很多时候不是你直接用的一个service,所以你的client version相对多一点。但是你的data不是这样子, data是从你这个server general available以后,所有的data都在里面,所以你code可能就有一年旧的version,两年旧的version,或者是两个月的更多是很新的。但你data的话经常有五年旧的data,十年旧的data,很常见的一件事情。所以这个data呢是Otlio扣着的,我再调调一下这个你包括跟System Design的时候就说你始终focus是focus on data这部分的System Design里面永远是Data是大于一切的,Data是永远比Code重要的,知道吧?就是特别你很多时候System Design的时候,如果你突然有个Algorithm想不起来,无所谓,就说有一个Algorithm能处理,能记住名字就可以了或者是你有一个具体的方式,有一个具体的小的technic,比如GioHash这种小的想着技巧忘了就忘了无所谓但是你这个data怎么处理,这个data是怎么样flow过去的这个是不能出问题的System Design里面具体一个小technic忘了无所谓的没有人car的你到底是用MD5还是用sharewarehash这两个之间有什么区别没有人car知道最好,但是不知道无所谓,但是你这个data怎么处理,这个data这个data怎么handle,这个data是怎么flow过去的这个是最重要的这个是需要很多经验积累,然后也需要一些理解,然后读一些paper,读一些article能理解更深的。好,所以说这个这块的话就是这块讲的XPrescial也是这个LinkedIn的这个Database,XPrescial这个LinkedIn的database 我看起到哪儿了对谢谢大家好,如果你是用MySQL,我们之前讲的MySQL或者SQL的话,你是Schema on write,所以你的整个table schema是一致的,但你如果用的不是这个SQL,是用的NoSQL,比如说Mongo的话,你其实同一个table里面有多个schema的存在。你有多个schema的存在当你take a snapshot的时候,比如说你要这个database take a snapshot,防止有人丢裤跑路这种是吧,你要take a snapshot,防止有人丢裤跑路,你要take a snapshot,然后那你的话,你其实如果是用mongo的话,你其实是你这个data可能是过去五年data都有,然后各种version都有,这种情况下的话,你dump的时候,其实你要把这个dump呢,用最新的scheme啊,所以你take a snapshot database,take a snapshot data,和这个database自己原生态存的,就是native存的data可能不是同一个data你这个dump的data可能是经过这个latest schema encoded过的,所以不是同一个data。所以这个每次database dump出来可能version都不一样,多少有点不一样。但是它的存在这个mongo里面这个data,就以document database的作举例,在mongo里面的original data可能是从来没有变过的,但它每次dump的时候会根据最近的schema来dump一遍。这个data两个data还是非常compatible的好,就是这一点。然后之前讲过不是讲了,有人问了这个LinkedIn用什么存Data,LinkedIn是用Expatial存Data,这个Expatial是在MySQL上实践的,有点像Document Database,但是它在MySQL上实践的,它Dump的时候也是遵循这个,就是你实际上有很多种不同version data,所以你Dump的时候也要用最新的schema进行处理,所以处理出来也是不一样的。就是之前讲的第一个就是database是通过 data flows through database这是第一个然后这是不对的,所有DataFlow through database,这个都是有问题的,都会出现各种各样的问题。然后第二个的话,DataFlow可以through REST API或 RPC,这个是比较常见的一个,就REST API和RPC,这也是这个说的Internet互联网公司最常见的一种形式。RPC不管是REST 还有RPC都有Client和Server你Client可能是webbrowser,你Server可能是一个webserver,很多时候如果是RPC,internal RPC,你Client也可能是一个webserver,你Server还是一个server。同时呢,你这个你这个看着
[65:00] 和server是相互的相对的你一个service可能同时是client的同时也是server就是你一个service比如说你这个Uber Trip Service你是user的server但你有可能是payment的client你会request payment做些事情user request你做些事情然后其次的话这个service呢你可能是server request也有可能是为了transfer data你可能是为了transfer data 是吧然后对,然后那天有人问了这个SOA就是service-oriented architecture和这个micro-service architecture有什么区别其实它的区别不大这个micro-service architecture其实是被redefined了rebranded了,它其实和这个十几年前就是Java比较火的时候那个service-oriented architectureREST的Java比较火的时候比较火的时候,那个service-oriented architecture是同一个东西其实这个核心部分是差距不大的但是我们谈的时候,你要谈micro service architecture,感觉是感觉更时髦一点,service-oriented architecture,感觉是java,rest API,那种比较十几年前比较老的东西,就是2B2b的那种东西但其实本质上是没有区别的本质上实际上是没有很大区别然后最后一点的话就是不管你是用micro service还是service-oriented architecture你实际上都是为了decouple你是为了把client和service之间decouple出来那你这样的话你client和server可以独立的进行deploy和evolve比如说你这个trip service和payment service你就可以有不同的组去维护然后他们之间可以相互的进行更新你trip更新的时候你不需要depend on payment更新你payment更新的时候不需要trip更新你们之间是通过API定义好的那你API定义好了以后你在里面怎么搞都可以你不管是把这个重新全部架构一边把node.js改成go是吧或者把java改成c++你可以重新架构一点只要你这个API没有变你这个agreement没有变你可以随便搞是吧然后这样的话这个系统总体来说是更稳定一点因为毕竟我们会发现这个code里面System Design里面很多部分都是怎么样就是让几个组之间更好协作的因为你毕竟这个code不是从天降的也不不是凭空生存的,你毕竟是用人来维护的。而这个系统设计中,系统维护中,DevOps里面最大的Error就来自于HumanError,所以这很多系统设计约定俗成的这种,不管是Micro Service Architecture, Service Oriented Architecture,它其实是为了更好的Coordinate Developer,更好的就是产生一种Againment,像它这种架构,它这种agreement的出错率是最低的好,这块我简单列了个REST API这个我列了一下这个REST API因为我觉得可能有的同学对REST API这个理解不够深入我列了个REST API,我今天可能就跳过了,没有时间讲就是我推荐你去,就是推荐大家如果你没有懂什么是REST API的话应该去读一下什么是REST API这个是非常fundamental的东西,不复杂不复杂,它的核心点就是一个uniform interface它是把所有的东西都是resource你通过action来get resource,或通过action来进行对这个resource进行create, create, update的操作和delete的操作这个REST API不复杂这块的话,我想额外说两点,REST API是没有对versioning进行考量的我之前讲这个你schema evolve 的时候schema evolve 的时候REST API是没有对Version进行更改的那像Salesforce用REST APISalesforce怎么样管理REST API呢Salesforce是直接把Version是放到那个UIL里面的就是你比如说这个就他比如说这个Resource假设这个Resource叫User他使用的时候会叫Version1或者user version 1然后user version 2它实际上把这个 version是直接放到这个 URL 里面所以你这个它并且Southforce是只 maintain过去的4到5个 version3到4个 version就是它只 maintain过去一年的 version如果你要你要是那个什么你用的 versionoutdated的话它会通知你这个 version要 sunset了你要更新成更新的 version所以这个Southforce是直接把这个 version放到 REST API 里面的Wrest API URL 里面还有一些2B的公司呢它是每个Client 它专门自己存一个version有的2B公司这么做比如说比如说这个假设有存在一个叫System Design Service的公司假设我成立一个叫System Design Service的公司我有Uber作为我的客户我有Facebook作为我的客户
[70:00] 我的Salesforce作为客户是吧我自己internally存一个version,所以Uber可能用我的第15个version,version 15,是吧,Susco可能用我的version 1,是吧,Lift可能用version 13,是吧,当我去,当我说我需要你upgrade version的时候,我是专门找你谈,就是我专门找Uber说,啊,这个我需要upgrade version了, version upgrade 以后降降的好处是吧update 以后这样这样的好处那你是不是花点时间把它update成最新的version当你准备好说我这个version update 好了那我就把那version bump一下 是吧bump 从1 bump到15这就是非常 tedious 的 management这是为什么我们说这个传统的REST API可能相比于最新的RPC version和这个schema involvement 的不好的一点,它没有version support。实际上如果我们看那个REST的话, REST其实是一个博士的毕业论文,是一个博士的 thesis,他实际上写REST的原因,他是提供一个是一个philosophy,是一个价值观,是一个philosophy,它不是说是一个standard,Rex是一个philosophy他写了六条 philosophy,只不过这个 philosophy 定义的很好,别人用的很多。相同例子很多,我们会讲那个 CAP 的时候。CAP的时候也会讲,CAP也是一个非常就是经常感觉 new guys 会说CAP,CAP也是一个 philosophy它也是一个帮助你做的东西,它其实不是一个很好的一个protocol,也不是一个很好的一个technical的一个东西,它是一个philosophy。但是unfortunately,所有的这个REST,CAP都比较火一点,但是它其实更多是一种philosophy。和REST相对应的是一个叫SOAP的东西,它这个,Design 是不一样的,SOAP是定义是Action更多一点,但是这个SOAP基本上只有一些传统2B公司在用的,所以你不知道无所谓的,SOAP不知道无所谓的,但是REST要理解,这个REST的Philosophy是怎么定义的,你要找几个例子来看,这个不太复杂,大家可以自己花点时间就可以搞懂了。好,我们讲这个Data Flows 入RPC 或Data Flows 入API,那它这个 Flows 是什么呢?有很多floor,这个RPC和API是有fundamentally flawed的但是它是过不去的卡吧,就有很多flow。我们一项项读一读,这块课想多花点时间,让大家理解RPC和API的flow的部分。当你使用RPC和API的时候,它就不像Local Function了Local function是非常predictable你最多有就是你的CPU有些context-switch的话,可能当前这个执行的command会被其他的这个process 被占掉了,但是总体来说是非常predictable你要不就 success,你要不就 feel.但你用 REST API 的时候,它是不是很predictable,可能这个request loss,就是Network出发前,这个request loss,有可能这个response loss所以很多时候当你用RPC的时候,你必须要去Timeout因为你这个request可能permanent loss 是吧?你这个request permanent loss,quare response permanent loss,所以你必须timeout所以这个当你用这个REST API或者是这个RPC的时候,我们统一叫RPC好了,当你用RPC是Remote Procedure Cloud, API Cloud的时候,你一定要handle一个就是timeout 你要处理timeout的问题这是一点是吧谢谢大家其次的话,如果你timeout的话,我们看第三个步骤,如果你timeout的话你其实是没法区分 你从看着端你是没法区分到底这个request成功了没有你可能是在request的时候timeout了,request的时候丢失了,你有可能是整个人SA server 端处理完response的时候丢失了所以当你timeout的时候你必须得retry,否则你这个request就没有go through 嘛。那你retry的话,那你server端有可能会接到同一个request多次。那这时候你所有的Rest API,所有的RPC设计,所有的API设计,一般都是要idempotent的。idempotent的意思就是你同一个request,request多次没有side effect你比如说我举个例子比如说你是个payment你payment比如说你server client 端说我手机端说我要对这个比如说你听我课就讲得很好,要打嗓是吧?那你打嗓的时候,你突然timeout了,所以你不知道你到底有pay没有pay我钱是吧?那你可能,因为你不知道pay没有pay我钱,可能再重复一下这个action当你重复这个action,假设第一次其实已经勾诉只是return response的时候timeout的话,那你第二次进行pay的时候,这个pay应该变成一个no-op,不做任何事情那你这个就对你邀请
[75:00] 你设计的时候你这个service就是ad important还在 important是一种的一种的一种的一种谢谢大家所以它会增加你这个设计逻辑的复杂度这也是我觉得我第一次到互联网工作公司工作的这个感觉就是你这个因为你有timeout,然后各种corner case处理,你会感觉这个程序员永远不会丢饭碗的,因为这个,我感觉我刚毕业的时候还会挺担心会丢饭碗,但是后来去互联网公司工作一下就发现这个corner case处理太多了,其实是一个非常简单的API call包括你怎么样做什么load balancing 是吧怎么样charge request 你怎么样处理这个timeout 啊怎么样处理各种counter case exception你会发现程序医师永远不会丢饭碗的?好然后其次的话就是当你靠local function的时候,你的这个execution time是predictable的,但是你靠network call的时候,你这个execution time可能从一毫秒到几秒之间变化非常大,depends on network,所以这个variable是比较大的。谢谢大家你这个就完全不predictable是吧然后最后的话是当你靠local function的时候你其实是一个pinter到local memory 你不需要把那个data你不需要把那个Data Structure全部copy一下,但是你做Network Request的时候,你必须做Encoding和Decoding,你必须把这个Data变成,就是Encoding成一个,不管是Binary还是一个TextStream,然后把它Transfer到另一个Service,然后从那个Service处理完的时候,也必须Encoding过去,然后传输给你,然后在Decode以后才能进行处理。所以你其实花了很多时间做encoding和decoding的就是RPCs里面最后一点是这个client server可能是在不同language 是吧?你比如说你client可以加完server可以是go,但是这个不同language之间,你比如说你有client支持10个language,server支持10个language,那你不同language的interaction是nsquared的。就可能有不同100个language之间相互 interact 那你中间会有各种各样复杂的问题各种各样复杂的问题啊,这个问题很多时候是这个fundamentally很难被解决的。好,最后一点就是我们讲过三种Data Flow的模式,一种是Data Flow,是Database是划签掉的,就是任何时候Data Flow都不应该通过Database来进行Transpass,然后一种是Data Flow通过REST API通过API或者通过RPC,这个是可以接受的,虽然它有各样的unpredictable,各样的问题,但是它是可以接受的。最后一种就是Data Flow通过Message,这款Message是Message Queue或者是Kafka,就是Kafka或者Message Queue都可以啊。它的好处是什么呢?首先是decouple,我们之前讨过了好多次,当你加一个message queue,或者当你加一个worker pool的时候,你就fundamentally natively decouple了你就是把这两个问题就分开了,separate concern,decouple这两个client server了然后你可以就是asynchronize处理是吧,asynchronize处理的话,你这个所有端比如说worker少的时候,你可以多加点worker,worker多的时候可以减少点worker是吧总体来说这两个问题就从逻辑上就分开了。你maintain的时候和那个debug的时候更好debug是吧?然后所以说这个message passing data flow 首先就是比如说你server突然down掉了,message也可以在那里等你serverup的时候可以这个处理,因为是Asynchronized处理的,然后你Server也可以就是Asynchronized skill 是吧,这是第一点,非常好的一点,然后 decouple 也是非常好的一点,然后其次的话,你如果这个Servor单挂掉的话,你Servor单也可以等等重启了以后接着处理Message,是吧?但是如果是RPC或者API的话,你Servor单挂掉,你直接就unavailable了。是吧,就是你如果收入单挂掉RPC,不管是RPC API,你收入单挂掉你的request直接on available,但是在这个message里面,你这个client request的中指就是产生了一个message你server有空的时候,server有空,worker有空的时候,处理的message就可以处理,所以你不是unavailable,是吧?你不会说是这个就挂掉了然后其次的话还有一个其次的就是你这个Client团不需要知道具体是哪一个server处理你的,你不需要知道你server的具体信息,你只要有一个worker在处理你就行了,有一个server在处理就行了,处理有块钱,你不需要知道这个server的IP address和这个paw number其次的话还有一点就是你这个Client端同一个message可以sign了多个server是吧这个常见的就是我记得在Design YouTube里面就是把user activity变成一个就是
[80:00] message passing data flow. So, you watch YouTube videos, Watch YouTube video and then it's gone Then the activity, user activity, can become a message. Then you can have several services to make this message. There is a service that can log in. Then you can log in. You can log in. This video will watch. There is a service called user history. User activity. And another service is machine learning. According to user activity, it should be recommended to you do what? For you to do some videos, machine learning. UseActivity will help you recommend, you may like watch videos, right? There are several services to handle. So we use a message to do a service and handle it. And then another one, You request a trip, Uber request a trip, you can do payment. That is also a request for more services to handle. How? And the last thing is the message passing communication and RPC. It's different. The message passing is asynchronous. sender is send and forget, sender sender is forgot, and you need to fix this worker after you need to call sender, it is asynchronous to fix it. But the request is usually synchronized. You hope that when you have server-side and you will have to fix your request. That's all for today's presentation. Do you have any questions? uh 还是 I have a question. "I have a real question in the work of the work. Shift is called "Create, throw, accept" Protobufs no exception is no other language. I will tell you how to do this. Protobufs can also be an exception. And then, the protocol buff is how to fix the system. Cocoa Buff for Exception? Cocoa Buff is a shared thing, it's not an exception. No, it should be a solution. What kind of exception is it? I was thinking about the problem. - I don't know how to use the message. What is the message? I forgot the detail. I still use the Prodweb. I used it with Swift, but I didn't use it with Perlva. um That's right. That's not a problem. It's a problem. It's a problem. them. It's not a protobuf. There is a lot of protobufs that I don't want to do this exception. Because it might not be able to do this exception. Thank you. Your exception is not wrapped in protobuf, it is always the end of the process. It is not a protobuf. I have a problem. I have done this. I have a problem. I have done this. I have a problem. You can set the arrow to a protobuf. You can see the error as a prototype field. This is okay. Thank you. This is probably a kind of a common method, just to put a field in a field. I'll just look at it. And it says REST and RRPC are the same. Rest is a philosophy REST is a way through HTTP cloud, and efficiency is lower. It is through HTTP transfer. RPC is through Binary encoding or decoding. So it can reduce the transfer of the transfer. transfer of the data. efficiency, so it's the effect. And this is the first thing. Efficiency RPC is more than rest. It's a lot. It's a lot. It's a 30% percent. I forgot to say it. and also according to your data pattern. And the second thing is RPC, the usual way of using the RPC. If you have API, you can support RPC. Because it is remote procedure com, remote function com. You actually do it when you do it. You actually have to do this RPC. The design of the system is more like the Car function. The heavy carrying function is where to operate. You just know that it is in a server.
[85:00] 你不看它到底是本地执行的还是在remote执行的但实际的生活中我们现在讲了RPCN很多 limitation,我们讲了RTCN很多 limitation你实际上还是要知道这个RPC在哪执行呢?就是你作为一个合格的系统工程师,你要知道RPC在哪执行呢?但是它设计理念就是,你就知道它在一个node上执行就可以了,不像在哪个node执行。然后其次的话,另一点的话是,REST的话一般都是就是你set一个REST这个server的话就是他有一套自己的 philosophy 然后它是不帮你generate code的 是吧,你这个像RPC的话,RPC framework,不管是这个,不管是这个thrift,protobuf,它其实额外更Generate code,你实际上有code generation layer所以你使用RPC的手机像靠一个remote function一样这个还是有点不一样的但是其实主要的区别还是Efficient的区别,就是这两个区别的主要区别是Efficient的区别。我觉得其他区别都是相对来说就是就是这个 API 区别啊,包括使用的 best practice 区别啊,这都是次项区别,主要区别还是这个 efficient 区别现在的话现有的话一般说对public的都是REST API对public的REST API,然后internal的,还有不同的cooperation之间的,或者是不同的这个API 的这种有RPC 比较多,比如说你设计一个 service 你说对外的对于 public 的 REST API 比较多一点,然后你 internal 的可能是 RPC 多一点谢谢大家是这样子的,因为它这样Internal RPC效率更高,然后这个另一方面我不知道这个RPC是不是这个Security问题可能有一些,你RPC的话是不是你这个Authentication Authorization这一块,Security是不是可能有一些额外的问题?但是一般来说你写RPC因为Remote Function Call,你可能就不验证Color,就是你已经验证过Color了。然后这块说这个 REST is implementation or definition of API pushup,Rest是个Philosophy就是这个 philosophy 我举个例子就是说谢谢大家就像NASI architecture是一个philosophy 是一个指导性的一个东西它没有任何实际的,就是没有任何实际的这个 implementation,REST是个Philosophy 就是Philosophy嘛哲学 哲学一个定义 philosophy, principle, theory, rest is philosophy, it's not that它不是那个implementation,它没有任何实际的指导。啊但是API是Application Programming Interface,这是非常广阔的一个问题,它是更多的时候是这两个就是完全是两个Abstract Context不一样的然后GraphQL,GraphQL是什么呢?GraphQL如果读一下的话,它其实是对REST的一种优化了。我记得我参加过一个RBNB的一个关于GraphQL的一个Seminar,两年前参加过一个Seminar。GraphQL是一个非常special的一个optimization,你如果看GraphQL的话,它的design其实也写的挺有趣的,它是一个非常lightweight,它不做authentication,也不做authorization,它针对的case是什么呢?它针对的case就比如说不管是Uber还是Airbnb这种,你一个user里面有太多东西了你比如说你这个user里面,你可能是一个user,比如说user,我这个user,我可能既是driver也可能是 Rider 是吧?那我是 Driver 里面,我可能有 Drive Record, Driving Record, 有一些 Last Online Time, 是吧? 我作为 Driver 这个 role 里面。我作为 Rider 里面有可能有各种各样的 field 是吧?你现在这个就是传统的互联网公司, 你一个 Entity 里面, 为了更好的去设计,Entity 很大, 比如说 User Entity 里面。可能有几百个field有几十个大的Field,然后几十个大Field有上千个小Field当你进行REST API Code的时候你就不能简单的通过getUser来读了你比如说GateUser你不可能把这1000个Fu的全部读出来吧所以这时候GraphQL就说我到底是根据什么request,就是你getUser的时候我到底根据什么request的来读我只要读其中的几个field就可以了,选择性的读几个field所以它是基于这个philosophy进行设计的这个也是也是这个也是新的一种设计方式吧大家可以看一下这个GraphQL,这也是非常活动的
[90:00] a very火力 It's a very hot one. It's like next version of REST API. It's a bit more of a new framework. And then the book of Design Data Intensive Application is a lot more than the way to explain the RPC problem. I feel like I'm not a good idea of RPC. And these questions are no better alternative. I think that RPC is no better alternative. This problem is definitely going to exist. That you do the same as the autonomous driving. The server and request are local. This problem is very small. This problem will be in 5G. After 5G, it will be solved by the solution. Because 5G's length is not very low. If 5G's length is very low, it can be connected to autonomous driving and 5G. Then you can use 5G remote driving to autonomous driving. This is a technology that can be solved. I think RPC has a lot of problems. The problem is unpredictable. And visible resources. You say data should be encoding and decoding. RESTOR RESULTS UNPREDICTABLE, and you have to do the server and add important. If you have to do the same thing, you can do the same thing. So you have to do the same thing. There is this problem, but the RPCI as a future, it can be used to through other ways to solve. I think we can go back to the PCIe. You have to do the software with a disk. This is very difficult. So if you have a better alternative, RPCI as a future. Okay, I think there's no other problem. Thank you. Well, today is here. Thank you. Thank you. Thank you.