Làm cách nào để ngăn chặn node.js bị sập? thử bắt không hoạt động


157

Theo kinh nghiệm của tôi, một máy chủ php sẽ ném một ngoại lệ vào nhật ký hoặc đến cuối máy chủ, nhưng node.js chỉ đơn giản là gặp sự cố. Bao quanh mã của tôi với một lần thử bắt không hoạt động vì mọi thứ được thực hiện không đồng bộ. Tôi muốn biết những gì người khác làm trong máy chủ sản xuất của họ.

Câu trả lời:


132

Các câu trả lời khác thực sự điên rồ khi bạn có thể đọc tại các tài liệu của chính Node tại http://nodejs.org/docs/latest/api/ process.html # process_event_uncaughtexception

Nếu ai đó đang sử dụng các câu trả lời đã nêu khác, hãy đọc Node Docs:

Lưu ý rằng đó uncaughtExceptionlà một cơ chế rất thô sơ để xử lý ngoại lệ và có thể bị xóa trong tương lai

PM2

Trước hết, tôi rất khuyên bạn nên cài đặt PM2cho Node.js. PM2 thực sự tuyệt vời trong việc xử lý sự cố và giám sát các ứng dụng Node cũng như cân bằng tải. PM2 ngay lập tức khởi động ứng dụng Node bất cứ khi nào nó gặp sự cố, dừng vì bất kỳ lý do nào hoặc ngay cả khi máy chủ khởi động lại. Vì vậy, nếu một ngày nào đó ngay cả sau khi quản lý mã của chúng tôi, ứng dụng gặp sự cố, PM2 có thể khởi động lại nó ngay lập tức. Để biết thêm thông tin, Cài đặt và Chạy PM2

Bây giờ trở lại giải pháp của chúng tôi để ngăn chặn ứng dụng bị sập.

Vì vậy, sau khi trải qua, cuối cùng tôi đã nghĩ ra những gì mà tài liệu Node gợi ý:

Không sử dụng uncaughtException, sử dụng domainsvới clusterthay thế. Nếu bạn sử dụng uncaughtException, hãy khởi động lại ứng dụng của bạn sau mỗi ngoại lệ chưa được xử lý!

TÊN MIỀN với cụm

Những gì chúng tôi thực sự làm là gửi phản hồi lỗi cho yêu cầu gây ra lỗi, đồng thời để những người khác hoàn thành trong thời gian bình thường của họ và ngừng lắng nghe các yêu cầu mới trong nhân viên đó.

Theo cách này, việc sử dụng tên miền đi đôi với mô-đun cụm, vì quy trình chính có thể rẽ nhánh một công nhân mới khi một công nhân gặp lỗi. Xem mã dưới đây để hiểu ý tôi là gì

Bằng cách sử dụng Domainvà khả năng phục hồi của việc tách chương trình của chúng tôi thành nhiều quy trình công nhân sử dụng Cluster, chúng tôi có thể phản ứng phù hợp hơn và xử lý các lỗi với độ an toàn cao hơn nhiều.

var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;

if(cluster.isMaster) 
{
   cluster.fork();
   cluster.fork();

   cluster.on('disconnect', function(worker) 
   {
       console.error('disconnect!');
       cluster.fork();
   });
} 
else 
{
    var domain = require('domain');
    var server = require('http').createServer(function(req, res) 
    {
        var d = domain.create();
        d.on('error', function(er) 
        {
            //something unexpected occurred
            console.error('error', er.stack);
            try 
            {
               //make sure we close down within 30 seconds
               var killtimer = setTimeout(function() 
               {
                   process.exit(1);
               }, 30000);
               // But don't keep the process open just for that!
               killtimer.unref();
               //stop taking new requests.
               server.close();
               //Let the master know we're dead.  This will trigger a
               //'disconnect' in the cluster master, and then it will fork
               //a new worker.
               cluster.worker.disconnect();

               //send an error to the request that triggered the problem
               res.statusCode = 500;
               res.setHeader('content-type', 'text/plain');
               res.end('Oops, there was a problem!\n');
           } 
           catch (er2) 
           {
              //oh well, not much we can do at this point.
              console.error('Error sending 500!', er2.stack);
           }
       });
    //Because req and res were created before this domain existed,
    //we need to explicitly add them.
    d.add(req);
    d.add(res);
    //Now run the handler function in the domain.
    d.run(function() 
    {
        //You'd put your fancy application logic here.
        handleRequest(req, res);
    });
  });
  server.listen(PORT);
} 

Mặc dù Domainđang chờ khấu hao và sẽ bị xóa vì sự thay thế mới được nêu trong Tài liệu của Node

Mô-đun này đang chờ khấu hao. Khi một API thay thế đã được hoàn thành, mô-đun này sẽ không được chấp nhận hoàn toàn. Người dùng hoàn toàn phải có chức năng mà tên miền cung cấp có thể dựa vào nó trong thời gian này nhưng sẽ phải chuyển sang một giải pháp khác trong tương lai.

Nhưng cho đến khi thay thế mới không được giới thiệu, Domain with Cluster là giải pháp tốt duy nhất mà Tài liệu Node gợi ý.

Để hiểu sâu DomainClusterđọc

https://nodejs.org/api/domain.html#domain_domain (Stability: 0 - Deprecated)

https://nodejs.org/api/cluster.html

Cảm ơn @Stanley Luo đã chia sẻ cho chúng tôi lời giải thích sâu sắc tuyệt vời này về Cụm và Tên miền

Cụm & tên miền


9
Một lời cảnh báo, Tên miền đang chờ khấu hao: liên kết . Phương thức được đề xuất, từ các tài liệu Node, là sử dụng cluster: link .
Paul

4
restart your application after every unhandled exception!Trong trường hợp 2000 người dùng đang sử dụng máy chủ web nút để truyền phát video và 1 người dùng có ngoại lệ thì việc khởi động lại sẽ không làm gián đoạn tất cả người dùng khác?
Vikas Bansal

2
@VikasBansal Có mà chắc chắn sẽ ngắt tất cả người sử dụng và đó là lý do nó xấu để sử dụng uncaughtExceptionvà sử dụng Domainvới Clusterthay vì vậy, nếu một người dùng phải đối mặt với một ngoại lệ vì vậy chỉ chủ đề của ông được lấy ra từ cụm và tạo ra cái mới cho anh ta. Và bạn cũng không cần phải khởi động lại máy chủ Node của mình. Mặt khác, nếu bạn sử dụng, uncaughtExceptionbạn phải khởi động lại máy chủ của mình mỗi khi bất kỳ người dùng nào của bạn gặp phải vấn đề. Vì vậy, sử dụng tên miền với cụm.
Thoáng

3
Chúng ta nên làm gì khi domainbị phản đối và loại bỏ hoàn toàn?
Jas

3
Tìm thấy hướng dẫn này cho những người không hiểu khái niệm về clusterworkers: sitepoint.com/ Quảng cáo
Stanley Luo

81

Tôi đặt mã này ngay dưới các yêu cầu và tuyên bố toàn cầu của mình:

process.on('uncaughtException', function (err) {
  console.error(err);
  console.log("Node NOT Exiting...");
});

làm việc cho tôi điều duy nhất tôi không thích về nó là tôi không nhận được nhiều thông tin như tôi sẽ làm nếu tôi để điều đó sụp đổ.


45
Một lời cảnh báo: phương pháp này hoạt động độc đáo, NHƯNG hãy nhớ rằng TẤT CẢ các phản hồi HTTP cần được kết thúc đúng cách. Điều đó có nghĩa là nếu xảy ra ngoại lệ chưa được xử lý trong khi bạn đang xử lý yêu cầu HTTP, bạn vẫn phải gọi end () trên Đối tượng http.ServerResponse. Tuy nhiên, bạn thực hiện điều này là tùy thuộc vào bạn. Nếu bạn không làm điều này, yêu cầu sẽ treo cho đến khi trình duyệt từ bỏ. Nếu bạn có đủ các yêu cầu này, máy chủ có thể hết bộ nhớ.
BMiner

3
@BMiner, bạn có thể cung cấp một triển khai tốt hơn? Tôi nhận thấy vấn đề này (yêu cầu treo) vì vậy điều này thực sự không tốt hơn là chỉ khởi động lại máy chủ bằng cách sử dụng foreverhoặc một cái gì đó.
pixelfreak

6
Điều này đòi hỏi một lời giải thích sâu sắc. Tôi biết điều này thật tệ, nhưng bất cứ khi nào có một ngoại lệ chưa được phát hiện, máy chủ của bạn cần khởi động lại càng sớm càng tốt. Thực sự, mục đích của sự kiện 'unsaughtException' là sử dụng nó như một cơ hội để gửi email cảnh báo, sau đó sử dụng process.exit (1); để tắt máy chủ. Bạn có thể sử dụng mãi mãi hoặc một cái gì đó tương tự để khởi động lại máy chủ. Mọi yêu cầu HTTP đang chờ xử lý sẽ hết thời gian và không thành công. Người dùng của bạn sẽ giận bạn. Nhưng, đó là giải pháp tốt nhất. Lý do tại sao bạn hỏi? Thanh toán stackoverflow.com/questions/8114977/
BMiner

3
Để có thêm thông tin từ lỗi chưa được phát hiện, hãy sử dụng: console.trace (err.stack);
Jesse Dunlap

2
CẢNH BÁO: Tài liệu cho nút nói, không có gì chắc chắn, rằng bạn không bao giờ nên làm điều này vì nó nguy hiểm điên rồ: nodejs.org/api/ process.html# process_event_uncaughtexception
Jeremy Logan

28

Như đã đề cập ở đây, bạn sẽ thấy error.stackcung cấp một thông báo lỗi đầy đủ hơn, chẳng hạn như số dòng gây ra lỗi:

process.on('uncaughtException', function (error) {
   console.log(error.stack);
});

12

Thử supervisor

npm install supervisor
supervisor app.js

Hoặc bạn có thể cài đặt foreverthay thế.

Tất cả điều này sẽ làm là khôi phục máy chủ của bạn khi nó gặp sự cố bằng cách khởi động lại nó.

forever có thể được sử dụng trong mã để phục hồi một cách duyên dáng bất kỳ quy trình nào gặp sự cố.

Các forevertài liệu có thông tin chắc chắn về việc thoát / xử lý lỗi theo chương trình.


9
Chắc chắn đây không phải là giải pháp ... Trong thời gian máy chủ ngừng hoạt động, nó không thể đáp ứng các yêu cầu mới đến. Một ngoại lệ có thể được ném ra từ mã ứng dụng - máy chủ cần phản hồi với lỗi 500, không chỉ là sự cố và hy vọng nó được khởi động lại.
Kiến Kutschera

20
Vì vậy, là một tin tặc, người ta có thể nhận ra rằng họ cần gửi một yêu cầu đơn giản đến máy chủ và bỏ lỡ một tham số yêu cầu - dẫn đến một undef trong javascript khiến cho node.js bị sập. Với đề nghị của bạn, tôi có thể giết toàn bộ cụm của bạn nhiều lần. Câu trả lời là làm cho ứng dụng thất bại một cách duyên dáng - tức là xử lý ngoại lệ chưa được xử lý và không bị sập. Nếu máy chủ xử lý nhiều phiên voip thì sao? nó không thể chấp nhận được để nó bị sập và cháy và cho tất cả các phiên hiện có để chết với nó. người dùng của bạn sẽ sớm rời đi.
Ant Kutschera

5
@AntKutschera đó là lý do ngoại lệ nên là trường hợp ngoại lệ. Ngoại lệ duy nhất nên bắn trong tình huống mà bạn không thể phục hồi và nơi quá trình này sụp đổ. Bạn nên sử dụng các phương tiện khác để xử lý các trường hợp đặc biệt này . Nhưng tôi thấy quan điểm của bạn. Bạn nên thất bại một cách duyên dáng nếu có thể. Tuy nhiên, có những trường hợp tiếp tục với trạng thái bị hỏng sẽ gây ra nhiều thiệt hại hơn.
Raynos

2
Vâng, có nhiều trường phái khác nhau ở đây. Cách tôi học nó (Java chứ không phải Javascript) có những mức độ chấp nhận được mà bạn mong đợi, có thể là ngoại lệ kinh doanh và sau đó có các ngoại lệ hoặc lỗi trong thời gian chạy, nơi bạn không nên khôi phục, như hết bộ nhớ. Một vấn đề với việc không thất bại một cách duyên dáng là một số thư viện mà tôi viết có thể tuyên bố rằng nó ném một ngoại lệ trong trường hợp có thể phục hồi được, cho biết nơi người dùng có thể sửa lỗi đầu vào của họ. trong ứng dụng của bạn, bạn không đọc tài liệu của tôi và chỉ gặp sự cố, trong đó người dùng có thể đã khôi phục hoàn toàn
Ant Kutschera

1
@AntKutschera Đây là lý do tại sao chúng tôi đăng nhập ngoại lệ. Bạn nên phân tích nhật ký sản xuất của mình để biết các ngoại lệ phổ biến và tìm hiểu xem và làm thế nào bạn có thể phục hồi từ chúng, thay vì để máy chủ gặp sự cố. Tôi đã sử dụng phương pháp đó với PHP, Ruby on Rails và Node. Bất kể bạn có thoát khỏi một quy trình hay không, mỗi khi bạn đưa ra một lỗi 500, bạn đang làm cho người dùng của mình không hài lòng. Đây không phải là thực hành cụ thể về JavaScript hoặc Node.
Eric Elliott

7

Sử dụng tính năng bắt thử có thể giải quyết các lỗi chưa được phát hiện, nhưng trong một số trường hợp phức tạp, nó sẽ không thực hiện đúng công việc như bắt chức năng async. Hãy nhớ rằng trong Node, mọi cuộc gọi chức năng không đồng bộ đều có thể chứa hoạt động sự cố ứng dụng tiềm năng.

Sử dụng uncaughtExceptionlà một cách giải quyết nhưng nó được công nhận là không hiệu quả và có khả năng bị xóa trong các phiên bản tương lai của Node, vì vậy đừng tin vào điều đó.

Giải pháp lý tưởng là sử dụng tên miền: http://nodejs.org/api/domain.html

Để đảm bảo ứng dụng của bạn hoạt động và chạy ngay cả khi máy chủ của bạn bị sập, hãy sử dụng các bước sau:

  1. sử dụng cụm nút để fork nhiều tiến trình trên mỗi lõi. Vì vậy, nếu một quá trình chết, một quá trình khác sẽ tự động khởi động. Kiểm tra: http://nodejs.org/api/cluster.html

  2. sử dụng tên miền để bắt hoạt động không đồng bộ thay vì sử dụng thử hoặc bắt. Tôi không nói rằng thử hay bắt là suy nghĩ tồi!

  3. sử dụng mãi mãi / giám sát viên để giám sát các dịch vụ của bạn

  4. thêm daemon để chạy ứng dụng nút của bạn: http://upstart.ubfox.com

hi vọng điêu nay co ich!


4

Hãy thử mô-đun nút pm2, nó rất phù hợp và có tài liệu tuyệt vời. Trình quản lý quy trình sản xuất cho các ứng dụng Node.js với bộ cân bằng tải tích hợp. xin vui lòng tránh unceptionException cho vấn đề này. https://github.com/Unitech/pm2


`khởi động lại ứng dụng của bạn sau mỗi ngoại lệ chưa được xử lý! 'Trong trường hợp 2000 người dùng đang sử dụng máy chủ web nút để truyền phát video và 1 người dùng có ngoại lệ thì việc khởi động lại sẽ không làm gián đoạn tất cả người dùng khác?
Vikas Bansal

Tôi đã rất hạnh phúc khi phát hiện ra PM2. phần mềm tuyệt vời
Mladen Janjetovic

0

UncaughtException là "một cơ chế rất thô sơ" (rất đúng) và các tên miền hiện không được chấp nhận. Tuy nhiên, chúng ta vẫn cần một số cơ chế để bắt lỗi xung quanh các miền (logic). Thư viện:

https://github.com/vacuumlabs/yacol

có thể giúp bạn làm điều này. Với một chút viết thêm, bạn có thể có ngữ nghĩa miền tốt đẹp xung quanh mã của bạn!


0

Hoạt động tuyệt vời trên restify:

server.on('uncaughtException', function (req, res, route, err) {
  log.info('******* Begin Error *******\n%s\n*******\n%s\n******* End Error *******', route, err.stack);
  if (!res.headersSent) {
    return res.send(500, {ok: false});
  }
  res.write('\n');
  res.end();
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.