Traffic Mirroring in Service Mesh with Java
/**
* 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:

  1. Istio Integration for native service mesh mirroring
  2. Custom Proxy for environments without service mesh
  3. Flexible Configuration with percentage-based mirroring
  4. Request Matching based on headers and conditions
  5. Comprehensive Metrics for monitoring mirroring effectiveness
  6. Error Handling with retry mechanisms
  7. Spring Boot Integration for easy deployment
  8. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper