Rumah > Artikel > hujung hadapan web > Pemikiran dan amalan pemisahan front-end dan back-end berdasarkan NodeJS (6) Nginx Node.js Java software stack deployment practice_node.js
Struktur tindanan perisian tradisional aplikasi dalam talian Taobao ialah Nginx Velocity Java, iaitu:
Dalam sistem ini, Nginx memajukan permintaan kepada aplikasi Java, yang memproses urus niaga dan kemudiannya memaparkan data ke halaman akhir menggunakan templat Velocity.
Selepas memperkenalkan Node.js, kami pasti menghadapi masalah berikut:
Bagaimanakah topologi timbunan teknologi harus direka bentuk dan bagaimanakah kaedah penggunaan harus dipilih untuk saintifik dan munasabah? Selepas projek siap, bagaimana untuk membahagikan trafik supaya mudah dan cepat untuk operasi dan penyelenggaraan? Apabila menghadapi masalah dalam talian, bagaimana untuk menyelesaikan bahaya secepat mungkin dan mengelakkan kerugian yang lebih besar? Bagaimana untuk memastikan kesihatan aplikasi dan mengurusnya pada tahap penjadualan pengimbangan beban? Mewarisi topologi sistem
Menurut pemikiran dan amalan kami mengenai pemisahan bahagian hadapan dan belakang (2) - Penerokaan templat berdasarkan pemisahan bahagian hadapan dan belakang, Velocity perlu digantikan dengan Node.js, supaya struktur ini menjadi :
Ini sudah tentu matlamat yang ideal. Walau bagaimanapun, memperkenalkan lapisan Node.js buat kali pertama dalam tindanan tradisional ialah percubaan baharu. Demi keselamatan, kami memutuskan untuk mendayakan teknologi baharu hanya pada halaman koleksi item kegemaran (shoucang.taobao.com/item_collect.htm), dan halaman lain akan terus menggunakan penyelesaian tradisional. Iaitu, Nginx menentukan jenis halaman yang diminta dan menentukan sama ada permintaan itu harus dimajukan ke Node.js atau Java. Jadi, struktur akhir menjadi:
Pelan penempatan
Struktur di atas nampaknya tiada masalah, tetapi sebenarnya masalah baru masih menanti di hadapan. Dalam struktur tradisional, Nginx dan Java digunakan pada pelayan yang sama, Nginx mendengar pada port 80 dan berkomunikasi dengan Java yang mendengar pada port tinggi 7001. Sekarang Node.js telah diperkenalkan, kita perlu menjalankan proses baharu yang mendengar port Patutkah kita menggunakan Node.js dan Nginx Java pada mesin yang sama, atau patutkah kita menggunakan Node.js dalam kelompok yang berasingan?
Mari bandingkan ciri-ciri dua kaedah:
Taobao Favorites ialah aplikasi dengan berpuluh-puluh juta purata PV harian, yang mempunyai keperluan yang sangat tinggi untuk kestabilan (malah, ketidakstabilan dalam talian mana-mana produk tidak boleh diterima). Jika anda menggunakan penyelesaian penggunaan kelompok yang sama, anda hanya memerlukan satu pengedaran fail dan dua aplikasi dimulakan semula untuk melengkapkan keluaran Jika rollback diperlukan, anda hanya perlu mengendalikan pakej garis dasar sekali. Dari segi prestasi, terdapat beberapa kelebihan teori untuk digunakan dalam kelompok yang sama (walaupun jalur lebar suis dan kependaman intranet sangat optimistik). Bagi hubungan satu-dengan-banyak atau banyak-dengan-satu, secara teorinya mungkin untuk menggunakan sepenuhnya pelayan, tetapi berbanding dengan keperluan kestabilan, perkara ini tidak begitu mendesak dan perlu diselesaikan. Oleh itu, dalam transformasi kegemaran, kami memilih penyelesaian penggunaan kelompok yang sama.
Mod skala kelabu
Untuk memastikan kestabilan maksimum, transformasi ini tidak langsung mengalih keluar kod Velocity sepenuhnya. Terdapat hampir 100 pelayan dalam kelompok aplikasi Kami menggunakan pelayan sebagai butiran untuk memperkenalkan trafik secara beransur-ansur. Dalam erti kata lain, walaupun proses Java Node.js berjalan pada semua pelayan, sama ada terdapat peraturan pemajuan yang sepadan pada Nginx menentukan sama ada permintaan untuk pengumpulan harta karun pada pelayan ini akan diproses oleh Node.js. Konfigurasi Nginx ialah:
location = "/item_collect.htm" { proxy_pass http://127.0.0.1:6001; # Node.js 进程监听的端口 }
只有添加了这条 Nginx 规则的服务器,才会让 Node.js 来处理相应请求。通过 Nginx 配置,可以非常方便快捷地进行灰度流量的增加与减少,成本很低。如果遇到问题,可以直接将 Nginx 配置进行回滚,瞬间回到传统技术栈结构,解除险情。
第一次发布时,我们只有两台服务器上启用了这条规则,也就是说大致有不到 2% 的线上流量是走 Node.js 处理的,其余的流量的请求仍然由 Velocity 渲染。以后视情况逐步增加流量,最后在第三周,全部服务器都启用了。至此,生产环境 100% 流量的商品收藏页面都是经 Node.js 渲染出来的(可以查看源代码搜索 Node.js 关键字)。
转
灰度过程并不是一帆风顺的。在全量切流量之前,遇到了一些或大或小的问题。大部分与具体业务有关,值得借鉴的是一个技术细节相关的陷阱。
健康检查
在传统的架构中,负载均衡调度系统每隔一秒钟会对每台服务器 80 端口的特定 URL 发起一次 <font face="NSimsun">get</font>
请求,根据返回的 HTTP Status Code 是否为 <font face="NSimsun">200</font>
来判断该服务器是否正常工作。如果请求 1s 后超时或者 HTTP Status Code 不为 <font face="NSimsun">200</font>
,则不将任何流量引入该服务器,避免线上问题。
这个请求的路径是 Nginx -> Java -> Nginx,这意味着,只要返回了 <font face="NSimsun">200</font>
,那这台服务器的 Nginx 与 Java 都处于健康状态。引入 Node.js 后,这个路径变成了 Nginx -> Node.js -> Java -> Node.js -> Nginx。相应的代码为:
var http = require('http'); app.get('/status.taobao', function(req, res) { http.get({ host: '127.1', port: 7001, path: '/status.taobao' }, function(res) { res.send(res.statusCode); }).on('error', function(err) { logger.error(err); res.send(404); }); });
但是在测试过程中,发现 Node.js 在转发这类请求的时候,每六七次就有一次会耗时几秒甚至十几秒才能得到 Java 端的返回。这样会导致负载均衡调度系统认为该服务器发生异常,随即切断流量,但实际上这台服务器是能够正常工作的。这显然是一个不小的问题。
排查一番发现,默认情况下, Node.js 会使用 <font face="NSimsun">HTTP Agent</font>
这个类来创建 HTTP 连接,这个类实现了 socket 连接池,每个主机+端口对的连接数默认上限是 5。同时 <font face="NSimsun">HTTP Agent</font>
类发起的请求中默认带上了 <font face="NSimsun">Connection: Keep-Alive</font>
,导致已返回的连接没有及时释放,后面发起的请求只能排队。
最后的解决办法有三种:
禁用 <font face="NSimsun">HTTP Agent</font>
,即在在调用 <font face="NSimsun">get</font>
方法时额外添加参数 <font face="NSimsun">agent: false</font>
,最后的代码为:
var http = require('http'); app.get('/status.taobao', function(req, res) { http.get({ host: '127.1', port: 7001, agent: false, path: '/status.taobao' }, function(res) { res.send(res.statusCode); }).on('error', function(err) { logger.error(err); res.send(404); }); });
设置 <font face="NSimsun">http</font>
对象的全局 socket 数量上限:
http.globalAgent.maxSockets = 1000;
在请求返回的时候及时主动断开连接:
http.get(options, function(res) { }).on("socket", function (socket) { socket.emit("agentRemove"); // 监听 socket 事件,在回调中派发 agentRemove 事件 });
实践上我们选择第一种方法。这么调整之后,健康检查就没有再发现其它问题了。
合
Node.js 与传统业务场景结合的实践才刚刚起步,仍然有大量值得深入挖掘的优化点。比比如,让 Java 应用彻底中心化后,是否可以考分集群部署,以提高服务器利用率。或者,发布与回滚的方式是否能更加灵活可控。等等细节,都值得再进一步研究。