1

I am using django chanels for sending real time notification, on the local development server it works fine with redis url redis://127.0.0.1:6379 but when I go to production on Heroku it couldn't connect meanwhile celery works fine on the same redis url on Heroku.

this is the channel layer I am using and the REDIS_URL is set properly in the environment variables both locally and on production.

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [env('REDIS_URL', default='redis://localhost:6379')],
        },
    },
}
    

The Error I get is the following:

2023-03-12T13:15:29.051013+00:00 app[web.1]: ERROR 2023-03-12 13:15:29,047 server 2 140084942075712 Exception inside application: Error 1 connecting to ec2-50-17-163-82.compute-1.amazonaws.com:7550. 1.
2023-03-12T13:15:29.051015+00:00 app[web.1]: Traceback (most recent call last):
2023-03-12T13:15:29.051016+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/redis/asyncio/connection.py", line 603, in connect
2023-03-12T13:15:29.051016+00:00 app[web.1]: await self.retry.call_with_retry(
2023-03-12T13:15:29.051016+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/redis/asyncio/retry.py", line 59, in call_with_retry
2023-03-12T13:15:29.051017+00:00 app[web.1]: return await do()
2023-03-12T13:15:29.051017+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/redis/asyncio/connection.py", line 640, in _connect
2023-03-12T13:15:29.051017+00:00 app[web.1]: reader, writer = await asyncio.open_connection(
2023-03-12T13:15:29.051018+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/streams.py", line 52, in open_connection
2023-03-12T13:15:29.051019+00:00 app[web.1]: transport, _ = await loop.create_connection(
2023-03-12T13:15:29.051019+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/base_events.py", line 1090, in create_connection
2023-03-12T13:15:29.051019+00:00 app[web.1]: transport, protocol = await self._create_connection_transport(
2023-03-12T13:15:29.051019+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/base_events.py", line 1120, in _create_connection_transport
2023-03-12T13:15:29.051020+00:00 app[web.1]: await waiter
2023-03-12T13:15:29.051020+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/sslproto.py", line 534, in data_received
2023-03-12T13:15:29.051020+00:00 app[web.1]: ssldata, appdata = self._sslpipe.feed_ssldata(data)
2023-03-12T13:15:29.051020+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/asyncio/sslproto.py", line 188, in feed_ssldata
2023-03-12T13:15:29.051021+00:00 app[web.1]: self._sslobj.do_handshake()
2023-03-12T13:15:29.051021+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/ssl.py", line 945, in do_handshake
2023-03-12T13:15:29.051021+00:00 app[web.1]: self._sslobj.do_handshake()
2023-03-12T13:15:29.051021+00:00 app[web.1]: ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1129)
2023-03-12T13:15:29.051021+00:00 app[web.1]: 
2023-03-12T13:15:29.051022+00:00 app[web.1]: During handling of the above exception, another exception occurred:
2023-03-12T13:15:29.051022+00:00 app[web.1]: 
2023-03-12T13:15:29.051022+00:00 app[web.1]: Traceback (most recent call last):
2023-03-12T13:15:29.051022+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/routing.py", line 62, in __call__
2023-03-12T13:15:29.051022+00:00 app[web.1]: return await application(scope, receive, send)
2023-03-12T13:15:29.051023+00:00 app[web.1]: File "/app/hawlak/notifications/channelsmiddleware.py", line 23, in __call__
2023-03-12T13:15:29.051023+00:00 app[web.1]: return await super().__call__(scope, receive, send)
2023-03-12T13:15:29.051023+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/middleware.py", line 24, in __call__
2023-03-12T13:15:29.051023+00:00 app[web.1]: return await self.inner(scope, receive, send)
2023-03-12T13:15:29.051023+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/routing.py", line 116, in __call__
2023-03-12T13:15:29.051023+00:00 app[web.1]: return await application(
2023-03-12T13:15:29.051023+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/consumer.py", line 94, in app
2023-03-12T13:15:29.051024+00:00 app[web.1]: return await consumer(scope, receive, send)
2023-03-12T13:15:29.051024+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/consumer.py", line 58, in __call__
2023-03-12T13:15:29.051024+00:00 app[web.1]: await await_many_dispatch(
2023-03-12T13:15:29.051024+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/utils.py", line 50, in await_many_dispatch
2023-03-12T13:15:29.051024+00:00 app[web.1]: await dispatch(result)
2023-03-12T13:15:29.051024+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/consumer.py", line 73, in dispatch
2023-03-12T13:15:29.051024+00:00 app[web.1]: await handler(message)
2023-03-12T13:15:29.051025+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels/generic/websocket.py", line 173, in websocket_connect
2023-03-12T13:15:29.051025+00:00 app[web.1]: await self.connect()
2023-03-12T13:15:29.051025+00:00 app[web.1]: File "/app/hawlak/notifications/consumers.py", line 9, in connect
2023-03-12T13:15:29.051025+00:00 app[web.1]: await self.channel_layer.group_add(
2023-03-12T13:15:29.051025+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/channels_redis/core.py", line 523, in group_add
2023-03-12T13:15:29.051025+00:00 app[web.1]: await connection.zadd(group_key, {channel: time.time()})
2023-03-12T13:15:29.051026+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/redis/asyncio/client.py", line 509, in execute_command
2023-03-12T13:15:29.051026+00:00 app[web.1]: conn = self.connection or await pool.get_connection(command_name, **options)
2023-03-12T13:15:29.051026+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/redis/asyncio/connection.py", line 1400, in get_connection
2023-03-12T13:15:29.051026+00:00 app[web.1]: await connection.connect()
2023-03-12T13:15:29.051026+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/redis/asyncio/connection.py", line 611, in connect
2023-03-12T13:15:29.051026+00:00 app[web.1]: raise ConnectionError(self._error_message(e))
2023-03-12T13:15:29.051027+00:00 app[web.1]: redis.exceptions.ConnectionError: Error 1 connecting to ec2-50-17-163-82.compute-1.amazonaws.com:7550. 1.
2023-03-12T13:15:29.052221+00:00 app[web.1]: 10.1.17.28:21462 - - [12/Mar/2023:13:15:29] "WSDISCONNECT /ws/notification/" - -

I tried a a solution to provide the hosts as a dict in the like this and I had a function the parse the url and returns its component but that solved the problem of "idna" encode of the URL but still can't connect

from urllib.parse import urlparse

def parse_redis_url(url):
    """ parses a redis url into component parts, stripping password from the host.
    Long keys in the url result in parsing errors, since labels within a hostname 
    cannot exceed 64 characters under
    idna rules.
    In that event, we remove the key/password so that it can be passed 
    separately to the RedisChannelLayer.
    Heroku REDIS_URL does not include the DB number, so we allow for a default value of '0'
    """
    parsed = urlparse(url)
    parts = parsed.netloc.split(':')
    host = ':'.join(parts[0:-1])
    port = parts[-1]
    path = parsed.path.split('/')[1:]
    db = int(path[0]) if len(path) >= 1 else 0

    user, password = (None, None)
    if '@' in host:
        creds, host = host.split('@')
        user, password = creds.split(':')
        host = f'{user}@{host}'

    return host, port, user, password, db

channels layers

REDIS_URL = env('REDIS_URL', default='redis://localhost:6379')
REDIS_HOST, REDIS_PORT, REDIS_USER, REDIS_PASSWORD, REDIS_DB = parse_redis_url(REDIS_URL)


CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [{
                'address': f'{env("REDIS_PROTOCOL")}://{REDIS_HOST}:{REDIS_PORT}',
                'db': REDIS_DB,
                'password': REDIS_PASSWORD,
            }],
        },
    },
}

I also tried several approaches from Heroku docs and django chanels docs but never managed to make it work.

1 Answers1

0

According to https://devcenter.heroku.com/articles/connecting-heroku-redis#connecting-in-python, you need to turn off SSL certificate validation with something like ssl_cert_reqs=None.

Also, take a look at: https://stackoverflow.com/a/69777460/2638485.

Alex Rothberg
  • 10,243
  • 13
  • 60
  • 120