com.netflix.eureka2.client.service.InterestChannelImpl Maven / Gradle / Ivy
package com.netflix.eureka2.client.service;
import com.netflix.eureka2.client.registry.EurekaClientRegistry;
import com.netflix.eureka2.client.transport.TransportClient;
import com.netflix.eureka2.interests.ChangeNotification;
import com.netflix.eureka2.interests.Interest;
import com.netflix.eureka2.interests.ModifyNotification;
import com.netflix.eureka2.interests.MultipleInterests;
import com.netflix.eureka2.protocol.discovery.AddInstance;
import com.netflix.eureka2.protocol.discovery.DeleteInstance;
import com.netflix.eureka2.protocol.discovery.InterestRegistration;
import com.netflix.eureka2.protocol.discovery.InterestSetNotification;
import com.netflix.eureka2.protocol.discovery.UpdateInstanceInfo;
import com.netflix.eureka2.registry.Delta;
import com.netflix.eureka2.registry.InstanceInfo;
import com.netflix.eureka2.service.InterestChannel;
import com.netflix.eureka2.transport.MessageConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.functions.Func1;
import rx.observers.SafeSubscriber;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* An implementation of {@link InterestChannel}. It is mandatory that all operations
* on the channel are serialized, by the external client. This class is not thread safe and all operations on it
* shall be executed by the same thread.
*
* Use {@link InterestChannelInvoker} for serializing operations on this channel.
*
* @author Nitesh Kant
*/
/*pkg-private: Used by EurekaClientService only*/class InterestChannelImpl
extends AbstractChannel implements ClientInterestChannel {
private static final Logger logger = LoggerFactory.getLogger(InterestChannelImpl.class);
private static final IllegalStateException INTEREST_NOT_REGISTERED_EXCEPTION =
new IllegalStateException("No interest is registered on this channel.");
/**
* Since we assume single threaded access to this channel, no need for concurrency control
*/
protected MultipleInterests channelInterest;
protected Observable> channelInterestStream;
protected Subscriber> channelInterestSubscriber;
protected enum STATES {Idle, Open, Closed}
private final InterestChannelMetrics metrics;
protected EurekaClientRegistry registry;
/**
* A local copy of instances received by this channel from the server. This is used for:
*
*
- Updates on the wire: Since we only get the delta on the wire, we use this map to get the last seen
{@link InstanceInfo} and apply the delta on it to get the new {@link InstanceInfo}
- Deletes on the wire: Since we only get the identifier for the instance deleted, we use this map to
get the last seen {@link InstanceInfo}
*
* Thread safety
*
* Since this channel directly leverages the underlying {@link MessageConnection} and our underlying stack guarantees
* that there are not concurrent updates sent to the input reader, we can safely assume that this code is single
* threaded.
*/
private final Map idVsInstance = new HashMap<>();
InterestChannelImpl(final EurekaClientRegistry registry, TransportClient client, InterestChannelMetrics metrics) {
super(STATES.Idle, client);
this.registry = registry;
this.metrics = metrics;
metrics.incrementStateCounter(STATES.Idle);
channelInterest = new MultipleInterests<>(); // blank channelInterest to start with
channelInterestSubscriber = new ChannelInterestSubscriber(registry);
channelInterestStream = createInterestStream();
}
// channel contract means this will be invoked in serial.
@Override
public Observable change(final Interest newInterest) {
Observable serverRequest = connect() // Connect is idempotent and does not connect on every call.
.switchMap(new Func1>() {
@Override
public Observable call(MessageConnection serverConnection) {
return serverConnection.submitWithAck(new InterestRegistration(newInterest))
.doOnCompleted(new UpdateLocalInterest(newInterest));
}
});
return Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super Void> subscriber) {
if (STATES.Closed == state.get()) {
subscriber.onError(CHANNEL_CLOSED_EXCEPTION);
} else if (moveToState(STATES.Idle, STATES.Open)) {
logger.debug("First time registration");
channelInterestStream.subscribe(channelInterestSubscriber);
} else {
logger.debug("Channel changes");
}
subscriber.onCompleted();
}
}).concatWith(serverRequest);
}
@Override
public Observable appendInterest(Interest toAppend) {
if (null == channelInterest) {
return Observable.error(INTEREST_NOT_REGISTERED_EXCEPTION);
}
return change(channelInterest.copyAndAppend(toAppend));
}
@Override
public Observable removeInterest(Interest toRemove) {
if (null == channelInterest) {
return Observable.error(INTEREST_NOT_REGISTERED_EXCEPTION);
}
return change(channelInterest.copyAndRemove(toRemove));
}
@Override
protected void _close() {
if (state.get() != STATES.Closed) {
moveToState(state.get(), STATES.Closed);
idVsInstance.clear();
super._close();
}
}
protected Observable> createInterestStream() {
return connect().switchMap(new Func1>>() {
@Override
public Observable extends ChangeNotification> call(final MessageConnection connection) {
return connection.incoming().filter(new Func1 © 2015 - 2025 Weber Informatics LLC | Privacy Policy