作者 | 董鹏 阿里巴巴技术专家
<a name="pLVMc"></a>
微服务
好处:实现跨团队的解耦,实现更高的并发(目前单机只能实现 c10k)不用再拷贝代码,基础服务可以公用,更好的支持服务治理,能够更好的兼容云计算平台。
RPC
-
rpc:像调用本地方法一样调用远程函数;
-
客户端:一般利用动态代理生成一个接口的实现类,在这个实现类里通过网络把接口名称、参数、方法序列化后传出去,然后控制同步调用还是异步调用,异步调用需要设置一个回调函数;
客户端还需要维护负载均衡、超时处理、连接池管理等,连接池维护了和多个 server 的连接,靠此做负载均衡,当某个服务器宕机后去除该连接。请求上下文维护了请求 ID 和回调函数,超时的请求当回复报文到达后由于找不到请求上下文就会丢弃。
-
服务端:维护连接,网络收到请求后反序列化获得方法名称,接口名称,参数名称后通过反射进行调用,然后将结果在传回客户端;
-
序列化的方式:一种是只序列化字段的值,反序列化的时候重新构建对象再把值设置进去,另外一种方式直接将整个对象的结构序列化成二进制。
前者节省空间,后者反序列化速度快,目前的序列化框架也是在反序列化时间和占用空间之间权衡。有点类似哈夫曼编码,或者数据库怎么存储一行一行的数据。
<a name="J2qhC"></a>
注册中心
一般有 3 种模式:
- f5 做集中式代理;
- 客户端嵌入式代理例如 dubbo;
- 还有一种是综合上面两种,多个客户端共用一个代理,代理作为一个独立进程部署在和客户端服务器同一台物理机上,ServiceMesh 就是这种模式。
zookeeper 不适合做注册中心的原因:zookeeper 为了一致性牺牲了可用性,但是注册中心实际上对一致性要求并不高,不一致产生的后果也就是某个服务下线了而客户端并不知道,但是客户端通过重试其他节点就可以了。
另外当发生网络分区的时候,如果超过半数节点挂了,zookeeper 就不可用,但是实际上它应该仍然可以对它所在机房的节点提供注册服务,例如三个机房分别放了 2 台、2 台、1 台,如果各个机房之间网络断了,但是机房内部上是通的,这样注册中心不可用即使内部节点也不能服务了。
zookeeper 并不是严格的一致性,它支持读写分离,其它节点收到写请求会转发给 master 节点,而其它节点可以支持读请求,当数据还没有从主节点复制过来的时候读到的可能是过期的数据。
<a name="nL2Lf"></a>
配置中心
配置中心的需求:保证高可用、实时通知、灰度发布、权限控制、一键回滚、环境隔离(开发/测试/生产)等,目前的开源实现:nacos disconf apollo。
- disconf:scan 模块扫描注解和监听器;
- store 模块将远程获取到的配置存储到本地,本地一个 job 检测配置是否有变化,有变化就通知监听器;
- fetch 模块从远程通过 http 获取配置;
- watch 模块监听 zookeeper 上节点的变化,有变化就会调用 fetch 进行获取。
apollo 有以下 4 个模块:
- portal 作为一个管理后台,提供管理员操作的入口。 有独立的数据库;
- adminservice 提供配置的修改和发布服务的底层服务,和 configservice 公用一个数据库 configdb,每次修改配置就会往数据库里插入一条记录 releasemessage;
- configservice 用一个定时任务去扫描数据库是否有新的 releasemessage,有的话就通知客户端,而客户端采用定时轮询的方式去查询 configservice 是否有新消息,这里采用 deferredresult 异步执行;
- eruka 为 adminservice 和 configservice 提供了注册发现的服务。客户端获取到配置文件后也会写入磁盘。
<a name="4HWfD"></a>
任务调度
- 执行器也就是应用本身,任务单元也就是具体执行任务的线程,能够主动注册调度器中,并在启动的时候进行更新,例如删除已经清空的任务;
- 调度中心支持集群部署避免单点,可以选举一个主节点,其它为 slave;
- 支持负载均衡算法为每个任务随机选择执行器,能够支持失败重试,将执行很慢或者失去连接的执行器移除;
- 支持控制任务并发,例如是否允许一个任务没执行完又被调度;
- 支持任务依赖,例如一个任务没执行完另一个任务不能执行,或者自动执行另外一个任务;
- 支持任务分片,将一个任务根据参数分片到不同的执行器上一起执行;
- 可以取消一个任务;
- 已经支持 glue 模式,可以不用发布就执行一个任务单元。
<a name="zBnks"></a>
分布式锁
- redis setnx 里面已经有参数可以支持分布式锁,但是最好能把锁的拥有方存到 value 里,释放的时候做比较,不然可能释放错锁,也就是会出现 A 释放了 B 的锁;
- zk 采用创建临时节点,其他创建失败的线程监听锁的状态。
SET resource_name my_random_value NX PX 30000
<a name="qGwDS"></a>
统一监控
- 收集日志并分析,日志也可以和 rpc 链路进行关联,也可以对日志进行降噪或者压缩存储;
- 提供 api 的方式以及拦截器模式,可以基于 javaagent 做到无嵌入;
- 实现 opentracing 链路追踪;
- 可以基于 disruptor ringbuffer 的生产消费者模式;
- 海量数据的存储 elasticsearch;
- 报表生成,监控指标设置;
- 各个节点进行收集,消息上传到服务端统一处理;
- 监控指标:rpc 链路、数据库、cpu 指标等、http 状态、各种中间件;
- 日志收集可以通过直接在日志框架上加拦截器,或者用 flink+kafka 收集。
<a name="tT64T"></a>
缓存
先清空缓存还是先更新数据库?
- 如果是更新缓存而不是删除缓存:则不管哪种方式都会造成缓存和数据库不一致;
- 如果是删除缓存:则先删除缓存在更新数据库,如果更新数据库失败了也没有太大影响,缓存被清了重新加载即可。但是也要考虑到缓存穿透的问题,如果这个时候大流量进来是否会压垮数据库?
以上是考虑到分布式事务中一个成功一个失败的情况,但是这种概率毕竟是小的,可以用在并发量不是很高但是对数据一致性要求很高的情况,如果并发很高建议先更新数据库后清空缓存。
如果先清空缓存,后更新数据库,在还没有更新到数据库的情况下另外一个事务去查询,发现缓存没命中就去数据库取,然后又写入缓存,之后上一个事务的数据库更新,这样就导致了缓存和数据库不一致,如果先更新数据库再清空缓存,更新完数据库后缓存还没更新,这个时候来读取缓存是旧的值,也出现不一致,但是最终清空缓存后会一致。
不过这种方式也会产生永久不一致,但是概率很小,例如一个读请求,没有命中缓存,这个时候可能另一个线程刚好清空缓存,然后它就去数据里面取,但是又有一个线程在它读完数据库后将数据库改为另外一个值,这样那个读请求写入到缓存的数据就是脏数据了。
redis 采用单线程模型,对只有 io 操作来说性能很好,但是 redis 也提供了计算功能,如排序聚合,cpu 在计算的时候所有的 io 操作都是阻塞的。
memecached 先申请一块内存,将其分割成大小不等的若干内存块以存储不同大小的键值对。这种方式效率高但是可能产生空间浪费。而 redis 只是单纯的包装了下 malloc 和 free。
redis 提供了两种方式持久化数据,一种方式是把某一时刻所有的数据都写入磁盘,另外一种方式通过增量日志的形式
memecache 提供了 cas 来保证数据一致性;redis 提供了事务,将一连串指令一起执行或者回滚。
memechache 只能通过一致性哈希来进行集群,而 redis 提供了集群功能,客户端做路由选择那个 master 节点,master 节点可以有多个 slave 节点做为备用和读。
redis 中的字符串没有采用 c 语言里的结构,额外加上了空闲内存和已占用内存,这样读取的时候由于已经知道 char 数组大小,所以可以直接取出,避免遍历操作,当字符串变大或缩小的时候可以避免重新分配内存,可以用到空闲空间,也就是 redis 会预分配一个空间。
另外 redis 里的哈希,用了两个 table 存储,主要为了扩容,也就是 rehash,这样当扩容的时候双方就可以互换,redis 采用渐近式扩容,也就是每一次操作都执行两个哈希表,当新增的时候只在新表。set 数据结构可以用来存储总的点赞次数,而 zset 是一个有序链表,为了加快查询用跳表进行存储。
- 如何防止缓存雪崩:缓存要高可用,可以设置多级缓存;
- 如何预防缓存穿透:设置不同的失效时间。
<a name="vnZ1A"></a>
消息队列
<a name="FwnUS"></a>
如何保证消息的顺序
严格的一致,只能一个生产者,发送到一个 broker 上,然后只有一个队列一个消费者,但是这种模式有很多弊端,一个地方异常将阻塞整个流程,RocketMQ 将这个问题交给应用层处理,也就是发送端自己选择发送到哪个队列,例如同一个订单的消息发送到同一个队列。但是算法在其中一个队列异常的时候也会有问题。
<a name="KLMmC"></a>
如何保证消息不重复
只要网络上传输肯定会有这种问题,所以应用层最好能够支持幂等,或者用一张去重表存储每一个处理过的消息 ID。
<a name="OlDQR"></a>
发送消息流程
-
先获取 topic 对应的路由信息(路由信息会从 namesrv 返回,在客户端缓存,返回这个 topic 对应哪几个 broker 以及每个 broker 上有多少个队列);
-
如果没有获取到,可能没有 topic,需要自动创建,自动创建是客户端发信息个 namesrv,namesrv在去请求 broker,broker 创建好后返回
-
根据路由策略获取一个 queue(从所有的 queue 中根据对应的路由策略获取 queue,然后再判断这个 queue 对应的 broker 是否健康,健康就返回),这个地方就可以做到 broker 的高可用;
-
所以我们发现消息是发给哪个 broker 的哪个 queue 是在客户端发送的时候决定的,不是在生成 commitlog 之后再派发的,这样我们就可以指定都到某一个固定 queue 了;
-
消息发送的时候会构建发送请求,里面包含了消息体、队列信息和 topic 信息等,消息体里面会增加一个消息ID;
-
如果消息重试多次后还是失败就会进入死信队列,一个固定的 topic。
<a name="2kkpk"></a>
消息存储
每个 commitlog 大小为 1G,第二个文件的起始偏移量就是 1G 的 byte 大小,当根据一个偏移量获取对应某个文件的时候,根据偏移量对 1G 取余就可以,这些 commitlog 文件通过一个文件队列维护,每次写文件返回队列的最后一个文件,然后需要加锁。
创建完文件后会进行预热,预热的时候会在每一个内存页 4kb 里面写一个 byte0,让系统对缓存页缓存,防止真正写入的时候发生缺页,mmap 的机制是只会记录一个虚拟地址,当缺页时才会去获取物理内存的地址。
创建文件有两种方式:
- 一种是 FileChannel.map 获取 MappedByteBuffer;
- 另外一种是使用堆外内存池,然后 flush。
<a name="jZwye"></a>
消息的消费
一个队列只能被一个客户端消费。
当存在多个队列,但只有一个客户端的时候,这个客户端需要去 4 个队列上消费,当只有一个队列的时候只会有一个客户端可以收到消息,所以一般情况下需要客户端数量和队列数量一致,客户端一般会保存每个队列消费的位置,因为这个队列只会有一个客户端消费,所以这个客户端每次消费都会记录下队列的 offset,broker 端,也会记录同一个 grouo 消费的 offset。
MappedByteBuffer 的原理是老的 read 是先将数据从文件系统读取到操作系统内核缓存,然后再将数据拷贝到用户态的内存供应用使用,而使用 mmap 可以将文件的数据或者某一段数据映射到虚拟内存,这个时候并没有进行数据读取,当用户访问虚拟内存的地址的时候会触发缺页异常,这个时候会从底层文件系统直接将数据读取到用户态内存。
而 MappedByteBuffer 通过 FileChannel 的 map 方法进行映射的时候会返回一个虚拟地址,MappedByteBuffer就是通过这个虚拟地址配合 UnSafe 获取字节数据。
操作系统在触发缺页异常的时候会去文件系统读取数据加载到内存,这个时候一般会进行预读取,一般为 4KB,当系统下次访问数据的时候就不会发生缺页异常,因为数据已经在内存里了,为了让 MappedByteBuffer 读取文件的速度更高,我们可以对 MappedByteBuffer 所映射的文件进行预热,例如将每个 pagecache 写一个数据,这样在真正写数据的时候就不会发生缺页了。
<a name="ZF2pg"></a>
分库分表
一般三种方式:在 dao 层和 orm 层利用 mybatis 拦截器,基于 jdbc 层进行拦截重写 JDBC 接口做增强,基于数据库代理。
jdbc 代理,实现 datasource,connection,preparestatement,druid 解析 sql,生成执行计划,利用 resultset 对结果集进行合并(group by order max sum)。
分表策略,一般是哈希,要保证分库和分表的算法完全没有关联,不然会数据分布不均匀。
数据扩容的时候可以通过配置中心动态的修改写入策略,如何一开始可以先读老表,数据同时写入新表和老表,等数据迁移完成后,在读新表并双写,之后在读新表写新表。
<a name="10"></a>
唯一 id
数据库自增 id,一次取多个,单机限制,另外数据库自增 id 内部也用了个锁,只是在 sql 执行结束即使事务没提交也会释放锁。
雪花算法变种 : 15 位时间戳,4 位自增序列,2 位区分订单类型,7 位机器ID,2 位分库后缀,2 位分表后缀,共 32 位。
利用 zookeeper 的顺序节点获取自增 ID。
<a name="11"></a>
分布式事务
两阶段提交:事务管理器,资源管理器,一阶段准备,二阶段提交 (XA 方案对业务无侵入,由数据库厂商提供支持,但是性能很差)。
事物补偿
TCC :也是两阶段,第一阶段尝试锁定资源,第二阶段确认或者回滚。
设计规范:
- 业务操作分成两部,例如转账:尝试阶段为冻结余额,第二阶段提交为从冻结余额扣款,回滚为解冻;
- 事务协调器记录主事务日志和分支事务日志,支持在任意一步发生异常后进行补偿或者逆向补偿保证最终一致性;
- 并发控制,降低锁的粒度提高并发,保证两个事务间不需要加排他锁,例如热点账户的转账操作,由于第一阶段进行了冻结,所以后面的扣减余额不同事务之间没有影响;
- 允许空回滚:可能一阶段的尝试操作发生超时,然后二阶段发起回滚,回滚的时候要判断一阶段是否进行过操作,如果一阶段没有收到请求,回滚操作直接返回成功;
- 避免一阶段操作悬挂:可能一阶段超时,二阶段回滚后,一阶段的请求到达,这时候要拒绝一阶段的尝试操作;
- 幂等控制,由于第一阶段和第二阶段的操作可能都会执行多次,另外操作接口最好能提供状态查询接口供后台的补偿任务正常执行。
框架事务(seata)
-
一阶段:框架会拦截业务 sql,根据语句执行前结果生成 undolog , 根据语句执行后对结果生成 redolog , 根据数据库表名加主键生成行锁;
-
二阶段:如果事务正常结束,将删除 undolog redolog 行锁,如果事务将回滚,则执行 undolog sql , 删除中间数据,在执行 undolog 的时候会校验脏写,也就是有没有其他事务已经修改了这行记录,就用 redolog 做对比,如果出现脏写只能人工修数据 (二阶段的清理工作可以异步执行)。
开启事务的时候会向 tc 申请一个全局的事务 id,这个事务 id 会通过 rpc 框架的拦截器传入到被调用端,然后放入 threadlocal,被调用方在执行 sql 的时候会去检查一下是否在一个全局事务里。
默认的隔离级别为读未提交,因为事务一阶段已经本地事务提交而全局事务并没有完成,后续可能会回滚,其他事务可以看到这个状态,提供的读已提交的方式是通过 for update,当解析到该语句的时候会检查是否存在行锁冲突,如果存在冲突就等待直到释放。
- tm 向 tc 发起开启一个全局事务,生成一个全局唯一的 xid;
- xid 在微服务调用链上进行传递;
- rm 向 tc 注册分支事务;
- tm 向 tc 发起全局提交或者回滚决议;
- tc 向 rm 发起回滚或提交请求。
一致性消息队列:先发送半消息,如果成功了在执行本地事务,本地事务成功就提交半消息,本地事务失败就回滚半消息,如果消息队列长期没有收到确认或者回滚可以反查本地事务的状态,消费端收到消息后,执行消费端业务,如果执行失败可以重新获取,执行成功发送消费成功的确认。
MYCAT
<a name="12"></a>
CAP
- C:一致性
- A:可用性
- P:分区容忍性
可以简单地这样理解:MySQL 单机是C;主从同步复制 CP;主从异步复制 AP。
Zookeeper 选择了 P,但是既没有实现 C,也没有实现 A,而是选择最终一致性。可以在多个节点上读取,但是只允许一个节点接受写请求,其他节点接收的写请求会转发给主节点,只要过半节点返回成功就会提交。
如果一个客户端连接的正好是没有被提交的 follower 节点,那么这个节点上读取到的数据就是旧的,这样就出现了数据的不一致,所以没有完全实现 C。由于需要过半节点返回成功才提交,如果超过半数返回失败或者不返回,那么 zookeeper 将出现不可用,所以也没有完全实现 A。
当然衡量一个系统是 CP 还是 AP,可以根据它牺牲 A 更多还是牺牲 C 更多,而 ZK 其实就是牺牲了 A 来满足 C,当超过集群半数的节点宕机后,系统将不可用,这也是不建议使用 zk 做注册中心的原因。
CAP 理论只是描述了在分布式环境中一致性、可用性、分区容忍不能同时满足,并没有让我们一定要三选二,由于网络分区在分布式环境下是不可避免的,所以为了追求高可用,往往我们会牺牲强一执行,采用弱一致性和最终一致性的方案,也就是著名的 BASE 理论,而 base 理论其实是针对传统关系型数据的 ACID 而言的。
但 ACID 的提出是基于单节点下的,在分布式环境下,如何协调数据一致性,也就是在数据的隔离级别上做出取舍,即使是单机的关系型数据库为了提高性能,也就是可用性,定义了隔离级别,去打破 ACID 里面的强一致性 C,当然数据库也是为业务服务的,某些业务或者说大部分业务都没有强一致性的需求。
<a name="13"></a>
秒杀的处理
- 动静分离:ajax 不刷新页面,缓存,cdn;
- 发现热点数据:业务流程上变通让热点业务隔离出来,也通过链路监控获取一段时间的热点数据;
- 隔离:业务隔离,数据库隔离;
- 兜底方案:服务降级,限流;
- 流量削峰:排队,过滤无效请求,答题或者验证码,消息队列;
- 减库存:(下单减库存用户不付款需要回滚,付款减库存最终可能库存不足需要退款,下单后占库存一段时间后在回滚)。
正常电商采用第三种,秒杀采用第一种,不超卖的控制不用放在应用层,直接在 sql 层加 where 语句进行判断,但是 mysql 针对同一行记录也就是同一个商品的减库存,肯定会高并发下争取行锁,这将导致数据库的 tps 下降(死锁检测会遍历所有需要等待锁的连接,这个操作非常耗 cpu),从而影响其他商品的销售,所以我们可以将请求在应用层进行排队,如果份额较少可以直接舍弃,另一种方案是在数据库层排队,这种方案需要采用 mysql 的补丁。
<a name="14"></a>
docker
<a name="0K4la"></a>
namespace
docker 在创建容器进程的时候可以指定一组 namespace 参数,这样容器就只能看到当前 namespace 所限定的资源、文件、设备、网络、用户、配置信息,而对于宿主机和其他不相关的程序就看不到了,PID namespace 让进程只看到当前 namespace 内的进程,Mount namespace 让进程只看到当前 namespace 内的挂载点信息,Network namespace 让进程只看到当前 namespace 内的网卡和配置信息,
<a name="FGf6Q"></a>
cgroup
全名 linux control group,用来限制一个进程组能够使用的资源上限,如 CPU、内存、网络等,另外 Cgroup 还能够对进程设置优先级和将进程挂起和恢复,cgroup 对用户暴露的接口是一个文件系统,/sys/fs/cgroup 下这个目录下面有 cpuset,memery 等文件,每一个可以被管理的资源都会有一个文件,如何对一个进程设置资源访问上限呢?
在 /sys/fs/cgroup 目录下新建一个文件夹,系统会默认创建上面一系列文件,然后 docker 容器启动后,将进程 ID 写入 taskid 文件中,在根据 docker 启动时候传人的参数修改对应的资源文件。
<a name="j8Sxc"></a>
chroot
通过 chroot 来更改 change root file system 更改进程的根目录到挂载的位置,一般会通过 chroot 挂载一个完整的 linux 的文件系统,但是不包括 linux 内核,这样当我们交付一个 docker 镜像的时候,不仅包含需要运行的程序还包括这个程序依赖运行的这个环境,因为我们打包了整个依赖的 linux 文件系统,对一个应用来说,操作系统才是它所依赖的最完整的依赖库。
<a name="toyGw"></a>
增量层
docker 在镜像的设计中引入层的概念,也就是用户在制作 docker 镜像中的每一次修改,都是在原来的 rootfs 上新增一层 roofs,之后通过一种联合文件系统 union fs 的技术进行合并,合并的过程中如果两个 rootfs 中有相同的文件,则会用最外层的文件覆盖原来的文件来进行去重操作。
举个例子,我们从镜像中心 pull 一个 mysql 的镜像到本地,当我们通过这个镜像创建一个容器的时候,就在这个镜像原有的层上新加了一个增 roofs,这个文件系统只保留增量修改,包括文件的新增删除、修改,这个增量层会借助 union fs 和原有层一起挂载到同一个目录,这个增加的层可以读写,原有的其他层只能读,于是就保证了所有对 docker 镜像的操作都是增量。
之后用户可以 commit 这个镜像将对该镜像的修改生成一个新的镜像,新的镜像就包含了原有的层和新增的层,只有最原始的层才是一个完整的 linux fs, 那么既然只读层不允许修改,我怎么删除只读层的文件呢?这时只需要在读写层(也就是最外层),生成一个 whiteout 文件来遮挡原来的文件就可以了。
<a name="15"></a>
发布与部署
目前的大部分公司采用下面的部署方式。
- 创建 pileline 指定项目名称和对应的 tag,以及依赖工程。一个 pipeline 指一个完整的项目生命周期(开发提交代码到代码仓库、打包、部署到开发环境、自动化测试、部署到测试环境、部署到生产环境);
- 根据项目名称和 tag 去 gitlab 上拉取最新的代码(利用 java 里的 Runtime 执行 shell 脚本);
- 利用 maven 进行打包,这个时候可以为 maven 创建一个单独的 workspace (shell 脚本);
- 根据预先写好的 docfile,拷贝 maven 打的包生成镜像,并上传镜像 (shell 脚本);
- 通过 K8s 的 api 在测试环境发布升级;
- 通过灰度等方案发布到生产环境。
阿里云-云原生应用平台-基础软件中台团队(原容器平台基础软件团队)招聘高级工程师/技术专家/高级技术专家
<a name="FjldP"></a>
TL;DR
阿里云 - 云原生应用平台 - 基础软件中台团队(原容器平台基础软件团队)诚邀 Kubernetes/容器/ Serverless/应用交付技术领域专家( P6-P8 )加盟。
工作年限:建议 P6-7 三年起,P8 五年起,具体看实际能力。<br />工作地点:
- 国内:北京,杭州,深圳;
- 海外:旧金山湾区、西雅图
简历立刻回复,2~3 周出结果。节后入职。
<a name="MY7Od"></a>
工作内容
基础产品事业部是阿里云智能事业群的核心研发部门,负责计算、存储、网络、安全、中间件、系统软件等研发。而云原生应用平台基础软件终态团队致力于打造稳定、标准、先进的云原生应用系统平台,推动行业面向云原生技术升级与革命。
在这里,既有 CNCF TOC 和 SIG 联席主席,也有 etcd 创始人、K8s Operator 创始人与 Kubernetes 核心维护成员组成的、国内最顶尖的 Kubernetes 技术团队。
在这里,你将同来自全球的云原生技术领域专家们(如 Helm 项目的创始人、Istio 项目的创始人)密切合作,在独一无二的场景与规模中从事 Kubernetes、Service Mesh、Serverless、Open Application Model ( OAM )等云计算生态核心技术的研发与落地工作,在业界标杆级的平台上,既赋能阿里巴巴全球经济体,更服务全世界的开发者用户。
- 以 Kubernetes 为核心,推动并打造下一代 "以应用为中心" 的基础技术体系;在阿里经济体场景中,研发和落地“以应用为中心”的基础设施架构和基于 Open Application Model ( OAM )的下一代 NoOps 体系,让 Kubernetes 与云原生技术栈发挥出真正的价值和能量;
<br />
- 研发多环境复杂应用交付核心技术;结合阿里与生态中的核心业务场景,打造多环境复杂应用交付的业界标准与核心依赖(对标 Google Cloud Anthos 和 Microsoft Azure Arc );
<br />
- 云原生应用平台核心产品及后端架构设计与开发工作;在生态核心技术与前沿架构的加持下,在世界级云厂商的平台场景中,用技术打造持续的云产品生命力与竞争力;
<br />
- 持续推动阿里经济体应用平台架构演进,包括 Serverless 基础设施、标准云原生标准 PaaS 构建、新一代应用交付体系构建等核心技术工作。
技术要求:Go/Rust/Java/C++,Linux,分布式系统
<a name="mdg5x"></a>
简历提交
lei.zhang AT alibaba-inc.com
来源:oschina
链接:https://my.oschina.net/u/3874284/blog/3163254