How to Register Spring Cloud with Consul Service Discovery
One of the main component of micro service architecture is service discovery (SD). So far in this blog I mentioned two tools for service discovery, which are Eureka and Consul. But if you may notice, whenever I used spring-cloud framework, I always choose Eureka. On the other hand, I pick consul when I talked about Golang.
Overview
The scope of this article only registering services into consul, and how to discover them.
Step-by-step guide
Before the first step, let us assume the consul already up and running at http://192.168.99.100:8500
Register your service into consul
- Go to http://start.spring.io/, select dependencies
Consul Discovery
- Open your project, add this lines in
application.properties
yml
)12345spring.cloud.consul.host=localhostspring.cloud.consul.port=8500spring.cloud.consul.discovery.acl-token=4efb1523-76a3-f476-e6d8-452220593089spring.cloud.consul.discovery.service-name=${spring.application.name}spring.cloud.consul.discovery.instance-id=${spring.application.name}-${random.value}
123456789key "" {policy = "write"}node "" {policy = "write"}service "" {policy = "write"} - Because consul needs health check, include
actuator
dependencies into maven configuration. Then for the sake of example, disable the security. (I DO NOT RECOMMEND TO DISABLE THIS. USE SPRING-SECURITY INSTEAD).1management.security.enabled=false - Create controller, let say
HelloController
123456789101112@RestControllerpublic class HelloController {private Logger logger = LoggerFactory.getLogger(getClass());private AtomicInteger atoi = new AtomicInteger();@GetMapping("/hello")public HelloDto getHello(){int counter = atoi.getAndIncrement();logger.info("Hello {}", counter);return new HelloDto(counter, "Hello!");}} - Run the service, then check to your consul ui (http://localhost:8500/ui)
Access Service through Feign
Please add feign dependency to your project.
- Enable discovery client and feign client in your services.
12345678@SpringBootApplication@EnableDiscoveryClient@EnableFeignClients("com.example.sandbox.consul.consulfeigndemo.feign")public class ConsulFeignDemoApplication {public static void main(String[] args) {SpringApplication.run(ConsulFeignDemoApplication.class, args);}}
- Configure your
application.properties
(oryml
) just like previous one. - Create Feign Interface.
12345@FeignClient("spring-consul-hello")public interface HelloFeign {@RequestMapping(value="/hello", method = RequestMethod.GET)HelloDto hi();}
- Create controller.
12345678910111213@RestControllerpublic class PingController {Logger logger = LoggerFactory.getLogger(getClass());@Autowiredprivate HelloFeign helloFeign;@GetMapping("/ping/hello")public HelloDto hello(){return helloFeign.hi();}}
- Start your spring boot application, then test the endpoint via
curl.
1curl -X GET http://localhost:8090/ping/hello
Advance Topic
Distinguish Your Service by Tags
One of the interesting feature in consul is tags. This feature is similar with Eureka instance metadata. With this feature, we can separate our services based on specific criteria. For instance by version. By filtering our service by version, we can leverage the functionality for blue-green
or canary
deployment technique.
Tag Your Service
- Add tags to your service. This can be done via
application.properties
file.1spring.cloud.consul.discovery.tags=version=1.0, entity=life - Deploy the service.
Access Service by Service Name and Tags
To make easy access, I created a configuration bean, to filter the desired service by name and tags. For example, the target service name is spring-consul-hello
. I will make a feature toggle as well, whenever you want to disable the tags filter.
- Create a configuration bean.
123456789101112131415@Configuration@ConfigurationProperties("consul")public class ConsulTagConfiguration {private final Map<String, Map<String,String>> tags = new HashMap<>();private boolean tagsFilterEnable;public Map<String, Map<String,String>> getTags() {return tags;}public boolean isTagsFilterEnable() {return tagsFilterEnable;}public void setTagsFilterEnable(boolean tagsFilterEnable) {this.tagsFilterEnable = tagsFilterEnable;}}
- Configure in
application.properties
.123consul.tags-filter-enable=trueconsul.tags.spring-consul-hello.version=1.0consul.tags.spring-consul-hello.entity=life - Create a custom ribbon configuration.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546@Configurationpublic class SpringConsulHelloConfig {private Logger logger = LoggerFactory.getLogger(getClass());@Autowiredprivate ConsulTagConfiguration tags;@Bean@ConditionalOnProperty(value = "consul.tags-filter-enable", havingValue = "true")public ServerListFilter<Server> serverListFilter(){return new ServerListFilter<Server>(){@Overridepublic List<Server> getFilteredListOfServers(List<Server> servers) {List<Server> result = new ArrayList<>();for (Server server : servers) {if (server instanceof ConsulServer) {ConsulServer consulServer = (ConsulServer) server;logger.info("server service id: {}, metainfo.getAppName: {}, metadata: {}", consulServer.getId(), consulServer.getMetaInfo().getAppName(), consulServer.getMetadata());if (consulServer.isPassingChecks() && isTagsValid(consulServer.getMetaInfo().getAppName(), consulServer.getMetadata())) {result.add(server);}}}return result;}private boolean isTagsValid(String appName, Map<String, String> metadata){Map<String, Map<String, String>> map = tags.getTags();if(!map.containsKey(appName)){ //empty tag is validreturn true;}Map<String, String> kv = map.get(appName);Set<String> keys = kv.keySet();for (String k : keys) {if(metadata.get(k) != null && metadata.get(k).equals(kv.get(k))){continue;} else {return false;}}return true;}};}}
- Deploy the service.
Accessing Key Value Consul Data Store
One of the most interesting feature in consul is their embedded key value data store. You can access or store the value by typing this:
1 2 3 |
ConsulClient client = new ConsulClient("localhost", 8500); client.setKVValue("a/b/c", value,"4efb1523-76a3-f476-e6d8-452220593089", null); Response<GetValue> response = client.getKVValue("a/b/c", "4efb1523-76a3-f476-e6d8-452220593089"); |
Note: you can get the url, port and token by accessing the properties. This is hard coded just for clarity.
Conclusion
Consul as a service discovery is a great option, beside eureka. What I really like about consul is many supporting tools, like prometheus, provide a feature for accessing the registered services. Moreover, many consul client from other programming language, provide libraries to register and retrieve the services. So we can have many options to deal with, when we want to build a new micro service.
That’s all from me right now. Have a good day.