零基础学Java——第十一章:实战项目 - 微服务入门

第十一章:实战项目 - 微服务入门

随着互联网应用的复杂性不断增加,单体应用(Monolithic Application)在可扩展性、可维护性、技术栈灵活性等方面逐渐暴露出一些问题。微服务架构(Microservices Architecture)应运而生,成为构建大型复杂应用的一种流行方式。

1. 什么是微服务?

微服务是一种架构风格,它将一个大型复杂应用拆分成一组小型的、独立部署的服务。每个服务都围绕特定的业务能力构建,并且可以独立开发、测试、部署和扩展。

核心思想

  • 单一职责:每个微服务只关注一项特定的业务功能。
  • 独立部署:每个微服务都可以独立部署,不依赖于其他服务的部署周期。
  • 技术异构性:不同的微服务可以使用不同的编程语言、数据库或技术栈。
  • 去中心化治理:团队可以独立负责自己的服务,包括技术选型和数据管理。
  • 弹性与容错:单个服务的故障不会导致整个系统崩溃。

生活中的例子

想象一个大型电商平台。如果采用单体架构,所有的功能(用户管理、商品管理、订单管理、支付、库存等)都在一个巨大的代码库中。如果采用微服务架构,这些功能会被拆分成独立的服务:

  • 用户服务 (User Service)
  • 商品服务 (Product Service)
  • 订单服务 (Order Service)
  • 支付服务 (Payment Service)
  • 库存服务 (Inventory Service)

这些服务之间通过轻量级的通信机制(通常是HTTP/REST API或消息队列)进行交互。

2. 微服务与单体应用的对比

特性 单体应用 (Monolithic) 微服务 (Microservices)
代码库 单一、庞大 多个、小型、独立
部署 整个应用作为一个单元部署 每个服务独立部署
扩展性 整体扩展,难以针对特定功能进行精细化扩展 可针对每个服务独立扩展
技术栈 通常统一技术栈 不同服务可采用不同技术栈
开发效率 初期快,后期因代码耦合和复杂性增加而变慢 初期可能较慢(需要处理分布式问题),后期因独立性而提高
容错性 单点故障可能导致整个应用不可用 单个服务故障影响范围有限,系统更具弹性
团队协作 大型团队在单一代码库上协作可能存在冲突和瓶颈 小型自治团队负责各自服务,并行开发效率高
复杂性 应用内部复杂性高 分布式系统带来的运维和管理复杂性高

3. 微服务的优势

  • 技术多样性:可以为每个服务选择最适合的技术栈。
  • 弹性伸缩:可以根据每个服务的负载情况独立进行伸缩。
  • 易于维护和理解:每个服务代码量小,业务逻辑清晰。
  • 独立部署,快速迭代:单个服务的修改和部署不影响其他服务,可以更快地交付新功能。
  • 更好的故障隔离:一个服务的故障不会轻易导致整个系统瘫痪。
  • 团队自治:小型团队可以独立负责一个或多个服务,提高开发效率和责任感。

4. 微服务的挑战

  • 分布式系统复杂性:需要处理网络延迟、服务间通信、数据一致性等问题。
  • 运维成本:需要管理和监控大量的服务实例,对自动化运维能力要求高。
  • 测试复杂性:端到端测试和集成测试变得更加复杂。
  • 服务发现与注册:需要机制来动态发现和注册服务实例。
  • 配置管理:需要统一管理各个服务的配置。
  • 链路追踪与监控:需要工具来追踪跨多个服务的请求,并监控系统健康状况。
  • 数据一致性:在分布式环境中保证数据最终一致性是一个挑战。

5. Java 微服务技术栈概览

Java生态系统为构建微服务提供了丰富的框架和工具。

5.1 Spring Boot

Spring Boot是构建Java微服务的首选框架。它简化了Spring应用的创建和部署,并且内置了对常见微服务模式的支持。

5.2 Spring Cloud

Spring Cloud是基于Spring Boot的一系列框架的有序集合,用于快速构建分布式系统中的一些常见模式(例如,配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)。

Spring Cloud 核心组件

  • 服务发现与注册 (Service Discovery & Registration)
    • Netflix Eureka (维护模式,推荐使用Consul或Nacos)
    • HashiCorp Consul
    • Alibaba Nacos
  • 客户端负载均衡 (Client-side Load Balancing)
    • Netflix Ribbon (维护模式,Spring Cloud LoadBalancer是推荐替代方案)
    • Spring Cloud LoadBalancer
  • 声明式REST客户端 (Declarative REST Client)
    • Netflix Feign (现在是OpenFeign)
    • Spring Cloud OpenFeign
  • API网关 (API Gateway)
    • Netflix Zuul (Zuul 1维护模式,Zuul 2不被Spring Cloud直接支持)
    • Spring Cloud Gateway (推荐)
  • 断路器 (Circuit Breaker)
    • Netflix Hystrix (维护模式)
    • Resilience4j (推荐)
    • Sentinel (Alibaba)
  • 配置中心 (Configuration Management)
    • Spring Cloud Config Server
    • HashiCorp Consul
    • Alibaba Nacos
  • 消息总线 (Message Bus)
    • Spring Cloud Bus (通常与Spring Cloud Config配合使用,实现配置动态刷新)
  • 分布式追踪 (Distributed Tracing)
    • Spring Cloud Sleuth (通常与Zipkin或Jaeger集成)

6. 构建一个简单的微服务示例 (使用 Spring Boot)

让我们构思两个简单的微服务:一个"问候服务"(Greeting Service)和一个"用户服务"(User Service)。"问候服务"会调用"用户服务"来获取用户名,然后返回个性化的问候语。

6.1 创建用户服务 (User Service)

  1. 使用 Spring Initializr 创建项目 :

    • Group: com.example.microservices
    • Artifact: user-service
    • Dependencies: Spring Web
  2. 创建 User POJO :

    java 复制代码
    package com.example.microservices.userservice;
    
    public class User {
        private Long id;
        private String username;
    
        public User(Long id, String username) {
            this.id = id;
            this.username = username;
        }
    
        // Getters and Setters
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
    }
  3. 创建 UserController :

    java 复制代码
    package com.example.microservices.userservice;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/users")
    public class UserController {
        private final Map<Long, User> users = new HashMap<>();
    
        public UserController() {
            users.put(1L, new User(1L, "Alice"));
            users.put(2L, new User(2L, "Bob"));
        }
    
        @GetMapping("/{id}")
        public User getUserById(@PathVariable Long id) {
            System.out.println("User Service: Received request for user ID: " + id);
            return users.getOrDefault(id, new User(0L, "Unknown"));
        }
    }
  4. 配置端口 (可选,避免冲突): 在 application.properties 中设置:

    properties 复制代码
    server.port=8081
  5. 运行 User Service .
    测试:访问 http://localhost:8081/users/1,应返回 {"id":1,"username":"Alice"}

6.2 创建问候服务 (Greeting Service)

  1. 使用 Spring Initializr 创建项目 :

    • Group: com.example.microservices
    • Artifact: greeting-service
    • Dependencies: Spring Web, Spring Boot Actuator (可选,用于健康检查等)
  2. 创建 User DTO (Data Transfer Object) (用于接收来自User Service的数据):

    java 复制代码
    package com.example.microservices.greetingservice;
    
    // 这个类结构需要和User Service返回的User对象一致
    public class User {
        private Long id;
        private String username;
    
        // Getters and Setters
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
    }
  3. 创建 GreetingController :

    java 复制代码
    package com.example.microservices.greetingservice;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    public class GreetingController {
    
        // RestTemplate用于进行HTTP调用
        private final RestTemplate restTemplate;
    
        @Autowired
        public GreetingController(RestTemplate restTemplate) {
            this.restTemplate = restTemplate;
        }
    
        @GetMapping("/greet/{userId}")
        public String greetUser(@PathVariable Long userId) {
            System.out.println("Greeting Service: Received request for user ID: " + userId);
            // 调用User Service获取用户信息
            // 注意:这里硬编码了User Service的地址,实际项目中应使用服务发现
            String userServiceUrl = "http://localhost:8081/users/" + userId;
            User user = restTemplate.getForObject(userServiceUrl, User.class);
    
            if (user != null && !"Unknown".equals(user.getUsername())) {
                return "Hello, " + user.getUsername() + "!";
            } else {
                return "Hello, Anonymous User!";
            }
        }
    }
  4. 配置 RestTemplate Bean (在主应用类中添加):

    java 复制代码
    package com.example.microservices.greetingservice;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class GreetingServiceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(GreetingServiceApplication.class, args);
        }
    
        @Bean // 将RestTemplate注册为一个Bean,Spring会管理它的生命周期
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
  5. 配置端口 : 在 application.properties 中设置 (例如 server.port=8082)。

  6. 运行 Greeting Service .
    测试:访问 http://localhost:8082/greet/1。Greeting Service会调用User Service,然后返回 Hello, Alice!
    如果User Service未运行或ID不存在,可能会返回 Hello, Anonymous User! 或报错。

注意:这个例子非常基础,它硬编码了服务地址。在实际的微服务架构中,你需要使用服务发现机制(如Eureka, Consul, Nacos)和客户端负载均衡(如Spring Cloud LoadBalancer)来动态查找和调用服务。

7. 服务发现与注册 (以 Nacos 为例,概念性介绍)

当微服务数量增多,手动管理它们的地址和端口变得不现实。服务发现与注册中心解决了这个问题。

  • 服务注册:每个微服务实例在启动时,向注册中心注册自己的网络位置(IP地址、端口号)和其他元数据。
  • 服务发现:当一个服务(如Greeting Service)需要调用另一个服务(如User Service)时,它会向注册中心查询User Service的可用实例列表。
  • 健康检查:注册中心会定期检查已注册服务的健康状况,并剔除不健康的实例。

Alibaba Nacos 是一个功能丰富的平台,提供服务发现、配置管理和服务管理。

大致流程

  1. 启动 Nacos Server
  2. User Service 配置 :
    • 添加 Nacos Discovery Starter 依赖 (spring-cloud-starter-alibaba-nacos-discovery)。

    • application.properties 中配置 Nacos Server 地址和应用名:

      properties 复制代码
      spring.application.name=user-service
      spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    • 在主类上添加 @EnableDiscoveryClient 注解。

  3. Greeting Service 配置 :
    • 添加 Nacos Discovery Starter 依赖。

    • 配置 Nacos Server 地址和应用名 (spring.application.name=greeting-service)。

    • 在主类上添加 @EnableDiscoveryClient 注解。

    • 修改 RestTemplate Bean,添加 @LoadBalanced 注解,使其能够通过服务名进行调用:

      java 复制代码
      @Bean
      @LoadBalanced // 开启负载均衡
      public RestTemplate restTemplate() {
          return new RestTemplate();
      }
    • 修改 GreetingController 中调用User Service的URL,使用服务名代替硬编码的IP和端口:

      java 复制代码
      // String userServiceUrl = "http://localhost:8081/users/" + userId; // 旧方式
      String userServiceUrl = "http://user-service/users/" + userId; // 新方式,user-service是User Service在Nacos中注册的服务名

当Greeting Service通过 http://user-service/... 调用时,Spring Cloud LoadBalancer (集成了Ribbon的功能) 会从Nacos获取 user-service 的可用实例列表,并选择一个实例进行调用。

8. API 网关 (以 Spring Cloud Gateway 为例,概念性介绍)

当微服务数量众多时,客户端直接与所有微服务通信会变得复杂且难以管理。API网关作为系统的唯一入口,提供了请求路由、聚合、安全、监控等功能。

Spring Cloud Gateway 是一个基于Spring Framework 5, Project Reactor和Spring Boot 2构建的API网关。

主要功能

  • 路由 (Routing):根据请求的路径、头部等信息将请求转发到后端相应的微服务。
  • 断言 (Predicates):匹配HTTP请求中的任何内容,如路径、方法、头部等,用于决定路由规则是否适用。
  • 过滤器 (Filters):在请求被路由前后执行一些逻辑,如修改请求/响应、认证、限流等。

大致配置

  1. 创建一个新的Spring Boot项目,添加 Spring Cloud Gateway 依赖。

  2. application.propertiesapplication.yml 中配置路由规则:

    yaml 复制代码
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true # 开启从注册中心自动发现服务并创建路由
              lower-case-service-id: true # 将服务名转为小写作为路径前缀
          routes:
            - id: user_service_route # 路由ID,唯一即可
              uri: lb://user-service # lb:// 表示从注册中心负载均衡地选择 user-service 实例
              predicates:
                - Path=/api/users/** # 当请求路径匹配 /api/users/** 时,应用此路由
              # filters: # 可以添加过滤器
                # - StripPrefix=1 # 例如,去掉路径中的第一个前缀 /api
    
            - id: greeting_service_route
              uri: lb://greeting-service
              predicates:
                - Path=/api/greetings/**
    server:
      port: 8080 # 网关端口

    如果开启了 discovery.locator.enabled=true,Gateway会自动为注册中心中的每个服务创建一个路由,路径通常是 /服务名小写/**。例如,可以直接通过 http://localhost:8080/user-service/users/1 访问User Service。

客户端现在只需要与API网关 (如 http://localhost:8080) 通信,网关会将请求路由到相应的后端微服务。

9. 总结与下一步

微服务架构为构建大型、复杂的分布式系统提供了一种灵活且可扩展的方式。Spring Boot和Spring Cloud为Java开发者构建微服务提供了强大的支持。

入门微服务需要掌握的关键概念

  • 服务拆分原则
  • 服务间通信 (REST API, 消息队列)
  • 服务发现与注册
  • 客户端负载均衡
  • API网关
  • 断路器与容错
  • 配置管理
  • 分布式追踪与监控

下一步可以探索的内容

  • 深入学习 Spring Cloud 各个组件:如Nacos/Consul, OpenFeign, Resilience4j, Spring Cloud Gateway, Spring Cloud Config等。
  • 容器化与编排:学习Docker和Kubernetes,用于微服务的打包、部署和管理。
  • 消息队列:学习RabbitMQ, Kafka等,实现异步通信和解耦。
  • 分布式事务:了解Saga模式、TCC模式等处理分布式事务的方案。
  • DevOps实践:学习CI/CD(持续集成/持续交付)流程,实现微服务的自动化构建、测试和部署。

微服务是一个庞大且不断发展的领域。从小处着手,逐步实践,你会慢慢掌握它的精髓。祝你学习愉快!

相关推荐
bing_1586 分钟前
MQTT 在Spring Boot 中的使用
java·spring boot·后端·mqtt
purpleseashell_Lili17 分钟前
react 基本写法
java·服务器·前端
咕噜咕噜啦啦31 分钟前
Python爬虫入门
开发语言·爬虫·python
oneDay++39 分钟前
# IntelliJ IDEA企业版高效配置指南:从主题到快捷键的终极优化
java·经验分享·intellij-idea·学习方法
dubochao_xinxi40 分钟前
✅ TensorRT Python 安装精简流程(适用于 Ubuntu 20.04+)
开发语言·python·ubuntu
感谢地心引力44 分钟前
【Matlab】最新版2025a发布,深色模式、Copilot编程助手上线!
开发语言·windows·matlab·copilot
Jasmin Tin Wei1 小时前
idea中的vcs不见了,如何解决
java·ide·intellij-idea
Java程序员-小白1 小时前
使用java -jar命令指定VM参数-D运行jar包报错问题
java·开发语言·jar
ClearViper32 小时前
Java的多线程笔记
java·开发语言·笔记
敷啊敷衍2 小时前
深入探索 C++ 中的 string 类:从基础到实践
开发语言·数据结构·c++