头图

💼 岗位职责:

  1. 负责腾讯蓝鲸后台相关的开发工作;
  2. 负责腾讯游戏相关底层支撑平台的开发工作。

📋 任职要求:

  1. 计算机相关专业,3 年及以上相关工作经验,有扎实的计算机理论基础;
  2. 三年以上 Linux 环境服务器开发的经验,精通 Socket 网络编程、服务器开发工作;
  3. 三年以上使用 Golang 语言开发的经验,熟悉 IO 复用、多线程、RPC 等基础技术和框架;
  4. 熟悉分布式系统,微服务系统架构设计及开发;熟练使用 MongoDB、Redis 等 NOSQL;
  5. 良好的学习能力、沟通能力、适应能力,责任心强,能在压力下独立解决问题;熟悉容器技术,了解微服务的优先;热爱互联网,喜欢挑战者优先。

✨ 面试问题回顾:

1. 自我介绍

自我介绍是面试的开场环节,要简洁明了地介绍自己的姓名、工作经验、擅长的技术领域以及个人优势。

2. 为什么离职

离职原因要真诚且合理,可以从职业发展、个人成长、工作环境等方面来回答。

3. 会哪些语言

主要使用 Golang,Java 等等,按自己的技术栈说明,尤其是说明自己的擅长的地方

4. 职业规划

职业规划要体现出自己的目标和努力方向。例如:在短期内,我希望能够深入学习 Golang 的高级特性和优化技巧,提升自己的开发效率和代码质量。长期目标是能够带领团队,为公司的技术发展做出更大的贡献。

5. 对未来从事行业的看法

可以结合行业趋势和自己的兴趣来回答。

6. 实现一个 golang 版简易内存缓存

实现一个简易的内存缓存,要求如下:

  • 支持存储任意对象
  • 支持指定 key 的过期时间,过期自动删除
package main

import (
    "time"
)

type CacheItem struct {
    value      interface{}
    expiration time.Time
}

type MemoryCache struct {
    items map[string]*CacheItem
}

func NewMemoryCache() *MemoryCache {
    return &MemoryCache{
       items: make(map[string]*CacheItem),
    }
}

func (c *MemoryCache) Set(key string, value interface{}, expiration time.Duration) {
    c.items[key] = &CacheItem{
       value:      value,
       expiration: time.Now().Add(expiration),
    }
}

func (c *MemoryCache) Get(key string) (interface{}, bool) {
    item, ok := c.items[key]
    if!ok {
       return nil, false
    }
    if time.Now().After(item.expiration) {
       delete(c.items, key)
       return nil, false
    }
    return item.value, true
}

7. 遍历 map 的时候为什么不直接删除元素

在遍历map的时候直接删除元素可能会导致遍历的顺序受到影响,或者某些元素可能会被跳过。这是因为map的遍历是无序的,并且在遍历过程中直接删除元素可能会导致内部数据结构的变化,从而影响遍历的结果。

8. map 的 value 使用指针和不使用指针有什么区别

  1. 指针的优点

    • map大对象可以减少内存 copy,因为只需要存储指针而不是整个对象。
    • 允许修改数据,通过指针可以直接修改指向的对象。
    • 可以方便的判断是否是nil
  2. 指针的缺点

    • 额外的访问开销,需要通过指针间接访问对象。
    • 内存管理复杂,可能存在空指针问题。
  3. 不使用指针的优点

    • 不需要间接访问内存,访问速度更快。
    • 避免空指针的引用,更加安全。
  4. 不使用指针的缺点

    • 增加内存开销,因为需要存储整个对象。
    • 不方便修改,需要重新赋值整个对象。

9. map 中 key 可以使用的类型

不能作为mapkey的类型有slicemapfunc。这是因为这些类型不满足mapkey必须是可比较类型的要求。而基本数据类型(如intstring等)和结构体(如果结构体的字段都是可比较类型)可以作为mapkey

10. go 的指针和 C/C++的区别

  1. Go 指针

    • 强调安全性,避免指针运算,减少了因指针运算导致的错误。
    • 有垃圾回收机制,不需要手动管理内存,降低了内存泄漏的风险。
    • 使用切片代替数组进行灵活的内存操作,更加方便和安全。
  2. C/C++ 指针

    • 支持指针运算,可以直接对指针进行加减操作,灵活性高但也容易出错。
    • 需要手动管理内存,容易导致内存泄漏和悬空指针问题。
    • C++ 有引用机制,但与 Go 的指针还是有很大的不同。

11. go 的并发模型

Go 的并发模型主要是协程和通道(channel)。协程是一种轻量级的线程,由 Go 运行时管理,创建和切换开销极小,适合高并发场景。通道用于协程之间的通信,可以保证数据的安全传递。

12. 说一下进程,线程,协程

  • 进程:独立运行的程序实例,拥有自己的内存空间和系统资源。进程切换开销较大,因为需要切换内存空间、寄存器等。
  • 线程:进程中的执行单元,共享进程的内存空间。线程切换开销较小,因为不需要切换内存空间。
  • 协程:比线程更轻量级的并发单元,由 Go 运行时管理。协程创建和切换开销极小,适合高并发场景。协程之间通过通道进行通信。

13. 协程切换是在用户态还是内核态

协程切换是在用户态进行的。这意味着协程的切换不需要进行系统调用,切换开销极小。相比之下,线程切换通常需要在内核态进行,开销较大。

14. channel 被关闭了,读和写有什么问题

  • :如果向一个已关闭的通道写数据,会导致 panic。
  • :如果从一个已关闭的通道读数据,会返回通道类型的零值。如果通道中还有未读取的数据,会先读取完这些数据,然后再返回零值。

15. 聊项目

  1. 消息队列

    • 数据倾斜问题:可以通过负载均衡、分区等方式来解决数据倾斜问题。
    • 数据不丢:可以使用持久化存储、确认机制等方式来保证数据不丢失。
    • 重复消费:可以通过幂等性处理、唯一标识等方式来解决重复消费问题。
  2. Redis

    • 使用hash:可以将一个复杂的对象存储在一个hash结构中,方便管理和查询。
    • 使用Pipline:可以将多个命令打包发送给 Redis 服务器,提高性能。
    • bigkey拆解:对于大的键值对,可以进行拆解,例如将一个大的列表拆分成多个小的列表,或者将一个大的字符串拆分成多个小的字符串。
  3. 数据库

    • 性能优化:可以通过索引、缓存、优化查询语句等方式来提高数据库的性能。
    • 分库分表:当数据量较大时,可以进行分库分表,将数据分散到多个数据库或表中,提高查询效率。

16. 云平台的主要功能

  1. 快速接入设备:提供方便的接口和工具,让设备能够快速接入云平台。
  2. 第三方接入方便:支持第三方应用接入云平台,提供开放的 API 和 SDK。
  3. 监控告警:实时监控设备和应用的运行状态,当出现异常情况时及时发出告警。
  4. 报表生成:生成各种报表,帮助用户了解设备和应用的运行情况。

17. 设备是怎么发现服务的

设备添加流程中,会告诉设备要连接哪些服务。例如,可以通过配置文件、网络广播、DNS 服务等方式让设备知道要连接的服务地址和端口号。

18. 为什么使用 TCP 长连接

  1. 减少连接建立和拆除的开销:TCP 连接的建立和拆除需要进行三次握手和四次挥手,开销较大。使用长连接可以避免频繁的连接建立和拆除,提高性能。
  2. 保持实时通信:长连接可以保持客户端和服务器之间的通信通道,方便实时数据的传输。
  3. 减少带宽消耗:对于频繁通信的场景,使用长连接可以减少每次连接建立和拆除时的网络开销。
  4. 心跳机制:可以通过心跳机制检测连接的有效性,及时发现和处理连接故障。
  5. 节约资源:长连接可以减少服务器的资源消耗,因为不需要频繁地创建和销毁连接。

19. TCP 长连接对服务器那些资源有消耗

  1. 内存消耗:每个连接都需要占用一定的内存空间,用于存储连接状态、数据缓冲区等。
  2. 文件描述符:每个连接都需要一个文件描述符,当连接数量较多时,可能会耗尽服务器的文件描述符资源。
  3. CPU 使用:处理连接的读写操作、心跳检测等都需要消耗 CPU 资源。
  4. 网络带宽:长连接可能会占用一定的网络带宽,特别是在数据传输频繁的情况下。
  5. 连接表:服务器需要维护一个连接表,记录所有的连接信息,这也会占用一定的内存空间。
  6. 网络栈资源:包括 TCP 协议栈的各种缓冲区、定时器等资源。

20. linux 系统 fd 的上限是多少?

在 Linux 系统中,文件描述符(fd)的上限可以通过系统配置进行修改。一般来说,默认的上限是比较小的,但可以通过修改系统参数来提高上限。例如,可以通过修改/proc/sys/fs/file-max文件来增加文件描述符的上限。理论上,根据机器的配置,可以说没有限制。

21. TCP 长连接是怎么维持的

  1. 传输层心跳机制:TCP 协议本身有保活机制,可以通过设置SO_KEEPALIVE选项来启用。当连接在一段时间内没有数据传输时,TCP 会发送保活探测报文,检测连接的有效性。
  2. 应用层心跳机制:在应用层实现心跳机制,可以更加灵活地控制连接的状态。例如,可以定期发送心跳报文,检测连接是否正常。
  3. SO_KEEPALIVE 选项:这是一种由操作系统提供的保活机制,可以在一定时间内没有数据传输时,自动发送探测报文,检测连接的有效性。

22. mqtt 负载均衡怎么做的?

  1. DNS 负载均衡:通过 DNS 服务器将客户端的请求分发到不同的 MQTT 服务器上。
  2. 代理层负载均衡:使用代理服务器,将客户端的请求转发到不同的 MQTT 服务器上。
  3. 集群负载均衡:将多个 MQTT 服务器组成一个集群,通过负载均衡算法将客户端的请求分发到不同的服务器上。

23. 服务器故障了怎么处理

  1. 故障转移:当服务器发生故障时,将客户端的连接转移到其他正常的服务器上。
  2. 监控检查和监控:通过监控系统及时发现服务器故障,并进行报警。
  3. 数据复制和同步:保证数据的冗余备份,当服务器故障时,可以从其他服务器恢复数据。
  4. 隔离修复:将故障服务器隔离,进行修复和调试。
  5. 重新加入集群:修复后的服务器可以重新加入集群,继续提供服务。

24. 为什么选择 kafka

选择 Kafka 的原因可能有以下几点:

  1. 高吞吐量:Kafka 能够处理大量的消息,具有很高的吞吐量。
  2. 分布式架构:Kafka 是分布式的,可以水平扩展,适应大规模的数据处理需求。
  3. 持久性:Kafka 可以将消息持久化存储,保证数据的可靠性。
  4. 高可用性:Kafka 具有高可用性,能够自动进行故障转移,保证系统的稳定性。
  5. 灵活的订阅模式:Kafka 支持多种订阅模式,可以满足不同的业务需求。

25. Apache pulsar 知道吗?

Apache Pulsar 是一个开源的分布式消息流平台,具有高吞吐量、低延迟、可扩展性强等特点。它与 Kafka 类似,但也有一些不同之处,例如 Pulsar 支持多租户、分层存储等功能。

26. 消息积压怎么办?

  1. 新 topic 去处理:可以创建一个新的 topic,将积压的消息转移到新的 topic 中进行处理。
  2. 增加消费者:增加消费者的数量,提高消息的处理速度。

27. 给 app 推消息是怎么实现的

给 app 推消息可以通过多种方式实现,例如使用推送服务提供商(如极光推送、个推等),或者自己搭建推送服务器。一般来说,推送消息需要以下步骤:

  1. 注册推送服务:在推送服务提供商处注册应用,获取应用的唯一标识。
  2. 集成推送 SDK:在 app 中集成推送服务提供商的 SDK,实现推送功能。
  3. 发送推送消息:通过推送服务提供商的接口或者自己搭建的推送服务器,向 app 发送推送消息。

28. 为什么要把推送服务分开

将推送服务分开可以实现解耦,提高系统的可维护性和可扩展性。如果将推送服务与其他业务逻辑混合在一起,会增加系统的复杂性,不利于维护和扩展。同时,将推送服务分开还可以提高推送的效率和稳定性,因为可以专门针对推送服务进行优化和管理。

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

没准能让你能刷到自己意向公司的最新面试题呢。

感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。


王中阳Go
799 声望295 粉丝