【问题标题】:Why am I getting "OpenSSL::SSL::SSLError: SSL_read: sslv3 alert bad record mac" intermittently from Heroku Redis?为什么我从 Heroku Redis 间歇性地收到“OpenSSL::SSL::SSLError: SSL_read: sslv3 alert bad record mac”?
【发布时间】:2018-10-18 02:25:23
【问题描述】:

自从从 Redis To Go 切换到 Heroku Redis 后,我们的 Ruby on Rails 应用程序中的 Redis 代码每天都会出现几次“OpenSSL::SSL::SSLError: SSL_read: sslv3 alert bad record mac”错误。

有什么想法吗?

【问题讨论】:

  • 为什么投反对票?

标签: heroku redis heroku-redis


【解决方案1】:

它并没有直接解决问题,但这是 Heroku 支持不得不说的:

这个问题一直是一个难以诊断的问题,因为它的频率低且不一致。我们有很多关于它发生的报告,涵盖目的地、应用程序语言、Heroku Dyno 配置以及许多其他细节。这个问题已经被工程师记录下来并且正在被工程师诊断,但是细节再次让我们几乎不可能这样做。

此外,我们没有管理传出连接的基础架构。我们确实有原始形式的网络使用信息(传输的字节数、数据包数量等),但与让 Heroku 路由器处理请求的传入连接不同,出站连接都是我们的基础设施提供商提供的“标准”连接。没有处理出站连接的 Heroku 特定的基础设施。唯一感兴趣的是 Dynos 使用的虚拟接口,以及 Dyno 主机的网络配置,但同样没有什么特别之处。它使用基础设施平台提供的主机通信所需的网络配置。

到目前为止,我本人和工程师都没有对这些问题提出简明的答案,鉴于它们的不一致,我们目前的建议是通过连接错误处理、根据需要记录和重试来更好地处理这些问题。

如果您有关于此错误发生方式的一致可重现方式的详细信息,那将对我们有很大帮助。

【讨论】:

    【解决方案2】:

    我通过重试这些错误在 Ruby on Rails 应用程序中解决了这个问题。到目前为止,它似乎已经摆脱了错误。我们过去每天大约有 5 个,而在过去的一天——自从引入了这个解决方法——就没有了。

    config/initializers/redis_heroku_error_handling.rb:

    # Hack to retry errors on Heroku Redis: https://stackoverflow.com/questions/50228454/why-am-i-getting-opensslsslsslerror-ssl-read-sslv3-alert-bad-record-mac
    
    raise "Ensure this hack still works!" if Redis::VERSION != "3.3.5"
    
    module RedisHerokuErrorHandlingHack
      def call(command)
        attempts_left = 3
    
        begin
          super
        rescue OpenSSL::SSL::SSLError => e
          raise unless e.message.include?("SSL_read: sslv3 alert bad record mac")
    
          attempts_left -= 1
          raise unless attempts_left > 0
    
          retry
        end
      end
    end
    
    class Redis::Client
      prepend RedisHerokuErrorHandlingHack
    end
    

    spec/initializers/redis_heroku_error_handling_spec.rb:

    require "rails_helper"
    
    describe RedisHerokuErrorHandlingHack do
      it "retries 'bad record mac' errors" do
        exception = OpenSSL::SSL::SSLError.new("SSL_read: sslv3 alert bad record mac")
        fail_then_maybe_succeed(exception: exception, fail_times: 2)
    
        expect($redis.get("hello")).to eq("success response")
      end
    
      it "fails if a few retries didn't help" do
        exception = OpenSSL::SSL::SSLError.new("SSL_read: sslv3 alert bad record mac")
        fail_then_maybe_succeed(exception: exception, fail_times: 3)
    
        expect { $redis.get("hello") }.to raise_error(/bad record mac/)
      end
    
      it "does not retry other errors" do
        fail_then_maybe_succeed(exception: "Boom", fail_times: 2)
    
        expect { $redis.get("hello") }.to raise_error("Boom")
      end
    
      private
    
      def fail_then_maybe_succeed(exception:, fail_times:)
        attempt_count = 0
    
        allow_any_instance_of(Redis::Client).to receive(:process) do |*args|
          attempt_count += 1
    
          if attempt_count <= fail_times
            raise exception
          else
            "success response"
          end
        end
      end
    end
    

    【讨论】:

      【解决方案3】:

      我相信您遇到了多处理问题,其中分叉进程关闭了父进程的 Redis 连接。我刚刚发现了一个错误,导致 resque 中出现同样的错误。

      https://github.com/resque/resque/pull/1739

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-27
        • 1970-01-01
        • 1970-01-01
        • 2013-05-15
        • 2014-11-06
        • 1970-01-01
        • 1970-01-01
        • 2012-03-06
        相关资源
        最近更新 更多