Nginx Ingress http 请求 413 状态码问题及解决方法

问题 最近在调用一个上传文件的接口时,发现接口调用响应状态码为 413,并且控制台显示跨域错误信息。查找了相关信息,得知 413 状态码表示请求的包体过大导致的。 出现这种情况,我想到了 2 种解决方案:1) 调整上传文件的方式 2) 调整网关的参数。 综合目前的现况,采取了第二种方式调整网关客户端请求体最大值的参数。 解决 通过查阅 nginx ingress 的文档,得知可以添加 nginx.ingress.kubernetes.io/proxy-body-size 注解来设置请求体的最大值,设置 nginx.ingress.kubernetes.io/proxy-body-size 值为合适的值后,再请求接口发现接口顺利响应。 小结 本文介绍了客户端请求接口时,由于 nginx 默认 proxy-body-size 参数太小,导致请求 413 的问题及相应的解决方案。 参考 https://opendocs.alipay.com/support/01rb44 https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md#custom-max-body-size

九月 16, 2023 · overstarry

playwright-go 浏览器截图

以前的文章介绍了 chromedp 进行浏览器网页截图,这次介绍一种新的网页截图的方法——即使用 playwright 进行浏览器网页截图。 playwright 介绍 Playwright 是一个用于自动化浏览器操作的开源工具集。它由微软开发并于 2020 年发布,旨在提供一种跨浏览器、跨平台的解决方案,可用于测试 Web 应用程序、编写爬虫、执行自动化任务等。 Playwright 支持多种主流浏览器,包括 Chrome、Firefox、Safari 和 Edge,它提供了一组简单易用的 API,可以模拟用户与 Web 页面的交互行为,例如点击、填写表单、导航等。与其他类似工具相比,Playwright 的一个重要特点是它的跨浏览器支持,这意味着你可以使用相同的代码在不同浏览器上运行你的自动化任务,而不需要为每个浏览器单独编写代码。 Playwright 还提供了强大的调试功能,可以帮助开发人员在自动化过程中定位和解决问题。它支持截图和录制操作,使得调试变得更加直观和高效。 另外,Playwright 还具有一些高级功能,例如可以模拟不同的设备、网络环境和地理位置,以及支持并发执行多个浏览器实例等,这些功能使得它在编写复杂的自动化任务时非常有用。 由于我日常主要使用 go 语言进行开发,所以本文的内容主要以 playwright 的 go 模块 playwright-go 为主要介绍。 安装 go get -u github.com/playwright-community/playwright-go 安装相关浏览器和操作系统依赖项: go run github.com/playwright-community/playwright-go/cmd/playwright@latest install --with-deps # Or go install github.com/playwright-community/playwright-go/cmd/playwright@latest playwright install --with-deps 也可以在代码中使用以下代码安装:err := playwright.Install() 通过安装截图可以看出安装了 3 大主流浏览器和 ffmpeg。 例子 接下来我们看一个官方的例子,从 Hacker News 中抓取当前投票最高的项目。 package main import ( "fmt" "log" "github.com/playwright-community/playwright-go" ) func main() { pw, err := playwright.Run() if err != nil { log.Fatalf("could not start playwright: %v", err) } browser, err := pw.Chromium.Launch() if err != nil { log.Fatalf("could not launch browser: %v", err) } page, err := browser.NewPage() if err != nil { log.Fatalf("could not create page: %v", err) } if _, err = page.Goto("https://news.ycombinator.com"); err != nil { log.Fatalf("could not goto: %v", err) } entries, err := page.Locator(".athing").All() if err != nil { log.Fatalf("could not get entries: %v", err) } for i, entry := range entries { title, err := entry.Locator("td.title > span > a").TextContent() if err != nil { log.Fatalf("could not get text content: %v", err) } fmt.Printf("%d: %s\n", i+1, title) } if err = browser.Close(); err != nil { log.Fatalf("could not close browser: %v", err) } if err = pw.Stop(); err != nil { log.Fatalf("could not stop Playwright: %v", err) } } ...

九月 2, 2023 · overstarry

使用 Google Api Go Client 调用 Google Adsense 报告接口失败的问题及解决方案

问题 最近需要使用 Google Api Go Client 获取 Google Adsense 的数据,由于常规 generate 接口返回的数据是有 10w 行的限制,不满足目前的使用需要,于是使用了 csv 的生成方法 generateCsv,和 generate 相比 generateCsv 能返回更多的数据并且不需要改动太多参数。 在调用 generateCsv 接口时,发现接口不能返回正确数据,提示错误,错误信息如下: panic: invalid character 'D' looking for beginning of value 根据错误信息可以得知错误应该是解析错误,通过方法接口和源代码看出,接口返回的是 csv 类型的数据,但 api 只针对 json 进行解析,没有判断数据类型,导致解析失败。 解决 找到问题后,我立即就想到一个方法,就是在 decode 代码里添加 csv 格式的判断,尝试了一下,由于对 csv 解析方法不熟悉,并且修改 Client 代码会导致模块更新不方便 (客户端代码都是生成的,于是决定另寻它法。 通过寻找各种资料 issue 后,找到了一个可能可行的方法,就是创建手动创建 http 连接发送请求。 根据相应的文档,很快就写好了相应的代码: package main import ( "context" "fmt" "golang.org/x/oauth2" "golang.org/x/oauth2/google" "google.golang.org/api/adsense/v2" "google.golang.org/api/option" "google.golang.org/api/transport/http" "io" "net/url" ) func main() { ctx := context.Background() var config = &oauth2.Config{ ClientID: "xxx.apps.googleusercontent.com", // from https://console.developers.google.com/project/<your-project-id>/apiui/credential ClientSecret: "GOCSPX-xxxxxx", // from https://console.developers.google.com/project/<your-project-id>/apiui/credential Endpoint: google.Endpoint, Scopes: []string{adsense.AdsenseReadonlyScope}, RedirectURL: "https://developers.google.com", } //token := newOAuthClient(ctx, config) client, _, err := http.NewClient(ctx, option.WithTokenSource(config.TokenSource(ctx, &oauth2.Token{RefreshToken: "code"}))) if err != nil { return } URL, err := url.Parse(fmt.Sprintf("https://adsense.googleapis.com/v2/accounts/pub-123123/reports:generateCsv")) if err != nil { return } query := URL.Query() query.Set("dateRange", "YESTERDAY") query.Set("dimensions", "URL_CHANNEL_NAME") query.Set("metrics", "PAGE_VIEWS") URL.RawQuery = query.Encode() resp, err := client.Get(URL.String()) if err != nil { return } defer resp.Body.Close() all, err := io.ReadAll(resp.Body) if err != nil { return } fmt.Println(string(all)) } 主要逻辑就是创建到 google cloud service 的连接,调用 generateCsv 接口即可。 ...

八月 26, 2023 · overstarry

gonew 介绍

前几周。在关注的 go 提案每周会议中,发现了一条比较感兴趣的内容:gonew: templates for new modules ,通过标题可以猜到 gonew 应该是一个通过配置项目模板生成新项目的模块。 通过对 discussions 中 rsc 所描述的内容进行分析,可以得知为什么要启动这么一个新项目: go 团队经常收到用户的请求,想要通过模板启动一个新项目,即以某种基本的项目模板来创建一个新 Go module。Russ 私下编写了一个实现这个功能的小工具:rsc.io/tmp/gonew. Russ 在 google 内部宣传该工具后,Google 内部的一些团队便定制了一些模板 (template) ,尤其是 ServiceWeaver 团队的响应尤为积极。这一切最终让 Russ 决定引入 golang.org/x/tools/cmd/gonew。 gonew 工具的引入大幅简化了 Go 项目的创建,同时由于对自定义项目模板的支持,也可以提高 Go 项目的标准化水平。目前 gonew 工具是实验性的,后续可能会增加新的特性,但目前的核心功能是会保留的。 通过对 discussions 中社区用户开发着的回应可以看出,大家纷纷讲述了没有 gonew 前所使用的工具,并对 gonew 建言献策,可以看出大部分的开发者都十分欢迎这个新功能的。 接下来就由我来介绍 gonew。 安装 通过以下命令安装 gonew: go install golang.org/x/tools/cmd/gonew@latest $ go install golang.org/x/tools/cmd/gonew@latest go: downloading golang.org/x/tools v0.12.0 go: downloading golang.org/x/mod v0.12.0 执行 gonew: ...

八月 19, 2023 · overstarry

ent 相同列名排序问题解决

前言 最近在开发的时候,需要进行数据库计算,主要是根据表中的某些字段进行汇总计算,但由于数据库表中已有同名字段名,ent 不会使用计算后的指标,默认使用 schema 中定义的字段,导致无法返回正确的结果。 针对这种情况,我能想到的方法有 2 种:1) 不使用同名的字段名 2) 查找 ent 是否有相关的解决方案。 这里我采用了第二种方法,查找相关的 issues, 通过查找相关 issue,找到了相关的解决方案:ent 的 sql/modifier 特性。 场景重现 定义一个新的数据库表结构,结构如下: func (Ad) Fields() []ent.Field { return []ent.Field{ field.Float("estimated_earnings"), field.Int("page_views"), field.Time("date"), field.Float("page_views_rpm").Optional(), } } page_views_rpm 字段是由 estimated_earnings 和 page_views 计算而来。 编写相应的查询代码: package main import ( "context" "entgo.io/ent/dialect/sql" "fmt" _ "github.com/lib/pq" "log" "modifier-demo/ent" "modifier-demo/ent/ad" "time" ) type Ads struct { EstimatedEarnings float64 `json:"estimated_earnings"` PageViews int64 `json:"page_views"` Date time.Time `json:"date"` PageViewsRpm float64 `json:"page_views_rpm"` } func main() { client, err := ent.Open("postgres", "host=127.0.0.1 port=5432 sslmode=disable user=postgres dbname=data_test password=mysecretpassword") if err != nil { log.Fatalf("failed opening connection to postgres: %v", err) } defer client.Close() // Run the auto migration tool. if err := client.Schema.Create(context.Background()); err != nil { log.Fatalf("failed creating schema resources: %v", err) } var a []Ads err = client.Debug().Ad.Query().Order(ent.Desc(ad.FieldPageViewsRpm)).GroupBy(ad.FieldDate).Aggregate(func(selector *sql.Selector) string { return sql.As(" CAST(COALESCE(SUM(estimated_earnings) / NULLIF(SUM(page_views)*1.0, 0.0)*1000, 0)AS numeric(10,2))", "page_views_rpm") }).Scan(context.TODO(), &a) if err != nil { return } fmt.Println(a) } 主要是根据 date 来汇总并重新计算 page_views_rpm 字段,运行代码后发现没有成功输出,打印后发现 ent 使用了旧的 PageViewsRpm 字段进行排序,导致 sql 无法顺利运行。 ...

八月 12, 2023 · overstarry

Golang_embed 简单介绍

最近需要使用 golang1.16 中的功能 embed ,本文简单记录下 embed 的使用。 embed 介绍 Go 1.16 引入了 embed 包,允许我们在编译时将静态文件(例如 .go、.html、.css、.js 等)嵌入到 Go 源文件中。这在构建静态网站、单页应用程序(SPA)和其他项目时非常有用。 主要有几个优点: 方便部署:不需要再部署静态资源文件,所有的资源都直接嵌入到可执行文件中。 安全:用户无法直接访问或修改嵌入的文件。 版本管理:和 Go 代码一起版本控制。 使用 嵌入为字符串 可以将文件内容保存到字符串变量中。 package main import ( _ "embed" "fmt" ) //go:embed hello.txt var s string func main() { fmt.Println(s) } 文件路径下有个 hello.txt,内容如下:hello, overstarry,代码运行输出:hello, overstarry 保存为 []bytes 还可以将文件内容保存为 []bytes 变量 package main import ( _ "embed" "fmt" ) //go:embed hello.txt var s []byte func main() { fmt.Println(string(s)) } 运行代码输出内容与上面一致。 ...

八月 5, 2023 · overstarry

Nginx 反向代理中出现的问题及解决方法

最近在使用 nginx 反向代理时遇到了一些问题,在本文记录一下问题及相应的解决方法。 问题 1 问题现象 错误日志如下: : host not found in upstream "xx.xx.vip"in /etc/nginx/conf.d/default.conf:17 nginx: [emerg] host not found in upstream "xx.xx.vip”in /etc/nginx/conf.d/default. conf:17 从错误日志可以看出这个问题主要是 nginx 无法解析相应的域名。 解决方法 怎么解决这个问题呢,我们只需添加相应的 dns 服务器即可 resolver 8.8.8.8; 。 问题 2 问题现象 错误日志如下: 2023/07/28 01:35:43 [error] 34#34: *44 SSL_do_handshake() failed (SSL: error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:SSL alert number 80) while SSL handshaking to upstream, client: 172.16.64.75, server: , request: "POST /v1/data/xx/filter HTTP/1.1", upstream: "https://xxxx:443/v1/data/xx/filter", host: "xx.xx.com", referrer: "https://xx.xx.com/user/login" 解决方法 这个问题主要是 https 相关配置的问题,我们只需添加这几行配置即可: ...

七月 28, 2023 · overstarry

使用 cert Manager 申请免费证书

cert manager 介绍 cert-manager 是一个可信证书管理器,可以自动为您的集群中的服务提供 SSL 证书和可靠的基础设施。它执行以下任务: 自动通过类似 Let’s Encrypt 和 eigene 之类的 Certificate Authority (CA) 重新生成即将过期的证书。 为您的集群中的服务自动生成证书。 提供一个组件库,可用于自签名证书或其他 CA API。 主要特性: 自动生成/重新生成证书 支持多种 CA 颁发的证书:Let’s Encrypt、自签名证书、HashiCorp Vault 等 支持多种记录类型:DNS01、HTTP01、Kubernetes Ingress 等 适用于支持 TLS 密码学的任何 Kubernetes API 对象 具有密钥轮换功能,可无缝替换即将到期的证书 流畅的 API,易于扩展 总的来说,cert-manager 可以让您将集群负载均衡的 TLS 实现自动化,减少运维负担。它主要用于解决基础设施中最常见的挑战:如何高可用地为应用提供 TLS 证书。 申请证书 接下来我来讲解本文的重点:如何使用 cert-manager 申请证书并配置于 ingress 上。 安装 cert-manager 执行 kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml 部署最新版本 cert-manager. 查看 pod 状态 部署后,过一会执行 kubectl get pods -n cert-manager 查看 pod 状态。 ...

七月 22, 2023 · overstarry

Golang i18n

最近在开发一个需求时,需要将英文转为中文,这就需要进行本地化的处理,通过查找相关的库,决定使用 gettext-go 来进行本地化的处理,本篇文章主要简单介绍 gettext-go 和 它在 k8s kubectl 中的运用。 gettext-go 简单介绍和使用 gettext-go 简单来说就是读取预先编写的 po 或 mo 文件来进行本地化的处理。 po 和 mo 文件是什么呢?接下来介绍一下 po 和 mo 文件 po 和 mo 文件介绍 .po 文件,.mo 文件是由 gettext 程序生成或者使用的源代码和编译结果。 1、.pot 文件 是一种模板文件,其实质与.po 文件一样,其中包含了从源代码中提取所有的翻译字符串的列表,主要提供给翻译人员使用。 2、.po 文件 (1)用程序 msginit 来分析 pot 文件,生成各语言对应的 po 文件,比如中文就是 zh_CN.po,法语就是 fr.po 文件。 (2)PO 是 Portable Object(可移植对象)的缩写形式,它是面向翻译人员的、提取于源代码的一种资源文件。 (3).po 文件可以用任何编辑器如 poEdit,vi,Emacs,editplus 打开,交给翻译人员来将其中的文字翻译成本国语言。 3、.mo 文件 (1)用 msgfmt 将.po 文件编译成 mo 文件,这是一个二进制文件,不能直接编辑。 (2)MO 是 Machine Object(机器对象)的缩写形式,它是面向计算机的、由.po 文件通过 GNU gettext 工具包编译而成的二进制文件,应用程序通过读取.mo 文件使自身的界面转换成用户使用的语言,如简体中文。 ...

七月 16, 2023 · overstarry

rueidis 简介

简介 rueidis 是一个快速的 Golang Redis 客户端,支持客户端缓存、Auto Pipelining、泛型 OM、RedisJSON、RedisBloom、RediSearch 等功能。 Features Auto pipelining for non-blocking redis commands RESP3 中的客户端缓存 Pub/Sub, Sharded Pub/Sub, Streams Redis Cluster, Sentinel, RedisJSON, RedisBloom, RediSearch, RedisTimeseries, 等。 具有客户端缓存和乐观锁定的通用对象映射 具有客户端缓存的分布式锁 rueidis mock OpenTelemetry 集成 Hooks and other 集成 提供类似 Go-redis API 的适配器 需要注意的是由于使用了一些 go1.20 版本才有的特性,如果想要使用低版本 go,必须安装相应的版本。 简单使用 package main import ( "context" "fmt" "github.com/redis/rueidis" ) func main() { // 创建 redis 客户端连接 client, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}}) if err != nil { panic(err) } defer client.Close() ctx := context.Background() // 执行 redis set 命令 err = client.Do(ctx, client.B().Set().Key("key1").Value("val").Nx().Build()).Error() if err != nil { panic(err) } hm, err := client.Do(ctx, client.B().Get().Key("key1").Build()).ToString() if err != nil { panic(err) } fmt.Println(hm) } go-redis 适配器 如何快速从 go-redis 切换到 rueidis 客户端呢,rueidis 提供了 rueidiscompat.NewAdapter 方法,通过 Adapter 可以使用熟悉的 go-redis 中的方法。 ...

七月 8, 2023 · overstarry