从零开始搭建属于自己的物联网平台(四)实现基于协议包的动态协议解析

源码

源码仓库

往期链接

从零开始搭建属于自己的物联网平台(一)需求分析以及架构设计

从零开始搭建属于自己的物联网平台(二)实现基于订阅发布的消息总线

从零开始搭建属于自己的物联网平台(三)基于netty实现mqtt server网关

业务需求

在物联网平台场景下,接入设备是多种多样的,这时候就不能以硬编码的形式在源码中实现,所以要实现动态的可以自由添加修改的协议解析方式

实现方式

针对这一场景,以加载jar包的形式来实现,指定标准的接口,只要在jar包中实现对应的接口,做标准的协议解析方法就可以了(这里要注意,要支持设备上下行消息解析)。

源码实现

协议对象接口

/**
 * 消息协议支持接口,通过实现此接口来自定义消息协议
 *
 * @author liuhualin
 * @since 1.0.0
 */
public interface ProtocolSupport extends Serializable {
    
    
    /**
     * @return 协议ID
     */
    String getId();

    /**
     * @return 协议名称
     */
    String getName();

    /**
     * @return 说明
     */
    String getDescription();

    /**
     * @return 获取支持的协议类型
     */
    List<Transport> getSupportedTransport();

    /**
     * 根据指定协议取得
     *
     * @return 获取支持的协议类型
     */
    DeviceMessageCodec getMessageCodecSupport(String transport);
}

协议对象的实现类

定义协议解析包对象应具有的功能

  • 上行协议解析
  • 下行协议解析

DeviceMessageCodec这里继承了两个接口,分别用来实现上行解析以及下行解析,并且DeviceMessageCodec定义了这个解析器对应的协议类型,最终结构是下面这个样子的:
一个协议包可能有多个DeviceMessageCodec(根据协议包解析的网络协议来的,比如需要解析TCP、MQTT两种那就添加两个DeviceMessageCodec),每个DeviceMessageCodec都实现了上行消息解析、下行消息解析两个功能。

/**
 * 实现自定义消息解析需要实现该类
 */
public interface ProtocolSupportProvider {
    
    

    ProtocolSupport create();
}

/**
 * 多传输协议支持的解析包
 */
@Data
public class CompositeProtocolSupport implements ProtocolSupport {
    
    

    /**
     * 协议ID
     */
    private String id;

    /**
     * 协议名称
     */
    private String name;

    /**
     * 协议描述
     */
    private String description;

    /**
     * 支持的协议类型
     */
    private Map<String, DeviceMessageCodec> messageCodecSupports = new ConcurrentHashMap<>();


    @Override
    public String getId() {
    
    
        return this.id;
    }

    @Override
    public String getName() {
    
    
        return this.name;
    }

    @Override
    public String getDescription() {
    
    
        return this.description;
    }

    @Override
    public List<Transport> getSupportedTransport() {
    
    
        return messageCodecSupports.entrySet().stream().map(Map.Entry::getValue).map(DeviceMessageCodec::getSupportTransport).collect(Collectors.toList());
    }

    @Override
    public DeviceMessageCodec getMessageCodecSupport(String transport) {
    
    
        return messageCodecSupports.get(transport);
    }

    public void addMessageCodecSupport(Transport transport, DeviceMessageCodec codec) {
    
    
        messageCodecSupports.put(transport.getId(), codec);
    }

    public void addMessageCodecSupport(DeviceMessageCodec codec) {
    
    
        addMessageCodecSupport(codec.getSupportTransport(), codec);
    }
}

解析协议包

以classloader的形式解析jar包,反射调用ProtocolSupportProvider的create

**
 * 自定义协议支持包注册器(内存)
 */
@Component
@Slf4j
public class MemoryProtocolSupportRegistry {
    
    

    /**
     * 协议支持包
     * <p>
     * K: 协议ID
     * V: 协议实体
     */
    private Map<String, ProtocolSupport> protocolSupportMap = new ConcurrentHashMap<>(16);

    /**
     * 取得解析器
     *
     * @param protocolId 协议ID
     */
    public ProtocolSupport getProtocolSupport(String protocolId) {
    
    
        return protocolSupportMap.get(protocolId);
    }

    /**
     * 发布协议
     */
    public void publishProtocol(String jarFilePath, String mainClass, String protocolId) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    
    
        decodeProtocolJar(jarFilePath,  mainClass,  protocolId);
    }

    /**
     * 发布协议
     */
    public void publishProtocol(ProtocolSupport protocolSupport) {
    
    
        protocolSupportMap.put(protocolSupport.getId(), protocolSupport);
    }

    /**
     * 解析协议jar包
     *
     * @param jarFilePath
     * @throws IllegalAccessException
     */
    private void decodeProtocolJar(String jarFilePath, String mainClass, String protocolId) {
    
    

        File jarFile = new File(jarFilePath);
        URL url;
        try {
    
    
            url = jarFile.toURI().toURL();
        } catch (Exception e) {
    
    
            throw new ServiceException("URL错误,加载不到jar");
        }
        if (null != url) {
    
    
            try {
    
    
                URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{
    
    url}, this.getClass().getClassLoader());
                Class<?> codec = urlClassLoader.loadClass(mainClass);
                Object instance = codec.getClass().forName(mainClass, true, urlClassLoader).newInstance();
                Method method = codec.getMethod("create");
                // 加载协议包,创建实体对象
                ProtocolSupport protocolSupport = (ProtocolSupport) method.invoke(instance);
                if (!StringUtils.equals(protocolSupport.getId(), protocolId)) {
    
    
                    throw new ServiceException("jar包内定义的ID与协议ID不匹配");
                }
                protocolSupportMap.put(protocolSupport.getId(), protocolSupport);
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
    
    
                throw new ServiceException("jar包解析异常");
            }
        }
    }


}

猜你喜欢

转载自blog.csdn.net/baidu_29609961/article/details/131898956