1. 概述
ID生成器是IM,電商系統中繞不過去的一個組件。對於系統中發生的每個事件,如IM發送的每一條消息,電商平臺的每個訂單都需要生成一個ID,這個ID成爲事件的唯一標識,提供之後的跟蹤與檢索工作。網絡上微信與美團等知名廠商都分享了自己的ID生成算法,對於大型系統,ID生成算法都是分佈式ID算法,基礎算法使用了snowflake或分段自增等算法。
ID生成器的調用通常爲RPC方式,目前常用的使用方式有GRPC,HTTP等調用方式。本文介紹如何使用JAVA RMI的方式快速實現一個簡單ID生成器。
2.接口定義
RMI的接口供ID生成器和調用ID生成器的客戶端使用,定義如下:
package com.kedacom.rmiinterface;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* @author zhang.kai
*
*/
public interface IIdGen extends Remote{
public long getNextId() throws RemoteException;
}
3. 服務端
服務端實現ID的分發,這裏使用一個簡單的自增算法,可以設定ID的初始值(這裏簡單地以服務初始化時間作爲初始值),定義如下:
package com.kedacom.rmiserver.service;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.atomic.AtomicLong;
import com.kedacom.rmiinterface.IIdGen;
/**
* @author zhang.kai
*
*/
public class IdGenService extends UnicastRemoteObject implements IIdGen{
private static final long serialVersionUID = -3742466067930952734L;
private final AtomicLong idSeed ;
/**
* @param port
* @throws RemoteException
*/
public IdGenService() throws RemoteException {
super();
idSeed = new AtomicLong(getInitValue());
}
@Override
public long getNextId() throws RemoteException {
return idSeed.getAndIncrement();
}
/**
* 獲取ID的初始值
* @return
*/
private long getInitValue() {
return System.currentTimeMillis();
}
}
服務對象的註冊代碼如下:
@Override
public void run(ApplicationArguments args) throws Exception {
//設置rmi服務端的hostname,該值與遠程調用對象綁定,用於客戶端尋址
System.setProperty("java.rmi.server.hostname",rmiSvrHostname);
//創建註冊中心
Registry reg = LocateRegistry.createRegistry(RmiConfigConsts.RMI_REGISTRY_PORT);
log.info("registry:"+reg);
//註冊遠程對象
IdGenService idgObj = new IdGenService();
log.info("remote obj:{}",idgObj);
Registry registry = LocateRegistry.getRegistry(RmiConfigConsts.RMI_REGISTRY_PORT);
registry.bind(RmiConfigConsts.IDGEN_RMI_OBJECT_NAME, idgObj);
log.info("idgen rmi obj ready!");
}
4.客戶端調用
客戶端調用代碼比較簡單,如下:
@Override
public void run(ApplicationArguments args) throws Exception {
// TODO Auto-generated method stub
// 獲取註冊中心
Registry registry = LocateRegistry.getRegistry(rmiSvrHostname, RmiConfigConsts.RMI_REGISTRY_PORT);
log.info("get registry");
// 在註冊中心中查找遠程對象
IIdGen idgen = (IIdGen) registry.lookup(RmiConfigConsts.IDGEN_RMI_OBJECT_NAME);
log.info("get idgen:{}", idgen);
// 循環調用該接口
for (int i = 0; i < 10; i++) {
log.info("{} remote idgen:{}", i, idgen.getNextId());
}
}
5.小結
JAVA RMI是JDK提供的一種原生RPC方式,使用的對象序列化方式爲JAVA對象的序列化方式,從測試的效率來看,在網絡環境下,一次調用的時間開銷在1ms以內。從實現方式上來看,代碼也比較簡單,缺點是與JAVA語言強綁定。本文只是示例了一個最簡單的ID生成算法,如果有更復雜邏輯在算法中,應考慮線程安全問題。本文的示例代碼可從https://github.com/solarkai/IdGenRmi獲取。