zoukankan      html  css  js  c++  java
  • SpringBoot + nodeJS + zookeeper 搭建微服务示例

    总体来说该项目由服务注册 + 服务发现 + 服务代理 + 服务调用四部分组成。

    使用java客户的开发服务注册组件,它是整个微服务架构中的服务注册表,使用Node.js客户端开发服务发现组件,它用于在服务注册表中根据具体的服务名称获取对应的服务配置。

      由项目1提供接口

      

    /**
         * 注册服务信息
         * @param serviceName 服务名称
         * @param serviceAddress 注册服务的地址
         */
        void register(String serviceName,String serviceAddress);

      由项目2依赖项目1,实现其提供的接口

    @Component
    public class ServiceRegistryImpl implements ServiceRegistry,Watcher{
        private static Logger logger = LoggerFactory.getLogger(ServiceRegistryImpl.class);
        private static CountDownLatch latch = new CountDownLatch(1);
        private static final int SESSION_TIMEOUT = 5000;
        private static final String REGISTRY_PATH = "/registry";
        private ZooKeeper zk;
        
        public ServiceRegistryImpl() {
        }
        /**
         * 
         * @param zkServers registry.servers
         */
        public ServiceRegistryImpl(String zkServers) {
             try {
                zk = new ZooKeeper(zkServers, SESSION_TIMEOUT, this);
                latch.await();
                logger.info("connected to zookeeper");
                logger.info("connected to zookeeper");
            } catch (Exception e) {
                logger.error("create zookeeper client failure",e);
                e.printStackTrace();
            }
        }
    
        @Override
        public void register(String serviceName, String serviceAddress) {
            
            try {
                //创建根节点(持久节点)
                String registryPath = REGISTRY_PATH;
                if(zk.exists(registryPath, false) == null) {
                    zk.create(registryPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                    logger.info("create registry node:{}",registryPath);
                }
                //创建服务节点(持久节点)
                String servicePath = registryPath + "/" + serviceName;
                if(zk.exists(servicePath, false) == null) {
                    zk.create(servicePath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                    logger.info("create service node:{}",servicePath);
                }
                //创建地址节点(临时顺序节点)
                String addressPath = servicePath + "/address-";
                if(zk.exists(addressPath, false) == null) {
                    String addressNode = zk.create(addressPath, serviceAddress.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
                    logger.info("create address node:{}",addressNode);
                    
                }
            } catch (Exception e) {
                logger.error("create node failure",e);
                e.printStackTrace();
            }
            
        }
    
        @Override
        public void process(WatchedEvent event) {
            if(event.getState() == Event.KeeperState.SyncConnected) {
                latch.countDown();
            }
            
        }
    
    }

      项目启动时注册服务

    @Component
    public class RegistryZooListener implements ServletContextListener {
    
        @Value("${server.address}")
        private String serverAddress;
    
        @Value("${server.port}")
        private int serverPort;
    
        @Autowired
        private ServiceRegistry serviceRegistry;
    
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
    
    
            ServletContext servletContext = servletContextEvent.getServletContext();
            WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
            RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
            //获取到所有的请求mapping
            Map<RequestMappingInfo, HandlerMethod> infoMap = mapping.getHandlerMethods();
    
            for (RequestMappingInfo info:infoMap.keySet()){
                String serviceName = info.getName();
                if (serviceName != null){
                    //注册服务
    //                System.out.println(serviceName);
                    serviceRegistry.register(serviceName, String.format("%s:%d",serverAddress,serverPort));
                }
            }
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
    
        }
    }

      接下来的工作将是服务发现

    ar express = require('express');
    
    var zookeeper = require('node-zookeeper-client');
    
    var httpProxy = require('http-proxy');
    
    
    var PORT = 1234;
    
    var CONNECTION_STRING = '127.0.0.1:2181';
    
    var REGISTER_ROOT = "/registry";
    
    //连接zookeeper
    var zk = zookeeper.createClient(CONNECTION_STRING);
    zk.connect();
    
    //创建代理服务器对象并监听错误事件
    var proxy = httpProxy.createProxyServer();
    proxy.on('error', function (err, req, res) {
        res.end();//输出空白数据
    });
    
    //启动web服务器
    var app = express();
    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('service-name : %s', serviceName);
        if (!serviceName) {
            console.log('Service-Name request head is not exist');
            res.end();
            return;
        }
    
        //获取服务路径
        var servicePath = REGISTER_ROOT + '/' + serviceName;
        console.log('service path is : %s', servicePath);
        //获取服务路径下的地址节点
        zk.getChildren(servicePath, function (error, addressNodes) {
            
            console.log("into zk getChildren!");
            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 is : %s',addressPath);
            //获取服务地址
            zk.getData(addressPath,function (error,serviceAddress) {
                if (error) {
                    console.log(error.stack);
                    res.end();
                    return;
                }
                console.log('serviceAddress is : %s',serviceAddress);
                if (!serviceAddress) {
                    console.log('serviceAddress is not exist');
                    res.end();
                    return;
                }
                console.log('TART###http://' + serviceAddress);
                proxy.web(req,res,{
                    target:'http://'+serviceAddress//目标地址
                })
            });
    
        });
    });
    app.listen(PORT,function () {
        console.log('server is running at %d',PORT);
    });

      随后调用即可

    $.ajax({
                method: "GET",
                url: 'user/hello',
                headers: {
                    'Service-Name': 'hello'
                },
                success: function (data) {
                    console.log("data:" + data);
                    $("#console").append(data + '<br>');
                },
                error:function (error){
                    console.log("error")
                }
            });
  • 相关阅读:
    日期操作
    sanchi
    502 Server dropped connection
    把项目挂载到composer上
    从composer上在本地创建一个项目
    初始化后,composer安装
    在项目目录初始化composer
    Linux安装composer
    linux网络编程之TCP/IP基础
    grep的用法
  • 原文地址:https://www.cnblogs.com/yangfei-beijing/p/8043114.html
Copyright © 2011-2022 走看看