成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

編寫自己的js運行時第二篇

開發 前端
第一版基于V8實現了一個樸素版的服務器,第二版支持了多進程架構,并且支持了SO_REUSEPORT。本文介紹一下第二版的一些實現,設計上還是比較隨意的,目前主要關注功能。

[[410500]]

前言:第一版基于V8實現了一個樸素版的服務器,第二版支持了多進程架構,并且支持了SO_REUSEPORT。本文介紹一下第二版的一些實現,設計上還是比較隨意的,目前主要關注功能。

首先我們看看第二版怎么使用。

1 通過fork共享端口

  1. const TCPServer = TCP(); 
  2.  
  3. const tcpServer = new TCPServer('127.0.0.1', 8989); 
  4.  
  5. tcpServer.socket(); 
  6. tcpServer.setReusePort(1); 
  7. tcpServer.bind(); 
  8. tcpServer.listen(); 
  9.  
  10. for (let i = 0; i < 3; i++) { 
  11.  
  12.     // 等于0說明是子進程,進入處理連接的邏輯,否則是主進程,循環創建多個進程 
  13.     if (Child_Process.fork() === 0) { 
  14.         while(1) { 
  15.             tcpServer.accept(); 
  16.         }  
  17.     } 
  18.  
  19.  
  20. // 主進程創建完子進程后自己進入阻塞狀態 
  21.  
  22. Child_Process.wait(); 

通過fork共享端口版本的原理是主進程首先創建一個socket并且綁定一個端口。然后通過fork的方式讓多個子進程共享監聽的端口。最后主進程進入阻塞模式。核心實現是fork,我們看看代碼。

  1. static Local<Object> ChildProcess(Isolate * isolate) { 
  2.   Local<ObjectTemplate> target = ObjectTemplate::New(isolate); 
  3.   Local<String> forkName = String::NewFromUtf8(isolate, "fork", NewStringType::kNormal, strlen("fork")).ToLocalChecked(); 
  4.   Local<String> waitName = String::NewFromUtf8(isolate, "wait", NewStringType::kNormal, strlen("wait")).ToLocalChecked(); 
  5.  
  6.   target->Set(forkName, FunctionTemplate::New(isolate, Child_Process::Fork)); 
  7.   target->Set(waitName, FunctionTemplate::New(isolate, Child_Process::Wait)); 
  8.   Local<Object> obj; 
  9.   bool ignore = target->NewInstance(isolate->GetCurrentContext()).ToLocal(&obj); 
  10.   return obj; 
  11.  

第二版加入了進程模塊,上面的代碼定義了進程模塊的功能。然后注入到全局變量,No.js目前的設計中,每個模塊是一個全局變量,和我們使用Object、Array一樣,不像Node.js的C++模塊是鏈成一條鏈表。

  1. // 模塊名稱 
  2. Local<Value> child_process_name = String::NewFromUtf8(isolate, "Child_Process",  strlen("Child_Process")).ToLocalChecked();// 注冊全局變量 
  3. global->Set(context, child_process_name, ChildProcess(isolate)); 

這樣就完成了模塊的注入,在JS層就可以使用了。下面我們看看具體的實現。

  1. class Child_Process { 
  2.     public
  3.  
  4.         static void Fork(const FunctionCallbackInfo<Value>& info) { 
  5.             info.GetReturnValue().Set(Number::New(info.GetIsolate(), fork())); 
  6.         } 
  7.  
  8.         static void Wait(const FunctionCallbackInfo<Value>& info) { 
  9.             int status; 
  10.             wait(&status); 
  11.         } 
  12.  
  13. }; 

實現很簡單,只是對fork函數的封裝,重點在于對fork函數的理解, 執行fork函數后會創建一個子進程,子進程的fork返回0,主進程返回子進程id,通過這個特性,我們可以寫一個if判斷處理下一步的邏輯。

2 通過fork+execve+reuserport共享端口

第二種模式是比較復雜且比較高性能的模式,之前的文章介紹過不同服務器架構的實現和優缺點,第一種fork共享端口的模式中,會有驚群和負載不均衡的問題,有興趣可以參考之前的文章,就不多介紹。接下來看第二種模式的使用(下面代碼是execve-server.js)。

  1. const TCPServer = TCP(); 
  2.  
  3. const tcpServer = new TCPServer('127.0.0.1', 8989); 
  4.  
  5. tcpServer.socket(); 
  6. tcpServer.setReusePort(1); 
  7. tcpServer.bind(); 
  8. tcpServer.listen(); 
  9.  
  10. const isMaster = Child_Process.getEnv("isMaster") === ""
  11.  
  12. if (isMaster) { 
  13.  
  14.     for (let i = 0; i < 3; i++) { 
  15.         Child_Process.execve("./No""execve-server.js");   
  16.     } 
  17.     Child_Process.wait(); 
  18.  
  19. else { 
  20.  
  21.     while(1) { 
  22.         tcpServer.accept(); 
  23.     } 
  24.  

我們知道多個進程是不能綁定同一個端口的,第一種模式中通過fork繞過了這個限制,第二版面對并解決了這個問題。上面代碼的邏輯看起來也很簡單,主進程創建多個子進程,并且在每個子進程里執行同一個文件execve-server.js。然后在execve-server.js中通過環境變量isMaster區分主子進程進行不同的處理,當然也可以執行新的文件。這里是為了提到isMaster這個環境變量。上面代碼中,重點是setReusePort和execve,下面我們具體看一下實現。

  1. static Local<Object> ChildProcess(Isolate * isolate) { 
  2.   Local<ObjectTemplate> target = ObjectTemplate::New(isolate); 
  3.   Local<String> execveName = String::NewFromUtf8(isolate, "execve", NewStringType::kNormal, strlen("execve")).ToLocalChecked(); 
  4.   Local<String> getEnvName = String::NewFromUtf8(isolate, "getEnv", NewStringType::kNormal, strlen("getEnv")).ToLocalChecked(); 
  5.  
  6.   target->Set(execveName, FunctionTemplate::New(isolate, Child_Process::Execve)); 
  7.   target->Set(getEnvName, FunctionTemplate::New(isolate, Child_Process::GetEnv)); 
  8.   Local<Object> obj; 
  9.   bool ignore = target->NewInstance(isolate->GetCurrentContext()).ToLocal(&obj); 
  10.   return obj; 
  11.  

同樣,先定義入口使得JS可以調用。另外給TCP模塊定義了一個新接口setReusePort。

  1. SetProtoMethod(isolate, TCPServer, "setReusePort", TCPServer::TCPServerSetUserPort); 

接下來看底層的實現,首先看TCPServerSetUserPort的實現。

  1. static void TCPServerSetUserPort(const FunctionCallbackInfo<Value>& info) { 
  2.     int on = info[0].As<Uint32>()->Value(); 
  3.     GetTCPServer(info.Holder())->Setsockopt(SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); 
  4.  
  5.  
  6.  
  7.  
  8. int Setsockopt(int levelint optionName, const void *optionValue, socklen_t option_len) { 
  9.  
  10.     return setsockopt(listerFd, level, optionName, optionValue, option_len); 
  11.  

簡單地對setsockopt的封裝,沒有太多需要講的。下面看環境變量和execve的邏輯。

  1. class Child_Process { 
  2.     public
  3.  
  4.         static void GetEnv(const FunctionCallbackInfo<Value>& info) { 
  5.             String::Utf8Value key(info.GetIsolate(), info[0]); 
  6.             char * value = getenv(*key); 
  7.             // Logger::log(value); 
  8.             Local<String> str = String::NewFromUtf8(info.GetIsolate(), value, NewStringType::kNormal, strlen(value)).ToLocalChecked(); 
  9.             info.GetReturnValue().Set(str); 
  10.         } 
  11.  
  12.         static void Execve(const FunctionCallbackInfo<Value>& info) { 
  13.             int length = info.Length(); 
  14.             char** args = new char*[length + 1]; 
  15.             int i = 0; 
  16.             for (i = 0; i < length; i++) { 
  17.                 String::Utf8Value arg(info.GetIsolate(), info[i]); 
  18.                 args[i] = strdup(*arg); 
  19.             } 
  20.             args[i] = NULL
  21.             char *env[] = { "isMaster=0"NULL }; 
  22.             // int fd[2]; 
  23.             // socketpair(AF_UNIX, SOCK_STREAM, 0, fd); 
  24.  
  25.             int pid = fork(); 
  26.             if (pid == 0) { 
  27.                 // close(fd[0]); 
  28.                 execve(args[0], args, env); 
  29.                 // execve會加載可執行文件,從新的入口開始執行,執行到這說明execve出錯了 
  30.                 write(1, strerror(errno), sizeof(strerror(errno))); 
  31.                 exit(-1); 
  32.             } 
  33.             // close(fd[1]); 
  34.             if (args) { 
  35.                 for (int i = 0; i < length && args[i]; i++) { 
  36.                     free(args[i]); 
  37.                 } 
  38.                 delete [] args; 
  39.             } 
  40.         } 
  41.  
  42. }; 

目前只實現了獲取環境變量的邏輯,主要是對getenv的封裝。execve的代碼看起來很多,主要是參數的處理,我們只需要關注下面的代碼。

  1. int pid = fork(); 
  2.  // 子進程重新加載新的可執行文件 
  3.  if (pid == 0) { 
  4.      // close(fd[0]); 
  5.      execve(args[0], args, env); 
  6.      // execve會加載可執行文件,從新的入口開始執行,執行到這說明execve出錯了 
  7.      write(1, strerror(errno), sizeof(strerror(errno))); 
  8.      exit(-1); 
  9.  } 

首先通過fork創建一個子進程,然后通過execve加載要執行的代碼(這里是./No execve-server.js)。重點是execve函數會重新加載可執行文件,然后從新的地址(可執行文件中指定)開始執行,所以我們看到execve后是不需要return的,因為下面的代碼不會執行了,除非execve執行出錯了,這里我們打印錯誤信息然后退出進程。第二種模式的好處就是我們可以隨意在多個js文件中綁定同一個端口而不會報錯,這得益于SO_REUSEPORT的特性。SO_REUSEPORT讓每個進程對應一個連接隊列,解決了驚群問題,并且內核負責連接分發的復雜均衡,不僅提高了性能,同時使得應用程序變得簡單。

3 和Node.js相比

Node.js的進程是通過fork+execve實現的,Cluster模塊基于進程模塊實現了多進程架構,主要有兩種模式:輪詢和共享,輪詢就是主進程接收連接分發給子進程處理,子進程不接收連接只負責處理業務邏輯。這種模式的好處是沒有驚群現象,但是主進程的能力會成為服務器的瓶頸,共享模式和本文的第一種一樣,多個子進程共享一個端口,但是實現不一樣,本文是主進程創建socket通過fork子進程共享,Node.js是主進程創建socket通過文件描述符的方式傳遞給子進程,不過殊途同歸,主要是讓多個子進程共享監聽socket。本文的第二種模式,目前Node.js還不支持,因為SO_REUSEPORT是比較新的特性,但是對性能提升非常大。

后記:以上就是第二版新增的功能,我們已經具備了一個可以處理請求的多進程架構服務器,但是目前還是單進程里串行處理請求的,我們還需要很多東西,文件、IPC、事件驅動模塊、HTTP解析器等等,后續會考慮把最近寫的Node.js io_uring Addon合進來。最近把頭文件和V8靜態庫都打包了,有興趣的同學可以自行編譯運行https://github.com/theanarkh/No.js。

 

責任編輯:姜華 來源: 編程雜技
相關推薦

2024-03-21 09:15:58

JS運行的JavaScrip

2021-08-27 00:21:19

JSJust源碼

2014-03-28 13:30:36

2022-10-08 00:00:00

V8channel對象

2011-06-21 10:28:49

Oracle

2015-07-20 15:44:46

Swift框架MJExtension反射

2017-04-10 14:46:29

AndroidGradleBuild.gradl

2022-08-02 10:26:09

網絡層網絡網絡協議

2011-03-14 16:05:17

2023-09-12 17:38:41

2022-10-08 00:06:00

JS運行V8

2020-12-07 13:31:43

GoMutex開發者

2021-10-14 09:53:38

鴻蒙HarmonyOS應用

2019-07-12 09:30:12

DashboardDockerDNS

2021-09-11 15:38:23

容器運行鏡像開放

2021-09-07 11:19:42

操作系統華為鴻蒙

2023-08-27 21:07:02

2023-08-21 09:37:57

MySQL工具MariaDB

2024-01-29 08:07:42

FlinkYARN架構

2022-09-07 08:11:30

LinuxLKRG結構體
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美成人a∨高清免费观看 色999日韩 | 亚洲视频免费 | 日韩高清不卡 | 国产91av视频在线观看 | 日本久久精品视频 | 精品一区二区三区四区外站 | 日本精品一区二区三区在线观看视频 | 久久午夜视频 | 天天综合永久入口 | 欧美一级免费片 | 久久一二区 | www.日韩| 成年人在线观看视频 | 精品一区二区三区视频在线观看 | 国产精品污污视频 | 久久99久久 | 欧美日韩高清 | 亚洲97 | 久久久久久亚洲精品 | 日韩另类视频 | 日韩欧美国产一区二区 | 欧美黄色网 | 精品国产一区二区国模嫣然 | 精品免费国产一区二区三区 | www.99re5.com| 国产精品一区视频 | 男女羞羞视频在线免费观看 | 老子午夜影院 | 亚洲免费三级 | 久久大陆 | 久在线视频播放免费视频 | 九一视频在线播放 | 91精品国产综合久久香蕉麻豆 | 毛片网在线观看 | 日韩午夜一区二区三区 | 日韩综合网 | 国产精品三级 | 欧美久久久网站 | 欧美精品乱码久久久久久按摩 | 国产黄色在线观看 | 羞羞视频网站免费看 |