How to setup eureka clients behind reverse proxy with different port? - java

I am currently trying to setup service discovery with eureka, but the clients register with the wrong port. Every Eureka client is on a different server behind its own nginx reverse proxy and it is reachable from outside via https on port 443, but the java eureka clients are configured on different port. I also tried configuring them on the same port as nginx exposes, but than the nginx server has infinited redirects and stops after some attempts with error "invalid redirect". Being on different port the eureka clients register at the server with the port configure in spring boot yaml server port config. If then a client tries to reach another client it uses the port configured in the spring boot application. I need to be able to register the eureka client on a different port than I am running the client. Is that possible? What am I missing here? Would be using Zuul as a gateway make a difference here?
Setup (every system is deployed on a different VServer behind NGINX reverse proxy):
Eureka Server
Multiple Eureka Clients (API, UI, etc)
Identity Management Keycloak SSO server not registered with eureka server

A port can only be used by one service at a time. Hence, you cannot configure nignx and spring boot to listen on the same port and receive the error message.
Spring boot does not know about the proxy setup and hence the eureka client registers the instance (with the spring.port) at the eureka server. You can configure the eurka instance with the EurekaInstanceConfig. In this case you want to change eureka.instance.port=443 to the one exposed by nginx.

Related

How to create several instances of a Microservice: SpringBoot and Spring Cloud

I am new to the Microservices, I came across several concepts like Service Registry and Load Balancing. I have following questions-
How multiple instances of a particular microservice are created?
How Service Registry using Eureka Server helps in distributing the load on the several instances of Microservice?
In my scenario, I created 3 different microservices and registered them on my service registry-
Service Registry Configuration-
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #this will ensure that this service
#is not itself registered as a client
fetch-registry: false
Client Configuration-
eureka:
instance:
prefer-ip-address: true
client:
fetch-registry: true #true by default
register-with-eureka: true #true by default
service-url:
defaultZone: http://localhost:8761/eureka
When I stop my services I still see the services as Up and Running on Eureka and get a warning as-
Can somebody please help me find the reason for this problem?
1. To communicate with another instance, you can use Eureka to find the address of the service you want to talk to. Eureka will give you a list of all the available instances of that service, and you can choose which one you want to communicate with.
2. The Eureka server is a microservice that keeps track of the locations of other microservices within the same system. These other microservices register themselves with the Eureka server so that they can be found and contacted by other microservices when needed. The Eureka server acts as a directory for the microservices, allowing them to find and communicate with each other. (not sure if that's what you asked).
3. In order to remove the warning:
You can set a renewal threshold limit in the Eureka server's properties file.
eureka.renewalPercentThreshold=0.85
1. To scale your microservice locally, you can run multiple instances of your Spring Boot application each on a different port.
First update the port on the microservice you wish to scale in the application.yml file to:
server:
port: 0
This will start the application on a random port each time you run it.
If you run 2 applications now, you will see 1 instance of your microservice on your Eureka dashboard. This is because they both have the same Eureka instance id. To fix this you need to generate a new instance id so add the below in the same application.yml:
spring:
application:
name: "hotel-service"
eureka:
instance:
instance-id: "${spring.application.name}:${random.value}"
Finally, just run the same application more than once. You can do this on InteliJ by right clicking on the main class and selecting run and then doing that again. Some extra setup may be required to run multiple instance of the same application on IntelliJ, please see link: How do I run the same application twice in IntelliJ?
If you are using Eclipse/STS, right click on project > Run as > Spring Boot App (do this twice or more).
Alternatively if you have maven installed. Open a terminal and run mvn spring-boot:run and then open a new terminal and run the command again.
Now you should see multiple instances of your application on the Eureka dashboard.
Note: In production scaling up a microservice is done on the by the Devops team, for example a container orchestration platform such as Kubernetes can be used to increase the instances of a microservices.
2. Generally an API gateway is used to route incoming network requests to microservices and do the load balancing while service discovery allows microservices to find and communicate with each other.
With Eureka service discovery, the microservices will register on the discovery server. An API gateway will also register on the Eureka server and will do load balancing.
One method of load balancing is the round-robin strategy, where the load balancer will rotate through the available instances in a sequential order. This helps to distribute the load evenly across the instances. There are also other load balancing methods, like Least Connection, Resource Based (Adaptive) etc.
3. The error your getting is due to the self preservation mode which comes with the Eureka server. The Eureka server expects a heartbeat from microservices every 30 seconds by default, if it doesn't receive a heartbeat within 90 seconds it will de-register a microservice from it. In a case where Eureka doesn't receive heartbeat signals from many services, it will de-register each microservice up to a certain limit. Then after it will enter self preservation mode and will not de-register any more microservices and will try to re-establish a connection, this is because a network issue could result in eureka server from not receiving heartbeats.
Since you are developing locally and you stopped running your microservices, you are seeing the expected behaviour of the Eureka server entering self preservation mode, which you can ignore.
You already register the microservices in eureka server.
Just run same service [hotel-service, rating service] on different port. Eureka server check name while registering microservice if It found same name It registered microservice with different {ip-address:port} format, you can also use same approch for load balancing

Is it possible to run springboot app on port 8081 but use port 8080 as url?

I plan to run my app on port 8081 since port 8080 is used by a separate local tomcat server but users are more concerned about not changing the URL that they are used to. that url includes port 8080 since the legacy app runs on the local tomcat server. Now, would it be possible to connect the new app to port 8081 but just on the url it would still be port 8080?
You need some kind of Proxy for this.
Every Port on your computer can be listened to by one application. Though you need an application that occupies this port and then forwards the request to some other application / port.
This can be done using for example Apache Webserver or Nginx. Or you can write a simple Spring Boot application that does the job: Run (and listen) at port 8080, and then use #Controller logic (or a Filter) to either forward the requests to port 8081 or to the Tomcat port.

How to specify the statsd client port?

I have one java spring boot application deployed on pcf environment. I have used statsd client library to send metrics to the statsd server. So the problem is how do I specify host and port in statsd client so that it can send metrics.
public StatsDClient statsDClient(
#Value("${metrics.statsd.host:localhost}") String host,
#Value("${metrics.statsd.port:8125}") int port,
#Value("${metrics.prefix:example.app}") String prefix
) {
return new NonBlockingStatsDClient(prefix, host, port);
}
I have to specify the host and port for the PCF deployed app, How to do it ?
If I am understanding it correctly you have two apps running in PCF.
One Spring boot app- which you are calling some client app.
Second is your statsd server
and you need to establish a communication between these two.
As Daniel mentioned in comment you have two ways to do it.
HTTP/TCP
Internal Route
Problem with option 1 is that you may not be able to use that with host and port separately as PCF internally doing that.
I'd recommended to use the second option- Internal route which gives you clear host name and port which you need to configure in client app.
Here are the steps you can follow to configure the Container-to-Container networking
Add a new route to your server app -i.e. app1.apps.internal
Create network policy with client and server app - link
Define property in config server or code-base whatever process you are using for property externalization - Your final URL would look like: app1.apps.internal:8080

Spring boot configure websocket on a different port than http

I have a spring boot application with websocket setup via sockJs.
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("*")
.withSockJS();
}
I am deploying it in openshift which has a restriction to use port 8000 for websocket. Is it possible to configure spring to use port 8000 for ws:// and keep http:// to port 80?
What OpenShift version are you using?
If you are using OpenShift 3, then a separate port is not used for WebSockets. Any HTTP traffic, whether WebSockets or not, would be exposed outside of the cluster on port 80, or port 443 for secure connections.
Within the container itself, it is typical in OpenShift 3 to use port 8080. Again this is for any HTTP traffic, including WebSockets.

CloudFoundry websocket failed: Establishing a tunnel via proxy server failed

Note: I am not using Pivotal CF.
I have a java application deployed on CloudFoundry. I am using embedded Jetty to host my Jersey REST API. This API is by default exposed on port 8080 by cloud foundry.
My application also needs some websockets to stream data to the browser. I am using Java-WebSocket (https://github.com/TooTallNate/Java-WebSocket) for this. On my local machine, I was using port 8887 for my websocket connection. Everything worked fine.
After deploying on CloudFoundry, I can access my REST API but not my websocket. After searching a bit online, I found that websocket connections are only allowed on port 4443 (http://docs.run.pivotal.io/release-notes/)
I changed my server side to reflect this
import org.java_websocket.server.WebSocketServer;
public class MyWebSocket extends WebSocketServer {
public MyWebSocket() throws UnknownHostException {
super(new InetSocketAddress(4443));
}
#Override
public void onOpen(org.java_websocket.WebSocket websocket, ClientHandshake handshake) {
// Handle this
}
}
On my client side, I am connecting the websocket using the following
wss://my_cf_app.com:4443/
But I am getting the following exception.
WebSocket connection to 'wss://my_cf_app.com:4443/' failed:
Establishing a tunnel via proxy server failed
I also tried to connect the websocket on server side using "PORT" environment variable of CF but I get "Address already in use" error in Java-WebSocket.
I have tried many different things but I am unable to figure this out. Any help would be awesome.
After deploying on CloudFoundry, I can access my REST API but not my websocket. After searching a bit online, I found that websocket connections are only allowed on port 4443 (http://docs.run.pivotal.io/release-notes/)
Port 4443 is specific to Pivotal Web Services (and some installs of CF that run on AWS). Most PCF installs do not have a separate port for WSS, but just use 443 along with the HTTPS traffic. The port used ultimately depends on the load balancer being used in front of the CF installation and what it supports.
You would never have your application listen on port 4443. Port 4443 is the external port for traffic where the load balancer listens. This traffic will be directed to the port assigned to your application, which is $PORT (env variable).
I also tried to connect the websocket on server side using "PORT" environment variable of CF but I get "Address already in use" error in Java-WebSocket.
This is the correct behavior, i.e. you should be listening on the port assigned through $PORT env variable. What the error is telling you is that something is already listening on this port and you cannot have two things listening on the same port.
There is only one port available per application at this time (likely to change in the future). For now, if you have two separate applications listening on two separate ports then you need to push them to CF as two separate applications.
What you can do to make them appear like one application to end users is to map each one to a specific path. See the --route-path argument of cf push or docs for cf create-route.
https://docs.cloudfoundry.org/devguide/deploy-apps/routes-domains.html#create-route

Categories