google-site-verification: google61fe8ba583a51912.html
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