news 2026/9/4 5:48:37

从门桥车到微服务:构建高可用分布式系统的弹性架构设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从门桥车到微服务:构建高可用分布式系统的弹性架构设计

最近在技术社区里,有一个词被反复提及,但很多开发者对其理解还停留在“能过复杂路况”的层面——“门桥车”。如果你以为这只是一个关于车辆底盘结构的冷门工程术语,那就错过了它背后蕴含的、对现代分布式系统架构设计的深刻启发。

在越野车领域,门桥(Portal Axle)通过将轮轴中心线抬高,显著增加了离地间隙,从而获得了碾压同级的通过性。这解决的痛点非常具体:在复杂、崎岖、充满未知障碍的环境中,如何保证核心动力系统(差速器、半轴)的安全与高效,同时让车轮获得更大的活动空间。

映射到我们的软件世界,这不正是微服务架构云原生应用边缘计算场景下,我们每天都在面对的挑战吗?你的应用就是那辆车,网络延迟、异构环境、资源竞争、局部故障就是路上的深坑、巨石和交叉轴。传统的“整体式车桥”架构(单体应用或简单服务拆分)在这种环境下举步维艰,核心服务(差速器)一旦被“托底”(如数据库连接耗尽、单点故障),整个系统就可能瘫痪。

本文将带你深入“门桥”的技术思想。我们不会真的去造车,而是通过一个完整的、可落地的Spring Cloud微服务示例项目,来诠释如何为你的应用打造“门桥式”的极致通过性。你将看到,通过服务网格(Service Mesh)的流量治理弹性熔断与降级自适应负载均衡以及可观测性这套组合拳,你的服务将能从容应对生产环境中各种“烂路”。

我们将从零开始,搭建一个模拟电商场景的微服务集群,并逐步引入“门桥”设计,让你不仅理解概念,更能亲手实现和验证。

1. 为什么你的微服务需要“门桥式”通过性?

在深入代码之前,我们必须先厘清问题。很多团队在实施微服务后,反而觉得系统更脆弱了。这往往不是因为微服务本身有问题,而是只做了“分”(拆分服务),却没有构建“分”之后所需的“通过性”保障。

想象一个简单的下单流程:用户请求 → 网关 → 订单服务 →(调用)库存服务 →(调用)支付服务 → 写入数据库。

在理想平坦的网络环境下,一切顺利。但在现实中,你会遇到:

  • “炮弹坑”(网络抖动):库存服务响应突然从10ms飙升到2000ms,导致订单服务线程池被占满,引发连锁雪崩。
  • “交叉轴”(资源死锁):支付服务因数据库锁等待,进而导致订单服务调用超时,用户反复重试,流量激增。
  • “深水区”(节点故障):某个库存服务实例突然宕机,如果流量继续打到该实例,会导致部分用户下单失败。
  • “崎岖山路”(异构环境):服务部署在混合云、边缘节点,网络状况和资源能力差异巨大。

传统的解决方式像是给“整体桥”换更厚的钢板(升级服务器配置)或更猛的发动机(优化代码),成本高且效果有限。而“门桥”思路是改变力的传递路径和隔离风险

  1. 抬升核心:通过熔断器(如Hystrix、Resilience4j)隔离对故障下游的调用,保护核心业务线程池。
  2. 独立悬挂:通过服务发现与负载均衡(如Ribbon、Spring Cloud LoadBalancer),让每个请求能智能地选择健康的实例,像每个车轮独立适应路面。
  3. 差速锁:通过分布式事务解决方案(如Seata)或最终一致性模式,在部分子系统异常时,保证整体业务逻辑能继续推进或安全回退。
  4. 全地形反馈:通过全链路追踪(如Sleuth + Zipkin)和指标监控(如Micrometer + Prometheus),实时感知系统“路况”,为运维决策提供数据支持。

接下来,我们就用Spring Cloud Alibaba这套强大的“越野套件”,来改装我们的应用。

2. 环境与项目准备

我们使用当前企业中最流行的Spring Cloud Alibaba生态进行演示,它提供了开箱即用的高可用组件。

环境要求:

  • JDK 8 或 11(推荐11)
  • Maven 3.6+
  • IntelliJ IDEA 或 Eclipse
  • Docker(用于运行Nacos、Sentinel等组件,非必须但强烈推荐)

项目初始化:我们将创建一个父工程portal-axle-demo,以及四个子模块:

  1. portal-gateway: Spring Cloud Gateway 作为API网关。
  2. portal-order-service: 订单服务。
  3. portal-stock-service: 库存服务。
  4. portal-payment-service: 支付服务。

首先,创建父工程pom.xml,统一管理依赖和版本。

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>portal-axle-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <name>portal-axle-demo</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <!-- 选用一个稳定的版本 --> <relativePath/> </parent> <properties> <java.version>11</java.version> <spring-cloud.version>2021.0.8</spring-cloud.version> <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <!-- Spring Cloud 依赖管理 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Alibaba 依赖管理 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <modules> <module>portal-gateway</module> <module>portal-order-service</module> <module>portal-stock-service</module> <module>portal-payment-service</module> </modules> </project>

3. 搭建服务注册与发现“底盘”(Nacos)

门桥车的第一个关键是坚固的底盘。在微服务中,服务注册与发现中心就是我们的底盘,它让服务之间能相互感知。我们选用Nacos。

使用Docker快速启动Nacos:

docker run --name nacos-standalone -e MODE=standalone -p 8848:8848 -d nacos/nacos-server:latest

访问http://localhost:8848/nacos,默认账号/密码:nacos/nacos

接下来,为每个服务模块添加Nacos客户端依赖。以portal-order-service为例,在其pom.xml中添加:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>

application.yml中配置Nacos服务器地址和应用名:

# portal-order-service/src/main/resources/application.yml server: port: 8081 # 订单服务端口 spring: application: name: portal-order-service # 服务名 cloud: nacos: discovery: server-addr: localhost:8848 # Nacos服务器地址 # 其他服务(stock, payment)配置类似,只需修改端口和应用名

在主启动类上添加@EnableDiscoveryClient注解。这样,服务启动后就会自动注册到Nacos。至此,我们的“底盘”就稳固了,服务之间知道了彼此的存在。

4. 实现“独立悬挂”:负载均衡与远程调用

有了底盘,我们需要让车轮(服务实例)能独立运动并智能选择路径。这就是负载均衡。我们使用OpenFeign进行声明式HTTP客户端调用,它默认集成了Ribbon(或Spring Cloud LoadBalancer)来实现客户端负载均衡。

首先,在订单服务中,添加OpenFeign依赖,并定义一个用于调用库存服务的Feign客户端。

<!-- portal-order-service/pom.xml 新增依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

创建Feign客户端接口:

// 文件路径:portal-order-service/src/main/java/com/example/order/client/StockClient.java package com.example.order.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = "portal-stock-service") // 指定要调用的服务名 public interface StockClient { /** * 扣减库存 * @param productId 商品ID * @param count 扣减数量 * @return 操作结果 */ @PostMapping("/stock/reduce") String reduceStock(@RequestParam("productId") String productId, @RequestParam("count") Integer count); }

在订单服务的主启动类上添加@EnableFeignClients注解以启用Feign。

// 文件路径:portal-order-service/src/main/java/com/example/order/OrderApplication.java package com.example.order; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients // 启用Feign客户端 public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }

现在,订单服务就可以像调用本地方法一样,通过StockClient去调用库存服务了。Feign和Ribbon会帮我们完成服务发现、负载均衡(默认轮询)和HTTP请求的所有细节。这就是“独立悬挂”——每个请求可以灵活地分配到不同的库存服务实例上。

5. 安装“差速锁”:熔断与降级(Sentinel)

当某个车轮(服务实例)陷入泥坑(故障)时,差速锁(熔断器)可以锁死这个车轮,将动力传递给其他好车轮,防止整车陷住。我们使用Sentinel实现熔断、降级和流量控制。

首先,启动Sentinel控制台(同样推荐Docker):

docker run --name sentinel -p 8858:8858 -d sentinel-dashboard:latest

访问http://localhost:8858,默认账号/密码:sentinel/sentinel

在订单服务中引入Sentinel和Feign的适配依赖:

<!-- portal-order-service/pom.xml 新增依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- Sentinel对OpenFeign的支持 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

配置Sentinel控制台地址并开启Feign对Sentinel的支持:

# portal-order-service/src/main/resources/application.yml 追加配置 spring: cloud: sentinel: transport: dashboard: localhost:8858 # Sentinel控制台地址 eager: true # 立即初始化,便于在控制台看到服务 feign: enabled: true # 开启对Feign的熔断支持

现在,我们来为StockClientreduceStock调用设置一个降级规则。当调用库存服务失败(超时或异常)时,执行降级逻辑。

修改StockClient,通过fallback属性指定降级类:

// 文件路径:portal-order-service/src/main/java/com/example/order/client/StockClient.java @FeignClient(name = "portal-stock-service", fallback = StockClientFallback.class) public interface StockClient { // ... 方法不变 }

创建降级类StockClientFallback

// 文件路径:portal-order-service/src/main/java/com/example/order/client/StockClientFallback.java package com.example.order.client; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Component @Slf4j public class StockClientFallback implements StockClient { @Override public String reduceStock(String productId, Integer count) { log.error("调用库存服务扣减库存失败,进入降级逻辑。productId: {}, count: {}", productId, count); // 这里可以实现多种降级策略: // 1. 返回一个默认值(如“库存扣减中”) // 2. 将扣减请求存入消息队列,异步重试 // 3. 抛出一个业务异常,由上层处理 return "服务暂时不可用,请稍后重试"; } }

这样,当库存服务不可用时,订单服务的调用不会无限等待或抛出异常导致自身崩溃,而是执行预设的降级逻辑,保证了订单服务主体功能的可用性。这就是“差速锁”在起作用,隔离了故障,保护了核心链路。

6. 构建“全地形反馈系统”:可观测性(Sleuth + Zipkin)

越野高手离不开对车况和地形的实时感知。在微服务中,这就是可观测性,包括链路追踪、指标监控和日志聚合。我们使用Spring Cloud Sleuth进行链路追踪,并用Zipkin进行可视化。

首先,启动Zipkin服务器(Docker):

docker run -d -p 9411:9411 --name zipkin openzipkin/zipkin

在所有服务模块(gateway, order, stock, payment)的pom.xml中添加Sleuth和Zipkin依赖:

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> </dependency>

在服务的application.yml中配置Zipkin服务器地址:

spring: zipkin: base-url: http://localhost:9411 # Zipkin服务器地址 sleuth: sampler: probability: 1.0 # 采样率,1.0表示100%采样,生产环境可调低

现在,启动所有服务,并通过网关发起一个下单请求。然后打开Zipkin控制台(http://localhost:9411),你就可以清晰地看到这个请求经过了网关、订单服务、库存服务、支付服务等每一个“车轮”的完整路径、耗时和依赖关系。任何环节的延迟或异常都一目了然,为性能优化和故障排查提供了强大的数据支撑。

7. 完整流程演示与验证

让我们编写一个简单的下单接口,串联起整个流程。

1. 库存服务接口:

// 文件路径:portal-stock-service/src/main/java/com/example/stock/controller/StockController.java package com.example.stock.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController @Slf4j public class StockController { @PostMapping("/stock/reduce") public String reduceStock(@RequestParam String productId, @RequestParam Integer count) { log.info("收到扣减库存请求,商品:{}, 数量:{}", productId, count); // 模拟业务处理耗时 try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 模拟随机失败,用于测试熔断降级 if (Math.random() > 0.7) { throw new RuntimeException("模拟库存服务异常"); } return String.format("商品%s库存扣减%d成功", productId, count); } }

2. 订单服务接口:

// 文件路径:portal-order-service/src/main/java/com/example/order/controller/OrderController.java package com.example.order.controller; import com.example.order.client.StockClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") @Slf4j @RequiredArgsConstructor public class OrderController { private final StockClient stockClient; @PostMapping("/create") public String createOrder(@RequestParam String productId, @RequestParam Integer count) { log.info("开始创建订单,商品:{}, 数量:{}", productId, count); // 1. 调用库存服务扣减库存 (通过Feign客户端,已集成负载均衡和熔断降级) String stockResult = stockClient.reduceStock(productId, count); log.info("库存服务返回:{}", stockResult); // 2. 模拟本地创建订单逻辑 // ... 此处省略订单入库等操作 log.info("订单创建成功"); return "订单创建成功,库存处理结果:" + stockResult; } }

3. 网关路由配置:

# portal-gateway/src/main/resources/application.yml server: port: 8080 spring: application: name: portal-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由 routes: - id: order-service-route uri: lb://portal-order-service # lb:// 表示负载均衡 predicates: - Path=/order/**

启动与验证步骤:

  1. 启动Nacos、Sentinel、Zipkin(如果还没启动)。
  2. 依次启动portal-stock-service,portal-payment-service,portal-order-service,portal-gateway
  3. 打开Nacos控制台 (localhost:8848),在“服务管理-服务列表”中确认所有服务均已注册。
  4. 使用Postman或curl发送请求:
    curl -X POST "http://localhost:8080/order/create?productId=P1001&count=2"
  5. 观察正常流程:你应该收到“订单创建成功,库存处理结果:商品P1001库存扣减2成功”的响应。
  6. 测试熔断降级:手动停止库存服务,再次发送请求。此时,由于库存服务不可用,Sentinel会触发熔断降级,你会收到降级类中返回的信息:“服务暂时不可用,请稍后重试”。同时,订单服务本身不会崩溃。
  7. 查看链路追踪:打开Zipkin控制台(localhost:9411),点击“查找痕迹”,你可以看到刚才请求的完整调用链路图,包括经过的每个服务和耗时。

8. 常见问题与排查思路

在实际部署和运行中,你可能会遇到以下问题:

问题现象可能原因排查方式解决方案
服务无法注册到Nacos1. Nacos服务未启动。
2. 网络不通或端口被占用。
3.application.yml中Nacos地址配置错误。
1. 检查Nacos容器或进程状态 (docker ps)。
2. 检查应用启动日志,看是否有连接Nacos的错误。
3. 使用telnet localhost 8848测试网络。
1. 启动Nacos。
2. 修正配置文件的spring.cloud.nacos.discovery.server-addr
Feign调用报UnknownHostException1. 调用方未添加@EnableFeignClients
2. 被调用服务名拼写错误或未注册。
3. Ribbon负载均衡器未正确初始化。
1. 检查调用方主类注解。
2. 在Nacos控制台确认服务名。
3. 检查依赖中是否包含spring-cloud-starter-loadbalancer(Spring Cloud 2020+)。
1. 添加注解。
2. 修正服务名。
3. 添加负载均衡器依赖。
Sentinel规则不生效1. Sentinel控制台未连接。
2. 依赖版本冲突。
3. 未在配置中开启Feign对Sentinel的支持。
1. 访问localhost:8858看控制台是否正常。
2. 检查应用日志是否有Sentinel初始化信息。
3. 确认spring.cloud.sentinel.feign.enabled=true
1. 启动Sentinel。
2. 统一Spring Cloud Alibaba版本。
3. 确保配置正确。
Zipkin看不到链路数据1. Zipkin服务未启动。
2. 采样率 (probability) 设置为0。
3. 服务与Zipkin网络不通。
1. 检查Zipkin容器状态。
2. 检查配置文件spring.sleuth.sampler.probability
3. 查看应用日志是否有发送追踪数据到Zipkin的错误。
1. 启动Zipkin。
2. 将采样率设为1.0用于调试。
3. 检查网络和防火墙设置。
网关路由4041. 网关未开启服务发现 (spring.cloud.gateway.discovery.locator.enabled=true)。
2. 路由配置的URI格式错误。
1. 检查网关配置。
2. 确认URI格式为lb://SERVICE-NAME
1. 开启服务发现或配置静态路由。
2. 修正URI。

9. 生产环境最佳实践与进阶思考

将“门桥”思想应用到生产环境,远不止引入几个组件那么简单。以下是一些关键的最佳实践:

  1. 配置管理外置:将Nacos不仅用作服务发现,更作为配置中心。将所有服务的配置(数据库连接、熔断规则、超时时间)集中管理,实现动态刷新,避免重启服务。
  2. 熔断规则精细化:不要对所有接口使用同一套熔断规则。在Sentinel控制台中,根据接口的SLA(服务等级协议)和业务重要性,设置不同的慢调用比例异常比例阈值和熔断时长。对于支付核心接口,规则应比查询接口更严格。
  3. 降级策略多样化:降级不只有返回默认值。根据场景可以采用:
    • 静默处理:对于非核心的辅助功能(如日志记录、积分更新),失败后仅记录日志,不影响主流程。
    • 备用服务:准备一个简化版的备用服务或缓存数据,在主服务不可用时切换。
    • 队列缓冲:将请求暂存到消息队列(如RocketMQ、Kafka),等待服务恢复后异步处理。
  4. 链路追踪采样策略:在生产环境中,100%采样会对性能有影响。应根据流量设置一个合理的采样率(如0.1),并可以结合动态采样,对错误请求和慢请求提高采样率。
  5. 多维度监控与告警:可观测性体系需要结合链路追踪(Zipkin/Jaeger)、指标监控(Prometheus + Grafana)和日志聚合(ELK)。设置关键指标(如QPS、RT、错误率)的告警,在系统出现“托底”风险前及时干预。
  6. 混沌工程验证:定期使用混沌工程工具(如ChaosBlade)模拟“烂路”场景,如随机杀死服务实例、注入网络延迟、模拟CPU满载等,主动验证系统的“通过性”是否如设计般健壮。

回到我们开头的比喻,为微服务架构增加“门桥”,本质上是通过架构手段,将不确定性的影响局部化、可视化、可管理化。它牺牲了一点初始的简单性(需要引入更多组件和概念),换来的是在复杂、动态、不可靠的网络环境中,系统整体稳定性和韧性的巨大提升。

这套“越野套件”的选择(Spring Cloud Alibaba)只是当前的一种流行实现。其核心思想——服务治理、弹性容错、可观测——是构建高可用分布式系统的通用法则。无论你使用的是Kubernetes + Istio的服务网格方案,还是其他微服务框架,理解并实践这些原则,才是让你应用拥有“极致通过性”的关键。

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

告别AI抽卡:基于Stable Diffusion的批量生成与优化工具实战

最近在B站AI创造公开赛上&#xff0c;我花了整整三天时间&#xff0c;从零到一开发了一个能彻底告别“AI抽卡”焦虑的小工具。如果你也受够了在各类AI绘画、AI视频工具中反复“掷骰子”&#xff0c;只为得到一张满意的图片或一段理想的视频&#xff0c;那么这篇文章就是为你准备…

作者头像 李华
网站建设 2026/9/4 5:46:54

大学毕业不去写字楼,我回村种地了

大学毕业那一年&#xff0c;身边绝大多数同学都奔赴大城市&#xff0c;写字楼、通勤地铁&#xff0c;是大家默认的人生路径。我也曾投递过多份城市岗位&#xff0c;却始终内心忐忑&#xff0c;脑海里反复浮现老家成片的田地。祖辈世代务农&#xff0c;我从小在田埂边长大&#…

作者头像 李华
网站建设 2026/9/4 5:46:02

阿斯盾GO3鼠标评测:中手用户如何选择稳定可靠的无线鼠标

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 5:44:06

AI漫剧制作全流程:从角色一致性到视频合成的技术实践

这次我们来看一个名为《With Light》E32的自制原创双女主都市职场AI漫剧项目。这个项目不是传统的视频剪辑&#xff0c;而是利用AI技术&#xff0c;从剧本、分镜、角色形象到最终视频生成&#xff0c;实现全流程或关键环节的自动化创作。对于内容创作者、独立制片人或对AI影视制…

作者头像 李华
网站建设 2026/9/4 5:43:41

Ollama本地大模型部署实战:下载加速、环境配置及IDE与Web接入

本地跑大模型这件事&#xff0c;最近已经不算什么新鲜技术了&#xff0c;但真正动手折腾过的人都知道&#xff0c;网上教程看着花哨&#xff0c;真到自己上手时&#xff0c;卡在下载、端口、模型名这些小问题上能折腾一下午。我这段时间把 Ollama 从服务器到个人电脑完整部署了…

作者头像 李华