Service Discovery With Eureka and Zuul
Learn how to use Spring Boot with Zuul and Eureka to create a simple discovery service, routing .NET applications through a Zuul gateway.
Join the DZone community and get the full member experience.
Join For FreeOne issue that we face day-to-day as developers is the speed of development. One of the coolest things to me in the ever-changing landscape of technology is how this issue is continually being addressed in an effort to makes our lives easier.
We have gone from having to write everything needed in a verbose way to being able to configure a simple REST API in a few lines of code with Spring Boot. The most amazing part of this is not only the ability to create the web services but also the ability to allow these services to communicate in a smart way. Spring has given us many tools to allow easy configuration and putting together things that just work (mostly).
This post is not to be considered a full guide to which the extent of these technologies can be leveraged. In this post, we give examples of how Spring Boot can be used (along with Zuul and Eureka) to create a simple discovery service.
There are other components that can be added for things like a configuration server to pull all application.properties
files from a common location that is updatable in real time, or circuit breakers to allow the graceful failing of different pieces of your API.
What this post will focus on is the service discovery between Spring Boot applications. We will also touch on how, using SteeltoeOSS, .NET applications can also take advantage of being a part of the service discovery and be routed through our Spring Boot-based Zuul Gateway. We will also look at how we can integrate Spring Security into our gateway to secure the entire API no matter the language.
Netflix Zuul
As an edge service, Zuul provides a lot of different functions. The main pieces we will talk about are the dynamic proxy and security.
Zuul will serve as our API gateway. This, along with the service discovery of Eureka, makes setting up new services a breeze. We will demonstrate a small Zuul application. It will register with a Eureka server and automatically set up dynamic routing based on other services that are also registered with Eureka to provide access to our APIs through one singular point. Zuul will also be configured with Spring Security in order to provide edge security across all our APIs.
Netflix Eureka
Eureka has a server and client component. A service registry with Eureka Server and a Discovery client with Eureka Client. We will set up a simple Eureka Server and multiple clients to register with this server to be available through our gateway.
SteeltoeOSS
Steeltoe is a library for .NET that lets us interact with the Netflix services as one unit. It has many functions leveraging Eureka, Hystrix, as well as Spring Cloud Config. We will demonstrate a small .NET application that will register itself with our service discovery server and be available through our Spring Boot Zuul gateway.
Spring Boot and Spring Security
Along with the rest of the amazing things that we are working with today, Spring Boot is at the heart of all of this. Spring makes setting up these services with minimal code very quick and painless. Spring Security goes hand in hand with Spring and makes setting up security easy and painless.
The little piece of security that we talk about here doesn't even come close to scratching the surface of what can be done. We are simply adding a dependency and a Bean that will give us an in-memory user to log in. This can be configured any number of ways, from the traditional database authentication to using OAuth2 or JWT. It even has integrations with popular services such as Facebook and Google.
Meat and Potatoes
Please see the companion repository here.
For this example, we have three Spring Boot applications and one .NET core application. All of these applications are very small and have the barebones configuration to get us started.
Eureka Server
Add the Eureka Server dependency to the Spring Boot pom.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
The application.properties
file for the Eureka server tells the server to not register itself. It also gives our application a name and a specific port.
spring.application.name = eureka - service
server.port = $ {
PORT: 8761
}
eureka.client.register - with - eureka = false
eureka.client.fetch - registry = false
EurekaExampleApplication.java
@SpringBootApplication
@EnableEurekaServer // This is the only annotation we need for this to become a Server
public class EurekaExampleApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaExampleApplication.class, args);
}
}
Zuul API Gateway
Zuul has been configured to be a Eureka Client and has been set up with Spring Security.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
The application.properties
file is pretty basic with just two properties.
spring.application.name = zuul - gateway
server.port = $ {
PORT: 9999
}
ZuulGatewayExampleApplication.java
Here we add a couple of annotations so that Zuul will act as a proxy for any auto-discovered services. We will also add it as a client. I'm also throwing in a default in-memory user that we can use to login to demonstrate the Spring Security portion.
By default, when you add the Spring Security dependency, the application locks down all endpoints so we must log in before we can hit any API endpoint.
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulGatewayExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayExampleApplication.class, args);
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Spring Boot Web Service
Again, configured to be a Eureka Client.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
The application.properties
file is just as simple as the Zuul application.
spring.application.name = spring - web - service
server.port = $ {
PORT: 8081
}
WebServiceExampleApplication.java
The only thing we add to our example API is the @EnableDiscoveryClient
to allow it to be registered in Eureka.
@SpringBootApplication
@EnableDiscoveryClient
public class WebServiceExampleApplication {
public static void main(String[] args) {
SpringApplication.run(WebServiceExampleApplication.class, args);
}
}
.NET Core Web Service
For the .NET Core Service, I have added a couple of dependencies but the overall configuration is still fairly small.
Dependency added for Discovery: Pivotal.Discovery.ClientCore (2.0.0-rc1)
.
Appsettings.json
"spring": {
"application": {
"name": "steeltoeoss-example"
}
},
"eureka": {
"client": {
"serviceUrl": "http://localhost:8761/eureka/",
"shouldRegisterWithEureka": true,
"shouldFetchRegistry": false
},
"instance": {
"port": 5000,
"isInstanceEnabledOnInit": true
}
}
Startup.cs
In the Startup.cs
, we register the Discovery Client.
namespace SteeltoeOSS_Example
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging();
services.AddDiscoveryClient(Configuration);
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
app.UseDiscoveryClient();
}
}
}
Run All the Things
Start up all the applications. Give them time to register with Eureka and give Zuul time to set up the routes. You should be able to reach all of the web services through the Zuul gateway. Depending on how your local system is configured, you might have to tweak settings to work with your localhost. By default, the .NET application uses a local hostname. If that isn't configured in your host file for some reason, Zuul will be trying to proxy to an unknown location.
To access these endpoints, you can go to http://localhost:9999/ where Zuul is running. To access a specific service, it is just as easy as adding the service name to the end with the specific route. http://localhost:9999/steeltoeoss-example/api/values for the .NET service or http://localhost:9999/spring-web-service/spring for the Spring service.
You can also go to the Eureka dashboard to see what services are registered and to get some more insight into how they are configured. I didn't go into Actuator here but adding that to your services will give you information on each service through the Eureka Server. http://localhost:8761.
Final Thoughts
For me, this is really exciting. But it is not the answer to every problem. These services have a place in development-just like the monolith still has a place. It's easy to get caught up in the new and shiny tech when we really need to step back and ask if we are using this because it's cool or because we really need it.
Microservices are all the rage, and these services we created are all about being micro. They are created in a way that lets us start and kill services indiscriminately. In conjunction with Hystrix or other circuit breakers, it makes a powerful set of tools that can be used to build a high performance and high availability system that is also secure.
One thing that really got me interested in this is the fact that I can run all of this from any environment. With .NET core it doesn't matter what kind of server you want to use. In this example, I was able to build and run everything in a Mac OS X environment, but it works just as well in Windows, or on a Linux system.
You can containerize these services in Docker to make it even easier to deploy to individual containers. This serves as a way for us to have any number of services, in a number of languages, in almost any configuration, with one common gateway.
Service discovery allows us to deploy these applications and auto-configure routing to these services without the need to bring down and reconfigure our gateway-while also providing security to all the underlying services.
Published at DZone with permission of Jarett Lear, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments