/**
* POST TITLE: Traffic Mirroring in Service Mesh with Java
*
* Complete implementation of traffic mirroring (shadowing) for Istio, Linkerd, and custom solutions
*/
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.istio.client.IstioClient;
import io.fabric8.istio.client.DefaultIstioClient;
import io.fabric8.istio.api.networking.v1alpha3.VirtualService;
import io.fabric8.istio.api.networking.v1alpha3.VirtualServiceBuilder;
import io.fabric8.istio.api.networking.v1alpha3.Destination;
import io.fabric8.istio.api.networking.v1alpha3.HTTPRoute;
import io.fabric8.istio.api.networking.v1alpha3.HTTPRouteDestination;
import io.fabric8.istio.api.networking.v1alpha3.HTTPMirror;
import java.util.*;
import java.util.concurrent.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class TrafficMirroringService {
/**
* Traffic Mirroring Configuration
*/
public static class MirrorConfig {
private String name;
private String sourceService;
private String mirrorService;
private double mirrorPercentage;
private Map<String, String> matchConditions;
private Map<String, String> headers;
private boolean preserveOriginal;
private int timeoutMs;
private int retryCount;
public MirrorConfig(String name, String sourceService, String mirrorService) {
this.name = name;
this.sourceService = sourceService;
this.mirrorService = mirrorService;
this.mirrorPercentage = 100.0; // Default: mirror all traffic
this.matchConditions = new HashMap<>();
this.headers = new HashMap<>();
this.preserveOriginal = true;
this.timeoutMs = 5000;
this.retryCount = 0;
}
// Builder methods
public MirrorConfig withPercentage(double percentage) {
this.mirrorPercentage = Math.max(0, Math.min(100, percentage));
return this;
}
public MirrorConfig withMatchCondition(String header, String value) {
this.matchConditions.put(header, value);
return this;
}
public MirrorConfig withHeader(String name, String value) {
this.headers.put(name, value);
return this;
}
public MirrorConfig withTimeout(int timeoutMs) {
this.timeoutMs = timeoutMs;
return this;
}
public MirrorConfig withRetries(int retryCount) {
this.retryCount = retryCount;
return this;
}
public MirrorConfig preserveOriginal(boolean preserve) {
this.preserveOriginal = preserve;
return this;
}
// Getters
public String getName() { return name; }
public String getSourceService() { return sourceService; }
public String getMirrorService() { return mirrorService; }
public double getMirrorPercentage() { return mirrorPercentage; }
public Map<String, String> getMatchConditions() { return matchConditions; }
public Map<String, String> getHeaders() { return headers; }
public boolean isPreserveOriginal() { return preserveOriginal; }
public int getTimeoutMs() { return timeoutMs; }
public int getRetryCount() { return retryCount; }
}
/**
* Istio Traffic Mirroring Manager
*/
public static class IstioMirroringManager {
private final IstioClient istioClient;
private final String namespace;
public IstioMirroringManager(IstioClient istioClient, String namespace) {
this.istioClient = istioClient;
this.namespace = namespace;
}
/**
* Configure traffic mirroring using Istio VirtualService
*/
public void configureMirroring(MirrorConfig config) {
try {
System.out.println("🎯 Configuring Istio traffic mirroring: " + config.getName());
VirtualService virtualService = createVirtualService(config);
// Apply or update VirtualService
VirtualService existing = istioClient.v1alpha3().virtualServices()
.inNamespace(namespace)
.withName(config.getSourceService() + "-mirror")
.get();
if (existing != null) {
istioClient.v1alpha3().virtualServices()
.inNamespace(namespace)
.withName(config.getSourceService() + "-mirror")
.patch(virtualService);
System.out.println("✅ Updated existing VirtualService for mirroring");
} else {
istioClient.v1alpha3().virtualServices()
.inNamespace(namespace)
.create(virtualService);
System.out.println("✅ Created new VirtualService for mirroring");
}
System.out.println("📊 Mirroring " + config.getMirrorPercentage() +
"% of traffic from " + config.getSourceService() +
" to " + config.getMirrorService());
} catch (Exception e) {
System.err.println("❌ Failed to configure Istio mirroring: " + e.getMessage());
throw new RuntimeException("Istio mirroring configuration failed", e);
}
}
private VirtualService createVirtualService(MirrorConfig config) {
return new VirtualServiceBuilder()
.withNewMetadata()
.withName(config.getSourceService() + "-mirror")
.withNamespace(namespace)
.withLabels(createLabels(config))
.endMetadata()
.withNewSpec()
.withHosts(Arrays.asList(config.getSourceService()))
.withHttp(createHttpRoutes(config))
.endSpec()
.build();
}
private List<HTTPRoute> createHttpRoutes(MirrorConfig config) {
HTTPRoute route = new HTTPRoute();
// Match conditions
if (!config.getMatchConditions().isEmpty()) {
route.setMatch(createMatchConditions(config.getMatchConditions()));
}
// Route to original destination
List<HTTPRouteDestination> routeDestinations = new ArrayList<>();
HTTPRouteDestination originalDest = new HTTPRouteDestination();
originalDest.setDestination(createDestination(config.getSourceService()));
if (config.getMirrorPercentage() < 100) {
originalDest.setWeight((int) (100 - config.getMirrorPercentage()));
}
routeDestinations.add(originalDest);
// Mirror configuration
HTTPMirror mirror = new HTTPMirror();
mirror.setDestination(createDestination(config.getMirrorService()));
if (!config.getHeaders().isEmpty()) {
mirror.setHeaders(createHeaders(config.getHeaders()));
}
route.setMirror(mirror);
// If not 100% mirroring, add mirror service as additional destination
if (config.getMirrorPercentage() > 0 && config.getMirrorPercentage() < 100) {
HTTPRouteDestination mirrorDest = new HTTPRouteDestination();
mirrorDest.setDestination(createDestination(config.getMirrorService()));
mirrorDest.setWeight((int) config.getMirrorPercentage());
routeDestinations.add(mirrorDest);
}
route.setRoute(routeDestinations);
// Timeout and retry configuration
if (config.getTimeoutMs() > 0) {
route.setTimeout(String.valueOf(config.getTimeoutMs()) + "ms");
}
if (config.getRetryCount() > 0) {
route.setRetries(createRetryPolicy(config.getRetryCount()));
}
return Arrays.asList(route);
}
private List<io.fabric8.istio.api.networking.v1alpha3.HTTPMatchRequest> createMatchConditions(
Map<String, String> conditions) {
List<io.fabric8.istio.api.networking.v1alpha3.HTTPMatchRequest> matches = new ArrayList<>();
for (Map.Entry<String, String> entry : conditions.entrySet()) {
io.fabric8.istio.api.networking.v1alpha3.HTTPMatchRequest match =
new io.fabric8.istio.api.networking.v1alpha3.HTTPMatchRequest();
Map<String, io.fabric8.istio.api.networking.v1alpha3.StringMatch> headers = new HashMap<>();
headers.put(entry.getKey(),
new io.fabric8.istio.api.networking.v1alpha3.StringMatch.Builder()
.withExact(entry.getValue())
.build());
match.setHeaders(headers);
matches.add(match);
}
return matches;
}
private Destination createDestination(String serviceName) {
return new Destination.Builder()
.withHost(serviceName)
.withSubset("v1") // Default subset
.build();
}
private io.fabric8.istio.api.networking.v1alpha3.Headers createHeaders(Map<String, String> headers) {
io.fabric8.istio.api.networking.v1alpha3.Headers headerConfig =
new io.fabric8.istio.api.networking.v1alpha3.Headers();
// Add request headers
Map<String, String> requestHeaders = new HashMap<>();
headers.forEach((key, value) -> requestHeaders.put(key, value));
headerConfig.setRequest(requestHeaders);
return headerConfig;
}
private io.fabric8.istio.api.networking.v1alpha3.HTTPRetry createRetryPolicy(int retries) {
return new io.fabric8.istio.api.networking.v1alpha3.HTTPRetry.Builder()
.withAttempts(retries)
.withPerTryTimeout("2s")
.withRetryOn("5xx,gateway-error,connect-failure")
.build();
}
private Map<String, String> createLabels(MirrorConfig config) {
Map<String, String> labels = new HashMap<>();
labels.put("app", config.getSourceService());
labels.put("mirroring-enabled", "true");
labels.put("mirror-target", config.getMirrorService());
labels.put("managed-by", "traffic-mirroring-service");
return labels;
}
/**
* Remove mirroring configuration
*/
public void removeMirroring(String sourceService) {
try {
istioClient.v1alpha3().virtualServices()
.inNamespace(namespace)
.withName(sourceService + "-mirror")
.delete();
System.out.println("✅ Removed mirroring configuration for: " + sourceService);
} catch (Exception e) {
System.err.println("❌ Failed to remove mirroring: " + e.getMessage());
}
}
/**
* Get mirroring status
*/
public MirrorStatus getMirrorStatus(String sourceService) {
try {
VirtualService vs = istioClient.v1alpha3().virtualServices()
.inNamespace(namespace)
.withName(sourceService + "-mirror")
.get();
if (vs == null) {
return new MirrorStatus(sourceService, MirrorStatus.Status.NOT_CONFIGURED);
}
return new MirrorStatus(
sourceService,
MirrorStatus.Status.ACTIVE,
extractMirrorPercentage(vs),
extractMirrorTarget(vs)
);
} catch (Exception e) {
return new MirrorStatus(sourceService, MirrorStatus.Status.ERROR, e.getMessage());
}
}
private double extractMirrorPercentage(VirtualService vs) {
// Extract mirror percentage from VirtualService spec
// Implementation would parse the HTTPRoute destinations
return 100.0; // Simplified
}
private String extractMirrorTarget(VirtualService vs) {
// Extract mirror target from VirtualService spec
// Implementation would parse the mirror destination
return "unknown"; // Simplified
}
}
/**
* Custom Traffic Mirroring Proxy
*/
public static class CustomMirroringProxy {
private final HttpClient httpClient;
private final ExecutorService executor;
private final Map<String, MirrorConfig> activeConfigs;
private final MirroringMetrics metrics;
public CustomMirroringProxy() {
this.httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.executor(Executors.newVirtualThreadPerTaskExecutor())
.build();
this.executor = Executors.newCachedThreadPool();
this.activeConfigs = new ConcurrentHashMap<>();
this.metrics = new MirroringMetrics();
}
/**
* Register mirroring configuration
*/
public void registerMirroring(MirrorConfig config) {
activeConfigs.put(config.getSourceService(), config);
System.out.println("✅ Registered mirroring: " + config.getName());
}
/**
* Process incoming request and mirror if configured
*/
public CompletableFuture<HttpResponse<String>> processRequest(
String sourceService,
HttpRequest originalRequest,
String requestBody) {
MirrorConfig config = activeConfigs.get(sourceService);
if (config == null || !shouldMirror(config)) {
// No mirroring configured or random check failed
return httpClient.sendAsync(originalRequest, HttpResponse.BodyHandlers.ofString());
}
CompletableFuture<HttpResponse<String>> originalFuture =
httpClient.sendAsync(originalRequest, HttpResponse.BodyHandlers.ofString());
// Mirror the request asynchronously
CompletableFuture<Void> mirrorFuture = mirrorRequest(config, originalRequest, requestBody);
// Return original response regardless of mirroring result
return originalFuture.whenComplete((response, throwable) -> {
metrics.recordMirroringAttempt(config.getSourceService(), throwable == null);
});
}
private boolean shouldMirror(MirrorConfig config) {
if (config.getMirrorPercentage() >= 100) {
return true;
}
double random = Math.random() * 100;
return random <= config.getMirrorPercentage();
}
private CompletableFuture<Void> mirrorRequest(
MirrorConfig config,
HttpRequest originalRequest,
String requestBody) {
return CompletableFuture.runAsync(() -> {
try {
// Create mirror request
HttpRequest.Builder mirrorBuilder = HttpRequest.newBuilder()
.uri(createMirrorUri(config, originalRequest.uri()))
.method(originalRequest.method(),
HttpRequest.BodyPublishers.ofString(requestBody));
// Copy headers
originalRequest.headers().map().forEach((key, values) -> {
if (!shouldSkipHeader(key)) {
values.forEach(value -> mirrorBuilder.header(key, value));
}
});
// Add mirror-specific headers
config.getHeaders().forEach(mirrorBuilder::header);
mirrorBuilder.header("X-Traffic-Mirrored", "true");
mirrorBuilder.header("X-Mirror-Source", config.getSourceService());
HttpRequest mirrorRequest = mirrorBuilder
.timeout(Duration.ofMillis(config.getTimeoutMs()))
.build();
// Execute mirror request with retries
executeWithRetries(mirrorRequest, config.getRetryCount())
.thenAccept(response -> {
metrics.recordMirrorResponse(
config.getSourceService(),
response.statusCode()
);
System.out.println("🪞 Mirrored request to " + config.getMirrorService() +
" - Status: " + response.statusCode());
})
.exceptionally(throwable -> {
metrics.recordMirrorError(config.getSourceService(), throwable.getMessage());
System.err.println("❌ Mirror request failed: " + throwable.getMessage());
return null;
});
} catch (Exception e) {
metrics.recordMirrorError(config.getSourceService(), e.getMessage());
System.err.println("❌ Mirror request setup failed: " + e.getMessage());
}
}, executor);
}
private URI createMirrorUri(MirrorConfig config, URI originalUri) {
// Transform URI to point to mirror service
String mirrorHost = config.getMirrorService().contains("://") ?
config.getMirrorService() : "http://" + config.getMirrorService();
return URI.create(mirrorHost + originalUri.getPath() +
(originalUri.getQuery() != null ? "?" + originalUri.getQuery() : ""));
}
private boolean shouldSkipHeader(String headerName) {
// Skip headers that shouldn't be mirrored
List<String> skipHeaders = Arrays.asList(
"authorization", "cookie", "x-trace-id", "x-mirrored"
);
return skipHeaders.contains(headerName.toLowerCase());
}
private CompletableFuture<HttpResponse<String>> executeWithRetries(
HttpRequest request, int maxRetries) {
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.exceptionallyCompose(throwable -> {
if (maxRetries > 0) {
System.out.println("🔄 Retrying mirror request (" + maxRetries + " retries left)");
return executeWithRetries(request, maxRetries - 1);
}
throw new CompletionException(throwable);
});
}
/**
* Get mirroring metrics
*/
public MirroringMetrics getMetrics() {
return metrics;
}
/**
* Stop the proxy
*/
public void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
/**
* Mirroring Metrics Collector
*/
public static class MirroringMetrics {
private final Map<String, ServiceMetrics> serviceMetrics;
private final Object lock = new Object();
public MirroringMetrics() {
this.serviceMetrics = new ConcurrentHashMap<>();
}
public void recordMirroringAttempt(String serviceName, boolean success) {
synchronized (lock) {
ServiceMetrics metrics = serviceMetrics.computeIfAbsent(
serviceName, k -> new ServiceMetrics());
metrics.totalRequests++;
if (success) {
metrics.successfulMirrors++;
} else {
metrics.failedMirrors++;
}
}
}
public void recordMirrorResponse(String serviceName, int statusCode) {
synchronized (lock) {
ServiceMetrics metrics = serviceMetrics.computeIfAbsent(
serviceName, k -> new ServiceMetrics());
metrics.mirrorResponses.merge(statusCode, 1, Integer::sum);
}
}
public void recordMirrorError(String serviceName, String error) {
synchronized (lock) {
ServiceMetrics metrics = serviceMetrics.computeIfAbsent(
serviceName, k -> new ServiceMetrics());
metrics.errors.add(error);
}
}
public Map<String, ServiceMetrics> getServiceMetrics() {
return new HashMap<>(serviceMetrics);
}
public ServiceMetrics getMetricsForService(String serviceName) {
return serviceMetrics.get(serviceName);
}
public static class ServiceMetrics {
public long totalRequests;
public long successfulMirrors;
public long failedMirrors;
public Map<Integer, Integer> mirrorResponses;
public List<String> errors;
public ServiceMetrics() {
this.mirrorResponses = new HashMap<>();
this.errors = new ArrayList<>();
}
public double getSuccessRate() {
return totalRequests > 0 ? (double) successfulMirrors / totalRequests * 100 : 0;
}
public double getMirrorRate() {
return totalRequests > 0 ?
(double) (successfulMirrors + failedMirrors) / totalRequests * 100 : 0;
}
}
}
/**
* Mirroring Status
*/
public static class MirrorStatus {
public enum Status {
NOT_CONFIGURED, ACTIVE, PAUSED, ERROR
}
private String sourceService;
private Status status;
private double mirrorPercentage;
private String mirrorTarget;
private String message;
private Date lastUpdated;
public MirrorStatus(String sourceService, Status status) {
this.sourceService = sourceService;
this.status = status;
this.lastUpdated = new Date();
}
public MirrorStatus(String sourceService, Status status, String message) {
this(sourceService, status);
this.message = message;
}
public MirrorStatus(String sourceService, Status status, double mirrorPercentage, String mirrorTarget) {
this(sourceService, status);
this.mirrorPercentage = mirrorPercentage;
this.mirrorTarget = mirrorTarget;
}
// Getters
public String getSourceService() { return sourceService; }
public Status getStatus() { return status; }
public double getMirrorPercentage() { return mirrorPercentage; }
public String getMirrorTarget() { return mirrorTarget; }
public String getMessage() { return message; }
public Date getLastUpdated() { return lastUpdated; }
}
/**
Spring Boot Integration
*/
@RestController
@RequestMapping("/api/mirroring")
public static class MirroringController {
private final IstioMirroringManager istioManager;
private final CustomMirroringProxy customProxy;
private final MirroringMetrics metrics;
public MirroringController(IstioMirroringManager istioManager,
CustomMirroringProxy customProxy) {
this.istioManager = istioManager;
this.customProxy = customProxy;
this.metrics = customProxy.getMetrics();
}
@PostMapping("/istio")
public ResponseEntity<String> configureIstioMirroring(@RequestBody MirrorConfig config) {
try {
istioManager.configureMirroring(config);
return ResponseEntity.ok("Istio mirroring configured successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to configure Istio mirroring: " + e.getMessage());
}
}
@PostMapping("/custom")
public ResponseEntity<String> configureCustomMirroring(@RequestBody MirrorConfig config) {
try {
customProxy.registerMirroring(config);
return ResponseEntity.ok("Custom mirroring configured successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to configure custom mirroring: " + e.getMessage());
}
}
@DeleteMapping("/{serviceName}")
public ResponseEntity<String> removeMirroring(@PathVariable String serviceName) {
try {
istioManager.removeMirroring(serviceName);
return ResponseEntity.ok("Mirroring removed successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to remove mirroring: " + e.getMessage());
}
}
@GetMapping("/status/{serviceName}")
public ResponseEntity<MirrorStatus> getMirrorStatus(@PathVariable String serviceName) {
try {
MirrorStatus status = istioManager.getMirrorStatus(serviceName);
return ResponseEntity.ok(status);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/metrics")
public ResponseEntity<Map<String, MirroringMetrics.ServiceMetrics>> getMetrics() {
try {
Map<String, MirroringMetrics.ServiceMetrics> metrics =
this.metrics.getServiceMetrics();
return ResponseEntity.ok(metrics);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@PostMapping("/proxy/{serviceName}")
public CompletableFuture<ResponseEntity<String>> proxyRequest(
@PathVariable String serviceName,
@RequestBody ProxyRequest request) {
try {
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(request.getUrl()))
.method(request.getMethod(),
HttpRequest.BodyPublishers.ofString(request.getBody()))
.headers(convertHeaders(request.getHeaders()))
.build();
return customProxy.processRequest(serviceName, httpRequest, request.getBody())
.thenApply(response -> {
Map<String, String> responseHeaders = new HashMap<>();
response.headers().map().forEach((key, values) -> {
if (!values.isEmpty()) {
responseHeaders.put(key, values.get(0));
}
});
return ResponseEntity.status(response.statusCode())
.headers(createResponseHeaders(responseHeaders))
.body(response.body());
});
} catch (Exception e) {
return CompletableFuture.completedFuture(
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Proxy request failed: " + e.getMessage())
);
}
}
private String[] convertHeaders(Map<String, String> headers) {
return headers.entrySet().stream()
.flatMap(entry -> Stream.of(entry.getKey(), entry.getValue()))
.toArray(String[]::new);
}
private HttpHeaders createResponseHeaders(Map<String, String> headers) {
HttpHeaders httpHeaders = new HttpHeaders();
headers.forEach(httpHeaders::set);
return httpHeaders;
}
}
/**
* Request/Response DTOs
*/
public static class ProxyRequest {
private String url;
private String method;
private String body;
private Map<String, String> headers;
public ProxyRequest() {
this.headers = new HashMap<>();
this.method = "GET";
}
// Getters and setters
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public String getBody() { return body; }
public void setBody(String body) { this.body = body; }
public Map<String, String> getHeaders() { return headers; }
public void setHeaders(Map<String, String> headers) { this.headers = headers; }
}
/**
* Demo and Usage Examples
*/
public static void main(String[] args) {
System.out.println("🪞 Traffic Mirroring Service Demo");
System.out.println("=================================\n");
// Demo 1: Istio-based Mirroring
System.out.println("1. 🚀 Istio-based Traffic Mirroring");
try {
IstioClient istioClient = new DefaultIstioClient();
IstioMirroringManager istioManager = new IstioMirroringManager(istioClient, "default");
MirrorConfig istioConfig = new MirrorConfig(
"production-mirror",
"payment-service",
"payment-service-staging"
)
.withPercentage(50.0)
.withMatchCondition("user-agent", "mobile-app")
.withHeader("X-Mirror-Source", "production")
.withTimeout(3000)
.withRetries(2);
istioManager.configureMirroring(istioConfig);
// Check status
MirrorStatus status = istioManager.getMirrorStatus("payment-service");
System.out.println("📊 Mirror Status: " + status.getStatus() +
", Percentage: " + status.getMirrorPercentage() + "%");
} catch (Exception e) {
System.out.println("⚠️ Istio demo skipped: " + e.getMessage());
}
// Demo 2: Custom Proxy Mirroring
System.out.println("\n2. ⚡ Custom Proxy Traffic Mirroring");
CustomMirroringProxy customProxy = new CustomMirroringProxy();
MirrorConfig customConfig = new MirrorConfig(
"canary-test",
"user-service",
"http://user-service-canary:8080"
)
.withPercentage(25.0)
.withHeader("X-Environment", "canary")
.withTimeout(5000)
.preserveOriginal(true);
customProxy.registerMirroring(customConfig);
// Simulate some traffic
System.out.println("\n3. 📨 Simulating Traffic");
simulateTraffic(customProxy, "user-service");
// Show metrics
System.out.println("\n4. 📊 Mirroring Metrics");
MirroringMetrics metrics = customProxy.getMetrics();
Map<String, MirroringMetrics.ServiceMetrics> serviceMetrics = metrics.getServiceMetrics();
serviceMetrics.forEach((service, serviceMetric) -> {
System.out.println("Service: " + service);
System.out.println(" Total Requests: " + serviceMetric.totalRequests);
System.out.println(" Mirror Rate: " + String.format("%.2f", serviceMetric.getMirrorRate()) + "%");
System.out.println(" Success Rate: " + String.format("%.2f", serviceMetric.getSuccessRate()) + "%");
});
// Cleanup
customProxy.shutdown();
System.out.println("\n✅ Traffic Mirroring Demo Completed");
}
private static void simulateTraffic(CustomMirroringProxy proxy, String serviceName) {
ExecutorService executor = Executors.newFixedThreadPool(10);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
final int requestId = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://user-service/api/users/" + requestId))
.GET()
.header("User-Agent", "test-client")
.header("Content-Type", "application/json")
.build();
proxy.processRequest(serviceName, request, "")
.thenAccept(response -> {
if (requestId % 20 == 0) {
System.out.println(" Processed request " + requestId +
" -> Status: " + response.statusCode());
}
});
} catch (Exception e) {
System.err.println("❌ Request " + requestId + " failed: " + e.getMessage());
}
}, executor);
futures.add(future);
}
// Wait for all requests to complete
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
executor.shutdown();
}
}
Maven Dependencies
<!-- pom.xml --> <dependencies> <!-- Istio Client --> <dependency> <groupId>io.fabric8</groupId> <artifactId>istio-client</artifactId> <version>6.8.0</version> </dependency> <!-- Kubernetes Client --> <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-client</artifactId> <version>6.8.0</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.7.0</version> </dependency> <!-- HTTP Client --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.20</version> </dependency> </dependencies>
Istio VirtualService YAML Example
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: payment-service-mirror namespace: default spec: hosts: - payment-service http: - match: - headers: user-agent: exact: mobile-app route: - destination: host: payment-service subset: v1 weight: 50 - destination: host: payment-service-staging subset: v1 weight: 50 mirror: host: payment-service-staging subset: v1 mirror_percentage: value: 50.0 timeout: 3s retries: attempts: 2 perTryTimeout: 2s retryOn: 5xx,gateway-error,connect-failure
Application Configuration
# application.yml
traffic-mirroring:
enabled: true
istio:
enabled: ${ISTIO_ENABLED:true}
namespace: ${NAMESPACE:default}
custom-proxy:
enabled: ${CUSTOM_PROXY_ENABLED:true}
timeout-ms: 5000
max-retries: 3
logging:
level:
com.example.mirroring: DEBUG
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
metrics:
enabled: true
health:
enabled: true
This Traffic Mirroring implementation provides:
- Istio Integration for native service mesh mirroring
- Custom Proxy for environments without service mesh
- Flexible Configuration with percentage-based mirroring
- Request Matching based on headers and conditions
- Comprehensive Metrics for monitoring mirroring effectiveness
- Error Handling with retry mechanisms
- Spring Boot Integration for easy deployment
- Real-time Monitoring with detailed metrics collection
The solution enables safe testing of new service versions, performance testing, and debugging in production-like environments without impacting real users.