教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang bufio.newscanner如何超时跳出_贝壳找房小程序从PHP到Golang的跃迁之路

golang bufio.newscanner如何超时跳出_贝壳找房小程序从PHP到Golang的跃迁之路

发布时间:2022-02-01   编辑:jiaochengji.com
教程集为您提供golang bufio.newscanner如何超时跳出,贝壳找房小程序从PHP到Golang的跃迁之路等资源,欢迎您收藏本站,我们将为您提供最新的golang bufio.newscanner如何超时跳出,贝壳找房小程序从PHP到Golang的跃迁之路资源

1. 前言

1.1 PHP是最好的语言

PHP确实有非常强大的优势。对于中小型Web服务,业务具有高度不确定性,产品迭代速度是第一目标,非常适合使用PHP作为创业启动语言。

1.2 使用PHP遇到的问题

小程序前台项目的特点是:IO密集型服务

小程序目前依赖众多下层服务,一个普通的小区接口依赖的下层API达到11个之多。在阻塞IO的模式下,所有等待延迟串行叠加,非常容易给前端造成比较高的后台等待,影响用户体验。特别是遇到个别服务的部分请求出现透传DB,SQL效率不高时,就更会雪上加霜,499突增,威胁到服务稳定性。

这样一来,对于一个PHP的N层调用场景来说,任何一层服务的失败都会直接影响终端获取数据的成功率,当每层及时成功返回数据的概率为P,则它的最终成功率就会变成:

P=(1-P1)*(1-P2)*(1-P3)*……*(1-PN)

于是,当前服务层要想优化,比较好的选择是:增加cache。

好处是减少向后请求,透传db的情况也会顺应得到一定的改善,耗时也会因为跳过了逻辑计算而得到收敛。

其后果是牺牲了实时性,换取性能的提高。然而,这可能导致房屋标价,出现短时显示不稳定;租价相差几百,卖价相差几千,都会让客户抓狂。性能提高的程度要看缓存命中率;过期时间长短需要平衡,过久则时效性差,过短则命中率不高。核心城市的购房者和卖房者相对密度大一些,而二三线城市就没那么好了。

多层cache情况下,每层设置cache过期时间为T,当有变更发生需要最终生效时,其最糟糕生效时间是:

T=T1 T2 T3 ……Tn

综上,伴随着调用模块数量的增加,延迟超标的接口会越来越多;增加cache之后,信息展示的不确定性会增多。左右为难,然如何破局?

1.3 选择Golang的原因

了解过Golang的同学,以下部分特性可能比较吸引你:

<ul><li>Goroutine协程</li><li>多路复用</li><li>Channel通道</li></ul>

小程序团队主要考虑以下几个点:

<ul><li>协程:将多个模块的拉取和格式化作为独立单元,进行单元级别并行</li><li>异步IO:节省CPU,提高服务并发能力</li><li>Channel通道:可以将一些轻量的异步动作用协程 channel实现,简化架构</li><li>强大的标准库:可以实现和PHP等价的逻辑</li><li>稳定性:常驻运行也依然稳定</li><li>丰富的工具:解决单元测试、代码风险检查</li><li>部署运行环境:贝壳已经支持线上运行和托管Golang服务</li></ul>

最终的收益将会体现为:

<ul><li>降低当前用户体验延迟</li><li>遏制接口延迟持续恶化</li><li>提高服务的稳定性</li></ul>

有的人说,为什么不使用swoole呢?从BAT的应用情况来看,swoole并未受到追捧;且swoole也可能像hhvm一样,只是过渡期选择。

有的人说,guzzle不香吗?跟协程能做的事情来讲,我们更看重模块级别的并行,而不仅仅是IO。

1.4 Golang为什么快

按照Golang布道师Dave Cheney在2014年Gocon大会上的分享来看,其中5点值得注意:

<ol start="1"><li>变量的处理和存储一个int32的变量只占用4字节,但是在python中需要24字节,在Java中需要16-24字节。内存占用少,使得CPU的cache效率更高。数组变量紧凑的内存结构,避免了无谓的指针跳转。</li><li>函数内联虽然增加了包大小,但是减少了函数调用的开销,特别是很多短小的函数。</li><li>垃圾回收通过逃逸分析方法,使得更多的变量申请空间可以在栈上得到分配,减轻了堆上资源垃圾回收的压力。一般程序员不需要关心是在栈上还是堆上。</li><li>协程在利用线程对进程来优化的思路基础上,进行了延伸。协程非常轻量,再加上协程切换一般发生在阻塞、系统调用、垃圾回收等时机,从而减少了等待。每个Go进程只需要少量系统线程,Go的runtime会将协程放到空闲操作系统线程上运行。</li><li>Goroutine的栈管理通过去掉guard page,在函数调用时增加栈空间检查的机制,并在必要时自动分配更多空间的方式,使得初始的空间可以很小,goroutine可以很轻量。而函数调用带来的频繁扩缩容,利用连续栈分配更大空间而得到解决。</li></ol>

2. 实践

2.1 目标

当时域名下114个API,大部分的延迟都不高,在调用量比较大,延迟比较突出的二手接口中,我们锁定了部分接口作为改造对象。

目标

二手业务8个接口

投入人力

3人

时间成本

6周

编码量

约2w行

2.2 实施步骤

2.2.1 业务框架

没有业务框架,直接进行业务重构就是耍流氓。但19年,公司还没有出品Golang业务框架,我们得自己搞定。

先向杨宇(比克)学习了沙场项目,获得了在贝壳服务器安装、运行、部署Golang服务的基本经验。宇哥多次指导,深表感谢。

使用涉及的工具有:

<ul><li>go mod:搞定代码包管理</li><li>go proxy:解决拉取内外仓库代码的差异问题</li><li>gin:搭建基础Golang web服务的简洁框架</li></ul>

2.2.2 跑通第一个接口

万丈高楼从地起,我们进行了以下关键实践:

<ul><li>协程并行封装:避免每个人单独封装,单独调试的麻烦。</li><li>对接部署环境:封装build逻辑,实现打包编译、环境变量管理。</li><li>超时管理:进行区分环境的不同超时设置,包括框架超时和http调用超时;将connect超时和read超时区分开。</li><li>签名验签:解决前后端验签实现,解决http调用验签实现。</li><li>日志分级:完成warning、fatal、panic、access等日志的分封。</li><li>监控告警:利用日志收集到fast,通过看板和日志报警管理稳定性,最为重要的是内存监控和协程数监控。</li><li>单元测试:利用go test实现基础代码的单测,避免偶现bug在未来长时间内不定时跑出来,并产生灵异现象。</li><li>local cache:在全局下发的一些公共数据上,可以达到比较好的加速效果。</li><li>环境区分:通过不同环境加载配置,实现环境隔离。</li><li>配置治理:将环境变量、命令行参数、配置文件统一规范为配置文件,保持单一入口和拉起程序的简化。</li><li>打包机联调:将环境差异干预配置到打包机,避免同一个文件在git仓库管理多个版本。</li><li>Diff平台:通过diff工具进行接口字段对比,避免逻辑丢失和差异。</li><li>自动部署:通过CI平台的Jenkins来实现代码到测试环境的自动部署,快速提高bug的解决和验测节奏。</li><li>热编译:通过对开源项目gowatch的二次开发,实现了代码自动编译和附带工具运行,避免写一行编一次。</li><li>目录管理:主要分离了base、data、model、controller几层,最大限度保持大家写PHP的习惯迁移。</li><li>服务保活:和OP一起适配systemd服务,在服务宕机时可以秒起进程。</li></ul>

在我们完成第一个接口nearby的逻辑编码的过程中,以上80%的特性也一起附带实现了,整个过程为10个整天(对语言不熟悉的话,需要适当添加时间;剩下20%是陆续边改造边实现的)。另外7个接口的逻辑重构工作,就可以据此为模板展开了。

但此时接口还没有增加redis缓存,新接口延迟比老PHP接口明显高出一截。

2.2.3 优化接口

加缓存可能是接口优化最直接有效的手段了,而且成本一般都不是问题;但请神容易送神难。丢失的实时性很难找回,带来的问题很难定位。数据显示错误时,是哪一层缓存错了,没有谁能说得清。所以我们打算在不加缓存的情况下,去优化延迟。

Golang最大的特性是协程并发,所以我们迫不及待开始用协程来试试效果。

在nearby接口中,我们分析出了2层逻辑可并行的场景:

<ul><li>getErshouList、getNewfangList、getZufangList实现并行</li><li>SearchResblockHouseSell和GetResblockSell实现并行</li></ul>

这样一来,解决的延迟就轻松超过了100ms。

在后来的xiaoqu接口中,有更加优异的表现,整体实现了4层逻辑并行。

并行代码难写吗?并非如此。

封装一层是降低代码复杂度的有效武器;如果不是,就再封装一层。base包下实现一个GoWait方法,并行调用就会非常优雅:

关于缓存,我们斟酌再三,最后还是在房源列表增加了cache,理由为:

假如,用户在列表看到10条房源信息,当他点击第一条,进入详情页能看到最实时的信息,时效性是以详情页为准,所以体验没有问题;

当用户返回列表点击第二条时,列表未刷新,则列表展示的第二条可能已经过时;如果第二条已经被删掉,点击会出现404;删除的那条,用户不一定会点击;此时我们如果更新列表,则可能发生位置或者或多或少的变化,用户感受到了不稳定;

所以最终,我们在房源列表这里对列表房源信息增加了redis的cache,保证了底层页的时效性和列表页的稳定感。

2.2.4 性能收集

一个事物,如果你不能准确的观察它,那你就很难控制它。

Golang服务容易暴露出线程安全、panic、泄露、空指针引用等问题,最为常见的就是协程泄露。监控协程数,是区分协程泄露还是句柄泄露的关键举措。

其次是资源泄露,如果服务频繁宕掉,极可能是panic未捕获,或者内存过高引起的oom。panic的问题可以通过添加默认recover的方式解决。内存过高被杀会影响服务稳定性,好在内存不是一分钟就涨上去的,我们可以在达到一定限度时就开始介入,避免宕机才后知后觉。

幸运的是,Golang的runtime可以轻松实现协程数和内存(并非实际内存)的获取。利用fast天眼平台的看板视图,很容易就做到服务的监控走势图:

如果是每天周期性波动,可以暂时松一口气。部分边界问题可能后期暴露,从而打破平静,建议增加阈值告警,避免上升为故障才后知后觉。这方面已经有不少先例了,痛过的人自然明白。

2.2.5 配套工具

为了更好的保证golang服务的质量,尽早发现问题,我们还引入了以下工具,大家可以根据情况选用:

go generate

自动生成代码

go fmt

代码格式化

go mod tidy

精简代码包

go mod vendor

拷贝包到vendor目录

go vet

检查代码错误

golint

检查代码风格

goswagger

Swag文档工具

gowatch

热编译工具

配合起来,就能够实现如图的连续编码体验:

大概就是三个节点的循环,体验跟PHP开发不相上下:

好的体验就能促就更多的人参与,目前项目里面5个人可以熟练进行Golang开发,新需求优先选择Golang服务实现。这样就不会因为个别人跑路而导致项目无人维护的了。大家都非常喜欢Golang这种标准、简洁、稳定、强大、易上手的语言。

2.2.6 灰度上线

这将是我们团队年度最重大变更之一,需要严格把控风险。把控风险最好的方式,就是将功能做成可灰度,避免一刀切。

我们在前端启动接口中下发配置,允许前端在命中灰度的情况下将调用域名切换到新接口。而新接口在调用方式上完全等同老接口,包括path、method、参数结构、验签、header规则。所以仅有域名差异,前端比较容易实现。

通过apollo实现离线配置,实时控制灰度程度。

当前端发现新域名不可用时,兜底使用老域名容错。

2.3 最终收益

通过此番改造,产生了以下效果:

<ul><li>稳定性提升0.02%左右</li><li>平均延迟降低15.6%</li><li>TP90延迟降低37.5%</li></ul>

而这个收益是在我们没有利用redis来给业务模块大面积覆盖cache而损失时效性的基础上得到的,同时未来也会因为可以使用并行,从而接口延迟快速上涨的情况得到很大的遏制,从而也减少了因为延迟上涨到不可接受,而被迫进行的重构工作。

服务上线8个月以来,一直稳定运行,且保持了健康快速的迭代,没有故障,这让我们有底气将经验分享给小伙伴们。

2.4 进一步规划

使用公司统一框架,完成组件标准化。

3. 结语

Golang是一门比较新的语言,用好了可以在不明显增加负担的情况下,带来不错的效果。希望在能给业务带来明显收益的前提下,更多的项目能够试用,并从中尝到甜头。上到Web服务,下到框架、组件、中间件,Golang都有很不错的应用,前途看好。

到此这篇关于“golang bufio.newscanner如何超时跳出_贝壳找房小程序从PHP到Golang的跃迁之路”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang bufio.newscanner如何超时跳出_贝壳找房小程序从PHP到Golang的跃迁之路
golang会取代php吗
深度解密Go语言之 map
【golang】map原理
golang微服务框架对比_斗鱼开源首秀——基于 Go 的微服务框架 Jupiter
学习golang开始前的准备工作
[GO语言基础] 一.为什么我要学习Golang以及GO语言入门普及
腾讯微服务框架 Tars 的 Go 性能提升之路
Golang map之tophash详解
由浅入深聊聊Golang的map

[关闭]
~ ~