美文网首页Golang开发相关我爱编程
作为后端, 面试官可能会问你的问题

作为后端, 面试官可能会问你的问题

作者: bysir | 来源:发表于2018-03-22 17:13 被阅读108次

    1. 基础

    浏览器请求一个域名的流程

    1. 浏览器通过udp向dns服务器请求得到ip地址
    2. 如果是https协议, 浏览器会向web服务器请求得到它的数字证书并验证其是否受信任, 具体看后面-[https认证流程].
    3. 浏览器通过tcp/ip协议发送http协议的数据包
    4. 服务器向浏览器相应数据

    衍生出以下知识点

    https认证流程
    1. 浏览器访问https网站, 发送随机数R1, 支持的加密算法类型, 协议版本, 压缩算法等
    2. 服务器返回随机数R2, 选定加密算法类型, 协议版本, 以及服务的证书.
    3. 浏览器验证数字证书是否有效, 若无效会给用户提示, 如何验证看下一个问题-[数字证书信任链]
    4. 浏览器生成随机数R3, 用刚刚得到的公钥加密并发送给服务器, 服务器用私钥解密后得到这个密码. 现在双方都拥有R1, R2, R3这三个随机数, 用这个三个数再次生成一个秘钥用于通信, 我们称这个秘钥为会话秘钥.
    5. 浏览器和服务器使用这个秘钥对通讯数据进行对称加密.

    Q: 为什么使用对称加密?
    A: 对称加密效率高, 只要密码不是固定的安全性也有保障.

    数字证书信任链

    如何定义一个证书是受信任的?
    只要CA是受信任的那么他签发的证书也是受信任的.

    那么问题就变成了如何证明这个证书是由它所说的CA签发的.

    通常,证书会包含 证书的公钥(pk),证书名,签发者(CA),签发者签名(sign)

    在验证证书的时候会使用CA的公钥来验签sign, 如果正确那就能说明这个证书是由CA签发的.

    那么CA又怎么证明自己是受信任的呢? CA也是一个证书, 同理向上证明CA的CA是受信任的即可, 那么就有一个问题是根(Root)如何证明自己是受信任的呢? 答案是Root不需要证明, 一般来说它会内置到操作系统里, 是绝对可信的.

    dns解析流程

    dns服务器与浏览器通信使用UDP协议. 由于dns通信的数据量不大, 要求速度较快, 即用即丢, 对时序以及正确性没太大要求, 用UDP正合适. TCP就有点消耗服务器性能了.

    1. 浏览器向dns服务器发送报文请求xxx域名的ip地址, 报文见下注
    2. 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设计思路

    未完待续

    相关文章

      网友评论

        本文标题:作为后端, 面试官可能会问你的问题

        本文链接:https://www.haomeiwen.com/subject/bgaaqftx.html