闲来无事,记录下前端时间使用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注册表中。
改进:
- 动态获取服务器指定所在网关的局域网IP和绑定端口
- 动态获取当前项目内需要注册的服务.
/**
* [@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;
}
}
}