Netflix / eureka

AWS Service registry for resilient mid-tier load balancing and failover.
Apache License 2.0
12.44k stars 3.75k forks source link

Getting http code status 400 when registring service manually with EurekaHttpClient #1414

Open softreaver opened 3 years ago

softreaver commented 3 years ago

First things first : HERE is a minimal repo to reproduce the error.

And here is what I posted earlier on StackOverflow but no solution has been proposed :

I am developing a wrapper to allow a microservice developed in a language other than Java to register with the Eureka discovery service.

To do that I am using the object com.netflix.discovery.shared.transport.EurekaHttpClient to perform HTTP requests manually against the Eureka server. For some reasons it respondes with HTTP Status code 400 (Bad request) when I try to register a service.

I analyzed HTTP requests with WireShark to see if I could find any difference and they seem to be identical : frame capture

On the left, the request made manually (which does not work) and on the right, the request made automatically (which works). On the picture, you can see that the JSON structures are identical.

The source code

@Service
public class ServiceHandler {
    final String serviceUrl = "http://localhost:8761/eureka/";
    private EurekaHttpClient eurekaHttpClient;

    @PostConstruct
    public void init() {
        TransportClientFactory factory = new RestTemplateTransportClientFactory();
        EurekaEndpoint defaultEndpoint = new DefaultEndpoint(serviceUrl);
        this.eurekaHttpClient = factory.newClient(defaultEndpoint);
    }

    public void register(ServiceInfo serviceInfo) {

        String instanceID = new StringBuilder()
            .append("service-register:")
            .append(serviceInfo.getAppName())
            .append(":")
            .append(serviceInfo.getPortNumber())
            .toString();

        String homeURL = new StringBuilder()
            .append("http://")
            .append(serviceInfo.getIPAddress())
            .append(":")
            .append(serviceInfo.getPortNumber())
            .append("/")
            .toString();

        InstanceInfo info = InstanceInfo.Builder.newBuilder()
            .setAppGroupName(serviceInfo.getGoupName())
            .setAppName(serviceInfo.getAppName())
            .setIPAddr(serviceInfo.getIPAddress())
            .setPort(serviceInfo.getPortNumber())
            .setInstanceId(instanceID)
            .setHostName("Delphine.home")
            .setHomePageUrlForDeser(homeURL)
            .setHealthCheckUrlsForDeser("/actuator/health", homeURL + "/actuator/health")
            .setDataCenterInfo(null)
            .setStatusPageUrlForDeser(homeURL + "/actuator/info")
            .setSecurePort(443)
            .build();

        EurekaHttpResponse<Void> httpResp = this.eurekaHttpClient.register(info);

        // Prints 400
        System.out.println(httpResp.getStatusCode());
    }
}

ServiceInfo is a simple model that contains the appName, port and IP

I can't figure out why the HTTP request is considered malformed.

Note : I tried the solution suggested here

Note 2 : I am using the version 3.0.0 of netflix eureka client : netflix eureka client version

troshko111 commented 3 years ago

I can try to help you here, there's nothing special the Java client does to register, I suggest you register using a curl to begin with so you really narrow it down. Post this (replace the values with what you need to post):

{
  "instance": {
    "instanceId": "$id",
    "app": "$app",
    "ipAddr": "$addr",
    "hostName": "$hostname",
    "status": "$status",
    "overriddenStatus": "UNKNOWN",
    "port": {
      "$": 8080,
      "@enabled": "true"
    },
    "securePort": {
      "$": 8443,
      "@enabled": "true"
    },
    "countryId": 1,
    "dataCenterInfo": {
      "@class": "com.netflix.appinfo.AmazonInfo",
      "name": "Amazon",
      "metadata": {
        "vpc-id": "$vpc",
        "ami-id": "$ami",
        "mac": "$mac",
        "accountId": "$acc",
        "instance-id": "$id",
        "instance-type": "$instance_type",
        "local-hostname": "$hostname",
        "local-ipv4": "$addr",
        "availability-zone": "$zone"
      }
    },
    "leaseInfo": {
      "renewalIntervalInSecs": 30,
      "durationInSecs": 90,
      "registrationTimestamp": 0,
      "lastRenewalTimestamp": 0,
      "evictionTimestamp": 0,
      "serviceUpTimestamp": 0
    },
    "metadata": {
      "ec2InstanceId": "$id",
      "imageId": "$img"
    },
    "appGroupName": "UNKNOWN",
    "homePageUrl": "$homepage",
    "statusPageUrl": "$statuspage",
    "healthCheckUrl": "$hc",
    "secureHealthCheckUrl": "$shc",
    "secureVipAddress": "$svip",
    "vipAddress": "$vip",
    "isCoordinatingDiscoveryServer": "false",
    "lastUpdatedTimestamp": "$LAST_REGISTRATION",
    "lastDirtyTimestamp": "$LAST_REGISTRATION",
    "asgName": "$asg"
  }
}

To your Eureka server using these options:

-X POST -H "Content-Type: application/json" $EUREKA_ADDR/eureka/v2/apps/$app

This should work fine. Also you crank up the logging on Eureka server to see why you're getting 400 in the first place.

softreaver commented 3 years ago

Hi @troshko111 Sorry for the late response. I just tried the curl command, and it is not working. I get HTTP 404 error :

image The data.discovery is your given JSON with some arbitrary values.

After activating the verbose mode for Spring I found this wich is certainly the reason of the 404 reponse : image

Do I need to activate the REST API routes on the Eureka server ?

I found this page very interresting.

troshko111 commented 3 years ago

You don't need to activate anything, your bash (assuming this is bash) does not do what you think it does, I suggest going over some basic tutorial like https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_03.html.

'`cat data.discovery`' will produce text `cat data.discovery` which is 20 bytes in size, that's why your curl output says it uploaded 20 bytes, data.discovery clearly is more than 20 characters.

softreaver commented 3 years ago

You're right, that was actually my second attempt (not very clever, I forgot the single quote effect :o). My first try was with double quotes but it was the same dead end : 404 HTTP error.

image

image

troshko111 commented 3 years ago

This is not a response from Eureka as far as I can tell, if you have debug logging on the first Eureka log would be

logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);

and then it'd return a 400 for any bad input. I have no idea how you have it setup but that is the part likely to have an issue.

vitoscili commented 1 year ago

Hi @softreaver did you solve the problem related to eurekahttpclient?