基于nodejs+zookeeper服务发现
标签:
Kubernetes
本文使用nodejs作为微服务API网关,从而将消费端的请求,随机路由到一个可用的服务节点上。核心代码如下:
本文参考了《架构探险》轻量级服务架构
本文示例代码:node-zookeeper-demo
var express = require('express');var zookeeper = require('node-zookeeper-client');var httpProxy = require('http-proxy');var cluster = require('cluster');var os = require('os');var cache = {};var CPUS = os.cpus().length;var PORT = 8084;var CONNECTION_STRING = '127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183';var REGISTRY_ROOT = '/registry';var app = express();if (cluster.isMaster) { for (var i = 0; i < CPUS; i++) { cluster.fork(); } }else { //连接zookeeper var zk = zookeeper.createClient(CONNECTION_STRING); zk.connect(); //创建代理服务器对象并监听错误事件 var proxy = httpProxy.createProxyServer(); proxy.on('error', function (err, req, res) { res.end();//输出空白响应数据 }); //启动web服务器 app.use(express.static('public')); app.all('*', function (req, res) { //处理图标请求 if (req.path == '/favicon.ico') { res.end(); return; } //获取服务名称 var serviceName = req.get('Service-Name'); console.log('ServiceName:%s', serviceName); if (!serviceName) { console.log('Service-Name request header is not exist'); res.end(); return; } //获取服务路径 var servicePath = REGISTRY_ROOT + "/" + serviceName; console.log('ServicePath:%s', servicePath); console.log('cache[serviceName]:'+JSON.stringify(cache)); if (cache[serviceName]) { //if(false){ //TODO /*zk.exists(servicePath, function (event) { if (event.NODE_DELETED) { cache = {}; } }, function (error, stat) { if (stat) { } })*/ console.log("-----------cache---------------"+cache[serviceName]); proxy.web(req, res, { target: 'http://' + cache[serviceName] //目标地址 }); } else { //获取服务路径下的地址节点 zk.getChildren(servicePath, function (error, addressNodes) { if (error) { console.log(error.stack); res.end(); return; } var size = addressNodes.length; if (size == 0) { console.log('address node is not exist'); res.end(); return; } //生成地址容器 var addressPath = servicePath + "/"; if (size == 1) { //若只有唯一地址,则获取该地址 addressPath += addressNodes[0]; } else { //若存在多个地址,则随机获取一个地址 addressPath += addressNodes[parseInt(Math.random() * size)]; } console.log('addressPath:%s', addressPath); //获取服务地址 zk.getData(addressPath, function (err, serviceAddress) { if (error) { console.log(error.stack); res.end(); return; } console.log('serviceAddress:%s', serviceAddress); if (!serviceAddress) { console.log('serviceAddress is not exist'); res.end(); return; } cache[serviceName] = serviceAddress; console.log("cache"+ serviceName+": "+cache[serviceName]); //执行反向代理 proxy.web(req, res, { target: 'http://' + serviceAddress //目标地址 }); }); }); } }); app.listen(PORT, function () { console.log('server is running at %d', PORT); }); }
使用supervisor app-gateway.js
启动API网关后:(supervisor可以定时监听文件的变化,代码更新无需重启)
启动网关
当然也需要启动在上文《基于ZooKeeper的服务注册实现》提到的两个客户端,让服务在ZooKeeper上注册。
测试页面
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Demo</title></head><body><div id="console"></div><div id="container"> <h1> hello world </h1></div><button id="btn" >点我一下调用服务</button></body><script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/jquery-2.2.3.min.js"></script><script> $(function () { $("#btn").click(function () { //alert("hello world"); $.ajax({ method: 'GET', url: '/hello', headers: { 'Service-Name': 'HelloService' }, success: function (data) { $("#console").text(data); } }) }); })</script></html>
点击测试按钮,在界面服务的显示运行结果"Hello"
运行结果
AB测试(Apache Bench)模拟1000个用户每次并发100请求:
注 :在测试前,将serviceName设为固定值:var serviceName='HelloService'
AB测试
阅读本文,请结合上篇文章《基于ZooKeeper的服务注册实现》进行理解。
作者:billJiang
链接:https://www.jianshu.com/p/a6b01048d94f
点击查看更多内容
1人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦