1. 基础
浏览器请求一个域名的流程
- 浏览器通过udp向dns服务器请求得到ip地址
- 如果是https协议, 浏览器会向web服务器请求得到它的数字证书并验证其是否受信任, 具体看后面-[https认证流程].
- 浏览器通过tcp/ip协议发送http协议的数据包
- 服务器向浏览器相应数据
衍生出以下知识点
https认证流程
- 浏览器访问https网站, 发送随机数R1, 支持的加密算法类型, 协议版本, 压缩算法等
- 服务器返回随机数R2, 选定加密算法类型, 协议版本, 以及服务的证书.
- 浏览器验证数字证书是否有效, 若无效会给用户提示, 如何验证看下一个问题-[数字证书信任链]
- 浏览器生成随机数R3, 用刚刚得到的公钥加密并发送给服务器, 服务器用私钥解密后得到这个密码. 现在双方都拥有R1, R2, R3这三个随机数, 用这个三个数再次生成一个秘钥用于通信, 我们称这个秘钥为会话秘钥.
- 浏览器和服务器使用这个秘钥对通讯数据进行对称加密.
Q: 为什么使用对称加密?
A: 对称加密效率高, 只要密码不是固定的安全性也有保障.
数字证书信任链
如何定义一个证书是受信任的?
只要CA是受信任的那么他签发的证书也是受信任的.
那么问题就变成了如何证明这个证书是由它所说的CA签发的.
通常,证书会包含 证书的公钥(pk),证书名,签发者(CA),签发者签名(sign)
在验证证书的时候会使用CA的公钥来验签sign, 如果正确那就能说明这个证书是由CA签发的.
那么CA又怎么证明自己是受信任的呢? CA也是一个证书, 同理向上证明CA的CA是受信任的即可, 那么就有一个问题是根(Root)如何证明自己是受信任的呢? 答案是Root不需要证明, 一般来说它会内置到操作系统里, 是绝对可信的.
dns解析流程
dns服务器与浏览器通信使用UDP协议. 由于dns通信的数据量不大, 要求速度较快, 即用即丢, 对时序以及正确性没太大要求, 用UDP正合适. TCP就有点消耗服务器性能了.
- 浏览器向dns服务器发送报文请求xxx域名的ip地址, 报文见下注
- dns服务器向浏览器发送报文通知id地址.
emm 怎么感觉说了当没说... 好吧, 本来就不复杂.
举例说明一个消息队列的消息生命周期
主要考点: 怎么确保一个消息送到了.
TCP握手与挥手流程
建立TCP需要与服务器握手三次,你先告诉服务器你要给服务器发东西(SYN),服务器应答你并告诉你它也要给你发东西(SYN、ACK),然后你应答服务器(ACK),总共来回了3次,称为3次握手。
关闭TCP需要与服务器进行四次通信,你先告诉服务器你要关闭了(FIN),服务器应答你说好,我收到你的请求了(ACK),然后服务器会继续传输还没传输完的数据,完成之后告诉你说我也要关了(FIN),然后你回应(ACK),称为4次挥手。
那为什么在关闭的时候不像建立的时候服务器将(ACK和FIN)一起发送给客户端? 原因是在这个ACK和FIN之间服务器可能还要发送一些没有发送完的数据给客户端,而建立的时候没有数据传输也就不需要分开。
redis数据结构有哪些
string(字符串), hash(哈希表), list(列表), set(集合), zset(有序集合)
其中可以排序的数据结构有: list, set, zset
string
命令: SET / GET
redis最基础的数据类型, 可以存储任何数据.
一个键最大能存储512MB
hash
命令: HMSET / HGET
hash是一个键值对集合, 也称键值对. 一般用来存储一个对象.
一个hash表可以存储 2^32 -1 个键值对
list
命令: LPUSH / LPOP / BLPOP / LRANGE
list是字符串列表, 按照插入的顺序排序, 可以指定从哪个方向添加获取取出元素.
一个list可以存储 2^32 -1 个元素
set
命令: SADD / SMEMBERS / SCARD
与list不同的是set的元素不能重复
set是string类型的无序集合, 有诸多高级命令, 常用的有
- SDIFF 求两个集合的差集
- SINTER 去两个集合的交集
- SUNION 求两个集合的并集
- SMOVE 移动一个集合里的一个元素到另一个集合
- SPOP 从集合中随机删除一个元素
一个set可以存储 2^32 -1 个元素
zset
命令: ZADD / ZRANGE / ZCOUNT / ZREM / ZREMRANGEBYLEX
zset是set的排序版, 元素根据score
来排序, 如ZADD命令:
ZADD key score1 member1 [score2 member2]
ZADD boom 1 C4 2 NTN
排序就是C4在前NTN在后.
与set同样 元素不能重复, 但是score可以重复.
由于排序特性与Redis的高效, zset经常用到需要实时排名的场景, 比如游戏中的土豪玩家充钱排行.
如何使用redis实现一个分布式锁
在多个服务器上要实现同步锁稍微有点复杂, 需要借助第三方程序实现. redis是很好的选择.
方案1: string数据类型+自旋
容易想到的实现是:
func lock() {while (GET key != '') ; SET key 1 EX expire}
func unlock() {DEL key}
可以看到当第一个调用者得到锁之后, 第二个调用者在得不到锁的情况下会进入自旋状态, 直到第一个调用者unlock或者超时之后才能得到锁.
这个方案的问题是:
- 自旋会消耗过多的性能
- 容易死锁, 所以在上面的伪代码里使用了expire来避免死锁
当然还有一个更加难以解决的问题是: 当调用者1拿到锁之后, 2申请锁并进入自旋, 3再次申请锁并进入自旋, 这时当1归还锁之后, 2和3都会拿到锁. 这就违背了锁的原则: 只能同时被拿到一次.
方案2: list数据类型+BLPOP
用BLPOP解决自旋引起的性能消耗, 用list保证同时只拿得到一个锁.
而BLPOP自带参数timeout能更简单的解决死锁问题.
func init () {if (LLEN key == 0){LPUSH key 1}}
func lock() {BLPOP key }
func unlock() { LPUSH key 1}
lock在list中取出一个key, 如果没有元素则阻塞. 为了防止一开始list中没有值需要执行init初始化.
init会在list中插入一个元素, 为保证只有一个锁需要先判断这个list的长度.
细心的朋友可能会发现一个问题, init中会出现并发问题.
这个方案要注意的问题是lock之后一定要调用unlock, 不然之后的lock都会有问题.
emm... 感觉这个方案还是有问题
方案3: baidu+google
作者放弃了自己实现, 于是百度找到方案, 方案通过
ping的原理, 使用的协议
mysql 读写锁
什么情况下mysql的写操作是并发安全的
mysql 几种引擎的区别
mysql 几种事务的区别
短连接原理及实现
解释线程/进程
什么是并发模型, 有哪些?
常见的GC算法
基本
- 标记清除法: 递归标记被引用的对象, 将没有标记到的对象进行垃圾回收
- 复制收集方式: 和标记清除法类似, 不过不用标记, 而是将使用到的对象复制到新空间, 然后将旧空间一并回收
- 引用计数方式: 对一个对象的引用计数, 当为0时就回收, 要求在每次被引用和取消引用的时候计数必须精确, 所以相对容易出现bug, 而且对于循环引用的问题不能解决. 由于计数不是并发安全的, 所以这个方法也不能很好的实现并行回收.
高级
高级方式是对基本算法的融合
- 分代回收: 将对象根据生命时间划分为<新生代>与<老生代>, 一般来说越新的对象越容易被清除掉. 所以在GC的时候, 优先处理新生代能带来很好的效率提升.
- 增量回收: 在应用进行中连续进行GC, 能很好的控制每次的GC时间. 相当于将一个大GC分成了几个小GC.
- 并行回收: 利用多核优势将GC与原程序尽量必行. 但也不能做到不暂停原程序.
总结
任何GC算法都是跟踪回收和计数回收两种思路的结合.
go使用的GC算法
2. 算法
常见的排序算法有哪些, 如何选用合适的算法?
3. Golang
goroutine与线程/进程区别
slice/array的区别
slice的底层是array, 结构如下
struct slice {
cap int
len int
ptr *[]T
}
其中ptr指向的就是底层的数组.
append在什么情况下会扩容, 如何扩容, 扩容会发生什么
当slice的cap不能再存放这么多元素的时候就会扩容, 扩张后的容量是原cap的两倍.
注: 好像不会一直是两倍, 在大于1024长度的时候会优化, 比如只增长1/4*1024. 到底是不是这样请google吧, 我暂时也没找到资料.
扩容的时候原slice指向的array会被释放, 并重新new了一个array存放元素, 所以append返回的slice有时候(没扩容)是原地址, 有时候(扩容后)不是原地址, 这点需要注意~
实现消费者/生产者模型
多个生产者+多个消费者模型使用GO很好实现, 就是使用多个goroutine和一个chan.
func main(){
c:=make(chan int,10)
}
chan由谁关闭?
对比 Actor 和 CSP 模型
4. Js
现代框架如Vue, React与JQuery的区别
Vue响应式数据的实现原理
Vuex设计思路
未完待续
网友评论