如何设计RPC框架

远程过程调用(Remote Procedure Call)的目的是让我们调用远程方法一样像本地方法一样没区别。

核心组件

RPC框架主要包含四个核心部分:

  1. 顶层代理设计

    • 提供统一的调用类,屏蔽底层细节
    • 通过动态代理实现接口的远程调用
  2. 序列化/反序列化

    • 将对象转换为二进制数据流
    • 常用序列化协议:Protobuf、JSON、Hessian等
  3. 拼接协议/解析协议

    • 定义请求和响应的数据格式
    • 包含服务名、方法名、参数类型、参数值等信息
  4. 底层网络传输

    • 基于TCP/UDP的网络通信
    • 支持长连接、连接池等优化手段

如何设计一个秒杀功能

问题点

  1. 瞬时流量的承接
  2. 防止超卖
  3. 防止黑产
  4. 避免正常服务受到影响

前端解决方案

  1. 用CDN缓存资源,减少压力
  2. 在前端随机限流,降低请求量
  3. 按钮防抖,防止用户多次点击

后端解决方案

  1. 用nginx做统一接入,实现负载均衡和限流
  2. 服务拆分,将秒杀功能拆分成独立的服务,避免其他功能受到影响
  3. 将秒杀数据拆分或缓存,可以使用分布式缓存或本地缓存
  4. 精准的库存扣减,防止超卖,此时需要加锁,数据库常用的是乐观锁
  5. 使用验证码答题等手段防止系统刷单
  6. 幂等操作防止重复加载
  7. 风控识别黑产,进行流量防控且需要动态黑名单

如何设计一个消息队列

重要角色

  1. 生产者:发送消息的一方
  2. 消费者:接收消息的一方
  3. broker:消息中转站,负责存储和转发消息
  4. 注册中心:用于broker/生产者/消费者的发现,即让生产者找到当前有什么可用的broker的ip和端口这些

流程

  1. 生产者生产消息发送到broker
  2. 消费者从broker中获取消息来消费

如何设计一个线程池

什么是线程池

线程池说白了就是一个存储线程的容器,用于统一管理线程的创建和销毁。

设计考虑点

  1. 初始线程数:线程池初始化时创建的线程数量
  2. 最大线程数:线程池能够容纳的最大线程数量
  3. 核心线程数:线程池运行起来后长期稳定的线程数,即使有空闲线程也不会被销毁
  4. 任务的设计和执行:是否设计任务优先级
  5. 超负荷如何解决
    • 设置超时然后丢弃,也可以根据优先级处理
    • 日志告警或者抛出异常

如何设计一个哈希表

哈希表是什么

一个key-value的集合,通过将key进行哈希散列后对散列值取模,找到对应的value。

设计要点

  1. 设计哈希函数:运算要快,还不能经常哈希碰撞
  2. 解决哈希冲突:链表法、开放寻址法等
  3. 若数组太大了则考虑转成红黑树:提高查询效率