предисловие
Так как bilibili всегда имел функцию количества онлайн-зрителей рукописей, я хочу знать, как он это делал, поэтому у меня есть эта заметка.
1. Выбор архитектуры
Часто используемая техническая архитектура
-
Записи журнала, такие как access_log, фильтруются с помощью URL-адреса и реализуются с помощью таких операций, как разделение даты.
-
недостаток
- Производительность в реальном времени не высока
- Высокие требования к вводу/выводу
-
преимущество
- Легко реализовать, просто нужно прочитать текст
-
-
WebSocket+Redis
-
недостаток
- Требуемая производительность сервера
- Реализация немного сложнее
-
преимущество
- Высокий режим реального времени
-
Решение, принятое Билибили (догадка)
-
сквозь
networkПроверьте это, вы можете видеть, что решение, принятое Bilibili, является вторым, используяWebSocketПуть -
Bilibili принимает связь
wss://broadcast.chat.bilibili.com:7826/subСлужить. -
Согласно чужим открытым источникам, должна передаваться такая штука, именуемая в дальнейшем [данные1]
0x00, 0x00, 0x00, 0x5B, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x7B, 0x22, 0x72, 0x6F, 0x6F, 0x6D, 0x5F, 0x69, 0x64, 0x22, 0x3A, 0x22, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x3A, 0x2F, 0x2F, 0x35, 0x30, 0x33, 0x33, 0x34, 0x35, 0x38, 0x30, 0x2F, 0x38, 0x38, 0x31, 0x32, 0x30, 0x37, 0x39, 0x32, 0x22, 0x2C, 0x22, 0x70, 0x6C, 0x61, 0x74, 0x66, 0x6F, 0x72, 0x6D, 0x22, 0x3A, 0x22, 0x77, 0x65, 0x62, 0x22, 0x2C, 0x22, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x73, 0x22, 0x3A, 0x5B, 0x31, 0x30, 0x30, 0x30, 0x5D, 0x7DЭто похоже на шестнадцатеричный массив байтов, проанализируйте его
/** * 可以分析出 * 0x00, 0x00, 0x00, 0x5B, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07,0x00, 0x00, 0x00, 0x01, 0x00, 0x00 * 这一部分应该是定义的消息头,无法解析出实际的意义 * 剩下的部分,解析后是 * {"room_id":"video://50334580/88120792","platform":"web","accepts":[1000]} */ @Test public void test3() { byte[] bytes = { 0x7B, 0x22, 0x72, 0x6F, 0x6F, 0x6D, 0x5F, 0x69, 0x64, 0x22, 0x3A, 0x22, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x3A, 0x2F, 0x2F, 0x35, 0x30, 0x33, 0x33, 0x34, 0x35, 0x38, 0x30, 0x2F, 0x38, 0x38, 0x31, 0x32, 0x30, 0x37, 0x39, 0x32, 0x22, 0x2C, 0x22, 0x70, 0x6C, 0x61, 0x74, 0x66, 0x6F, 0x72, 0x6D, 0x22, 0x3A, 0x22, 0x77, 0x65, 0x62, 0x22, 0x2C, 0x22, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x73, 0x22, 0x3A, 0x5B, 0x31, 0x30, 0x30, 0x30, 0x5D, 0x7D }; StringBuilder result = new StringBuilder(); for (int index = 0, len = bytes.length; index <= len - 1; index += 1) { int char1 = ((bytes[index] >> 4) & 0xF); char chara1 = Character.forDigit(char1, 16); int char2 = ((bytes[index]) & 0xF); char chara2 = Character.forDigit(char2, 16); result.append(chara1); result.append(chara2); } System.out.println(new String(new BigInteger(result.toString(), 16).toByteArray())); }-
Фокус должен быть на room_id, здесь
videoделится на две части,50334580Это должно быть видеоavномер (сейчас используется номер bv, но на самом деле есть соответствующий номер av),88120792Это должна быть информация о маркированном экране видео.cid -
По коду должен быть только один адрес подключения
aid/cidВ качестве ключа, после нажатия на видео соединение, выполнить соединение WebSocket, затем отправить [DATA1], затем сервер возвращаетb'\x00\x00\x00+\x00\x12\x00\x01\x00\x00\x00\x08\x00\x00\x00\x01\x00\x00{"code":0,"message":"ok"}' -
я пошлю один
base64Закодированная информация о пользователе или что-то в этом роде? Я не понял код здесь, а потом вернулсяb'\x00\x00\x00n\x00\x12\x00\x01\x00\x00\x00\x03\x00\x00\x00\t\x00\x00{"code":0,"message":"0","data":{"room":{"online":3,"room_id":"video://85919470/146861497"}}}'
-
2. Мое решение — Java
- Использование подключения Webscoket, подписка пользователя
/video/{vid} - в соответствии с
vidсоздать соответствующийMapместо хранения,vidПерепискаwebsocket-session - Отправить в фоновое сообщение с
userId - в соответствии с
vidОтправьте в Redis, используйте видеоvidВ качестве ключа используйте Redissetструктура данных, хранениеuserId,пройти черезscard keyПроверить количество людей онлайн - Обновляйте через Heatbeat или регулярно обновляйте.
- Пользователь закрывает вкладку, отключает сервис websocket, приносит
vidСкажите, уберите соответствующий в Redisvidкак ключuserId
3. Недостаточно
Так как могут быть случаи, когда пользователи открывают несколько видео одного и того же видео одновременно, если одно из них будет закрыто, другие также будут отключены, а последние онлайн-просмотрщики не могут время от времени подталкиваться.
код ссылкиGitHub.com/spray456/no…
import websocket
import base64
import requests
import json
import datetime
import time
def get_cid(av):
response = requests.get("https://api.bilibili.com/x/web-interface/view?aid=" + str(av))
response.encoding = 'utf-8'
res = response.text
# print(res)
data = json.loads(res)
c = data['data']['cid']
# print(c)
return c
def make_send(av):
cid = str(get_cid(av))
res = b'\x00\x00\x00\\x00\x12\x00\x01\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00{"room_id":"video://' + str(av).encode('utf-8') + '/'.encode('utf-8') + cid.encode('utf-8') + '","platform":"web","accepts":[1000]}'.encode('utf-8')
return res
def get_online(text):
cache = text.find(b'"online":')
# print(cache)
cache2 = text[cache+9:].find(b',')
get = int(text[cache+9:cache2+9+cache])
print(get)
return get
def connect(plz):
url = "wss://broadcast.chat.bilibili.com:7823/sub"
normal = base64.b64decode('AAAAIQASAAEAAAACAAAACQAAW29iamVjdCBPYmplY3Rd')
ws = websocket.create_connection(url,timeout=10)
ws.send(bytes(plz))
get = ws.recv()
print(get)
print(normal)
ws.send(bytes(normal))
get = ws.recv()
print(get)
if get.find(b'online') != -1:
# online = get_online(get)
online=get_online(get)
return online
else :
print("None")
def get_online_from_av(av):
send = make_send(av)
online = connect(send)
return online
def write_file(onlines,times):
with open(file_name,'a') as file_obj:
file_obj.write(str(times) + ',' + str(onlines) + '\r')
get_online_from_av(85919470)
# file_name = str(input("File_Name(a.txt):"))
# avid = int(input('AVid(85919470):'))
# while True:
# online=get_online_from_av(avid)
# now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# print(now_time)
# # write_file(online,now_time)
# time.sleep(60)
\