Skip to content

Commit 7f57054

Browse files
committed
Added externalHosts config setting, refactored RSocketClientTransport to support connection on multiple addresses
1 parent db83884 commit 7f57054

File tree

8 files changed

+128
-98
lines changed

8 files changed

+128
-98
lines changed

services-api/src/main/java/io/scalecube/services/ServiceEndpoint.java

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.io.ObjectInput;
77
import java.io.ObjectOutput;
88
import java.util.ArrayList;
9+
import java.util.Arrays;
910
import java.util.Collection;
1011
import java.util.Collections;
1112
import java.util.HashMap;
@@ -23,7 +24,7 @@ public class ServiceEndpoint implements Externalizable {
2324
private static final long serialVersionUID = 1L;
2425

2526
private String id;
26-
private Address address;
27+
private List<Address> addresses;
2728
private Set<String> contentTypes;
2829
private Map<String, String> tags;
2930
private Collection<ServiceRegistration> serviceRegistrations;
@@ -38,7 +39,8 @@ public ServiceEndpoint() {}
3839

3940
private ServiceEndpoint(Builder builder) {
4041
this.id = Objects.requireNonNull(builder.id, "ServiceEndpoint.id is required");
41-
this.address = Objects.requireNonNull(builder.address, "ServiceEndpoint.address is required");
42+
this.addresses =
43+
Objects.requireNonNull(builder.addresses, "ServiceEndpoint.addresses is required");
4244
this.contentTypes = Collections.unmodifiableSet(new HashSet<>(builder.contentTypes));
4345
this.tags = Collections.unmodifiableMap(new HashMap<>(builder.tags));
4446
this.serviceRegistrations =
@@ -57,8 +59,8 @@ public String id() {
5759
return id;
5860
}
5961

60-
public Address address() {
61-
return address;
62+
public List<Address> addresses() {
63+
return addresses;
6264
}
6365

6466
public Set<String> contentTypes() {
@@ -88,7 +90,7 @@ public Collection<ServiceReference> serviceReferences() {
8890
public String toString() {
8991
return new StringJoiner(", ", ServiceEndpoint.class.getSimpleName() + "[", "]")
9092
.add("id=" + id)
91-
.add("address=" + address)
93+
.add("addresses=" + addresses)
9294
.add("contentTypes=" + contentTypes)
9395
.add("tags=" + tags)
9496
.add("serviceRegistrations(" + serviceRegistrations.size() + ")")
@@ -100,8 +102,11 @@ public void writeExternal(ObjectOutput out) throws IOException {
100102
// id
101103
out.writeUTF(id);
102104

103-
// address
104-
out.writeUTF(address.toString());
105+
// addresses
106+
out.writeInt(addresses.size());
107+
for (Address address : addresses) {
108+
out.writeUTF(address.toString());
109+
}
105110

106111
// contentTypes
107112
out.writeInt(contentTypes.size());
@@ -128,8 +133,12 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept
128133
// id
129134
id = in.readUTF();
130135

131-
// address
132-
address = Address.from(in.readUTF());
136+
// addresses
137+
final int addressesSize = in.readInt();
138+
addresses = new ArrayList<>(addressesSize);
139+
for (int i = 0; i < addressesSize; i++) {
140+
addresses.add(Address.from(in.readUTF()));
141+
}
133142

134143
// contentTypes
135144
int contentTypesSize = in.readInt();
@@ -161,7 +170,7 @@ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundExcept
161170
public static class Builder {
162171

163172
private String id;
164-
private Address address = Address.NULL_ADDRESS;
173+
private List<Address> addresses = new ArrayList<>();
165174
private Set<String> contentTypes = Collections.emptySet();
166175
private Map<String, String> tags = Collections.emptyMap();
167176
private Collection<ServiceRegistration> serviceRegistrations = new ArrayList<>();
@@ -170,7 +179,7 @@ private Builder() {}
170179

171180
private Builder(ServiceEndpoint other) {
172181
this.id = other.id;
173-
this.address = other.address;
182+
this.addresses = new ArrayList<>(other.addresses);
174183
this.contentTypes = new HashSet<>(other.contentTypes);
175184
this.tags = new HashMap<>(other.tags);
176185
this.serviceRegistrations = new ArrayList<>(other.serviceRegistrations);
@@ -181,8 +190,12 @@ public Builder id(String id) {
181190
return this;
182191
}
183192

184-
public Builder address(Address address) {
185-
this.address = Objects.requireNonNull(address, "address");
193+
public Builder addresses(Address... addresses) {
194+
return addresses(Arrays.asList(addresses));
195+
}
196+
197+
public Builder addresses(List<Address> addresses) {
198+
this.addresses = new ArrayList<>(Objects.requireNonNull(addresses, "addresses"));
186199
return this;
187200
}
188201

services-api/src/main/java/io/scalecube/services/ServiceReference.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import io.scalecube.net.Address;
44
import io.scalecube.services.api.Qualifier;
5+
import java.util.ArrayList;
56
import java.util.Collections;
67
import java.util.HashMap;
8+
import java.util.List;
79
import java.util.Map;
810
import java.util.Set;
911
import java.util.StringJoiner;
@@ -20,7 +22,7 @@ public class ServiceReference {
2022
private final Set<String> contentTypes;
2123
private final Map<String, String> tags;
2224
private final String action;
23-
private final Address address;
25+
private final List<Address> addresses;
2426
private final boolean isSecured;
2527

2628
/**
@@ -40,7 +42,7 @@ public ServiceReference(
4042
this.tags = mergeTags(serviceMethodDefinition, serviceRegistration, serviceEndpoint);
4143
this.action = serviceMethodDefinition.action();
4244
this.qualifier = Qualifier.asString(namespace, action);
43-
this.address = serviceEndpoint.address();
45+
this.addresses = new ArrayList<>(serviceEndpoint.addresses());
4446
this.isSecured = serviceMethodDefinition.isSecured();
4547
}
4648

@@ -72,8 +74,8 @@ public String action() {
7274
return action;
7375
}
7476

75-
public Address address() {
76-
return this.address;
77+
public List<Address> addresses() {
78+
return addresses;
7779
}
7880

7981
public boolean isSecured() {
@@ -95,7 +97,7 @@ private Map<String, String> mergeTags(
9597
public String toString() {
9698
return new StringJoiner(", ", ServiceReference.class.getSimpleName() + "[", "]")
9799
.add("endpointId=" + endpointId)
98-
.add("address=" + address)
100+
.add("addresses=" + addresses)
99101
.add("qualifier=" + qualifier)
100102
.add("contentTypes=" + contentTypes)
101103
.add("tags=" + tags)

services-gateway/src/main/java/io/scalecube/services/gateway/transport/StaticAddressRouter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public StaticAddressRouter(Address address) {
2828
new ServiceMethodDefinition(UUID.randomUUID().toString()),
2929
new ServiceRegistration(
3030
UUID.randomUUID().toString(), Collections.emptyMap(), Collections.emptyList()),
31-
ServiceEndpoint.builder().id(UUID.randomUUID().toString()).address(address).build());
31+
ServiceEndpoint.builder().id(UUID.randomUUID().toString()).addresses(address).build());
3232
}
3333

3434
@Override

services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/RSocketClientChannel.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,23 @@
88
import io.scalecube.services.transport.api.ClientChannel;
99
import java.lang.reflect.Type;
1010
import org.reactivestreams.Publisher;
11-
import org.slf4j.Logger;
12-
import org.slf4j.LoggerFactory;
1311
import reactor.core.publisher.Flux;
1412
import reactor.core.publisher.Mono;
1513
import reactor.netty.channel.AbortedException;
1614

1715
public class RSocketClientChannel implements ClientChannel {
1816

19-
private static final Logger LOGGER = LoggerFactory.getLogger(RSocketClientChannel.class);
20-
21-
private final Mono<RSocket> rsocket;
17+
private final Mono<RSocket> promise;
2218
private final ServiceMessageCodec messageCodec;
2319

24-
public RSocketClientChannel(Mono<RSocket> rsocket, ServiceMessageCodec codec) {
25-
this.rsocket = rsocket;
20+
public RSocketClientChannel(Mono<RSocket> promise, ServiceMessageCodec codec) {
21+
this.promise = promise;
2622
this.messageCodec = codec;
2723
}
2824

2925
@Override
3026
public Mono<ServiceMessage> requestResponse(ServiceMessage message, Type responseType) {
31-
return rsocket
27+
return promise
3228
.flatMap(rsocket -> rsocket.requestResponse(toPayload(message)))
3329
.map(this::toMessage)
3430
.map(msg -> ServiceMessageCodec.decodeData(msg, responseType))
@@ -37,7 +33,7 @@ public Mono<ServiceMessage> requestResponse(ServiceMessage message, Type respons
3733

3834
@Override
3935
public Flux<ServiceMessage> requestStream(ServiceMessage message, Type responseType) {
40-
return rsocket
36+
return promise
4137
.flatMapMany(rsocket -> rsocket.requestStream(toPayload(message)))
4238
.map(this::toMessage)
4339
.map(msg -> ServiceMessageCodec.decodeData(msg, responseType))
@@ -47,7 +43,7 @@ public Flux<ServiceMessage> requestStream(ServiceMessage message, Type responseT
4743
@Override
4844
public Flux<ServiceMessage> requestChannel(
4945
Publisher<ServiceMessage> publisher, Type responseType) {
50-
return rsocket
46+
return promise
5147
.flatMapMany(rsocket -> rsocket.requestChannel(Flux.from(publisher).map(this::toPayload)))
5248
.map(this::toMessage)
5349
.map(msg -> ServiceMessageCodec.decodeData(msg, responseType))

services-transport-parent/services-transport-rsocket/src/main/java/io/scalecube/services/transport/rsocket/RSocketClientTransport.java

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import io.scalecube.utils.MaskUtil;
2222
import java.util.Collection;
2323
import java.util.Collections;
24+
import java.util.List;
2425
import java.util.Map;
2526
import java.util.concurrent.ConcurrentHashMap;
27+
import java.util.concurrent.atomic.AtomicInteger;
2628
import org.slf4j.Logger;
2729
import org.slf4j.LoggerFactory;
2830
import reactor.core.publisher.Mono;
@@ -31,7 +33,7 @@ public class RSocketClientTransport implements ClientTransport {
3133

3234
private static final Logger LOGGER = LoggerFactory.getLogger(RSocketClientTransport.class);
3335

34-
private final ThreadLocal<Map<Address, Mono<RSocket>>> rsockets =
36+
private final ThreadLocal<Map<String, Mono<RSocket>>> connections =
3537
ThreadLocal.withInitial(ConcurrentHashMap::new);
3638

3739
private final CredentialsSupplier credentialsSupplier;
@@ -64,17 +66,72 @@ public RSocketClientTransport(
6466

6567
@Override
6668
public ClientChannel create(ServiceReference serviceReference) {
67-
final Map<Address, Mono<RSocket>> monoMap = rsockets.get(); // keep reference for threadsafety
68-
final Address address = serviceReference.address();
69-
Mono<RSocket> mono =
70-
monoMap.computeIfAbsent(
71-
address,
69+
final String endpointId = serviceReference.endpointId();
70+
final Map<String, Mono<RSocket>> connections = this.connections.get();
71+
72+
Mono<RSocket> promise =
73+
connections.computeIfAbsent(
74+
endpointId,
7275
key ->
73-
getCredentials(serviceReference)
74-
.flatMap(creds -> connect(key, creds, monoMap))
76+
connect(serviceReference, connections)
7577
.cache()
76-
.doOnError(ex -> monoMap.remove(key)));
77-
return new RSocketClientChannel(mono, new ServiceMessageCodec(headersCodec, dataCodecs));
78+
.doOnError(ex -> connections.remove(key)));
79+
80+
return new RSocketClientChannel(promise, new ServiceMessageCodec(headersCodec, dataCodecs));
81+
}
82+
83+
private Mono<RSocket> connect(
84+
ServiceReference serviceReference, Map<String, Mono<RSocket>> connections) {
85+
return Mono.defer(
86+
() -> {
87+
final String endpointId = serviceReference.endpointId();
88+
final List<Address> addresses = serviceReference.addresses();
89+
final AtomicInteger currentIndex = new AtomicInteger(0);
90+
91+
return Mono.defer(
92+
() -> {
93+
final Address address = addresses.get(currentIndex.get());
94+
return connect(serviceReference, connections, address, endpointId);
95+
})
96+
.doOnError(ex -> currentIndex.incrementAndGet())
97+
.retry(addresses.size() - 1)
98+
.doOnError(
99+
th ->
100+
LOGGER.warn(
101+
"Failed to connect ({}/{}), cause: {}",
102+
endpointId,
103+
addresses,
104+
th.toString()));
105+
});
106+
}
107+
108+
private Mono<RSocket> connect(
109+
ServiceReference serviceReference,
110+
Map<String, Mono<RSocket>> connections,
111+
Address address,
112+
String endpointId) {
113+
return getCredentials(serviceReference)
114+
.flatMap(
115+
creds ->
116+
RSocketConnector.create()
117+
.payloadDecoder(PayloadDecoder.DEFAULT)
118+
.setupPayload(encodeConnectionSetup(new ConnectionSetup(creds)))
119+
.connect(() -> clientTransportFactory.clientTransport(address)))
120+
.doOnSuccess(
121+
rsocket -> {
122+
LOGGER.debug("[{}] Connected successfully", address);
123+
// Setup shutdown hook
124+
rsocket
125+
.onClose()
126+
.doFinally(
127+
s -> {
128+
connections.remove(endpointId);
129+
LOGGER.debug("[{}] Connection closed", address);
130+
})
131+
.doOnError(
132+
th -> LOGGER.warn("[{}] Exception on close: {}", address, th.toString()))
133+
.subscribe();
134+
});
78135
}
79136

80137
private Mono<Map<String, String>> getCredentials(ServiceReference serviceReference) {
@@ -103,37 +160,6 @@ private Mono<Map<String, String>> getCredentials(ServiceReference serviceReferen
103160
});
104161
}
105162

106-
private Mono<RSocket> connect(
107-
Address address, Map<String, String> creds, Map<Address, Mono<RSocket>> monoMap) {
108-
return RSocketConnector.create()
109-
.payloadDecoder(PayloadDecoder.DEFAULT)
110-
.setupPayload(encodeConnectionSetup(new ConnectionSetup(creds)))
111-
.connect(() -> clientTransportFactory.clientTransport(address))
112-
.doOnSuccess(
113-
rsocket -> {
114-
LOGGER.debug("[rsocket][client][{}] Connected successfully", address);
115-
// setup shutdown hook
116-
rsocket
117-
.onClose()
118-
.doFinally(
119-
s -> {
120-
monoMap.remove(address);
121-
LOGGER.debug("[rsocket][client][{}] Connection closed", address);
122-
})
123-
.doOnError(
124-
th ->
125-
LOGGER.warn(
126-
"[rsocket][client][{}][onClose] Exception occurred: {}",
127-
address,
128-
th.toString()))
129-
.subscribe();
130-
})
131-
.doOnError(
132-
th ->
133-
LOGGER.warn(
134-
"[rsocket][client][{}] Failed to connect, cause: {}", address, th.toString()));
135-
}
136-
137163
private Payload encodeConnectionSetup(ConnectionSetup connectionSetup) {
138164
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
139165
try {

0 commit comments

Comments
 (0)