使用java.rmi实现一个简易的RPC框架

闲来无事,记录下前端时间使用java.rmi实现项目RPC框架过程中遇上的坑。

首先记录Java-RMI的用法。很是简单: #####server 与 client 均实现java.rmi.Remote接口:

public interface ClusterService extends java.rmi.Remote {
	String reply() throws RemoteException;
}

server: #####实现ClusterService:

public class TestAction  extends UnicastRemoteObject implements ClusterService{
   private static transient final long serialVersionUID = 0L;
   public CacheAction() throws RemoteException    {super();}
   public String reply() throws RemoteException{
       return "test";
   }
}

注册服务: #####绑定RMI端口

java.rmi.registry.LocateRegistry.createRegistry(9999)
//注册服务
new javax.naming.InitialContext().rebind("rmi://127.0.0.1:9999/index", new TestAction());

客户端使用:

((ClusterService)(new InitialContext().lookup("rmi://127.0.0.1:9999/index"))).reply();
//reply :: test

客户端首先是将RMI服务绑定到端口9999上。然后客户端在根据serverIP:serverport/bindName 进行调用服务。

DEMO没问题,那么接下来就可以开始准备组网了。


##1. ClusterRegistrar 该类主要是完成服务器RMI功能的注册,并将实现了ClusterService接口及注解RegistredService的类注册到RMI注册表中。


改进:

  1. 动态获取服务器指定所在网关的局域网IP和绑定端口
  2. 动态获取当前项目内需要注册的服务.

/**
 * [@author](https://my.oschina.net/arthor) yuyi
 */
public class ClusterRegistrar {
    protected final static Logger log = LoggerFactory.getLogger(ClusterRegistrar.class);
    private static final Map<ClusterService, EventTag> map = new HashMap<>();
    public static String gateway = "0.0.0.0";
    public static Integer masklength = 0;
    private boolean registry(Context namingContext, String url, ClusterService serivce, String name) {
        try {
            log.info("registred action [{}]", url = url + name);
            namingContext.rebind(url, serivce);
        } catch (NamingException e) {
            log.error(e.getMessage(), e);
            return false;
        }
        return true;
    }
    public static void main(String[] args) {
        register();
    }
    private static int registry_count = 0;
    public void _registry() {
        Random r = new Random();
        r.setSeed(System.currentTimeMillis());
        int port = 0;
        do {
            port = r.nextInt(0xFFFF);
        } while (!NetUtils.isUsableLocalPort(port));
        String ip = NetUtils.getLocalhost(gateway, masklength);
        String url = "rmi://" + ip + ":" + port + "/";
        log.info("registring rpc[rmi] server :[{}] ", url);
        try {
            LocateRegistry.createRegistry(port);
            Context namingContext = new InitialContext();
            ClassLoaderUtil.scanning(ClusterRegistrar.class.getPackage().getName(), clz -> {
                if (ClusterService.class.isAssignableFrom(clz) && (clz.getModifiers() & Modifier.ABSTRACT) == 0) {
                    RegistredService registredService = clz.getAnnotation(RegistredService.class);
                    if (registredService == null || registredService.value() == null) {
                        return;
                    }
                    try {
                        if ((clz.getConstructor(new Class<?>[] {}).getModifiers() & Modifier.PUBLIC) != 0) {
                            // public 无参构造函数
                            ClusterService service = (ClusterService) clz.newInstance();
                            registry(namingContext, url, service, registredService.value().name());
                            map.put(service, registredService.value());
                        }
                    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException
                            | SecurityException e) {
                        log.error(e.getMessage(), e);
                        return;
                    }
                }
            });
            RPCServer.server.init(url, map);
            log.info("Successfully register  remote object.");
        } catch (Exception e) {
            log.error(e.getMessage());
            if (registry_count++ > 3) {
                log.error(e.getMessage(), e);
                log.error("error register  remote object.");
                return;
            }
            _registry();
        }
    }
    public static void register() {
        ClusterRegistrar clusterRegistrar = new ClusterRegistrar();
        ThreadRuningtUtil.runTask(clusterRegistrar::_registry);
    }
}
public static String getLocalhost(String gateway, int mask) {
    InetAddress candidateAddress = null;
    NetworkInterface iface;
    InetAddress inetAddr;
    try {
        for (Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces
                .hasMoreElements();) {
            iface = ifaces.nextElement();
            for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements();) {
                inetAddr = inetAddrs.nextElement();
                if (false == inetAddr.isLoopbackAddress()) {
                    if (inetAddr.isSiteLocalAddress()) {
                        if (checkSameSegment(gateway, mask, inetAddr.getHostAddress()))
                            return inetAddr.getHostAddress();
                    } else if (null == candidateAddress) {
                        // 非site-local地址做为候选地址返回
                        candidateAddress = inetAddr;
                    }
                }
            }
        }
    } catch (SocketException e) {
        // ignore socket exception, and return null;
    }
    if (null == candidateAddress) {
        try {
            candidateAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            // ignore
        }
    }
    if ((candidateAddress instanceof Inet4Address)
            && checkSameSegment(gateway, mask, candidateAddress.getHostAddress()))
        return candidateAddress.getHostAddress();
    throw new IllegalArgumentException(" 没有对应网段的IP  ");
}
/**
 * 根据网关和子网掩码长度判断是否在同一网络
 * 
 * [@param](https://my.oschina.net/u/2303379) gateway
 *            网关地址
 * [@param](https://my.oschina.net/u/2303379) mask
 * [@param](https://my.oschina.net/u/2303379) ip
 * [@return](https://my.oschina.net/u/556800)
 */
public static boolean checkSameSegment(String gateway, int mask, String ip) {
    assert isIp(gateway) && (mask <= 32 && mask >= 0) && isIp(ip);
    String gateway_val = ip2BinStr(gateway);
    String ip_val = ip2BinStr(ip);
    return ip_val.startsWith(gateway_val.substring(0, mask));
}
/**
 * 转化为二进制字符串
 * @param ip
 * @return
 */
public static String ip2BinStr(String ip) {
    String[] arr = ip.split("\\.");
    String rs = "";
    for (String str : arr) {
        String s = Integer.toBinaryString(Integer.parseInt(str));
        if (s.length() < 8) {
            int diff = 8 - s.length();
            for (int i = 0; i < diff; i++) {
                s = "0" + s;
            }
        }
        rs += s;
    }
    return rs;
}
    public static boolean isIp(String val) {
    Pattern p = Pattern.compile(
            "^(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)\\.(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)$");
    Matcher m = p.matcher(val);
    return m.matches();
}
/**
 * 检测本地端口可用性
 * 
 * @param port
 *            被检测的端口
 * @return 是否可用
 */
public static boolean isUsableLocalPort(int port) {
    if (false == isValidPort(port)) {
        // 给定的IP未在指定端口范围中
        return false;
    }
    try {
        new Socket(LOCAL_IP, port).close();
        // socket链接正常,说明这个端口正在使用
        return false;
    } catch (Exception e) {
        return true;
    }
}

##2. ClusterService

/**
 * 事件标签
 * @author yuyi
 */
public enum EventTag {
    /** cache 同步 */
    SynchronizeCache(CacheAction.class),
    /** 注册服务器 */
    RegisterServer(RemoteServerRegisteredAction.class),
    private Class<? extends ClusterService> clz;
    private EventTag(Class<? extends ClusterService> clz) {
        this.clz = clz;
    }
    public Class<? extends ClusterService> getClz() {
        return clz;
    }
    public static EventTag valueOf(Class<? extends ClusterService> clz) {
        for (EventTag e : EventTag.values()) {
            if (e.getClz().equals(clz))
                return e;
            }
        return null;
    }
    public void setClz(Class<? extends ClusterService> clz) {
        this.clz = clz;
    }
}
/**
 * 注册服务
 * @author yuyi
 */
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface RegistredService {
    EventTag value();
}

##3. RegistredService

/**
 * 事件标签
 * [@author](https://my.oschina.net/arthor) yuyi
 */
public enum EventTag {
	/** cache 同步 */
	SynchronizeCache(CacheAction.class),
	/** 注册服务器 */
	RegisterServer(RemoteServerRegisteredAction.class),
	private Class<? extends ClusterService> clz;
	private EventTag(Class<? extends ClusterService> clz) {
		this.clz = clz;
	}
	public Class<? extends ClusterService> getClz() {
		return clz;
	}
	public static EventTag valueOf(Class<? extends ClusterService> clz) {
		for (EventTag e : EventTag.values()) {
			if (e.getClz().equals(clz))
				return e;
			}
		return null;
	}
	public void setClz(Class<? extends ClusterService> clz) {
		this.clz = clz;
	}
}
/**
 * 注册服务
 * [@author](https://my.oschina.net/arthor) yuyi
 */
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface RegistredService {
	EventTag value();
}

##4. RPCServer

public class RPCServer {
    public static final RPCServer server = new RPCServer();
    private Server current_server = new Server(NetUtils.getLocalMacAddress(), null);
    public Map<String, Boolean> localTasks = new SimpleCache<>();
    public void init(String host, Map<ClusterService, EventTag> map) {
        current_server.setHost(host);
        refresh();
        ThreadRuningtUtil.runTask(() -> RemoteServerRegisteredAction.register(current_server, map));
    }
    public String getCurrentMac() {
        return current_server.getMac();
    }
    public String getCurrentHost() {
        return current_server.getHost();
    }
    public static class Server {
        private String mac;
        private String host;
        public Server(String mac2, String host2) {
            setHost(host2);
            setMac(mac2);
        }
        public String getMac() {
            return mac;
        }
        public void setMac(String mac) {
            this.mac = mac;
        }
        public String getHost() {
            return host;
        }
        public void setHost(String host) {
            this.host = host;
        }
    }
}

##5. AbstractRPCService

public abstract class AbstractRPCService extends UnicastRemoteObject implements ClusterService {
    private static final long serialVersionUID = 1L;
    protected final static Logger log = LoggerFactory.getLogger(AbstractRPCService.class);
    private static final String neterror = "ClusterService_NET_ERROR";
    public AbstractRPCService() throws RemoteException {
        super();
    }
    protected String[] getApplyServices() {
        return getApplyServices(this.getClass());
    }
    protected static String[] getApplyServices(Class<? extends ClusterService> cls) {
        EventTag e = getAttentionEvent(cls);
        Assert.anyIllegalArgument(" class[" + cls + "]  not define attention event !", e == null);
        return JedisClient.task(j -> {
            // 获得所有远程服务器信息
            Map<String, Object> map = j.getMap(Server.class.getName());
            return j.getArr(e.name()).stream().filter(tj -> {
                if (map.containsKey(tj)) {
                    if (!map.get(tj).equals(RPCServer.server.getCurrentMac()))
                        return true;
                    return false;
                }
                JedisClient.run(a -> {
                    log.info("删除不存在Server中的异常请求[{}].", tj);
                    a.lrem(e.name(), (Serializable) tj);
                });
                return false;
            }).map(m -> map.get(m).toString() + e.name()).toArray(String[]::new);
        });
    }
    /**
     * 
     * @param ask
     */
    protected static void applyAction(Class<? extends ClusterService> cls, AskModel ask) {
        String[] urls = getApplyServices(cls);
        for (String url : urls) {
            remoteCall(url, ask);
        }
    }
    /**
     * 
     * @param ask
     */
    protected void applyAction(AskModel ask) {
        String[] urls = getApplyServices();
        for (String url : urls) {
            remoteCall(url, ask);
        }
    }
    /**
     * 远程调用
     * 
     * @param remoteUrl
     * @return
     */
    protected static Object remoteCall(String remoteUrl, AskModel ask) {
        return remoteCall(remoteUrl, a -> a.reply(ask), true);
    }
    /**
     * 远程调用
     * 
     * @param remoteUrl
     * @return
     */
    protected static <T> T remoteCall(String remoteUrl, Function<ClusterService, T> action, boolean isRecursive) {
        try {
            Context namingContext = new InitialContext();
            ClusterService r = (ClusterService) namingContext.lookup(remoteUrl);
            return action.apply(r);
        } catch (Exception e) {
            log.error("error url [{}]", remoteUrl);
            log.error(e.getMessage(), e);
            JedisClient.run(a -> {
                Integer i = a.hget(neterror, remoteUrl, Integer.class);
                if (i != null && 3 >= i) {
                    Map<String, Object> macs = a.getMap(Server.class.getName());
                    macs.forEach((k, v) -> {
                        if (remoteUrl.startsWith((String) v)) {
                            log.info("异常请求已超出3次,关闭该请求[{}].-删除key[{}]value[{}]", remoteUrl,
                                    remoteUrl.substring(remoteUrl.lastIndexOf("/") + 1), k);
                            a.lrem(remoteUrl.substring(remoteUrl.lastIndexOf("/") + 1), k);
                            a.hdel(neterror, remoteUrl);
                        }
                    });
                    return;
                } else
                    a.hset(neterror, remoteUrl, i == null ? 1 : i + 1);
            });
            try {
                return action.apply(new NetErrorImp());
            } catch (Exception e1) {
                log.error(e1.getMessage(), e1);
                throw new RuntimeException(e1);
            }
            // throw new RuntimeException(e);
        }
    }
    public static EventTag getAttentionEvent(Class<? extends ClusterService> clz) {
        return EventTag.valueOf(clz);
    }
    static interface Function<T, R> {
        public R apply(T t) throws Exception;
    }
}

示例:

/**
 * cache 操作器
 * @author yuyi
 */
@RegistredService(EventTag.SynchronizeCache)
public class CacheAction extends AbstractRPCService {
    protected final static Logger log = LoggerFactory.getLogger(CacheAction.class);
    private static transient final long serialVersionUID = -2929269581432562044L;
    private static final SessionEngine session = SessionEngine.getInstance();
    public CacheAction() throws RemoteException {
        super();
    }
    /**
     * 发起一个远程 删除请求
     * 
     * @param key
     */
    public static void deleteRemoteServerCahche(String key) {
        ThreadRuningtUtil.runTask(() -> {
            CacheAsk ask = new CacheAsk();
            ask.setAskId(UUID.randomUUID().toString());
        // 暂停广播
            // ask.setIps(RPCServer.server.getRemoteServers());
            ask.setCacheKey(key);
            RPCServer.server.localTasks.put(ask.getAskId(), true);
            applyAction(CacheAction.class, ask);
            JedisClient.task(jedisClient -> {
                if (jedisClient.exists(key)) {
                    return jedisClient.del(key);
                }
                return null;
            });
        });
    }
    @Override
    public Object reply(AskModel ask) throws RemoteException {
        synchronized (CacheAction.class) {
            if (!(ask instanceof CacheAsk)) {
                throw new RemoteException(" ask type not`s CacheAsk ");
            }
            if (RPCServer.server.localTasks.containsKey(ask.getAskId())) {
                return "OK,but is old news";
            }
            RPCServer.server.localTasks.put(ask.getAskId(), true);
            CacheAsk cacheAsk = (CacheAsk) ask;
            if (!delete(cacheAsk.getCacheKey())) {
                throw new RemoteException("删除过程中发生错误");
            }
            return "OK";
        }
    }
    /**
     * 删除cache
     * 
     * @param key
     * @return
     */
    private static boolean delete(String key) {
        session.remove(key, true);
        return true;
    }
    /**
     * Cache 请求实例
     * @author yuyi
     */
    public static class CacheAsk extends AskModel {
        private static final long serialVersionUID = 1L;
        private String cacheKey;
        public String getCacheKey() {
            return cacheKey;
        }
        public void setCacheKey(String cacheKey) {
            this.cacheKey = cacheKey;
        }
    }
}

转载于:https://my.oschina.net/yuyizyk/blog/1919726

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值