一个基于RMI的ID生成器简单模型 1. 概述 2.接口定义 3. 服务端 4.客户端调用 5.小结

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获取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章