高质量编程与性能调优实战 | 豆包MarsCode AI刷题

52 阅读5分钟

下面是我2023年暑假参加字节青训营写的项目 在深入探究如下这段源码所关联的任务时,我们将逐步解析其逻辑,并全面思考可能的优化方向。

// GetUserById 通过id获取用户基本信息
func (s *UserRPCServer) GetUserById(ctx context.Context, req *pb.DouyinUserRequest) (*pb.DouyinUserResponse, error) {
    //游客浏览则返回基本信息
    if req.GetToken() == "" {
        userReq := service.GetUserByIdService{Id: uint(req.GetFollowId())}
        return userReq.GetUserById(ctx)
    }

    // 用户基本信息请求体
    videoCountKey := cache.VideoCacheCountKey(req.GetFollowId())
    userReq := service.GetUserByIdService{Id: uint(req.GetFollowId())}
    user, err := userReq.GetUserById(ctx)
    if err!= nil {
        return nil, err
    }

    // 是否关注请求体
    isFollowreq := service.IsFollowService{
        UserId:       uint(req.GetUserId()),
        FollowUserId: uint(req.GetFollowId()),
    }
    isFollow, err := isFollowreq.IsFollow(ctx)
    if err!= nil {
        return nil, err
    }

    // 获取用户关注数与被关注数
    favCount, err := grpc.GetFavCount(ctx, uint(req.GetFollowId()))
    if err!= nil {
        return nil, err
    }
    if favCount.StatusCode!= 0 {
        return nil, e.NewError(e.Error)
    }

    //用户作品数量
    key, err := cache.RedisGetKey(ctx, videoCountKey)
    if err!= nil {
        return nil, e.NewError(e.Error)
    }
    var vidCount int64
    //当缓存中不存在则通过grpc获取
    if key == "" {
        vidResp, err := grpc.GetPublishList(ctx, uint(req.GetFollowId()), req.GetToken())
        if err!= nil || vidResp.GetStatusCode()!= 0 {
            return nil, e.NewError(e.Error)
        }
        vidCount = int64(len(vidResp.VideoList))
        //将数据存入缓存
        _ = cache.RedisSetKey(ctx, videoCountKey, vidCount)
    } else {
        vidCount = util.StrToInt64(key)
    }
    // 获取用户基本信息
    userInfo := user.GetUser()
    return &pb.DouyinUserResponse{User: &pb.User{
        Id:              req.FollowId,
        Name:            userInfo.GetName(),
        FollowCount:     userInfo.FollowCount,
        FollowerCount:   userInfo.FollowerCount,
        IsFollow:        isFollow.GetIsFollow(),
        FavCount:        int64(favCount.GetFavCount()),
        WorkCount:       vidCount,
        TotalFavorited:  favCount.GetFavCount_,
        BackgroundImage: "https://ts2.cn.mm.bing.net/th?id=OIP-C.HfZqICAPqMQslH0cMrIDFQHaKe&w=210&h=297&c=8&rs=1&qlt=90&o=6&dpr=1.1&pid=3.1&rm=2",
        Signature:       "测试用户",
        Avatar:          "https://ts2.cn.mm.bing.net/th?id=OIP-C.druUEHdZrBEuZPn2w80Y1QHaNK&w=187&h=333&c=8&rs=1&qlt=90&o=6&dpr=1.1&pid=3.1&rm=2",
    }}, nil
}

一、代码功能剖析

此代码定义了GetUserById函数,它是UserRPCServer结构体的一个方法,核心任务是依据给定的id获取用户基本信息。

  1. 游客浏览逻辑

    • 函数起始处通过检查req.GetToken()是否为空来判断是否为游客浏览。若为空,则构造service.GetUserByIdService类型的userReq,将req.GetFollowId()转换为uint作为其Id,随后调用userReq.GetUserById(ctx)获取并直接返回游客浏览时的用户基本信息。
  2. 非游客浏览逻辑

    • 用户基本信息获取

      • 首先构建userReq,用于获取用户基本信息,同样设置Id字段。接着调用userReq.GetUserById(ctx)获取用户信息,若出错则立即返回错误。
    • 是否关注判断

      • 创建service.IsFollowService类型的isFollowreq,设置UserIdFollowUserId,然后调用isFollowreq.IsFollow(ctx)判断当前用户对目标用户的关注状态,出错时返回错误。
    • 关注数与被关注数获取

      • 调用grpc.GetFavCount(ctx, uint(req.GetFollowId()))获取目标用户的关注数与被关注数相关信息,若获取过程出错或者返回的StatusCode不为0,则返回错误。
    • 用户作品数量获取

      • 先构建videoCountKey用于从缓存获取用户作品数量。通过cache.RedisGetKey(ctx, videoCountKey)尝试获取缓存值。若缓存中不存在(key == ""):调用grpc.GetPublishList(ctx, uint(req.GetFollowId()), req.GetToken())获取用户发布列表,若出错或返回状态码不为0则返回错误,之后根据视频列表长度设置vidCount并将其存入缓存。若缓存中有值,则将获取到的key转换为int64类型作为vidCount
  3. 响应构建与返回

    • 最后,从获取到的用户信息中提取详细内容,构建pb.DouyinUserResponse,填充User结构体中的各项信息,如IdNameFollowCount等,同时设置固定的BackgroundImageSignatureAvatar等字段,最终返回构建好的响应结构体。

二、可能的优化方向深度思考

  1. 缓存策略优化

    • 目前对于用户作品数量的缓存操作较为基础。可以引入更智能的缓存更新策略,例如设置动态的缓存过期时间,根据用户活跃度或者数据更新频率来调整。比如,对于活跃用户,缩短缓存过期时间,以保证数据的及时性;对于不活跃用户,适当延长缓存过期时间,减少缓存更新的开销。
    • 考虑缓存的预加载机制。在系统空闲或者低负载时段,提前将热门用户或者可能被频繁访问的用户数据加载到缓存中,提高数据获取的响应速度。
    • 优化缓存键的设计。当前videoCountKey的生成方式可能较为简单,可以结合更多用户特征或者业务逻辑来生成更具区分度和语义化的缓存键,方便缓存管理和数据查询。
  2. 错误处理统一与精细化

    • 代码中错误返回形式多样,有直接返回err,也有通过e.NewError(e.Error)返回。可以建立一个集中的错误处理模块,将所有可能的错误进行分类和封装。例如,定义GRPCErrorCacheErrorServiceError等不同类型的错误结构体,在错误发生时,根据错误来源创建相应类型的错误对象并返回。这样,上层调用者可以根据错误类型进行更精准的处理,如针对GRPCError可以尝试重新连接或切换grpc服务节点,对于CacheError可以检查缓存配置或者进行缓存修复操作。
    • 增加错误日志记录功能。在错误处理模块中,将错误信息详细记录到日志文件中,包括错误发生的时间、位置、相关参数等,便于后续的故障排查和系统监控。
  3. 性能优化

    • 对于频繁调用的grpc方法,如grpc.GetFavCountgrpc.GetPublishList,可以探索批量调用的可行性。例如,将多个用户的id收集起来,一次性发送批量请求,减少grpc调用的往返次数,提高整体性能。

    • 采用连接池技术优化grpc连接管理。创建一个grpc连接池,在需要进行grpc调用时,从连接池中获取连接,调用结束后将连接归还到连接池,避免每次调用都创建和销毁连接带来的性能开销。

    • 对数据获取和处理流程进行异步化改造。例如,在获取用户作品数量时,如果缓存中不存在,可以先异步发起grpc请求获取数据并更新缓存,同时直接返回一个默认的或者上次缓存的值给调用者,后续当grpc请求完成后再更新缓存数据,这样可以减少调用者的等待时间,提高系统的并发处理能力。

通过对这段源码的全面解读以及对优化方向的深入思考,我们能够更好地把握代码的现状,并为后续的高质量编程和性能提升奠定坚实的基础。