Tcp协议为什么是可靠的协议?
拥塞控制和滑动窗口是怎么做的
http状态码的含义
三次握手和四次挥手的原理,状态转变
为什么要三次握手,四次挥手
Rpc是什么
rpc原理
Rpc怎么用
mysql的acid英文全称是什么
分别怎么保证的?
为什么redo undo能保障原子性
mysql
Cap是什么?
Mysql隔离级别有哪些,分别如何保障的。举个例子
mvcc原理
Mysql常用的存储引擎有哪些
b+树原理
Mysql和redis如何保障数据的一致性
存储时候先存储哪个,删除时候先删除哪个
(先存db,再存cache,因为db没有,cache肯定没有,数据是一致的
先删db,再删cache,数据保持最终一致 还是强一致。)
Redis有哪些数据结构
什么场景用set和zset
redis里zset的z是什么意思,zset和普通set的区别是什么
如何用redis实现一个异步队列
如何用redis实现一个延时队列
Redis哪些操作是原子性的
Setnx 如何保证原子性的
Key和超时时间如何保证原子性
Redis数据持久化的方式有哪些,分别如何做持久化的,aof和rdb分别有何优缺点
为什么选择用redis?(缓存,举例说明缓存哪些东西)
Kafka发布订阅模式,原理,还有其他模式吗
Kafka 如何保障高性能,高吞吐,低延迟
零拷贝技术原理是什么,用来做什么。
为什么要用kafka呢
kafka索引失效
Map和slice是并发安全的吗
Slice和数组有啥区别
Golang如何实现并发
select关键字用于什么场景
等待携程处理完再运行之后的代码
使用waitgroup
defer panic和recover怎么使用
强一致性
最终一致性
(有些石板就是为了铺路,让你走过这段路,意义仅此而已,也是必须的,不是说你的目的就是石板)
java spring是用来做什么的
部分反转链表
两个有序链表合并成一个有序琏表
数组的前m位和后n位换位置
堆排序 桶排序 快排的原理
二分查找
前序遍历改为中序遍历
递归
分治
监控用的什么开源工具
1、push中台用户中心的东西看看
2、kafka源码
3、golang 高并发 继承 channel
1)按序输出并发打印结果,2次
2)按优先级输出三个优先级的事物
4、redis源码
2)redis 实现队列,用什么数据结构。2次
5、经历再修改一下,拉新一体化不写了
参考文章:https://www.cnblogs.com/williamjie/p/9959406.html
https://juejin.cn/post/6974539862800072718
数组 Arrays
数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的入参传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。
初始化
[5] int {1,2,3,4,5}
长度为5的数组,其元素值依次为:1,2,3,4,5
[5] int {1,2}
长度为5的数组,其元素值依次为:1,2,0,0,0 。在初始化时没有指定初值的元素将会赋值为其元素类型int的默认值0,string的默认值是""
[...] int {1,2,3,4,5}
长度为5的数组,其长度是根据初始化时指定的元素个数决定的
[5] int { 2:1,3:2,4:3}
长度为5的数组,key:value,其元素值依次为:0,0,1,2,3。在初始化时指定了2,3,4索引中对应的值:1,2,3
[...] int {2:1,4:3}
长度为5的数组,起元素值依次为:0,0,1,0,3。由于指定了最大索引4对应的值3,根据初始化的元素个数确定其长度为5
数组通过下标访问元素,可修改其元素值
切片 Slices
数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型Slices切片,与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。切片中有两个概念:一是len长度,二是cap容量,长度是指已经被赋过值的最大下标+1,可通过内置函数len()获得。容量是指切片目前可容纳的最多元素个数,可通过内置函数cap()获得。切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。
切片可以通过数组来初始化,也可以通过内置函数make()初始化 .初始化时len=cap,在追加元素时如果容量cap不足时将按len的2倍扩容查看示例代码,在线运行示例代码
s :=[] int {1,2,3 }
直接初始化切片,[]表示是切片类型,{1,2,3}初始化值依次是1,2,3.其cap=len=3
s := arr[:]
初始化切片s,是数组arr的引用
s := arr[startIndex:endIndex]
将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片
s := arr[startIndex:]
缺省endIndex时将表示一直到arr的最后一个元素
s := arr[:endIndex]
缺省startIndex时将表示从arr的第一个元素开始
s1 := s[startIndex:endIndex]
通过切片s初始化切片s1
s :=make([]int,len,cap)
通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
赋值与使用
切片是引用类型,在使用时需要注意其操作。查看示例代码,在线运行示例代码切片可以通过内置函数append(slice []Type,elems ...Type)追加元素,elems可以是一排type类型的数据,也可以是slice,因为追加的一个一个的元素,因此如果将一个slice追加到另一个slice中需要带上"...",这样才能表示是将slice中的元素依次追加到另一个slice中。另外在通过下标访问元素时下标不能超过len大小,如同数组的下标不能超出len范围一样。
s :=append(s,1,2,3,4)
s :=append(s,s1...)
golang 的 slice 和 map 都是并发不安全的
如果同时多个 goroutine 对 slice 和 map 进行操作.
slice 在并发中不会报错但可能丢失数据(并发添加数据)、而 map 则可能出现直接报错"concurrent map read and map write" .
https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html
在Go语言中,我们通常会用到panic和recover来抛出错误和捕获错误,这一对操作在单协程环境下我们正常用就好了,并不会踩到什么坑。但是在多协程并发环境下,我们常常会碰到以下两个问题。假设我们现在有2个协程,我们叫它们协程A和B好了:
如果协程A发生了panic,协程B是否会因为协程A的panic而挂掉?
如果协程A发生了panic,协程B是否能用recover捕获到协程A的panic?
答案分别是:会、不能。
那么下面我们来一一验证,并给出在具体的业务场景下的最佳实践。
golang
1、发送http请求的过程以及用到的协议,传输层用到的是tcp还是udp
TCP协议与UDP协议作为传输层最常用的两种传输协议,这两种协议都是使用IP作为网络层协议进行传输
2、tcp与udp协议以及三次握手 四次挥手的详细过程 ?传输的具体东西
3、http协议有哪些 ?区别是啥?你们公司开发用的是什么http协议?https的加密过程?请求种类
https://www.cnblogs.com/kubidemanong/p/9390021.html https
https://mp.weixin.qq.com/s?__biz=MzA5MzY4NTQwMA==&mid=2651008910&idx=4&sn=049fa10eecb3de61b98d7f58bbb78f74&chksm=8bad9c79bcda156fc2097361977595d4a19a56ded9c8b1ca55890bfe800db0551b1723f2d7b9&mpshare=1&scene=1&srcid=09036gz7lxSUvWuTL0363d7F&sharer_sharetime=1567513711371&sharer_shareid=5feaf6ea5a6234f11d5d06cc03167db3&key=48945bec6f7dd4310e8f3763c85e244ec4ddcda9d15c8f967f9370e8e25223b37631bb7e9289506af2b82e004a6160924f162a6c494870fa732697d83d65df61ae9ee01a6f8f36c06071cb4bfd9029ec&ascene=1&uin=NjY4MzA3OTEy&devicetype=Windows%2010&version=62060841&lang=zh_CN&pass_ticket=GJNVu/qLZV5ey1Lfbyvmr74GjG7YL77H8P2wrft0%2bA52lzJQBNPJ%2b6zw98q3%2bvH%2b
Get:请求指定的页面信息,并返回实体主体
Post:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中,post请求可能会导致新的资源的建立和/或已有资源的修改
Head:类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
Options:允许客户端查看服务器的性能
Put:从客户端向服务器传送的数据取代指定的文档的内容
Delete:请求服务器删除指定的页面
Trace:回显服务器收到的请求,主要用于测试或诊断
Connect:http/1.1协议中预留给能够将连接改为管道方式的代理服务器
4、mysql的索引什么时候不能生效?
https://blog.csdn.net/qq_34258346/article/details/80272198?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161396611516780266246912%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161396611516780266246912&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-7-80272198.first_rank_v2_pc_rank_v29_10&utm_term=mysql%E7%B4%A2%E5%BC%95%E5%A4%B1%E6%95%88
5、mysql事务的特性?mysql的原子性是怎么做到的?
https://blog.csdn.net/xiaosuanmiao123/article/details/82661115?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161397444816780266279437%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=161397444816780266279437&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-5-82661115.first_rank_v2_pc_rank_v29_10&utm_term=mysql%E4%BA%8B%E5%8A%A1%E7%9A%84%E7%89%B9%E6%80%A7
https://blog.csdn.net/weixin_34342992/article/details/91677242?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=8561fd55-8094-44b7-8c98-25e6ad05d8e6&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
6、redis用来做什么在项目里,redis的值更新怎么做的?redis宕机了之后怎么处理?mysql记录操作日志的日志名称
https://blog.csdn.net/weixin_44052482/article/details/104976213?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161397614016780271589467%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161397614016780271589467&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-1-104976213.first_rank_v2_pc_rank_v29_10&utm_term=redis%E7%94%A8%E5%9C%A8%E6%9B%B4%E6%96%B0%E4%BB%80%E4%B9%88%E9%A1%B9%E7%9B%AE%E9%87%8C
提供了 AOF 和 RDB 两种数据的持久化保存方式,保证了 Redis 重启后数据不丢失
https://blog.csdn.net/m0_37609579/article/details/89786703?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161397614016780271589467%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161397614016780271589467&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-3-89786703.first_rank_v2_pc_rank_v29_10&utm_term=redis%E7%94%A8%E5%9C%A8%E6%9B%B4%E6%96%B0%E4%BB%80%E4%B9%88%E9%A1%B9%E7%9B%AE%E9%87%8C
https://blog.csdn.net/Y0Q2T57s/article/details/80682013
https://blog.csdn.net/Y0Q2T57s/article/details/80682013
redis的定期删除key
项目里怎么处理的房间已经被预定了?怎么处理比较好?
7、protobuf和json的区别?为什么比json快?json也支持多种语言啊?json都不用生成代码呢?
8、docker在容器关闭之后,怎么删除那些容器的文件的
9、k8s里面的资源分配
介绍一下你的项目?你的重点项目?
Golang
go的调度go struct能不能比较go defer(for defer)select可以用于什么context包的用途client如何实现长连接主协程如何等其余协程完再操作slice,len,cap,共享,扩容map如何顺序读取实现set实现消息队列(多生产者,多消费者)大文件排序基本排序,哪些是稳定的http get跟headhttp 401,403http keep-alivehttp能不能一次连接多次请求,不等后端返回tcp与udp区别,udp优点,适用场景time-wait的作用数据库如何建索引孤儿进程,僵尸进程死锁条件,如何避免linux命令,查看端口占用,cpu负载,内存占用,如何发送信号给一个进程git文件版本,使用顺序,merge跟rebasemysql索引为什么要用B+树?
Golang-
网络相关
1. TCP 和 UDP 的区别是什么?
2. TCP 的三次握手解释一下,为什么是三次握手?(答三点)
3. UDP 在什么时候使用?
4. 浏览器输入一个域名之后会发生什么?
5. 登陆 baidu 之后,下次打开浏览器就不用再登陆了,怎么做到?
6. https 和 http 的区别是什么?公钥私钥是怎么来的?浏览器如何判断公钥的正确性?
数据库相关
1. 事务属性 ACID 都是什么?
2. 乐观锁和悲观锁的区别?乐观锁怎么判断一致性?
3. 用过哪些存储引擎?Innodb 和 Myiasm 的区别是什么?
4. 你们有没有用到其他类型的数据库?(比如mongodb,我说没有,面试官就没让我回答,应该多看看)
其他
如何防止死锁,可以举例各种语言中都是怎么做的?(1. 不可以复制锁,2. 还有其他的么)
算法题
蛇形打印一棵二叉树上的 value
二面:
自我介绍一下
说一下有挑战的项目RPC 相关:
1. RPC 调用中,客户端都发生了什么,要完成哪些工作?(serialize,connection,parse,我主要说了 serialize 中的 protobuf)
2. RPC 客户端如何处理请求失败?(面试官想问客户端如何实现失败重试机制,我引申了 nginx 中的重试方式,提到了幂等性重试)
架构相关:
1. 水平拓展一个新的机器时,都有哪些操作或步骤?(拉取机器的镜像,下载代码,启动服务)
2. 启动服务的时候,都有个启动的时间,这段时间机器可以有流量么?如果没有流量,什么时候开始可以有流量?用什么机制让 Haproxy 开始给新机器发流量?(应该就是服务发现机制)
3. 同上,更新代码部署时,有个重新启动的时间?这段时间有流量么?如果没有,什么时候恢复流量,怎么恢复流量?比如如何在 Haproxy 注册部署好的机器的 ip ?(我谈到了蓝绿部署,在 proxy 中删掉再恢复机器的 ip)
算法题:有 10 万个数,我们要找出最大的 1000 个数,如何做?(因为只要 1000 个数,甚至不关心它们的顺序,可以用比 nlogn 排序更快的方法,因为可以不用对 10 万个数全都排序) 面试官提示,可以用小根堆,取出前 1000 个数组成一个小根堆,后面不断跟 root 比较,小的抛弃,大的插入堆,并替换 root。
1、map在不同的语言里有几种实现方式?
深入理解HashMap和TreeMap的区别https://www.cnblogs.com/flydean/p/hashmap-vs-treemap.html
1)TreeMap是排序的而HashMap不是。
2)
1)hashmap
2)树
2、kafka在重新分配任务的时候,使用的算法是什么?
3、并发模型都有哪些?
4、golang里的并发模型是如何做的?携程和线程在操作系统层面是如何切换的?
5、golang里的多态是怎么实现的?
6、golang的面向对象怎么做?
7、b+树的结构
8、b树的结构
9、慢查询如何优化?
10、map
1、zset基础命令
https://cloud.tencent.com/developer/article/1402590
https://juejin.cn/post/6844903701547581454
https://juejin.cn/post/6867315947770740744
1、数据结构
2、添加zadd
3、删除zrem
4、返回key的个数zcard
5、ZREVRANGE
参考文章:https://juejin.cn/post/6945282409248063518
、
2、
3、redis变慢
https://juejin.cn/post/6921377202223185933
参考文章:
内容比较全:https://juejin.cn/post/6985795301369970702
https://juejin.cn/post/6983845888121241608
链接:https://juejin.cn/post/6984206046487117837
1、持久化
2、lua脚本
3、分布式锁
https://juejin.cn/post/6971240328581873701 写的比较好
https://juejin.cn/post/6984226247484112927
定义:如图
优点:可以加锁
缺点:加锁不彻底,有漏洞
解决方法:redission 深度4
redisson原理:
4、redis使用可能的问题
https://juejin.cn/post/6934930917500649486 这篇也不错 缓存雪崩、穿透、击穿
1)缓存穿透
问题表现:redis中查不到数据,转而去查mysql,没有起到因缓存而提升系统性能的目的
解决方法:布隆过滤器,存储已有的数据,查询时候先查布隆过滤器,看是否是已有数据,不是的话直接返回,是的话,再去查询缓存以及数据库了。
2)缓存雪崩
问题表现:redis中大量key同一时间失效,转而去查mysql
解决方法:给不同的key设置不同的过期时间
3)缓存击穿
问题表现:redis中查不到数据,转而去查mysql,结果mysql也查不到数据
解决方法:
5、布隆过滤器
布隆过滤器(Bloom Filter)概念
原理:当一个元素加入到布隆过滤器的集合中时,通过 K 个 hash 函数将这个元素映射成为一个数组中的下标,并将数组下标所在的值设置为 1,在检索值是否存在时,只需要通过 K 次 hash 函数找到对应的坐标,并判断这些元素的值是否为 1,若全部为 1 则有可能存在,若全为 0 则肯定不存在。
应用场景:1)它的存在时为了检索一个元素是否存在一个集合(并不是 Redis 中的集合)中,它的空间效率和时间效率都远超过一般的算法。2)缓存穿透。3)数据去重
问题:1)数据量庞大的话会有一定的误判率。2)就是无法删除。
解决方法:1)布隆过滤器是有一定的错误率的,若我们对所存的值进行预估,设置存储数量的时候尽量设置的大一些,这样可能会避免错误的判断。2)布隆过滤器的缺点其一是对已经增加的值无法进行删除,那么有一个过滤器可以解决这个问题,那就是布谷鸟过滤器,功能几乎与布隆过滤器一致,但是布谷鸟过滤器是可以将已增加的元素进行删除。感兴趣的可以看一下。
深度普通为4,5开始是有深度的开始。
1、 几百完数据 设计一个榜单 如何设计 榜单根据送礼数评出,保障3s内查到数据。如何设计redis 。(往zset方向想)
2、1亿数据 设计榜单,保障3s内查到,如何设计。
3、redis主从同步的几种模式
4、zset的跳表和b+树有啥区别
5、分布式锁,如何设置和释放。setnx del 根据过期时间
6、你觉得比较深入的一门技术是啥,最后聊了redis的应用场景,出了道系统设计就是1+2
7、布隆过滤器,可以存储几亿数据吗?
可以,但是数据过多效率就会变慢,只好查询kv
8、缓存穿透、缓存击穿、缓存雪崩
https://juejin.cn/post/6844903813904596999
6、redis主从复制
https://juejin.cn/post/6981744631000072205
7、redis读写分离
8、redis主从同步
https://blog.csdn.net/weixin_42711549/article/details/83061052
主从同步方式:1)全量策略。2)增量策略
Redis主从复制可以根据是否是全量分为全量同步和增量同步。
全量同步:https://juejin.cn/post/6844903766534127624
全量同步,引发系统过载问题
https://hellokangning.github.io/zh/post/100-server-load-because-of-redis-sync/
一、降级:降级一般而言指的是我们自身的系统出现了故障而降级。
保证核心功能的可用性,而选择性的降低一些功能的可用性,或者直接关闭该功能。
丢车保帅
举例:比如贴吧类型的网站,当服务器吃不消的时候,可以选择把发帖功能关闭,注册功能关闭,改密码,改头像这些都关了,为了确保登录和浏览帖子这种核心的功能。
二、熔断:熔断一般是指依赖的外部接口出现故障的情况断绝和外部接口的关系。
持续监控,一旦超过某个域值,所有的发挥都将返回错误
表现1:1s内进入10请求,平均响应时间大于某个阈值,则判为熔断。(平均响应时间)
表现2: 1s内进入10请求,6个以上请求都返回异常,则判为熔断。(异常数)
表现3: 1s内进入10请求,60%个以上请求都返回异常,则判为熔断。(异常比例)
解决方法:周期内获取机器负载情况,内存使用率,达到阈值,则触发熔断保护,抛弃一部分随机流量。
降级一般而言指的是我们自身的系统出现了故障而降级。而熔断一般是指依赖的外部接口出现故障的情况断绝和外部接口的关系。
例如你的A服务里面的一个功能依赖B服务,这时候B服务出问题了,返回的很慢。这种情况可能会因为这么一个功能而拖慢了A服务里面的所有功能,因此我们这时候就需要熔断!即当发现A要调用这B时就直接返回错误(或者返回其他默认值啊啥的),就不去请求B了。我这还是举了两个服务的调用,有些那真的是一环扣一环,出问题不熔断,那真的是会雪崩。
当然也有人认为熔断不就是降级的一种的,我觉得你非要说熔断也属于一种降级我也没法反驳,但是它们本质上的突出点和想表达的意思还是有一些不同的。
那什么时候熔断合适呢?也就是到达哪个阈值进行熔断,5分钟内50%的请求都超过1秒?还是啥?参考降级。
三、限流:
限流,直接跟请求说对不起再见!也就是系统规定了多少承受能力,只允许这么些请求能过来,其他的请求就说再见了。
一般限制的指标有:请求总量或某段时间内请求总量。
请求总量:比如秒杀的,秒杀100份产品,我就放5000名进来,超过的直接拒绝请求了。
某段时间内请求总量:比如规定了每秒请求的峰值是1W,这一秒内多的请求直接拒绝了。咱们下一秒再见。
限流策略:1)令牌筒算法。2)漏筒算法。3)滑动窗口
参考文章:
https://my.oschina.net/dyyweb/blog/1863522
https://juejin.cn/post/6844903838231576589
当我们的系统的访问量突然剧增,大量的请求涌入过来,最典型的就是秒杀业务了,我们可能会知道会有一波高峰,这时候该如何处理?
而且现在很多情况我们还需要调用第三方接口例如支付等,因此我们还得考虑如果第三方那边出问题了,返回异常的慢,我们系统该如何处理。
常见的处理方式有三种:降级、熔断、限流。
降级
降级也就是服务降级,当我们的服务器压力剧增为了保证核心功能的可用性,而选择性的降低一些功能的可用性,或者直接关闭该功能。这就是典型的丢车保帅了。就比如贴吧类型的网站,当服务器吃不消的时候,可以选择把发帖功能关闭,注册功能关闭,改密码,改头像这些都关了,为了确保登录和浏览帖子这种核心的功能。
一般而言都会建立一个独立的降级系统,可以灵活且批量的配置服务器的降级功能。当然也有用代码自动降级的,例如接口超时降级、失败重试多次降级等。具体失败几次,超时设置多久,由你们的业务等其他因素决定。开个小会,定个值,扔线上去看看情况。根据情况再调优。
熔断
降级一般而言指的是我们自身的系统出现了故障而降级。而熔断一般是指依赖的外部接口出现故障的情况断绝和外部接口的关系。
例如你的A服务里面的一个功能依赖B服务,这时候B服务出问题了,返回的很慢。这种情况可能会因为这么一个功能而拖慢了A服务里面的所有功能,因此我们这时候就需要熔断!即当发现A要调用这B时就直接返回错误(或者返回其他默认值啊啥的),就不去请求B了。我这还是举了两个服务的调用,有些那真的是一环扣一环,出问题不熔断,那真的是会雪崩。
当然也有人认为熔断不就是降级的一种的,我觉得你非要说熔断也属于一种降级我也没法反驳,但是它们本质上的突出点和想表达的意思还是有一些不同的。
那什么时候熔断合适呢?也就是到达哪个阈值进行熔断,5分钟内50%的请求都超过1秒?还是啥?参考降级。
限流
上面说的两个算是请求过来我们都受理了,这个限流就更狠了,直接跟请求说对不起再见!也就是系统规定了多少承受能力,只允许这么些请求能过来,其他的请求就说再见了。
一般限制的指标有:请求总量或某段时间内请求总量。
请求总量:比如秒杀的,秒杀100份产品,我就放5000名进来,超过的直接拒绝请求了。
某段时间内请求总量:比如规定了每秒请求的峰值是1W,这一秒内多的请求直接拒绝了。咱们下一秒再见。
作者:yes的练级攻略
链接:https://juejin.cn/post/6844903838231576589
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。