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

PostgreSQL 怎么解决 division by zero 问题

问题 最近在使用 sql 进行数值计算时,发现 sql 语句运行报错,报错信息如下:division by zero,综合分析 sql 语句得出是在进行除法运算时,除数为 0 导致的。 解决 接下来我来介绍几种解决这种问题的方法: NULLIF 和 COALESCE 函数 我们可以使用 NULLIF 函数检查变量是否是 0 值,如果是 0 则为 null。使用 COALESCE 函数检查分子和分母是否有 NULL 值,然后返回默认值。 例子: SELECT COALESCE(dividend / NULLIF(divisor, 0), default_value) FROM xx CASE 表达式 我们可以使用 case when 来检查除数是否为 0,如果是 0 则使用默认值。 例子: SELECT COALESCE(dividend / NULLIF(divisor, 0), default_value) FROM xx 最终我采用了第一种方法,顺利的解决了问题,还需要多说的是在 postgresql sql 语句使用过程中,要注意所使用函数需要的参数的类型,遇到需要转换参数类型的情况,可以使用 case 函数进行类型转换。 小结 本文讲述了我遇到的 PostgreSQL 进行数值计算时遇到的 division by zero 问题的情况及解决方法。 参考 https://www.postgresql.org/docs/current/functions-conditional.html#FUNCTIONS-NULLIF https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-EXPRESS-EVAL https://pganalyze.com/docs/log-insights/app-errors/U128 https://stackoverflow.com/questions/17681375/avoid-division-by-zero-in-postgresql

七月 1, 2023 · overstarry

Kubernetes Health check

本文我来讲解 Kubernetes 中的一个重要概念:容器的健康检查。 介绍 在 Kubernetes 中,你可以为 Pod 里的容器定义一个健康检查“探针”(Probe)。 这样,kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器镜像是否运行(来自 Docker 返回的信息)作为依据。 这种机制,是生产环境中保证应用健康存活的重要手段。 k8s 主要有三种健康检查的探针:1) LivenessProbe 存活探针 2) ReadinessProbe 就绪探针 3) StartupProbe 启动探针 kubelet 使用存活探针来确定什么时候要重启容器。例如,存活探针可以探测到应用死锁(应用程序在运行,但是无法继续执行后面的步骤)情况。重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。 存活探针的常见模式是为就绪探针使用相同的低成本 HTTP 端点,但具有更高的 failureThreshold。这样可以确保在硬性终止 Pod 之前,将观察到 Pod 在一段时间内处于非就绪状态。 kubelet 使用就绪探针可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。 kubelet 使用启动探针来了解应用容器何时启动。如果配置了这类探针,你就可以控制容器在启动成功后再进行存活性和就绪态检查,确保这些存活、就绪探针不会影响应用的启动。启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。 probe 介绍 接下来我来讲解用的较多的 2 个探针:1) LivenessProbe 存活探针 2) ReadinessProbe 就绪探针 LivenessProbe 许多应用由于长时间运行导致程序异常,需要重启服务才能继续正常使用,Kubernetes 提供了存活探针 (LivenessProbe) 来发现并处理这种情况。 我们先创建一个 pod, pod 的文件如下: ...

六月 23, 2023 · overstarry

Rancher Cattle Cluster Agent Could not Resolve Host

起因 最近在使用 rancher 导入外部 k8s 集群时,遇到了一个问题:在要导入的集群上执行命令后,创建的 pod 运行错误,错误日志大概如下: INFO: Environment: CATTLE_ADDRESS=100.66.209.198 CATTLE_CA_CHECKSUM= CATTLE_CLUSTER=true CATTLE_CLUSTER_AGENT_PORT=tcp://11.11.10.11:80 CATTLE_CLUSTER_AGENT_PORT_443_TCP=tcp://11.11.10.11:443 CATTLE_CLUSTER_AGENT_PORT_443_TCP_ADDR=10.96.0.125 CATTLE_CLUSTER_AGENT_PORT_443_TCP_PORT=443 CATTLE_CLUSTER_AGENT_PORT_443_TCP_PROTO=tcp CATTLE_CLUSTER_AGENT_PORT_80_TCP=tcp://10.96.0.125:80 CATTLE_CLUSTER_AGENT_PORT_80_TCP_ADDR=10.96.0.125 CATTLE_CLUSTER_AGENT_PORT_80_TCP_PORT=80 CATTLE_CLUSTER_AGENT_PORT_80_TCP_PROTO=tcp CATTLE_CLUSTER_AGENT_SERVICE_HOST=10.96.0.125 CATTLE_CLUSTER_AGENT_SERVICE_PORT=80 CATTLE_CLUSTER_AGENT_SERVICE_PORT_HTTP=80 CATTLE_CLUSTER_AGENT_SERVICE_PORT_HTTPS_INTERNAL=443 CATTLE_CLUSTER_REGISTRY= CATTLE_INGRESS_IP_DOMAIN=sslip.io CATTLE_INSTALL_UUID=333850e4-f500-43a2-a359-e1dfd94e4f35 CATTLE_INTERNAL_ADDRESS= CATTLE_IS_RKE=false CATTLE_K8S_MANAGED=true CATTLE_NODE_NAME=cattle-cluster-agent-55b9954958-5679q CATTLE_SERVER=https://xx.xx.vip CATTLE_SERVER_VERSION=v2.6.6 INFO: Using resolv.conf: search cattle-system.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 ERROR: https://xx.xx.vip/ping is not accessible (Could not resolve host: xx.xx.vip) 可以很好的看出错误的原因是域名 dns 解析错误。 解决 在发现问题后,可以根据错误日志进行相应的解决,我们先查看 coredns 的日志 kubectl logs deployment/coredns -n kube-system [ERROR] plugin/errors: 2 XX.XX.vip. A: read udp 100.108.11.198:32988->100.100.2.136:53: i/o timeout [ERROR] plugin/errors: 2 XX.XX.vip. A: read udp 100.108.11.198:53477->100.100.2.136:53: i/o timeout [ERROR] plugin/errors: 2 XX.XX.vip. AAAA: read udp 100.108.11.198:40436->100.100.2.136:53: i/o timeout 可以看到相应域名解析错误,根据以前的经验,先重启 coredns 容器尝试是否能解决问题,重启后发现问题未能解决 (可能需要重启相应节点的机器。 ...

六月 16, 2023 · overstarry

sealos version compare error

前言 最近在使用 sealos 安装更新 k8s 的过程中发现了一个问题:nfo new cluster version v1.25.10 behind the current version v1.25.8。 现在我来简单描述下过程:我想要将 k8s 版本从 v1.25.8 升级到 v1.25.10,运行更新命令:sealos run labring/kubernetes:v1.25.10-4.2.0 -f 后,出现了以下错误信息: 2023-06-09T11:30:06 info new cluster version v1.25.10 behind the current version v1.25.8 ,通过对错误信息的理解,发现提示 v1.25.10 版本比 v1.25.8 小,通过我对版本号的理解,这应该是个错误。 发现及解决问题 发现了问题,接下来要寻找出现问题的代码和解决问题。通过 Github 的代码搜索功能搜索关键字 behind the current version 顺利找到问题代码所在。 对找到的代码进行分析,发现是 versionutil.Compare(curversion, version) 方法出现了错误,我们来看看这个函数的代码: func Compare(v1, v2 string) bool { v1 = strings.Replace(v1, "v", "", -1) v2 = strings.Replace(v2, "v", "", -1) v1 = strings.Split(v1, "-")[0] v2 = strings.Split(v2, "-")[0] v1List := strings.Split(v1, ".") v2List := strings.Split(v2, ".") if len(v1List) != 3 || len(v2List) != 3 { logger.Error("error version format %s %s", v1, v2) return false } if v1List[0] > v2List[0] { return true } else if v1List[0] < v2List[0] { return false } if v1List[1] > v2List[1] { return true } else if v1List[1] < v2List[1] { return false } if v1List[2] >= v2List[2] { return true } return false } 通过对代码的分析,发现是对主次修订版本号对比时出现了问题,代码中是简单的使用了字符串比较,字符串数字比较会以字典序进行比较,所以会出现 “8” 大于 “10"的情况。正常的数字比较应该是将字符串转为数字进行相应比较。 ...

六月 10, 2023 · overstarry

Kubernetes pod 修改 hosts 文件

前言 最近看了 k8s 的书,学习了一些新的知识,将会分几篇来介绍学习到的知识,本文来先介绍 k8s 中如何修改 pod 的 hosts 文件。 我们知道当 DNS 出现问题时,可以向 Pod 的/etc/hosts 文件添加条目来提供主机名解析 Pod 级别覆盖。该如何向 hosts 文件中添加条目呢?可以使用 PodSpec 中的 HostAliases 字段添加自定义条目。 虽然我们也可以直接进入 pod 修改 host 文件来实现,但这样 pod 重建时会被覆盖,所以我们应该使用 HostAliases 来进行修改,因为该文件会由 Kubelet 管理,并且 可以在 Pod 创建/重启过程中被重写。 使用 我们该如何操作呢,接下来由我来介绍使用步骤: 1 先创建 Deployment YAML 文件来创建后台运行的 busybox pod apiVersion: apps/v1 kind: Deployment metadata: name: busybox-deployment spec: replicas: 1 selector: matchLabels: app: busybox template: metadata: labels: app: busybox spec: containers: - name: busybox image: busybox args: [ "sleep", "3600" ] resources: limits: memory: "128Mi" cpu: "500m" requests: memory: "64Mi" cpu: "250m" volumeMounts: - name: busybox-volume mountPath: /data volumes: - name: busybox-volume emptyDir: {} 查看 pod ip ...

六月 3, 2023 · overstarry

Apisix 忽略 uri 大小写

前言 最近需要忽略请求 uri 大小写,即不管 uri 的大小写都返回小写 uri 所请求的响应内容,例如请求 xx.vip/A 实际请求 xx.vip/a, 本文主要以 apisix 来讲解如何实现这个功能。 解决 遇到这个需求,首先肯定采用 apisix 的插件来实现,根据前面多次的经验,这个功能应该可以使用 serverless 插件中的 serverless-pre-function 插件来实现,具体的流程如下: 1 获取请求的 uri 2 将 uri 转为小写 3 修改请求的 uri 具体的插件内容如下: "serverless-pre-function": { "_meta": { "disable": false }, "functions": [ "return function(conf, ctx) local uri = ctx.var.uri;ctx.var.uri= string.lower(uri);ngx.log(ngx.ERR, \"match uri \", ctx.var.uri ); end" ], "phase": "rewrite" } 添加插件后,再次请求路径,发现请求结果还是没有变,查看日志发现 uri 修改成功了,不知为何还是没有修改成功。 后面又仔细研究了 proxy-rewrite 插件中修改 uri 的代码,发现它是使用了 nginx 标准的函数 ngx.req.set_uri 来赋值新的 uri。 ...

五月 26, 2023 · overstarry

sqlc 初体验

简介 sqlc 可以生成从 SQL 生成类型安全代码。它的工作原理是: 使用 SQL 书写查询语句。 运行 sqlc 生成 Go 代码,该代码为这些查询提供类型安全的接口。 在应用程序代码中调用生成的代码与数据库进行交互。 安装 sqlc 有多种安装方式: macOS: brew install sqlc Ubuntu: sudo snap install sqlc go 安装: # Go >= 1.17: go install github.com/kyleconroy/sqlc/cmd/sqlc@latest # Go < 1.17: go get github.com/kyleconroy/sqlc/cmd/sqlc docker 安装: docker pull kjconroy/sqlc 其它: https://github.com/kyleconroy/sqlc/releases 入门使用 插件 sqlc.yaml 首先创建 sqlc.yaml 文件,sqlc 会在当前目录下查找 sqlc.yaml 或 sqlc.json 文件。 # sqlc.yaml version: 1 packages: - path: "tutorial" name: "tutorial" engine: "mysql" schema: "schema.sql" queries: "query.sql" 创建 sql 文件 sqlc 需要得知数据库表结构和相应的 sql 语句,我们创建 schema.sql 定义表结构: ...

五月 20, 2023 · overstarry

Apisix Docker 部署网站重定向端口错误问题

前言 前段时间在使用 apisix 添加路由时,需要将 http 转为 https, 在配置 http_to_https 后,访问相应网址时发现 https 的端口不是我们所配置的默认端口 443,而是 9443 端口。 可以看到访问 http://localhost 会跳转至错误的地址:https://localhost:9443/ ,正确的地址应该是 https://localhost,这是怎么回事呢? 分析 接下来我来简单对问题进行简单分析。 我是采用 docker 的方式部署的 apisix,这是我们的配置文件: version: "3" services: apisix-dashboard: image: apache/apisix-dashboard:3.0.0-alpine restart: always volumes: - ./dashboard_conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml ports: - "9000:9000" networks: apisix: apisix: image: apache/apisix:${APISIX_IMAGE_TAG:-3.2.0-debian} restart: always volumes: - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro depends_on: - etcd ##network_mode: host ports: - "9180:9180/tcp" - "80:9080/tcp" - "9091:9091/tcp" - "443:9443/tcp" - "9092:9092/tcp" networks: apisix: etcd: image: bitnami/etcd:3.4.15 restart: always volumes: - etcd_data:/bitnami/etcd environment: ETCD_ENABLE_V2: "true" ALLOW_NONE_AUTHENTICATION: "yes" ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379" ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379" ports: - "2379:2379/tcp" networks: apisix: web1: image: nginx:1.19.0-alpine restart: always volumes: - ./upstream/web1.conf:/etc/nginx/nginx.conf ports: - "9081:80/tcp" environment: - NGINX_PORT=80 networks: apisix: networks: apisix: driver: bridge volumes: etcd_data: driver: local 可以看到我们将 apisix 容器的 http 端口和 https 端口映射为 80 和 443,按照常理来说,http_to_https 后的端口应该也是 443 端口才对。我猜测重定向时,apisix 还是采用配置文件中设定的 https 端口才导致跳转的 url 端口错误。 ...

五月 13, 2023 · overstarry