本文目录导读:

- 项目结构
- Maven配置 (pom.xml)
- 主应用类
- 配置类
- 实体类
- 服务客户端 (Feign)
- 服务实现
- 控制器
- 应用配置 (application.yml)
- Istio 配置
- Docker 部署配置
- Kubernetes 部署文件
- Prometheus 配置
- 使用说明
- 监控和追踪
我来为您提供一个完整的服务网格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实现了完整的服务网格解决方案。