news 2026/9/25 3:06:48

使用 Google Cloud Run 与 Redis 构建高可用、可水平扩展的 Atlantis 部署架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Google Cloud Run 与 Redis 构建高可用、可水平扩展的 Atlantis 部署架构
  • DevOps
  • CI/CD
  • 基础设施

【免费下载链接】atlantis

Terraform Pull Request Automation

项目地址:https://gitcode.com/gh_mirrors/at/atlantis
点击查看免费下载

本文基于 runatlantis.io 官方博客《Atlantis on Google Cloud Run》整理并辅以当前仓库源码佐证,完整讲解如何将 Terraform Pull Request Automation 工具 Atlantis 从自管 VM 迁移到 Google Cloud Run 这类无服务器容器平台:以 Redis 作为集中式锁后端、以共享负载均衡器统一路由多个实例、并用服务账号模拟(impersonation)实现最小权限隔离。读完本文,你将掌握一套可复制的、消除单点故障并支持横向扩容的 Atlantis 部署方案,并理解其底层锁数据库切换机制。

::: info 虽然本文以 Google Cloud Run 为例编写,该部署架构同样适用于 AWS Fargate、Azure Container Instances 与 Kubernetes。 :::

本文覆盖 Terraform 代码中最关键的部分;完整的可运行示例位于 runatlantis 社区的 atlantis-contrib 仓库(atlantis-on-cloud-run目录),建议配合阅读。

为什么不再用自管 VM

大多数 Atlantis 部署运行在自管 VM 上。这一方案虽然熟悉且直接,却伴随一系列挑战:

  • 身份权限过强:VM 服务账号往往通过直接授权或模拟(impersonation)间接拥有大量资源与项目的访问权;
  • 缺少高可用:Atlantis 默认把锁后端直接写到磁盘(BoltDB),VM 一旦宕机,Atlantis 便不可用;
  • 单点故障:单一实例既无法横向扩容,也带来单点风险;
  • 持续运维负担:打补丁、操作系统升级、备份等运维工作长期存在。

本文展示的替代方案是:在 Google Cloud Run 这类无服务器容器平台上运行多个 Atlantis 实例,使用集中式数据库(Redis)存放锁,并把多个实例放到负载均衡器后面。每个实例以自己的身份、有限的权限运行,只管理属于自己的项目。这一架构消除了单点故障、支持水平伸缩,并落地了最小权限(least-privilege)安全模型。

我们要构建什么

整体架构如下:

  1. 外部 HTTP(S) 负载均衡器:负责把请求路由到多个 Atlantis 实例;
  2. 多个 Atlantis 实例:运行在 Google Cloud Run 上;
  3. Memorystore for Redis 实例:提供集中式锁后端,让多个实例协调锁、避免冲突。

我们略过的内容

为控制篇幅,原文刻意省略了部分重要细节:网络搭建、DNS、Docker 镜像拉取、通配符 TLS 证书,以及 Atlantis 的每一个配置旋钮——本文聚焦于与架构最相关的部分。

需要特别强调的是:强烈建议把 Atlantis 部署在隔离的 VPC 中,并开启 Private Service Access。这样 Atlantis 只与 Google API 通信来完成工作,不会触达你的其他基础设施。

BoltDB:只有一个写入者时很棒

Atlantis 默认使用 BoltDB 作为锁后端。BoltDB 是一个简单、内嵌的键值存储,直接写入磁盘(server/server.go中boltdb.New(userConfig.DataDir)即把数据目录作为参数传入)。它适合单实例部署,但把 Atlantis 锁死在单节点架构上——要获得高可用与水平伸缩,必须把它替换为多个 Atlantis 实例可以安全共享的托管分布式数据库。

从当前仓库源码可以确认默认值:cmd/server.go中DefaultLockingDBType = "boltdb",即不显式配置时 Atlantis 走 BoltDB 分支。服务端在启动时按--locking-db-type的值做分支选择(见 server/server.go):

  • "redis":创建 Redis 数据库客户端(支持单节点与集群两种模式);
  • "boltdb":在数据目录下创建内嵌 BoltDB。

一个“管理一切”的 Atlantis

为了便于创建和管理多个 Atlantis 实例,可以额外部署一个专用的“管理(management)”Atlantis 实例,负责其他 Atlantis 实例的生命周期管理(创建、更新、删除)。作者通常把它放在独立的 Google Cloud 项目(如atlantis-mgmt)中,并配一个专门的 Git 仓库。

只要搭好一个实例,复制它并放到共享负载均衡器之后即可,后续复制成本很低。

Redis:分布式锁后端

自 Atlantis v0.19.0 起,Redis 就是受支持的锁后端。Redis 是内存数据结构存储,这里用作多个 Atlantis 实例的中央锁后端:每个实例连接同一个 Redis 实例,从而协调锁、避免冲突。

Redis 还支持持久化:

  • RDB(Redis Database):按指定间隔对数据集做时间点快照;
  • AOF(Append Only File):记录 Redis 服务器收到的每次写操作。

这意味着即使 Redis 实例重启,锁也不会丢失。

要创建的第一个资源就是 Redis 实例:

resource "google_redis_instance" "atlantis" { name = "atlantis" tier = "STANDARD_HA" redis_version = "REDIS_7_2" memory_size_gb = 1 region = "your-region" authorized_network = "your-network-id" connect_mode = "PRIVATE_SERVICE_ACCESS" persistence_config { persistence_mode = "RDB" rdb_snapshot_period = "TWENTY_FOUR_HOURS" } maintenance_policy { # ... } project = "your-project-id" lifecycle { prevent_destroy = true } }

关键点:

  • 该实例拥有1 GiB内存,开启RDB持久化,每24 小时做一次快照;
  • connect_mode = "PRIVATE_SERVICE_ACCESS"使 Redis 只能在 VPC 内部访问——创建实例前必须先为 VPC 配置好 Private Service Access;
  • tier = "STANDARD_HA"提供高可用副本;
  • prevent_destroy = true保护锁数据不被意外销毁。

源码视角:Atlantis 如何连接 Redis

从源码看,Redis 后端完整实现位于 server/core/redis/redis.go。其Config结构体支持Hostname、Port、Password、Username、TLSEnabled、InsecureSkipVerify、DB与ClusterAddresses(redis.go)。NewWithConfig会做如下选择:

  • 若ClusterAddresses非空 → 使用 Redis Cluster 客户端;
  • 否则 → 使用单节点客户端(host:port),并执行Ping校验连接可用性。

server/server.go在switch userConfig.LockingDBType中把ATLANTIS_REDIS_HOST、ATLANTIS_REDIS_PORT、ATLANTIS_REDIS_PASSWORD、ATLANTIS_REDIS_USERNAME、ATLANTIS_REDIS_TLS_ENABLED、ATLANTIS_REDIS_INSECURE_SKIP_VERIFY、ATLANTIS_REDIS_DB、ATLANTIS_REDIS_CLUSTER_ADDRESSES等配置装配进redis.NewWithConfig(server/server.go)。

这些 flag 的定义与默认值位于 cmd/server.go:

配置项环境变量 / flag默认值说明
--locking-db-typeATLANTIS_LOCKING_DB_TYPEboltdb锁数据库类型(boltdb/redis)
--redis-hostATLANTIS_REDIS_HOST无Redis 主机名(锁类型为 redis 时使用)
--redis-portATLANTIS_REDIS_PORT6379Redis 端口
--redis-dbATLANTIS_REDIS_DB0使用的 Redis 逻辑数据库编号
--redis-passwordATLANTIS_REDIS_PASSWORD无Redis 密码
--redis-usernameATLANTIS_REDIS_USERNAME无Redis 用户名
--redis-tls-enabledATLANTIS_REDIS_TLS_ENABLEDfalse是否启用 TLS 连接
--redis-insecure-skip-verifyATLANTIS_REDIS_INSECURE_SKIP_VERIFYfalse是否跳过 TLS 证书校验
--redis-cluster-addressesATLANTIS_REDIS_CLUSTER_ADDRESSES无逗号分隔的集群节点地址(host:port),设置后启用集群模式

从 cmd/server.go 的 flag 描述可以看到:--locking-db-type用于“存储 plan/apply 锁的锁数据库类型”,--redis-host等仅在锁类型为redis时生效。

锁键的存储设计也值得了解:项目锁键格式为pr/{repoFullName}/{path}/{workspace}/{projectName}(由models.GenerateLockKey生成,见 redis.go);TryLock通过GET+SET实现原子加锁(redis.go);UnlockIfOwnedByPull用一段 Lua 脚本保证“只有该 PR 持有的锁才被删除”的原子性(redis.go)。此外,启动时NewWithConfig还会执行一次旧格式锁键到新格式的迁移(使用Scan而非Keys,以兼容 Redis Cluster),迁移失败不会阻塞启动,会在下次启动重试(redis.go)。

部署到 Cloud Run

Cloud Run 是无服务器容器平台:自动伸缩应用、处理 HTTP 请求、抽象底层基础设施,只按实际使用的计算资源计费;每个服务运行在单一服务账号下,由该账号定义其权限——非常适合部署一个或多个 Atlantis 实例。

服务端配置 atlantis/management.yaml

先创建服务端 Atlantis 配置atlantis/management.yaml:

repos: - id: github.com/acme/example apply_requirements: [approved, mergeable] import_requirements: [approved, mergeable] allowed_overrides: ["workflow"] allowed_workflows: ["example"] delete_source_branch_on_merge: true workflows: example: plan: steps: - init - plan apply: steps: - apply

这里用allowed_workflows把仓库限定到名为example的工作流,并设置了apply_requirements: [approved, mergeable](需批准且可合并)等门槛。

创建管理实例的 Cloud Run 服务

下面的 Terraform 配置只突出与本文架构相关的 Atlantis 环境变量;完整可运行示例见 atlantis-contrib 仓库的atlantis-on-cloud-run目录。

resource "google_cloud_run_v2_service" "atlantis_management" { provider = google-beta name = "atlantis-management" location = "your-region" deletion_protection = false ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" invoker_iam_disabled = true launch_stage = "GA" template { scaling { min_instance_count = 1 max_instance_count = 1 } execution_environment = "EXECUTION_ENVIRONMENT_GEN2" service_account = google_service_account.atlantis_management.email containers { image = "ghcr.io/runatlantis/atlantis:v0.35.1" resources { limits = { cpu = "1" memory = "2Gi" } } volume_mounts { name = "atlantis" mount_path = "/app/atlantis" } env { name = "ATLANTIS_PORT" value = "8080" } env { name = "ATLANTIS_DATA_DIR" value = "/app/atlantis" } env { name = "ATLANTIS_USE_TF_PLUGIN_CACHE" value = "true" } env { name = "ATLANTIS_LOCKING_DB_TYPE" value = "redis" } env { name = "ATLANTIS_REDIS_HOST" value = google_redis_instance.atlantis.host } env { name = "ATLANTIS_REDIS_DB" value = "0" } env { name = "ATLANTIS_ATLANTIS_URL" value = "https://management.atlantis.acme.com" } env { name = "ATLANTIS_REPO_CONFIG_JSON" value = jsonencode(yamldecode(file("${path.module}/atlantis/management.yaml"))) } } vpc_access { egress = "ALL_TRAFFIC" network_interfaces { network = "your-network-id" subnetwork = "your-subnetwork-id" } } volumes { name = "atlantis" empty_dir { medium = "MEMORY" size_limit = "5Gi" } } } project = "your-project-id" }

这些环境变量与源码中的配置项一一对应:

  • ATLANTIS_LOCKING_DB_TYPE=redis:等价于--locking-db-type redis,触发 server/server.go 中的 Redis 分支,使锁后端从 BoltDB 切换到 Redis;
  • ATLANTIS_REDIS_HOST/ATLANTIS_REDIS_DB:指定集中式锁数据库的地址与逻辑库(--redis-host/--redis-db,默认库为 0,见 cmd/server.go);
  • ATLANTIS_REPO_CONFIG_JSON:把management.yaml先yamldecode再jsonencode注入为服务端仓库配置(对应源码中的--repo-config-json,user_config.go);
  • ATLANTIS_DATA_DIR=/app/atlantis:数据目录指向挂载的临时卷;
  • ATLANTIS_USE_TF_PLUGIN_CACHE=true:开启 Terraform provider 插件缓存(对应--use-tf-plugin-cache,cmd/server.go),减少重复下载。

ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"让服务只接受来自内部负载均衡器的流量;vpc_access把出站流量全部导向 VPC 网络,确保 Atlantis 只按需访问 Google API。

临时存储(Ephemeral Storage)

Atlantis 是 I/O 密集型应用:它会 checkout 仓库、运行 Terraform 命令、下载 provider、拉取模块,因此需要可写的文件系统存放临时数据。在 Cloud Run 上,这由临时存储承担——容器实例停止或重启时会被清空。

好在 Atlantis 的运行方式决定了其临时存储需求有限:

  • PR 数据在合并或关闭后即从文件系统移除;
  • provider 可以用ATLANTIS_USE_TF_PLUGIN_CACHE缓存;
  • Terraform 二进制已包含在容器镜像中。

因此配置一个5 GiB 的内存empty_dir卷,挂载到/app/atlantis并设为ATLANTIS_DATA_DIR:

# ... volumes { name = "atlantis" empty_dir { medium = "MEMORY" size_limit = "5Gi" } } # ... volume_mounts { name = "atlantis" mount_path = "/app/atlantis" } # ... env { name = "ATLANTIS_DATA_DIR" value = "/app/atlantis" } env { name = "ATLANTIS_USE_TF_PLUGIN_CACHE" value = "true" }

内存卷速度快且不依赖持久磁盘;由于锁等持久状态已迁往 Redis,临时存储清空不会影响可用性。

让实例保持“温热”

Cloud Run 实例在不使用时可以缩容到零,这会导致冷启动以及内存临时存储的丢失。为避免这一点,设置min_instance_count = 1,保证至少一个实例始终运行、随时可以处理请求:

scaling { min_instance_count = 1 max_instance_count = 1 }

模拟身份与最小权限(Impersonation and Least Privilege)

每个 Cloud Run 服务都运行在一个定义其身份的服务账号下。与其给这个账号过大的权限,不如使用一个只拥有“模拟其他更受限服务账号”权限的服务账号。

对管理 Atlantis 实例来说不必如此(它本来就只部署在单个项目上),但对那些管理多个项目/环境的其他 Atlantis 实例来说,这一点至关重要。通过模拟身份,可以确保每个 Atlantis 实例只拥有管理其特定项目所需的权限。

典型做法:创建一个基础 Atlantis 服务账号(atlantis-example),再为每个环境创建独立服务账号(如atlantis-example-dev、atlantis-example-prod)。基础账号在这些环境账号上被授予roles/iam.serviceAccountTokenCreator角色,并在 Atlantis 运行 Terraform 命令时模拟它们;反过来,这些环境专属账号只被授予其管理项目所需的权限。这样即使某个 Atlantis 实例被攻破,爆炸半径也仅限该实例管理的资源。

Terraform 配置示例:

locals { atlantis_network_service_accounts = [ "atlantis-example-dev", "atlantis-example-prod", ] } # Base Atlantis service account resource "google_service_account" "atlantis_example" { account_id = "atlantis-example" project = local.project_id } # Per-environment service accounts resource "google_service_account" "atlantis_example_service_accounts" { for_each = toset(local.atlantis_example_service_accounts) account_id = each.value project = local.project_id } # Allow base SA to impersonate the env-specific ones resource "google_service_account_iam_member" "atlantis_example_impersonation" { for_each = google_service_account.atlantis_example_service_accounts service_account_id = each.value.name role = "roles/iam.serviceAccountTokenCreator" member = "serviceAccount:${google_service_account.atlantis_example.email}" }

模拟本身通过在atlantis/example.yaml工作流中设置GOOGLE_IMPERSONATE_SERVICE_ACCOUNT环境变量启用。在该配置中,Atlantis 管理dev与prod两个环境——在plan和apply期间切换到对应的环境服务账号:

repos: - id: github.com/acme/example apply_requirements: [approved, mergeable] import_requirements: [approved, mergeable] delete_source_branch_on_merge: true allowed_overrides: ["workflow"] allowed_workflows: ["example-dev", "example-prod"] workflows: example-dev: plan: steps: - env: name: GOOGLE_IMPERSONATE_SERVICE_ACCOUNT value: example-dev@acme-atlantis-mgmt.iam.gserviceaccount.com - run: rm -rf .terraform - init: extra_args: ["-lock=false", "-backend-config=env/dev/backend-config.tfvars"] - plan: extra_args: ["-lock=false", "-var-file=env/dev/vars.tfvars"] apply: steps: - env: name: GOOGLE_IMPERSONATE_SERVICE_ACCOUNT value: example-dev@acme-atlantis-mgmt.iam.gserviceaccount.com - apply: extra_args: ["-lock=false"] example-prod: plan: steps: - env: name: GOOGLE_IMPERSONATE_SERVICE_ACCOUNT value: example-prod@acme-atlantis-mgmt.iam.gserviceaccount.com - run: rm -rf .terraform - init: extra_args: ["-lock=false", "-backend-config=env/prod/backend-config.tfvars"] - plan: extra_args: ["-lock=false", "-var-file=env/prod/vars.tfvars"] apply: steps: - env: name: GOOGLE_IMPERSONATE_SERVICE_ACCOUNT value: example-prod@acme-atlantis-mgmt.iam.gserviceaccount.com - apply: extra_args: ["-lock=false"]

工作流中env步骤对应 Atlantis 自定义工作流的envstep(实现见 server/core/runtime/env_step_runner.go),它会在执行 Terraform 命令前注入环境变量。这里还演示了init/plan/apply的extra_args用法:通过-backend-config与-var-file为不同环境传入不同的后端配置与变量文件,并用-lock=false避免在多实例架构下与集中锁产生冗余交互。

共享负载均衡器

运行多个 Atlantis 实例(各自负责不同的项目或环境)时,关键是把流量路由到正确的实例。与其给每个实例独立的公网端点,不如通过一个全局 HTTPS 负载均衡器集中流量。

下面的负载均衡器使用基于主机的路由,按子域名把请求分发到对应实例。例如,network.atlantis.acme.com的请求被路由到管理 network 项目的 Atlantis 实例,workloads.atlantis.acme.com则进入管理 workload 项目的实例。

resource "google_compute_url_map" "atlantis" { name = "atlantis" default_url_redirect { host_redirect = "atlantis.acme.com" https_redirect = true redirect_response_code = "MOVED_PERMANENTLY_DEFAULT" strip_query = false } host_rule { hosts = ["network.atlantis.acme.com"] path_matcher = "atlantis-network-webhooks" } host_rule { hosts = ["workloads.atlantis.acme.com"] path_matcher = "atlantis-workloads-webhooks" } host_rule { hosts = ["management.atlantis.acme.com"] path_matcher = "atlantis-management-webhooks" } path_matcher { name = "atlantis-network-webhooks" default_service = google_compute_backend_service.atlantis_network.id path_rule { paths = ["/events"] service = google_compute_backend_service.atlantis_network_webhooks.id } } path_matcher { name = "atlantis-workloads-webhooks" default_service = google_compute_backend_service.atlantis_workloads.id path_rule { paths = ["/events"] service = google_compute_backend_service.atlantis_workloads_webhooks.id } } path_matcher { name = "atlantis-management-webhooks" default_service = google_compute_backend_service.atlantis_management.id path_rule { paths = ["/events"] service = google_compute_backend_service.atlantis_management_webhooks.id } } project = "your-project-id" } resource "google_compute_ssl_policy" "restricted" { name = "restricted" profile = "RESTRICTED" min_tls_version = "TLS_1_2" project = "your-project-id" } resource "google_compute_target_https_proxy" "atlantis" { name = "atlantis" url_map = google_compute_url_map.atlantis.id ssl_certificates = [ google_compute_managed_ssl_certificate.atlantis_network.id, google_compute_managed_ssl_certificate.atlantis_workloads.id, google_compute_managed_ssl_certificate.atlantis_management.id, ] ssl_policy = google_compute_ssl_policy.restricted.id project = "your-project-id" } resource "google_compute_global_forwarding_rule" "atlantis" { name = "atlantis" target = google_compute_target_https_proxy.atlantis.id port_range = "443" ip_address = google_compute_global_address.atlantis.address load_balancing_scheme = "EXTERNAL_MANAGED" project = "your-project-id" }

配置要点:

  • default_url_redirect把未知主机重定向到atlantis.acme.com并强制 HTTPS;
  • 每个主机规则对应一个path_matcher,其中把/events路径单独路由到 webhook 专用后端;
  • 使用RESTRICTED配置文件、TLS_1_2最低版本的 SSL 策略;
  • 全局转发规则监听 443,绑定托管证书与固定 IP。

为什么每个实例需要两个后端

每个 Atlantis 实例在负载均衡器中被注册为两个独立的 backend service:一个服务主 HTTP 端点,另一个服务/eventswebhook 端点。这种分离的意义在于:

  • 主界面可以放在 Identity-Aware Proxy(IAP)后面,只有授权用户才能访问;
  • webhook 端点保持公网可达,让 GitHub 或 GitLab 能无阻碍地投递事件。

示例(workloads 实例):

resource "google_compute_backend_service" "atlantis_workloads" { name = "atlantis-workloads" protocol = "HTTP" port_name = "http" timeout_sec = 30 load_balancing_scheme = "EXTERNAL_MANAGED" security_policy = google_compute_security_policy.atlantis.id backend { group = google_compute_region_network_endpoint_group.atlantis_workloads.id } iap { enabled = true } project = "your-project-id" } resource "google_compute_backend_service" "atlantis_workloads_webhooks" { name = "atlantis-workloads-webhooks" protocol = "HTTP" port_name = "http" timeout_sec = 30 load_balancing_scheme = "EXTERNAL_MANAGED" security_policy = google_compute_security_policy.atlantis_events_webhook.id backend { group = google_compute_region_network_endpoint_group.atlantis_workloads.id } project = "your-project-id" }

可见主后端开启了iap { enabled = true },webhook 后端则不开启。

保护 /events 端点

强烈建议用安全策略(security policy)保护/events端点,阻挡非法流量:

  • 至少限制为 Git 提供商使用的 IP 网段;
  • 并拦截一些常见的 Web 漏洞模式(可参考 Cloud Armor 预置 WAF 规则)。

Atlantis 的事件路由入口在仓库中对应 server/controllers/events/events_controller.go(/events端点由 server/router.go 注册),它会校验 webhook 请求并把 GitHub、GitLab 等平台的事件转发给命令执行管线,因此该端点必须既能被 Git 平台访问,又要受到安全策略约束。

总结

把 Atlantis 部署在 Google Cloud Run 上,配合共享的 Redis 锁后端与共享负载均衡器,可以交付一个高可用、可水平伸缩、安全的 Atlantis 部署:

  • 高可用:锁与状态存于托管 Redis(带持久化与高可用副本),不再依赖单机磁盘;
  • 水平伸缩:多个实例共享同一锁后端,负载均衡器按子域名分发请求;
  • 最小权限:每个实例以独立服务账号运行,通过 impersonation 只获得管理自身项目所需的最小权限,缩小爆炸半径;
  • 无服务器运维:Cloud Run 自动扩缩容、按用量计费,无需维护虚拟机。

由于篇幅所限,本文并未覆盖全部细节(网络、DNS、证书等)。完整的可运行 Terraform 示例请查阅 atlantis-contrib 仓库的atlantis-on-cloud-run目录,社区作者也曾在公开演讲中介绍过该架构。相关讨论可以到 Cloud Native Computing Foundation Slack 工作区的 #atlantis 频道进行。

  • DevOps
  • CI/CD
  • 基础设施

【免费下载链接】atlantis

Terraform Pull Request Automation

项目地址:https://gitcode.com/gh_mirrors/at/atlantis
点击查看免费下载

相关推荐

上一篇:pkg 错误代码完全指南:10个常见 exit code 含义与快速解决方法
下一篇:3000亿参数MoE模型开源!ERNIE 4.5如何重塑AI产业格局

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 3:06:24

基于Python的豆瓣电影情感分析推荐系统设计

1. 需求拆解与整体架构:这个系统到底解决什么问题说起电影推荐,很多人第一反应是豆瓣的“猜你喜欢”。但实际用过的人都知道,这个功能隔三差五给你推一些评分很高、口碑爆棚的电影,点进去看了才发现根本不是你的菜。评分高不代表你…

作者头像 李华
网站建设 2026/9/25 3:04:23

BullMQ 去除子任务失败依赖:removeDependencyOnFailure 选项深入解析

后端消息队列任务调度 【免费下载链接】bullmq BullMQ - Message Queue and Batch processing for NodeJS, Python, .NET, Elixir, Rust and PHP based on Redis or PostgreSQL 项目地址: https://gitcode.com/gh_mirrors/bu/bullmq 点击查看 免费下载 导读 在基于…

作者头像 李华
网站建设 2026/9/25 3:04:18

dsh-market 测试体系拆解:四层测试如何守护真实 pnpm 安装链

dsh-market 测试体系拆解:四层测试如何守护真实 pnpm 安装链 【免费下载链接】dsh-market The plugin market inside DeepSeek Harness — browse, search, one-click install DSH 可视化插件市场 项目地址: https://gitcode.com/gh_mirrors/ds/dsh-market …

作者头像 李华
网站建设 2026/9/25 3:02:37

.NET + Semantic Kernel 搭建 MCP 能力层实战解析

MCP(Model Context Protocol)是2025年AI工程圈最绕不开的热词。如果你最近在做Agent相关项目,大概率已经发现,MCP把“工具怎么暴露给AI”这件事彻底标准化了。而.NET这一端,最有组合价值的就是Semantic Kernel&#xf…

作者头像 李华