精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

提升 Node.js UDP 的性能?

開發 前端
recvmmsg 通過減少系統調用提升性能,但是不一定在什么場景下都有可觀的效果,比如在 QPS 高時可能比較有意義,另外因為每次都需要提前分配內容,所以數據量小時可能會導致每次分配過多無用的內存,然后又被釋放。

網絡 IO 性能一直是熱門的討論話題,不管是操作系統還是應用軟件也在不斷優化網絡 IO 的性能。我們知道系統調用是相對耗時的,所以性能優化的一種方式就是減少系統調用。本文介紹最近嘗試在 Node.js 中通過引入 recvmmsg 提升 UDP 接收數據性能的相關內容,相比之前每調一次系統調用讀取一個數據包,recvmmsg 可以一次讀取多個包,從而減少系統調用提升性能。

UDP 的實現

下面是在 Node.js 中使用 UDP 的例子。

const dgram = require('dgram')

const socket = dgram.createSocket({type: 'udp4', msgCount: 10});
socket.bind(9999, function() {
    socket.on('message',function() {
      
    });
})

其底層實現如下。

  • 初始化 udp handle。
r = uv_udp_init(env->event_loop(), &handle_);
  • 注冊讀事件。
int UDPWrap::RecvStart() {
  int err = uv_udp_recv_start(&handle_, OnAlloc, OnRecv);
  return err;
}

uv_udp_recv_start 用于注冊讀事件,當有數據到來時執行 OnAlloc 分配內存,然后執行 OnRecv 消費數據。

  • 分配保存數據的內存。 OnAlloc 實現如下:
uv_buf_t UDPWrap::OnAlloc(size_t suggested_size) {
  return env()->allocate_managed_buffer(suggested_size);
}

uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) {
  std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
      isolate(),
      suggested_size,
      BackingStoreInitializationMode::kUninitialized);
  uv_buf_t buf = uv_buf_init(static_cast<char*>(bs->Data()), bs->ByteLength());
  released_allocated_buffers_.emplace(buf.base, std::move(bs));
  return buf;
}

Node.js 通過 V8 的 BackingStore 申請內存,然后把這塊內存傳給 Libuv 讀取數據,最后記錄內存首地址和 BackingStore 的映射關系,后續會用到。

  • 消費數據 Libuv 讀取數據后會調 OnRecv 通知調用者。
void UDPWrap::OnRecv(ssize_t nread,
                     const uv_buf_t& buf_,
                     const sockaddr* addr,
                     unsigned int flags) {
  Environment* env = this->env();
  Isolate* isolate = env->isolate();
  // 通過 buf 找到對應的 BackingStore,然后回調 JS
  std::unique_ptr<BackingStore> bs = env->release_managed_buffer(buf_);
  Local<Value> argv[] = {
      Integer::New(isolate, static_cast<int32_t>(nread)),
      object(),
      Undefined(isolate),
      Undefined(isolate)
  };

  if (nread < 0) {
    MakeCallback(env->onmessage_string(), arraysize(argv), argv);
    return;
  } else if (nread == 0) {
    bs = ArrayBuffer::NewBackingStore(isolate, 0);
  } else if (static_cast<size_t>(nread) != bs->ByteLength()) { // 讀取的數據和預分配的不一致,需要創建新的 BackingStore 并把數據復制過去
    std::unique_ptr<BackingStore> old_bs = std::move(bs);
    bs = ArrayBuffer::NewBackingStore(
        isolate, nread, BackingStoreInitializationMode::kUninitialized);
    memcpy(bs->Data(), old_bs->Data(), nread);
  }
  // ...
  // ...
}

可以看到 UDP 的實現不算復雜。接著看如何在這個基礎上引入 recvmmsg 能力。

引入 recvmmsg

Libuv 本身支持 recvmmsg,所以我們不需要關系平臺兼容性問題(目前支持多個平臺,但并不支持在所有平臺中使用)。

  • 設置使用 recvmmsg。
env->event_loop(), &handle_, AF_UNSPEC | UV_UDP_RECVMMSG);

通過 UV_UDP_RECVMMSG 標記可以使得 Libuv 使用 recvmmsg 接收 UDP 數據。

  • 分配內存。 分配內存這里有點棘手,使用 UV_UDP_RECVMMSG 時需要分配一大塊內容,足以保存多個數據包的數據,而之前的實現中,每次是通過 BackingStore 分配一塊獨立的內容,用完后就釋放的。所以這里不能使用 BackingStore 了,直接使用原生的內存分配。
uv_buf_t UDPWrap::OnAlloc(size_t suggested_size) {
  if (using_recvmmsg()) {
    // msg_count_ 表示每次讀取多少個數據包
    suggested_size *= msg_count_;
    // 在當前對象記錄正在使用的內存,后面分析
    mmsg_buf_ = uv_buf_init(reinterpret_cast<char*>(malloc(suggested_size)),
                            suggested_size);
    return mmsg_buf_;
  }
  return env()->allocate_managed_buffer(suggested_size);
}

通過 malloc 分配接收多個數據包所需要的一大片內存,每個數據包對應其中的一個分片。

  • 消費數據 消費數據和之前的邏輯也有些不同,之前是消費完數據后就可以釋放對應的內存了,但是引入 recvmmsg 后,每次是分配一大塊內存的,每個數據包對應其中的一個分片,所以消費完一個數據包還不能釋放整個內存,需要全部數據包才能釋放這個內容。Libuv 的實現如下:
// buf->len 對應 OnAlloc 分片的一大片內存,除以 UV__UDP_DGRAM_MAXSIZ 表示一次最多接收幾個數據包
  chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;
  if (chunks > ARRAY_SIZE(iov))
    chunks = ARRAY_SIZE(iov);
  for (k = 0; k < chunks; ++k) {
    iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE;
    iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE;
    // ...
  }

  do
    nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
  while (nread == -1 && errno == EINTR);

  if (nread < 1) {
    if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
      handle->recv_cb(handle, 0, buf, NULL, 0);
    else
      handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0);
  } else {
    /* pass each chunk to the application */
    for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) {
      flags = UV_UDP_MMSG_CHUNK;
      if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC)
        flags |= UV_UDP_PARTIAL;

      chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len);
      handle->recv_cb(handle,
                      msgs[k].msg_len,
                      &chunk_buf,
                      msgs[k].msg_hdr.msg_name,
                      flags);
    }

    /* one last callback so the original buffer is freed */
    if (handle->recv_cb != NULL)
      handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
  }

另外之前是通過 BackingStore 分配內存來和 uv_buf_t 關聯來實現的,我們這里則是通過 malloc 直接分配的內容,所以接收數據時就不能通過 uv_buf_t 來查看對應的 BackingStore 了。

void UDPWrap::OnRecv(ssize_t nread,
                     const uv_buf_t& buf_,
                     const sockaddr* addr,
                     unsigned int flags) {
  Environment* env = this->env();
  Isolate* isolate = env->isolate();
  std::unique_ptr<BackingStore> bs;
  // 處理完本輪所有數據包,釋放內存
  auto cleanup = OnScopeLeave([&]() {
    if (using_recvmmsg() && (nread <= 0 || (flags & UV_UDP_MMSG_FREE))) {
      release_buf();
    }
  });
  // 歷史邏輯
  if (!using_recvmmsg()) {
    bs = env->release_managed_buffer(buf_);
  }
  Local<Value> argv[] = {
      Integer::New(isolate, static_cast<int32_t>(nread)),
      object(),
      Undefined(isolate),
      Undefined(isolate)
  };

  if (nread < 0) {
    MakeCallback(env->onmessage_string(), arraysize(argv), argv);
    return;
  } else if (nread == 0) {
    bs = ArrayBuffer::NewBackingStore(isolate, 0);
  } else if (using_recvmmsg()) {
    // 創建一個 BackingStore 并把數據包的數據復制過去
    bs = ArrayBuffer::NewBackingStore(
        isolate, nread, BackingStoreInitializationMode::kUninitialized);
    memcpy(bs->Data(), buf_.base, nread);
  } else if (static_cast<size_t>(nread) != bs->ByteLength()) {
    std::unique_ptr<BackingStore> old_bs = std::move(bs);
    bs = ArrayBuffer::NewBackingStore(
        isolate, nread, BackingStoreInitializationMode::kUninitialized);
    memcpy(bs->Data(), old_bs->Data(), nread);
  }
  // ...
}

上面的改動包括兩方面,一是兼容之前的收到數據時創建 BackingStore 的邏輯,這樣可以很大程度地復用之前的邏輯,二是判斷是否需要釋放內存。

  • 避免內存泄漏 從上面的處理中可以看到,當本輪的所有數據包處理完后,我們就會正常釋放申請的大片內存,但是存在一個問題是假設分配了 1MB * 10 大小的內存,然后通過 recvmmsg 從操作系統獲取了 10 個數據包,但是處理第一個數據包時用戶調了 close 關閉了 socket,這樣會導致 Libuv 不會回調 Node.js 了,從而導致之前申請的大片內存沒有釋放,造成內存泄漏。所以在申請內存時需要記錄這個內存,然后 socket 關閉時釋放(獲取處理完所有的數據包后釋放)。
UDPWrap::~UDPWrap() {
  // Libuv does not release the memory of memory which allocated
  // by handle->alloc_cb when we call close in handle->read_cb,
  // so we should release the memory here if necessary.
  // 開啟了 recvmmsg 并且沒有釋放申請的內存則釋放
  if (uv_udp_using_recvmmsg(reinterpret_cast<uv_udp_t*>(&handle_)) {
    release_buf();
  }
}

void release_buf() {
  if (mmsg_buf_.base != nullptr) {
    free(mmsg_buf_.base);
    mmsg_buf_ = uv_buf_init(nullptr, 0);
  }
}

總結

recvmmsg 通過減少系統調用提升性能,但是不一定在什么場景下都有可觀的效果,比如在 QPS 高時可能比較有意義,另外因為每次都需要提前分配內容,所以數據量小時可能會導致每次分配過多無用的內存,然后又被釋放。該特性目前還沒有在 Node.js 中進行性能測試。

  1. PR:https://github.com/nodejs/node/pull/59126
責任編輯:武曉燕 來源: 編程雜技
相關推薦

2015-12-14 10:39:14

2019-07-09 14:50:15

Node.js前端工具

2015-03-10 10:59:18

Node.js開發指南基礎介紹

2013-11-01 09:34:56

Node.js技術

2020-12-28 08:48:44

JS工具fastify

2020-12-14 15:40:59

Nodefastifyjs

2013-10-23 17:17:31

Node.jsdoT

2022-06-07 08:07:05

GoNode.js

2022-05-25 10:04:43

Go編程

2021-12-25 22:29:57

Node.js 微任務處理事件循環

2012-02-03 09:25:39

Node.js

2020-05-29 15:33:28

Node.js框架JavaScript

2020-12-14 08:55:00

Node.js服務性框架

2015-11-04 09:18:41

Node.js應用性能

2012-10-24 14:56:30

IBMdw

2011-11-10 08:55:00

Node.js

2011-11-01 10:30:36

Node.js

2011-09-08 13:46:14

node.js

2011-09-09 14:23:13

Node.js

2011-09-02 14:47:48

Node
點贊
收藏

51CTO技術棧公眾號

国产福利在线看| 国产一级大片在线观看| 国产91亚洲精品久久久| 亚洲天堂成人在线观看| 国产精品乱码视频| 日韩一级在线视频| 99精品视频在线观看播放| 日韩精品一区二区在线观看| 黄色免费观看视频网站| 在线观看国产原创自拍视频| 成人免费视频国产在线观看| 国产精品久久久久久久久男| 久久久久久久久久久久久久免费看 | 99热这里只有精品5| 国产精品久久久久毛片大屁完整版| 伊人久久久久久久久久久久久| 欧美激情第四页| 日韩不卡免费高清视频| 亚洲激情图片一区| 日本一区二区三区视频在线播放 | 免费不卡在线观看| 久久久在线免费观看| 黄色一级片一级片| 欧美激情网址| 欧美一区在线视频| 天天爽人人爽夜夜爽| 久久男人av资源站| 亚洲另类中文字| 天堂资源在线亚洲资源| 色欲久久久天天天综合网| 激情欧美日韩一区二区| 国产精品高清免费在线观看| 国产欧美日韩另类| 亚洲综合中文| 久久精品免费电影| аⅴ天堂中文在线网| 视频福利一区| 亚洲成人久久电影| 四虎国产精品免费| 亚洲欧洲日韩精品在线| 欧洲视频一区二区| 777久久久精品一区二区三区| 国内小视频在线看| 亚洲综合免费观看高清完整版 | 91精品免费视频| 亚洲精品国产欧美在线观看| 亚洲尤物在线| 91精品国产99| 日韩欧美性视频| 亚洲黄页一区| 国模精品系列视频| 久久综合色综合| 欧美国产精品| 欧美激情第一页xxx| 久热这里有精品| 婷婷综合在线| 久久99精品久久久久久琪琪| 麻豆网址在线观看| 亚洲欧美日韩高清在线| 久久精品最新地址| 成人免费视频网站入口::| 偷偷www综合久久久久久久| 日韩视频一区在线| 看黄色录像一级片| 亚洲欧洲美洲一区二区三区| 欧美超级免费视 在线| 精品一区在线观看视频| 欧美二区视频| 97国产精品视频| 亚洲男人的天堂在线视频| 一本久久知道综合久久| 国产69久久精品成人| 一级成人黄色片| 男人的天堂亚洲一区| 国产日韩在线视频| 国产日韩欧美一区二区东京热 | 人妻在线日韩免费视频| 最新国产一区| 色老头一区二区三区| 天海翼在线视频| 欧美日韩中文| 青青草成人在线| 一级黄在线观看| 国产99久久久国产精品免费看| 国产另类第一区| 狠狠v欧美ⅴ日韩v亚洲v大胸| 亚洲国产精品v| www.国产亚洲| 日韩精品99| 91精品国产综合久久精品麻豆| 麻豆免费在线观看视频| 伊人成综合网yiren22| 视频在线观看一区二区| 久久久久亚洲av成人片| 日韩中文字幕不卡| 97人人香蕉| 免费a在线观看| 亚洲精品成人精品456| 无码播放一区二区三区| 亚洲三级电影| 精品亚洲国产视频| 特一级黄色录像| 国产情侣久久| 亚洲aⅴ日韩av电影在线观看| 天堂网www中文在线| 国产精品国产三级国产aⅴ无密码| 青青草国产免费| 欧美一区二区三区婷婷| 亚洲第一页在线| 任你操精品视频| 免费一区视频| www 成人av com| 日日夜夜精品一区| 一本到三区不卡视频| 欧美一区二区三区影院| 成人在线丰满少妇av| 韩剧1988在线观看免费完整版| 亚洲综合网av| 久久婷婷国产综合精品青草| 大地资源网在线观看免费官网| 欧美日韩在线精品一区二区三区激情综合 | 国产欧美日韩网站| 中文字幕综合| 亚洲人成在线一二| 国产精品第9页| 国产成人午夜电影网| 一本久久a久久精品vr综合| 在线一区av| 日韩成人激情在线| 久久久一区二区三区四区| 久久国产剧场电影| 欧美日韩国产免费一区二区三区| 黄色小说在线播放| 欧美一级片在线看| 国产精品久久久久久久电影| 国产一级二级三级| 精品一区二区免费| 日韩av大全| 欧美专区福利免费| 亚洲女人天堂网| 亚洲 欧美 日韩 综合| 成人高清视频在线观看| 久久99久久久久久| av成人综合| 久久久女女女女999久久| 国产av精国产传媒| 亚洲精品乱码久久久久| 毛毛毛毛毛毛毛片123| 亚洲最新色图| 96成人在线视频| 牛牛电影国产一区二区| 精品久久久网站| 国产一级在线免费观看| 粉嫩高潮美女一区二区三区| 欧美一区二区激情| 秋霞在线一区| 欧美专区在线播放| 国产乱子伦三级在线播放| 色狠狠一区二区| 欧美乱大交做爰xxxⅹ小说| 日韩精品亚洲一区| 亚洲一二三区精品| 国产精品中文| 欧美激情一级精品国产| 亚洲欧美另类日韩| 日韩欧美国产高清91| 疯狂揉花蒂控制高潮h| 天堂在线一区二区| 一区二区三区我不卡| 国产欧美视频在线| 久久久久久av| 你懂的视频在线观看| 欧美日韩日本视频| 老女人性淫交视频| av高清不卡在线| 热久久精品免费视频| 91精品高清| 国产福利久久精品| 成人香蕉视频| 久久精品中文字幕免费mv| 亚洲欧美黄色片| 在线一区二区三区四区五区| 黄色录像二级片| a级精品国产片在线观看| 黄色一级一级片| 午夜片欧美伦| 久久国产手机看片| 欧美aaa级| 97久久久免费福利网址| yourporn在线观看中文站| 日韩一区二区麻豆国产| 69精品久久久| 中文字幕不卡一区| 亚洲精品国产成人av在线| 日韩二区三区四区| 日本中文字幕在线视频观看| 精品日本12videosex| 99热在线播放| 美女色狠狠久久| 国精产品一区一区三区有限在线| 在线视频1区2区| 亚洲国产天堂久久综合网| 亚洲天堂中文字幕在线| 亚洲一区二区综合| 黄色裸体一级片| 91论坛在线播放| 成人三级做爰av| 美女一区二区三区| 日韩在线一级片| 欧美一区不卡| 五月天婷亚洲天综合网鲁鲁鲁| 国产精品巨作av| 成人黄色在线观看| **在线精品| 久久久日本电影| 久cao在线| 在线不卡国产精品| 婷婷亚洲一区二区三区| 欧美一区二区日韩一区二区| 91黑人精品一区二区三区| 亚洲电影一区二区三区| www.99re7| 国产精品视频第一区| 国产精品无码久久久久久| 成人免费毛片嘿嘿连载视频| 性色av浪潮av| 久久99精品久久久久久国产越南| 十八禁视频网站在线观看| 在线综合亚洲| 黄色一级视频在线播放| 欧美成人亚洲| 黄色污污在线观看| 久久精品播放| 亚洲第一在线综合在线| 国产欧美高清视频在线| 久久er99热精品一区二区三区| 成人动漫视频| 国产福利久久精品| av成人综合| 国产精品久久一区二区三区| 成人精品动漫一区二区三区| 99九九视频| 99re热精品视频| 国产麻豆乱码精品一区二区三区 | 欧美日韩视频专区在线播放| 男人天堂视频网| 日本国产一区二区| 一二三区免费视频| 色哟哟欧美精品| 波多野结衣一区二区三区在线| 色天使久久综合网天天| av毛片在线免费观看| 91福利在线导航| 凹凸精品一区二区三区| 欧美色手机在线观看| 国产又粗又长又大视频| 欧美一区二区日韩一区二区| 高清国产mv在线观看| 亚洲第一中文字幕| 无码国产伦一区二区三区视频 | 91福利在线导航| 中文字幕第三页| 在线不卡中文字幕播放| av一级黄色片| 亚洲高清一区二| 青青草免费在线| 中文日韩电影网站| 草莓福利社区在线| 久久久久九九九九| 国模冰冰炮一区二区| 国产精品欧美一区二区| 在线视频成人| 国产一区二区自拍| 精品久久国产| 麻豆传媒网站在线观看| 亚洲三级影院| 三级在线视频观看| 国产精品538一区二区在线| 好吊色视频一区二区三区| 久久久亚洲欧洲日产国码αv| 欧美性生交大片| 亚洲在线中文字幕| 亚洲国产成人精品女人久久| 制服丝袜激情欧洲亚洲| 天天操天天射天天舔| 国产一区二区三区精品久久久| 免费日本一区二区三区视频| 欧美韩日一区二区| 欧美特大特白屁股xxxx| 91色p视频在线| 欧美美女黄色| 中文字幕一区二区三区精彩视频| 在线日韩av| 国产又大又黄又粗又爽| 成人精品视频一区二区三区| 麻豆视频免费在线播放| 午夜精品免费在线观看| 伊人网站在线观看| 亚洲福利视频在线| 在线中文资源天堂| 69av成年福利视频| 成人在线精品| 欧日韩一区二区三区| 欧美在线不卡| 中文字幕第36页| 成人av电影免费观看| 青花影视在线观看免费高清| 日韩欧美在线看| 亚洲高清在线观看视频| 日日噜噜噜夜夜爽亚洲精品| 日韩脚交footjobhd| 99高清视频有精品视频| 成人看的视频| 少妇高潮喷水久久久久久久久久| 国产成a人无v码亚洲福利| 国产美女永久免费无遮挡| 性做久久久久久免费观看| 国产欧美熟妇另类久久久| 久久经典视频| 国产精品1区在线| 久久这里只有精品视频首页| 国产免费拔擦拔擦8x在线播放| 国产美女久久精品| 伊人成综合网伊人222| 真实国产乱子伦对白视频| 久久97超碰国产精品超碰| 人与嘼交av免费| 日韩欧美aⅴ综合网站发布| 人妻一区二区三区免费| 欧美精品亚州精品| 男人天堂久久| 亚洲国产精品一区二区第四页av| 国产精品久久国产愉拍| 精品国产一区在线| 亚洲影院在线观看| 国产高清免费av| 另类图片亚洲另类| 高清久久一区| 在线精品日韩| 精品一区二区三区在线播放| 女人裸体性做爰全过| 欧美在线观看一区二区| 成人亚洲综合天堂| 国产精品久久久久久久久| 亚洲视频分类| 妺妺窝人体色www在线观看| 国产性做久久久久久| 香蕉污视频在线观看| 国产午夜精品全部视频播放| 无人区在线高清完整免费版 一区二| 免费观看成人高| 日韩在线一二三区| 国产亚洲精品熟女国产成人| 欧洲中文字幕精品| av大片在线观看| 成人激情视频免费在线| 亚洲影视一区二区三区| 亚洲熟妇一区二区| 亚洲成人自拍网| 亚洲欧美色视频| 国产精品成久久久久三级| 欧美r级电影| gogo亚洲国模私拍人体| 午夜视频在线观看一区| 欧美日韩激情视频一区二区三区| 日本欧美黄网站| 成人影视亚洲图片在线| 特级黄色片视频| 亚洲高清免费视频| 你懂的视频在线| 国产视频福利一区| 中文字幕免费精品| 污网站免费观看| 色呦呦国产精品| 欧美激情二区| 国产视频精品网| 久久一区视频| 色欲人妻综合网| 日韩h在线观看| 欧美视频第一| 国产一区二区四区| 国产女人aaa级久久久级 | 成人做爰66片免费看网站| 99精品视频免费观看| 性欧美精品中出| 欧美一区欧美二区| www.综合| 精品一区二区成人免费视频| 成人av网站大全| www.av88| 午夜精品久久久久久久白皮肤 | 高清精品久久| 男人天堂999| 日韩毛片精品高清免费| 天天综合网在线| 91免费欧美精品| 国产精品最新自拍| 男人与禽猛交狂配| 亚洲人午夜精品| 成人性生交大片免费看中文视频|