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。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
让消费能够感知到服务提供者的状态发生了变化(宕机,重启)