|
@@ -1,14 +1,16 @@
|
|
|
+import json
|
|
|
import time
|
|
|
|
|
|
-from MySQLdb import DatabaseError
|
|
|
from django.db import transaction
|
|
|
from django.http import QueryDict
|
|
|
from django.views import View
|
|
|
-from Ansjer.config import LOGGER
|
|
|
+
|
|
|
from AgentModel.models import DeviceCustomUID, CustomUIDPool
|
|
|
+from Ansjer.config import LOGGER
|
|
|
+from Model.models import LogModel
|
|
|
+from Object.RedisObject import RedisObject
|
|
|
from Object.ResponseObject import ResponseObject
|
|
|
from Service.CommonService import CommonService
|
|
|
-from Object.RedisObject import RedisObject
|
|
|
|
|
|
|
|
|
class DeviceCustomUIDView(View):
|
|
@@ -41,14 +43,15 @@ class DeviceCustomUIDView(View):
|
|
|
if operation == 'getUID': # 获取电池电量列表
|
|
|
return self.get_custom_uid(request, request_dict, response)
|
|
|
|
|
|
- @classmethod
|
|
|
- def get_custom_uid(cls, request, request_dict, response):
|
|
|
+ @staticmethod
|
|
|
+ def get_custom_uid(request, request_dict, response):
|
|
|
ip = CommonService.get_ip_address(request)
|
|
|
- LOGGER.info('获取定制客户UID:{},ip:{}'.format(request_dict, ip))
|
|
|
- mac = request_dict.get('mac', None)
|
|
|
- token = request_dict.get('token', None)
|
|
|
- time_stamp = request_dict.get('time_stamp', None)
|
|
|
+ LOGGER.info(f'获取定制客户UID:{request_dict},ip:{ip}')
|
|
|
+ mac = request_dict.get('mac')
|
|
|
+ token = request_dict.get('token')
|
|
|
+ time_stamp = request_dict.get('time_stamp')
|
|
|
|
|
|
+ # 参数校验
|
|
|
if not all([mac, token, time_stamp]):
|
|
|
LOGGER.error(f'{mac}请求绑定uid参数缺失')
|
|
|
return response.json(444)
|
|
@@ -58,77 +61,97 @@ class DeviceCustomUIDView(View):
|
|
|
LOGGER.error(f'{mac}时间戳校验失败time:{time_stamp},tk:{token}')
|
|
|
return response.json(13)
|
|
|
|
|
|
- # 分布式锁配置
|
|
|
- redis = RedisObject()
|
|
|
-
|
|
|
- lock_key = f"uid_assign:{mac}"
|
|
|
- request_id = f"{mac}_{time_stamp}"
|
|
|
+ redis = RedisObject(db=3)
|
|
|
+ lock_key = f"MAC:ASSIGN:{mac}"
|
|
|
+ request_id = f"{mac}_{int(time.time())}"
|
|
|
|
|
|
try:
|
|
|
- # 第一次检查现有绑定
|
|
|
- existing_binding = DeviceCustomUID.objects.filter(
|
|
|
- device_mac=mac,
|
|
|
- status=1
|
|
|
- ).select_related('uid').first()
|
|
|
-
|
|
|
- if existing_binding:
|
|
|
- LOGGER.info(f'{mac}已存在有效绑定')
|
|
|
- return response.json(0, data={'uid': existing_binding.uid.uid})
|
|
|
-
|
|
|
- # 获取分布式锁(设置5秒过期时间)
|
|
|
- if not redis.try_lock(lock_key, request_id, expire=5, time_unit_second=1):
|
|
|
- LOGGER.error(f'{mac}获取锁失败')
|
|
|
+ # 预检查绑定状态(缓存优化)
|
|
|
+ cached_uid = redis.get_data(f"DEVICE:MAC:{mac}")
|
|
|
+ if cached_uid:
|
|
|
+ return response.json(0, data={'uid': cached_uid})
|
|
|
+
|
|
|
+ # 分布式锁配置(增加锁持有检测)
|
|
|
+ acquired = False
|
|
|
+ for _ in range(3):
|
|
|
+ if redis.try_lock(lock_key, request_id, expire=5, time_unit_second=1):
|
|
|
+ acquired = True
|
|
|
+ break
|
|
|
+ time.sleep(0.3)
|
|
|
+ if not acquired:
|
|
|
+ LOGGER.warning(f'{mac}系统繁忙请稍后重试')
|
|
|
return response.json(5)
|
|
|
-
|
|
|
- # 二次检查(防止在等待锁期间被其他请求处理)
|
|
|
- existing_binding = DeviceCustomUID.objects.filter(
|
|
|
- device_mac=mac,
|
|
|
- status=1
|
|
|
- ).select_related('uid').first()
|
|
|
-
|
|
|
- if existing_binding:
|
|
|
- LOGGER.info(f'{mac}二次检查存在绑定')
|
|
|
- return response.json(0, data={'uid': existing_binding.uid.uid})
|
|
|
-
|
|
|
- # 核心分配逻辑
|
|
|
with transaction.atomic():
|
|
|
- # 获取可用UID(使用SELECT FOR UPDATE锁定行)
|
|
|
- available_uid = CustomUIDPool.objects.select_for_update() \
|
|
|
- .filter(status=0) \
|
|
|
- .order_by('id') \
|
|
|
- .first()
|
|
|
-
|
|
|
- if not available_uid:
|
|
|
- LOGGER.error(f'{mac}UID池已耗尽')
|
|
|
- return response.json(173)
|
|
|
-
|
|
|
- # 更新UID状态
|
|
|
- n_time = int(time.time())
|
|
|
- available_uid.status = 1
|
|
|
- available_uid.updated_time = n_time
|
|
|
- available_uid.save(update_fields=['status', 'updated_time'])
|
|
|
-
|
|
|
- # 创建绑定记录
|
|
|
- DeviceCustomUID.objects.create(
|
|
|
- uid=available_uid,
|
|
|
+ # 二次检查(带数据库查询)
|
|
|
+ existing = DeviceCustomUID.objects.filter(
|
|
|
device_mac=mac,
|
|
|
- status=1,
|
|
|
- created_time=n_time,
|
|
|
- updated_time=n_time
|
|
|
- )
|
|
|
-
|
|
|
- LOGGER.info(f'{mac}成功分配UID: {available_uid.uid}')
|
|
|
- return response.json(0, data={'uid': available_uid.uid})
|
|
|
+ status=1
|
|
|
+ ).only('uid').first()
|
|
|
+ if existing:
|
|
|
+ # 更新缓存
|
|
|
+ redis.set_ex_data(f"DEVICE:MAC:{mac}", existing.uid, 10)
|
|
|
+ return response.json(0, {'uid': existing.uid})
|
|
|
+ count = 0
|
|
|
+ while count < 5:
|
|
|
+ available_uid = (
|
|
|
+ CustomUIDPool.objects
|
|
|
+ .filter(status=0)
|
|
|
+ .order_by('updated_time')
|
|
|
+ .first()
|
|
|
+ )
|
|
|
+
|
|
|
+ if not available_uid:
|
|
|
+ LOGGER.error(f'{mac}UID池资源耗尽')
|
|
|
+ return response.json(173)
|
|
|
+ # 查询到未被绑定的UID进行redis加锁
|
|
|
+ uid_lock = redis.try_lock(f"DEVICE:UID:{available_uid.uid}",
|
|
|
+ request_id, expire=3, time_unit_second=1)
|
|
|
+ if not uid_lock:
|
|
|
+ count += 1
|
|
|
+ continue
|
|
|
+ # 原子操作更新
|
|
|
+ rows_updated = CustomUIDPool.objects.filter(
|
|
|
+ id=available_uid.id,
|
|
|
+ status=0 # 乐观锁校验
|
|
|
+ ).update(
|
|
|
+ status=1,
|
|
|
+ updated_time=int(time.time())
|
|
|
+ )
|
|
|
+
|
|
|
+ if int(rows_updated) <= 0: # 更新失败
|
|
|
+ count += 1
|
|
|
+ continue
|
|
|
+
|
|
|
+ if not rows_updated:
|
|
|
+ LOGGER.warning(f'{mac}UID并发竞争更新失败')
|
|
|
+ return response.json(14)
|
|
|
+ # 创建设备关联记录
|
|
|
+ DeviceCustomUID.objects.create(
|
|
|
+ device_mac=mac,
|
|
|
+ uid=available_uid.uid,
|
|
|
+ status=1,
|
|
|
+ updated_time=int(time.time()),
|
|
|
+ created_time=int(time.time())
|
|
|
+ )
|
|
|
+
|
|
|
+ content = json.loads(json.dumps(request_dict))
|
|
|
+ log = {
|
|
|
+ 'ip': ip,
|
|
|
+ 'user_id': 1,
|
|
|
+ 'status': 200,
|
|
|
+ 'time': int(time.time()),
|
|
|
+ 'content': json.dumps(content),
|
|
|
+ 'url': 'api/device/custom/getUID',
|
|
|
+ 'operation': '定制设备mac{}绑定uid: {}'.format(mac, available_uid.uid),
|
|
|
+ }
|
|
|
+ LogModel.objects.create(**log)
|
|
|
+
|
|
|
+ # 设置缓存
|
|
|
+ redis.set_ex_data(f"MAC_UID_CACHE:{mac}", available_uid.uid, 10)
|
|
|
+ return response.json(0, {'uid': available_uid.uid})
|
|
|
|
|
|
- except DatabaseError as e:
|
|
|
- LOGGER.error(f'{mac}数据库操作异常: {str(e)}')
|
|
|
- return response.json(18)
|
|
|
except Exception as e:
|
|
|
- LOGGER.error(f'系统异常: {str(e)}')
|
|
|
+ LOGGER.error(f'mac:{mac}系统异常: {str(e)}', exc_info=True)
|
|
|
return response.json(500)
|
|
|
finally:
|
|
|
- # 确保释放锁
|
|
|
- if redis.release_lock(lock_key, request_id):
|
|
|
- LOGGER.debug(f'{mac}锁释放成功')
|
|
|
- else:
|
|
|
- LOGGER.warning(f'{mac}锁释放异常')
|
|
|
+ redis.release_lock(lock_key, request_id)
|