All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.polaris.cluster.zookeeper.ZookeeperClusterService Maven / Gradle / Ivy

The newest version!
package io.polaris.cluster.zookeeper;

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import io.polaris.core.cluster.ClusterService;
import io.polaris.core.cluster.InstanceNode;
import io.polaris.core.cluster.InstanceQueryException;
import io.polaris.core.cluster.InstanceRegisterException;
import io.polaris.core.lang.TypeRef;
import io.polaris.core.service.ServiceName;
import io.polaris.core.string.Strings;
import io.polaris.json.Jacksons;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLProvider;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.x.discovery.ServiceCache;
import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.details.InstanceSerializer;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;

import com.google.common.collect.Lists;

/**
 * @author Qt
 * @since  Apr 17, 2024
 */
@SuppressWarnings("ALL")
@Slf4j
@ServiceName("zookeeper")
public class ZookeeperClusterService implements ClusterService {
	private static final String DEFAULT_NAMESPACE = "/public";
	@Setter
	@Getter
	private ZookeeperConfig config;
	private CuratorFramework client;
	private ServiceDiscovery serviceDiscovery;
	private ServiceCache serviceCache;
	private volatile InstanceNode selfInstance;
	private volatile boolean started = false;

	public ZookeeperClusterService() {
		this(null);
	}

	public ZookeeperClusterService(ZookeeperConfig config) {
		this.config = config;
		Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
	}

	private void doStart() {
		if (!started) {
			doStop();
			if (config == null) {
				throw new IllegalStateException("config is null");
			}
			RetryPolicy retryPolicy = new ExponentialBackoffRetry(config.getBaseSleepTimeMs(), config.getMaxRetries());

			CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
				.retryPolicy(retryPolicy)
				.connectString(config.getHostPort());
			if (config.isEnableAcl()) {
				String authInfo = config.getAuth();
				if ("digest".equals(config.getSchema())) {
					try {
						authInfo = DigestAuthenticationProvider.generateDigest(authInfo);
					} catch (NoSuchAlgorithmException e) {
						throw new IllegalStateException(e.getMessage(), e);
					}
				} else {
					throw new IllegalStateException("Support digest schema only.");
				}
				final List acls = Lists.newArrayList();
				acls.add(new ACL(ZooDefs.Perms.ALL, new Id(config.getSchema(), authInfo)));
				acls.add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE));

				ACLProvider provider = new ACLProvider() {
					@Override
					public List getDefaultAcl() {
						return acls;
					}

					@Override
					public List getAclForPath(String s) {
						return acls;
					}
				};
				builder.aclProvider(provider);
				builder.authorization(config.getSchema(), config.getAuth().getBytes());
			}
			this.client = builder.build();
			String path = (Strings.isBlank(config.getNamespace()) ? DEFAULT_NAMESPACE :
				this.config.getNamespace());

			this.serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceNode.class)
				.client(client)
				.basePath(path)
				.watchInstances(true)
				.serializer(new InstanceSerializer() {
					@Override
					public byte[] serialize(ServiceInstance instance) throws Exception {
						return Jacksons.toJsonBytes(instance);
					}

					@Override
					public ServiceInstance deserialize(byte[] bytes) throws Exception {
						return Jacksons.toJavaObject(bytes, new TypeRef>() {});
					}
				})
				.build();
			try {
				this.client.start();
				this.client.blockUntilConnected();
				this.serviceDiscovery.start();
				this.serviceCache = serviceDiscovery.serviceCacheBuilder().name(config.getServiceName()).build();
				this.serviceCache.start();
				this.started = true;
			} catch (Throwable e) {
				log.error(e.getMessage(), e);
				throw new IllegalStateException(e.getMessage(), e);
			}
		}
	}

	private void doStop() {
		try {
			if (serviceCache != null) {
				serviceCache.close();
			}
		} catch (Throwable e) {
			log.warn(e.getMessage(), e);
		}
		try {
			if (serviceDiscovery != null) {
				serviceDiscovery.close();
			}
		} catch (Throwable e) {
			log.warn(e.getMessage(), e);
		}
		try {
			if (client != null) {
				CuratorFrameworkState state = client.getState();
				if (state == null || state != CuratorFrameworkState.STOPPED) {
					client.close();
				}
			}
		} catch (Throwable e) {
			log.warn(e.getMessage(), e);
		}
		started = false;
		client = null;
		serviceDiscovery = null;
		serviceCache = null;
	}

	private void prepare() {
		if (!started) {
			doStart();
		}
	}

	public void start() {
		doStart();
	}

	public void stop() {
		doStop();
	}


	@Override
	public synchronized void register(InstanceNode instanceNode) throws InstanceRegisterException {
		prepare();
		instanceNode.check();
		try {
			ServiceInstance thisInstance
				= ServiceInstance.builder()
				.name(config.getServiceName())
				//.id(UUID.randomUUID().toString())
				.id(instanceNode.getId())
				.address(instanceNode.getHost())
				.port(instanceNode.getPort())
				.payload(instanceNode)
				.build();
			serviceDiscovery.registerService(thisInstance);
			this.selfInstance = instanceNode;
		} catch (Throwable e) {
			throw new InstanceRegisterException(e.getMessage(), e);
		}
	}

	@Override
	public List query() {
		prepare();
		List list = new ArrayList<>(20);
		try {
			List> serviceInstances = serviceCache.getInstances();
			boolean hasSelf = false;
			for (ServiceInstance serviceInstance : serviceInstances) {
				InstanceNode instance = serviceInstance.getPayload();
				if (instance.equals(selfInstance)) {
					instance.setSelf(true);
					hasSelf = true;
				} else {
					instance.setSelf(false);
				}
				list.add(instance);
			}
			if (selfInstance != null && !hasSelf) {
				list.add(selfInstance);
			}
		} catch (Throwable e) {
			throw new InstanceQueryException(e.getMessage(), e);
		}
		return list;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy