Notes
Notes
'application.properties'. One in GIT and the other one present locally. Which one will take
precedence?
To ensure that the properties file from GIT takes higher priority, it needs to be accessed first
before the local properties file for which the bootstrap context is used.
Spring Cloud creates a parent context to the spring application context called the ‘bootstrap’
context. This context takes precedence over the application context. This context is responsible
for loading configuration details from an external source. Both these contexts share the Spring
Environment, thus making configuration usage seamless.
Since the bootstrap context takes precedence the URI of the config-server must be mentioned
in bootstrap.properties/YAML for the clients.
Note:
In the recent releases of Spring Boot versions >2.4.x, the bootstrap context initialization
using property sources like bootstrap.properties/bootstrap.yml is deprecated.
As the demos are using Spring Boot 2.5.3 version we can add these configuration data to
the application.properties file itself. No need for additional bootstrap.properties.
Also, the property to be added in the microservices is modified in recent releases
as spring.config.import=optional:configserver:http://localhost:1111. Refer to the
downloaded demos for the complete code.
To connect to the Infygithub/Github instead of the password we can also use the
Personal Access Token. The access token can be generated from -> Settings ->
Developer Settings -> Personal Access Tokens
Select the scope configurations based on the requirement and generate the token:
Spring uses Environment to get the configuration details from various sources such as the
environment variables, properties files, YAML files, etc. These are built in property sources.
When we use cloud config, it adds an additional property source to the Environment such that
it takes the properties from the cloud config server.
When cloud config is used, the additional property source takes a higher priority. It means
that, any duplicate properties in other property sources are ignored.
We can access the configuration files by using endpoint on the config server in any one of the
below patterns:
1. http://<config_server_host>:<port>:/<application>-<profile>.yml
2. http://<config_server_host>:<port>/<application>-
<profile>.properties
3.
The properties file used in the GIT must have the same name as that of the
client's spring.application.name
We can have multiple profiles as well and if a profile is not mentioned it will load the default
profile, which is the same name as that of the spring.application.name.
If we try to access a microservice property for a given profile, the order of files used will be:
3. application.yml file
4. application.properties file
For example, if we have a property called x=10 in application.yml and x=20 in the
application.properties, the final value used will be 10
The config-server is contacted by the clients only once, during the start of the project. Therefore
any changes made to the configuration after the application starts will not be reflected in the
application.
We can also configure the clients retry attempts to contact the config server using properties like
If a port is not specified for the config server, it runs in its default port 8888. Also, if the Cloud-
Config server is down, then the client will throw an error not during startup, but while trying to
access the property at runtime. To avoid this we can have the failFast property set to true. By
this, the client will fail at startup time rather than at the run time.
1. spring.cloud.config.failFast=true
2.
Also, in order to avoid Config-server to be a single point of failure, we usually deploy multiple
instances of it to ensure high availability. If the cloud config server is unavailable, it will use the
properties files in the individual applications as a fallback
When we make any changes to the properties file in GIT, the config-clients automatically do not
update themselves with the modified values. This is because the configurations are taken
only once at the time of startup. For example, if we modify the property of CustomerMS, we
have to restart CustomerMS so that it can again fetch the properties from the config server.
However, this is not a practical approach.
When we make any changes to the properties file in GIT, the config-clients automatically do not
update themselves with the modified values. This is because the configurations are taken
only once at the time of startup. To overcome this we need to :
1. <dependency>
2. <groupId>org.springframework.boot</groupId>
3. <artifactId>spring-boot-starter-actuator</artifactId>
4. </dependency>
5.
In order to access the actuator endpoints using HTTP, we need to both enable and
expose them by adding the below property in the relevant microservices:
1. management.endpoints.web.exposure.include=*
Send a POST request to the /refresh endpoint of the service. This refreshes the
microservice without restarting/redeploying it.
The problem with using the /refresh endpoint is that we have to manually fire a POST request to
it. It is not automatically done when the configuration changes. Also, we need to fire a request
to /refresh on all the services which might get affected by the change in the property. That means
we have to keep track of which property is used in which application. If we have 100
microservices using the property, we need to fire /refresh on all those microservices.
The solution is to use Spring-Cloud-Bus. This, along with a Queuing service like RabbitMQ, will
trigger refresh events on all dependent microservices. However, Spring-Cloud-Bus is beyond the
scope of the curriculum.
Since this uses the Bootstrap context, we need to use bootstrap.properties file
The config clients contact the config server at startup to gather the configurations
1. <dependency>
2. <groupId>org.springframework.cloud</groupId>
3. <artifactId>spring-cloud-starter-netflix-
ribbon</artifactId>
4. </dependency>
1. @Bean @LoadBalanced
2. public RestTemplate restTemplate() {
3. return new RestTemplate();
4. }
1. custribbon.ribbon.eureka.enabled=false
2. custribbon.ribbon.listOfServers=http://localhost:8001,http://
localhost:8002
1. List<Long> friends =
template.getForObject("http://custribbon/customers/" +
phoneNo+"/friends", List.class);
7. Run the application, with the two instances of FriendFamilyMS running in two different
ports
Notes:
If you want to start an instance of Friend FamilyMS on a different port use the following
command after the maven build of your project is done.
target/<<jar name>>-exec.jar
By default, the ribbon uses the NoOpPing strategy for checking if the services are up. However,
NoOpPing is a dummy strategy. It assumes that all services are up. Thus it will keep pinging the
services even if they are down. We can configure the Ping strategy so that we stop sending
requests to services that are down.
Also, Ribbon by default uses the Round Robin load balancing strategy.
Note: In recent releases by default, the IPing strategy is used. But in a similar way, we can
configure any strategy that is needed.
Ribbon can be configured as:
1. <clientName>.<nameSpace>.<propertyName>=<value>
For example:
1. custribbon.default.NFLoadBalancerRuleClassName=com.netflix.loadba
lancer.RandomRule
In the third stage of the application, we will register our microservices with a Eureka Service
Discovery server. The details of the Eureka are also stored in the GIT repo which can be
accessed using the ConfigServer
1. Create a Spring Starter project with the name infytel-eureka
1.
2. <dependencyManagement>
3. <dependencies>
4. <dependency>
5. <groupId>org.springframework.cloud</groupId>
6. <artifactId>spring-cloud-
dependencies</artifactId>
7. <version>Greenwich.RELEASE</version>
8. <type>pom</type>
9. <scope>import</scope>
10. </dependency>
11. </dependencies>
12. </dependencyManagement>
13.
14.
15. <dependency>
16.
<groupId>org.springframework.cloud</groupId>
17. <artifactId>spring-cloud-starter-
netflix-eureka-server</artifactId>
18. </dependency>
1.
2. spring.application.name=Eureka1
3. server.port=5555
4. eureka.client.fetch-registry=false
5. eureka.client.register-with-eureka=false
6. eureka.client.service-url.defaultZone=http://
localhost:5555/eureka
1.
2. <dependency>
3.
<groupId>org.springframework.cloud</groupId>
4. <artifactId>spring-cloud-starter-netflix-
eureka-client</artifactId>
5. </dependency>
6. Add @EnableDiscoveryClient in all microservices application file
1. eureka.client.service-url.defaultZone=http://
localhost:5555/eureka
9. Remove the String friendUri; from CustomerController and update the code in accessing
the friend-family-service as:
1.
2. List<ServiceInstance>
instances=client.getInstances("FriendFamilyMS");
3. ServiceInstance instance=instances.get(0);
Note: The service discovery happens through the spring.application.name value of the
services. Hence they should not change.
register-with-eureka property when set to true ( which is by default ), will register an application
with the Eureka Sever. Such an application is also called a Eureka Instance. The Eureka
Instance will start sending heartbeats to the Eureka Server. If the Eureka server does not receive
heartbeats from an Instance within a configurable time limit ( every 30 secs by default), it
considers the Instance to be down and deregisters it from the registry.
The fetch-registry property will fetch the registry from the Eureka Sever once at startup time and
will cache it. It will check the Eureka Server at regular intervals ( by default at every 30 secs) to
see if there are any changes. If there are changes, it fetches only the updates and the
unchanged parts will be continued to be accessed from the cache.
A Eureka server is also a client. Because it can register itself with other Eureka servers and form
a cluster.
Every client has to register itself with Eureka and it will also try to fetch the registry details. If we
want to run only a single Eureka Server instance, then these two properties( fetch-registry,
register-with-eureka) should be false. Otherwise, it is trying to register itself with itself.
Every registered client gets access to what is known as a Discovery client. The Discovery client
is actually a service endpoint, which returns an enum of all ServiceInstance instances of the
clients registered with the Service Registry.
You can take a look at all the instances registered with the service registry using this endpoint as
follows:
http://{eureka-host}:{eureka-port}/{eureka}/apps/{spring-application-name}
In a cloud environment, we cannot predict the host and port of different microservices.
By using the service discovery pattern, we can dynamically find the services registered
Ribbon is typically used along with Eureka. Earlier we had seen how we can use Ribbon
with a static list of servers. Instead of using a static list, we can get a dynamic list of
servers by using it with Eureka.
When used with Eureka, not only will Ribbon get the server list, but it also will depend on
Eureka to know if a service is up or not.
3. Access the PlanMS and FriendMS through the rest template object and use the names of
the service instead of the URI. Since Eureka is used, the URI will be picked automatically
based on the service name.
1. PlanDTO
planDTO=template.getForObject("http://PLANMS"+"/plans/"+custDTO.g
etCurrentPlan().getPlanId(), PlanDTO.class);
2. List<Long>
friends=template.getForObject("http://FRIENDFAMILYMS"+"/customers
/"+phoneNo+"/friends", List.class);
Eureka is rarely run as a single instance, as it would become a single point of failure. Typically
we run multiple instances of Eureka forming a cluster. In a cluster, each Eureka server replicates
the information in the other servers.
1. 127.0.0.1 Eur1
2. 127.0.0.1 Eur2
3. 127.0.0.1 Eur3
4.
3. Use the below yml file in the infytel-eureka server
1.
2. spring:
3. profiles: Eureka1
4. application:
5. name: Eureka
6. server:
7. port: 2222
8. eureka:
9. instance:
10. hostname: Eur1
11. client:
12. registerWithEureka: true
13. fetchRegistry: true
14. serviceUrl:
15. defaultZone:
http://Eur2:2223/eureka/,http://Eur3:2224/eureka/
16.
17. ---
18. spring:
19. profiles: Eureka2
20. application:
21. name: Eureka
22. server:
23. port: 2223
24. eureka:
25. instance:
26. hostname: Eur2
27. client:
28. registerWithEureka: true
29. fetchRegistry: true
30. serviceUrl:
31. defaultZone:
http://Eur1:2222/eureka/,http://Eur3:2224/eureka/
32.
33. ---
34. spring:
35. profiles: Eureka3
36. application:
37. name: Eureka
38. server:
39. port: 2224
40. eureka:
41. instance:
42. hostname: Eur3
43. client:
44. registerWithEureka: true
45. fetchRegistry: true
46. serviceUrl:
47. defaultZone:
http://Eur1:2222/eureka/,http://Eur2:2223/eureka/
3. Comma separated values in the defaultZone indicate peer awareness. Eureka1, Eureka2
,and Eureka3 are peers of each other and hence will replicate the details across each
other.
4. Update the application.properties file in GIT for the below property:
1. eureka.client.service-url.defaultZone=http://Eur1:2222/
eureka,http://Eur2:2223/eureka,http://Eur3:2224/eureka
5. Run all the three profiles of the Eureka server and restart all the microservices
6. You will get three dashboards in three different Eureka ports. Since we have a cluster,
each dashboard will have the same details of microservices as the other two Eureka
Servers in the cluster.
7. Bring down a microservice. You will find that since each Eureka server in a cluster
replicates itself, all Eureka servers in the cluster will now have the same updated
information.