2013-06-27 23 views
10

Tôi thấy nhiều chức năng đồng bộ trong số file system library. chẳng hạn như fs.readFileSync(filename, [options]).Nếu nodejs sử dụng non blocking IO, fs.readFileSync được triển khai như thế nào?

Làm thế nào (và tại sao) các chức năng này được triển khai nếu nút có IO không đồng bộ/không chặn và không có phương thức ngủ - và tôi có thể sử dụng cùng một cơ chế để thực hiện các chức năng đồng bộ khác không?

+0

Câu hỏi hay! có lẽ một người sẽ cần phải tái tạo lại coroutines cho javascript và phơi bày vòng lặp sự kiện để cho phép viết mã không chặn đồng bộ trong node.js. – Alex

Trả lời

15
fs.readFileSync() 

thực sự chỉ là một wrapper cho hàm

fs.readSync() 

. Vì vậy, câu hỏi là fs.readSync() được thực hiện như thế nào so với fs.read(). Nếu bạn nhìn vào việc triển khai thực hiện hai hàm này, chúng đều tận dụng lợi thế của mô-đun kết buộc. Trong trường hợp này được intialized để

var binding = process.binding('fs'). 

và các cuộc gọi được

binding.read(fd, buffer, offset, length, position, wrapper);//async 
var r = binding.read(fd, buffer, offset, length, position);//sync 

tương ứng. Một khi chúng ta đang ở trong mô-đun "ràng buộc", chúng tôi đang ở trong v8, nút _ #####. Cc đất. Việc thực hiện ràng buộc ('fs') có thể được tìm thấy trong mã kho lưu trữ nút, trong node_file.cc. Công cụ nút cung cấp quá tải cho các cuộc gọi C++, một cuộc gọi lại, một cuộc gọi không thực hiện. Mã node_file.cc tận dụng lợi thế của lớp req_wrap. Đây là trình bao bọc cho động cơ v8. Trong node_file.cc, chúng ta thấy điều này:

#define ASYNC_CALL(func, callback, ...)       \ 
    FSReqWrap* req_wrap = new FSReqWrap(#func);      \ 
    int r = uv_fs_##func(uv_default_loop(), &req_wrap->req_,  \ 
     __VA_ARGS__, After);          \ 
    req_wrap->object_->Set(oncomplete_sym, callback);    \ 
    req_wrap->Dispatched();           \ 
    if (r < 0) {             \ 
    uv_fs_t* req = &req_wrap->req_;        \ 
    req->result = r;            \ 
    req->path = NULL;            \ 
    req->errorno = uv_last_error(uv_default_loop()).code;   \ 
    After(req);             \ 
    }                \ 
    return scope.Close(req_wrap->object_); 

#define SYNC_CALL(func, path, ...)        \ 
    fs_req_wrap req_wrap;           \ 
    int result = uv_fs_##func(uv_default_loop(), &req_wrap.req, __VA_ARGS__, NULL); \ 
    if (result < 0) {            \ 
    int code = uv_last_error(uv_default_loop()).code;    \ 
    return ThrowException(UVException(code, #func, "", path)); \ 
    } 

Lưu ý rằng SYNC_CALL sử dụng một gói khác. Dưới đây là mã cho các nhà xây dựng req_wrap có liên quan đối với phương pháp ASYNC, được tìm thấy trong req_wrap.h

ReqWrap() { 
    v8::HandleScope scope; 
    object_ = v8::Persistent<v8::Object>::New(v8::Object::New()); 

    v8::Local<v8::Value> domain = v8::Context::GetCurrent() 
            ->Global() 
            ->Get(process_symbol) 
            ->ToObject() 
            ->Get(domain_symbol); 

    if (!domain->IsUndefined()) { 
     // fprintf(stderr, "setting domain on ReqWrap\n"); 
     object_->Set(domain_symbol, domain); 
    } 

    ngx_queue_insert_tail(&req_wrap_queue, &req_wrap_queue_); 
    } 

Chú ý rằng chức năng này đang tạo ra một đối tượng phạm vi v8 mới để xử lý các hoạt động của sự kiện này. Đây là nơi phần không đồng bộ của công cụ không đồng bộ xảy ra. Công cụ v8 khởi chạy một môi trường giải thích javascript mới để xử lý cuộc gọi cụ thể này một cách riêng biệt. Tóm lại, nếu không xây dựng/sửa đổi phiên bản nút của riêng bạn, bạn không thể thực hiện các phiên bản cuộc gọi không đồng bộ/đồng bộ của riêng mình, giống như cách mà nút đó thực hiện. Điều đó đang được nói, không đồng bộ thực sự chỉ áp dụng cho các hoạt động I/O. Có lẽ một mô tả lý do tại sao bạn nghĩ rằng bạn cần những thứ để được đồng bộ hơn sẽ được theo thứ tự. Nói chung, nếu bạn tin rằng nút không hỗ trợ điều gì đó bạn muốn làm, bạn chỉ cần không nắm lấy cơ chế gọi lại để nó có tiềm năng đầy đủ.

Điều đó đang được nói, bạn có thể xem xét sử dụng mô-đun nút sự kiện để triển khai trình xử lý sự kiện của riêng mình nếu bạn cần hành vi không đồng bộ. Và bạn có thể xem xét các phần mở rộng tự nhiên nếu có những thứ bạn tuyệt vọng cần phải làm đồng bộ, tuy nhiên, tôi khuyên bạn nên chống lại điều này. Hãy xem xét cách bạn có thể làm việc trong vòng lặp sự kiện không đồng bộ để có được những gì bạn cần làm theo cách này. Chấp nhận phong cách suy nghĩ này, hoặc chuyển sang ngôn ngữ khác.

Bắt buộc ngôn ngữ xử lý mọi thứ theo cách mà nó không muốn xử lý chúng là cách tuyệt vời để viết mã xấu.

+1

Câu trả lời rất kỹ lưỡng. 'buộc một ngôn ngữ để xử lý mọi thứ theo cách nó không muốn xử lý chúng '- là một câu nói thú vị. Đó không phải là chính xác những gì mà người dùng nodeJS đang làm với 'fs.readFileSync'? Tại sao nó tồn tại? Có lẽ có chỗ cho các cuộc gọi đồng bộ trong nút sau khi tất cả? –

+0

Stackoverflow ... không bao giờ biết khi nào bạn đưa cho ai đó một công cụ mà họ chưa sẵn sàng sử dụng.Đó là câu cuối cùng (tại sao chọn một câu được đề xuất trong một nhóm sự kiện ???) là một tuyên bố từ chối ... này, tôi không ủng hộ ý tưởng cố sửa đổi Node! Nhưng nếu bạn phải, đây là cách bạn làm điều đó. Bạn có thể đúng, có thể có chỗ cho nhiều cuộc gọi đồng bộ hơn và để cho các nhà phát triển quyết định sử dụng, nhưng ngay bây giờ, các nhà phát triển của Node đã quyết định rằng đây không phải là trường hợp, đó chính xác là quan điểm của tôi. – ChrisCM

+0

Theo câu hỏi của bạn, các phương thức FS đồng bộ tồn tại vì hai lý do. A: thu thập thông tin cấu hình như một phần của bước khởi tạo là một nhu cầu tương đối phổ biến và đơn giản hơn đồng bộ. B: một loạt các yêu cầu đĩa đồng bộ trong những trường hợp nhất định thực sự hiệu quả hơn là làm ngập đĩa với một loạt các yêu cầu không đồng bộ mà nó không được chuẩn bị để xử lý. Điều này cũng đúng với máy chủ IO (các cuộc tấn công DDOS) nó chỉ là một tình huống khó khăn hơn (như 10000x) để tạo ra. Tuy nhiên, nó là rất dễ dàng cho một quá trình duy nhất để quá tải một ổ đĩa và làm chậm chính nó xuống theo cấp số nhân. – ChrisCM

Các vấn đề liên quan