Operator 多可用区选主优化与 Lease 版本分析

什么是 Lease? Lease 对象的核心作用是表示某个实体(通常是一个 Pod 或进程)对某项资源或角色的“持有权”,并且这种持有权是有时间限制的。通过定期续约(renew),持有者可以保持其控制权。如果持有者未能续约,租约到期后,其他实体可以接管。 常见的应用场景包括: 领导选举:在分布式系统中,确保只有一个实例(Leader)执行特定任务,其他实例作为 Follower。 资源协调:跟踪和管理资源的临时所有权。 心跳机制:通过续约时间(RenewTime)检测持有者是否仍然活跃。 Kubernetes 内部的一些组件(如 kube-controller-manager 和 kube-scheduler)就使用 Lease 来实现高可用性和领导选举。 Lease 的工作原理 创建租约: 一个进程(如你的代码)创建一个 Lease 对象,并声明自己为持有者(HolderIdentity)。 续约: 持有者需要定期更新 RenewTime,证明自己仍然活跃。通常通过客户端(如 kubectl 或 Go 客户端)调用 Kubernetes API 来更新。 失效与接管: 如果持有者未能及时续约(例如进程崩溃),其他进程可以通过检查 RenewTime 和 LeaseDurationSeconds 判断租约是否过期,并尝试接管。 领导选举: 多个实例竞争同一 Lease 对象时,只有成功创建或更新它的实例成为 Leader。 示例场景 假设你用这个 Lease 来实现领导选举: 你有一个分布式应用,有 3 个 Pod:pod-1、pod-2、pod-3。 它们都尝试创建或更新同一个 Lease 对象(例如 my-leader-lease)。 pod-1 成功创建,设置 HolderIdentity: “pod-1”,成为 Leader。 pod-1 每 10 秒更新 RenewTime,保持领导地位。 如果 pod-1 崩溃,RenewTime 未更新,pod-2 检测到租约过期,接管并更新 HolderIdentity: “pod-2”。 现有逻辑 // Copyright 2018 The Operator-SDK Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "context" "time" "github.com/operator-framework/operator-sdk/pkg/k8sutil" // Operator-SDK 提供的工具包,用于获取运行环境信息(如命名空间、Pod 信息) corev1 "k8s.io/api/core/v1" // Kubernetes Core v1 API,包含 Pod、ConfigMap 等资源定义 apierrors "k8s.io/apimachinery/pkg/api/errors" // Kubernetes API 错误处理包 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // Kubernetes 元数据定义包,包含 ObjectMeta 等 "k8s.io/apimachinery/pkg/util/wait" // 提供等待和退避逻辑的工具 crclient "sigs.k8s.io/controller-runtime/pkg/client" // controller-runtime 提供的通用客户端,用于操作 Kubernetes 资源 "sigs.k8s.io/controller-runtime/pkg/client/config" // 获取 Kubernetes 配置(如 kubeconfig) ) // maxBackoffInterval 定义了尝试成为 Leader 时,最大退避等待时间(16秒)。 // 在多次尝试失败后,等待时间会逐渐增加,但不会超过这个值。 const maxBackoffInterval = time.Second * 16 // Become 函数确保当前 Pod 成为其命名空间内的 Leader。 // 如果代码运行在集群外(本地),会跳过领导选举直接返回 nil。 // 它通过创建一个带有当前 Pod 作为 OwnerReference 的 ConfigMap 来实现领导选举。 // ConfigMap 的名称是 lockName,同一时间只能有一个同名 ConfigMap 存在,成功创建者即为 Leader。 // 当 Leader Pod 终止时,垃圾回收机制会删除 ConfigMap,其他 Pod 可以接管。 func leaderBecome(ctx context.Context, lockName string) error { log.Info("Trying to become the leader.") // 日志:尝试成为 Leader // 获取当前 Operator 运行的命名空间 ns, err := k8sutil.GetOperatorNamespace() if err != nil { // 如果无法获取命名空间(可能是本地运行或无权限) if err == k8sutil.ErrNoNamespace || err == k8sutil.ErrRunLocal { log.Info("Skipping leader election; not running in a cluster.") // 日志:不在集群中,跳过选举 return nil } return err // 其他错误,返回 } // 获取 Kubernetes 配置(通常来自 kubeconfig 或 ServiceAccount) config, err := config.GetConfig() if err != nil { return err // 配置获取失败,返回错误 } // 创建 controller-runtime 的客户端,用于操作 Kubernetes 资源 client, err := crclient.New(config, crclient.Options{}) if err != nil { return err // 客户端创建失败,返回错误 } // 获取当前 Pod 的 OwnerReference,表示当前 Pod 是谁 owner, err := myOwnerRef(ctx, client, ns) if err != nil { return err // 获取 OwnerReference 失败,返回错误 } // 检查是否已存在由当前 Pod 拥有的 ConfigMap(可能是进程重启的情况) existing := &corev1.ConfigMap{} key := crclient.ObjectKey{Namespace: ns, Name: lockName} // ConfigMap 的键(命名空间+名称) err = client.Get(ctx, key, existing) switch { case err == nil: // ConfigMap 已存在 // 检查现有 ConfigMap 的 OwnerReferences for _, existingOwner := range existing.GetOwnerReferences() { if existingOwner.Name == owner.Name { // 如果 Owner 是当前 Pod log.Info("Found existing lock with my name. I was likely restarted.") // 日志:发现已有锁,可能是重启 log.Info("Continuing as the leader.") // 日志:继续作为 Leader return nil // 直接返回,当前 Pod 已是 Leader } log.Info("Found existing lock", "LockOwner", existingOwner.Name) // 日志:发现其他 Pod 持有的锁 } case apierrors.IsNotFound(err): // ConfigMap 不存在 log.Info("No pre-existing lock was found.") // 日志:没有预先存在的锁 default: // 其他错误 log.Error(err, "Unknown error trying to get ConfigMap") // 日志:获取 ConfigMap 时发生未知错误 return err } // 创建一个新的 ConfigMap,用作领导锁 cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: lockName, // ConfigMap 名称,由 lockName 指定 Namespace: ns, // 命名空间 OwnerReferences: []metav1.OwnerReference{*owner}, // 设置当前 Pod 为 Owner }, } // 尝试创建 ConfigMap,成为 Leader backoff := time.Second // 初始退避时间为 1 秒 for { // 无限循环,直到成功或上下文取消 err := client.Create(ctx, cm) // 尝试创建 ConfigMap switch { case err == nil: // 创建成功 log.Info("Became the leader.") // 日志:成功成为 Leader return nil // 返回,当前 Pod 成为 Leader case apierrors.IsAlreadyExists(err): // ConfigMap 已存在(其他 Pod 是 Leader) existingOwners := existing.GetOwnerReferences() // 获取当前 ConfigMap 的 Owner switch { case len(existingOwners) != 1: // ConfigMap 应该只有一个 Owner log.Info("Leader lock configmap must have exactly one owner reference.", "ConfigMap", existing) case existingOwners[0].Kind != "Pod": // Owner 必须是 Pod log.Info("Leader lock configmap owner reference must be a pod.", "OwnerReference", existingOwners[0]) default: // 正常情况:ConfigMap 由一个 Pod 拥有 leaderPod := &corev1.Pod{} key = crclient.ObjectKey{Namespace: ns, Name: existingOwners[0].Name} // 当前 Leader Pod 的键 err = client.Get(ctx, key, leaderPod) // 获取 Leader Pod 信息 switch { case apierrors.IsNotFound(err): // Leader Pod 已删除 log.Info("Leader pod has been deleted, waiting for garbage collection do remove the lock.") // 日志:等待垃圾回收删除 ConfigMap case err != nil: // 获取 Pod 失败 return err case isPodEvicted(*leaderPod) && leaderPod.GetDeletionTimestamp() == nil: // Leader Pod 被驱逐但未标记删除 log.Info("Operator pod with leader lock has been evicted.", "leader", leaderPod.Name) // 日志:Leader 被驱逐 log.Info("Deleting evicted leader.") // 日志:删除被驱逐的 Leader err := client.Delete(ctx, leaderPod) // 删除 Leader Pod if err != nil { log.Error(err, "Leader pod could not be deleted.") // 日志:删除失败 } case leaderPod.GetDeletionTimestamp() != nil: // Leader Pod 已被标记删除(新增逻辑) log.Info("Operator pod with leader lock has been terminating/completed.", "leader", leaderPod.Name) // 日志:Leader Pod 正在终止 log.Info("Deleting a-lock cm.") // 日志:删除 ConfigMap errCm := client.Delete(ctx, cm) // 删除 ConfigMap if errCm != nil { log.Error(errCm, "Leader pod could not be deleted.") // 日志:删除 ConfigMap 失败 } err := client.Delete(ctx, leaderPod) // 删除 Leader Pod if err != nil { log.Error(err, "Leader pod could not be deleted.") // 日志:删除 Pod 失败 } default: // Leader Pod 仍然活跃 log.Info("Not the leader. Waiting.") // 日志:当前不是 Leader,继续等待 } } // 等待一段时间后重试,带有随机抖动(Jitter)避免所有 Follower 同时竞争 select { case <-time.After(wait.Jitter(backoff, .2)): // 等待 backoff 时间(带 20% 随机抖动) if backoff < maxBackoffInterval { // 如果退避时间未达到最大值 backoff *= 2 // 退避时间翻倍 } continue // 继续下一次循环 case <-ctx.Done(): // 上下文取消 return ctx.Err() // 返回上下文错误 } default: // 创建 ConfigMap 时发生其他错误 log.Error(err, "Unknown error creating ConfigMap") // 日志:未知错误 return err } } } // myOwnerRef 返回当前运行 Pod 的 OwnerReference。 // 它依赖环境变量 POD_NAME(通过 downwards API 设置)来识别当前 Pod。 func myOwnerRef(ctx context.Context, client crclient.Client, ns string) (*metav1.OwnerReference, error) { // 获取当前 Pod 的信息 myPod, err := k8sutil.GetPod(ctx, client, ns) if err != nil { return nil, err // 获取失败,返回错误 } // 构造 OwnerReference owner := &metav1.OwnerReference{ APIVersion: "v1", // API 版本 Kind: "Pod", // 资源类型 Name: myPod.ObjectMeta.Name, // Pod 名称 UID: myPod.ObjectMeta.UID, // Pod 的唯一标识符 } return owner, nil } // isPodEvicted 检查 Pod 是否被驱逐(Evicted)。 // Pod 被驱逐时,Phase 为 Failed,且 Reason 为 "Evicted"。 func isPodEvicted(pod corev1.Pod) bool { podFailed := pod.Status.Phase == corev1.PodFailed // Pod 状态为 Failed podEvicted := pod.Status.Reason == "Evicted" // 原因是 Evicted return podFailed && podEvicted // 两者都满足则为被驱逐 } 现有逻辑总结 目标: 通过创建一个 ConfigMap(名称为 a-lock)来实现领导选举,成功创建者成为 Leader。 ConfigMap 的 OwnerReference 指向当前 Pod,当 Pod 删除时,ConfigMap 会被垃圾回收。 流程: 检查环境:如果不在集群中运行,跳过领导选举。 检查现有锁:如果已有由当前 Pod 拥有的 ConfigMap,直接恢复为 Leader。 创建锁:尝试创建新的 ConfigMap,如果失败(已有其他 Leader),检查当前 Leader 状态。 处理失效: 如果 Leader Pod 已删除,等待垃圾回收。 如果 Leader Pod 被驱逐或标记删除,主动删除 Pod 和 ConfigMap。 否则,等待并重试(带有退避机制)。 与 Lease 的对比 相似点:都用于领导选举,依赖 Kubernetes API 的原子性操作。 不同点: Lease 是专门为领导选举设计的轻量资源,内置续约机制(RenewTime)。 ConfigMap 是通用资源,通过 OwnerReference 和垃圾回收间接实现接管,逻辑更复杂。 请求开销: Lease 需要定期续约和查询。 此代码使用轮询和退避,Follower 需反复尝试创建 ConfigMap,也会产生多次请求。 为什么基于 ConfigMap 的方案在主机房断电后表现不佳?(另一个Operator没能及时成为Leader) 1. ConfigMap 依赖垃圾回收机制 机制: 在现有代码中,ConfigMap 的 OwnerReference 指向 Leader Pod。当 Leader Pod 被删除时,Kubernetes 的垃圾回收器(Garbage Collector, GC)会自动删除 ConfigMap,从而允许新的 Pod 成为 Leader。 主机房断电的影响: 如果主机房断电,运行 Leader Pod 的节点会突然下线。此时,Kubernetes 集群需要检测到节点不可用,并将该节点上的 Pod 标记为删除(DeletionTimestamp)。 问题:节点失联后,Kubernetes 不会立即认为 Pod 已删除,而是依赖 Node Controller 和 Taint-based Eviction 等机制来更新状态,这个过程可能需要几分钟甚至更久。 延迟来源: Node NotReady 检测:Master 检测节点失联通常依赖 node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable 污点,默认超时(node-monitor-grace-period)是 40 秒。 Pod Eviction 超时:Pod 被驱逐的默认超时(eviction-hard 或 eviction-soft)可能配置为几分钟(常见默认值如 5 分钟)。 垃圾回收延迟:即使 Pod 被标记删除,GC 删除 ConfigMap 也有一定延迟(通常几秒到几分钟,取决于集群负载和 GC 调度)。 2. 代码中的轮询和退避逻辑 机制: 现有代码使用轮询方式尝试创建 ConfigMap,如果失败(IsAlreadyExists),会检查现有 Leader Pod 状态,并以指数退避(backoff)等待。 退避时间从 1 秒开始,每次翻倍,直到最大值 maxBackoffInterval = 16 秒。 主机房断电的影响: 当 Leader Pod 的节点断电后,Follower Pod 检测到 ConfigMap 仍存在,但无法立即接管。它会进入等待循环,每次等待时间逐渐增加(1s → 2s → 4s → 8s → 16s)。 问题:如果 GC 迟迟未删除 ConfigMap,Follower 的等待时间会累积。例如,等待 5 次退避的总时间是 1 + 2 + 4 + 8 + 16 = 31 秒,但如果需要更多次(因为 GC 延迟),时间会更长。 延迟来源: 退避累积:退避逻辑本身会拖慢接管速度。 未优化检测:代码依赖主动轮询,而非实时监听(Watch),无法立即感知 ConfigMap 删除。 3. Kubernetes 集群的容错机制 机制: Kubernetes 在设计时倾向于“谨慎”处理节点失联,避免误判(false positive)。这意味着在确认节点和 Pod 真正失效前,会有较长的宽限期。 主机房断电的影响: 如果整个主机房断电,多个节点可能同时失联,Master 的 Node Controller 和 API Server 负载会激增,导致状态更新变慢。 问题:Pod 和 ConfigMap 的状态更新可能被推迟,进一步延长新 Leader 上任时间。 延迟来源: Node Controller 过载:大规模节点失效可能导致状态同步延迟。 API Server 压力:大量资源状态变更可能使 API 调用变慢。 4. 代码中的改进逻辑(DeletionTimestamp) 机制: 现有代码新增了检查 leaderPod.GetDeletionTimestamp() != nil,当 Leader Pod 被标记删除时,主动删除 ConfigMap 和 Pod。 主机房断电的影响: 这个逻辑依赖 Pod 被标记删除,但断电后 Pod 状态更新依赖 Node Controller,可能需要几分钟。 问题:如果节点失联时间过长,DeletionTimestamp 不会立即设置,主动删除 ConfigMap 的逻辑无法触发。 延迟来源: 状态更新延迟:节点失联到 Pod 被标记删除的时间(默认可能 5-10 分钟)。 断电场景:15 分钟的延迟虽然不常见,但并非完全不合理,尤其在以下条件下: Node Eviction 超时较长:如果集群配置了较长的宽限期(如 kube-controller-manager 的 –pod-eviction-timeout=5m 或更高),Pod 状态更新可能延迟 5-10 分钟。 GC 延迟:大规模断电后,GC 处理大量资源,调度延迟可能累加到几分钟。 退避累积:Follower 的退避逻辑在极端情况下可能执行多次(比如 10-20 次),每次 16 秒,总计 160-320 秒(约 3-5 分钟)。 集群负载:如果 Master 或 API Server 因断电事件过载,状态同步可能进一步推迟。 估算: Node 失联检测:5 分钟(默认宽限期)。 Pod 驱逐 + GC:2-5 分钟。 Follower 退避:3-5 分钟。 与 Lease 方案的对比 Lease 的优势: 主动续约:Leader 定期更新 RenewTime,Follower 可通过时间戳判断租约是否过期,无需依赖 GC。 更快接管:租约过期后(通常 15-30 秒),Follower 可立即尝试接管。 典型延迟:在断电场景下,接管时间通常在几十秒到 1-2 分钟(取决于 LeaseDurationSeconds 和网络恢复)。 现有 ConfigMap 方案: 接管时间受限于 Node 状态更新和 GC,异常场景下可能高达数十分钟。 Lease 资源在 Kubernetes 中的版本局限性分析 背景 Kubernetes 中的 Lease 资源(隶属 coordination.k8s.io API 组)用于节点心跳和领导选举等协调机制,是一个不算核心但也不可忽视的功能。然而,官方文档(https://kubernetes.io/docs/reference/)对Lease 的版本演进记录极为有限: ...

2025-03-19 · 14 min

Mac生产力软件推荐

Mac生产力软件推荐:提升效率的实用工具 Mac 系统以其流畅和优雅著称,但搭配一些生产力工具,能让你的工作效率更上一层楼。下面推荐几款精心挑选的软件,涵盖截图、窗口管理、系统优化等场景,助你充分发挥 Mac 的潜力。 Snipaste:高效截图与贴图利器 Snipaste 是一款将截图与贴图功能完美结合的工具。它能快速捕捉屏幕内容,并将截图固定在桌面,方便随时参考或对比,提升工作效率。 核心亮点:像素级精确截图、丰富的标注功能、可悬浮的贴图模式。 适用场景:设计师对比设计稿、开发者调试界面、内容创作者整理素材。 下载地址:Snipaste 官网 BetterAndBetter:操作效率的增强器 BetterAndBetter 是一款轻量化的 macOS 操作优化工具,类似 BetterTouchTool,但更简洁。它支持自定义手势和快捷键,让你的鼠标和触控板操作更得心应手。 核心亮点:鼠标手势、触控板快捷操作、窗口管理功能。 适用场景:从 Windows 转换到 Mac 的用户,或追求个性化操作体验的人。 下载地址:BetterAndBetter 官网 Rectangle:窗口管理的免费助手 Rectangle 是一款开源的窗口管理工具,通过快捷键快速调整窗口大小和位置,让多任务处理变得井然有序。 核心亮点:多样化的窗口布局、灵活的快捷键设置。 适用场景:程序员同时查看代码和文档、设计师处理多屏设计。 下载地址:Rectangle GitHub BetterDisplay:外接显示器的最佳伴侣 BetterDisplay 专注于优化显示器设置,解锁 macOS 的隐藏选项,提供更高的分辨率和刷新率,显著改善多屏工作体验。 核心亮点:支持 HiDPI 分辨率、HDR 模式、多显示器管理。 适用场景:视频编辑师和设计师使用外接显示器时的画面优化。 下载地址:BetterDisplay 官网 AltTab:程序切换的顺畅体验 AltTab 模仿 Windows 的“Alt+Tab”切换功能,为 macOS 用户提供更直观的应用切换方式,效率大幅提升。 核心亮点:全屏窗口预览、支持自定义快捷键。 适用场景:Windows 转 Mac 用户,或需要频繁切换应用的人。 下载地址:AltTab 官网 OwlOCR:图片文字提取神器 OwlOCR 是一款高效的 OCR 工具,能快速识别图片或屏幕中的文字,准确率高且操作简单。 核心亮点:支持多语言识别、批量处理功能。 适用场景:学生整理笔记、研究人员提取文档内容。 下载地址:OwlOCR 官网 Maccy:剪贴板管理的得力助手 Maccy 是一款轻量剪贴板工具,能保存复制历史并支持快速调用,极大方便频繁复制粘贴的用户。 核心亮点:支持多种格式(文本、图片等)、历史记录搜索。 适用场景:程序员处理代码片段、文案编辑整理素材。 下载地址:Maccy GitHub Mos:鼠标滚动的优化专家 Mos 专为第三方鼠标设计,提供平滑的滚动体验,并允许用户自定义滚动方向和速度。 ...

2025-03-08 · 4 min

Elasticsearch写入优化

收到工单 用户提工单到L2说写入消息堆积了,第一时间检查ElasticSearch这边的日志和监控,把一百多个节点的日志都遍历看了一遍,并没有看到任何异常的关键字,再把 Grafana 监控看了一个遍,也没有任何明显异常的指标,只有少数几个data节点超出85%水位,没法办法只能从写入端的日志去着手了,刚开始就看到每分钟内多次的 http 429报错,还有就是socket timeout的报错,从下午排查到晚上,实在ElasticSearch这边没有任何异常,客户那边也上会了,问了下,你们这个重试是怎样的?说等待十秒重试一次吧,另外客户是通过Bulk API来批量发送请求的,但是每次Bulk发送的大小不固定,小于2000直接发送,大于2000截断发送(这个肯定是不合理的,后面再讲),这个等待重试,加入ElasticSearch因为写入请求太多导致阻塞,那么简单的等待重试,肯定是解决不了任何问题的,当天就推荐用户修改为退避式重试,修改完后的写入端日志里,不再有429的报错了,但是还有会有socket timeout的报错,结合之前注意到的,猜想是请求发送的次数太多了,然而单个Bulk的数据量很明显不到推荐值,没有被充分利用,官方推荐的Bulk大小是5-15MB,然而目前的情况是,几十条甚至一两条的写入请求也会被直接发送出去。推荐用户修改Bulk单次提交数据量后,仍然没有任何缓解的效果,消息队列消费不完,还是经常性出现堆积。data节点的配置是16C64G,检查了写入线程数也就是默认是16,这个没法改,最大也就改到17,写入线程队列是6.X ElasticSearch的默认值200,这个改大也不能解决消费慢的问题,只能让更多的线程在排队。其他方面,不同索引的刷新配置refresh_interval设置的30-60s,这个也是一个合理的值,其他的translog落盘配置,检查过也都是默认值。唯一一处疑点就是mapping的max_field设置的是10000,max_depth设置的是20,这个不是正常值,但是也不会导致写入慢吧,用户反应他们的mapping并不复杂,也不存在20层嵌套,逻辑上分析也没啥问题,我定义一个瓶子最大能装1吨水,也不是说我的瓶子真的要装1吨水。 ...

2024-11-16 · 8 min

香港三日游+港卡记录

2024年开始一直有在支付宝上买纳斯达克和标普的指数基金,另外由于上次讲到的,X被我养成了,所以经常能看到很多牛人在谈论美股,我也开始了解了一些,之前也看有人在玩美股,据说最简单的办法就是办一张港卡,所以就有了这次旅行。 不得不说,想说在查攻略这方面还是挺不错的,开始申请港澳通行证办理签注,然后花费两天年假,提前去广东深圳, 带上爸妈一起去旅游,虽然对于我不仅仅是旅游。 办理通行证各个地方各个省份的入口不一样,但流程大多相同,在网上预约,然后去线下填表拍照录指纹,然后会有同志问你去干什么,至于签注可以根据自己的需要选择不同的类型,另外团队签和个人签这个没什么区别,不用太在意。 到达深圳以后,在酒店休息一晚,考虑如何去到香港可以选择轮渡/高铁/公交,我选择的是高铁速度最快通关最便捷,从深圳北站前往香港西九龙站,只需要不到20分钟人民币¥75,我没有体验普通的公交和口岸的过关方式,据说排队的人会很多,通关会耗时更久。 像坐普通的高铁一样,从深圳北出发到达香港西九龙入关,然后你录入的的指纹就可以发挥作用了, 按下指纹后,校验通过会给你一张过关小票,这个小票一定要留好, 后面办理港卡的时候会用到。 从西九龙站出来就能够看到维多利亚港, 对面的楼认识的不多,但是牛逼就完事了。 我是六点多从深圳出发, 在香港的交通方面可以直接使用支付宝,需要提前在支付宝的左上角切换地址为香港,然后领取交通卡。 大部分的公交地铁都可以直接刷码, 只在三天的旅途中遇到了一辆司机说“请去下一辆,不支持支付宝”。 乘坐地铁前往葵芳, 从地铁站出来大概走不到500米就到达汇丰葵芳分行,因为老人出站时刷了码,但是反应慢了没出来,联系站务人员花费了一些时间。 到达时已经8:30多了,这时候银行门口已经排满了人,大概是20多人, 等着来都来了的原则,只能开始排队了。 9:00准时开门, 这里的银行不像内地那么气派,面积不大,也没有前台,只有门口一个业务员问你要办理什么业务,然后他帮你取对应的号码, 他讲粤语,我回答普通话,办卡,他没有回答,我直接给了我一张号码,A17。 等待的人比较多, 座位比较少,老年人比较多。 另外去香港之前记得先打开漫游,我忘了这件事情,导致手机卡显示没有信号,连上银行的Wi-Fi,联系运营商客服开通漫游功能,接收短信验证码即可,至于上网问题应该在入港之前就购买一张流量卡。 ...

2024-10-09 · 11 min

记录被喝茶这件事

常在河边走 特意打开Twitter看了一下,我是21年3月份注册的,现在看来马斯克收购推特,然后改名为X对我来说是一个利好,因为我只需要在地址栏敲一个X就能够联想到推特的域名(因为我已经把推特的从我的iTab书签页里面删掉了),为什么要删掉?就是这篇博文的原因。 18,19年那时候我会上油管,看一下伟大祖国最近的军事发展科技成果,或者是关注一下一些旁征博引的野史,那时候推特对我来说和脸书一样,乏味,没啥意思,后来当它知道我是干什么的?个性化推荐开始 才开始觉得有点意思,至少比国内的平台更自由开放,我看到了很多程序员的分享,包括当然也有键政派,也有女菩萨派,还有裸聊派…… 崭露头角 直到22年推荐了我一个李老师不是李老师的账号,这个人进入到我的视野 我发现我好像看到了什么不该看的,让我想起来那么一个图,一个猪趴在围墙上,看见几个人在杀另一只猪。 当然上网冲浪这么久也不是第一次看到这样的内容,虽然他一直出现在我的timeline里面,但是我从来没有想过要关注他。 登峰造极 22,23年我在北京,当时疫情发生了一些事情,一些血气方刚的大学生,上海也发生了一些事情,估计那段时间他也挺忙的,各种投稿,我都看了,但我没有任何表示,我也没关注他,直到有一天看他发了个帖子说不要关注他,因为有人因为关注了他被约谈了。好家伙,我心想幸好我没关注😅 与工作搏斗 2024年,这份工作不像之前那么轻松,上班已经消耗了我一天的精力,下班只想躺着刷手机,在一次网上冲浪的过程中我鬼使神差就点开了他的主页,看了一下他最近发的但我没有关注到的内容,实际上我已经疲于应对我的工作了,甚至都没有精力去关注他发的内容,因为我关注了又能怎样,我解决不了任何问题。如果是别的任何一个正常的推特用户,那么仅仅是白白消耗了我0.01卡的能量点一个没有任何意义的关注而已,但偏偏是他,让我在接下来的半个月里被各种找不到地方吐的恶心。 还是湿了鞋 那天早上刚骑车到车棚,接到一个电话,是家乡的地址,接听之后是标准的家乡方言,我从来没有听到过推特的方言发音,那天我听到了,我们那的方言在说‘特’的时候让我觉得十分别扭,‘推’也没好到哪里去。 ...

2024-08-10 · 4 min

git合并方法学习

用字符画描述git的合并方法 1. 合并(Merge) main 分支 A---B---C \ dev 分支 D---E---F 在 main 分支上执行 `git merge dev` 后: main 分支 A---B---C-----------G <- (G 是合并提交) \ / dev 分支 D---E---F 2. 变基(Rebase) main 分支 A---B---C \ dev 分支 D---E---F 在 dev 分支上执行 `git rebase main` 后,再在 main 分支上执行 `git merge dev`: main 分支 A---B---C---D'---E'---F' 3. 压缩合并(Squash and Merge) main 分支 A---B---C \ dev 分支 D---E---F 在 main 分支上执行 `git merge --squash dev` 后,再执行提交: main 分支 A---B---C-----------G' <- (G' 是一个新的提交,合并来自 D、E 和 F 的变更) 4. 拣选提交(Cherry-pick)(不是标准合并所有更改的方法) main 分支 A---B---C \ dev 分支 D---E---F 在 main 分支上对 dev 分支的每个提交执行 `git cherry-pick <提交哈希值>`: main 分支 A---B---C---D'---E'---F' <- (每个提交被逐一复制) 合并方法的对比表格 方法 特点 适用场景 结果 Merge 保留所有分支的历史 标准的合并操作,适用于大多数场景 创建一个新的合并提交 Rebase 使历史线性化 个人分支上的工作或清洁历史 不保留原始分支提交的顺序 Squash and Merge 将所有更改压缩为一个提交 当你想要简化复杂分支的历史时 一个新的单一提交 Cherry-pick 手动选择特定提交 只合并某些特定的更改 对选择的每个提交创建新的提交 请注意,这些方法中,Merge 和 Rebase 是最常用的策略来整合一个开发分支(如 dev)到主分支(如 main)。Squash and Merge 通常用于在合并之前压缩多个提交以保持清洁的历史。Cherry-pick 方法并不是一个真正的分支合并策略,它只是用于将选定的提交从一个分支复制到另一个分支,因此它不应用于将一个完整的功能分支合并到主分支的场景。

2024-03-26 · 2 min

祝大哥今年发大财日进斗金

好久没有更新了,借此机会更新一下,老早就看了天气预报今天会下雨,于是就做好了不出门的准备,一觉睡到十点多,一看窗外也没有下雨啊,但是还是照计划行事吧,开启懒狗模式,饿了点了个外卖,想起几天前就是315,又爆了什么猛料呢?梅菜扣肉,某奶茶……触目惊心,可是仔细一想,央视那些记者真是习得东亚小岛传统技能忍者术啊,那叫一个能忍,暗中调查一年,然后在第二年的315憋个大招?已经不知道用什么语言来表达了。说回点外卖,自从去年在北京住青旅找工作的那段时间,那时候在蓝鸟上看到一句话说“此时北上广的青旅里,挤满了找工作的年轻人。”我已经很少很少点外卖了,这次也是懒狗模式的缘故,美团送了个张30-10的券,点了个杨国福麻辣烫,上一次点外卖也是这家,而且内容也都类似(千张,莴苣片,油豆皮,青菜……),我从不或者很少选肉、肉丸子,因为自从我听了一句话叫做“好肉不做馅”就一直把这句话放在心里。 外卖到了,收到外卖员的电话了,打电话说外卖到了,我说好,出门去拿,打开门,他在门口,是一个看起来三十多不到四十的中年人,按照的点外卖经历,有时候,你接到电话,他其实才到楼下,等你想起来再去拿,其实外卖已经在门口,人早就走了。要么就是你收到电话是,外卖已经给你扔门口了,他也走远了,但今天不一样,他就在门口站着,我打开门接过外卖,说了声谢谢,就回屋了。其实到此为止也不至于我今天下午想这么多东西,以至于想写点东西。 下午,闲得蛋疼并不是卷的我,出门骑车去公司的路上,我收到一个电话,我一看浙江杭州号码,接过来,对面说“大哥好,我是给你送麻辣烫的那个外卖员,麻烦您能给我一个五星好评吗?帮我过一下新手的任务,祝大哥今年发大财日进斗金。”,我说好,其实我知道按照我的性格,当他说是过新手任务的时候,我就会决定帮他这个忙了。后面那句直接让我楞了一秒,我才憋出一句”你也一样“,虽然平时工作中,和外部一些初入职场的年轻人对接时,他们也会用一些谦辞”大佬、老师、大哥、老哥“,但是这次不一样,他曾经就站在我面前,一个三四十的中年人,为了生计,为了通过美团外卖员的新人任务,把我一个二十多岁的年轻人叫大哥,他可能是某个小孩的爸爸,可能是某个女人的丈夫,可能是别人的儿子。 ...

2024-03-17 · 2 min

ElasticSearch中熔断器

Elasticsearch Service 提供了多种官方的熔断器(circuit breaker),用于防止内存使用过高导致 ES 集群因为 OutOfMemoryError 而出现问题。Elasticsearch 设置有各种类型的子熔断器,负责特定请求处理的内存限制。此外,还有一个父熔断器,用于限制所有子熔断器上使用的内存总量。 Circuit breaker settings 断路器设置 Elasticsearch contains multiple circuit breakers used to prevent operations from causing an OutOfMemoryError. Each breaker specifies a limit for how much memory it can use. Additionally, there is a parent-level breaker that specifies the total amount of memory that can be used across all breakers. Elasticsearch 包含多个断路器,用于防止操作导致 OutOfMemoryError。每个断路器都指定了其可以使用的内存量的限制。此外,还有一个父级断路器,用于指定可在所有断路器中使用的内存总量。 Except where noted otherwise, these settings can be dynamically updated on a live cluster with the cluster-update-settings API. 除非另有说明,否则可以使用 cluster-update-settings API 在实时集群上动态更新这些设置。 For information about circuit breaker errors, see Circuit breaker errors. 有关断路器错误的信息,请参阅断路器错误。 下面提到的静态和动态的意思是,这个配置参数的属性 动态设置(Dynamic Settings): 可以使用集群更新设置API在运行中的集群上进行配置和更新。 在未启动或已关闭的节点上,也可以通过elasticsearch.yml文件进行本地配置。 静态设置(Static Settings): 只能在未启动或已关闭的节点上通过elasticsearch.yml文件进行配置。 必须在集群中的每个相关节点上进行设置。 主要用于配置静态的集群设置和节点设置。 静态设置的配置只在节点启动时生效,不会受到集群运行时的影响。 Parent circuit breaker父断路器 The parent-level breaker can be configured with the following settings: 可以使用以下设置配置父级断路器: indices.breaker.total.use_real_memory (Static) Determines whether the parent breaker should take real memory usage into account (true) or only consider the amount that is reserved by child circuit breakers (false). Defaults to true. (静态)确定父断路器是应考虑实际内存使用情况 (true) 还是仅考虑子断路器保留的内存量 (false)。默认值为 true。 indices.breaker.total.limit (Dynamic) Starting limit for overall parent breaker. Defaults to 70% of JVM heap if indices.breaker.total.use_real_memory is false. If indices.breaker.total.use_real_memory is true, defaults to 95% of the JVM heap. (动态)整体母断路器的起始限制。如果 JVM 为 false,则默认为 JVM 堆的 70%indices.breaker.total.use_real_memory。如果 indices.breaker.total.use_real_memory 为 true,则默认为 JVM 堆的 95%。 Field data circuit breaker字段数据断路器 The field data circuit breaker estimates the heap memory required to load a field into the field data cache. If loading the field would cause the cache to exceed a predefined memory limit, the circuit breaker stops the operation and returns an error. 字段数据断路器估计将字段加载到字段数据缓存中所需的堆内存。如果加载该字段会导致缓存超出预定义的内存限制,则断路器将停止操作并返回错误。 ...

2023-11-20 · 6 min

Elasticsearch稳定性介绍

Elasticsearch 的团队致力于不断改进 Elasticsearch 和 Apache Lucene,以保护您的数据。与任何分布式系统一样,Elasticsearch 非常复杂,每个部分都可能遇到需要正确处理的边缘情况。我们将讨论在面对硬件和软件故障时,为改进 Elasticsearch 在健壮性和弹性方面所做的持续努力。 提升ElasticSearch的弹性:https://www.elastic.co/cn/videos/improving-elasticsearch-resiliency ElasticSearch和弹性:https://www.elastic.co/cn/elasticon/conf/2016/sf/elasticsearch-and-resiliency 参考资料:https://www.elastic.co/guide/en/elasticsearch/resiliency/current/index.html 本文不是ElasticSearch的Release docs,只关注与ElasticSearch在弹性/稳定性方面的进展。 V5.0.0 使用两阶段提交进行集群状态发布 Elasticsearch中的主节点会持续监控集群节点,并在某个节点无法及时响应其PING请求时将其从集群中移除。如果主节点剩下的节点过少,它将主动放弃主节点的角色,然后开始新的主节点选举过程。当网络分区导致主节点失去许多从节点时,在检测到节点丢失并且主节点下行之前,有一个很短的时间窗口。在这个时间窗口内,主节点可能会错误地接受和确认集群状态变更。为了避免这种情况,我们在集群状态发布中引入了一个新的阶段,其中提议的集群状态会发送给所有节点,但尚未提交。只有在足够多的节点主动确认更改后,更改才会被提交,并向节点发送提交消息#13062。A master node in Elasticsearch continuously monitors the cluster nodes and removes any node from the cluster that doesn’t respond to its pings in a timely fashion. If the master is left with too few nodes, it will step down and a new master election will start.When a network partition causes a master node to lose many followers, there is a short window in time until the node loss is detected and the master steps down. During that window, the master may erroneously accept and acknowledge cluster state changes. To avoid this, we introduce a new phase to cluster state publishing where the proposed cluster state is sent to all nodes but is not yet committed. Only once enough nodes actively acknowledge the change, it is committed and commit messages are sent to the nodes. See #13062.一句话总结:局部断网导致主节点与其他节点失联时,在其他节点检测到,并且投票出新的主节点前,有一个很短的时间窗口,在这个时间窗口内,主节点可能会错误的接受变更改进:引入了一个新的状态来解决这个问题,想修改集群的状态,需要足够多的节点主动确认后才可以。 ...

2023-11-16 · 15 min

随便写点什么吧

胡言乱语【1】 昨天去了西溪湿地公园,在网络上搜了下攻略发现,有收费区和免费区,而且景色还没什么两样,果断选择无成本的方案,免费的就是最贵的,结果就是人挤人,还有二货明明走在机动车道了,还不满足,还要并排着走(大有不服你来撞我的意思)。开车的人还不能鸣笛,只能找机会借对面的道绕过去,速度缓慢,走走停停,我心想要是开个油车手动挡走这条路简直就是地狱级。 胡言乱语【2】 碰见一个三口之家朝我走过来,爸爸手里拿着两片叶子,放在嘴边吹响了,小男孩说,看来爸爸在农村长大还是有用的(会吹叶子做的哨子),爸爸和妈妈都笑了,问他那你现在在哪里啊?然后我已经走远了。一位寒窗苦读艰苦奋斗的农村青年终于在城市站稳脚跟,周末带着老婆孩子去感受城市里的农村(湿地)。 胡言乱语【3】 杭州是新一线城市,武汉也是新一线城市,武汉房价比不过杭州,武行东湖也比不过杭州西湖,在历史气息这块我想还不能直接妄下结论,虽然没有像杭州那样留下一首忆江南的诗,但是近代史很多关键性的事件也是在武汉发生。 关于西溪湿地里高庄的历史 高庄又名西溪山庄,始建于清顺治十四年(1657)至康熙三年(1664年)之间,是清代文人高士奇在西溪的别墅。 高士奇,杭州人,其学识渊博,能诗文,擅书法,精考证,善鉴赏,被清人比作李白、宋濂一流人物,所藏书画甚富,康熙二十八年(1689),康熙南巡时,曾临幸西溪山庄,并赐“竹窗”二字和诗一首:“花源路几重,柴桑出沃土。烟翠竹窗幽,雪香梅岸古”。 传说康熙曾南巡到西溪,独与高士奇泛小舟至高庄观览良久,高士奇将康熙驻趾之亭取名为“宸览亭”亭柱上有楹联:“翠辇曾停红莲依旧,宸游不再斯事难逢。”居中的石碑上四个大字“高庄宸迹”,此处就是当年康熙皇帝留在西溪的胜景遗迹。 因为是在免费区,人更多了,大部分都是穿着古装在摆拍的,不得不佩服妹子的专业能力,还有道具的书,捧在手里,拿着小扇子。 而我只是看着水池边的白条,但凡有个网兜,捞起来炸至金黄,还真是美味😋呢。

2023-10-29 · 2 min