Spring Boot app with Eureka DiscoveryClient fails to start - java

I'm trying to write a simple Spring Boot application that can (1) register with a Netflix Eureka server, and (2) query the Eureka server to retrieve details of other registered services.
My client class has an #Autowired field of type com.netflix.discovery.DiscoveryClient that is used to talk to Eureka and query it to learn about other services. On my main class I have the annotation #EnableDiscoveryClient:
#SpringBootApplication
#EnableDiscoveryClient
public class AppBootstrap {
public static void main(String[] args) {
SpringApplication.run(AppBootstrap.class, args);
}
}
In my application.yml file under src/main/resources I have:
eureka:
instance:
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 20
prefer-ip-address: true
secure-port: 443
non-secure-port: 80
metadata-map:
instanceId: my-test-instance
client:
service-url:
defaultZone: http://localhost:9080/eureka/
registry-fetch-interval-seconds: 6
instance-info-replication-interval-seconds: 6
register-with-eureka: true
fetch-registry: true
heartbeat-executor-thread-pool-size: 5
eureka-service-url-poll-interval-seconds: 10
When I start my app the service fails to boot, throwing an exception that is rooted at:
Caused by: java.lang.AbstractMethodError: org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.getInstanceI
d()Ljava/lang/String;
at com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider.get(EurekaConfigBasedInstanceInfoProvider
.java:53)
at com.netflix.appinfo.ApplicationInfoManager.initComponent(ApplicationInfoManager.java:90)
... 25 more
I've no idea what's going on here. Any ideas? I believe the app should still start even if my Eureka config is incorrect, but it falls over at start time.
Secondly, am I using the correct DiscoveryClient? Ideally I'd like to make it general such that I could use it with Eureka, Consul or ZooKeeper as examples. I find the documentation isn't great at illucidating exactly what's required when using these Spring Cloud / Netflix discovery components.

You can use
org.springframework.cloud.client.discovery.DiscoveryClient
then you can get the list of instances with discoveryClient.getInstances
ServiceInstance instance = discoveryClient.getInstances(service).get(0);
instance.getUri().toString();
If you use another components like RestTemplate, Ribbon, etc you only need to use the name of the service (name registered in eureka) in the URL
restTemplate.getForObject("http://PRODUCTSMICROSERVICE/products/{id}", Product.class, id)
You can see more here
https://spring.io/blog/2015/01/20/microservice-registration-and-discovery-with-spring-cloud-and-netflix-s-eureka

I received the autowiring error in my experience when i was using discoveryclient to get the information in the class outside any function. So I was using eureka to find out the port for my service as the port was described as 0 hence service was picking up port dynamically while starting as spring boot application. I needed to know the port programmatically . In the controller i used the code like below in wrong way
public class HelloController {
private static final Logger LOG = LoggerFactory.getLogger(HelloController.class);
#Autowired
private DiscoveryClient discoveryClient;
int port = discoveryClient.getLocalServiceInstance().getPort();
#RequestMapping("/hello/{id}")
public String sayhello(#PathVariable String id)
{
String s ="A very nice and warm welcome to the world "+id;
LOG.info(String.format("calling helloservice for %s",id));
LOG.info(String.format("calling helloservice for port %d",port));
return s;
}
Once i put the port code inside the sayhello method the error went away. So the correct way of retreiving the port is as below
public class HelloController {
private static final Logger LOG = LoggerFactory.getLogger(HelloController.class);
#Autowired
private DiscoveryClient discoveryClient;
#RequestMapping("/hello/{id}")
public String sayhello(#PathVariable String id)
{
String s ="A very nice and warm welcome to the world "+id;
int port = discoveryClient.getLocalServiceInstance().getPort();
LOG.info(String.format("calling helloservice for %s",id));
LOG.info(String.format("calling helloservice for port %d",port));
return s;
}

If we are using the latest versions of Spring Boot then we wouldn't require the #EnableDiscoveryClient or #EnableEurekaClient to be defined in the main class. This happens in the background by Spring when we add the dependencies in pom.xml
Please make sure your files have the below basic informations.
pom.xml
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.0-SNAPSHOT</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.properties or YAML file as per choice
spring.application.name=eureka-client
eureka.client.service-url.defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
eureka.instance.prefer-ip-address= true
server.port= 8082
No changes or # Annotations required in the main class in Application.java
Please checkout my GIT Repository here for the working code.

add application.yml file these settings;
Our product application run at this port
server :
port : 8482
Our service will be register by own service name
spring :
application :
name : product-service
# To be register we assign eureka service url
eureka:
client:
service-url :
defaultZone:
${EUREKA_URI:http://localhost:8481/eureka} # add your port where your eureka server running
instance :
prefer-ip-address : true
# Logging file path
logging :
file :
path : target/${spring.application.name}.log

Related

How to refresh app instances using Spring cloud bus with data which isn't controlled by config server?

I'm trying to use Spring cloud bus with Kafka in my microservices application, and indeed I could use it, but only data which is controlled by Spring cloud config server got refreshed!
I'm using jdbc back-end with my config server, and in order to simulate my need, I'm changing some value in properties file in one of my services, beside the properties table, and call the /monintor end point again (mentioned here section 4.3 https://www.baeldung.com/spring-cloud-bus); as a result, only data coming from properties table is changed.
This is the yml file for my Config server
spring:
cloud:
config:
server:
jdbc:
sql: SELECT KEY,VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?
order: 1
stream:
kafka:
binder:
brokers: localhost:9092
datasource:
url: jdbc:mysql://localhost:3306/sweprofile?zeroDateTimeBehavior=convertToNull
username: 123
password: 123ertbnm
hikari:
maximum-pool-size: 10
connection-timeout: 5000
profiles:
active:
- jdbc
application:
name: configServer
These are yml files for One of my Miscroservices and its propeties file respectively
spring:
datasource:
username: 123
password: 123ertbnm
url: jdbc:mysql://localhost:3306/sweprofile?zeroDateTimeBehavior=convertToNull
jpa:
properties:
hibernate:
format_sql: true
ddl-auto: none
application:
name: auth-service
cloud:
config:
discovery:
enabled: true
service-id: configServer
bus:
refresh:
enabled: true
profiles:
active: jdbc
management:
endpoints:
web:
exposure:
include: ["health","info","refresh", "bus-refresh"]
# This line is dummy data for testing purpose
ali.man = " Ola 12333"
This is snapshot from rest controller
#RestController
#RequestMapping("/user")
#RefreshScope
public class AuthController {
private UserAuthService userAuthService;
#Value("${name}")
private String name; // changed normally
// Calling the key value mentioned in properties file after changing
#Value("${ali.man}")
private String k; // -> not changed
public AuthController(UserAuthService userAuthService) {
this.userAuthService = userAuthService;
}
#GetMapping("authTest")
public String getAuth() {
return name + k;
}
}
What did I miss? Why value from Properties file is not changed? hopefully I can use Spring cloud bus with Kafka to refresh these external data.
After some hours of investigation, I found that there is some recommended way. Cloud bus can send Refresh Event and Spring boot has RefreshEvent Listener to that event; this what I build my solution on.
So when event is sent by the bus; all instances will do the same logic ( Refreshing data ) on the loaded in memory configurations.
I used this snippet to apply this
#Configuration
public class ReloadLookupEvent implements ApplicationListener<RefreshScopeRefreshedEvent> {
#Autowired
private CacheService cacheService;
#Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
cacheService.refreshLookUp();
}
}
I could refresh all other configurations on demand, maybe it is a workaround, but acceptable.

Spring boot 2 connection to rabbitmq via Apache Camel

I have problem with connection to rabbitmq via Apache Camel on Spring Boot 2.
I did following steps:
My dependencies:
implementation "org.apache.camel:camel-spring-boot-starter:${camelVersion}"
implementation "org.apache.camel:camel-jackson-starter:${camelVersion}"
implementation "org.apache.camel:camel-core:${camelVersion}"
implementation "org.apache.camel:camel-rabbitmq-starter:${camelVersion}"
implementation "org.springframework.boot:spring-boot-starter-amqp"
Application.yaml
spring:
rabbitmq:
dynamic: true
host: 192.168.1.1
port: 5672
username: X
password: Y
And I have following route:
#Component
public class BasicRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:loggerQueue")
.id("loggerQueue")
.to("rabbitmq://TEST-QUEUE.exchange?queue=TEST-QUEUE.queue&autoDelete=false&connectionFactory=#rabbitConnectionFactory")
.end();
}
}
Finnaly I have still following issue:
2019-03-06 12:46:05.766 WARN 19464 --- [ restartedMain] o.a.c.c.rabbitmq.RabbitMQProducer : Failed to create connection. It will attempt to connect again when publishing a message.
java.net.ConnectException: Connection refused: connect
Connection seems ok, I tested it. Something is bad with rabbitConnectionFactory.
I don't know what I have bad.
The problem appears to be that RabbitMQComponent is expecting to find a connection factory of type com.rabbitmq.client.ConnectionFactory.
However, the springboot auto-configure is creating a connection factory of type org.springframework.amqp.rabbit.connection.CachingConnectionFactory.
So, whenever the RabbitMQComponent attempts to find the appropriate connection factory, because it is looking for the specific type, and because it does not subclass the rabbitmq ConnectionFactory, it returns a null value, and fails to use the appropriate host name and configuration parameters specified in your application.yml.
You should also see the following in your log if you have debug level set:
2019-12-15 17:58:53.631 DEBUG 48710 --- [ main] o.a.c.c.rabbitmq.RabbitMQComponent : Creating RabbitMQEndpoint with host null:0 and exchangeName: asterix
2019-12-15 17:58:55.927 DEBUG 48710 --- [ main] o.a.c.c.rabbitmq.RabbitMQComponent : Creating RabbitMQEndpoint with host null:0 and exchangeName: asterix-sink
EDIT:
The CachingConnectionFactory is configured with the required Rabbit connection factory as part of the autoconfiguration. However, you need to provide a link to the correct factory.
Therefore, you need to add a #Bean to disambiguate.
#Configuration
#RequiredArgsConstructor
public class CamelConfig {
private final CachingConnectionFactory rabbitConnectionFactory;
#Bean
com.rabbitmq.client.ConnectionFactory rabbitSourceConnectionFactory() {
return rabbitConnectionFactory.getRabbitConnectionFactory();
}
}
and in your endpoint configuration:
rabbitmq:asterix?connectionFactory=#rabbitSourceConnectionFactory
Note that the # is optional, as it gets stripped out within the code when it is trying to find the rabbit connection factory bean.
In your application.yml, configure the connection parameters (the url is no longer included in the endpoint URI).
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest

Spring externalize configuration yml

I am trying to configure the spring-boot application with yml and properties. I might have made mistakes at multiple places :)
I run my application as below with following contents :
java -jar /opt/elast-search-0.0.1-SNAPSHOT.jar --spring.config.location=. --spring.config.name=elast.properties,ere.yml
My YAML file looks like :
log:
count: 1
searchText: someText
services:
- um
- sa
minutesTime: 1
count: 2
searchText: someMoreText
services:
- um2
- sa2
minutesTime: 2
Without the elast.properties file and the second bean content (i.e. from count to the minutesTime section in yaml) I see the yml is loading and I am running it with :
java -jar elast-search-0.0.1-SNAPSHOT.jar --spring.config.location=ere.yml
I am running multiple properties file because I want to have a default configuration as well and the module is still evolving. I have #Autowired the class in the spring boot application.
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "log")
public class LogRule {
private int count;
private String searchText;
private String[] services;
private int minutesTime;
}
I want to read all YAML configuration as a list.
I want to read the configuration file if the file is changed or at regular intervals.

Configure Multiple Database on Spring-Boot with JPA and Hibernate Web Application

I have ONE spring boot (1.5.4.RELEASE) project using java 8 deployed on AWS HPC. This project architect scope works for Spring Web Application(Website), Rest API Services(Mobile Developer) & Account Administration for Company.
So there is 3 different respective Database like (2-SQL Server & 1-MySQL).
Here on stack-overflow, I'm posting my question for find a best way to implementation this Spring-Boot Project by help of talented stack-overflow users.
Here is my configure properties files.
application.properties
#For Public Website
spring.datasource.clone.web.driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.clone.web.url = jdbc:sqlserver://127.0.0.01\\dbo:1433;databaseName=PROD_WEB;
# Username and password
spring.datasource.web.username = web
spring.datasource.web.password = ED5FLW64ZU976Q36
#For Rest API
spring.datasource.clone.url = jdbc:mysql://localhost:3306/PROD_REST;
# Username and password
spring.datasource.clone.username = rest
spring.datasource.clone.password = Firewall77#
#For Account Administration for Company Users
spring.datasource.admin.url = jdbc:sqlserver://127.0.0.01\\dbo:1433;databaseName=PROD_ADMIN;
# Username and password
spring.datasource.admin.username = admin
spring.datasource.admin.password = Firewall77#
# Backup & Cron Policy
...
I would greatly appreciate for some very good suggestion to implement it. your knowledge on this subject would help me, Thanks.
You need to implement two different beans, one for each datasource and make them take the corresponding configuration properties respectively:
First bean will be responsible for the first datasource configuration, and should be daclared as primary datasource with #Primary, so it can be setup as the main datasource for the project.
The second bean will configure the second datasource.
This is how they should be implemented in Spring:
#Bean
#Primary
#ConfigurationProperties(prefix="spring.datasource.web")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="spring.datasource.rest")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
Here's how should be your application.properties configured, to take these two beans configuration into account:
#For Public Website
spring.datasource.web.driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.web.url = jdbc:sqlserver://127.0.0.01\\dbo:1433;databaseName= PROD_WEB;
# Username and password
spring.datasource.web.username = web
spring.datasource.web.password = ED5FLW64ZU976Q36
#For Rest API
spring.datasource.rest.driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.rest.url = jdbc:mysql://localhost:3306/PROD_REST;
# Username and password
spring.datasource.rest.username = rest
spring.datasource.rest.password = Firewall77#
Note:
I changed the configuration properties here so they can be differentiated between web and rest datasources:
Properties starting with spring.datasource.web will be dedicated to configure the first datasource.
Properties starting with spring.datasource.rest will be dedicated to configure the second datasource.
try to use DataSource configuration specified at Configuration for each data sources for more help check this Using multiple datasources with Spring Boot and Spring Data

Spring-jms. How to customize broker url for rabbitmq?

All examples I read related with activeMq and spring-boot has especial property to change the url of broker:
spring.activemq.broker-url=<SOME_URL>
By default it uses default settings: default url and default port.
But I use rabbirMq and I want to know how to change broker url
I've read this one
I've added application.properties to the src/main/resources
with following content(host absolutely wrong, I expected to see error):
spring.rabbitmq.host=olololo
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
But it doesn't affect application.
Looks like spring(boot) doesn't read these prioerties.
P.S.
Project structure looks like this:
Spring Boot does not have auto configuration support for rabbitmq-jms (the link you referenced is the native RabbitMQ AMQP auto configuration).
For the JMS connection factory, you will have to do the configuration yourself...
#Bean
public RMQConnectionFactory connectionFactory(#Value("${spring.rabbitmq.host}") String host,
#Value("${spring.rabbitmq.port}") int port) {
RMQConnectionFactory cf = new RMQConnectionFactory();
cf.setHost(host);
cf.setPort(port);
return cf;
}

Categories