dushkin
dushkin

Reputation: 2111

Spring Boot: Connection timed out when trying to call a service from a service

I have 2 microservices + an Eureka Server in which they are registerd. I made really everything I could think of, yet when I try to call the login service from the manager service, I always get "Connection timed out".

POST http://localhost:9903/login

{
    "username":"adm4",
    "password":"adm4adm4"
}

I have tried to work with Spring RestTemplate and WebClient and also Apache HttpClient. All the times, the flow reaches the post method, and I get the same result. I guess it must be some configuration issue.

I am working on localhost with all modules.

It really drives me crzay!

Please advise. I appreciate it.

The relevant info is as follows. Please tell me if you need more info.

First of all you can see that the services are registered and up:

enter image description here

Next the code:

Manager (calling) Service: (I left inside all my previous attempts commented)

@PostMapping("/login")
    public void login(@RequestBody LoginRequest loginRequest) throws Exception {

        String url = getBaseUrl("bbsim-login-service") + "/api/auth/signin";

/*         CloseableHttpClient httpclient = HttpClients.createDefault();

        try {
            HttpPost httpPost = new HttpPost(getBaseUrl("bbsim-login-service") + "/api/auth/signin");

            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("username", loginRequest.getUsername()));
            params.add(new BasicNameValuePair("password", loginRequest.getPassword()));
            httpPost.setEntity(new UrlEncodedFormEntity(params));

            CloseableHttpResponse response = httpclient.execute(httpPost);
            System.out.println(response.getStatusLine().getStatusCode()); 
        } finally {
            httpclient.close();
        }
 */
/*         HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        // Connect timeout: time is in milliseconds
        clientHttpRequestFactory.setConnectTimeout(30000);
        // Read timeout: time is in milliseconds
        clientHttpRequestFactory.setReadTimeout(30000);

        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

        HttpEntity<LoginRequest> request = new HttpEntity<>(loginRequest);
        JwtResponse res = restTemplate.postForObject(url, request, JwtResponse.class);
        System.out.println(res); 
*/

        localApiClient
                .post()
                .uri(url)
                .body(Mono.just(loginRequest), LoginRequest.class)
                .retrieve()
                .bodyToMono(JwtResponse.class)
                .block();

    }

    private String getBaseUrl(String serviceName) {

        Application application = eurekaClient.getApplication(serviceName);
        InstanceInfo instanceInfo = application.getInstances().get(0);
        String hostname = instanceInfo.getHostName();
        int port = instanceInfo.getPort();
        return "http://" + hostname + ":" + port;
    }

application.yml:

server.port: 9903

spring:
    application.name: bbsim-manager-service

eureka:
    client:
        serviceUrl:
            defaultZone: ${EUREKA_URI:http://localhost:8088/eureka}
            registryFetchIntervalSeconds: 1
            # register-with-eureka: true
            # fetch-registry: true
    instance:
        leaseRenewalIntervalInSeconds: 1

If I understand well, the request does not reach the login service at all.


Login (called) service:

@PostMapping("/signin")
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {

    Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));

    SecurityContextHolder.getContext().setAuthentication(authentication);
    String jwt = jwtUtils.generateJwtToken(authentication);
    
    UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();      
    List<String> roles = userDetails.getAuthorities().stream()
            .map(item -> item.getAuthority())
            .collect(Collectors.toList());

    return ResponseEntity.ok().body(new JwtResponse(jwt, 
                userDetails.getId(), 
                userDetails.getUsername(), 
                userDetails.getEmail(), 
            roles));
}

application.yml file:

server.port: 9902

spring:
    application:
        name: bbsim-login-service

eureka:
    client:
        serviceUrl:
            defaultZone: http://localhost:8088/eureka/
            registryFetchIntervalSeconds: 1
            instance:
                leaseRenewalIntervalInSeconds: 1

I addition, I tried the following - giving me the same results:

curl -d "@data.json" -H "Content-Type: application/json" -X POST http://localhost:9903/login

where data.json has the body contents.

Upvotes: 2

Views: 11067

Answers (2)

jccampanero
jccampanero

Reputation: 53441

This will not be a complete answer but I hope it helps you with your issue.

I think your problem could be related with a mix of the different IP address of your machine.

First, I think Eureka is exposing your services like host.docker.internal, as indicated, the logical name that references the host machine through the different docker containers, for the reason explained in this SO question.

Basically, it seems that the docker software is updating your hosts file with entries for host.docker.internal and gateway.docker.internal and Eureka probably is taking that alias as the one for the machine IP that is being advertised. Please, see the accepted answer in the aforementioned question.

When you run Spring Boot normally the underlying server (Tomcat, Jetty, Undertow) will listen for connections in the 0.0.0.0 address, i.e., in all the network interfaces available, including localhost. This is what troubles me, because as indicated, the service should be accessible through all the IPs in the machine.

In any way, I think you can try several things to solve your issue.

Probably the best approach to solve the problem will be to configure the hostname of your Eureka server and/or your Eureka clients to a common one.

For example, you can configure your server and clients to be exposed as localhost.

For that purpose, you need to include the following configuration property in their respective config files:

eureka:
  instance:
    hostname: localhost

Upvotes: 1

nicholasnet
nicholasnet

Reputation: 2287

Looks like you are using Docker. You are trying to connect to localhost but other services are running in other container hence localhost won’t work. Would you please try 0.0.0.0 or host.docker.internal in your YAML file and see if that will work.

In other words you will need to edit following.

server.port: 9903
    
    spring:
        application.name: bbsim-manager-service
    
    eureka:
        client:
            serviceUrl:
                defaultZone: ${EUREKA_URI:http://host.docker.internal:8088/eureka}
                registryFetchIntervalSeconds: 1
                # register-with-eureka: true
                # fetch-registry: true
        instance:
            leaseRenewalIntervalInSeconds: 1

or change EUREKA_URI env variable to reflect that. Also in your service YAML

    server.port: 9902
    
    spring:
        application:
            name: bbsim-login-service
    
    eureka:
        client:
            serviceUrl:
                defaultZone: ${EUREKA_URI:http://host.docker.internal:8088/eureka/}
                registryFetchIntervalSeconds: 1
                instance:
                    leaseRenewalIntervalInSeconds: 1

Upvotes: 1

Related Questions