0
# Service Management
1
2
The ServiceClient provides comprehensive user service interactions including endpoint discovery, availability checking, routing configuration, and direct service method calls. Services are HTTP-based programs that expose RESTful endpoints.
3
4
## ServiceClient
5
6
```java { .api }
7
public class ServiceClient {
8
// Constructors
9
public ServiceClient(ClientConfig config);
10
public ServiceClient(ClientConfig config, RESTClient restClient);
11
12
// Service information methods
13
public ServiceSpecification get(ProgramId service);
14
public List<ServiceHttpEndpoint> getEndpoints(ServiceId service);
15
public void checkAvailability(ServiceId service);
16
public URL getVersionedServiceURL(ServiceId service);
17
public URL getServiceURL(ServiceId service);
18
19
// Route configuration methods
20
public Map<String, Integer> getRouteConfig(ServiceId serviceId);
21
public void storeRouteConfig(ServiceId serviceId, Map<String, Integer> routeConfig);
22
public void deleteRouteConfig(ServiceId serviceId);
23
24
// Service call methods
25
public HttpResponse callServiceMethod(ServiceId serviceId, String methodPath);
26
}
27
```
28
29
## Service Types and Information
30
31
```java { .api }
32
public class ServiceSpecification {
33
public String getName();
34
public String getClassName();
35
public String getDescription();
36
public Map<String, HttpServiceHandlerSpecification> getHandlers();
37
public ResourceSpecification getResources();
38
}
39
40
public class ServiceHttpEndpoint {
41
public String getMethod();
42
public String getPath();
43
}
44
45
public class ServiceId {
46
public static ServiceId of(ApplicationId application, String service);
47
public ApplicationId getApplication();
48
public String getService();
49
}
50
51
public class HttpResponse {
52
public int getResponseCode();
53
public Map<String, List<String>> getHeaders();
54
public String getResponseMessage();
55
public byte[] getResponseBody();
56
public String getResponseBodyAsString();
57
}
58
```
59
60
## Service Discovery and Information
61
62
### Service Specification
63
64
```java
65
// Get service specification
66
ApplicationId appId = ApplicationId.of(namespace, "web-analytics", "1.0.0");
67
ProgramId serviceProgram = ProgramId.of(appId, ProgramType.SERVICE, "analytics-service");
68
ServiceSpecification spec = serviceClient.get(serviceProgram);
69
70
System.out.println("Service: " + spec.getName());
71
System.out.println("Class: " + spec.getClassName());
72
System.out.println("Description: " + spec.getDescription());
73
74
// Get handlers information
75
Map<String, HttpServiceHandlerSpecification> handlers = spec.getHandlers();
76
for (Map.Entry<String, HttpServiceHandlerSpecification> entry : handlers.entrySet()) {
77
System.out.println("Handler: " + entry.getKey());
78
HttpServiceHandlerSpecification handler = entry.getValue();
79
System.out.println(" Class: " + handler.getClassName());
80
System.out.println(" Endpoints: " + handler.getEndpoints().size());
81
}
82
83
// Get resource requirements
84
ResourceSpecification resources = spec.getResources();
85
System.out.println("Memory: " + resources.getMemoryMB() + " MB");
86
System.out.println("VCores: " + resources.getVirtualCores());
87
```
88
89
### Endpoint Discovery
90
91
```java
92
// Get all endpoints for a service
93
ServiceId serviceId = ServiceId.of(appId, "analytics-service");
94
List<ServiceHttpEndpoint> endpoints = serviceClient.getEndpoints(serviceId);
95
96
System.out.println("Service endpoints:");
97
for (ServiceHttpEndpoint endpoint : endpoints) {
98
System.out.println(" " + endpoint.getMethod() + " " + endpoint.getPath());
99
}
100
101
// Common endpoint patterns
102
for (ServiceHttpEndpoint endpoint : endpoints) {
103
String method = endpoint.getMethod();
104
String path = endpoint.getPath();
105
106
if ("GET".equals(method) && path.contains("/status")) {
107
System.out.println("Health check endpoint: " + method + " " + path);
108
} else if ("POST".equals(method)) {
109
System.out.println("Data submission endpoint: " + method + " " + path);
110
} else if ("GET".equals(method) && path.contains("/metrics")) {
111
System.out.println("Metrics endpoint: " + method + " " + path);
112
}
113
}
114
```
115
116
### Service Availability
117
118
```java
119
// Check if service is available
120
try {
121
serviceClient.checkAvailability(serviceId);
122
System.out.println("Service is available: " + serviceId.getService());
123
} catch (ServiceUnavailableException e) {
124
System.err.println("Service unavailable: " + serviceId.getService());
125
} catch (ServiceNotFoundException e) {
126
System.err.println("Service not found: " + serviceId.getService());
127
}
128
129
// Wait for service availability
130
public void waitForServiceAvailability(ServiceId serviceId, int maxAttempts, long delayMs) {
131
for (int i = 0; i < maxAttempts; i++) {
132
try {
133
serviceClient.checkAvailability(serviceId);
134
System.out.println("Service is now available: " + serviceId.getService());
135
return;
136
} catch (ServiceUnavailableException e) {
137
if (i == maxAttempts - 1) {
138
throw new RuntimeException("Service did not become available after " + maxAttempts + " attempts");
139
}
140
System.out.println("Service not available, attempt " + (i + 1) + "/" + maxAttempts);
141
try {
142
Thread.sleep(delayMs);
143
} catch (InterruptedException ie) {
144
Thread.currentThread().interrupt();
145
throw new RuntimeException("Interrupted while waiting for service", ie);
146
}
147
}
148
}
149
}
150
```
151
152
## Service URL Management
153
154
### URL Generation
155
156
```java
157
// Get service URLs
158
URL versionedUrl = serviceClient.getVersionedServiceURL(serviceId);
159
URL unversionedUrl = serviceClient.getServiceURL(serviceId);
160
161
System.out.println("Versioned URL: " + versionedUrl);
162
System.out.println("Unversioned URL: " + unversionedUrl);
163
164
// Build endpoint URLs
165
String baseUrl = unversionedUrl.toString();
166
for (ServiceHttpEndpoint endpoint : endpoints) {
167
String endpointUrl = baseUrl + endpoint.getPath();
168
System.out.println(endpoint.getMethod() + " -> " + endpointUrl);
169
}
170
```
171
172
### Dynamic URL Construction
173
174
```java
175
// Build URLs for different environments
176
public class ServiceURLBuilder {
177
private final ServiceClient serviceClient;
178
179
public ServiceURLBuilder(ServiceClient serviceClient) {
180
this.serviceClient = serviceClient;
181
}
182
183
public String buildEndpointURL(ServiceId serviceId, String path, boolean versioned) {
184
try {
185
URL baseUrl = versioned ?
186
serviceClient.getVersionedServiceURL(serviceId) :
187
serviceClient.getServiceURL(serviceId);
188
189
return baseUrl.toString() + (path.startsWith("/") ? path : "/" + path);
190
} catch (Exception e) {
191
throw new RuntimeException("Error building service URL", e);
192
}
193
}
194
195
public String buildParameterizedURL(ServiceId serviceId, String pathTemplate, Object... params) {
196
String path = String.format(pathTemplate, params);
197
return buildEndpointURL(serviceId, path, false);
198
}
199
}
200
201
// Usage
202
ServiceURLBuilder urlBuilder = new ServiceURLBuilder(serviceClient);
203
String userUrl = urlBuilder.buildParameterizedURL(serviceId, "/users/%s", "user123");
204
String metricsUrl = urlBuilder.buildEndpointURL(serviceId, "/metrics", false);
205
```
206
207
## Route Configuration
208
209
### Traffic Routing
210
211
```java
212
// Get current route configuration
213
Map<String, Integer> currentRoutes = serviceClient.getRouteConfig(serviceId);
214
System.out.println("Current routes: " + currentRoutes);
215
216
// Configure traffic routing between service versions
217
Map<String, Integer> routeConfig = Map.of(
218
"v1.0.0", 80, // 80% traffic to v1.0.0
219
"v1.1.0", 20 // 20% traffic to v1.1.0 (canary deployment)
220
);
221
222
serviceClient.storeRouteConfig(serviceId, routeConfig);
223
System.out.println("Updated route configuration for gradual rollout");
224
225
// Blue-green deployment routing
226
Map<String, Integer> blueGreenRoutes = Map.of(
227
"blue", 0, // Old version (no traffic)
228
"green", 100 // New version (all traffic)
229
);
230
serviceClient.storeRouteConfig(serviceId, blueGreenRoutes);
231
```
232
233
### Advanced Routing Strategies
234
235
```java
236
// Gradual traffic migration
237
public void performGradualMigration(ServiceId serviceId, String oldVersion, String newVersion) {
238
int[] trafficSplits = {90, 80, 60, 40, 20, 0}; // Gradual reduction of old version traffic
239
240
for (int oldTraffic : trafficSplits) {
241
int newTraffic = 100 - oldTraffic;
242
243
Map<String, Integer> routes = Map.of(
244
oldVersion, oldTraffic,
245
newVersion, newTraffic
246
);
247
248
try {
249
serviceClient.storeRouteConfig(serviceId, routes);
250
System.out.println("Route config: " + oldVersion + "=" + oldTraffic + "%, " +
251
newVersion + "=" + newTraffic + "%");
252
253
// Wait and monitor before next step
254
Thread.sleep(300000); // 5 minutes
255
256
// Check service health before continuing
257
serviceClient.checkAvailability(serviceId);
258
259
} catch (Exception e) {
260
System.err.println("Error during migration step: " + e.getMessage());
261
// Rollback to previous configuration
262
Map<String, Integer> rollbackRoutes = Map.of(oldVersion, 100);
263
serviceClient.storeRouteConfig(serviceId, rollbackRoutes);
264
throw new RuntimeException("Migration failed, rolled back", e);
265
}
266
}
267
268
System.out.println("Migration completed successfully");
269
}
270
271
// Remove route configuration (default routing)
272
serviceClient.deleteRouteConfig(serviceId);
273
System.out.println("Route configuration removed, using default routing");
274
```
275
276
## Service Method Calls
277
278
### Direct Service Calls
279
280
```java
281
// Make direct calls to service methods
282
try {
283
// GET request
284
HttpResponse response = serviceClient.callServiceMethod(serviceId, "/status");
285
System.out.println("Status response: " + response.getResponseCode());
286
System.out.println("Body: " + response.getResponseBodyAsString());
287
288
// Check response headers
289
Map<String, List<String>> headers = response.getHeaders();
290
if (headers.containsKey("Content-Type")) {
291
System.out.println("Content-Type: " + headers.get("Content-Type").get(0));
292
}
293
294
} catch (ServiceUnavailableException e) {
295
System.err.println("Service call failed - service unavailable");
296
} catch (IOException e) {
297
System.err.println("Network error during service call: " + e.getMessage());
298
}
299
```
300
301
### Advanced HTTP Client Usage
302
303
```java
304
// Service interaction with custom HTTP client
305
public class ServiceInteractor {
306
private final ServiceClient serviceClient;
307
private final ServiceId serviceId;
308
309
public ServiceInteractor(ServiceClient serviceClient, ServiceId serviceId) {
310
this.serviceClient = serviceClient;
311
this.serviceId = serviceId;
312
}
313
314
public String getServiceStatus() {
315
try {
316
HttpResponse response = serviceClient.callServiceMethod(serviceId, "/status");
317
if (response.getResponseCode() == 200) {
318
return response.getResponseBodyAsString();
319
} else {
320
throw new RuntimeException("Service status check failed: " + response.getResponseCode());
321
}
322
} catch (Exception e) {
323
throw new RuntimeException("Error checking service status", e);
324
}
325
}
326
327
public Map<String, Object> getServiceMetrics() {
328
try {
329
HttpResponse response = serviceClient.callServiceMethod(serviceId, "/metrics");
330
if (response.getResponseCode() == 200) {
331
String jsonResponse = response.getResponseBodyAsString();
332
// Parse JSON response (using your preferred JSON library)
333
return parseJsonResponse(jsonResponse);
334
} else {
335
throw new RuntimeException("Metrics retrieval failed: " + response.getResponseCode());
336
}
337
} catch (Exception e) {
338
throw new RuntimeException("Error retrieving service metrics", e);
339
}
340
}
341
342
public boolean isHealthy() {
343
try {
344
serviceClient.checkAvailability(serviceId);
345
HttpResponse healthResponse = serviceClient.callServiceMethod(serviceId, "/health");
346
return healthResponse.getResponseCode() == 200;
347
} catch (Exception e) {
348
return false;
349
}
350
}
351
352
private Map<String, Object> parseJsonResponse(String json) {
353
// Implement JSON parsing using your preferred library (Jackson, Gson, etc.)
354
// This is a placeholder implementation
355
return Map.of("status", "parsed");
356
}
357
}
358
```
359
360
## Service Health Monitoring
361
362
### Health Check Implementation
363
364
```java
365
// Comprehensive service health monitoring
366
public class ServiceHealthMonitor {
367
private final ServiceClient serviceClient;
368
private final ServiceId serviceId;
369
370
public ServiceHealthMonitor(ServiceClient serviceClient, ServiceId serviceId) {
371
this.serviceClient = serviceClient;
372
this.serviceId = serviceId;
373
}
374
375
public ServiceHealthStatus checkHealth() {
376
ServiceHealthStatus.Builder statusBuilder = ServiceHealthStatus.builder()
377
.serviceId(serviceId)
378
.timestamp(System.currentTimeMillis());
379
380
try {
381
// Check basic availability
382
serviceClient.checkAvailability(serviceId);
383
statusBuilder.available(true);
384
385
// Check endpoints
386
List<ServiceHttpEndpoint> endpoints = serviceClient.getEndpoints(serviceId);
387
statusBuilder.endpointCount(endpoints.size());
388
389
// Test health endpoint if available
390
for (ServiceHttpEndpoint endpoint : endpoints) {
391
if ("GET".equals(endpoint.getMethod()) &&
392
(endpoint.getPath().contains("/health") || endpoint.getPath().contains("/status"))) {
393
394
HttpResponse response = serviceClient.callServiceMethod(serviceId, endpoint.getPath());
395
statusBuilder.healthEndpointStatus(response.getResponseCode());
396
statusBuilder.healthEndpointResponse(response.getResponseBodyAsString());
397
break;
398
}
399
}
400
401
// Check route configuration
402
Map<String, Integer> routes = serviceClient.getRouteConfig(serviceId);
403
statusBuilder.routeConfiguration(routes);
404
405
statusBuilder.healthy(true);
406
407
} catch (ServiceNotFoundException e) {
408
statusBuilder.available(false).healthy(false).error("Service not found");
409
} catch (ServiceUnavailableException e) {
410
statusBuilder.available(false).healthy(false).error("Service unavailable");
411
} catch (Exception e) {
412
statusBuilder.available(false).healthy(false).error("Error: " + e.getMessage());
413
}
414
415
return statusBuilder.build();
416
}
417
418
public void monitorContinuously(long intervalMs, HealthStatusCallback callback) {
419
Thread monitorThread = new Thread(() -> {
420
while (!Thread.currentThread().isInterrupted()) {
421
try {
422
ServiceHealthStatus status = checkHealth();
423
callback.onHealthStatus(status);
424
Thread.sleep(intervalMs);
425
} catch (InterruptedException e) {
426
Thread.currentThread().interrupt();
427
break;
428
} catch (Exception e) {
429
callback.onError(e);
430
}
431
}
432
});
433
434
monitorThread.setDaemon(true);
435
monitorThread.start();
436
}
437
438
@FunctionalInterface
439
public interface HealthStatusCallback {
440
void onHealthStatus(ServiceHealthStatus status);
441
442
default void onError(Exception e) {
443
System.err.println("Health monitoring error: " + e.getMessage());
444
}
445
}
446
}
447
448
// Health status data class
449
public class ServiceHealthStatus {
450
private final ServiceId serviceId;
451
private final long timestamp;
452
private final boolean available;
453
private final boolean healthy;
454
private final int endpointCount;
455
private final Integer healthEndpointStatus;
456
private final String healthEndpointResponse;
457
private final Map<String, Integer> routeConfiguration;
458
private final String error;
459
460
// Constructor, getters, and builder implementation
461
public static Builder builder() {
462
return new Builder();
463
}
464
465
public static class Builder {
466
// Builder implementation
467
}
468
}
469
```
470
471
## Error Handling
472
473
Service management operations may throw these exceptions:
474
475
- **ServiceNotFoundException**: Service does not exist
476
- **ServiceUnavailableException**: Service is not currently available
477
- **ServiceNotRunningException**: Service exists but is not running
478
- **RouteConfigurationException**: Invalid route configuration
479
- **UnauthenticatedException**: Authentication required
480
- **UnauthorizedException**: Insufficient permissions
481
482
```java
483
try {
484
ServiceSpecification spec = serviceClient.get(serviceProgram);
485
System.out.println("Service specification retrieved");
486
} catch (ServiceNotFoundException e) {
487
System.err.println("Service not found: " + serviceId.getService());
488
} catch (UnauthorizedException e) {
489
System.err.println("No permission to access service: " + e.getMessage());
490
} catch (IOException e) {
491
System.err.println("Network error: " + e.getMessage());
492
}
493
```
494
495
## Best Practices
496
497
1. **Health Monitoring**: Implement continuous health monitoring for services
498
2. **Route Management**: Use route configuration for gradual deployments
499
3. **Error Handling**: Implement proper retry logic and circuit breakers
500
4. **URL Management**: Use versioned URLs for backward compatibility
501
5. **Performance**: Monitor service response times and throughput
502
6. **Security**: Ensure proper authentication and authorization for service calls
503
504
```java
505
// Good: Comprehensive service management with error handling and monitoring
506
public class ServiceManager {
507
private final ServiceClient serviceClient;
508
private final ServiceId serviceId;
509
private final ServiceHealthMonitor healthMonitor;
510
511
public ServiceManager(ServiceClient serviceClient, ServiceId serviceId) {
512
this.serviceClient = serviceClient;
513
this.serviceId = serviceId;
514
this.healthMonitor = new ServiceHealthMonitor(serviceClient, serviceId);
515
}
516
517
public void deployWithHealthCheck(Map<String, Integer> routeConfig) {
518
try {
519
// Store new route configuration
520
serviceClient.storeRouteConfig(serviceId, routeConfig);
521
522
// Wait for deployment to stabilize
523
Thread.sleep(30000); // 30 seconds
524
525
// Verify service health
526
ServiceHealthStatus status = healthMonitor.checkHealth();
527
if (!status.isHealthy()) {
528
// Rollback on failure
529
System.err.println("Service unhealthy after deployment, rolling back");
530
serviceClient.deleteRouteConfig(serviceId); // Use default routing
531
throw new RuntimeException("Deployment failed health check");
532
}
533
534
System.out.println("Deployment successful and service is healthy");
535
536
} catch (Exception e) {
537
System.err.println("Deployment failed: " + e.getMessage());
538
throw new RuntimeException("Service deployment failed", e);
539
}
540
}
541
542
public String callServiceSafely(String methodPath, int maxRetries) {
543
Exception lastException = null;
544
545
for (int i = 0; i < maxRetries; i++) {
546
try {
547
serviceClient.checkAvailability(serviceId);
548
HttpResponse response = serviceClient.callServiceMethod(serviceId, methodPath);
549
550
if (response.getResponseCode() == 200) {
551
return response.getResponseBodyAsString();
552
} else if (response.getResponseCode() >= 500) {
553
// Server error - retry
554
Thread.sleep(1000 * (i + 1)); // Exponential backoff
555
continue;
556
} else {
557
// Client error - don't retry
558
throw new RuntimeException("Service call failed: " + response.getResponseCode());
559
}
560
561
} catch (Exception e) {
562
lastException = e;
563
if (i < maxRetries - 1) {
564
System.out.println("Service call failed, retrying... (" + (i + 1) + "/" + maxRetries + ")");
565
try {
566
Thread.sleep(1000 * (i + 1));
567
} catch (InterruptedException ie) {
568
Thread.currentThread().interrupt();
569
throw new RuntimeException("Interrupted during retry", ie);
570
}
571
}
572
}
573
}
574
575
throw new RuntimeException("Service call failed after " + maxRetries + " attempts", lastException);
576
}
577
}
578
```