zoukankan      html  css  js  c++  java
  • (031)Spring Boot之服务的注册与发现,使用zookeeper演示负载均衡

      1、相关知识

      先说一下两种负载均衡的方式,一种是静态的,例如使用nginx,需要把服务端配置到nginx里,当增删节点时手动维护。另一种是动态的,当服务启动时动态的将服务注册到注册中心,一般注册中心保存的是服务的IP、端口,调用方只需知道注册中心的IP、端口、服务名,就能获取到服务的IP、端口信息。常用zookeeper、consul,etcd、redis等实现注册中心。下面使用zookeeper演示一下服务的注册与发现及一个简单的负载均衡。

      2、准备工作

      注册中心使用的是zookeeper-3.4.6,参考我的这篇博客:https://www.cnblogs.com/javasl/p/12044446.html

      服务提供方和服务调用方工程,参考我的这篇博客:https://www.cnblogs.com/javasl/p/11966678.html

      3、服务注册

      修改mall-product的pom.xml文件,添加服务注册依赖(2.11.0对应zookeeper-3.4.6):

    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-x-discovery-server</artifactId>
        <version>2.11.0</version>
    </dependency>

      修改配置文件application.properties,添加zookeeper的地址、端口

    zookeeper.address=192.168.7.151:2181

      新建注册类com.edu.spring.mall.product/ServiceRegister.java

    package com.edu.spring.mall.product;
    
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.RetryOneTime;
    import org.apache.curator.x.discovery.ServiceDiscovery;
    import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
    import org.apache.curator.x.discovery.ServiceInstance;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ServiceRegister implements ApplicationRunner{
        
        @Value("${zookeeper.address}")
        private String zkAddress;
        
        public void run(ApplicationArguments args) throws Exception {
            CuratorFramework client = CuratorFrameworkFactory.newClient(zkAddress, new RetryOneTime(1000));
            client.start();
            client.blockUntilConnected();
            ServiceInstance<Object> instance = ServiceInstance.builder().name("product").address("192.168.7.103").port(8080).build();
            ServiceDiscovery<Object> serviceDiscovery = ServiceDiscoveryBuilder.builder(Object.class).client(client)
                    .basePath("/soa").build();
            serviceDiscovery.registerService(instance);
            serviceDiscovery.start();
            System.out.println("service register ok");
        }
    }

      服务名是product;本服务的地址端口分别是192.168.7.103、8080;zookeeper中保存文件路径是/soa。

      启动zookeeper集群,查看目录

      

      启动mall-product服务,再次查看目录,自动创建了/soa/product目录,注册了服务的IP、端口信息

      

       服务注册成功,保存文件路径为:java中定义的基础路径+服务名

      4、服务发现

      修改mall-web的pom.xml文件,添加服务发现依赖:

    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-x-discovery</artifactId>
        <version>2.11.0</version>
    </dependency>

      新建测试类com.edu.spring.web/Client.java

    package com.edu.spring.web;
    
    import java.util.Collection;
    
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.RetryOneTime;
    import org.apache.curator.x.discovery.ServiceDiscovery;
    import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
    import org.apache.curator.x.discovery.ServiceInstance;
    import org.springframework.web.client.RestTemplate;
    
    public class Client {
        public static void main(String[] args) throws Exception {
            
            CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.7.151:2181", new RetryOneTime(1000));
            client.start();
            client.blockUntilConnected();
            
            ServiceDiscovery<Object> serviceDiscovery = ServiceDiscoveryBuilder.builder(Object.class).client(client)
                    .basePath("/soa").build();
            Collection<ServiceInstance<Object>> list = serviceDiscovery.queryForInstances("product");
            
            list.forEach((instance)->{
                String servicePath = instance.getAddress()+":"+instance.getPort();
                RestTemplate res=new RestTemplate();
                String body= res.getForObject("http://"+servicePath+"/soa/product/20",String.class);
                System.out.println("调用服务:"+servicePath);
                System.out.println(body);
            });
        }
    }

      运行输出结果如下,服务发现成功:

      

      5、负载均衡

      复制一份mall-product命名为mall-product2,代表另一个服务。

      修改配置文件application.properties,添加项目端口号

    server.port=9090

      修改注册类com.edu.spring.mall.product/ServiceRegister.java,端口号改为9090

    ServiceInstance<Object> instance = ServiceInstance.builder().name("product").address("192.168.7.103").port(9090).build();

      启动mall-product、mall-product2,并且查看zookeeper,发现多了一个服务。

      

      运行客户端程序,发现两个服务都被调用了一次。

      

      下面实现一个简单的负载均衡

      在客户端新建com.edu.spring.web/LoadBalance.java

    package com.edu.spring.web;
    
    import java.util.List;
    
    public class LoadBalance {
    
        private int index = 0;
        private List<String> services;
        
        public LoadBalance(List<String> services) {
            this.services = services;
        }
        
        public String choose() {
            String service = services.get(index);
            index++;
            if(index >= services.size()) {
                index = 0;
            }
            return service;
        }
    }

      修改测试类com.edu.spring.web/Client.java,调用10次服务方。

    package com.edu.spring.web;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.RetryOneTime;
    import org.apache.curator.x.discovery.ServiceDiscovery;
    import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
    import org.apache.curator.x.discovery.ServiceInstance;
    import org.springframework.web.client.RestTemplate;
    
    public class Client {
        public static void main(String[] args) throws Exception {
            
            CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.7.151:2181", new RetryOneTime(1000));
            client.start();
            client.blockUntilConnected();
            
            ServiceDiscovery<Object> serviceDiscovery = ServiceDiscoveryBuilder.builder(Object.class).client(client)
                    .basePath("/soa").build();
            Collection<ServiceInstance<Object>> list = serviceDiscovery.queryForInstances("product");
            
            List<String> serviceList = new ArrayList<String>();
            list.forEach((instance)->{
                serviceList.add(instance.getAddress()+":"+instance.getPort());
            });
            
            LoadBalance lb = new LoadBalance(serviceList);
            
            for(int i=0;i<10;i++) {
                RestTemplate res=new RestTemplate();
                String servicePath=lb.choose();
                String body= res.getForObject("http://"+servicePath+"/soa/product/20",String.class);
                System.out.println("调用服务:"+servicePath);
                System.out.println(body);
            }
        }
    }

      输出结果如下,轮询负载成功:

      

      假如停掉9090服务,zookeeper中会自动清除掉这个服务节点,此时在运行客户端,只能调用8080服务了

      

      注:停止掉9090服务后,zookeeper不会马上清除掉该服务节点,有延迟(5s左右),这段时间运行客户端会无法调用9090,报错。

      

  • 相关阅读:
    创龙TMS320C6748开发板串口和中断学习笔记
    RTL8195AM开发板使用
    CC3100BoosterPack和CC31XXEMUBOOST板子的测试
    利尔达NB-IOT的PSM和eDRX低功耗模式笔记
    【原创】大数据基础之Zookeeper(3)选举算法
    【原创】大数据基础之Zookeeper(2)源代码解析
    【原创】大数据基础之Zookeeper(1)介绍、安装及使用
    【原创】论码农的财富修养
    【原创】大叔案例分享(2)处理大批量数据时如何实现“高效”同时实现“断点续传”功能
    【原创】大数据基础之Spark(1)Spark Submit即Spark任务提交过程
  • 原文地址:https://www.cnblogs.com/javasl/p/12949041.html
Copyright © 2011-2022 走看看