从零到一搭建SpringCloud微服务,一场代码世界的“分家”大戏

大家好,我是小悟。

第一部分:微服务是什么?一场"分家过日子"的哲学

你开了一家超级火爆的"程序猿餐厅",一开始所有菜都在一个大厨房做:

  • 单体架构:一个厨子(服务器)包揽所有菜,点单、炒菜、煲汤、洗碗全管
  • 客人点个蛋炒饭,整个厨房都得动起来
  • 想升级蛋炒饭配方?得关店装修(停机部署)
  • 厨子感冒了?全店停业(单点故障)

微服务就像把大厨房拆了

  • 蛋炒饭部、火锅部、奶茶部各自独立
  • 每个部门有自己的小厨房(服务实例)
  • 通过传菜机器人(服务通信)协作
  • 奶茶部炸了?没事,蛋炒饭照常供应(故障隔离)

第二部分:开干!八步搭建SpringCloud全家桶

先拜码头(环境准备)

bash 复制代码
# 必备三件套
Java 11+(别用Java 8了,它都退休了)
Maven 3.6+(Java界的包工头)
IDE(IntelliJ IDEA - 程序员的法拉利)

第1步:创建父工程 - 家族的"族谱"

xml 复制代码
<!-- pom.xml - 家族总章程 -->
<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.coder.micro</groupId>
    <artifactId>micro-family</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    
    <!-- 声明SpringCloud版本 - 家族用的家规版本 -->
    <properties>
        <spring.cloud.version>2021.0.3</spring.cloud.version>
        <spring.boot.version>2.7.14</spring.boot.version>
    </properties>
    
    <!-- 依赖管理 - 所有子孙该用什么版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <modules>
        <module>eureka-server</module>
        <module>gateway</module>
        <module>order-service</module>
        <module>user-service</module>
    </modules>
</project>

第2步:Eureka服务注册中心 - 家族的"户口本"

bash 复制代码
# 想象成家族微信群:
# 谁在(服务上线)@所有人
# 谁睡了(服务下线)改备注
# 谁失联了(心跳检测)踢出群
csharp 复制代码
// EurekaServerApplication.java - 户口本管理员
@SpringBootApplication
@EnableEurekaServer  // 开启户口本功能
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

// application.yml - 户口本配置
server:
  port: 8761  # 户口本办公室门牌号

eureka:
  client:
    register-with-eureka: false  # 户口本不用自己登记自己
    fetch-registry: false  # 不用拉取别人的信息
  server:
    eviction-interval-timer-in-ms: 30000  # 30秒检查一次谁失联了

第3步:API网关 - 家族的"前台小姐姐"

yaml 复制代码
// GatewayApplication.java
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

// Gateway配置 - 前台路由规则
spring:
  cloud:
    gateway:
      routes:
        - id: order-service  # 找点单部的路
          uri: lb://ORDER-SERVICE  # lb=负载均衡,像电梯分流入流
          predicates:
            - Path=/api/orders/**  # 所有/orders开头的请求
            
        - id: user-service  # 找会员部的路
          uri: lb://USER-SERVICE
          predicates:
            - Path=/api/users/**
            
        # 全局过滤器 - 前台的小本本
      default-filters:
        - AddRequestHeader=X-Request-From, gateway  # 给每个请求盖个章

第4步:订单服务 - 家族的"点单部"

less 复制代码
// OrderServiceApplication.java
@SpringBootApplication
@EnableEurekaClient  // 大喊一声:我上线了!
@EnableFeignClients  // 开启"打电话"功能
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// OrderController.java - 点单部接待处
@RestController
@RequestMapping("/orders")
@Slf4j
public class OrderController {
    
    @Autowired
    private UserServiceClient userServiceClient;  // 用户部的联系电话
    
    @PostMapping
    public OrderDTO createOrder(@RequestBody OrderRequest request) {
        log.info("接到新订单:{}", request);
        
        // 打个电话问用户部:这人是不是VIP?
        UserDTO user = userServiceClient.getUser(request.getUserId());
        
        if (user.isVip()) {
            log.info("VIP用户,加急处理!");
            return processVipOrder(request);
        }
        
        return processNormalOrder(request);
    }
    
    // 假装这里有很多业务逻辑...
    private OrderDTO processVipOrder(OrderRequest request) {
        return OrderDTO.builder()
                .orderId(UUID.randomUUID().toString())
                .status("VIP_优先处理")
                .message("老板,您的订单已插队!")
                .build();
    }
}

第5步:用户服务 - 家族的"会员部"

less 复制代码
// UserController.java
@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
    
    // 用户部的VIP名单
    private Map<String, UserDTO> userDatabase = new HashMap<>();
    
    @PostConstruct
    public void init() {
        userDatabase.put("1001", new UserDTO("1001", "张大款", true));
        userDatabase.put("1002", new UserDTO("1002", "李铁柱", false));
    }
    
    @GetMapping("/{userId}")
    public UserDTO getUser(@PathVariable String userId) {
        log.info("查询用户:{}", userId);
        
        // 模拟网络延迟
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return userDatabase.getOrDefault(userId, 
                new UserDTO(userId, "未知用户", false));
    }
}

第6步:服务间通信 - 家族的"内部电话系统"

less 复制代码
// UserServiceClient.java - 订单部用来呼叫用户部的电话
@FeignClient(name = "USER-SERVICE")  // 声明要呼叫的服务名
public interface UserServiceClient {
    
    @GetMapping("/users/{userId}")  // 电话号码和拨号方式
    UserDTO getUser(@PathVariable String userId);
    
    // 这就是SpringCloud的魔法:
    // 看起来像本地调用,实际上是HTTP请求
    // 像极了微信语音,看似在身边,实际可能隔了个太平洋
}

第7步:配置中心 - 家族的"公告栏"

yaml 复制代码
// 每个服务都要有这个配置
spring:
  config:
    import: optional:configserver:http://localhost:8888  # 看公告栏地址
  cloud:
    config:
      fail-fast: false  # 公告栏坏了也不影响营业
      
# 公告栏上写着:
# order-service.yml:
#   优惠活动:双十一打五折
#   超时时间:30秒
#   
# user-service.yml:
#   VIP门槛:消费满10000
#   签到积分:10分/天

第8步:熔断降级 - 家族的"应急方案"

less 复制代码
// OrderService中调用用户服务时
@FeignClient(name = "USER-SERVICE", 
             fallback = UserServiceFallback.class)  // 备胎方案
public interface UserServiceClient {
    // ...
}

// 用户部失联时的备胎方案
@Component
@Slf4j
public class UserServiceFallback implements UserServiceClient {
    
    @Override
    public UserDTO getUser(String userId) {
        log.warn("用户服务失联,启用默认用户信息");
        
        // 返回兜底数据,保证订单服务不崩溃
        return UserDTO.builder()
                .userId(userId)
                .username("默认用户")
                .vip(false)
                .fromFallback(true)  // 标记这是备胎数据
                .build();
    }
}

第三部分:启动全家桶的"开机仪式"

bash 复制代码
# 启动顺序很重要,像玩多米诺骨牌:
1. 启动户口本(Eureka Server)
   cd eureka-server && mvn spring-boot:run
   # 访问 http://localhost:8761 看谁在线

2. 启动会员部(User Service)
   cd user-service && mvn spring-boot:run
   # 在Eureka页面应该能看到USER-SERVICE

3. 启动点单部(Order Service)
   cd order-service && mvn spring-boot:run

4. 启动前台(Gateway)
   cd gateway && mvn spring-boot:run

# 测试一下:
curl http://localhost:8080/api/orders \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"userId": "1001", "productId": "P001"}'
  
# 应该返回:VIP订单已插队!

第四部分:微服务开发的"生存法则"

1. 服务拆分原则 - "分家不分心"

diff 复制代码
// 好的拆分:按业务能力
- 订单服务:管下单、支付、退款
- 库存服务:管商品库存
- 物流服务:管配送

// 坏的拆分:按技术层
- Controller服务:只放Controller  // 别这么干!
- Service服务:只放Service      // 这是给自己挖坑!
- DAO服务:只放DAO            // 你会哭的!

2. 数据库设计 - "各管各的账"

sql 复制代码
-- 订单服务有自己的订单表
CREATE TABLE orders (
    id VARCHAR(32) PRIMARY KEY,
    user_id VARCHAR(32),  -- 只存用户ID,不存用户详情
    amount DECIMAL(10, 2)
    -- 不在这里创建user表的外键!这是微服务大忌!
);

-- 用户服务有自己的用户表
CREATE TABLE users (
    id VARCHAR(32) PRIMARY KEY,
    name VARCHAR(100),
    vip_level INT
    -- 不知道订单表的存在,各过各的
);

3. 分布式事务 - "家族的信任危机"

typescript 复制代码
// 使用Seata处理分布式事务
@GlobalTransactional  // 分布式事务注解
public void placeOrder(OrderRequest request) {
    // 1. 扣库存(调用库存服务)
    inventoryService.reduceStock(request.getProductId());
    
    // 2. 创建订单
    orderService.createOrder(request);
    
    // 3. 扣款(调用支付服务)
    paymentService.deductBalance(request.getUserId());
    
    // 任何一步失败,所有操作都会回滚
    // 像极了"要么全部成功,要么全部撤销"
}

总结:微服务之路的苦与乐

你得到的"超能力":

  1. 独立部署:改个按钮颜色不用重启整个系统
  2. 技术异构:订单用Java,推荐用Python,数据分析用Go
  3. 弹性伸缩:双十一给订单服务多加10台机器
  4. 容错性:推荐服务挂了?商品详情页照样能打开

你面临的"挑战":

  1. 分布式debug:找bug像侦探破案,线索分布在5个服务里
  2. 网络延迟:本地方法调用1ms,服务间调用可能100ms
  3. 数据一致性:用户余额减少了,订单却没创建成功?
  4. 运维复杂度:原来管1个应用,现在要管20个服务

最后:

微服务不是银弹,是银制餐具

  • 适合大型团队(各自做饭不打架)
  • 适合快速迭代(各部分独立升级)
  • 适合复杂系统(分而治之)

但如果你只是开个煎饼摊

  • 一个平锅(单体应用)够了
  • 非要搞5个小灶(微服务)?
  • 光协调哪个摊鸡蛋哪个撒葱花就够你受的

微服务解决的是"人"的问题,不是"技术"的问题。当团队大到需要分山头、业务复杂到需要分领域时,才是微服务登场的最佳时机。否则,你就是用导弹打蚊子------效果震撼,但真的没必要!


代码世界的真理:没有最好的架构,只有最合适的架构。微服务不是终点,而是你架构演化路上的一个里程碑。创建第一个微服务,记得先从小处着手,毕竟,罗马不是一天建成的,微服务也不是一次拆完的!

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
于樱花森上飞舞2 小时前
【多线程】常见的锁策略与锁
java·开发语言·算法·java-ee
吃喝不愁霸王餐APP开发者2 小时前
使用Mockito与WireMock对美团霸王餐接口进行契约测试与集成验证
java·json
明洞日记2 小时前
【设计模式手册023】外观模式 - 如何简化复杂系统
java·设计模式·外观模式
独自归家的兔2 小时前
面试实录:三大核心问题深度拆解(三级缓存 + 工程规范 + 逻辑思维)
java·后端·面试·职场和发展
故旧2 小时前
PyTorch 2.0 核心技术深度解析torch.compile 从原理到实践
后端
毕设源码-郭学长2 小时前
【开题答辩全过程】以 共享单车后台管理系统为例,包含答辩的问题和答案
java·开发语言·tomcat
北城以北88882 小时前
SpringBoot--SpringBoot集成RabbitMQ
java·spring boot·rabbitmq·java-rabbitmq
Zsh-cs2 小时前
SpringMVC
java·springmvc
用户8307196840823 小时前
Java 并发进化史:从踩坑到躺赢
java