乐趣区

关于后端:服务优化发现问题解决报警提高稳定性

作为一名程序员,咱们不能只关注代码的实现和上线,而漠视了线上环境的运行和优化。

近期遇到了两个线上服务的问题,一个后端利用和一个前端我的项目,它们存在一些 bug 和历史遗留问题。为了不影响用户的应用体验,决定对它们进行一次优化。

后端服务

这个后端服务是年初的时候有共事到职了,交到了我这里,没接手的时候不晓得,没想到接手后,到处都是问题,天天各种报警,基本上隔三差五就要重启。

尽管一开始的时候晓得这个服务不是很稳固,日常会有一些队列音讯沉积,然而不在本人手上,不晓得问题会这么多,动不动就沉积上亿条音讯,天天慢 SQL 和高负载报警。

平时工作日的时候收到报警不是很在意,棘手重启一下就算了,然而当每次周末或者出门在外的时候,收到报警心里还是蛮荒的。

抱着做一个问题的终结者的想法,最初还是筹备花工夫把这个服务做一下手术,从根本上解决问题。

成果

先说一下成果,这个服务从优化过后,基本上除了迭代就再也没有须要重新启动过,更不存在隔三差五的重启,当初每天的报警量从之前的一天几百条变为 0,队列无任何沉积。

优化过程

优化的过程中最难的是发现问题,只有能精准的找到问题所在,解决起来还是很容易的。

优化次要分两步:1. 解决慢 SQL;2. 解决沉积报警;

慢 SQL

解决慢 SQL 的思路很简略,依据慢 SQL 日志,找到对应的慢 SQL 进行优化即可。优化能够从两个方向来进行,一种是基于 SQL 自身来进行优化,另一种是能够通过缓存来解决。这里须要依据具体的业务来抉择,如果不是常常变动的数据,则能够通过减少缓存来解决,刚好我这里就能够满足。

通过剖析能够通过减少 Redis 缓存来解决这个问题,所以通过引入的 Redis 解决了慢 SQL 问题。

音讯沉积

队列音讯沉积的解决形式无非也就是两种,缩小数据量,放慢处理速度。

音讯队列外面的音讯因为是上游发过来的,没方法从发送方进行缩小,不过剖析了一下音讯类型,发现有很多音讯的类型是齐全不须要关怀的,所以第一步减少音讯过滤,将无用的音讯间接提交掉。

另外之前遇到音讯沉积的时候,察看到生产音讯的 TPS 特地低,有时候只有个位数,齐全不失常,而且每次重启过后 TPS 能够达到几千的级别,并且每次沉积的时候在日志层面都有一些“断开连接”的谬误。

所以从日志层面剖析,必定是生产线程出了问题,导致生产能力降落从而沉积,从而问题就转变为为什么线程会出现异常。

认真查了下利用层面的监控,发现利用有频繁的 FullGC 产生,奇怪的是为什么频繁 FullGc 却没有触发报警呢?看了一眼几乎要吐血,因为 FullGc 的报警开关被关了。。。

至此基本上能晓得问题的起因了,因为产生了 FullGc 导致 STW,而后生产线程挂了,导致音讯沉积,重启后内存开释从新进行生产。接下来的问题就转变为排查 FullGc 的起因了。

排查 FullGc 的根本流程首先必定是 dump 一下内存的 heap,而后剖析一下内存泄露的代码块。通过 dump 下来的日志,发现在代码中应用 ThreadLocal,然而没有开释,从而导致频次的 FullGc。问题到这基本上也解决了,批改了相干的中央,从新上线,稳固运行。

至此没有沉积,没有报警,没有重启,爽歪歪!

总结

敬畏线上,不要放过任何一个线上的异样和报警!

有时候问题的表象并不是真正的起因,咱们须要精准的找到本源,解决问题最难的中央是找到问题!

别干轻易敞开线上监控报警的事件!

另外之所以能疾速的定位到问题所在也是因为零碎有着很好的异样监控,能够监测到慢 SQL 和沉积报警,这也通知咱们平时的服务监控是很重要的。

前端我的项目

之前有个外部服务,在部署服务的时候,nginx 配置了 httphttps 两个 server,公司外部应用的时候始终都用的是 https,后果明天经营共事忽然说拜访不了了,通过观察发现是 http 协定拜访不通。

失常的逻辑是如果用户在地址栏间接输出 xxx.com 的时候默认是走的 http 协定 80 端口,在 nginx 层会转发到 https 的 443 端口,也就是会有一个重定向的过程。

查看了一下 nginx 的配置文件,发现在 80 这个 server 外面没有配置 server_name,批改如下就好了。

只能说太大意了

server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  xxx.com www.xxx.com;
        root         /usr/share/nginx/html;
                return 301 https://$server_name$request_uri;
    }
    
 server {
        listen       443 ssl http2 default_server;
        listen       [::]:443 ssl http2 default_server;
        server_name  xxx.com www.xxx.com;
        root         /usr/share/nginx/html;

        ssl_certificate "xxx.crt";
        ssl_certificate_key "xxx.key";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers PROFILE=SYSTEM;
        ssl_prefer_server_ciphers on;
        location / {
                    proxy_pass http://backend$request_uri;
            proxy_set_header  Host $host:$server_port;
            proxy_set_header  X-Real-IP  $remote_addr;
                    proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            client_max_body_size  10m;
            # 实现前端打字成果
                    proxy_buffering off;
        }

        error_page 404 /404.html;
            location = /40x.html { }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {}}

页面加载工夫优化

另外在应用的时候还发现,有的时候网页或者手机关上网站须要好几秒能力把整个页面渲染进去,本人用起来都很不爽更别说什么用户体验了。

通过浏览器的 network 栏目,发现网站在加载的时候会联网拜访一个 css 文件,这个 css 文件外面会用到很多字体文件,而且这些字体文件也是从网络实时下载的。

看了下 Issue 发现也有其他人遇到了这个问题,这个更夸大间接加载了 42 秒。

通过将这个问题提交下载下来,而后间接拜访,不再从网络上下载。手动将这个 css 文件下载下来过后,发现外面还援用的很多字体文件,如下所示,总共 388 个,这样是手动一个个下载那不是要了老命。

所以须要通过脚本来进行下载,通过询问 ChatGPT 让它帮咱们写一个 go 语言脚本来执行这个逻辑。

残缺的代码如下所示

package main

import (
    "bufio"
    "fmt"
    "net/http"
    "os"
    "regexp"
    "strings"
)

func main() {
    const cssPath = "css2.css"
    const fontDir = "fonts"
    const urlPrefix = "https:"

    // 读取 CSS 文件
    cssFile, err := os.Open(cssPath)
    if err != nil {panic(fmt.Sprintf("Failed to open %s: %s", cssPath, err))
    }
    defer cssFile.Close()

    // 创立字体存储目录
    if err := os.MkdirAll(fontDir, 0755); err != nil {panic(fmt.Sprintf("Failed to create font directory: %s", err))
    }

    // 解析 CSS 文件
    scanner := bufio.NewScanner(cssFile)
    for scanner.Scan() {line := scanner.Text()
        if strings.Contains(line, "url(") && strings.Contains(line, ".woff2") {
            // 应用正则表达式提取 WOFF2 文件 URL
            re := regexp.MustCompile(`url\(["']?([^"']+\.(woff2))`)
            matches := re.FindStringSubmatch(line)

            if len(matches) >= 2 {fontUrl := matches[1]
                if strings.HasPrefix(fontUrl, "//") {fontUrl = urlPrefix + fontUrl}

                // 下载 WOFF2 文件
                fmt.Printf("Downloading %s...\n", fontUrl)
                res, err := http.Get(fontUrl)
                if err != nil {fmt.Printf("Failed to download %s: %s\n", fontUrl, err)
                    continue
                }
                defer res.Body.Close()
                 // 创立字体文件
                fontPath := fmt.Sprintf("%s/%s", fontDir, matches2)
                fontFile, err := os.Create(fontPath)
                if err != nil {fmt.Printf("Failed to create font file %s: %s\n", fontPath, err)
                    continue
                }
                defer fontFile.Close()

                // 写入字体文件
                _, err = fontFile.ReadFrom(res.Body)
                if err != nil {fmt.Printf("Failed to write to font file %s: %s\n", fontPath, err)
                } else {fmt.Printf("Font file %s downloaded.\n", fontPath)
                }
            }
        }
    }
}

ChatGPT 不仅给出了代码,还给出了解释

此脚本遵循以下步骤:

  1. 关上 CSS 文件
  2. 逐行读取文件内容
  3. 对每一行应用正则表达式进行匹配,查找字体文件链接
  4. 应用 http.Get() 发送 HTTP 申请下载字体文件
  5. 创立本地文件,并将字体数据写入该文件

下面代码通过 go run download.go 间接运行脚本发现是能够失常运行的,然而一开始是有个问题的那就是没有思考到多个文件会笼罩,咱们简略批改几行就能够失常应用了。

index := strings.LastIndex(matches[1], "/")
filename := matches[1][index+1:]
// 创立字体文件
fontPath := fmt.Sprintf("%s/%s", fontDir, filename)

运行后的成果是这样的,全副下载下来,咱们须要做的就是在 css 文件中通过快捷键全副替换一下就好了。

优化过后文件的下载速度稳固了一秒以内,尽管还能够通过 CDN 等形式进一步优化,然而感觉目前是没必要的。当初剩下的就是受限于服务器的宽带和网络了,不过整体是能够承受的了。

试了下挪动端关上的速度也有所晋升。

总结

通过下面的过程,能够看到 ChatGPT 是真的能够帮咱们进步工作效率的,写一个脚本没什么难度,花点工夫也是能够写进去的,然而有了这样的工具大大的节俭了咱们的工夫,对于生成的内容须要能看懂和能进行批改就行了。

然而工具也只是工具,还是要学会应用才行,不能太自觉的依赖。

退出移动版