Java实现服务网格案例

wen java案例 3

本文目录导读:

Java实现服务网格案例

  1. 项目结构
  2. Maven配置 (pom.xml)
  3. 主应用类
  4. 配置类
  5. 实体类
  6. 服务客户端 (Feign)
  7. 服务实现
  8. 控制器
  9. 应用配置 (application.yml)
  10. Istio 配置
  11. Docker 部署配置
  12. Kubernetes 部署文件
  13. Prometheus 配置
  14. 使用说明
  15. 监控和追踪

我来为您提供一个完整的服务网格Java实现案例,使用Spring Cloud和Istio的集成方案。

项目结构

service-mesh-demo/
├── pom.xml
├── src/main/java/com/example/
│   ├── ServiceMeshApplication.java
│   ├── config/
│   │   ├── SleuthConfig.java
│   │   └── ResilienceConfig.java
│   ├── controller/
│   │   ├── OrderController.java
│   │   ├── UserController.java
│   │   └── PaymentController.java
│   ├── service/
│   │   ├── OrderService.java
│   │   ├── UserService.java
│   │   └── PaymentService.java
│   └── interceptor/
│       └── TraceInterceptor.java
└── resources/
    ├── application.yml
    └── istio/
        ├── virtual-service.yaml
        └── destination-rule.yaml

Maven配置 (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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>service-mesh-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.8</spring-cloud.version>
    </properties>
    <dependencies>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Cloud OpenFeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- Spring Cloud Sleuth -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <!-- Resilience4j -->
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>2.0.2</version>
        </dependency>
        <!-- Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- Prometheus -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <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>
</project>

主应用类

package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.sleuth.zipkin2.EnableZipkinServer;
@SpringBootApplication
@EnableFeignClients
public class ServiceMeshApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceMeshApplication.class, args);
    }
}

配置类

1 追踪配置 (SleuthConfig.java)

package com.example.config;
import brave.sampler.Sampler;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class SleuthConfig {
    @Bean
    public Sampler defaultSampler() {
        return Sampler.ALWAYS_SAMPLE;
    }
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    @Bean
    public ZipkinProperties zipkinProperties() {
        ZipkinProperties properties = new ZipkinProperties();
        properties.setBaseUrl("http://zipkin:9411");
        properties.setEnabled(true);
        return properties;
    }
}

2 弹性配置 (ResilienceConfig.java)

package com.example.config;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class ResilienceConfig {
    @Bean
    public CircuitBreaker circuitBreaker() {
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofMillis(1000))
            .permittedNumberOfCallsInHalfOpenState(3)
            .slidingWindowSize(10)
            .minimumNumberOfCalls(5)
            .build();
        return CircuitBreaker.of("orderService", config);
    }
    @Bean
    public Retry retry() {
        RetryConfig config = RetryConfig.custom()
            .maxAttempts(3)
            .waitDuration(Duration.ofMillis(100))
            .build();
        return Retry.of("orderService", config);
    }
    @Bean
    public TimeLimiter timeLimiter() {
        TimeLimiterConfig config = TimeLimiterConfig.custom()
            .timeoutDuration(Duration.ofSeconds(3))
            .build();
        return TimeLimiter.of("orderService", config);
    }
}

实体类

package com.example.model;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    private Long id;
    private Long userId;
    private String orderNumber;
    private BigDecimal amount;
    private String status;
    private LocalDateTime createTime;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String username;
    private String email;
    private String phone;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Payment {
    private Long id;
    private Long orderId;
    private BigDecimal amount;
    private String paymentMethod;
    private String status;
    private LocalDateTime paymentTime;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
            .code(200)
            .message("success")
            .data(data)
            .build();
    }
    public static <T> ApiResponse<T> error(int code, String message) {
        return ApiResponse.<T>builder()
            .code(code)
            .message(message)
            .build();
    }
}

服务客户端 (Feign)

package com.example.client;
import com.example.model.ApiResponse;
import com.example.model.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
    @GetMapping("/api/users/{id}")
    ApiResponse<User> getUserById(@PathVariable("id") Long id);
}
@Component
class UserClientFallback implements UserClient {
    @Override
    public ApiResponse<User> getUserById(Long id) {
        return ApiResponse.error(503, "用户服务不可用");
    }
}
package com.example.client;
import com.example.model.ApiResponse;
import com.example.model.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = "payment-service", fallback = PaymentClientFallback.class)
public interface PaymentClient {
    @PostMapping("/api/payments")
    ApiResponse<Payment> createPayment(@RequestBody Payment payment);
    @GetMapping("/api/payments/order/{orderId}")
    ApiResponse<Payment> getPaymentByOrderId(@PathVariable("orderId") Long orderId);
}
@Component
class PaymentClientFallback implements PaymentClient {
    @Override
    public ApiResponse<Payment> createPayment(Payment payment) {
        return ApiResponse.error(503, "支付服务不可用");
    }
    @Override
    public ApiResponse<Payment> getPaymentByOrderId(Long orderId) {
        return ApiResponse.error(503, "支付服务不可用");
    }
}

服务实现

1 订单服务 (OrderService.java)

package com.example.service;
import com.example.client.PaymentClient;
import com.example.client.UserClient;
import com.example.model.*;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.retry.annotation.Retry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID;
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {
    private final UserClient userClient;
    private final PaymentClient paymentClient;
    // 使用ConcurrentHashMap存储订单(模拟数据库)
    private final ConcurrentHashMap<Long, Order> orderStore = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Long, List<Order>> userOrdersStore = new ConcurrentHashMap<>();
    @CircuitBreaker(name = "orderService", fallbackMethod = "createOrderFallback")
    @Retry(name = "orderService")
    public ApiResponse<Order> createOrder(Long userId, BigDecimal amount) {
        log.info("开始创建订单,用户ID: {}, 金额: {}", userId, amount);
        // 调用用户服务获取用户信息
        ApiResponse<User> userResponse = userClient.getUserById(userId);
        if (userResponse.getCode() != 200) {
            return ApiResponse.error(userResponse.getCode(), "获取用户信息失败");
        }
        User user = userResponse.getData();
        log.info("成功获取用户信息: {}", user.getUsername());
        // 创建订单
        Long orderId = System.currentTimeMillis();
        Order order = Order.builder()
            .id(orderId)
            .userId(userId)
            .orderNumber(generateOrderNumber(userId))
            .amount(amount)
            .status("CREATED")
            .createTime(LocalDateTime.now())
            .build();
        orderStore.put(orderId, order);
        userOrdersStore.computeIfAbsent(userId, k -> new ArrayList<>()).add(order);
        // 调用支付服务
        Payment payment = Payment.builder()
            .orderId(orderId)
            .amount(amount)
            .paymentMethod("BALANCE")
            .status("PENDING")
            .build();
        ApiResponse<Payment> paymentResponse = paymentClient.createPayment(payment);
        if (paymentResponse.getCode() != 200) {
            order.setStatus("PAYMENT_FAILED");
            return ApiResponse.error(500, "支付创建失败");
        }
        order.setStatus("PAID");
        log.info("订单创建成功,订单号: {}", order.getOrderNumber());
        return ApiResponse.success(order);
    }
    @CircuitBreaker(name = "orderService", fallbackMethod = "getOrderFallback")
    public ApiResponse<Order> getOrder(Long orderId) {
        log.info("查询订单,订单ID: {}", orderId);
        Order order = orderStore.get(orderId);
        if (order == null) {
            return ApiResponse.error(404, "订单不存在");
        }
        // 异步获取支付信息
        CompletableFuture<ApiResponse<Payment>> paymentFuture = 
            CompletableFuture.supplyAsync(() -> paymentClient.getPaymentByOrderId(orderId));
        try {
            ApiResponse<Payment> paymentResponse = paymentFuture.get();
            if (paymentResponse.getCode() == 200) {
                order.setStatus("PAID");
            }
        } catch (Exception e) {
            log.warn("获取支付信息失败", e);
        }
        return ApiResponse.success(order);
    }
    @CircuitBreaker(name = "orderService", fallbackMethod = "getUserOrdersFallback")
    public ApiResponse<List<Order>> getUserOrders(Long userId) {
        log.info("查询用户订单,用户ID: {}", userId);
        List<Order> orders = userOrdersStore.getOrDefault(userId, new ArrayList<>());
        return ApiResponse.success(orders);
    }
    private String generateOrderNumber(Long userId) {
        return "ORD-" + userId + "-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
    }
    // 降级方法
    private ApiResponse<Order> createOrderFallback(Long userId, BigDecimal amount, Throwable t) {
        log.error("创建订单降级,用户ID: {}", userId, t);
        return ApiResponse.error(503, "订单服务暂时不可用,请稍后重试");
    }
    private ApiResponse<Order> getOrderFallback(Long orderId, Throwable t) {
        log.error("查询订单降级,订单ID: {}", orderId, t);
        return ApiResponse.error(503, "订单服务暂时不可用,请稍后重试");
    }
    private ApiResponse<List<Order>> getUserOrdersFallback(Long userId, Throwable t) {
        log.error("查询用户订单降级,用户ID: {}", userId, t);
        return ApiResponse.error(503, "订单服务暂时不可用,请稍后重试");
    }
}

2 用户服务 (UserService.java)

package com.example.service;
import com.example.model.ApiResponse;
import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
public class UserService {
    private final ConcurrentHashMap<Long, User> userStore = new ConcurrentHashMap<>();
    @PostConstruct
    public void init() {
        // 初始化一些测试数据
        userStore.put(1L, User.builder()
            .id(1L)
            .username("zhangsan")
            .email("zhangsan@example.com")
            .phone("13800138000")
            .build());
        userStore.put(2L, User.builder()
            .id(2L)
            .username("lisi")
            .email("lisi@example.com")
            .phone("13900139000")
            .build());
    }
    public ApiResponse<User> getUserById(Long id) {
        log.info("查询用户信息,用户ID: {}", id);
        // 模拟延迟
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        User user = userStore.get(id);
        if (user == null) {
            return ApiResponse.error(404, "用户不存在");
        }
        return ApiResponse.success(user);
    }
}

3 支付服务 (PaymentService.java)

package com.example.service;
import com.example.model.ApiResponse;
import com.example.model.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
public class PaymentService {
    private final ConcurrentHashMap<Long, Payment> paymentStore = new ConcurrentHashMap<>();
    public ApiResponse<Payment> createPayment(Payment payment) {
        log.info("创建支付记录,订单ID: {}", payment.getOrderId());
        // 模拟支付处理
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Long paymentId = System.currentTimeMillis();
        payment.setId(paymentId);
        payment.setPaymentTime(LocalDateTime.now());
        payment.setStatus("SUCCESS");
        // 生成支付流水号
        payment.setPaymentMethod("WeChat Pay");
        paymentStore.put(paymentId, payment);
        log.info("支付成功,支付ID: {}", paymentId);
        return ApiResponse.success(payment);
    }
    public ApiResponse<Payment> getPaymentByOrderId(Long orderId) {
        log.info("查询支付记录,订单ID: {}", orderId);
        Payment payment = paymentStore.values().stream()
            .filter(p -> p.getOrderId().equals(orderId))
            .findFirst()
            .orElse(null);
        if (payment == null) {
            return ApiResponse.error(404, "支付记录不存在");
        }
        return ApiResponse.success(payment);
    }
}

控制器

1 订单控制器 (OrderController.java)

package com.example.controller;
import com.example.model.ApiResponse;
import com.example.model.Order;
import com.example.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.math.BigDecimal;
import java.util.List;
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
    private final OrderService orderService;
    @PostMapping
    public ResponseEntity<ApiResponse<Order>> createOrder(
            @RequestParam("userId") Long userId,
            @RequestParam("amount") BigDecimal amount) {
        return ResponseEntity.ok(orderService.createOrder(userId, amount));
    }
    @GetMapping("/{orderId}")
    public ResponseEntity<ApiResponse<Order>> getOrder(@PathVariable Long orderId) {
        return ResponseEntity.ok(orderService.getOrder(orderId));
    }
    @GetMapping("/user/{userId}")
    public ResponseEntity<ApiResponse<List<Order>>> getUserOrders(@PathVariable Long userId) {
        return ResponseEntity.ok(orderService.getUserOrders(userId));
    }
}

2 用户控制器 (UserController.java)

package com.example.controller;
import com.example.model.ApiResponse;
import com.example.model.User;
import com.example.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<User>> getUserById(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }
}

3 支付控制器 (PaymentController.java)

package com.example.controller;
import com.example.model.ApiResponse;
import com.example.model.Payment;
import com.example.service.PaymentService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/payments")
@RequiredArgsConstructor
public class PaymentController {
    private final PaymentService paymentService;
    @PostMapping
    public ResponseEntity<ApiResponse<Payment>> createPayment(@RequestBody Payment payment) {
        return ResponseEntity.ok(paymentService.createPayment(payment));
    }
    @GetMapping("/order/{orderId}")
    public ResponseEntity<ApiResponse<Payment>> getPaymentByOrderId(@PathVariable Long orderId) {
        return ResponseEntity.ok(paymentService.getPaymentByOrderId(orderId));
    }
}

应用配置 (application.yml)

server:
  port: 8080
spring:
  application:
    name: order-service
  sleuth:
    sampler:
      probability: 1.0
  zipkin:
    base-url: http://zipkin:9411
    enabled: true
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus,metrics
  metrics:
    export:
      prometheus:
        enabled: true
  endpoint:
    health:
      show-details: always
resilience4j:
  circuitbreaker:
    instances:
      orderService:
        register-health-indicator: true
        sliding-window-size: 10
        sliding-window-type: COUNT_BASED
        minimum-number-of-calls: 5
        permitted-number-of-calls-in-half-open-state: 3
        automatic-transition-from-open-to-half-open-enabled: true
        wait-duration-in-open-state: 5s
        failure-rate-threshold: 50
        event-consumer-buffer-size: 10
  retry:
    instances:
      orderService:
        max-attempts: 3
        wait-duration: 100ms
        retry-exceptions:
          - java.io.IOException
  timelimiter:
    instances:
      orderService:
        timeout-duration: 3s
        cancel-running-future: true
logging:
  level:
    com.example: DEBUG
    org.springframework.cloud.sleuth: DEBUG

Istio 配置

1 虚拟服务 (virtual-service.yaml)

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: order-service-virtual-service
  namespace: default
spec:
  hosts:
  - order-service
  http:
  - match:
    - headers:
        version:
          exact: v1
    route:
    - destination:
        host: order-service
        subset: v1
      weight: 100
  - route:
    - destination:
        host: order-service
        subset: v2
      weight: 0

2 目标规则 (destination-rule.yaml)

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: order-service-destination-rule
  namespace: default
spec:
  host: order-service
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    connectionPool:
      tcp:
        maxConnections: 100
        connectTimeout: 100ms
      http:
        http1MaxPendingRequests: 100
        http2MaxRequests: 1000
        maxRequestsPerConnection: 100
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 60s
      maxEjectionPercent: 50
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

3 网关配置 (gateway.yaml)

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: ingress-gateway
  namespace: default
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*.example.com"

4 服务入口 (service-entry.yaml)

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-services
  namespace: default
spec:
  hosts:
  - "*.google.com"
  ports:
  - number: 80
    name: http
    protocol: HTTP
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL

Docker 部署配置

1 Dockerfile

FROM openjdk:11-jre-slim
LABEL maintainer="your-email@example.com"
WORKDIR /app
COPY target/service-mesh-demo-1.0.0.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

2 docker-compose.yml

version: '3.8'
services:
  order-service:
    build: .
    container_name: order-service
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - EUREKA_CLIENT_ENABLED=true
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    networks:
      - mesh-network
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
  zipkin:
    image: openzipkin/zipkin:2.23
    container_name: zipkin
    ports:
      - "9411:9411"
    networks:
      - mesh-network
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - mesh-network
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - mesh-network
networks:
  mesh-network:
    driver: bridge

Kubernetes 部署文件

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: default
  labels:
    app: order-service
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
      version: v1
  template:
    metadata:
      labels:
        app: order-service
        version: v1
    spec:
      containers:
      - name: order-service
        image: your-registry/order-service:1.0.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: ZIPKIN_BASE_URL
          value: "http://zipkin:9411"
      restartPolicy: Always
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: default
  labels:
    app: order-service
spec:
  selector:
    app: order-service
  ports:
  - port: 8080
    targetPort: 8080
    name: http
  type: ClusterIP
---
# service-monitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: order-service-monitor
  namespace: monitoring
  labels:
    app: order-service
spec:
  selector:
    matchLabels:
      app: order-service
  endpoints:
  - port: http
    path: /actuator/prometheus
    interval: 15s

Prometheus 配置

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
scrape_configs:
  - job_name: 'spring-boot-apps'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets:
        - 'order-service:8080'
        labels:
          application: 'order-service'
          group: 'service-mesh'
  - job_name: 'istio-mesh'
    kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
        - default
        - istio-system
    relabel_configs:
    - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
      regex: istio-proxy;(https|http)
      action: keep

使用说明

1 本地运行

# 构建项目
mvn clean package
# 运行服务
java -jar target/service-mesh-demo-1.0.0.jar
# 测试API
curl -X POST "http://localhost:8080/api/orders?userId=1&amount=100.00"
curl -X GET "http://localhost:8080/api/orders/1234567890"
curl -X GET "http://localhost:8080/api/orders/user/1"

2 部署到Kubernetes

# 构建Docker镜像
docker build -t order-service:1.0.0 .
# 推送到仓库
docker push your-registry/order-service:1.0.0
# 部署服务
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 部署Istio配置
istioctl install --set profile=demo
kubectl apply -f gateway.yaml
kubectl apply -f virtual-service.yaml
kubectl apply -f destination-rule.yaml

监控和追踪

1 访问监控面板

  • Zipkin UI: http://localhost:9411
  • Prometheus UI: http://localhost:9090
  • Grafana UI: http://localhost:3000

2 查看追踪信息

# 查看Pod状态
kubectl get pods
# 查看服务日志
kubectl logs -f deployment/order-service
# 查看Zipkin追踪
curl "http://localhost:9411/api/v2/traces?serviceName=order-service"

这个服务网格案例展示了如何使用Java和Spring Cloud实现服务网格的核心功能,包括服务发现、负载均衡、熔断、重试、追踪等,并结合Istio实现了完整的服务网格解决方案。

抱歉,评论功能暂时关闭!