Django代码逻辑异常:Redis故障时无法从PostgreSQL读取数据
问题排查:Redis故障时Django API未从数据库读取数据返回404
问题描述
编写的Django APIView预期通过inscode从Redis读取数据并返回;当Redis故障时,从PostgreSQL数据库读取数据。但实际Redis故障时,代码并未从数据库读取数据,而是返回404状态码。编写的测试用例验证读取数据时返回200,但测试未通过。
代码实现
class GetOrderBook(APIView): def get(self, request, format=None): # get parameters inscode = None if 'i' in request.GET: inscode = request.GET['i'] if inscode != None: try: #raise ('disabling redis!') r_handle = redisconnection data = r_handle.get(inscode) if data != None: return Response(200) else: print('not found in cache') except BaseException as err: print(err) orderbook = OrderBook.objects.filter( symbol__inscode=inscode).order_by('rownum') if len(orderbook) > 0: data = OrderBookSer(orderbook, many=True).data data_formatted = {'buynum': [0]*5, 'buyvol': [0]*5, 'buyprice': [ 0]*5, 'sellnum': [0]*5, 'sellvol': [0]*5, 'sellprice': [0]*5} return Response(data_formatted, status=status.HTTP_200_OK) else: return Response({'Bad Request'}, status.HTTP_404_NOT_FOUND) return Response({'Bad Request'}, status.HTTP_400_BAD_REQUEST)
测试用例
def test_redis_function(self, api_client, test_user): url = self.url api_client.force_authenticate(user=test_user) response = api_client.get(url, {'i': '2400322364771558'}) assert response.status_code == status.HTTP_200_OK
问题排查与修复
核心问题点
Redis命中时的响应逻辑错误
原代码中return Response(200)是将数字200作为响应体返回,而非设置HTTP状态码为200,属于参数传递错误。异常捕获范围过宽
捕获BaseException会包含所有异常(如代码逻辑错误),可能掩盖其他问题,应仅捕获Redis相关异常。数据库查询效率低下
使用len(orderbook)判断查询结果是否存在,会触发全表扫描,更高效的方式是使用exists()方法。错误响应消息不准确
404状态码对应的响应消息应为资源不存在,而非"Bad Request"(对应400状态码)。
修复后的代码
from redis.exceptions import RedisError class GetOrderBook(APIView): def get(self, request, format=None): # 简化参数获取方式 inscode = request.GET.get('i') if inscode: try: r_handle = redisconnection data = r_handle.get(inscode) if data is not None: # 正确返回缓存数据与状态码 return Response({'data': data}, status=status.HTTP_200_OK) # Redis未命中,自动进入数据库查询逻辑 except RedisError as err: print(f"Redis连接异常: {err}") # 数据库查询逻辑 orderbook = OrderBook.objects.filter( symbol__inscode=inscode).order_by('rownum') if orderbook.exists(): data = OrderBookSer(orderbook, many=True).data data_formatted = { 'buynum': [0]*5, 'buyvol': [0]*5, 'buyprice': [0]*5, 'sellnum': [0]*5, 'sellvol': [0]*5, 'sellprice': [0]*5 } # 可根据实际需求从data中填充data_formatted字段 return Response(data_formatted, status=status.HTTP_200_OK) else: return Response({'error': '订单簿不存在'}, status=status.HTTP_404_NOT_FOUND) return Response({'error': '缺少inscode参数'}, status=status.HTTP_400_BAD_REQUEST)
测试用例优化(模拟Redis故障场景)
from unittest.mock import patch from redis.exceptions import ConnectionError def test_redis_fallback_to_database(self, api_client, test_user): url = self.url api_client.force_authenticate(user=test_user) # 模拟Redis连接异常 with patch('your_app.redisconnection.get') as mock_redis_get: mock_redis_get.side_effect = ConnectionError("Redis服务不可用") response = api_client.get(url, {'i': '2400322364771558'}) assert response.status_code == status.HTTP_200_OK
内容的提问来源于stack exchange,提问作者HesamHashemi
相关产品推荐
相关产品推荐

