Skip to content

Commit ab03056

Browse files
Addressing code review comments
1 parent eed8a3e commit ab03056

File tree

24 files changed

+1473
-849
lines changed

24 files changed

+1473
-849
lines changed

docs/src/main/asciidoc/cloud-map.adoc

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
[#spring-cloud-aws-cloudmap]
2+
== Spring Cloud AWS CloudMap
3+
This article talks about the new Spring Cloud AWS CloudMap module.
4+
5+
=== What is service discovery?
6+
7+
Service discovery is the mechanism through which a microservices can locate other microservices in the network. Service discovery is a critical part of microservices architecture as it enables the microservices to identify and communicate with each other.
8+
9+
There are two types of service discovery: Server-side and Client-side.
10+
11+
1. Server-side service discovery allows clients applications to find other services through a router (like API gateway or a load balancer).
12+
2. Client-side service discovery allows clients applications to find services by looking through or querying a service registry, in which service instances and endpoints are all within the service registry.
13+
14+
=== What is AWS Cloud Map?
15+
https://aws.amazon.com/cloud-map/[AWS Cloud Map] is a client-side service registry and service discovery solution provided as a ready-to-use, highly available service. Rather than build your own client service registry, you can leverage AWS Cloud Map to register your application and its running instances, and then use either the AWS Cloud Map API or DNS lookup to resolve a service's name to a current active endpoint.
16+
17+
With Cloud Map, you can define custom names for your application resources, and it maintains the updated location of these dynamically changing resources. This increases your application availability because your web service always discovers the most up-to-date locations of its resources.
18+
19+
=== Spring Cloud integration with AWS Cloud Map
20+
21+
Spring Cloud AWS adds support for registering and discovering service using AWS Cloud Map through Spring Boot https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-files-importing[config import feature].
22+
23+
Maven coordinates, using <<index.adoc#bill-of-materials, Spring Cloud AWS BOM>>:
24+
25+
[source,xml]
26+
----
27+
<dependency>
28+
<groupId>io.awspring.cloud</groupId>
29+
<artifactId>spring-cloud-aws-starter-aws-cloudmap</artifactId>
30+
</dependency>
31+
----
32+
33+
34+
=== Service registration
35+
To register a microservice to Cloud Map we need to specify the following:
36+
37+
1. Namespace - A namespace is a way to group services for an application. When you create a namespace, you specify how you want to discover service instances that you register with AWS Cloud Map
38+
2. Service name - A service is a template for registering service instances, which allow you to locate the resources for an application using AWS Cloud Map `DiscoverInstances` API action
39+
3. Service Instance - A service instance contains information about how to locate a resource, such as a web server, for an application. After you register instances, you locate them by using AWS Cloud Map `DiscoverInstances` API action.
40+
41+
Spring Cloud integration with AWS Cloud Map allows you to register a microservice to Cloud Map using the following configuration:
42+
43+
1. Automatic registration - Spring Cloud AWS Cloud Map module automatically registers the microservice to Cloud Map when the application starts. In order to do this, just include `spring-cloud-aws-starter-aws-cloudmap` dependency in your application, and Cloud Map integration module will register your microservice under `default-namespace` namespace and `spring.application.name or default-service` service name.
44+
2. Manual registration - Cloud Map properties can be provided part of the application configuration, here is a sample:
45+
46+
```properties
47+
spring.cloud.aws.cloudmap.registry.description=Namespace for sample cloudmap registry service
48+
spring.cloud.aws.cloudmap.registry.port=80
49+
spring.cloud.aws.cloudmap.registry.service=a-service
50+
spring.cloud.aws.cloudmap.registry.nameSpace=a-namespace
51+
```
52+
53+
=== Service discovery
54+
To discover a microservice using Cloud Map we need to specify the following:
55+
56+
Enable `DiscoveryClient` - You can use `@EnableDiscoveryClient` annotation to integrate with Spring Cloud `DiscoveryClient`, here is a sample:
57+
58+
```java
59+
@SpringBootApplication
60+
@EnableDiscoveryClient
61+
public class SpringCloudAwsCloudMapSample {
62+
@Autowired
63+
private DiscoveryClient discoveryClient;
64+
65+
@Bean
66+
ApplicationRunner applicationRunner() {
67+
return args -> LOGGER.info("Total instances: {}", discoveryClient.getServices().size());
68+
}
69+
}
70+
```
71+
* Using application configuration to specify the services - You can specify the services that can be discovered using `spring.cloud.aws.cloudmap.discovery.*` property, here is a sample:
72+
73+
```properties
74+
spring.cloud.aws.cloudmap.discovery.discoveryList[0].service=a-service #array of services
75+
spring.cloud.aws.cloudmap.discovery.discoveryList[0].nameSpace=a-namespace
76+
```
77+
78+
=== Using ServiceDiscoveryClient
79+
80+
The starter automatically configures and registers a `ServiceDiscoveryClient` bean in the Spring application context. The `ServiceDiscoveryClient` bean can be used to register or discovery service instances from AWS Cloud Map.
81+
82+
[source,java]
83+
----
84+
import org.springframework.stereotype.Component;
85+
import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient;
86+
...
87+
@Autowired
88+
private ServiceDiscoveryClient serviceDiscoveryClient;
89+
...
90+
Map<String, String> attributes = new HashMap<>();
91+
attributes.put(AWS_INSTANCE_IPV_4, registrationDetails.get(IPV_4_ADDRESS));
92+
attributes.put(REGION, System.getenv("AWS_REGION"));
93+
attributes.put(NAMESPACE_ID, nameSpaceId);
94+
attributes.put(SERVICE_ID, serviceId);
95+
attributes.put(SERVICE_INSTANCE_ID, serviceInstanceId);
96+
97+
// Register instance
98+
final String operationId = serviceDiscovery.registerInstance(RegisterInstanceRequest.builder()
99+
.instanceId(serviceInstanceId).serviceId(serviceId).attributes(attributes).build())
100+
.operationId();
101+
----
102+
103+
=== Customizing ServiceDiscoveryClient
104+
105+
To use custom `ServiceDiscoveryClient` in `spring.config.import`, provide an implementation of `BootstrapRegistryInitializer`. For example:
106+
107+
[source,java]
108+
----
109+
package com.app;
110+
111+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
112+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
113+
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
114+
import software.amazon.awssdk.regions.Region;
115+
import software.amazon.awssdk.services.servicediscovery.ServiceDiscoveryClient;
116+
117+
import org.springframework.boot.BootstrapRegistry;
118+
import org.springframework.boot.BootstrapRegistryInitializer;
119+
120+
class AWSCloudMapBootstrapConfiguration implements BootstrapRegistryInitializer {
121+
122+
@Override
123+
public void initialize(BootstrapRegistry registry) {
124+
registry.register(ServiceDiscoveryClient.class, context -> {
125+
AwsCredentialsProvider awsCredentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create("yourAccessKey", "yourSecretKey"));
126+
return ServiceDiscoveryClient.builder().credentialsProvider(awsCredentialsProvider).region(Region.EU_WEST_2).build();
127+
});
128+
}
129+
}
130+
----
131+
132+
---
133+
134+
Note that this class must be listed under `org.springframework.boot.BootstrapRegistryInitializer` key in `META-INF/spring.factories`:
135+
136+
[source, properties]
137+
----
138+
org.springframework.boot.BootstrapRegistryInitializer=com.app.AWSCloudMapBootstrapConfiguration
139+
----
140+
141+
Note that this class must be listed under `org.springframework.boot.BootstrapRegistryInitializer` key in `META-INF/spring.factories`:
142+
143+
[source, properties]
144+
----
145+
org.springframework.boot.BootstrapRegistryInitializer=com.app.AWSCloudMapBootstrapConfiguration
146+
----
147+
148+
If you want to use autoconfigured `ServiceDiscoveryClient` but change underlying SDKClient or ClientOverrideConfiguration you will need to register bean of type `AwsClientConfigurerCloudMap`:
149+
Autoconfiguration will configure `ServiceDiscoveryClient` Bean with provided values after that, for example:
150+
151+
If you want to use autoconfigured `ServiceDiscoveryClient` but change underlying SDKClient or ClientOverrideConfiguration you will need to register bean of type `AwsClientConfigurerCloudMap`:
152+
Autoconfiguration will configure `ServiceDiscoveryClient` Bean with provided values after that, for example:
153+
154+
[source,java]
155+
----
156+
package com.app;
157+
158+
import io.awspring.cloud.autoconfigure.cloudmap.AwsCloudMapStoreClientCustomizer;
159+
import java.time.Duration;
160+
import org.springframework.boot.BootstrapRegistry;
161+
import org.springframework.boot.BootstrapRegistryInitializer;
162+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
163+
import software.amazon.awssdk.http.SdkHttpClient;
164+
import software.amazon.awssdk.http.apache.ApacheHttpClient;
165+
import software.amazon.awssdk.services.ssm.SsmClientBuilder;
166+
167+
class AWSCloudMapBootstrapConfiguration implements BootstrapRegistryInitializer {
168+
169+
@Override
170+
public void initialize(BootstrapRegistry registry) {
171+
registry.register(AwsCloudMapStoreClientCustomizer.class,
172+
context -> new AwsCloudMapStoreClientCustomizer() {
173+
174+
@Override
175+
public ClientOverrideConfiguration overrideConfiguration() {
176+
return ClientOverrideConfiguration.builder().apiCallTimeout(Duration.ofMillis(500))
177+
.build();
178+
}
179+
180+
@Override
181+
public SdkHttpClient httpClient() {
182+
return ApacheHttpClient.builder().connectionTimeout(Duration.ofMillis(1000)).build();
183+
}
184+
});
185+
}
186+
}
187+
----
188+
189+
=== Configuration
190+
191+
The Spring Boot Starter for Cloud Map integration provides the following configuration options for service registration:
192+
193+
[cols="4,3,1,1"]
194+
|===
195+
| Name | Description | Required | Default value
196+
197+
| `spring.cloud.aws.cloudmap.registry.description` | Namespace for sample cloudmap registry service. | No | `default-namespace`
198+
| `spring.cloud.aws.cloudmap.registry.port` | Port in which the microservice is exposed. | No | `null`
199+
| `spring.cloud.aws.cloudmap.registry.service` | Service name for registering the cloudmap service. | No | `default-service or spring.application.name`
200+
| `spring.cloud.aws.cloudmap.region` | Configures region used by `ServiceDiscoveryClient`. | No | `null`
201+
|===
202+
203+
204+
The Spring Boot Starter for Cloud Map integration provides the following configuration options for discovering services:
205+
206+
[cols="4,3,1,1"]
207+
|===
208+
| Name | Description | Required | Default value
209+
210+
| `spring.cloud.aws.cloudmap.discovery.discoveryList[*].service` | Array of Cloudmap services the module will discover part of the startup. | No | `null`
211+
| `spring.cloud.aws.cloudmap.discovery.discoveryList[*].nameSpace` | Array of Cloudmap namespaces the module will discover part of the startup. | No | `null`
212+
| `spring.cloud.aws.cloudmap.region` | Configures region used by `ServiceDiscoveryClient`. | No | `null`
213+
|===
214+
215+
=== IAM Permissions
216+
Following IAM permissions are required by Spring Cloud AWS to register and discover services in AWS Cloud Map:
217+
218+
[cols="1"]
219+
|===
220+
| **Policies**
221+
| `servicediscovery:ListServices`
222+
| `servicediscovery:GetOperation`
223+
| `servicediscovery:DiscoverInstances`
224+
| `servicediscovery:DeleteNamespace`
225+
| `servicediscovery:ListNamespaces`
226+
| `servicediscovery:RegisterInstance`
227+
| `servicediscovery:CreateService`
228+
| `servicediscovery:DeregisterInstance`
229+
| `servicediscovery:DeleteService`
230+
| `servicediscovery:GetNamespace`
231+
| `servicediscovery:GetInstance`
232+
| `servicediscovery:GetService`
233+
| `servicediscovery:ListInstances`
234+
|===
235+
236+
Sample IAM policy:
237+
238+
[source,json,indent=0]
239+
----
240+
{
241+
"Version": "2012-10-17",
242+
"Statement": [
243+
{
244+
"Sid": "Sid1",
245+
"Effect": "Allow",
246+
"Action": [
247+
"servicediscovery:ListServices",
248+
"servicediscovery:GetOperation",
249+
"servicediscovery:DiscoverInstances",
250+
"servicediscovery:DeleteNamespace",
251+
"servicediscovery:ListNamespaces",
252+
"servicediscovery:RegisterInstance",
253+
"servicediscovery:CreateService",
254+
"servicediscovery:DeregisterInstance",
255+
"servicediscovery:DeleteService",
256+
"servicediscovery:GetNamespace",
257+
"servicediscovery:GetInstance",
258+
"servicediscovery:GetService",
259+
"servicediscovery:ListInstances"
260+
],
261+
"Resource": "*"
262+
}
263+
]
264+
}
265+
----
266+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.autoconfigure.cloudmap;
17+
18+
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
19+
import software.amazon.awssdk.services.ssm.SsmClientBuilder;
20+
21+
/**
22+
* @author Hari Ohm Prasath
23+
* @since 3.0.0
24+
*/
25+
public interface AwsCloudMapStoreClientCustomizer extends AwsClientCustomizer<SsmClientBuilder> {
26+
}
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import io.awspring.cloud.autoconfigure.core.AwsClientBuilderConfigurer;
2323
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
2424
import org.springframework.beans.factory.ObjectProvider;
25-
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2625
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2726
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2827
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -40,14 +39,13 @@
4039
*/
4140
@Configuration(proxyBeanMethods = false)
4241
@EnableConfigurationProperties(CloudMapProperties.class)
43-
@ConditionalOnClass({ ServiceDiscoveryClient.class, ServiceRegistration.class, CloudMapAutoRegistration.class })
4442
@ConditionalOnProperty(prefix = CloudMapProperties.CONFIG_PREFIX, name = "enabled", matchIfMissing = true)
45-
public class CloudMapBootstrapConfiguration {
43+
public class CloudMapAutoConfiguration {
4644

4745
private final ApplicationContext context;
4846
private final CloudMapProperties properties;
4947

50-
public CloudMapBootstrapConfiguration(CloudMapProperties properties, ApplicationContext context) {
48+
public CloudMapAutoConfiguration(CloudMapProperties properties, ApplicationContext context) {
5149
this.properties = properties;
5250
this.context = context;
5351
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.autoconfigure.cloudmap;
17+
18+
import org.springframework.context.ApplicationEventPublisher;
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
@Configuration
23+
public class CloudMapEventPublisherFactory {
24+
25+
@Bean
26+
public ApplicationEventPublisher createListener() {
27+
return new ApplicationEventPublisher() {
28+
@Override
29+
public void publishEvent(Object event) {
30+
31+
}
32+
};
33+
}
34+
}

0 commit comments

Comments
 (0)