Distributed KV Store - PoC / Experiements
Distributed KV Store with example python program (Using Python-evnconsul)
Example use case of using Consul KV to retrieve Configuration values to have a stateless micro service.
Environment:
Consul agent running at <consul_ip>.
Django based micro service.
Manage DB configuration in settings.py of the micro service.
Steps:
Add database configuration key values to Consul:
curl -X PUT -d 'django.db.backends.mysql' http://<consul_ip>:8500/v1/kv/<service_name>/database/enginecurl -X PUT -d '<DB_NAME>' http://<consul_ip>:8500/v1/kv/<service_name>/database/namecurl -X PUT -d '<DB_IP>' http://<consul_ip>:8500/v1/kv/<service_name>/database/ipcurl -X PUT -d 3306 http://<consul_ip>8500/v1/kv/<service_name>/database/portcurl -X PUT -d '<DB_USERNAME>' http://<consul_ip>:8500/v1/kv/<service_name>/database/usernamecurl -X PUT -d '<DB_PASSWORD>' http://<consul_ip>:8500/v1/kv/<service_name>/database/password
In settings.py of Django, retrieve the DB configuration values as:
import envconsul
ENV_CONSUL = envconsul.EnvConsul( service_name='<service_name>', host='<consul_ip>', port=8500,)
DB_ENGINE = ENV_CONSUL.get_str('/database/engine')DB_NAME = ENV_CONSUL.get_str('/database/name')DB_IP = ENV_CONSUL.get_str('/database/ip')DB_PORT = ENV_CONSUL.get('/database/port')DB_USERNAME = ENV_CONSUL.get_str('/database/username')DB_PASSWORD = ENV_CONSUL.get_str('/database/password')
# Use the values obtained from Consul in DATABASES config of Django.
DATABASES = { 'default': { 'ENGINE': DB_ENGINE, 'NAME': DB_NAME, 'HOST': DB_IP, 'PORT': DB_PORT, 'USER': DB_USERNAME, 'PASSWORD': DB_PASSWORD, },}
This way, DB configuration can easily be passed to all the micro services.
Example shown is using python-envconsul to fetch KVs. Need to use better library such as python-consul which provides capabilities to fetch KV as notifications whenever there are changes in the values.
<TODO>
Distributed KV Store with example python program (Using Python-Consul)
this is to experiment notification functionality and configuration reloading.
Distributed KV Store with example Java program (Using cfg4J)
Environment:
Consul agent running at <consul_ip>.
Java based micro service.
Manage DB configuration values in a ".configuration" file of a micro service.
Steps:
Add database configuration key values to Consul:
curl -X PUT -d '<DB_NAME>' http://<consul_ip>:8500/v1/kv/<service_name>/database/namecurl -X PUT -d '<DB_IP>' http://<consul_ip>:8500/v1/kv/<service_name>/database/ipcurl -X PUT -d 3306 http://<consul_ip>8500/v1/kv/<service_name>/database/portcurl -X PUT -d '<DB_USERNAME>' http://<consul_ip>:8500/v1/kv/<service_name>/database/usernamecurl -X PUT -d '<DB_PASSWORD>' http://<consul_ip>:8500/v1/kv/<service_name>/database/password
Example usage of cfg4j to retrieve the above Key Values from Consul:
import org.cfg4j.provider.ConfigurationProvider;import org.cfg4j.provider.ConfigurationProviderBuilder;import org.cfg4j.source.ConfigurationSource;import org.cfg4j.source.consul.ConsulConfigurationSourceBuilder;import org.cfg4j.source.context.environment.ImmutableEnvironment;import org.cfg4j.source.reload.ReloadStrategy;import org.cfg4j.source.reload.strategy.PeriodicalReloadStrategy;import org.cfg4j.source.compose.MergeConfigurationSource;import org.cfg4j.source.files.FilesConfigurationSource;import java.lang.reflect.Field;import java.util.Collections;import java.nio.file.Path;import java.nio.file.Paths;import java.util.HashMap;import java.util.concurrent.TimeUnit;public class Consulcf4j {public interface ReadDBConfig { String name(); String ip(); Integer port(); String username(); String password(); }public static void main(String... args) throws Exception{ HashMap<String, Object> prev_map = new HashMap<String, Object>(); HashMap<String, Object> curr_map = new HashMap<String, Object>(); prev_map.put("name", ""); prev_map.put("ip", ""); prev_map.put("port", 0); prev_map.put("username", ""); prev_map.put("password", ""); ConfigurationSource source = new ConsulConfigurationSourceBuilder() .withHost("localhost") .build(); ReloadStrategy reloadStrategy = new PeriodicalReloadStrategy(5, TimeUnit.SECONDS);//ConfigurationSource localOverrideSource = new FilesConfigurationSource(() -> Collections.singletonList(Paths.get("appliction.properties"))); //MergeConfigurationSource mergeConfigurationSource = new MergeConfigurationSource(localOverrideSource, source);ConfigurationProvider provider = new ConfigurationProviderBuilder() .withConfigurationSource(source) .withEnvironment(new ImmutableEnvironment("vfcnslcm")) .withReloadStrategy(reloadStrategy) .build(); ReadDBConfig conf = provider.bind("database", ReadDBConfig.class);while(true){ curr_map.put("name", conf.name()); curr_map.put("ip", conf.ip()); curr_map.put("port", conf.port()); curr_map.put("username", conf.username()); curr_map.put("password", conf.password());for(String key: curr_map.keySet()){if(!prev_map.get(key).equals(curr_map.get(key))){ System.out.println("Values: ");for(String k: curr_map.keySet()){ System.out.println(curr_map.get(k)); }break; } } prev_map.clear(); prev_map.putAll(curr_map); }// String value = provider.getProperty("engine", String.class);// System.out.println(value);}}
This way, DB configuration can easily be passed to all the micro services.
This can be used in Spring Beans as shown in http://www.cfg4j.org/releases/latest/#configuration-provider