Golang微服务:Task服务调用gRPC适配前端分页需求方案
跨微服务分页过滤的解决方案
针对Task服务与App服务跨库,分页查询任务时过滤后数量不足的问题,提供以下几种实用方案:
1. 冗余查询+批量过滤补全
前端请求每页10条数据时,Task服务主动查询冗余数量的任务(比如15条,冗余比例根据业务过滤率调整),然后将这批任务的App ID批量传递给App服务,一次性完成过滤和App信息获取。
- 拿到过滤后的有效任务后,若数量满足10条,直接返回前10条;若不足,继续从Task数据库查询下一批任务,重复过滤流程,直到凑够10条或确认无更多数据。
- Golang实现要点:用循环分批查询Task数据,调用App服务的批量gRPC接口(比如
BatchGetAppInfoAndFilter),避免单次请求数据量过大,同时设置最大重试次数防止无限循环。
示例伪代码:
func GetTaskPage(pageSize int) ([]TaskWithAppInfo, error) { var result []TaskWithAppInfo offset := 0 maxRetry := 5 retryCount := 0 for len(result) < pageSize && retryCount < maxRetry { // 从Task库查询冗余数据,比如pageSize*2 tasks, err := taskDB.QueryTasks(offset, pageSize*2) if err != nil { return nil, err } if len(tasks) == 0 { break } // 提取所有App ID appIDs := extractAppIDs(tasks) // 调用App服务批量过滤并获取信息 validAppInfos, err := appClient.BatchFilterAndGetInfo(context.Background(), appIDs) if err != nil { return nil, err } // 匹配有效任务并收集 for _, task := range tasks { if info, ok := validAppInfos[task.AppID]; ok { result = append(result, TaskWithAppInfo{Task: task, AppInfo: info}) if len(result) == pageSize { break } } } offset += pageSize*2 retryCount++ } return result[:min(len(result), pageSize)], nil }
2. 反向获取过滤条件,Task端精准查询
让App服务提供返回符合过滤规则的App ID集合的接口,Task服务先调用该接口拿到允许的App ID,再直接在自己的数据库中按分页查询属于这些ID的任务。
- 优势:从根源避免过滤后数量不足的问题,查出来的任务本身就符合App服务的过滤规则。
- 注意事项:如果App ID数量极大,要让App服务支持分页返回ID,Task服务分批查询后合并,再去Task数据库做
IN查询(注意数据库IN的数量限制,超过则拆分多个查询)。
3. 异步预缓存符合条件的任务列表
如果App服务的过滤规则不会频繁变动,可以采用预缓存策略:
- 定期(比如每小时)或在App服务过滤规则更新时,Task服务主动拉取过滤规则,全量查询符合条件的任务,提前分页并缓存到Redis等中间件。
- 前端请求分页时,直接从缓存中读取对应页的数据,无需实时调用App服务过滤。
- 适用场景:过滤规则稳定、对数据实时性要求不高的业务。
4. 改用游标分页替代偏移量分页
放弃传统的limit + offset偏移量分页,改用游标分页:
- 前端请求时,传递上一页最后一条有效任务的唯一标识(比如
taskID+appID)作为游标,Task服务从游标位置开始查询一批任务,传给App服务过滤,直到凑够10条有效数据,然后将当前页最后一条任务的游标返回给前端。 - 优势:不会因为中间数据被过滤导致分页逻辑混乱,每次请求都是基于上一次的有效结果继续查询,更适合跨服务过滤的场景。
内容的提问来源于stack exchange,提问作者cloudsky mini
相关产品推荐
相关产品推荐

