golang开发面经字节跳动(三轮技术面)(代码片段)

小生凡一 小生凡一     2022-12-07     190

关键词:

文章目录

写在前面

整体面试下来,感觉其实字节对于语言本身并没有很多的涉及,更加注重基础,比如数据结构与算法,计算机网络,组成原理,操作系统,数据库等等,语言本身并没有涉及太多。

这里就省去了一些我简历上的问题,也就是深挖项目。

笔试

一面

epoll、select、poll 区别

select 机制刚开始的时候,需要把 fd_set 从用户空间拷贝到内核空间,并且检测的 fd 数是有限制的,由 FD_SETSIZE 设置,一般是1024。数组实现

poll 的实现和 select 非常相似,只是描述 fd集合 的方式不同,poll使用 pollfd结构 而不是 select的 fd_set 结构,其他的都差不多。链表实现

epol l引入了 epoll_ctl系统调用,将高频调用的 epoll_wait 和低频的 epoll_ctl 隔离开。epoll_ctl 通过(EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL)三个操作来分散对需要监控的fds集合的修改,做到了有变化才变更,将select或poll高频、大块内存拷贝(集中处理)变成epoll_ctl的低频、小块内存的拷贝(分散处理),避免了大量的内存拷贝。 epoll使用 红黑树 来组织监控的fds集合

epoll 的水平触发和边缘触发的区别

Edge Triggered (ET) 边沿触发:

  1. socket 的接收缓冲区状态变化时触发读事件,即空的接收缓冲区刚接收到数据时触发读事件。
  2. socket 的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件。
  3. 仅在缓冲区状态变化时触发事件。

Level Triggered (LT) 水平触发:

  1. socket 接收缓冲区不为空,有数据可读,则读事件一直触发。
  2. socket 发送缓冲区不满可以继续写入数据,则写事件一直触发。

TCP 的流量控制

因为我们总希望数据传输的更快一些。但如果发送方把数据发得过快,接收方就可能来不及接收,这就会造成数据的丢失。流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。

利用滑动窗口机制可以很方便地在 TCP连接上实现发送方流量控制。通过接收方的确认报文中的窗口字段,发送方能够准确地控制发送字节数。

为什么有了流量控制还要有拥塞控制?

流量控制是避免发送方的数据填满接收方的缓存,但并不知道网络中发生了什么。

计算机网络是处在一个共享的环境中。因此也有可能会发生网络的拥堵。在网络出现拥堵时,如果继续发送大量的数据包,可能会导致数据包时延丢失,这时 TCP 就会重传数据,但是⼀重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包

控制的目的就是避免发送方的数据填满整个网络。为了在发送方调节所要发送数据的数据量,定义了⼀个叫做「拥塞窗口」的概念。拥塞窗口 cwnd 是发送方维护的⼀个的状态变量,它会根据网络的拥塞程度动态变化的。

TCP 不是可靠传输吗?为什么会丢包呢?

TCP的可靠传输是会在丢包的时候进行重传,来形成可靠的传输,丢包这是网络的问题,而不是TCP机制的问题。

那你介绍一下拥塞控制的算法?

拥塞控制一共有四个算法:

  1. 慢启动:TCP 在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是⼀点⼀点的提高发送数据包的数量。慢启动的算法规则:当发送方每收到⼀个 ACK,拥塞窗⼝ cwnd 的大小就会加 1
  2. 拥塞避免:当拥塞窗口 cwnd「超过」慢启动门限 ssthresh 就会进⼊拥塞避免算法。那么进⼊拥塞避免算法后,它的规则是:每当收到⼀个 ACK 时,cwnd 增加 1/cwnd
  3. 快重传:当接收方发现丢了⼀个中间包的时候,发送三次前⼀个包的ACK于是发送端就会快速地重传,不必等待超时再重传。
  4. 快恢复:快重传和快恢复⼀般同时s使用,快速恢复算法是认为,你还能收到 3 个重复的 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。进⼊快速恢复之前, cwnd 和 ssthresh 已被更新了:cwnd = cwnd/2 ,也就是设置为原来的⼀半; ssthresh = cwnd 。

进程、线程的区别

进程线程
系统中正在运行的一个应用程序系统分配处理器时间资源的基本单元
程序一旦运行就是进程进程之内独立执行的一个单元执行流
资源分配的最小单位程序执行的最小单位

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些 要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程

Go里面GMP模型是怎么样的?

G:表示goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;另外G对象是可以重用的。
P:表示逻辑processor,P 的数量决定了系统内最大可并行的 G 的数量(前提:系统的物理cpu核数 >= P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。
M:M 代表着真正的执行计算资源,物理 Processor。

G 如果想运行起来必须依赖 P,因为 P 是它的逻辑处理单元,但是 P 要想真正的运行,他也需要与 M 绑定,这样才能真正的运行起来,P 和 M 的这种关系就相当于 Linux 系统中的用户层面的线程和内核的线程是一样的

算法:旋转矩阵,牛客上写过,easy,秒

二面

上来就一个算法,结束又一个算法,难受。。

如何用栈实现队列

我们可以通过切片来模拟出队列。
只需要完成简单的 push,pop,再判断是否为空即可!

package test_queue

import (
	"fmt"
	"testing"
)

//结构体,包含两个一维切片
type GoQueue struct 
	stack []int
	back  []int


//初始化,
func NewQueue() GoQueue 
	return GoQueue
		stack: make([]int, 0),
		back:  make([]int, 0),
	


func (q *GoQueue) Push(x int) 
	q.stack = append(q.stack, x)


func (q *GoQueue) Pop() int 
	if len(q.back) == 0 
		for len(q.stack) != 0 
			val := q.stack[len(q.stack)-1]
			q.stack = q.stack[:len(q.stack)-1] //切片,更新栈
			q.back = append(q.back, val)
		
	
	val := q.back[len(q.back)-1]
	q.back = q.back[:len(q.back)-1]
	return val


func (q *GoQueue) Empty() bool 
	return len(q.back) == 0 && len(q.stack) == 0


func TestQueue(t *testing.T) 
	q := NewQueue()
	q.Push(1)
	q.Push(2)
	q.Push(3)
	for !q.Empty() 
		fmt.Println(q.Pop())
	

如何判断一个链表有没有环?

  1. 用hash来判断是否存在相同的值,也就是环。
  2. 快慢指针:快指针走两步,慢指针走一步,最终会相遇。

那为什么快慢指针一定能够相遇?

因为当快指针出现在慢指针后面之后,每一次快指针往前走两步、慢指针往前走一步,相当于快指针和慢指针之间的相对距离减少1步。当快指针刚刚绕到慢指针后面时,快指针离慢指针有 n 步。那么,对于接下来的每一次快指针往前走两步、慢指针往前走一步,快指针和慢指针之间的距离由 n 步变成 n-1 步、由 n-1 步变成 n-2 步、……、由 3 步变成 2 步、由 2 步变成 1 步、由 1 步变成 0 步。最终相遇。

  • 快慢指针第一次相遇的时候,慢指针少走了多少?

我们设从起点到 X 点的距离是 x,因此当在第一次相遇的时候,在n点,慢指针的路程是 x+n ,而因为快指针的速度是慢指针的一倍,它们从头开始,运动时间就是完全一致的,因此当二者发生相遇的时候,有如下几点值得注意:

  1. 当二者发生相遇时,慢指针一定已经进入了环中;
  2. 快指针一定先于慢指针进入环中;
  3. 快指针移动过的路程累计是慢指针的1倍,也就是2 * ( x + n ),其中早在路程为x时,快指针已经进入了环,它在环中运动了 x+2n 的路程。

在 n 点时,慢指针的路程是 x+n,快指针的路程是2*(x+n),说明此时快指针比慢指针多走了 x+n,这同时也说明,从 n 处开始,继续往后推移 x+n 个距离,可以再次回到Z点,因此 x+n 的距离至少是环巡距离的一倍。慢指针和快指针在环中第一次相遇时的路程差值,是环路程的倍数。
所以慢指针少走了环巡距离的一倍。

你用的是 mysql 是吧,那 B树 和 B+树 的区别是?

B 树

B+树

  1. B+ 树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为log (n)。而 B-树 查询时间复杂度不固定,与 key 在树中的位置有关,最好为 O(1)
  2. B+ 树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而 B- 树 每个节点 key 和 data 在一起,则无法区间查找。同时我们访问某数据后,可以将相近的数据预读进内存,这是利用了空间局部性
  3. B+ 树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确。

介绍一下死锁产生的必要条件

互斥条件,请求和保持条件,不剥夺条件,环路等待条件。

如何实现互斥锁?

一个互斥锁需要有阻塞和唤醒功能,所以需要一下几个情况。

  1. 需要有一个标记锁状态的 state 变量。
  2. 需要记录哪个线程持有了锁。
  3. 需要有一个队列维护所有的线程。

如何实现自旋锁?

当时真不会,只磕磕绊绊说了一些自旋锁的东西。。

大家可以看这篇博客 自旋锁C++实现方式

算法:三数之和。秒

三面

kafka 和 其他消息队列,比如 rocketmq,rabbitmq ,有什么优势?

kafka 具备非常高可靠的分布式架构,功能简单,只支持一些主要的MQ功能,像一些消息查询,消息回溯等功能没有提供。时效是在ms级别以内的。

kafka如何保证消息不丢失?

那我们可以从两个方面也就是生产者,消费者以及 broker ,来说说

  1. 生产者:生产者(Producer) 调用 send 方法发送消息之后,消息可能因为网络问题并没有发送过去。Kafka提供了同步发送消息方法,会返回一个Future对象,调用get()方法进行阻塞等待,就可以知道消息是否发送成功。如果消息发送失败的话,可以通过 Producer 的 retries(重试次数)参数进行设置,当出现网络问题之后能够自动重试消息发送,避免消息丢失。另外,建议还要设置重试间隔,因为间隔太小的话重试的效果就不明显。
  2. 消费者:消息在被追加到 Partition(分区) 的时候都会分配一个特定的偏移量(offset)。偏移量(offset)表示 Consumer 当前消费到的 Partition(分区)的所在的位置。 Kafka 通过偏移量(offset)可以保证消息在分区内的顺序性。当消费者拉取到了分区的某个消息之后,消费者会自动提交了 offset。自动提交的话会有一个问题,试想一下,当消费者刚拿到这个消息准备进行真正消费的时候,突然挂掉了,消息实际上并没有被消费,但是 offset 却被自动提交了。 可以通过enable.auto.commit设置为false,关闭自动提交 offset ,每次在真正消费完消息之后之后再自己手动提交 offset
  3. broker:当消息发送到了分区的leader副本,leader 副本所在的 broker 突然挂掉,那么就要从 follower 副本重新选出一个 leader ,但是 leader 的数据还有一些没有被 follower 副本的同步的话,就会造成消息丢失。解决办法就是 将producer设置 acks = all

https为什么是安全的?

因为 https 是基于 ssl 和 tls 加密而成的,https 的 s 就代表 ssl 和 tls。

ssl/tls 是怎么保证安全的?经过几次握手?

经过四次握手,客户端向服务器端索要并验证公钥,双方协商生成"对话密钥",双方采用"对话密钥"进行加密通信。
四次握手主要是交换以下信息:

  1. 数字证书:该证书包含了公钥等信息,一般是由服务器发给客户端,接收方通过验证这个证书是不是由信赖的CA签发,或者与本地的证书相对比,来判断证书是否可信;假如需要双向验证,则服务器和客户端都需要发送数字证书给对方验证;
  2. 三个随机数:这三个随机数构成了后续通信过程中用来对数据进行对称加密解密的“对话密钥”。

首先客户端先发第一个随机数N1,然后服务器回了第二个随机数N2(这个过程同时把之前提到的证书发给客户端),这两个随机数都是明文的;而第三个随机数N3,客户端用数字证书的公钥进行非对称加密,发给服务器;
而服务器用只有自己知道的私钥来解密,获取第三个随机数。
这样,服务端和客户端都有了三个随机数N1+N2+N3,然后两端就使用这三个随机数来生成“对话密钥”,在此之后的通信都是使用这个“对话密钥”来进行对称加密解密
因为这个过程中,服务端的私钥只用来解密第三个随机数,从来没有在网络中传输过,这样的话,只要私钥没有被泄露,那么数据就是安全的。

  1. 加密通信协议:就是双方商量使用哪一种加密方式,假如两者支持的加密方式不匹配,则无法进行通信;

事务的四大特性?

原子性、隔离性、一致性、持久性

用过哪些排序?

快排,堆排,归并比较常用。

快排一定最快吗?

不一定,当待排序的序列已经有序,不管是升序还是降序。此时快速排序最慢,一般当数据量很大的时候,用快速排序比较好,为了避免原来的序列有序。

场景题:如果我有100G文件,但是只有 500 M 的内存,这些文件存着一行行的数字,如何获取最小的10个?

这是经典的 Top K 问题,分治+堆排,我们可以先进行一个将文件进行分治,将100G的文件的分成一个个的 500 M,以此放到内存中每个500M的文件取出进行最小堆的排序,取出前10个写到新文件中。再对着一个个的新文件进行合并成 500 M 再进行最小堆的排序,最终获取最小的10个数字。

算法:最长有序括号,常见题,秒

参考链接

[1] https://blog.csdn.net/weixin_35973945/article/details/124245495
[2] https://www.cnblogs.com/yaochunhui/p/14128777.html

golang开发面经滴滴(三轮技术面)(代码片段)

文章目录写在前面笔试一面进程间通信方式栈上分配内存快还是堆上,为什么?channel底层七层模型tcp、udpredis持久化的方式以及使用场景如何实现线程池?算法:二叉树俯视图二面怎么判断给定ip是否在给定ip区间内... 查看详情

golang开发面经滴滴(三轮技术面)(代码片段)

文章目录写在前面笔试一面进程间通信方式栈上分配内存快还是堆上,为什么?channel底层七层模型tcp、udpredis持久化的方式以及使用场景如何实现线程池?算法:二叉树俯视图二面怎么判断给定ip是否在给定ip区间内... 查看详情

golang开发面经百度(三轮技术面)(代码片段)

文章目录写在前面笔试一面算法:判断是否为镜面二叉树算法:二叉树的俯视图一个协程被网络io卡住了,对应的线程会不会卡住?go里面make和new有什么区别?map是怎么实现的?二面go里面slice和array有区别... 查看详情

golang开发面经百度(三轮技术面)(代码片段)

文章目录写在前面笔试一面算法:判断是否为镜面二叉树算法:二叉树的俯视图一个协程被网络io卡住了,对应的线程会不会卡住?go里面make和new有什么区别?map是怎么实现的?二面go里面slice和array有区别... 查看详情

字节跳动八进八出,offer到手,发现项目不重要算法才最重要(代码片段)

...术面+一轮hr面,已经成功拿到offer。由于去年实习面经没有发,所以跟着本次提前批面经一共八轮面试一起发了。 查看详情

2022字节跳动数仓实习面经(23面hr面)

...字节跳动数据研发面试经历.2022百度面经:2022百度大数据开发工程师实习面试经历.下面我将对2 查看详情

字节跳动面经分享(android客户端)(代码片段)

...ffermail一面(60分钟)前15分钟个人介绍,什么时候接触的安卓开发,实习能实习多久,介绍一下项目你说你的软件出现了OOM,你怎么解决的?线上cr 查看详情

含泪写下这篇面经:拒绝头条,放弃华为,6面字节跳动终拿offer!年薪70w+!

前言先说一下我的情况,8月被导员通知优研资格没有我,开始准备考上交819,9月华为大规模秋招,第一批集招offer拿到,拒了,之后学姐给了白金码,选择放弃考研全力备战字节面试,准备了一个月,10.13面试,三轮面完,10.19... 查看详情

golang开发面经蔚来(两轮技术面)(代码片段)

文章目录一面1.channel缓冲与非缓冲2.mysql引擎3.索引如何建立?4.linux如何看进程5.redis字符串的底层6.线程池理解7.线程池的拒绝策略8.悲观锁,乐观锁9.HTTP各个版本的区别10.HTTP2.0之前怎么实现服务器推送机制?11.websocket... 查看详情

字节跳动超高难度三面java程序员面经,java开发电脑选择

一、SpringCloud1.服务发现EurekaEureka的高可用ConsulConsul安装与使用Consul常用命令Consul高可用2.服务提供者3.服务消费者RibbonFeign4.熔断器HystrixHystrixDashboardTurbine5.配置中心6.APIGateway二、使用Docker构建微服务1.Docker介绍2.Docker的安装3.Docker... 查看详情

字节跳动面经——图形图像算法实习

目录项目:C++:图形学:编程题:项目:1.项目的目的2.项目的创新性3.你的职责4.项目细节,具体每个部分怎么实现的,用了哪些技术C++:1.重载和重写2.参数传递时,传值、传引用和传指针的区别3.指针和引用的区别4.函数返回... 查看详情

字节跳动后端开发实习面经一二三面+hr面(已offer)

*号表示没答上一面(1h)1.问之前的实习2.redis的基本数据结构,string的底层3.binlog存储日志的格式,statement造成主备不一致的过程4.多机房部署mysql会有什么问题,解决数据同步问题的方案5.http和https的区别*6.utf... 查看详情

字节跳动面经

作者:好运来好运来offer来来来链接:https://www.nowcoder.com/discuss/718280?type=post&order=time&pos=&page=1&ncTraceId=&channel=-1&source_id=search_po 查看详情

字节跳动面经

作者:好运来好运来offer来来来链接:https://www.nowcoder.com/discuss/718280?type=post&order=time&pos=&page=1&ncTraceId=&channel=-1&source_id=search_po 查看详情

2022字节跳动数据仓库实习面经(代码片段)

👊先和大家说一下情况,3月4号面试的字节跳动数据研发岗位直接把我挂了,我满脸疑惑,但是抱着学习和提升自我的心态,打电话问问hr,像看看面试官给我面试的评价,hr说,面试官就两行,... 查看详情

golang开发面经b站(两轮技术面)(代码片段)

文章目录写在前面笔试一面Go的GMP模型GO的GCGo的map底层是怎么实现的?遍历map是有序的吗?为什么?map作为函数是什么传递?在函数里面修改map会影响原来的吗?那数组呢?切片呢?linux有用过是吧?如... 查看详情

golang开发面经知乎(两轮技术面)(代码片段)

文章目录写在前面笔试一面进程和线程的区别?虚拟地址是什么?内存分段分页讲讲?http1.0,1.1,2.0区别?post和get的区别TCP连接是怎么样的?为什么是三次?断开为什么是四次?三次握手四次... 查看详情

golang开发面经深信服(两轮技术面)(代码片段)

文章目录写在前面一面了解过切片和数组吗?有什么区别?那这样初始化可以吗?有什么问题?用过map吧?怎么遍历map?那遍历map是有序的吗?为什么是无序的?用过chan吧?怎么声明一个chan呢&... 查看详情