无法从EMR中运行的Spark应用程序中删除AWS SQS消息

时间:2016-06-29 12:32:23

标签: java amazon-web-services amazon-ec2 amazon-sqs

我正在AWS EMR集群中运行Apache Spark应用程序。应用程序从AWS SQS检索消息,根据消息数据进行一些计算,然后删除每条消息。

我在具有NAT实例的私有子网上的VPC中运行EMR群集。

我面临的问题是我无法删除邮件。我能够检索所有消息并且能够发送消息,但我无法删除它们。

我在EMR群集上使用以下安全性 EC2 instance profile:EMR_EC2_DefaultRole EMR role:EMR_DefaultRole

这些角色中的每一个都附加了以下政策: AmazonSQSFullAccessAmazonElastiCacheFullAccessAmazonElasticMapReduceFullAccessAmazonVPCFullAccess

我认为问题在于权限,但AmazonSQSFullAccess授予了完全权限,因此我没有选择权。

这是删除邮件的Java代码:

public class SQSMessageBroker
{
    private AmazonSQS _amazonSqs;

    public SQSMessageBroker()
    {
        // Create the SQS client
        createSQSClient();
    }

    public void deleteMessage(String queueUrl, String receiptHandle)
        {
            DeleteMessageRequest deleteMessageRequest = new DeleteMessageRequest(queueUrl, receiptHandle);

            _amazonSqs.deleteMessage(deleteMessageRequest);
        }

  private void createSQSClient()
    {
        _amazonSqs = new AmazonSQSClient();
        _amazonSqs.setRegion(Region.getRegion(Regions.EU_WEST_1));
    }
}

我的应用程序中SQSMessageBroker是一个单身人士。 当我在本地运行相同的代码时,一切都很好。我在本地创建了一个AWS用户,并且已经将密钥和密钥添加到.aws文件中。

修改

经过大量研究和测试后,我发现了这一点:

  1. 看来它不是权限问题(至少不是由EMR启动的EC2实例)。我连接到实例,安装了aws cli,检索了一条消息并成功删除了它。
  2. _amazonSqs.deleteMessage(deleteMessageRequest);代码不会抛出任何异常。看起来好像请求超时,但不会抛出超时异常。 deleteMessage之后的任何代码都未执行。
  3. 我在一个单独的线程中处理每条消息,所以我在每个线程中添加了Thread.UncaughtExceptionHandler,但也没有抛出任何异常。
  4. 我怀疑问题可能在ReceiptHandle,更确切地说,因为我在几台机器上运行了一个Spark群集,所以我认为机器的IP,名称或其他类似的东西都编码在{ {1}}并且ReceiptHandle可能已从其他计算机执行,因此这就是它无效的原因。这就是我用一台机器创建一个Spark集群的原因。可悲的是,我仍然无法删除该消息。

1 个答案:

答案 0 :(得分:2)

经过大量的调试和测试后,我终于找到了问题所在。

正如所料,这不是许可问题。问题是,由EMR启动并运行Spark应用程序的EC2实例包含Java的所有AWS包的某个版本(包括SQS包)。包含这些包的路径被添加到Hadoop,Yarn和Spark中。因此,当我的应用程序启动时,它使用了机器上已有的软件包并收到错误消息。 (错误记录在Yarn日志中。我花了一些时间才弄明白。)

我正在使用maven shade插件为我的应用程序构建超级jar,所以我认为我可以尝试着色(重新定位)AWS包。这将允许我将依赖项封装在我的应用程序中。可悲的是,这不行。似乎亚马逊正在使用包内的反射,并且他们对某些类的名称进行了硬编码,从而使着色无用。(在我的着色包中找不到硬编码的类)

因此,经过一些更多的挫折后,我找到了以下解决方案:

  1. 创建一个EMR步骤,将我的超级罐从S3下载到机器。
  2. 使用以下spark-submit选项创建Spark应用程序步骤:
  3. --driver-class-path /path_to_your_jar/myapp.jar --class com.myapp.startapp

    此处的关键是--driver-class-path选项。您可以阅读更多相关信息here。基本上我将我的超级jar添加到Spark驱动程序类路径中,允许应用程序使用我的依赖项。

    到目前为止,这是我找到的唯一可接受的解决方案。如果你知道另一个或更好的,请写评论或答案。

    我希望这个答案对一些不幸的灵魂有用。这会让我度过几个难以忍受的日子。