動的なプロトコル解析を実現
ソースコード
過去のリンク
独自の IoT プラットフォームをゼロから構築する (1) 要件分析とアーキテクチャ設計
独自の IoT プラットフォームをゼロから構築する (2) サブスクリプションベースのメッセージ バスを実現する
独自の IoT プラットフォームをゼロから構築する (3) ) netty をベースにした mqtt サーバー ゲートウェイを実装
ビジネスニーズ
モノのインターネットプラットフォームのシナリオでは、さまざまなアクセスデバイスが存在しますが、現時点ではハードコーディングの形でソースコードに実装することができないため、自由に追加・変更できる動的なプロトコル解析手法を実装する必要があります。変更されました。
実現方法
このシナリオでは、対応するインターフェイスが 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 は、アップリンク解析とダウンリンク解析をそれぞれ実装するために使用される 2 つのインターフェイスを継承し、DeviceMessageCodec はこのパーサーに対応するプロトコル タイプを定義します。最終的な構造は次のとおりです: プロトコル パッケージには複数の DeviceMessageCodecs が含まれる場合があります (プロトコル パッケージによると)
。解析されたネットワーク プロトコルは、たとえば、TCP と MQTT が必要な場合は 2 つの DeviceMessageCodec を解析する必要があることに由来し、各 DeviceMessageCodec はアップリンク メッセージ解析とダウンリンク メッセージ解析の 2 つの機能を実現します。
/**
* 实现自定义消息解析需要实现该类
*/
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);
}
}
プロトコルパケットの解析
クラスローダーの形式で jar パッケージを解析し、反射的に ProtocolSupportProvider の作成を呼び出します。
**
* 自定义协议支持包注册器(内存)
*/
@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包解析异常");
}
}
}
}