package netflix.ocelli.eureka; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.DiscoveryClient; import netflix.ocelli.Instance; import netflix.ocelli.SnapshotToInstance; import rx.Observable; import rx.Scheduler; import rx.functions.Func0; import rx.functions.Func1; import rx.schedulers.Schedulers; import javax.inject.Inject; import java.util.List; import java.util.concurrent.TimeUnit; /** * Wrapper for v1 DisoveryClient which offers a convenient DSL to express interests * in host streams. * * {@code * <pre> * RoundRobinLoadBalancer lb = RoundRobinLoadBalancer.create(); * * EurekaInterestManager manager = new EurekaInterestMangaer(discoveryClient); * Subscription sub = manager * .newInterest() * .forApplication("applicationName") * .withRefreshInterval(30, TimeUnit.SECONDS) * .withScheduler(scheduler) * .asObservable() * .compose(InstanceCollector.<InstanceInfo>create()) * .subscribe(lb); * * lb.flatMap(operation); * </pre> * } * * @author elandau */ public class EurekaInterestManager { private static final int DEFAULT_REFRESH_RATE = 30; private final DiscoveryClient client; @Inject public EurekaInterestManager(DiscoveryClient client) { this.client = client; } public InterestDsl newInterest() { return new InterestDsl(client); } /** * DSL to simplify specifying the interest * * @author elandau */ public static class InterestDsl { private final DiscoveryClient client; private String appName; private String vip; private boolean secure = false; private String region; private long interval = DEFAULT_REFRESH_RATE; private TimeUnit intervalUnits = TimeUnit.SECONDS; private Scheduler scheduler = Schedulers.computation(); private InterestDsl(DiscoveryClient client) { this.client = client; } public InterestDsl withScheduler(Scheduler scheduler) { this.scheduler = scheduler; return this; } public InterestDsl withRefreshInterval(long interval, TimeUnit units) { this.interval = interval; this.intervalUnits = units; return this; } public InterestDsl forApplication(String appName) { this.appName = appName; return this; } public InterestDsl forVip(String vip) { this.vip = vip; return this; } public InterestDsl forRegion(String region) { this.region = region; return this; } public InterestDsl isSecure(boolean secure) { this.secure = secure; return this; } public Observable<Instance<InstanceInfo>> asObservable() { return create(createLister()); } private Observable<Instance<InstanceInfo>> create(final Func0<List<InstanceInfo>> lister) { return Observable .interval(interval, intervalUnits, scheduler) .onBackpressureDrop() .flatMap(new Func1<Long, Observable<List<InstanceInfo>>>() { @Override public Observable<List<InstanceInfo>> call(Long t1) { try { return Observable.just(lister.call()); } catch (Exception e) { return Observable.empty(); } } }, 1) .serialize() .compose(new SnapshotToInstance<InstanceInfo>()); } private Func0<List<InstanceInfo>> createLister() { if (appName != null) { if (vip != null) { return _forVipAndApplication(vip, appName, secure); } else { if (region != null) { return _forApplicationAndRegion(appName, region); } else { return _forApplication(appName); } } } else if (vip != null) { if (region != null) { return _forVip(vip, secure); } else { return _forVipAndRegion(vip, secure, region); } } throw new IllegalArgumentException("Interest combination not supported"); } private Func0<List<InstanceInfo>> _forApplication(final String appName) { return new Func0<List<InstanceInfo>>() { @Override public List<InstanceInfo> call() { return client.getApplication(appName).getInstances(); } }; } private Func0<List<InstanceInfo>> _forApplicationAndRegion(final String appName, final String region) { return new Func0<List<InstanceInfo>>() { @Override public List<InstanceInfo> call() { return client.getApplicationsForARegion(region).getRegisteredApplications(appName).getInstances(); } }; } private Func0<List<InstanceInfo>> _forVip(final String vip, final boolean secure) { return new Func0<List<InstanceInfo>>() { @Override public List<InstanceInfo> call() { return client.getInstancesByVipAddress(vip, secure); } }; } private Func0<List<InstanceInfo>> _forVipAndRegion(final String vip, final boolean secure, final String region) { return new Func0<List<InstanceInfo>>() { @Override public List<InstanceInfo> call() { return client.getInstancesByVipAddress(vip, secure, region); } }; } private Func0<List<InstanceInfo>> _forVipAndApplication(final String vip, final String appName, final boolean secure) { return new Func0<List<InstanceInfo>>() { @Override public List<InstanceInfo> call() { return client.getInstancesByVipAddressAndAppName(vip, appName, secure); } }; } } }