(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,报错。

  

猜你喜欢

转载自www.cnblogs.com/javasl/p/12949041.html
今日推荐