本项目的后端是用Django
开发的,而我主要做的是判题端的通信调度,判题端是用Python
手撸的,并且采用socket
进行通信。
遇到的问题:
- 多个socket进行通信的架构
之前没有遇到过创建多个socket来通信的结构,这里记录下代码。
判题端用socketserver创建服务器,后端用socket创建多个客户端连接多个判题端。
class Clients:
def __init__(self):
self.judgers = Judger.objects.all()
self.clients = {}
for judger in self.judgers:
self.clients[judger.ip] = socket.socket(
socket.AF_INET, socket.SOCK_STREAM)
try:
self.clients[judger.ip].connect((judger.ip, JUDGER_PORT))
logger.info('clients add %s' % (judger.ip))
except:
del self.clients[judger.ip]
if not self.clients:
raise ConnectionError('[client] 当前没有判题机在线')
- 外部文件import Django内部文件的model问题
外部文件import django内部文件的model会出现引用失败的情况,需要加上如下代码
pathname = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, pathname)
sys.path.insert(0, os.path.abspath(os.path.join(pathname, '..')))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "OnlineJudge.settings")
django.setup()
- Django ORM断连问题
这个搞得我头大,之前用过隔一个小时发一个wakeup包的方法,现在看到一个原生的方法还不错,在执行orm出错的语句前加上它就能无视6小时诅咒了。
db.close_old_connections()
- 大文件传输问题
TCP传递大文件会遇到问题,这里的方法是,把需要传递的包切成1024等分的包数组,然后传包数提醒判题服务器,再把每个小包传过去,这里传递每个包后可以收下判题服务器的返回'ok',防止粘包。
body_list = [body[i:i+1024] for i in range(0, len(body), 1024)]
body_list_lenght = str(len(body_list)).encode('utf-8')
def send_data(self, client, body_list, body_list_lenght):
client.send(body_list_lenght)
logger.info('send body_list_lenght: %d ' %len(body_list) )
for item in body_list:
client.send(item)
res = str(client.recv(10)) # 防止粘包
logger.debug('send: %d | %s' % (len(item),res) )
res = str(client.recv(10))
logger.info('send body: %s'%res)
- 判题端的结构
第一次写不用框架的python独立应用哈,还是前端菜鸟写的,结构有点奇葩轻喷
数据库初始化封装成 model.py
对model的操作封装成 model_api.py
对接客户端的服务端封装成 server.py
分配任务处理数据开辟线程的封装成 controller.py
判题端实际上是从原来后端里分离出来的,所以依然有 judge.py
和 Judger.py
docker的单例化文件 docker_client.py
文件夹有 log
存放日志, question
存放输入输出样例、 code
存放临时代码
- 多线程判题
开辟线程前必须要注意对数据库的活动容器记录必须在主线程,不然的话绝对出神奇的错误,然后用threading.Thread().start()
启动线程,并且配合信号量sem = Semaphore()
,在使用的时候python有语法糖,只需要 with sem:
就行啦