private NodeCache makeNodeCache(final ServiceInstance<T> instance) { if (!watchInstances) { return null; } final NodeCache nodeCache = new NodeCache(client, pathForInstance(instance.getName(), instance.getId())); try { nodeCache.start(true); } catch (Exception e) { log.error("Could not start node cache for: " + instance, e); } NodeCacheListener listener = new NodeCacheListener() { @Override public void nodeChanged() throws Exception { if (nodeCache.getCurrentData() != null) { ServiceInstance<T> newInstance = serializer.deserialize(nodeCache.getCurrentData().getData()); Entry<T> entry = services.get(newInstance.getId()); if (entry != null) { synchronized (entry) { entry.service = newInstance; } } } else { log.warn("Instance data has been deleted for: " + instance); } } }; nodeCache.getListenable().addListener(listener); return nodeCache; }
@Override public void updateService(final ServiceInstance<T> service) throws Exception { Entry<T> entry = services.get(service.getId()); if (entry == null) { throw new Exception("Service not registered: " + service); } synchronized (entry) { entry.service = service; byte[] bytes = serializer.serialize(service); String path = pathForInstance(service.getName(), service.getId()); client.setData().forPath(path, bytes); } }
@VisibleForTesting protected void internalRegisterService(ServiceInstance<T> service) throws Exception { byte[] bytes = serializer.serialize(service); String path = pathForInstance(service.getName(), service.getId()); final int MAX_TRIES = 2; boolean isDone = false; for (int i = 0; !isDone && (i < MAX_TRIES); ++i) { try { CreateMode mode = (service.getServiceType() == ServiceType.DYNAMIC) ? CreateMode.EPHEMERAL : CreateMode.PERSISTENT; client.create().creatingParentsIfNeeded().withMode(mode).forPath(path, bytes); isDone = true; } catch (KeeperException.NodeExistsException e) { client.delete().forPath(path); // must delete then re-create so that watchers fire } } }
/** * Register/re-register/update a service instance * * @param service service to add * @throws Exception errors */ @Override public void registerService(ServiceInstance<T> service) throws Exception { Entry<T> newEntry = new Entry<T>(service); Entry<T> oldEntry = services.putIfAbsent(service.getId(), newEntry); Entry<T> useEntry = (oldEntry != null) ? oldEntry : newEntry; synchronized (useEntry) { if (useEntry == newEntry) // i.e. is new { useEntry.cache = makeNodeCache(service); } internalRegisterService(service); } }
/** * @param client the client * @param basePath base path to store data * @param serializer serializer for instances (e.g. {@link JsonInstanceSerializer}) * @param thisInstance instance that represents the service that is running. The instance will get * auto-registered * @param watchInstances if true, watches for changes to locally registered instances */ public ServiceDiscoveryImpl( CuratorFramework client, String basePath, InstanceSerializer<T> serializer, ServiceInstance<T> thisInstance, boolean watchInstances) { this.watchInstances = watchInstances; this.client = Preconditions.checkNotNull(client, "client cannot be null"); this.basePath = Preconditions.checkNotNull(basePath, "basePath cannot be null"); this.serializer = Preconditions.checkNotNull(serializer, "serializer cannot be null"); if (thisInstance != null) { Entry<T> entry = new Entry<T>(thisInstance); entry.cache = makeNodeCache(thisInstance); services.put(thisInstance.getId(), entry); } }
public ExampleServer(CuratorFramework client, String path, String serviceName, String description) throws Exception { // in a real application, you'd have a convention of some kind for the // URI layout UriSpec uriSpec = new UriSpec("{scheme}://foo.com:{port}"); thisInstance = ServiceInstance.<InstanceDetails>builder() .name(serviceName) .payload(new InstanceDetails(description)) .port((int) (65535 * Math.random())) // in a real application you'd use a common port .uriSpec(uriSpec) .build(); // if you mark your payload class with @JsonRootName the provided // JsonInstanceSerializer will work JsonInstanceSerializer<InstanceDetails> serializer = new JsonInstanceSerializer<InstanceDetails>(InstanceDetails.class); serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class) .client(client) .basePath(path) .serializer(serializer) .thisInstance(thisInstance) .build(); }
/** * Unregister/remove a service instance * * @param service the service * @throws Exception errors */ @Override public void unregisterService(ServiceInstance<T> service) throws Exception { Entry<T> entry = services.remove(service.getId()); internalUnregisterService(entry); }