RPC_RMI服务集群部署_消费_服务发现

理解服务的集群部署

理解服务发现

服务的集群部署

  服务单机部署

   将某个应用服务,部署到一台服务器,所有服务消费者的并发访问都发送到一台服务器.

   在系统业务的发展,不露出相应的问题:

  •        高并发访问题: 会出现系统资源不够用(带宽,cpu,内存)等等
  •        系统的单节点故障: 当单节点服务器出现宕机故障的时候,会出现服务访问没法访问的问题 .

  服务的集群部署

      将某个应用服务部署到,同时部署到 n 个服务器节点,n 个服务器节点对外提供相同服务.

      同时可以提供备用节点服务器.

     通过集群部署的方式可以解决,服务的高并发访问和单节点故障问题:

    高并发访问:

       通过特定的负载均衡(Load Balance)算法,将并发访问的请求,分发到不同节点的服务器进行处理,以将每一台服务器的负载压力,实现负载均衡

  常用负载均算法:

     轮询法(Round Robin

     随机法(Random

     源地址 Hash 法(Hash

     加权轮询法(Weight Round Robin

     加权随机法(Weight Random

     最小连接数法(Least Connections

单节点故障:

当实现服务集群部署的时候,单个服务器节点宕机,可以由其他正常的服务器节点,正

常向外提供服务,不会出现服务的不可访问问题,实现服务的(HA)高可用

Demo

RMI 模拟服务器集群部署

建立 UserService 接口

package com.zxw.service;

import java.rmi.Remote;
import java.rmi.RemoteException;

/**
 * Remote:标识其方法可以从非本地虚拟机上调用
 * (个人理解:别的客户机通过UserService接口调用其实现类UserServiceImpl的方法)
 *
 * 创建  需要发布的服务  对应的业务接口
 */
public interface UserService extends Remote {

    public String helloRmi(String name) throws RemoteException;

}

建立 UserServiceImpl 实现类

package com.zxw.service.impl;

import com.zxw.service.UserService;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * UnicastRemoteObject:实现 Remote 远程对象的导出
 * (调用其无参构造,其无参构造再调用其父类的无参构造,
 *     protected UnicastRemoteObject() throws RemoteException
 *     {
 *         this(0);
 *     }
 * 父类无参构造调用一个参数的方法
 *     该一个参数的方法是导出远程对象
 *     protected UnicastRemoteObject(int port) throws RemoteException
 *     {
 *         this.port = port;
 *         exportObject((Remote) this, port);
 *     }
 * )
 */
public class UserServiceImpl extends UnicastRemoteObject implements UserService {

    public UserServiceImpl() throws RemoteException {
        super();//调用父类无参构造
    }

    public String helloRmi(String name) throws RemoteException {
        return "hello"+name;
    }
}

发布集群服务

package com.zxw.app;

import com.zxw.service.UserService;
import com.zxw.service.impl.UserServiceImpl;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

/**
 * 发布远程服务
 */
public class ProviderApp {
    /**
     * Naming:给提供远程服务的对象的绑定 url(生产者)
     *         通过远程的url,获得提供远程服务的代理对象(消费者)
     * LocateRegistry:指定发布的远程服务的方法接口
     * @param args
     */
    public static void main(String[] args) {
        try {
            //将远程服务发布到本地的7777\8888\9999端口,此处只贴9999作为实例
            LocateRegistry.createRegistry(9999);
            //发布远程服务的访问url(消费者通过该rul访问生产者发布的远程服务,远程服务的具体实现有UserServiceImpl实现)
            String name="rmi://localhost:9999/rmi";
            //创建一个提供具体服务的对象
            UserService us = new UserServiceImpl();
            //给提供远程服务的对象绑定一个url
            Naming.bind(name,us);

            System.out.println("====rmi远程发布9999====");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

RMI 消费集群服务

建立 UserService 接口(与服务提供者一致)

实现集群服务消费

package com.zxw.app;

import com.zxw.service.UserService;

import java.rmi.Naming;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class ConsumerApp {
    /**
     * Naming:给提供远程服务的对象的绑定 url(生产者)
     *        通过远程的url,获得提供远程服务的代理对象(消费者)
     * @param args
     */
    public static void main(String[] args) {
        List<String> urls = new ArrayList<String>();

        urls.add("rmi://localhost:7777/rmi");
        urls.add("rmi://localhost:8888/rmi");
        urls.add("rmi://localhost:9999/rmi");

        String url =null;
        while (true){
            try {
                //通过随机的负载均衡算法,产生随机的访问地址
                int index= ThreadLocalRandom.current().nextInt(urls.size());
                //发布的远程服务的访问 url
                url = urls.get(index);
                //通过生产者发布的url获得提供远程服务的代理对象
                UserService userService =(UserService)Naming.lookup(url);

                System.out.println("获取的远程服务的代理对象:"+userService);

                //通过远程服务的代理对象调用远程服务的具体方法
                String helloRmi = userService.helloRmi(url+"zxw");
                System.out.println(helloRmi);

                Thread.sleep(3000);
            }catch (Exception e){
                urls.remove(url); //剔除不可用的服务的地址,3个服务中有一个宕机,也不会影响整体效果
                e.printStackTrace();
            }
        }
    }
}

以上代码在移除不能用的服务地址后,如果再将宕机的地址启动,并不能再将重启的服务重新加入可用服务地址中,此时需要服务发现

服务注册与发现

  • 服务容器负责启动,加载,运行服务提供者。
  • 服务提供者在启动时,向注册中心注册自己提供的服务 URL
  • 服务消费者在启动时,向注册中心订阅自己所需的服务。
  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

让消费能够感知到服务提供者的状态发生了变化(宕机,重启)