马蜂窝搜索基于 Golang 并发代理的一次架构升级

2023-10-13 07:20

本文主要是介绍马蜂窝搜索基于 Golang 并发代理的一次架构升级,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

搜索业务是马蜂窝流量分发的重要入口。很多用户在使用马蜂窝时,都会有目的性地主动搜索与自己旅行需求相关的各种信息,衣食住行,事无巨细,从而做出最符合需求的旅行决策。

因此在马蜂窝,搜索业务交互的下游模块非常多,主要有目的地、POI、热门景点、美食、商场、酒店、问答、攻略、机票火车票等等,通过实时、精准地返回搜索结果,帮助用户做出个性化旅行决策。

面对越来越高的流量,马蜂窝技术团队积极尝试对搜索架构进行优化和升级,来保证搜索业务的稳定和性能。

方案背景

由于历史原因,优化前的搜索服务与下游模块交的互方式主要为调用各下游模块提供的函数,并且采用串行调用。

图 1: 马蜂窝搜索业务架构和技术体系

搜索技术体系

  • 存储——MySQL、Memcache

  • 模块交互——Function Call

  • 检索——Elasticsearch

搜索业务架构

我们将搜索业务抽象为三个功能模块:

1. 决策系统

负责根据用户意图、运营策略、点击日志等数据,结合决策系统相关算法和模型,决策应该展示哪些模块(游记、商品等)及各模块展示顺序。

2. Agent

负责根据决策系统确定要展示的模块,从 Elasticsearch 和业务方获取模块(如游记、商品等)数据。

3. Format

负责根据不同模块的 UI 交互定义格式化数据,补充 UI 交互缺失数据。

串行的函数级调用方式,使之前的搜索服务架构存在一系列问题:

  • 业务间耦合度高。随着交互模块越来越多,导致搜索服务耗时变得很长,平均达到 400-500 ms;

  • 由于与各业务间交互的方式是 Function Call,使上游很难控制下游模块阻塞时间;

  • 下游调用增加响应时间相应呈线性增长,使其很难再叠加新的功能,可扩展性差;

  • 如果下游模块出现故障,会由于接口阻塞引起超时,导致搜索服务整体都受到影响,表现出白页,用户体验严重下降。

图 2:问题分析

因此,我们需要找到一种方式来降低搜索服务对于下游模块的依赖,以及模块间的耦合,从而提升架构的整体可用性和性能。

基于 Golang 的并发代理实现

经过调研,我们开发了基于 Golang 协程实现的并发请求代理工具,将之前函数级调用的方式变为基于 TCP/IP 的 HTTP 接口调用来与下游模块解耦,同时将串行调用变为并发,实现超时控制和异常容错处理。

主要技术选型——协程(Goroutine)

Goroutine 是 Golang 轻量级线程实现,由 Go runtime 管理。它是 Go 并行设计的核心,也是 Golang 最重要的特性之一,相比于进程、线程任务的抢占式调度,需要频繁进行上下文信息的内核和用户空间切换,Goroutine 可以由程序控制,使得它更易用、更高效、更轻便。

Goroutine 维护了一组数据结构和多个线程,任务放在一个待执行队列中,由 Goroutine 维护的线程来拉取执行。当任务执行了操作系统的 IO 操作等需要等待时,Goroutine 利用 Linux IO 多路复用技术 (Epoll、Select) 进行执行队列的任务切换来实现并发。

相比于其他语言的线程,其默认占用内存为 2KB, 远小于其他语言的 M 级别。在性能开销方面,由于任务调度基本有程序控制,开销也远小于线程。

选型的过程中,我们对比了 PHP 的 Swoole、Java 多线程并行处理方案,它们的 CPU 和内存消耗比 Golang 的 Goroutine 要高出很多,并且并行请求数量会受到资源的限制,在高并发的情况下如果控制不当会导致服务崩溃。而使用 Goroutine 实现的并发代理,可以轻松支持千万级别的并发请求。

图 3:并行与并发

Golang 并发代理实现

代理服务按请求的处理流程,可以划分为 HTTP Server ——> 参数处理——> 并行请求 (协程调度)——> HTTP 模块 ——> API 层。目前我们的方案支持 HTTP/HTTPS 协议的请求。

图 4:并发代理架构图

各模块功能概要

  1. HTTP Sever:使用 Go 语言 httpserver package 实现,用于接收和处理有代理需求的上游模块的 HTTP 请求;

  2. 参数处理:根据定义好的交互协议,将上游模块的请求解析为并行请求商品、游记等下游模块的请求任务;

  3. 协程调度:使用 Go 语言的 Goroutine 实现,负责执行对下游模块的并发请求任务;

  4. HTTP 模块:使用 Go 语言的 ioutil/http package 实现,负责与下游 API 模块以 HTTP 协议形式交互;

  5. API 模块:将下游模块的函数调用封装为 TCP/IP接口,将函数形式交互变为 HTTP 接口形式交互。

搜索业务应用代理后,整体架构变化为:

图 5:并发代理在搜索业务中的应用

小结与后续规划

基于 Golang 的并发代理在马蜂窝搜索业务中已经使用了一段时间,很好地解决了之前存在的一些问题。目前,搜索服务平均耗时已经降低到240ms 左右,架构的可用性和可扩展性也得到很大提升,并且有效提高了系统资源的利用率。

现在并发代理只支持 HTTP,后续会增加 RPC,来更好地支持整体的服务化改造。在推进和实施搜索架构升级的过程中,我们也会把更多的经验分享出来,希望大家持续关注。

本文作者:王江涛,马蜂窝搜索推荐研发工程师。

关注马蜂窝技术,找到更多你想要的内容

这篇关于马蜂窝搜索基于 Golang 并发代理的一次架构升级的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/201639

相关文章

Spring Boot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)

《SpringBoot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)》本文将以一个实际案例(用户管理系统)为例,详细解析SpringBoot中Co... 目录引言:为什么学习Spring Boot分层架构?第一部分:Spring Boot的整体架构1.1

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

Java 结构化并发Structured Concurrency实践举例

《Java结构化并发StructuredConcurrency实践举例》Java21结构化并发通过作用域和任务句柄统一管理并发生命周期,解决线程泄漏与任务追踪问题,提升代码安全性和可观测性,其核心... 目录一、结构化并发的核心概念与设计目标二、结构化并发的核心组件(一)作用域(Scopes)(二)任务句柄

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

Ubuntu如何升级Python版本

《Ubuntu如何升级Python版本》Ubuntu22.04Docker中,安装Python3.11后,使用update-alternatives设置为默认版本,最后用python3-V验证... 目China编程录问题描述前提环境解决方法总结问题描述Ubuntu22.04系统自带python3.10,想升级

解决升级JDK报错:module java.base does not“opens java.lang.reflect“to unnamed module问题

《解决升级JDK报错:modulejava.basedoesnot“opensjava.lang.reflect“tounnamedmodule问题》SpringBoot启动错误源于Jav... 目录问题描述原因分析解决方案总结问题描述启动sprintboot时报以下错误原因分析编程异js常是由Ja

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

MySQL中处理数据的并发一致性的实现示例

《MySQL中处理数据的并发一致性的实现示例》在MySQL中处理数据的并发一致性是确保多个用户或应用程序同时访问和修改数据库时,不会导致数据冲突、数据丢失或数据不一致,MySQL通过事务和锁机制来管理... 目录一、事务(Transactions)1. 事务控制语句二、锁(Locks)1. 锁类型2. 锁粒

Linux升级或者切换python版本实现方式

《Linux升级或者切换python版本实现方式》本文介绍在Ubuntu/Debian系统升级Python至3.11或更高版本的方法,通过查看版本列表并选择新版本进行全局修改,需注意自动与手动模式的选... 目录升级系统python版本 (适用于全局修改)对于Ubuntu/Debian系统安装后,验证Pyt