Api Gateway Part 3: Zuul & Consul
This blog is a part of building scalable Spring Boot microservices based project. It comprises of creating connecting microservices using Zuul, service discovery using Consul, security using Spring Security, distributed session using Spring Session and products such as MongoDB, Redis. Here is the chronological sequence of how I wrote article. https://medium.com/@nepalBrothers/web-development-scalable-web-app-using-spring-boot-and-spring-cloud-1f9960e1d61a
Over the series, we have developed the following architecture with the following:
- an API-Gateway using Zuul
- stateful microservice Account, whoAmI and stateless microservice Product
- Able to handle Authentication by using Account service
- Distributed Session using Redis, and database as MongoDB
Show me the code
The code also contains how to run
What can we do more?
Currently, our microservice is bind with Ports. In the real world scenario, there will be multiple instances of microservice, and serving the traffic using the port will not be maintainable.
The solution to that is to call the services using the serviceId. This way, we will call the serviceId, and based on what Id is returned, we will call that IP automatically. A serviceId can represent multiple services registered to it under the same serviceId.
For instance, our Api-Gateway will call by the serviceId, and when we make a call to that serviceId, it will in turn make a call to any of those load balanced instances. This way, we can scale as much as we want.
We do so by using Consul for this case. There are alternatives as well, such as Netflix’s Eureka. However, Consul is more powerful than Eureka. There is more to Consul and we can read it in official site, but I will quickly show how I was able to integrate Consul to the designed microservices.
Make Api-Gateway use Consul
For this, we will need to add Consul dependencies in our project. Best way to pull up dependencies is Spring Initializer. We can add these dependencies to our old pom.xml file
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
Also, Annotating our Spring Boot application with EnableDiscoveryClient will make our API-Gateway discoverable by Consul.
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
class ApiGatewayApplication
Run Consul
Once Consul is installed, we can run it with the following command:
consul agent -dev -node machine
This basically tells the consul agent to start as dev and assign the name of the node to machine
Run our API Gateway
We can see this in the logs once we Run our API-Gateway.
Also, Consul provides us its UI, which comes handy to manage and see the consul. http://localhost:8500/ui/dc1/services
We can see a red cross in our apiGateway, and it is because Consul does Health Check to see if this service is up and running. Consul will only send the traffic to healthy services only.
Currently, Consul is not able to check the health. So, one way to resolve is to add spring actuator in the classpath, which will then provide endpoints for healthcheck.
Add this in pom.xml dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Modify Zuul properties so that it knows actuator healthchecks are not available for proxying to downstream services.
zuul:
ignored-patterns: /actuator/**
Lets restart and see if it resolves the issue
Awesome! We have now set our API-Gateway to use Consul.
Lets make our microservices Discoverable and have Zuul use Consul Services
Making our existing microservices discoverable with Consul is easy. We only need to add dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--for health check -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
And we just need to make the services discoverable by adding annotation:
@EnableDiscoveryClient
Also, if the old projects don’t have Spring Cloud in the pom.xml, we need to add them as well.
Just like our Product microservice did not have it, we had to modify pom.xml for that. We can add this under <project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
and
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
inside <properties> tag.
We need to do this for those services whom we want to be discoverable. This means, Account, whoAmI, Product service, as well as API-Gateway.
Lets tell Zuul to consider serviceId than uri
We just need to modify a little bit in the bootstrap.yml file for it.
zuul:
ignored-patterns: /actuator/**
routes:
product:
path: /product/**
serviceId: product
# url: http://localhost:8080
stripPrefix: true
whoAmI:
path: /whoAmI/**
serviceId: whoAmI
# url: http://localhost:8092
stripPrefix: true
sensitiveHeaders:
account:
path: /**
serviceId: account
# url: http://localhost:8091
stripPrefix: false
sensitiveHeaders:
Thats it.
Now, lets run the services. They themselves register to the consul and make them available.
Lets check
We can check them by going to http://localhost:8500/ui/
Great. We can now refer to the serviceId than the IP, which is awesome.
Lets check product endpoint:
As we can see it is working.