softwaremill / elasticmq

In-memory message queue with an Amazon SQS-compatible interface. Runs stand-alone or embedded.
https://softwaremill.com/open-source/
Apache License 2.0
2.52k stars 193 forks source link

SQS queue url seems to hardcoded to 9324 #33

Closed stevenzwu closed 8 years ago

stevenzwu commented 9 years ago

this will cause connection error when trying to use AmazonSQSClient to send msg later, because it tries to connect to port 9324 instead of the correct ephemeral port 59923.

here is the output from the test code below.

16:29:30,699  INFO main TheSQSRestServerBuilder:158 - Started SQS rest server, bind address localhost:0, visible server address http://localhost:9324
[INFO] [12/22/2014 16:29:31.121] [elasticmq-akka.actor.default-dispatcher-4] [akka://elasticmq/user/IO-HTTP/listener-0] Bound to localhost/127.0.0.1:0
actual port bound: 59923
16:29:31,741  INFO elasticmq-akka.actor.default-dispatcher-4 QueueManagerActor:25 - Creating queue QueueData(foo,MillisVisibilityTimeout(30000),PT0S,PT0S,2014-12-22T16:29:31.726-08:00,2014-12-22T16:29:31.726-08:00)
queueUrl: http://localhost:9324/queue/foo

here is the complete test code to reproduce the issue

package com.netflix.schlep.sqs;

import akka.io.Tcp;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.ListQueuesResult;
import com.netflix.schlep.sqs.aws.FixedAWSCredentialsProvider;
import org.elasticmq.rest.sqs.SQSRestServer;
import org.elasticmq.rest.sqs.SQSRestServerBuilder;
import org.junit.Assert;
import org.junit.Test;
import scala.util.Try;

import java.util.List;

public class ElasticMQPortTest {

    @Test
    public void portZeroTest() throws Exception {
        final String hostname = "localhost";
        int port = 0;
        SQSRestServer sqsServer = SQSRestServerBuilder
                .withInterface(hostname)
                .withPort(port)
                .start();
        sqsServer.waitUntilStarted();
        Try<Object> tryVal = sqsServer.startFuture().value().get();
        if(tryVal.isSuccess()) {
            Tcp.Bound bound = (Tcp.Bound) tryVal.get();
            // extract the actual port number bound
            port = bound.localAddress().getPort();
        } else {
            throw new RuntimeException("startup failed");
        }

        final String endpoint = String.format("http://%s:%d", hostname, port);
        AmazonSQSClient sqsClient = new AmazonSQSClient(new FixedAWSCredentialsProvider("fakeAccessId", "fakeSecretKey"));
        sqsClient.setEndpoint(endpoint);

        final String queueName = "foo";
        sqsClient.createQueue(queueName);
        ListQueuesResult listQueuesResult = sqsClient.listQueues(queueName);
        List<String> queueUrls = listQueuesResult.getQueueUrls();
        Assert.assertEquals(1, queueUrls.size());
        final String queueUrl = queueUrls.get(0);
        System.out.println("queueUrl: " + queueUrl);
    }
}
adamw commented 9 years ago

You need to also specify the nodeAddress with .withServerAddress(new NodeAddress("http", "localhost", 9324, "")). This is the address that is reported in when creating the queue url (it may be different then the bound one because of network setup).

However this is still inconvenient, when using a custom port, the server address should be changed as well, unless a custom one is provided. So that's a thing to fix.

stevenzwu commented 9 years ago

yes. Because I want to let system choose a ephemeral port, I passed port 0 to withPort(...) method. I can't really construct NodeAddress with port 0 to withServerAddress(...) method. I would expect server address to be changed to the actual bound port (e.g. 59923).

conniec commented 9 years ago

is there a difference between using withServerAddress and withInterface with withPort? ie. are they setting up the same thing?

adamw commented 9 years ago

No; withInterface and withPort specify to what interface/port the socket will bind. withServerAddress is the address that will be returned e.g. in the queue url strings. These are usually the same, but may differ if e.g. the server is behind a NAT, in a docker container etc.

markhatton commented 9 years ago

@adamw this caught me out as well and I almost abandoned ElasticMQ as a result.

Since it is the common case that the bound and node addresses are equal, wouldn't it make sense if they were permitted to differ by exception only? Otherwise the API violates the principle of least surprise.

adamw commented 9 years ago

Yes I definitely want to take care of this in the next release, once I'll have some more time :)

adamw commented 8 years ago

I'm going to close this issue; although confusing, I did not find a good way to automatically set node-address basing on bind address. Even the defaults (bind: 0.0.0.0, node-address host: localhost) are different, plus ElasticMQ can be configured through code or config file.

I added a note in the readme, close to the top, hopefully people will spot that :)

sfanish commented 6 years ago

I have started elasticMQ server by using "java -jar elasticmq-server-0.13.8.jar" command and below response i got.

12:04:09.476 [main] INFO org.elasticmq.server.Main$ - Starting ElasticMQ server (0.13.8) ... 12:04:10.265 [elasticmq-akka.actor.default-dispatcher-3] INFO akka.event.slf4j. Slf4jLogger - Slf4jLogger started 12:04:11.555 [elasticmq-akka.actor.default-dispatcher-3] INFO o.e.rest.sqs.TheS QSRestServerBuilder - Started SQS rest server, bind address 0.0.0.0:9324, visibl e server address http://localhost:9324 12:04:11.555 [main] INFO org.elasticmq.server.Main$ - === ElasticMQ server (0.1 3.8) started in 2628 ms === 12:19:12.784 [elasticmq-akka.actor.default-dispatcher-13] INFO o.elasticmq.acto r.QueueManagerActor - Creating queue QueueData(SQS_QUEUE_NAME,MillisVisibilityTi meout(30000),PT60S,PT0S,2017-12-28T12:19:12.618+05:30,2017-12-28T12:19:12.618+05 :30,None,None)

But when i tried to access http://localhost:9324, it is displaying blank page with "The requested resource could not be found." message. Is something wrong with my SQS server ?

adamw commented 6 years ago

@sfanish no, there's no http interface, so you shouldn't expect to be able to open the page in the browser. Instead, that address exposes a number of REST endpoints which can be accessed using an AWS client - and specifying http://localhost:9324 as the endpoint, instead of the AWS one.