[fix] change

This commit is contained in:
wujiawei
2024-05-29 10:39:56 +08:00
commit 2a0f58f0d1
464 changed files with 63472 additions and 0 deletions

View File

@ -0,0 +1,92 @@
### 内网穿透使用
#### 模块说明
| 模块 | 所属层级 | 描述 | 端口 |
|------------------------------------------------------------------------------------|------|------------------------|----------------------------------------------------------------|
| [wu-smart-agent-network-heartbeat-common](wu-smart-agent-network-heartbeat-common) | 基础模块 | 基于Netty数据解码、编码、通道处理器声明 | 无 |
| [wu-smart-agent-network-heartbeat-server](wu-smart-agent-network-heartbeat-server) | 启动模块 | 内网穿透服务端 | http端口6001、tcp端口:7001 (默认tcp端口=http端口+1000 如6001+1000=7001) |
| [wu-smart-agent-network-heartbeat-client](wu-smart-agent-network-heartbeat-client) | 启动模块 | 内网穿透客户端 | 6004 |
#### 功能
1.将局域网IP映射到公网IP
2. 支持tcp、http映射
#### 使用
```text
客户端配置信息
```
```yaml
spring:
middleground:
netty:
inet-host: 127.0.0.1 # 服务端地址
inet-port: 7001 #服务端端口
client-id: local # 客户端ID
```
```text
服务端配置客户端映射地址
数据库表【internal_network_penetration_mapping】 添加数据
```
| 客户端ID | 客户端真实地址 | 客户端真实端口 | 创建时间 | id | 是否删除 | 更新时间 | 访客端口 | 描述 |
|--------------|----------------|---------|------|----|------|------|-------|------------------------------------------------------------|
| local | 127.0.0.1 | 18080 | null | 1 | 0 | null | 19080 | 访客通过 --> 19080 --> 访问 --> 客户端 local本地的 18080 |
| local | 127.0.0.1 | 28080 | null | 2 | 0 | null | 29080 | 访客通过 --> 29080 --> 访问 --> 客户端 local本地的 28080 |
| local | 127.0.0.1 | 3306 | null | 3 | 0 | null | 4306 | 访客通过 --> 4306 --> 访问 --> 客户端 local本地的 3306 |
| local | 192.168.17.185 | 80 | null | 4 | 0 | null | 30080 | 访客通过 --> 30080 --> 访问 --> 客户端 local局域网内192.168.17.185的 80 |
| middleground | web-nginx | 80 | null | 5 | 0 | null | 31570 | 访客通过 --> 31570 --> 访问 --> 客户端 local局域网内web-nginx的 80 |
#### 部署
##### 云端部署
```text
云端部署:内网穿透服务端
如果云端需要部署云上暂存+内网穿透功能:需要部署 内网穿透服务端、暂存服务、内网穿透客户端、云上离线网关
```
| 模块 | 说明 | 部署内网穿透必须 | 部署内网穿透+云上暂存必须 |
|------------------------------------------------------------------|------------|----------|---------------|
| [wu-smart-agent-network-heartbeat-server](wu-smart-agent-network-heartbeat-server) | 内网穿透+心跳服务端 | ☑️ | ☑️ |
| [wu-smart-agent-network-heartbeat-client](wu-smart-agent-network-heartbeat-client) | 内网穿透+心跳客户端 | ✖️ | ☑️ |
| [wu-lazy-cloud-staging-provider](wu-lazy-cloud-staging-provider) | 暂存服务 | ✖️ | ☑️ |
| [wu-lazy-cloud-central-gateway](wu-lazy-cloud-central-gateway) | 云上暂存网关 | ✖️ | ☑️ |
| [wu-lazy-cloud-central-gateway](wu-lazy-cloud-central-gateway) | 云下暂存网关 | ✖️ | ✖️ |
##### 云网关部署
```text
云网关部署内网穿透客户端
```
| 模块 | 说明 | 部署内网穿透必须 | 部署内网穿透+云上暂存必须 |
|------------------------------------------------------------------|------------|----------|---------------|
| [wu-smart-agent-network-heartbeat-server](wu-smart-agent-network-heartbeat-server) | 内网穿透+心跳服务端 | ✖️ | ✖️ |
| [wu-smart-agent-network-heartbeat-client](wu-smart-agent-network-heartbeat-client) | 内网穿透+心跳客户端 | ✖️ | ☑️ |
| [wu-lazy-cloud-staging-provider](wu-lazy-cloud-staging-provider) | 暂存服务 | ✖️ | ✖️ |
| [wu-lazy-cloud-central-gateway](wu-lazy-cloud-central-gateway) | 云上暂存网关 | ✖️ | ☑️ |
| [wu-lazy-cloud-central-gateway](wu-lazy-cloud-central-gateway) | 云下暂存网关 | ✖️ | ✖️ |
##### 独立租户部署
```text
拥有内网穿透能力:需要部署内网穿透客户端
内网穿透+离线暂存能力: 需要部署内网穿透客户端、离线网关、离线暂存服务
```
| 模块 | 说明 | 部署内网穿透必须 | 部署内网穿透+云上暂存必须 |
|------------------------------------------------------------------|------------|----------|---------------|
| [wu-smart-agent-network-heartbeat-server](wu-smart-agent-network-heartbeat-server) | 内网穿透+心跳服务端 | ✖️ | ✖️ |
| [wu-smart-agent-network-heartbeat-client](wu-smart-agent-network-heartbeat-client) | 内网穿透+心跳客户端 | ☑️ | ☑️ |
| [wu-lazy-cloud-staging-provider](wu-lazy-cloud-staging-provider) | 暂存服务 | ✖️ | ☑️ |
| [wu-lazy-cloud-central-gateway](wu-lazy-cloud-central-gateway) | 云上暂存网关 | ✖️ | ✖️ |
| [wu-lazy-cloud-central-gateway](wu-lazy-cloud-central-gateway) | 云下暂存网关 | ✖️ | ☑️ |

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1,77 @@
@startuml
title 内网穿透
actor 访客
package "客户端"{
node "Netty客户端" {
component [客户端当前通道]{
[客户端心跳通道]
component [客户端代理通信通道]{
[客户端通信通道读数据]
[客户端通信通道返回数据]
}
}
' [客户端当前通道] <...right... [客户端真实代理通道]: 返回真实服务请求结果
' [客户端当前通道] ...right..> [客户端真实代理通道]: 转发二进制请求到真实服务通道
}
node “客户端真实服务”{
component [客户端需要代理的真实服务A]{
[客户端真实通道读数据]
[客户端真实通道返回数据]
}
}
' [客户端真实代理通道] ...right...> [客户端真实服务]: 发送真实二进制请求到真实服务
}
package "服务端"{
node "Netty服务端" {
component [Netty服务端通道] {
component [服务端心跳通道]{
}
component [服务端代理通信通道]{
[服务端通信通道读数据]
[服务端通信通道返回数据]
}
}
component [Netty服务端绑定访客端口] {
component [服务端访客真实通道]{
[服务端访客真实通道读数据]
[服务端访客真实通道返回数据]
}
}
}
}
[服务端心跳通道] <----> [客户端心跳通道]:长连接channel
[访客] ..> [服务端访客真实通道读数据]: 访客访问数据
[服务端访客真实通道读数据] ...> [服务端通信通道读数据]: 服务端访客数据转发到通信通道
[服务端通信通道读数据] ..down..> [客户端通信通道读数据]: 服务端通信将数据转发到客户端通信通道
[客户端通信通道读数据] ..down..> [客户端真实通道读数据]: 客户端通信通道将数据转发道客户端端真实代理通道
[客户端真实通道读数据] ..left..> [客户端真实通道返回数据]: 处理数据。。。
[客户端真实通道返回数据] ..up..> [客户端通信通道返回数据]: 客户端真实服务返回数据
[客户端通信通道返回数据] ..up..> [服务端通信通道返回数据]: 将客户端返回的数据发送给访客真实通道
[服务端通信通道返回数据] ..up..> [服务端访客真实通道返回数据]: 返回数据
@enduml

55
Cluster.puml Normal file
View File

@ -0,0 +1,55 @@
@startuml
'https://plantuml.com/component-diagram
title 网络代理集群模式设计集群初始化 node1 初始化后自动初始化node2
package "Network Cluster" {
database "Network DB" {
}
node "node1"{
node "Network Server Node1" {
}
node "Network Client Node1" {
}
"Network Server Node1" ----> "Network Server Node1" : 5. rescan node config and register
"Network Server Node1" --left--> "Network DB" :node1 config story get and use
' "Network DB" --down-- "Network Client Node1" :node1 config get and use
"Network Client Node1" --up--> "Network Server Node1" :node1 register
' "Network Server Node2" --> "Network Client Node1" :scan node config and register
}
node "node2"{
node "Network Server Node2" {
}
node "Network Client Node2" {
}
"Network Server Node2" ----> "Network Server Node2" : rescan node config and register
"Network Server Node2" --right--> "Network DB" :1. node2 config story get and use
' "Network DB" --down--> "Network Client Node2" :node2 config get and use
"Network Client Node2" --up--> "Network Server Node2" :2. node2 register..
"Network Server Node2" --down--> "Network Client Node2" :3. node2 register success
"Network Client Node2" --left-> "Network Server Node1" :4. send msg to all register server ,tail it scan node config and register
' "Network Client Node2" ---> "Network Server Node2" :send msg to all register server ,tail it scan node config and register
}
}
@enduml

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

320
README.md Normal file
View File

@ -0,0 +1,320 @@
<p align="center">
<a target="_blank" href="https://search.maven.org/search?q=wu-smart-agent-network%20wu-smart-agent-network">
<img src="https://img.shields.io/nexus/s/top.wu2020/wu-smart-agent-network?server=https%3A%2F%2Foss.sonatype.org&style=flat&logo=log" alt="Maven" />
</a>
<a target="_blank" href="https://search.maven.org/search?q=wu-smart-agent-network%20wu-smart-agent-network">
<img src="https://img.shields.io/maven-central/v/top.wu2020/wu-smart-agent-network" alt="Maven" />
</a>
<a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.txt">
<img src="https://img.shields.io/:license-Apache2-blue.svg" alt="Apache 2" />
</a>
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html">
<img src="https://img.shields.io/badge/JDK-11-green.svg" alt="jdk-11" />
</a>
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html">
<img src="https://img.shields.io/badge/JDK-17-green.svg" alt="jdk-17" />
</a>
<br />
<img src="https://img.shields.io/badge/SpringBoot-v3.x-blue">
<br />
<a target="_blank" href='https://gitee.com/wujiawei1207537021/wu-smart-agent-network'>
<img src='https://gitee.com/wujiawei1207537021/wu-smart-agent-network/badge/star.svg' alt='Gitee star'/>
</a>
<a target="_blank" href='https://gitee.com/wujiawei1207537021/wu-smart-agent-network'>
<img src='https://img.shields.io/github/stars/wujiawei1207537021/wu-smart-agent-network?style=flat&logo=github' alt='GitHub star'/>
</a>
<a target="_blank" href='https://gitee.com/wujiawei1207537021/wu-smart-agent-network'>
<img src='https://img.shields.io/github/forks/wujiawei1207537021/wu-smart-agent-network?style=flat&logo=github' alt='GitHub star'/>
</a>
</p>
#### 项目介绍
wu-smart-agent-network
是一款基于([wu-framework-parent](https://gitee.com/wujiawei1207537021/wu-framework-parent)孵化出的项目内部使用Lazy
ORM操作数据库主要功能是网络穿透对于没有公网IP的服务进行公网IP映射
使用环境JDK17 Spring Boot 3.0.2
#### 项目地址
[Gitee](https://gitee.com/wujiawei1207537021/wu-smart-agent-network)
[GitHub](https://github.com/wujiawei1207537021/wu-smart-agent-network)
#### 架构图
![architecture.png](architecture.png)
#### 实现原理
##### 服务端创建socket服务端绑定本地端口用于客户端连接
```java
package org.framework.smart.agent.network.heartbeat.server.netty.socket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.framework.smart.agent.network.heartbeat.server.netty.filter.NettyServerFilter;
public class NettyOnCloudNettyServerSocket {
private final EventLoopGroup bossGroup = new NioEventLoopGroup();
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private final NettyServerFilter nettyServerFilter;// 通道业务处理
private ChannelFuture channelFuture;
public NettyOnCloudNettyServerSocket(NettyServerFilter nettyServerFilter) {
this.nettyServerFilter = nettyServerFilter;
}
/**
* 启动服务端
*
* @throws Exception
*/
public void startServer(int serverPort) throws Exception {
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
// 给服务端channel设置属性
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(nettyServerFilter)
;
channelFuture = b.bind(serverPort).sync();
channelFuture.addListener((ChannelFutureListener) channelFuture -> {
// 服务器已启动
});
channelFuture.channel().closeFuture().sync();
} finally {
shutdown();
// 服务器已关闭
}
}
public void shutdown() {
if (channelFuture != null) {
channelFuture.channel().close().syncUninterruptibly();
}
if ((bossGroup != null) && (!bossGroup.isShutdown())) {
bossGroup.shutdownGracefully();
}
if ((workerGroup != null) && (!workerGroup.isShutdown())) {
workerGroup.shutdownGracefully();
}
}
}
```
##### 客户端通过class NettyClientSocket 连接服务端
```java
package org.framework.smart.agent.network.heartbeat.client.netty.socket;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.application.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.client.netty.filter.NettyClientFilter;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 客户端连接服务端
*/
@Slf4j
public class NettyClientSocket {
private static final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
/**
* 服务端host
*/
private final String inetHost;
/**
* 服务端端口
*/
private final int inetPort;
/**
* 当前客户端id
*/
@Getter
private final String clientId;
/**
* nacos配置信息处理应用
*/
@Getter
private final ClientNettyConfigApplication clientChangeEvent;
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList; // 处理服务端发送过来的数据类型
public NettyClientSocket(String inetHost, int inetPort, String clientId, ClientNettyConfigApplication clientChangeEvent, List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.inetHost = inetHost;
this.inetPort = inetPort;
this.clientId = clientId;
this.clientChangeEvent = clientChangeEvent;
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
public void newConnect2Server() throws InterruptedException {
newConnect2Server(inetHost, inetPort, clientId, clientChangeEvent);
}
protected void newConnect2Server(String inetHost, int inetPort, String clientId, ClientNettyConfigApplication clientChangeEvent) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new NettyClientFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList), this))
;
log.info("连接服务端IP:{},连接服务端端口:{}", inetHost, inetPort);
ChannelFuture future = bootstrap.connect(inetHost, inetPort);
Channel channel = future.channel();
log.info("使用的客户端ID:" + clientId);
future.addListener((ChannelFutureListener) futureListener -> {
if (futureListener.isSuccess()) {
log.info("连接服务端成功");
// 告诉服务端这条连接是client的连接
NettyProxyMsg nettyMsg = new NettyProxyMsg();
nettyMsg.setType(MessageType.REPORT_CLIENT_CONNECT_SUCCESS);
nettyMsg.setClientId(clientId);
nettyMsg.setData((clientId).getBytes());
ChannelAttributeKeyUtils.buildClientId(channel, clientId);
channel.writeAndFlush(nettyMsg);
// 在线
clientChangeEvent.clientOnLine(clientId);
} else {
log.info("每隔2s重连....");
// 离线
clientChangeEvent.clientOffLine(clientId);
futureListener.channel().eventLoop().schedule(() -> {
try {
newConnect2Server(inetHost, inetPort, clientId, clientChangeEvent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, TimeUnit.SECONDS);
}
});
}
/**
* 关闭连接
*/
public void shutdown() {
if ((eventLoopGroup != null) && (!eventLoopGroup.isShutdown())) {
eventLoopGroup.shutdownGracefully();
}
}
}
```
##### 通过客户端与服务端建立的连接进行访客端口绑定
上述连接会形成一个channel我们称之为通道本文中简单叫**心跳通道**
第一步 页面GUI进行新增访客端口而后将访客端口与客户端绑定如果客户端已经启动使用页面客户端下线触发第二步
第二步 客户端与访客端口绑定后使用**心跳通道** 发送客户端告诉客户端,你帮我绑定你本地真实端口
第三步 访客访问,访客通过访客端口访问数据,此时访客通道打开截取访客发送的数据,然后将数据发送给客户真实通道,数据返回后再返回给访客通道
### 功能
1.内网穿透
2.服务端自主下发数据到客户端
3.流量监控
### 项目结构
| 模块 | 版本 | 描述 |
|-----------------------------------------------------------------------------------------------------------------------------------------|----------------------|------------------------------|
| [wu-smart-agent-network-heartbeat-common](wu-smart-agent-network-heartbeat-common) | 1.2.6-JDK17-SNAPSHOT | 内网穿透公共模块(声明接口、枚举、常量、适配器、解析器) |
| [wu-smart-agent-network-heartbeat-client](wu-smart-agent-network-heartbeat-client) | 1.2.6-JDK17-SNAPSHOT | 客户端(支持二次开发) |
| [wu-smart-agent-network-heartbeat-server](wu-smart-agent-network-heartbeat-server) | 1.2.6-JDK17-SNAPSHOT | 服务端(支持二次开发) |
| [wu-smart-agent-network-ui](wu-smart-agent-network-heartbeat-server-ui) | 1.2.6-JDK17-SNAPSHOT | 服务端页面 |
| [wu-smart-agent-network-heartbeat-client-start](wu-smart-agent-network-heartbeat-sample/wu-smart-agent-network-heartbeat-client-sample) | 1.2.6-JDK17-SNAPSHOT | 客户端样例 |
| [wu-smart-agent-network-heartbeat-server-start](wu-smart-agent-network-heartbeat-sample/wu-smart-agent-network-heartbeat-server-sample) | 1.2.6-JDK17-SNAPSHOT | 服务端样例 |
### 使用技术
| 框架 | 版本 | 描述 |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|--------------|
| spring-boot | 3.0.7 | springboot框架 |
| [wu-framework-web](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-framework-web) | 1.2.6-JDK17-SNAPSHOT | web容器 |
| [Lazy -ORM](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-inner-intergration/wu-database-parent) | 1.2.6-JDK17-SNAPSHOT | ORM |
| mysql-connector-j | 8.0.33 | mysql驱动 |
| [wu-authorization-server-platform-starter](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-smart-platform/wu-authorization-server-platform-starter) | 1.2.6-JDK17-SNAPSHOT | 用户授权体系 |
### 使用环境
IDEA
Mac、Windows
JAVA >=13
MAVEN
### 启动
docker启动
docker run -d -it -p 18080:18080 --name wu-smart-agent-network-heartbeat-server registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-agent-network-heartbeat-server:1.2.6-JDK17-SNAPSHOT
http://127.0.0.1:18080/swagger-ui/index.html
源码启动
#### 页面操作
启动项目后打开服务端界面
![img.png](url_info.png)
默认账号密码admin/admin
![img.png](login.png)
初始化项目
![img.png](init_menu.png)
添加角色
![img.png](init_role.png)
为用户授权
![img.png](authRoe2User.png)
刷新页面
![img.png](cloud_client.png)
客户端管理(客户端会自动注册)
![img.png](cloud_client.png)
网络映射管理(修改后者新增需要映射的客户端)
![img.png](mapping.png)
访客端口池管理(服务器端需要开放的端口)
![img.png](visitor_port.png)
流量管理(每个客户端使用的流量)
![img.png](flow.png)
流量日统计报表
![clientPerDayFlow.png](clientPerDayFlow.png)
客户端近七天使用流量
![clientPortPerDayFlow.png](clientPortPerDayFlow.png)

BIN
architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
authRoe2User.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
clientPerDayFlow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

BIN
clientPortPerDayFlow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

BIN
cloud_client.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
init_menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
init_role.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

50
k8s-node-pod-network.puml Normal file
View File

@ -0,0 +1,50 @@
@startuml
'https://plantuml.com/component-diagram
title k8s中node中的pod之间通讯
package "k8s node pod" {
node "node1 "{
node "node1-pod1" {
}
node "node1-pod2" {
}
"node1-pod1" ---- "node1-pod2" :正常
}
node "node2问题节点"{
node "node2-pod1" {
}
node "node2-pod2" {
}
"node2-pod1" --right-- "node2-pod2" :正常
"node1-pod1" -- "node2-pod1" :无法通信
"node1-pod1" -- "node2-pod2" :无法通信
}
node "node3"{
node "node3-pod1" {
}
node "node3-pod2" {
}
"node3-pod1" ---- "node3-pod2" :正常
"node3-pod1" -- "node2-pod1" :正常
"node3-pod1" -- "node2-pod2" :正常
}
}
@enduml

BIN
login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

BIN
main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
mapping.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

259
pom.xml Normal file
View File

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>wu-framework-parent</artifactId>
<groupId>top.wu2020</groupId>
<version>1.2.6-JDK17-SNAPSHOT</version>
</parent>
<artifactId>wu-smart-agent-network</artifactId>
<packaging>pom</packaging>
<version>1.2.6-JDK17-SNAPSHOT</version>
<description>云上云下</description>
<modules>
<!-- 云上服务组件 -->
<module>wu-smart-agent-network-heartbeat-server</module>
<module>wu-smart-agent-network-heartbeat-server-cluster</module>
<module>wu-smart-agent-network-heartbeat-client</module>
<module>wu-smart-agent-network-heartbeat-common</module>
<!-- 样例 -->
<module>wu-smart-agent-network-heartbeat-start</module>
</modules>
<properties>
</properties>
<dependencies>
<!-- mapstruct -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-dependencies</artifactId>
<version>1.2.6-JDK17-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>maven_central</id>
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
</repositories>
<profiles>
<profile>
<id>oss</id>
<build>
<plugins>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<locale>en_US</locale>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<doclint>none</doclint>
</configuration>
</execution>
</executions>
</plugin>
<!-- GPG -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- flatten -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-->发布的地址<-->
<distributionManagement>
<snapshotRepository>
<id>snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>snapshots</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>snapshots</id>
<!-->发布的地址<-->
<distributionManagement>
<snapshotRepository>
<id>maven-snapshots</id>
<name>deployment</name>
<url>http://192.168.17.221:31004/repository/maven-snapshots/</url>
</snapshotRepository>
<repository>
<id>maven-releases</id>
<name>deployment</name>
<url>http://192.168.17.221:31004/repository/maven-releases/</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.28</version>
<!-- 使用graalvm提供的可达性元数据很多第三方库就直接可以构建成可执行文件了 -->
<configuration>
<!-- for agent -->
<agent>
<defaultMode>Standard</defaultMode>
<options>
<builtinCallerFilter>true</builtinCallerFilter>
<builtinHeuristicFilter>true</builtinHeuristicFilter>
<enableExperimentalPredefinedClasses>true
</enableExperimentalPredefinedClasses>
<enableExperimentalUnsafeAllocationTracing>true
</enableExperimentalUnsafeAllocationTracing>
<trackReflectionMetadata>true</trackReflectionMetadata>
</options>
<metadataCopy>
<merge>true</merge>
</metadataCopy>
</agent>
<!-- for metadata repository -->
<metadataRepository>
<enabled>true</enabled>
</metadataRepository>
</configuration>
<executions>
<execution>
<id>add-reachability-metadata</id>
<goals>
<goal>add-reachability-metadata</goal>
</goals>
</execution>
<execution>
<id>test-native</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
</execution>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

BIN
url_info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

11
version.md Normal file
View File

@ -0,0 +1,11 @@
### 版本更新
#### 1.2.3-JDK17-SNAPSHOT
【fix】修正流量计算保存两位小数
[fix] 添加配置spring.lazy.netty.enable 控制是否开启客户端默认是自动连接服务端的 需要手动关闭
#### 1.2.6-JDK17-SNAPSHOT
[fix] 客户端添加按钮删除
[fix] 修改浏览器title内网穿透
[fix] HandleChannelTypeAdvanced 添加权重 order 越小越靠前
[fix] 新增网络映射新增、修改、删除、自动变更

BIN
visitor_port.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -0,0 +1,22 @@
FROM registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-framework-parent:jdk-17.0.7-x64
MAINTAINER wujiawei <1207537021@qq.com>
RUN echo "Asia/Shanghai" > /etc/timezone
ENV APP_JAR_NAME=$APP_NAME \
JAVA_OPTS="-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom" \
PARAMS=""
COPY target/*.jar /app.jar
ENTRYPOINT exec java -server $JAVA_OPTS -jar /app.jar $PARAMS

View File

@ -0,0 +1,16 @@
FROM alpine
MAINTAINER wujiawei <1207537021@qq.com>
RUN echo "Asia/Shanghai" > /etc/timezone
COPY target/lazy-cloud-heartbeat-client /native-app
ENTRYPOINT ["/bin/sh" ,"-c", "exec ./native-app"]

View File

@ -0,0 +1,55 @@
#### 构建native 镜像
```shell
mvn clean compile
mvn spring-boot:process-aot -Pnative
mvn native:build -Pnative
```
### 构建docker镜像
```shell
docker build -t docker-registry.wujiawei.com/lazy/lazy-under-cloud-heartbeat-client:lazy-2.4.2-native-SNAPSHOT_latest -f Native-Dockerfile .
docker push docker-registry.wujiawei.com/lazy/lazy-under-cloud-heartbeat-client:lazy-2.4.2-native-SNAPSHOT_latest
```
## BUILD IMAGE
```shell
#docker login --username=1207537021@qq.com registry.cn-hangzhou.aliyuncs.com
docker build -t registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-agent-network-heartbeat-client:server-jdk17-master .
docker push registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-agent-network-heartbeat-client:server-jdk17-master
```
### run
```shell
docker run -d -it --name client -e spring.lazy.netty.inet-host=124.222.48.62 -e spring.lazy.netty.inet-port=30676 -e spring.lazy.netty.client-id=node1 registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-agent-network-heartbeat-client:server-jdk17-master
```
```shell
gu install native-image
gu list
mvn native:build
```
```shell
mvn clean native:compile -Pnative
```
```RUN
docker run -d -it -p 18080:18080 --name wu-smart-agent-network-heartbeat-client registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-smart-agent-network-heartbeat-client:server-jdk17-master
http://127.0.0.1:18080/swagger-ui/index.html
```

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-smart-agent-network</artifactId>
<version>1.2.6-JDK17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>wu-smart-agent-network-heartbeat-client</artifactId>
<description>云下心跳客户端</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- 通用心跳包 -->
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-smart-agent-network-heartbeat-common</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.50</version>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web-spring-starter</artifactId>
</dependency>
<!-- h2数据库驱动 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-database-lazy-plus-starter</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>maven_central</id>
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
</repositories>
</project>

View File

@ -0,0 +1,9 @@
package org.framework.smart.agent.network.heartbeat.client;
import org.springframework.context.annotation.ComponentScan;
import org.wu.framework.lazy.orm.core.stereotype.LazyScan;
@ComponentScan(basePackages = "org.framework.smart.agent.network.heartbeat.client")
@LazyScan(scanBasePackages = "org.framework.smart.agent.network.heartbeat.client.infrastructure.entity")
public class EnableHeartbeatClientAutoConfiguration {
}

View File

@ -0,0 +1,54 @@
package org.framework.smart.agent.network.heartbeat.client.application;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
/**
* 客户端状态变更事件
* @see ClientChangeEvent
*/
@Deprecated
public interface ClientChangeApplication {
/**
* 推送客户端在线
*/
void clientOnLine(String clientId);
/**
* 推送客户端在线
* @param clientId 客户端
* @param inetHost 服务端ip
* @param inetPort 服务端端口
*/
void clientOnLine(String inetHost, int inetPort,String clientId);
/**
* 推送客户端离线
*/
void clientOffLine(String clientId);
/**
* 推送客户端离线
* @param clientId 客户端
* @param inetHost 服务端ip
* @param inetPort 服务端端口
*/
void clientOffLine(String inetHost, int inetPort,String clientId);
/**
* 暂存开启
*
* @param clientId 租户ID
*/
void stagingOpen(String clientId);
/**
* 暂存关闭
*
* @param clientId 客户端ID 对应的租户
*/
void stagingClose(String clientId);
}

View File

@ -0,0 +1,128 @@
package org.framework.smart.agent.network.heartbeat.client.application;
import org.wu.framework.web.response.Result;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesRemoveCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesStoryCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesUpdateCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesQueryListCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesQueryOneCommand;
import org.framework.smart.agent.network.heartbeat.client.application.dto.LazyNettyServerPropertiesDTO;
import java.util.List;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyApplication
**/
public interface LazyNettyServerPropertiesApplication {
/**
* describe 新增服务端配置信息
*
* @param lazyNettyServerPropertiesStoryCommand 新增服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息新增后领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerProperties> story(LazyNettyServerPropertiesStoryCommand lazyNettyServerPropertiesStoryCommand);
/**
* describe 批量新增服务端配置信息
*
* @param lazyNettyServerPropertiesStoryCommandList 批量新增服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息新增后领域对象集合
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<List<LazyNettyServerProperties>> batchStory(List<LazyNettyServerPropertiesStoryCommand> lazyNettyServerPropertiesStoryCommandList);
/**
* describe 更新服务端配置信息
*
* @param lazyNettyServerPropertiesUpdateCommand 更新服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerProperties> updateOne(LazyNettyServerPropertiesUpdateCommand lazyNettyServerPropertiesUpdateCommand);
/**
* describe 查询单个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryOneCommand 查询单个服务端配置信息
* @return {@link Result<LazyNettyServerPropertiesDTO>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerPropertiesDTO> findOne(LazyNettyServerPropertiesQueryOneCommand lazyNettyServerPropertiesQueryOneCommand);
/**
* describe 查询多个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryListCommand 查询多个服务端配置信息
* @return {@link Result <List<LazyNettyServerPropertiesDTO>>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<List<LazyNettyServerPropertiesDTO>> findList(LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand);
/**
* describe 分页查询多个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryListCommand 分页查询多个服务端配置信息
* @return {@link Result <LazyPage<LazyNettyServerPropertiesDTO>>} 分页服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyPage<LazyNettyServerPropertiesDTO>> findPage(int size, int current, LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand);
/**
* describe 删除服务端配置信息
*
* @param lazyNettyServerPropertiesRemoveCommand 删除服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerProperties> remove(LazyNettyServerPropertiesRemoveCommand lazyNettyServerPropertiesRemoveCommand);
/**
* 启动socket
*
* @param lazyNettyServerProperties 配置
*/
void starterOneClientSocket(LazyNettyServerProperties lazyNettyServerProperties);
/**
* 启动所有 配置的socket
*/
void starterAllClientSocket();
/**
* 关闭 客户端socket
* @param needCloseLazyNettyServerProperties 配置
*/
void destroyOneClientSocket(LazyNettyServerProperties needCloseLazyNettyServerProperties);
/**
* 关闭 客户端socket
*/
void destroyClientSocket();
}

View File

@ -0,0 +1,93 @@
package org.framework.smart.agent.network.heartbeat.client.application.assembler;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesRemoveCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesStoryCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesUpdateCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesQueryListCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesQueryOneCommand;
import org.framework.smart.agent.network.heartbeat.client.application.dto.LazyNettyServerPropertiesDTO;
import org.mapstruct.factory.Mappers;
import org.mapstruct.Mapper;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyAssembler
**/
@Mapper
public interface LazyNettyServerPropertiesDTOAssembler {
/**
* describe MapStruct 创建的代理对象
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerPropertiesDTOAssembler INSTANCE = Mappers.getMapper(LazyNettyServerPropertiesDTOAssembler.class);
/**
* describe 应用层存储入参转换成 领域对象
*
* @param lazyNettyServerPropertiesStoryCommand 保存服务端配置信息对象
* @return {@link LazyNettyServerProperties} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerProperties toLazyNettyServerProperties(LazyNettyServerPropertiesStoryCommand lazyNettyServerPropertiesStoryCommand);
/**
* describe 应用层更新入参转换成 领域对象
*
* @param lazyNettyServerPropertiesUpdateCommand 更新服务端配置信息对象
* @return {@link LazyNettyServerProperties} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerProperties toLazyNettyServerProperties(LazyNettyServerPropertiesUpdateCommand lazyNettyServerPropertiesUpdateCommand);
/**
* describe 应用层查询入参转换成 领域对象
*
* @param lazyNettyServerPropertiesQueryOneCommand 查询单个服务端配置信息对象参数
* @return {@link LazyNettyServerProperties} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerProperties toLazyNettyServerProperties(LazyNettyServerPropertiesQueryOneCommand lazyNettyServerPropertiesQueryOneCommand);
/**
* describe 应用层查询入参转换成 领域对象
*
* @param lazyNettyServerPropertiesQueryListCommand 查询集合服务端配置信息对象参数
* @return {@link LazyNettyServerProperties} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerProperties toLazyNettyServerProperties(LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand);
/**
* describe 应用层删除入参转换成 领域对象
*
* @param lazyNettyServerPropertiesRemoveCommand 删除服务端配置信息对象参数
* @return {@link LazyNettyServerProperties} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerProperties toLazyNettyServerProperties(LazyNettyServerPropertiesRemoveCommand lazyNettyServerPropertiesRemoveCommand);
/**
* describe 持久层领域对象转换成DTO对象
*
* @param lazyNettyServerProperties 服务端配置信息领域对象
* @return {@link LazyNettyServerPropertiesDTO} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerPropertiesDTO fromLazyNettyServerProperties(LazyNettyServerProperties lazyNettyServerProperties);
}

View File

@ -0,0 +1,77 @@
package org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyQueryListCommand
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties_query_List_command",description = "服务端配置信息")
public class LazyNettyServerPropertiesQueryListCommand {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,76 @@
package org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyQueryOneCommand
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties_query_one_command",description = "服务端配置信息")
public class LazyNettyServerPropertiesQueryOneCommand {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,77 @@
package org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyRemoveCommand
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties_remove_command",description = "服务端配置信息")
public class LazyNettyServerPropertiesRemoveCommand {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,76 @@
package org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyStoryCommand
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties_story_command",description = "服务端配置信息")
public class LazyNettyServerPropertiesStoryCommand {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,76 @@
package org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyUpdateCommand
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties_update_command",description = "服务端配置信息")
public class LazyNettyServerPropertiesUpdateCommand {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,76 @@
package org.framework.smart.agent.network.heartbeat.client.application.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyDTO
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties_command_dto",description = "服务端配置信息")
public class LazyNettyServerPropertiesDTO {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,270 @@
package org.framework.smart.agent.network.heartbeat.client.application.impl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.application.LazyNettyServerPropertiesApplication;
import org.framework.smart.agent.network.heartbeat.client.application.assembler.LazyNettyServerPropertiesDTOAssembler;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.*;
import org.framework.smart.agent.network.heartbeat.client.application.dto.LazyNettyServerPropertiesDTO;
import org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerPropertiesRepository;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.client.netty.socket.NettyClientSocket;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.wu.framework.core.NormalUsedString;
import org.wu.framework.database.lazy.web.plus.stereotype.LazyApplication;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.web.response.Result;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyApplicationImpl
**/
@Slf4j
@LazyApplication
public class LazyNettyServerPropertiesApplicationImpl implements LazyNettyServerPropertiesApplication {
@Resource
LazyNettyServerPropertiesRepository lazyNettyServerPropertiesRepository;
@Resource
private ClientChangeEvent clientChangeEvent;
@Resource
private List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList; // 处理服务端发送过来的数据类型
@Resource
private NettyClientProperties nettyClientProperties;// 默认配置文件配置
// 缓存连接socket
private final ConcurrentHashMap<LazyNettyServerProperties, NettyClientSocket> cacheNettyClientSocketMap = new ConcurrentHashMap<>();
public static final ThreadPoolExecutor NETTY_CLIENT_EXECUTOR =
new ThreadPoolExecutor(20, 50, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1));
/**
* describe 新增服务端配置信息
*
* @param lazyNettyServerPropertiesStoryCommand 新增服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息新增后领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerProperties> story(LazyNettyServerPropertiesStoryCommand lazyNettyServerPropertiesStoryCommand) {
LazyNettyServerProperties lazyNettyServerProperties = LazyNettyServerPropertiesDTOAssembler.INSTANCE.toLazyNettyServerProperties(lazyNettyServerPropertiesStoryCommand);
// 如果状态正在运行中直接忽略
lazyNettyServerPropertiesRepository.exists(lazyNettyServerProperties)
.accept(exists -> {
if (!exists) {
starterOneClientSocket(lazyNettyServerProperties);
}
});
lazyNettyServerProperties.setType(PropertiesType.DB);
return lazyNettyServerPropertiesRepository.story(lazyNettyServerProperties);
}
/**
* describe 批量新增服务端配置信息
*
* @param lazyNettyServerPropertiesStoryCommandList 批量新增服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息新增后领域对象集合
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<List<LazyNettyServerProperties>> batchStory(List<LazyNettyServerPropertiesStoryCommand> lazyNettyServerPropertiesStoryCommandList) {
List<LazyNettyServerProperties> lazyNettyServerPropertiesList = lazyNettyServerPropertiesStoryCommandList.stream().map(LazyNettyServerPropertiesDTOAssembler.INSTANCE::toLazyNettyServerProperties).collect(Collectors.toList());
return lazyNettyServerPropertiesRepository.batchStory(lazyNettyServerPropertiesList);
}
/**
* describe 更新服务端配置信息
*
* @param lazyNettyServerPropertiesUpdateCommand 更新服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerProperties> updateOne(LazyNettyServerPropertiesUpdateCommand lazyNettyServerPropertiesUpdateCommand) {
LazyNettyServerProperties lazyNettyServerProperties = LazyNettyServerPropertiesDTOAssembler.INSTANCE.toLazyNettyServerProperties(lazyNettyServerPropertiesUpdateCommand);
return lazyNettyServerPropertiesRepository.story(lazyNettyServerProperties);
}
/**
* describe 查询单个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryOneCommand 查询单个服务端配置信息
* @return {@link Result<LazyNettyServerPropertiesDTO>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerPropertiesDTO> findOne(LazyNettyServerPropertiesQueryOneCommand lazyNettyServerPropertiesQueryOneCommand) {
LazyNettyServerProperties lazyNettyServerProperties = LazyNettyServerPropertiesDTOAssembler.INSTANCE.toLazyNettyServerProperties(lazyNettyServerPropertiesQueryOneCommand);
return lazyNettyServerPropertiesRepository.findOne(lazyNettyServerProperties).convert(LazyNettyServerPropertiesDTOAssembler.INSTANCE::fromLazyNettyServerProperties);
}
/**
* describe 查询多个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryListCommand 查询多个服务端配置信息
* @return {@link Result<List<LazyNettyServerPropertiesDTO>>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<List<LazyNettyServerPropertiesDTO>> findList(LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand) {
LazyNettyServerProperties lazyNettyServerProperties = LazyNettyServerPropertiesDTOAssembler.INSTANCE.toLazyNettyServerProperties(lazyNettyServerPropertiesQueryListCommand);
return lazyNettyServerPropertiesRepository.findList(lazyNettyServerProperties).convert(lazyNettyServerPropertiess -> lazyNettyServerPropertiess.stream().map(LazyNettyServerPropertiesDTOAssembler.INSTANCE::fromLazyNettyServerProperties).collect(Collectors.toList()));
}
/**
* describe 分页查询多个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryListCommand 分页查询多个服务端配置信息
* @return {@link Result<LazyPage<LazyNettyServerPropertiesDTO>>} 分页服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyPage<LazyNettyServerPropertiesDTO>> findPage(int size, int current, LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand) {
LazyNettyServerProperties lazyNettyServerProperties = LazyNettyServerPropertiesDTOAssembler.INSTANCE.toLazyNettyServerProperties(lazyNettyServerPropertiesQueryListCommand);
return lazyNettyServerPropertiesRepository.findPage(size, current, lazyNettyServerProperties).convert(page -> page.convert(LazyNettyServerPropertiesDTOAssembler.INSTANCE::fromLazyNettyServerProperties));
}
/**
* describe 删除服务端配置信息
*
* @param lazyNettyServerPropertiesRemoveCommand 删除服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerProperties> remove(LazyNettyServerPropertiesRemoveCommand lazyNettyServerPropertiesRemoveCommand) {
LazyNettyServerProperties lazyNettyServerProperties = LazyNettyServerPropertiesDTOAssembler.INSTANCE.toLazyNettyServerProperties(lazyNettyServerPropertiesRemoveCommand);
// 关闭连接
destroyOneClientSocket(lazyNettyServerProperties);
return lazyNettyServerPropertiesRepository.remove(lazyNettyServerProperties);
}
/**
* 启动socket
*
* @param lazyNettyServerProperties 配置
*/
@Override
public void starterOneClientSocket(LazyNettyServerProperties lazyNettyServerProperties) {
boolean enabled = nettyClientProperties.isEnabled();
if (enabled) {
String inetHost = lazyNettyServerProperties.getInetHost();
Integer inetPort = lazyNettyServerProperties.getInetPort();
String clientId = lazyNettyServerProperties.getClientId();
NettyClientSocket nettyClientSocket = new
NettyClientSocket(inetHost, inetPort, clientId,
NormalUsedString.DEFAULT,
clientChangeEvent, handleChannelTypeAdvancedList);
cacheNettyClientSocketMap.put(lazyNettyServerProperties, nettyClientSocket);
// 更新状态为运行中
lazyNettyServerProperties.setConnectStatus(NettyClientStatus.RUNNING);
lazyNettyServerPropertiesRepository.story(lazyNettyServerProperties);
Thread thread = new Thread(() -> {
try {
nettyClientSocket.newConnect2Server();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// 当前服务连接Netty客户端:{},Netty端口:{}
log.info("Current service connection Netty client: {}, Netty port: {}", inetHost, inetPort);
NETTY_CLIENT_EXECUTOR.execute(thread);
}
}
/**
* 启动所有 配置的socket
*/
@Override
public void starterAllClientSocket() {
boolean enabled = nettyClientProperties.isEnabled();
if (enabled) {
// 查询所有配置
lazyNettyServerPropertiesRepository.findList(new LazyNettyServerProperties()).accept(lazyNettyServerPropertiesDTOS -> {
for (LazyNettyServerProperties nettyServerProperties : lazyNettyServerPropertiesDTOS) {
starterOneClientSocket(nettyServerProperties);
}
});
}
}
/**
* 关闭 客户端socket
*
* @param needCloseLazyNettyServerProperties 配置
*/
@Override
public void destroyOneClientSocket(LazyNettyServerProperties needCloseLazyNettyServerProperties) {
// 关闭指定socket
cacheNettyClientSocketMap.forEach(((nettyServerProperties, nettyClientSocket) -> {
String clientId = nettyServerProperties.getClientId();
String inetHost = nettyServerProperties.getInetHost();
Integer inetPort = nettyServerProperties.getInetPort();
String needCloseInetHost = needCloseLazyNettyServerProperties.getInetHost();
Integer needCloseInetPort = needCloseLazyNettyServerProperties.getInetPort();
String needCloseClientId = needCloseLazyNettyServerProperties.getClientId();
if (Objects.equals(clientId, needCloseClientId)
&& Objects.equals(inetPort, needCloseInetPort)
&& Objects.equals(inetHost, needCloseInetHost)) {
nettyClientSocket.shutdown();
// 关闭客户端:{}与服务端连接:{}:{}
log.warn("Close client: {} Connect to server: {}: {}", clientId, inetHost, inetPort);
}
}));
}
/**
* 关闭 客户端socket
*/
@Override
public void destroyClientSocket() {
// 关闭socket
cacheNettyClientSocketMap.forEach(((nettyServerProperties, nettyClientSocket) -> {
nettyClientSocket.shutdown();
String clientId = nettyServerProperties.getClientId();
String inetHost = nettyServerProperties.getInetHost();
Integer inetPort = nettyServerProperties.getInetPort();
// 关闭客户端:{}与服务端连接:{}:{}
log.warn("Close client: {} Connect to server: {}: {}", clientId, inetHost, inetPort);
}));
}
}

View File

@ -0,0 +1,82 @@
package org.framework.smart.agent.network.heartbeat.client.config;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.netty.socket.NettyClientSocket;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.wu.framework.core.NormalUsedString;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* description 自动配置
*
* @author 吴佳伟
* @date 2023/09/12 18:22
* @see InitConfig
*/
@Deprecated
@Slf4j
public class ClientAutoConfiguration implements CommandLineRunner {
private final NettyClientProperties nettyClientProperties;
private final ClientChangeEvent clientChangeEvent;
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList; // 处理服务端发送过来的数据类型
public static final ThreadPoolExecutor NETTY_CLIENT_EXECUTOR = new ThreadPoolExecutor(1, 1, 200, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(1));
public ClientAutoConfiguration(NettyClientProperties nettyClientProperties,
ClientChangeEvent clientChangeEvent,
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.nettyClientProperties = nettyClientProperties;
this.clientChangeEvent = clientChangeEvent;
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
@Bean(destroyMethod = "shutdown")
public NettyClientSocket nettyClientSocket() {
String inetHost = nettyClientProperties.getInetHost();
int inetPort = nettyClientProperties.getInetPort();
String clientId = nettyClientProperties.getClientId();
return new NettyClientSocket(inetHost, inetPort, clientId, NormalUsedString.DEFAULT, clientChangeEvent, handleChannelTypeAdvancedList);
}
/**
* @param args
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
String inetHost = nettyClientProperties.getInetHost();
int inetPort = nettyClientProperties.getInetPort();
String clientId = nettyClientProperties.getClientId();
NettyClientSocket nettyClientSocket = new NettyClientSocket(
inetHost, inetPort,
clientId, NormalUsedString.DEFAULT,
clientChangeEvent, handleChannelTypeAdvancedList);
Thread thread = new Thread(() -> {
try {
nettyClientSocket.newConnect2Server();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
log.info("当前服务连接Netty客户端:{},Netty端口:{}", inetHost, inetPort);
NETTY_CLIENT_EXECUTOR.execute(thread);
}
}

View File

@ -0,0 +1,89 @@
package org.framework.smart.agent.network.heartbeat.client.config;
import org.framework.smart.agent.network.heartbeat.client.netty.advanced.*;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import java.util.List;
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnProperty(prefix = NettyClientProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class HeartbeatClientConfiguration {
/**
* 服务端 处理客户端心跳
*
* @return ClientHandleChannelHeartbeatTypeAdvanced
*/
@Bean
public ClientHandleChannelHeartbeatTypeAdvanced clientChannelHeartbeatTypeAdvanced() {
return new ClientHandleChannelHeartbeatTypeAdvanced();
}
/**
* 处理 客户端代理的真实端口自动读写
*
* @return ClientHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced
*/
@Bean
public ClientHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced handleDistributeSingleClientRealAutoReadConnectTypeAdvanced() {
return new ClientHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced();
}
/**
* 处理 接收服务端发送过来的聊天信息
*
* @return ClientHandleDistributeSingleClientMessageTypeAdvanced
*/
@Bean
public ClientHandleDistributeSingleClientMessageTypeAdvanced handleDistributeSingleClientMessageTypeAdvanced() {
return new ClientHandleDistributeSingleClientMessageTypeAdvanced();
}
@Bean
public ClientHandleDistributeSingleClientRealCloseVisitorTypeAdvanced handleDistributeSingleClientRealCloseVisitorTypeAdvanced() {
return new ClientHandleDistributeSingleClientRealCloseVisitorTypeAdvanced();
}
@Bean
public ClientReportHandleChannelTransferTypeAdvancedHandleDistribute handleChannelTransferTypeAdvancedHandleDistribute(NettyClientProperties nettyClientProperties) {
return new ClientReportHandleChannelTransferTypeAdvancedHandleDistribute(nettyClientProperties);
}
@Bean
public HandleDistributeConnectSuccessNotificationTypeAdvancedHandle handleDistributeConnectSuccessNotificationTypeAdvancedHandle(ClientChangeEvent clientChangeEvent) {
return new HandleDistributeConnectSuccessNotificationTypeAdvancedHandle(clientChangeEvent);
}
@Bean
public HandleClientChannelActiveAdvanced handleClientChannelActiveAdvanced(NettyClientProperties nettyClientProperties) {
return new HandleClientChannelActiveAdvanced(nettyClientProperties);
}
@Bean
public HandleDistributeDisconnectTypeAdvancedHandle handleDistributeDisconnectTypeAdvancedHandle(ClientChangeEvent clientChangeEvent) {
return new HandleDistributeDisconnectTypeAdvancedHandle(clientChangeEvent);
}
@Bean
public HandleDistributeStagingClosedTypeAdvanced handleDistributeStagingClosedTypeAdvanced() {
return new HandleDistributeStagingClosedTypeAdvanced();
}
@Bean
public HandleDistributeStagingOpenedTypeAdvanced handleDistributeStagingOpenedTypeAdvanced() {
return new HandleDistributeStagingOpenedTypeAdvanced();
}
@Bean
public ClientHandleDistributeSingleClientRealConnectTypeAdvanced clientHandleDistributeSingleClientRealConnectTypeAdvanced(NettyClientProperties nettyClientProperties,
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
return new ClientHandleDistributeSingleClientRealConnectTypeAdvanced(nettyClientProperties, handleChannelTypeAdvancedList);
}
}

View File

@ -0,0 +1,87 @@
package org.framework.smart.agent.network.heartbeat.client.config;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.application.LazyNettyServerPropertiesApplication;
import org.framework.smart.agent.network.heartbeat.client.infrastructure.entity.LazyNettyServerPropertiesDO;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import java.util.Objects;
/**
* 初始化配置
*/
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Slf4j
@Configuration
public class InitConfig implements CommandLineRunner, DisposableBean {
private final NettyClientProperties nettyClientProperties;
private final LazyLambdaStream lazyLambdaStream;
private final LazyNettyServerPropertiesApplication lazyNettyServerPropertiesApplication;
public InitConfig(NettyClientProperties nettyClientProperties, LazyLambdaStream lazyLambdaStream, LazyNettyServerPropertiesApplication lazyNettyServerPropertiesApplication) {
this.nettyClientProperties = nettyClientProperties;
this.lazyLambdaStream = lazyLambdaStream;
this.lazyNettyServerPropertiesApplication = lazyNettyServerPropertiesApplication;
}
@Override
public void run(String... args) throws Exception {
try {
// 存储配置到db
initDb2Config();
// 启动客户端连接
lazyNettyServerPropertiesApplication.starterAllClientSocket();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 存储配置到db
*/
public void initDb2Config() {
String clientId = nettyClientProperties.getClientId();
String inetHost = nettyClientProperties.getInetHost();
int inetPort = nettyClientProperties.getInetPort();
if (Objects.isNull(clientId) ||
Objects.isNull(inetHost)) {
log.warn("配置信息为空,请通过页面添加配置信息:{}", nettyClientProperties);
return;
}
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = new LazyNettyServerPropertiesDO();
lazyNettyServerPropertiesDO.setClientId(clientId);
lazyNettyServerPropertiesDO.setInetHost(inetHost);
lazyNettyServerPropertiesDO.setInetPort(inetPort);
lazyNettyServerPropertiesDO.setType(PropertiesType.CONFIG);
lazyNettyServerPropertiesDO.setIsDeleted(false);
// 根据服务端端口、port 唯一性验证
boolean exists = lazyLambdaStream.exists(LazyWrappers.<LazyNettyServerPropertiesDO>lambdaWrapper()
.eq(LazyNettyServerPropertiesDO::getInetHost, inetHost)
.eq(LazyNettyServerPropertiesDO::getInetPort, inetPort)
.eq(LazyNettyServerPropertiesDO::getClientId, clientId)
);
if (!exists) {
lazyLambdaStream.insert(lazyNettyServerPropertiesDO);
}
}
/**
* 程序关闭后执行
*/
@Override
public void destroy() {
lazyNettyServerPropertiesApplication.destroyClientSocket();
}
}

View File

@ -0,0 +1,36 @@
package org.framework.smart.agent.network.heartbeat.client.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* netty 客户服务端地址配置属性
*/
@ConfigurationProperties(prefix = NettyClientProperties.PREFIX, ignoreUnknownFields = true)
@Configuration
@Data
public class NettyClientProperties {
public static final String PREFIX = "spring.lazy.netty.client";
/**
* 服务端地址 如127.0.0.1
*/
private String inetHost;
/**
* 服务端端口 如7001
*/
private int inetPort;
/**
* 服务端path
*/
private String inetPath = "lazy-cloud-heartbeat-server";
/**
* 客户端ID 如1024
*/
private String clientId;
/**
* 是否开启 默认是
*/
private boolean enabled = true;
}

View File

@ -0,0 +1,14 @@
package org.framework.smart.agent.network.heartbeat.client.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 属性类型
*/
@AllArgsConstructor
@Getter
public enum PropertiesType {
DB,
CONFIG
}

View File

@ -0,0 +1,141 @@
package org.framework.smart.agent.network.heartbeat.client.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.wu.framework.web.spring.EasyController;
import org.springframework.web.bind.annotation.*;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.annotation.Resource;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesRemoveCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesStoryCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesUpdateCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesQueryListCommand;
import org.framework.smart.agent.network.heartbeat.client.application.command.lazy.netty.server.properties.LazyNettyServerPropertiesQueryOneCommand;
import org.framework.smart.agent.network.heartbeat.client.application.LazyNettyServerPropertiesApplication;
import org.framework.smart.agent.network.heartbeat.client.application.dto.LazyNettyServerPropertiesDTO;
import java.util.List;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyController
**/
@Tag(name = "服务端配置信息提供者")
@EasyController("/v1/api/lazy/netty/server/properties")
public class LazyNettyServerPropertiesProvider {
@Resource
private LazyNettyServerPropertiesApplication lazyNettyServerPropertiesApplication;
/**
* describe 新增服务端配置信息
*
* @param lazyNettyServerPropertiesStoryCommand 新增服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息新增后领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "新增服务端配置信息")
@PostMapping("/story")
public Result<LazyNettyServerProperties> story(@RequestBody LazyNettyServerPropertiesStoryCommand lazyNettyServerPropertiesStoryCommand){
return lazyNettyServerPropertiesApplication.story(lazyNettyServerPropertiesStoryCommand);
}
/**
* describe 批量新增服务端配置信息
*
* @param lazyNettyServerPropertiesStoryCommandList 批量新增服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息新增后领域对象集合
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "批量新增服务端配置信息")
@PostMapping("/batchStory")
public Result<List<LazyNettyServerProperties>> batchStory(@RequestBody List<LazyNettyServerPropertiesStoryCommand> lazyNettyServerPropertiesStoryCommandList){
return lazyNettyServerPropertiesApplication.batchStory(lazyNettyServerPropertiesStoryCommandList);
}
/**
* describe 更新服务端配置信息
*
* @param lazyNettyServerPropertiesUpdateCommand 更新服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "更新服务端配置信息")
@PutMapping("/updateOne")
public Result<LazyNettyServerProperties> updateOne(@RequestBody LazyNettyServerPropertiesUpdateCommand lazyNettyServerPropertiesUpdateCommand){
return lazyNettyServerPropertiesApplication.updateOne(lazyNettyServerPropertiesUpdateCommand);
}
/**
* describe 查询单个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryOneCommand 查询单个服务端配置信息
* @return {@link Result<LazyNettyServerPropertiesDTO>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "查询单个服务端配置信息")
@GetMapping("/findOne")
public Result<LazyNettyServerPropertiesDTO> findOne(@ModelAttribute LazyNettyServerPropertiesQueryOneCommand lazyNettyServerPropertiesQueryOneCommand){
return lazyNettyServerPropertiesApplication.findOne(lazyNettyServerPropertiesQueryOneCommand);
}
/**
* describe 查询多个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryListCommand 查询多个服务端配置信息
* @return {@link Result<List<LazyNettyServerPropertiesDTO>>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "查询多个服务端配置信息")
@GetMapping("/findList")
public Result<List<LazyNettyServerPropertiesDTO>> findList(@ModelAttribute LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand){
return lazyNettyServerPropertiesApplication.findList(lazyNettyServerPropertiesQueryListCommand);
}
/**
* describe 分页查询多个服务端配置信息
*
* @param lazyNettyServerPropertiesQueryListCommand 分页查询多个服务端配置信息
* @return {@link Result<LazyPage<LazyNettyServerPropertiesDTO>>} 分页服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "分页查询多个服务端配置信息")
@GetMapping("/findPage")
public Result<LazyPage<LazyNettyServerPropertiesDTO>> findPage(@Parameter(description ="分页大小") @RequestParam(defaultValue = "10", value = "size") int size,
@Parameter(description ="当前页数") @RequestParam(defaultValue = "1", value = "current") int current,@ModelAttribute LazyNettyServerPropertiesQueryListCommand lazyNettyServerPropertiesQueryListCommand){
return lazyNettyServerPropertiesApplication.findPage(size,current,lazyNettyServerPropertiesQueryListCommand);
}
/**
* describe 删除服务端配置信息
*
* @param lazyNettyServerPropertiesRemoveCommand 删除服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Operation(summary = "删除服务端配置信息")
@DeleteMapping("/remove")
public Result<LazyNettyServerProperties> remove(@ModelAttribute LazyNettyServerPropertiesRemoveCommand lazyNettyServerPropertiesRemoveCommand){
return lazyNettyServerPropertiesApplication.remove(lazyNettyServerPropertiesRemoveCommand);
}
}

View File

@ -0,0 +1,76 @@
package org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties;
import lombok.Data;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyDomain
**/
@Data
@Accessors(chain = true)
@Schema(title = "lazy_netty_server_properties",description = "服务端配置信息")
public class LazyNettyServerProperties {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
private Integer inetPort;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,118 @@
package org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import java.util.List;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyDomainRepository
**/
public interface LazyNettyServerPropertiesRepository {
/**
* describe 新增服务端配置信息
*
* @param lazyNettyServerProperties 新增服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息新增后领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerProperties> story(LazyNettyServerProperties lazyNettyServerProperties);
/**
* describe 批量新增服务端配置信息
*
* @param lazyNettyServerPropertiesList 批量新增服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息新增后领域对象集合
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<List<LazyNettyServerProperties>> batchStory(List<LazyNettyServerProperties> lazyNettyServerPropertiesList);
/**
* describe 查询单个服务端配置信息
*
* @param lazyNettyServerProperties 查询单个服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerProperties> findOne(LazyNettyServerProperties lazyNettyServerProperties);
/**
* describe 查询多个服务端配置信息
*
* @param lazyNettyServerProperties 查询多个服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息DTO对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<List<LazyNettyServerProperties>> findList(LazyNettyServerProperties lazyNettyServerProperties);
/**
* describe 分页查询多个服务端配置信息
*
* @param size 当前页数
* @param current 当前页
* @param lazyNettyServerProperties 分页查询多个服务端配置信息
* @return {@link Result<LazyPage<LazyNettyServerProperties>>} 分页服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyPage<LazyNettyServerProperties>> findPage(int size,int current,LazyNettyServerProperties lazyNettyServerProperties);
/**
* describe 删除服务端配置信息
*
* @param lazyNettyServerProperties 删除服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<LazyNettyServerProperties> remove(LazyNettyServerProperties lazyNettyServerProperties);
/**
* describe 是否存在服务端配置信息
*
* @param lazyNettyServerProperties 是否存在服务端配置信息
* @return {@link Result<Boolean>} 服务端配置信息是否存在
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
Result<Boolean> exists(LazyNettyServerProperties lazyNettyServerProperties);
/**
* 客户端连接服务端状态在线
* @param lazyNettyServerProperties 数据
*/
void onLine(LazyNettyServerProperties lazyNettyServerProperties);
/**
* 推送客户端离线
* @param lazyNettyServerProperties 数据
*/
void offLine(LazyNettyServerProperties lazyNettyServerProperties);
}

View File

@ -0,0 +1,48 @@
package org.framework.smart.agent.network.heartbeat.client.infrastructure.converter;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.framework.smart.agent.network.heartbeat.client.infrastructure.entity.LazyNettyServerPropertiesDO;
import org.mapstruct.factory.Mappers;
import org.mapstruct.Mapper;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyInfrastructureConverter
**/
@Mapper
public interface LazyNettyServerPropertiesConverter {
/**
* describe MapStruct 创建的代理对象
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerPropertiesConverter INSTANCE = Mappers.getMapper(LazyNettyServerPropertiesConverter.class);
/**
* describe 实体对象 转换成领域对象
*
* @param lazyNettyServerPropertiesDO 服务端配置信息实体对象
* @return {@link LazyNettyServerProperties} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerProperties toLazyNettyServerProperties(LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO);
/**
* describe 领域对象 转换成实体对象
*
* @param lazyNettyServerProperties 服务端配置信息领域对象
* @return {@link LazyNettyServerPropertiesDO} 服务端配置信息实体对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
LazyNettyServerPropertiesDO fromLazyNettyServerProperties(LazyNettyServerProperties lazyNettyServerProperties);
}

View File

@ -0,0 +1,97 @@
package org.framework.smart.agent.network.heartbeat.client.infrastructure.entity;
import lombok.Data;
import lombok.experimental.Accessors;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import org.wu.framework.lazy.orm.core.stereotype.LazyTableIndex;
import org.wu.framework.core.stereotype.LayerField;
import org.wu.framework.core.stereotype.LayerField.LayerFieldType;
import org.wu.framework.lazy.orm.core.stereotype.LazyTable;
import org.wu.framework.lazy.orm.core.stereotype.LazyTableField;
import org.wu.framework.lazy.orm.core.stereotype.*;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.lang.String;
import java.time.LocalDateTime;
import java.lang.Integer;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyInfrastructureEntity
**/
@Data
@Accessors(chain = true)
@LazyTable(tableName = "lazy_netty_server_properties",comment = "服务端配置信息")
@Schema(title = "lazy_netty_server_properties",description = "服务端配置信息")
public class LazyNettyServerPropertiesDO {
/**
*
* 客户身份ID
*/
@Schema(description ="客户身份ID",name ="clientId",example = "")
@LazyTableFieldUnique(name="client_id",comment="客户身份ID",columnType="varchar(255)")
private String clientId;
/**
*
* 状态(on_line、off_line)
*/
@Schema(description ="状态(on_line、off_line)",name ="connectStatus",example = "")
@LazyTableField(name="connect_status",comment="状态(on_line、off_line)",columnType="varchar(255)")
private NettyClientStatus connectStatus;
/**
*
* 创建时间
*/
@Schema(description ="创建时间",name ="createTime",example = "")
@LazyTableField(name="create_time",comment="创建时间")
private LocalDateTime createTime;
/**
*
* 服务端host
*/
@Schema(description ="服务端host",name ="inetHost",example = "")
@LazyTableFieldUnique(name="inet_host",comment="服务端host",columnType="varchar(255)")
private String inetHost;
/**
*
* 服务端端口
*/
@Schema(description ="服务端端口",name ="inetPort",example = "")
@LazyTableFieldUnique(name="inet_port",comment="服务端端口",columnType="int")
private Integer inetPort;
/**
*
* 是否删除
*/
@Schema(description ="是否删除",name ="isDeleted",example = "")
@LazyTableField(name="is_deleted",comment="是否删除")
private Boolean isDeleted;
/**
*
* 类型配置、DB
*/
@Schema(description ="类型配置、DB",name ="type",example = "")
@LazyTableField(name="type",comment="类型配置、DB",columnType="varchar(255)")
private PropertiesType type;
/**
*
* 更新时间
*/
@Schema(description ="更新时间",name ="updateTime",example = "")
@LazyTableField(name="update_time",comment="更新时间")
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,15 @@
package org.framework.smart.agent.network.heartbeat.client.infrastructure.mapper;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyInfrastructureMapper
**/
public interface LazyNettyServerPropertiesMapper {
}

View File

@ -0,0 +1,213 @@
package org.framework.smart.agent.network.heartbeat.client.infrastructure.persistence;
import org.framework.smart.agent.network.heartbeat.client.config.PropertiesType;
import org.framework.smart.agent.network.heartbeat.client.infrastructure.entity.LazyNettyServerPropertiesDO;
import org.framework.smart.agent.network.heartbeat.client.infrastructure.converter.LazyNettyServerPropertiesConverter;
import org.framework.smart.agent.network.heartbeat.client.infrastructure.mapper.LazyNettyServerPropertiesMapper;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerPropertiesRepository;
import org.springframework.stereotype.Repository;
import java.util.stream.Collectors;
import org.wu.framework.lazy.orm.database.lambda.stream.wrapper.LazyWrappers;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
import jakarta.annotation.Resource;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.wu.framework.lazy.orm.database.lambda.stream.lambda.LazyLambdaStream;
import java.util.List;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
/**
* describe 服务端配置信息
*
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
* @see org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyInfrastructurePersistence
**/
@Repository
public class LazyNettyServerPropertiesRepositoryImpl implements LazyNettyServerPropertiesRepository {
@Resource
LazyLambdaStream lazyLambdaStream;
/**
* describe 新增服务端配置信息
*
* @param lazyNettyServerProperties 新增服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息新增后领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerProperties> story(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
String inetHost = lazyNettyServerPropertiesDO.getInetHost();
Integer inetPort = lazyNettyServerPropertiesDO.getInetPort();
String clientId = lazyNettyServerPropertiesDO.getClientId();
lazyNettyServerPropertiesDO.setIsDeleted(false);
// 查询 ip、端口、客户端
boolean exists = lazyLambdaStream.exists(LazyWrappers.<LazyNettyServerPropertiesDO>lambdaWrapper()
.eq(LazyNettyServerPropertiesDO::getInetHost, inetHost)
.eq(LazyNettyServerPropertiesDO::getInetPort, inetPort)
.eq(LazyNettyServerPropertiesDO::getClientId, clientId)
);
if (exists) {
// 更新
lazyLambdaStream.update(lazyNettyServerPropertiesDO, LazyWrappers.<LazyNettyServerPropertiesDO>lambdaWrapper()
.eq(LazyNettyServerPropertiesDO::getInetHost, inetHost)
.eq(LazyNettyServerPropertiesDO::getInetPort, inetPort)
.eq(LazyNettyServerPropertiesDO::getClientId, clientId)
);
} else {
lazyLambdaStream.insert(lazyNettyServerPropertiesDO);
}
return ResultFactory.successOf();
}
/**
* describe 批量新增服务端配置信息
*
* @param lazyNettyServerPropertiesList 批量新增服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息新增后领域对象集合
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<List<LazyNettyServerProperties>> batchStory(List<LazyNettyServerProperties> lazyNettyServerPropertiesList) {
List<LazyNettyServerPropertiesDO> lazyNettyServerPropertiesDOList = lazyNettyServerPropertiesList.stream().map(LazyNettyServerPropertiesConverter.INSTANCE::fromLazyNettyServerProperties).collect(Collectors.toList());
lazyLambdaStream.upsert(lazyNettyServerPropertiesDOList);
return ResultFactory.successOf();
}
/**
* describe 查询单个服务端配置信息
*
* @param lazyNettyServerProperties 查询单个服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerProperties> findOne(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
LazyNettyServerProperties lazyNettyServerPropertiesOne = lazyLambdaStream
.selectOne(
LazyWrappers.lambdaWrapperBean(lazyNettyServerPropertiesDO)
.eq(LazyNettyServerPropertiesDO::getIsDeleted,false)
, LazyNettyServerProperties.class);
return ResultFactory.successOf(lazyNettyServerPropertiesOne);
}
/**
* describe 查询多个服务端配置信息
*
* @param lazyNettyServerProperties 查询多个服务端配置信息
* @return {@link Result<List<LazyNettyServerProperties>>} 服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<List<LazyNettyServerProperties>> findList(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
List<LazyNettyServerProperties> lazyNettyServerPropertiesList = lazyLambdaStream.selectList(
LazyWrappers.lambdaWrapperBean(lazyNettyServerPropertiesDO)
.eq(LazyNettyServerPropertiesDO::getIsDeleted, false)
, LazyNettyServerProperties.class);
return ResultFactory.successOf(lazyNettyServerPropertiesList);
}
/**
* describe 分页查询多个服务端配置信息
*
* @param size 当前页数
* @param current 当前页
* @param lazyNettyServerProperties 分页查询多个服务端配置信息
* @return {@link Result<LazyPage<LazyNettyServerProperties>>} 分页服务端配置信息领域对象
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyPage<LazyNettyServerProperties>> findPage(int size, int current, LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
LazyPage<LazyNettyServerProperties> lazyPage = new LazyPage<>(current, size);
LazyPage<LazyNettyServerProperties> lazyNettyServerPropertiesLazyPage = lazyLambdaStream.selectPage(
LazyWrappers
.lambdaWrapperBean(lazyNettyServerPropertiesDO)
.eq(LazyNettyServerPropertiesDO::getIsDeleted, false)
, lazyPage, LazyNettyServerProperties.class);
return ResultFactory.successOf(lazyNettyServerPropertiesLazyPage);
}
/**
* describe 删除服务端配置信息
*
* @param lazyNettyServerProperties 删除服务端配置信息
* @return {@link Result<LazyNettyServerProperties>} 服务端配置信息
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<LazyNettyServerProperties> remove(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
lazyLambdaStream.delete(LazyWrappers.lambdaWrapperBean(lazyNettyServerPropertiesDO));
return ResultFactory.successOf();
}
/**
* describe 是否存在服务端配置信息
*
* @param lazyNettyServerProperties 服务端配置信息领域对象
* @return {@link Result<Boolean>} 是否存在 true 存在false 不存在
* @author Jia wei Wu
* @date 2024/04/03 03:00 下午
**/
@Override
public Result<Boolean> exists(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
Boolean exists = lazyLambdaStream.exists(LazyWrappers.lambdaWrapperBean(lazyNettyServerPropertiesDO));
return ResultFactory.successOf(exists);
}
/**
* 客户端连接服务端状态在线
*
* @param lazyNettyServerProperties 数据
*/
@Override
public void onLine(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
lazyLambdaStream.update(lazyNettyServerPropertiesDO,LazyWrappers.<LazyNettyServerPropertiesDO>lambdaWrapper()
.eq(LazyNettyServerPropertiesDO::getInetPort,lazyNettyServerPropertiesDO.getInetPort())
.eq(LazyNettyServerPropertiesDO::getInetHost,lazyNettyServerPropertiesDO.getInetHost())
.eq(LazyNettyServerPropertiesDO::getClientId,lazyNettyServerPropertiesDO.getClientId())
);
}
/**
* 推送客户端离线
*
* @param lazyNettyServerProperties 数据
*/
@Override
public void offLine(LazyNettyServerProperties lazyNettyServerProperties) {
LazyNettyServerPropertiesDO lazyNettyServerPropertiesDO = LazyNettyServerPropertiesConverter.INSTANCE.fromLazyNettyServerProperties(lazyNettyServerProperties);
lazyLambdaStream.update(lazyNettyServerPropertiesDO,LazyWrappers.<LazyNettyServerPropertiesDO>lambdaWrapper()
.eq(LazyNettyServerPropertiesDO::getInetPort,lazyNettyServerPropertiesDO.getInetPort())
.eq(LazyNettyServerPropertiesDO::getInetHost,lazyNettyServerPropertiesDO.getInetHost())
.eq(LazyNettyServerPropertiesDO::getClientId,lazyNettyServerPropertiesDO.getClientId())
);
}
}

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.framework.smart.agent.network.heartbeat.client.infrastructure.mapper.LazyNettyServerPropertiesMapper">
<resultMap id="BaseResultMap" type="org.framework.smart.agent.network.heartbeat.client.infrastructure.entity.LazyNettyServerPropertiesDO">
<id column="id" property="id" />
<result column="create_time" property="createTime" />
<result column="update_time" property="updateTime" />
<result column="is_deleted" property="isDeleted" />
<result column="inet_host" property="inetHost" />
<result column="inet_port" property="inetPort" />
<result column="client_id" property="clientId" />
<result column="type" property="type" />
<result column="connect_status" property="connectStatus" />
</resultMap>
</mapper>

View File

@ -0,0 +1,29 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.AbstractHandleChannelHeartbeatTypeAdvanced;
/**
* 服务端 处理客户端心跳
* TYPE_HEARTBEAT
*/
public class ClientHandleChannelHeartbeatTypeAdvanced extends AbstractHandleChannelHeartbeatTypeAdvanced<NettyProxyMsg> {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
public void doHandler(Channel channel, NettyProxyMsg msg) {
NettyProxyMsg hb = new NettyProxyMsg();
hb.setType(MessageType.TYPE_HEARTBEAT);
// channel.writeAndFlush(hb);
}
}

View File

@ -0,0 +1,26 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeSingleClientMessageTypeAdvanced;
/**
* 接收服务端发送过来的聊天信息
*/
@Slf4j
public class ClientHandleDistributeSingleClientMessageTypeAdvanced extends AbstractHandleDistributeSingleClientMessageTypeAdvanced<NettyProxyMsg> {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param nettyProxyMsg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg nettyProxyMsg) {
byte[] clientId = nettyProxyMsg.getClientId();
byte[] data = nettyProxyMsg.getData();
log.info("接收客户端:{},发送过来的聊天信息:{}", new String(clientId), new String(data));
}
}

View File

@ -0,0 +1,29 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.NettyRealIdContext;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced;
@Slf4j
public class ClientHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced extends AbstractHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced<NettyProxyMsg> {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param nettyProxyMsg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg nettyProxyMsg) {
// 获取访客ID
byte[] visitorId = nettyProxyMsg.getVisitorId();
// 获取访客对应的真实代理通道
Channel realChannel = NettyRealIdContext.getReal(visitorId);
if (realChannel != null) {
realChannel.config().setOption(ChannelOption.AUTO_READ, true);
}
}
}

View File

@ -0,0 +1,26 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.NettyCommunicationIdContext;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.NettyRealIdContext;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeSingleClientRealCloseVisitorTypeAdvanced;
@Slf4j
public class ClientHandleDistributeSingleClientRealCloseVisitorTypeAdvanced extends AbstractHandleDistributeSingleClientRealCloseVisitorTypeAdvanced<NettyProxyMsg> {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param nettyProxyMsg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg nettyProxyMsg) {
// 关闭代理的真实通道
byte[] visitorId = nettyProxyMsg.getVisitorId();
NettyRealIdContext.clear(visitorId);
NettyCommunicationIdContext.clear(visitorId);
}
}

View File

@ -0,0 +1,56 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties;
import org.framework.smart.agent.network.heartbeat.client.netty.socket.NettyClientRealSocket;
import org.framework.smart.agent.network.heartbeat.common.InternalNetworkPenetrationRealClient;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeSingleClientRealConnectTypeAdvanced;
import java.util.List;
/**
* 客户端创建真实代理同奥
*/
@Slf4j
public class ClientHandleDistributeSingleClientRealConnectTypeAdvanced extends AbstractHandleDistributeSingleClientRealConnectTypeAdvanced<NettyProxyMsg> {
private final NettyClientProperties nettyClientProperties;// 服务端地址信息
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList;
public ClientHandleDistributeSingleClientRealConnectTypeAdvanced(NettyClientProperties nettyClientProperties, List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.nettyClientProperties = nettyClientProperties;
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg msg) {
// 创建真实端口监听
byte[] clientIdBytes = msg.getClientId();
byte[] visitorPort = msg.getVisitorPort();
byte[] clientTargetIp = msg.getClientTargetIp();
byte[] clientTargetPort = msg.getClientTargetPort();
byte[] visitorIdBytes = msg.getVisitorId();
InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient =
InternalNetworkPenetrationRealClient
.builder()
.clientId(new String(clientIdBytes))
.visitorPort(Integer.valueOf(new String(visitorPort)))
.clientTargetIp(new String(clientTargetIp))
.clientTargetPort(Integer.valueOf(new String(clientTargetPort)))
.visitorId(new String(visitorIdBytes))
.build();
// 绑定真实服务端口
NettyClientRealSocket.buildRealServer(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList);
}
}

View File

@ -0,0 +1,58 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.NettyRealIdContext;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeChannelTransferTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.enums.MessageTypeEnums;
/**
* 服务端处理客户端数据传输
*
* @see MessageTypeEnums#DISTRIBUTE_CLIENT_TRANSFER
*/
@Slf4j
public class ClientReportHandleChannelTransferTypeAdvancedHandleDistribute extends AbstractHandleDistributeChannelTransferTypeAdvanced<NettyProxyMsg> {
private final NettyClientProperties nettyClientProperties;
public ClientReportHandleChannelTransferTypeAdvancedHandleDistribute(NettyClientProperties nettyClientProperties) {
this.nettyClientProperties = nettyClientProperties;
}
/**
* 处理当前数据
*
* @param channel 当前通道
* @param nettyProxyMsg 通道数据
*/
@Override
public void doHandler(Channel channel, NettyProxyMsg nettyProxyMsg) {
log.debug("接收到服务端需要内网穿透的数据:{}" , nettyProxyMsg);
String clientId = nettyClientProperties.getClientId();
byte[] visitorPort = nettyProxyMsg.getVisitorPort();
byte[] clientTargetIp = nettyProxyMsg.getClientTargetIp();
byte[] clientTargetPort = nettyProxyMsg.getClientTargetPort();
byte[] visitorId = nettyProxyMsg.getVisitorId();
// 真实服务通道
Channel realChannel = NettyRealIdContext.getReal(new String(visitorId));
if (realChannel == null) {
log.error("无法获取访客:{} 真实服务", new String(visitorId));
return;
}
// 把数据转到真实服务
ByteBuf buf = channel.config().getAllocator().buffer(nettyProxyMsg.getData().length);
buf.writeBytes(nettyProxyMsg.getData());
realChannel.writeAndFlush(buf);
}
}

View File

@ -0,0 +1,34 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties;
import org.framework.smart.agent.network.heartbeat.common.ChannelContext;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleClientChannelActiveAdvanced;
import org.framework.smart.agent.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
/**
* 客户端通道 is active
*/
public class HandleClientChannelActiveAdvanced extends AbstractHandleClientChannelActiveAdvanced<NettyProxyMsg> {
private final NettyClientProperties nettyClientProperties;
public HandleClientChannelActiveAdvanced(NettyClientProperties nettyClientProperties) {
this.nettyClientProperties = nettyClientProperties;
}
/**
* 处理当前数据
*
* @param channel 当前通道
* @param nettyProxyMsg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg nettyProxyMsg) {
// 缓存当前通道
byte[] clientIdByte = nettyProxyMsg.getClientId();
String clientId = new String(clientIdByte);
ChannelContext.push(channel, clientId);
ChannelAttributeKeyUtils.buildClientId(channel, clientId);
}
}

View File

@ -0,0 +1,44 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeConnectSuccessNotificationTypeAdvancedHandle;
import java.util.List;
/**
* 客户端连接成功通知
*/
@Slf4j
public class HandleDistributeConnectSuccessNotificationTypeAdvancedHandle extends AbstractHandleDistributeConnectSuccessNotificationTypeAdvancedHandle<NettyProxyMsg> {
private final ClientChangeEvent clientChangeEvent;
public HandleDistributeConnectSuccessNotificationTypeAdvancedHandle(ClientChangeEvent clientChangeEvent) {
this.clientChangeEvent = clientChangeEvent;
}
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg msg) {
// 客户端ID{},客户端:{}连接成功
log.warn("Client ID: {}, Client Data : {} Connection successful", new String(msg.getClientId()), new String(msg.getData()));
// 存储其他客户端状态
List<String> clientIdList = JSONObject.parseArray(new String(msg.getData()), String.class);
for (String tenantId : clientIdList) {
clientChangeEvent.clientOnLine(tenantId);
}
}
}

View File

@ -0,0 +1,43 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeDisconnectTypeAdvancedHandle;
/**
* 服务端处理客户端断开连接处理
* TYPE_DISCONNECT
*/
@Slf4j
public class HandleDistributeDisconnectTypeAdvancedHandle extends AbstractHandleDistributeDisconnectTypeAdvancedHandle<NettyProxyMsg> {
private final ClientChangeEvent clientChangeEvent;
public HandleDistributeDisconnectTypeAdvancedHandle(ClientChangeEvent clientChangeEvent) {
this.clientChangeEvent = clientChangeEvent;
}
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
public void doHandler(Channel channel, NettyProxyMsg msg) {
// 服务下线
byte[] data = msg.getData();
byte[] clientId = msg.getClientId();
String tenantId = new String(clientId);
//客户端:{}下线
log.warn("Client: {} Offline", tenantId);
clientChangeEvent.clientOffLine(tenantId);
}
}

View File

@ -0,0 +1,30 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeStagingClosedTypeAdvanced;
/**
* 服务端下发暂存关闭消息处理
*/
@Slf4j
public class HandleDistributeStagingClosedTypeAdvanced extends AbstractHandleDistributeStagingClosedTypeAdvanced<NettyProxyMsg> {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg msg) {
String clientId = new String(msg.getClientId());
log.info("客户端:{}离线暂存关闭", clientId);
// 修改redis 客户端暂存状态
// String stagingStatusKey = StagingConfigKeyConstant.getStagingStatusKey(clientId);
// stringRedisTemplate.opsForValue().set(stagingStatusKey, StagingStatus.CLOSED.name());
}
}

View File

@ -0,0 +1,34 @@
package org.framework.smart.agent.network.heartbeat.client.netty.advanced;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.AbstractHandleDistributeStagingOpenedTypeAdvanced;
/**
* 服务端下发暂存开启消息处理
*/
@Slf4j
public class HandleDistributeStagingOpenedTypeAdvanced extends AbstractHandleDistributeStagingOpenedTypeAdvanced<NettyProxyMsg> {
public HandleDistributeStagingOpenedTypeAdvanced() {
}
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
protected void doHandler(Channel channel, NettyProxyMsg msg) {
String clientId = new String(msg.getClientId());
//客户端:{}离线暂存开启
log.warn("Client: {} Offline temporary storage enabled", new String(msg.getClientId()));
// 修改redis 客户端暂存状态
// String stagingStatusKey = StagingConfigKeyConstant.getStagingStatusKey(clientId);
// stringRedisTemplate.opsForValue().set(stagingStatusKey, StagingStatus.OPENED.name());
}
}

View File

@ -0,0 +1,52 @@
package org.framework.smart.agent.network.heartbeat.client.netty.event;
/**
* 客户端状态变更事件
*/
public interface ClientChangeEvent {
/**
* 推送客户端在线
*/
void clientOnLine(String clientId);
/**
* 推送客户端在线
* @param clientId 客户端
* @param inetHost 服务端ip
* @param inetPort 服务端端口
* @param serverId 服务端ID
*/
void clientOnLine(String inetHost, int inetPort,String serverId ,String clientId);
/**
* 推送客户端离线
*/
void clientOffLine(String clientId);
/**
* 推送客户端离线
*
* @param inetHost 服务端ip
* @param inetPort 服务端端口
* @param serverId 服务端ID
* @param clientId 客户端
*/
void clientOffLine(String inetHost, int inetPort,String serverId,String clientId);
/**
* 暂存开启
*
* @param clientId 租户ID
*/
void stagingOpen(String clientId);
/**
* 暂存关闭
*
* @param clientId 客户端ID 对应的租户
*/
void stagingClose(String clientId);
}

View File

@ -0,0 +1,131 @@
package org.framework.smart.agent.network.heartbeat.client.netty.event;
import jakarta.annotation.Resource;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerProperties;
import org.framework.smart.agent.network.heartbeat.client.domain.model.lazy.netty.server.properties.LazyNettyServerPropertiesRepository;
import org.framework.smart.agent.network.heartbeat.common.enums.NettyClientStatus;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class DefaultClientChangeEventImpl implements ClientChangeEvent {
@Resource
private LazyNettyServerPropertiesRepository lazyNettyServerPropertiesRepository;
// private final StringRedisTemplate stringRedisTemplate;
// private final RedisProviderTemplate redisProviderTemplate;
//
// public DefaultClientChangeEvent1Impl(StringRedisTemplate stringRedisTemplate, RedisProviderTemplate redisProviderTemplate) {
// this.stringRedisTemplate = stringRedisTemplate;
// this.redisProviderTemplate = redisProviderTemplate;
// }
/**
* 推送客户端在线
*/
@Override
public void clientOnLine(String clientId) {
// // 获取当前客户端ID
// if (ObjectUtils.isEmpty(clientId)) {
// clientId = stringRedisTemplate.opsForValue().get(ClientConfigKeyUtils.CLIENT_ID_KEY);
// }
// String clientStatusKey = ClientConfigKeyUtils.getClientStatusKey(clientId);
// // 如果可以已经在线状态不推送
// stringRedisTemplate.opsForValue().set(clientStatusKey, NettyClientStatus.ON_LINE.name());
// ClientOnLineState clientOnLineState = new ClientOnLineState();
// clientOnLineState.setClientId(clientId);
// clientOnLineState.setOnLineState(NettyClientStatus.ON_LINE.name());
// // 暂存扫描触发
// redisProviderTemplate.send(RedisChannelConstant.REDIS_CLIENT_ONLINE_OR_OFFLINE_CHANNEL,clientOnLineState);
}
/**
* 推送客户端在线
*
* @param inetHost 服务端ip
* @param inetPort 服务端端口
* @param serverId 服务端ID
* @param clientId 客户端
*/
@Override
public void clientOnLine(String inetHost, int inetPort, String serverId, String clientId) {
LazyNettyServerProperties lazyNettyServerProperties = new LazyNettyServerProperties();
lazyNettyServerProperties.setClientId(clientId);
lazyNettyServerProperties.setInetHost(inetHost);
lazyNettyServerProperties.setInetPort(inetPort);
lazyNettyServerProperties.setConnectStatus(NettyClientStatus.ON_LINE);
lazyNettyServerPropertiesRepository.onLine(lazyNettyServerProperties);
// 更改状态未在线
clientOnLine(clientId);
}
/**
* 推送客户端离线
*/
@Override
public void clientOffLine(String clientId) {
// if (ObjectUtils.isEmpty(clientId)) {
// clientId = stringRedisTemplate.opsForValue().get(ClientConfigKeyUtils.CLIENT_ID_KEY);
// }
// String clientStatusKey = ClientConfigKeyUtils.getClientStatusKey(clientId);
// // 离线状态
// stringRedisTemplate.opsForValue().set(clientStatusKey, NettyClientStatus.OFF_LINE.name());
// // 暂存状态
// stagingOpen(clientId);
// // 暂存扫描触发
// ClientOnLineState clientOnLineState = new ClientOnLineState();
// clientOnLineState.setClientId(clientId);
// clientOnLineState.setOnLineState(NettyClientStatus.OFF_LINE.name());
// redisProviderTemplate.send(RedisChannelConstant.REDIS_CLIENT_ONLINE_OR_OFFLINE_CHANNEL,clientOnLineState);
}
/**
* 推送客户端离线
*
* @param inetHost 服务端ip
* @param inetPort 服务端端口
* @param serverId
* @param clientId 客户端
*/
@Override
public void clientOffLine(String inetHost, int inetPort, String serverId, String clientId) {
LazyNettyServerProperties lazyNettyServerProperties = new LazyNettyServerProperties();
lazyNettyServerProperties.setClientId(clientId);
lazyNettyServerProperties.setInetHost(inetHost);
lazyNettyServerProperties.setInetPort(inetPort);
lazyNettyServerProperties.setConnectStatus(NettyClientStatus.OFF_LINE);
lazyNettyServerPropertiesRepository.offLine(lazyNettyServerProperties);
clientOffLine(clientId);
}
@Override
public void stagingOpen(String clientId) {
// String stagingStatusKey = StagingConfigKeyConstant.getStagingStatusKey(clientId);
// stringRedisTemplate.opsForValue().set(stagingStatusKey, StagingStatus.OPENED.name());
}
/**
* 暂存关闭
*
* @param clientId 租户ID
*/
@Override
public void stagingClose(String clientId) {
// if (clientId == null) {
// clientId = stringRedisTemplate.opsForValue().get(ClientConfigKeyUtils.CLIENT_ID_KEY);
// }
// String stagingStatusKey = StagingConfigKeyConstant.getStagingStatusKey(clientId);
// stringRedisTemplate.opsForValue().set(stagingStatusKey, StagingStatus.CLOSED.name());
}
}

View File

@ -0,0 +1,45 @@
package org.framework.smart.agent.network.heartbeat.client.netty.filter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.framework.smart.agent.network.heartbeat.client.netty.handler.NettyClientHandler;
import org.framework.smart.agent.network.heartbeat.client.netty.socket.NettyClientSocket;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.smart.agent.network.heartbeat.common.decoder.NettyProxyMsgDecoder;
import org.framework.smart.agent.network.heartbeat.common.encoder.NettyProxyMsgEncoder;
public class NettyClientFilter extends ChannelInitializer<SocketChannel> {
private final ChannelTypeAdapter channelTypeAdapter;
private final NettyClientSocket nettyClientSocket;
public NettyClientFilter(ChannelTypeAdapter channelTypeAdapter, NettyClientSocket nettyClientSocket) {
this.channelTypeAdapter = channelTypeAdapter;
this.nettyClientSocket = nettyClientSocket;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
/* * 解码和编码,应和服务端一致 * */
// pipeline.addLast(new NettyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
// pipeline.addLast(new NettMsgEncoder());
// 解码、编码
pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
pipeline.addLast(new NettyProxyMsgEncoder());
// pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
//入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式
//因为服务端设置的超时时间是5秒所以设置4秒
pipeline.addLast(new IdleStateHandler(0, 4, 0));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("doHandler", new NettyClientHandler(channelTypeAdapter, nettyClientSocket)); //客户端的逻辑
}
}

View File

@ -0,0 +1,30 @@
package org.framework.smart.agent.network.heartbeat.client.netty.filter;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import org.framework.smart.agent.network.heartbeat.client.netty.handler.NettyClientRealHandler;
public class NettyClientRealFilter extends ChannelInitializer<SocketChannel> {
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default connectionClose
* the {@link Channel}.
*/
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new NettyClientRealHandler());
// // 解码、编码
// pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
// pipeline.addLast(new NettMsgEncoder());
// pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
// pipeline.addLast(new NettyProxyMsgEncoder());
}
}

View File

@ -0,0 +1,42 @@
package org.framework.smart.agent.network.heartbeat.client.netty.filter;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import org.framework.smart.agent.network.heartbeat.client.netty.handler.NettyClientVisitorRealHandler;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.smart.agent.network.heartbeat.common.decoder.NettyProxyMsgDecoder;
import org.framework.smart.agent.network.heartbeat.common.encoder.NettyProxyMsgEncoder;
/**
* netty 客户端连接真实服服务端访客拦截器
*/
public class NettyClientVisitorRealFilter extends ChannelInitializer<SocketChannel> {
private final ChannelTypeAdapter channelTypeAdapter;
public NettyClientVisitorRealFilter(ChannelTypeAdapter channelTypeAdapter) {
this.channelTypeAdapter = channelTypeAdapter;
}
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
*
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default connectionClose
* the {@link Channel}.
*/
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// // 解码、编码
// pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
// pipeline.addLast(new NettMsgEncoder());
pipeline.addLast(new NettyProxyMsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
pipeline.addLast(new NettyProxyMsgEncoder());
pipeline.addLast(new NettyClientVisitorRealHandler(channelTypeAdapter));
}
}

View File

@ -0,0 +1,24 @@
package org.framework.smart.agent.network.heartbeat.client.netty.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.util.Date;
public class HeartBeatClientHandler extends ChannelInboundHandlerAdapter {
private final int lossConnectCount = 0;
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
System.out.println("客户端循环心跳监测发送: " + new Date());
if (evt instanceof IdleStateEvent event) {
if (event.state() == IdleState.WRITER_IDLE) {
ctx.writeAndFlush("biubiu");
}
}
}
}

View File

@ -0,0 +1,121 @@
package org.framework.smart.agent.network.heartbeat.client.netty.handler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.netty.socket.NettyClientSocket;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* description
*
* @author 吴佳伟
* @date 2023/09/13 10:29
*/
@Slf4j
public class NettyClientHandler extends SimpleChannelInboundHandler<NettyProxyMsg> {
private final ChannelTypeAdapter channelTypeAdapter;
private final NettyClientSocket nettyClientSocket;
public NettyClientHandler(ChannelTypeAdapter channelTypeAdapter, NettyClientSocket nettyClientSocket) {
this.channelTypeAdapter = channelTypeAdapter;
this.nettyClientSocket = nettyClientSocket;
}
/**
* @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
* belongs to
* @param msg the message to handle
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, NettyProxyMsg msg) {
// log.info("第" + count + "次" + ",客户端接受的消息:" + msg);
// log.info("第" + count + "次" + ",客户端接受的消息内容:" + new String(msg.getData()));
// count++;
// 接收服务端、或者是代理端的信息
Channel channel = ctx.channel();
// log.info("type:{},clientId:{},data:{}",msg.getMysqlType(),new String(msg.getClientId()),new String(msg.getData()));
channelTypeAdapter.handler(channel, msg);
}
/**
* 建立连接时
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 建立连接时
log.info("When establishing a connection{}" , new Date());
ctx.fireChannelActive();
String clientId = nettyClientSocket.getClientId();
// 处理客户端连接成功
Channel channel = ctx.channel();
NettyProxyMsg nettyMsg = new NettyProxyMsg();
nettyMsg.setType(MessageType.CLIENT_CHANNEL_ACTIVE);
nettyMsg.setClientId(clientId);
channelTypeAdapter.handler(channel, nettyMsg);
}
/**
* 关闭连接时
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//服务端主动关闭当前客户端连接时
log.info("When the server actively closes the current client connection{}" , new Date());
final EventLoop eventLoop = ctx.channel().eventLoop();
eventLoop.schedule(() -> {
try {
nettyClientSocket.newConnect2Server();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, 1L, TimeUnit.SECONDS);
super.channelInactive(ctx);
}
/**
* 心跳请求处理 * 每4秒发送一次心跳请求; *
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception {
if (obj instanceof IdleStateEvent event) {
if (IdleState.WRITER_IDLE.equals(event.state())) { //如果写通道处于空闲状态,就发送心跳命令
String clientId = nettyClientSocket.getClientId();
NettyProxyMsg nettyMsg = new NettyProxyMsg();
nettyMsg.setType(MessageType.TYPE_HEARTBEAT);
nettyMsg.setData(clientId.getBytes(StandardCharsets.UTF_8));
nettyMsg.setClientId(clientId.getBytes(StandardCharsets.UTF_8));
ctx.writeAndFlush(nettyMsg);// 发送心跳数据
} else if (event.state() == IdleState.WRITER_IDLE) { // 如果检测到写空闲状态,关闭连接
// 离线、暂存通知
String clientId = nettyClientSocket.getClientId();
Channel channel = ctx.channel();
NettyProxyMsg nettyMsg = new NettyProxyMsg();
nettyMsg.setType(MessageType.DISTRIBUTE_CLIENT_DISCONNECTION_NOTIFICATION);
nettyMsg.setClientId(clientId.getBytes(StandardCharsets.UTF_8));
channelTypeAdapter.handler(channel, nettyMsg);
ctx.close();
}
} else {
super.userEventTriggered(ctx, obj);
}
}
}

View File

@ -0,0 +1,85 @@
package org.framework.smart.agent.network.heartbeat.client.netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.NettyCommunicationIdContext;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
/**
* 来自客户端 真实服务器返回的数据请求
*/
@Slf4j
public class NettyClientRealHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
// 客户端发送真实数据到代理了
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
log.debug("接收客户端真实服务数据:{}", new String(bytes));
String visitorId = ChannelAttributeKeyUtils.getVisitorId(ctx.channel());
Integer visitorPort = ChannelAttributeKeyUtils.getVisitorPort(ctx.channel());
String clientId = ChannelAttributeKeyUtils.getClientId(ctx.channel());
// 访客通信通道 上报服务端代理完成
Channel visitorChannel = NettyCommunicationIdContext.getVisitor(visitorId);
NettyProxyMsg returnMessage = new NettyProxyMsg();
returnMessage.setType(MessageType.REPORT_CLIENT_TRANSFER);
returnMessage.setVisitorId(visitorId);
returnMessage.setClientId(clientId);
returnMessage.setVisitorPort(visitorPort);
returnMessage.setData(bytes);
visitorChannel.writeAndFlush(returnMessage);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String clientId = ChannelAttributeKeyUtils.getClientId(ctx.channel());
String visitorId = ChannelAttributeKeyUtils.getVisitorId(ctx.channel());
// 客户端真实通信通道
Channel visitor = NettyCommunicationIdContext.getVisitor(visitorId);
if (visitor != null) {
// 上报关闭这个客户端的访客通道
NettyProxyMsg closeVisitorMsg = new NettyProxyMsg();
closeVisitorMsg.setType(MessageType.REPORT_SINGLE_CLIENT_CLOSE_VISITOR);
closeVisitorMsg.setVisitorId(visitorId);
visitor.writeAndFlush(closeVisitorMsg);
}
super.channelInactive(ctx);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
// String vid = ctx.channel().attr(Constant.VID).get();
// if (StringUtil.isNullOrEmpty(vid)) {
// super.channelWritabilityChanged(ctx);
// return;
// }
// Channel proxyChannel = Constant.vpc.get(vid);
// if (proxyChannel != null) {
// proxyChannel.config().setOption(ChannelOption.AUTO_READ, ctx.channel().isWritable());
// }
super.channelWritabilityChanged(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,66 @@
package org.framework.smart.agent.network.heartbeat.client.netty.handler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.ChannelContext;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.smart.agent.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
/**
* 客户端访客通信通道 处理器
*/
@Slf4j
public class NettyClientVisitorRealHandler extends SimpleChannelInboundHandler<NettyProxyMsg> {
private final ChannelTypeAdapter channelTypeAdapter;
public NettyClientVisitorRealHandler(ChannelTypeAdapter channelTypeAdapter) {
this.channelTypeAdapter = channelTypeAdapter;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, NettyProxyMsg nettyProxyMsg) throws Exception {
Channel channel = ctx.channel();
channelTypeAdapter.handler(channel, nettyProxyMsg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String clientId = ChannelAttributeKeyUtils.getClientId(ctx.channel());
String visitorId = ChannelAttributeKeyUtils.getVisitorId(ctx.channel());
// 关闭访客
ChannelContext.ClientChannel clientChannel = ChannelContext.get(clientId);
if (clientChannel != null) {
Channel channel = clientChannel.getChannel();
// 上报关闭这个客户端的访客通道
NettyProxyMsg closeVisitorMsg = new NettyProxyMsg();
closeVisitorMsg.setType(MessageType.REPORT_SINGLE_CLIENT_CLOSE_VISITOR);
closeVisitorMsg.setVisitorId(visitorId);
channel.writeAndFlush(closeVisitorMsg);
}
super.channelInactive(ctx);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
super.channelWritabilityChanged(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}

View File

@ -0,0 +1,53 @@
//package org.framework.smart.agent.network.heartbeat.client.netty.listener;
//
//
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.stereotype.Component;
//import org.framework.smart.agent.network.heartbeat.client.netty.config.NettyClientProperties;
//import org.framework.smart.agent.network.heartbeat.client.rpc.StagingNoticeApiRpc;
//import org.framework.smart.agent.network.heartbeat.common.constant.RedisChannelConstant;
//
///**
// * 客户端暂存开启、关闭通知
// *
// * @see RedisChannelConstant#REDIS_CLIENT_STAGING_OPENED_OR_CLOSED_CHANNEL
// */
//@Slf4j
//@Component
//public class ClientStagingOpenedOrClosedListener {
//
// private final StagingNoticeApiRpc stagingNoticeApiRpc;
// private final NettyClientProperties nettyServerProperties;
//
//
// public ClientStagingOpenedOrClosedListener(StagingNoticeApiRpc stagingNoticeApiRpc, NettyClientProperties nettyServerProperties) {
// this.stagingNoticeApiRpc = stagingNoticeApiRpc;
// this.nettyServerProperties = nettyServerProperties;
//
// }
//
//// /**
//// * 使用redis监听注解监听数据
//// *
//// * @param consumerRecord 客户端暂存开启、关闭通知
//// */
//// @EasyRedisListener(topics = RedisChannelConstant.REDIS_CLIENT_STAGING_OPENED_OR_CLOSED_CHANNEL)
//// public void subscription(ConsumerRecord<String, ClientStagingRedisChannelBo> consumerRecord, Acknowledgment acknowledgment) {
//// ClientStagingRedisChannelBo payload = consumerRecord.payload();
//// String clientId = payload.getClientId();
//// // 如果客户端ID为空默认当前客户端
//// if (ObjectUtils.isEmpty(clientId)) {
//// clientId = nettyServerProperties.getClientId();
//// }
//// StagingStatus stagingStatus = payload.getStagingStatus();
//// log.info("客户端:【{}】暂存:【{}】通知", clientId, stagingStatus);
////
////
//// if (StagingStatus.OPENED.equals(stagingStatus)) {
//// stagingNoticeApiRpc.stagingOpened(clientId);
//// } else if (StagingStatus.CLOSED.equals(stagingStatus)) {
//// stagingNoticeApiRpc.stagingClosed(clientId);
//// }
//// acknowledgment.acknowledge();
//// }
//}

View File

@ -0,0 +1,183 @@
package org.framework.smart.agent.network.heartbeat.client.netty.socket;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties;
import org.framework.smart.agent.network.heartbeat.client.netty.filter.NettyClientRealFilter;
import org.framework.smart.agent.network.heartbeat.client.netty.filter.NettyClientVisitorRealFilter;
import org.framework.smart.agent.network.heartbeat.common.*;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 客户端连接真实服务
*/
@Slf4j
public class NettyClientRealSocket {
static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
/**
* 连接真实服务
*
* @param internalNetworkPenetrationRealClient 访客信息
* @param nettyClientProperties 服务端地址信息
*/
public static void buildRealServer(InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient,
NettyClientProperties nettyClientProperties,
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
buildNewRealServer(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList);
}
/**
* @param internalNetworkPenetrationRealClient 访客信息
* @param nettyClientProperties 服务端地址信息
*/
private static void buildNewRealServer(InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient,
NettyClientProperties nettyClientProperties,
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
try {
String clientId = internalNetworkPenetrationRealClient.getClientId();
String clientTargetIp = internalNetworkPenetrationRealClient.getClientTargetIp();
Integer clientTargetPort = internalNetworkPenetrationRealClient.getClientTargetPort();
Integer visitorPort = internalNetworkPenetrationRealClient.getVisitorPort();
String visitorId = internalNetworkPenetrationRealClient.getVisitorId();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new NettyClientRealFilter());
bootstrap.connect(clientTargetIp, clientTargetPort).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
// 客户端链接真实服务成功 设置自动读写false 等待访客连接成功后设置成true
Channel realChannel = future.channel();
realChannel.config().setOption(ChannelOption.AUTO_READ, false);
log.info("访客通过 客户端:【{}】,绑定本地服务,IP:{},端口:{} 新建通道成功", clientId, clientTargetIp, clientTargetPort);
// 客户端真实通道
NettyRealIdContext.pushReal(realChannel, visitorId);
// 绑定访客ID到当前真实通道属性
ChannelAttributeKeyUtils.buildVisitorId(realChannel, visitorId);
ChannelAttributeKeyUtils.buildClientId(realChannel, clientId);
ChannelAttributeKeyUtils.buildVisitorPort(realChannel, visitorPort);
// 通知服务端访客连接成功
// 新建一个通道处理
newVisitorConnect2Server(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList);
// 是否等 服务端相应访客通道已经可以自动读写
// realChannel.config().setOption(ChannelOption.AUTO_READ, true);
// 模拟发送
String byteData = "GET /swagger-ui/index.html HTTP/1.1\n" +
"Host: 127.0.0.1:19080\n" +
"Connection: keep-alive\n" +
"Cache-Control: max-age=0\n" +
"sec-ch-ua: \"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"\n" +
"sec-ch-ua-mobile: ?0\n" +
"sec-ch-ua-platform: \"macOS\"\n" +
"Upgrade-Insecure-Requests: 1\n" +
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\n" +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\n" +
"Sec-Fetch-Site: none\n" +
"Sec-Fetch-Mode: navigate\n" +
"Sec-Fetch-User: ?1\n" +
"Sec-Fetch-Dest: document\n" +
"Accept-Encoding: gzip, deflate, br\n" +
"Accept-Language: zh-CN,zh;q=0.9\n" +
"Cookie: XXL_JOB_LOGIN_IDENTITY=7b226964223a312c22757365726e616d65223a2261646d696e222c2270617373776f7264223a226531306164633339343962613539616262653536653035376632306638383365222c22726f6c65223a312c227065726d697373696f6e223a6e756c6c7d; Hm_lvt_173e771eef816c412396d2cb4fe2d632=1703040917\n";
// ChannelContext.ClientChannel clientChannel = ChannelContext.get(String.valueOf(visitorPort).getBytes(StandardCharsets.UTF_8));
// Channel channel = clientChannel.getChannel();
// channel.writeAndFlush(byteData.getBytes(StandardCharsets.UTF_8));
// future.channel().attr(Constant.VID).set(internalNetworkPenetrationRealClient);
// Constant.vrc.put(internalNetworkPenetrationRealClient, future.channel());
// ProxySocket.connectProxyServer(internalNetworkPenetrationRealClient);
} else {
log.error("客户:【{}】,无法连接当前网络内的目标IP【{}】,目标端口:【{}】", clientId, clientTargetIp, clientTargetPort);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建访客连接服务端
*
* @param internalNetworkPenetrationRealClient 内网穿透信息
* @param nettyClientProperties 服务端配置信息
* @param handleChannelTypeAdvancedList 处理器适配器
* @throws InterruptedException 异常
*/
protected static void newVisitorConnect2Server(InternalNetworkPenetrationRealClient internalNetworkPenetrationRealClient,
NettyClientProperties nettyClientProperties,
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new NettyClientVisitorRealFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList)))
;
String inetHost = nettyClientProperties.getInetHost();
int inetPort = nettyClientProperties.getInetPort();
// local client id
String clientId = nettyClientProperties.getClientId();
String visitorId = internalNetworkPenetrationRealClient.getVisitorId();
Integer visitorPort = internalNetworkPenetrationRealClient.getVisitorPort();
String clientTargetIp = internalNetworkPenetrationRealClient.getClientTargetIp();
Integer clientTargetPort = internalNetworkPenetrationRealClient.getClientTargetPort();
String visitorClientId = internalNetworkPenetrationRealClient.getClientId();
// 客户端新建访客通道 连接服务端IP:{},连接服务端端口:{}
log.info("Client creates a new visitor channel to connect to server IP: {}, connecting to server port: {}", inetHost, inetPort);
ChannelFuture future = bootstrap.connect(inetHost, inetPort);
// 使用的客户端ID:{}
log.info("Client ID used: {}" , visitorClientId);
future.addListener((ChannelFutureListener) futureListener -> {
Channel channel = futureListener.channel();
if (futureListener.isSuccess()) {
NettyProxyMsg myMsg = new NettyProxyMsg();
myMsg.setType(MessageType.REPORT_SINGLE_CLIENT_REAL_CONNECT);
myMsg.setClientId(visitorClientId);
myMsg.setVisitorPort(visitorPort);
myMsg.setClientTargetIp(clientTargetIp);
myMsg.setClientTargetPort(clientTargetPort);
myMsg.setVisitorId(visitorId);
channel.writeAndFlush(myMsg);
// 绑定客户端真实通信通道
NettyCommunicationIdContext.pushVisitor(channel, visitorId);
ChannelAttributeKeyUtils.buildVisitorId(channel, visitorId);
ChannelAttributeKeyUtils.buildClientId(channel, visitorClientId);
// 客户端真实通道自动读写打开
Channel visitor = NettyRealIdContext.getReal(visitorId);
visitor.config().setOption(ChannelOption.AUTO_READ, true);
} else {
log.info("每隔2s重连....");
// 离线
channel.eventLoop().schedule(() -> {
try {
newVisitorConnect2Server(internalNetworkPenetrationRealClient, nettyClientProperties, handleChannelTypeAdvancedList);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, TimeUnit.SECONDS);
}
});
}
}

View File

@ -0,0 +1,122 @@
package org.framework.smart.agent.network.heartbeat.client.netty.socket;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.client.netty.event.ClientChangeEvent;
import org.framework.smart.agent.network.heartbeat.client.netty.filter.NettyClientFilter;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.NettyServerContext;
import org.framework.smart.agent.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.utils.ChannelAttributeKeyUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 客户端连接服务端
*/
@Slf4j
public class NettyClientSocket {
private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
/**
* 服务端host
*/
private final String inetHost;
/**
* 服务端端口
*/
private final int inetPort;
/**
* 当前客户端id
*/
@Getter
private final String clientId;
/**
* 当前连接的服务端ID
*/
private final String serverId;
/**
* 客户端状态变更事件
*/
@Getter
private final ClientChangeEvent clientChangeEvent;
private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList; // 处理服务端发送过来的数据类型
public NettyClientSocket(String inetHost, int inetPort, String clientId, String serverId, ClientChangeEvent clientChangeEvent, List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.inetHost = inetHost;
this.inetPort = inetPort;
this.clientId = clientId;
this.serverId = serverId;
this.clientChangeEvent = clientChangeEvent;
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
public void newConnect2Server() throws InterruptedException {
newConnect2Server(inetHost, inetPort, clientId, serverId, clientChangeEvent);
}
protected void newConnect2Server(String inetHost, int inetPort, String clientId, String serverId, ClientChangeEvent clientChangeEvent) throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new NettyClientFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList), this))
;
log.info("use clientId:{} connect to server IP:{},server port :{}", clientId, inetHost, inetPort);
ChannelFuture future = bootstrap.connect(inetHost, inetPort);
// 客户端连接服务端的channel
Channel serviceChannel = future.channel();
future.addListener((ChannelFutureListener) futureListener -> {
if (futureListener.isSuccess()) {
log.info("clientId:{},connect to server IP:{},server port :{} isSuccess ", clientId, inetHost, inetPort);
// 告诉服务端这条连接是client的连接
NettyProxyMsg nettyMsg = new NettyProxyMsg();
nettyMsg.setType(MessageType.REPORT_CLIENT_CONNECT_SUCCESS);
nettyMsg.setClientId(clientId);
nettyMsg.setData((clientId).getBytes());
ChannelAttributeKeyUtils.buildClientId(serviceChannel, clientId);
serviceChannel.writeAndFlush(nettyMsg);
NettyServerContext.pushServerEndpointChannel(serverId, clientId, serviceChannel);
// 在线 客户端注册服务端成功
clientChangeEvent.clientOnLine(inetHost, inetPort,serverId, clientId);
} else {
log.warn("Reconnect every 2 seconds....");
// 离线
NettyServerContext.removeServerEndpointChannels(serverId, clientId);
clientChangeEvent.clientOffLine(inetHost, inetPort,serverId, clientId);
futureListener.channel().eventLoop().schedule(() -> {
try {
newConnect2Server(inetHost, inetPort, clientId, serverId, clientChangeEvent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, TimeUnit.SECONDS);
}
});
}
/**
* 关闭连接
*/
public void shutdown() {
if ((eventLoopGroup != null) && (!eventLoopGroup.isShutdown())) {
eventLoopGroup.shutdownGracefully();
}
}
}

View File

@ -0,0 +1,65 @@
//package org.framework.smart.agent.network.heartbeat.client.rpc;
//
//import org.wu.framework.database.lazy.web.plus.stereotype.LazyRpc;
//import org.wu.framework.web.response.Result;
//import org.wu.framework.web.response.ResultFactory;
//import io.netty.channel.Channel;
//import org.framework.smart.agent.network.heartbeat.common.ChannelContext;
//import org.framework.smart.agent.network.heartbeat.common.MessageType;
//import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
//
//import java.nio.charset.StandardCharsets;
//import java.util.List;
//
//@LazyRpc
//public class StagingNoticeApiRpc {
//
//
// /**
// * 前提当前客户端曾经有过离线、而后上线了,但是呢现在有暂存的数据,因此暂存是打开的,所以这个时候可以通知服务端让其告知其他客户户我暂存了
// * 通知自己暂存了
// *
// * @param clientId 租户ID
// * @return
// */
// public Result<Void> stagingOpened(String clientId) {
// List<ChannelContext.ClientChannel> clientChannels = ChannelContext.get();
// for (ChannelContext.ClientChannel clientChannel : clientChannels) {
// // 上报 当前通道暂存开启了
// Channel channel = clientChannel.getChannel();
//
// NettyProxyMsg nettyMsg = new NettyProxyMsg();
// nettyMsg.setType(MessageType.REPORT_CLIENT_STAGING_OPENED);
// nettyMsg.setData((clientId
// .getBytes(StandardCharsets.UTF_8)));
// nettyMsg.setClientId((clientId
// .getBytes(StandardCharsets.UTF_8)));
// channel.writeAndFlush(nettyMsg);
// }
//
// return ResultFactory.successOf();
// }
//
// /**
// * 暂存恢复
// *
// * @param clientId 租户ID
// * @return
// */
// public Result<Void> stagingClosed(String clientId) {
// List<ChannelContext.ClientChannel> clientChannels = ChannelContext.get();
// for (ChannelContext.ClientChannel clientChannel : clientChannels) {
// // 上报 当前通道暂存关闭了
// Channel channel = clientChannel.getChannel();
// NettyProxyMsg nettyMsg = new NettyProxyMsg();
// nettyMsg.setType(MessageType.REPORT_CLIENT_STAGING_CLOSED);
// nettyMsg.setData((clientId
// .getBytes(StandardCharsets.UTF_8)));
// nettyMsg.setClientId((clientId
// .getBytes(StandardCharsets.UTF_8)));
// channel.writeAndFlush(nettyMsg);
// }
//
// return ResultFactory.successOf();
// }
//}

View File

@ -0,0 +1,65 @@
package org.framework.smart.agent.network.heartbeat.client.ui;
import org.springframework.context.annotation.Configuration;
import org.wu.framework.web.ui.LazyUI;
/**
* netty 客户端本地UI
*/
@Configuration
public class NettyClientLocalLazyUI implements LazyUI {
public static final String UI_URL = "/netty-client-local-ui/**";
public static final String UI_URL_INDEX = "/netty-client-local-ui/index.html";
public static final String CLASSPATH = "classpath:/netty-client-local-ui/v1/";
/**
* 是否支持 default false
* <p>
* pathPatterns 格式 /acw-client-ui/**
* locations 格式 classpath:/acw-local-client/v1/
* </p>
*
* @return true、false
*/
@Override
public boolean support() {
return true;
}
/**
* @return UI 描述
*/
@Override
public String desc() {
return "Netty 本地客户端 UI";
}
/**
* @return UI 访问的path
* 例如:/acw-client-ui/**
*/
@Override
public String pathPatterns() {
return UI_URL;
}
/**
* 返回页面首页地址
*
* @return String
* 例如 /acw-client-ui/index.html
*/
@Override
public String index() {
return UI_URL_INDEX;
}
/**
* @return 文件资源
* 例如classpath:/acw-local-client/v1/
*/
@Override
public String locations() {
return CLASSPATH;
}
}

View File

@ -0,0 +1,6 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.framework.smart.agent.network.heartbeat.client.EnableHeartbeatClientAutoConfiguration,\
org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties,\
org.framework.smart.agent.network.heartbeat.client.config.InitConfig,\
org.framework.smart.agent.network.heartbeat.client.config.HeartbeatClientConfiguration

View File

@ -0,0 +1,4 @@
org.framework.smart.agent.network.heartbeat.client.EnableHeartbeatClientAutoConfiguration
org.framework.smart.agent.network.heartbeat.client.config.NettyClientProperties
org.framework.smart.agent.network.heartbeat.client.config.InitConfig
org.framework.smart.agent.network.heartbeat.client.config.HeartbeatClientConfiguration

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="./favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Java 使用 Lazy ORM 网络穿透客户端!</title>
<script type="module" crossorigin src="assets/index-BTaJShFE.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-C9GutiFy.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.wu2020</groupId>
<artifactId>wu-smart-agent-network</artifactId>
<version>1.2.6-JDK17-SNAPSHOT</version>
</parent>
<artifactId>wu-smart-agent-network-heartbeat-common</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>top.wu2020</groupId>
<artifactId>wu-framework-web</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,199 @@
package org.framework.smart.agent.network.heartbeat.common;
import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通道上下文
*/
@Slf4j
public class ChannelContext {
private final static ConcurrentHashMap<String/*clientId*/, ClientChannelImpl/*通道*/>
channelIdClientChannelDTOConcurrentHashMap = new ConcurrentHashMap<>();
/**
* 新增通道
*
* @param channel 通道
* @param clientId 客户端ID
*/
public static void push(Channel channel, String clientId) {
ChannelId channelId = channel.id();
ClientChannelImpl clientChannelImpl = new ClientChannelImpl();
clientChannelImpl.setChannelId(channelId);
clientChannelImpl.setChannel(channel);
clientChannelImpl.setClientId(clientId.getBytes(StandardCharsets.UTF_8));
// 如果客户端已经存在 移除
if (channelIdClientChannelDTOConcurrentHashMap.containsKey(clientId)) {
// clear(clientId);
}
channelIdClientChannelDTOConcurrentHashMap.put(clientId, clientChannelImpl);
}
/**
* 新增通道
*
* @param channel 通道
* @param clientId 客户端ID
*/
public static void push(Channel channel, byte[] clientId) {
ChannelId channelId = channel.id();
ClientChannelImpl clientChannelImpl = new ClientChannelImpl();
clientChannelImpl.setChannelId(channelId);
clientChannelImpl.setChannel(channel);
clientChannelImpl.setClientId(clientId);
channelIdClientChannelDTOConcurrentHashMap.put(new String(clientId), clientChannelImpl);
}
/**
* 获取所有通道
*
* @return 返回所有通道信息
*/
public static List<ClientChannel> get() {
return new ArrayList<>(channelIdClientChannelDTOConcurrentHashMap.values());
}
/**
* 根据通道ID获取通道信息
*
* @param clientId 客户端ID
* @return 通道信息
*/
public static ClientChannel get(byte[] clientId) {
if (channelIdClientChannelDTOConcurrentHashMap
.containsKey(new String(clientId))) {
return channelIdClientChannelDTOConcurrentHashMap
.get(new String(clientId));
} else {
// 无法通过客户端ID[{}]获取通道信息
log.error("Unable to obtain channel information through client ID [{}]",new String(clientId));
return null;
}
}
/**
* 根据通道ID获取通道信息
*
* @param clientId 客户端ID
* @return 通道信息
*/
public static ChannelContext.ClientChannel get(String clientId) {
return get(clientId.getBytes(StandardCharsets.UTF_8));
}
/**
* 关闭通道
*
* @param clientId 客户端ID
*/
public static void clear(String clientId) {
ClientChannel clientChannel = get(clientId);
if (clientChannel != null) {
remove(clientId);
Channel channel = clientChannel.getChannel();
if (channel != null && channel.isActive()) {
channel.close();
}
} else {
// log warm
// 无法通过客户ID:[{}]移除客户端
log.warn("Unable to remove client through customer ID: [{}]", clientId);
}
}
/**
* 通过客户端ID移除客户端通道
*
* @param clientId 客户端ID
*/
public static void remove(byte[] clientId) {
ClientChannel clientChannel = get(clientId);
if (clientChannel != null) {
channelIdClientChannelDTOConcurrentHashMap.remove(new String(clientId));
} else {
// log warm 无法通过客户ID:[{}]移除客户端
log.warn("Unable to remove client through customer ID: [{}]", new String(clientId));
}
}
/**
* 通过客户端ID移除客户端通道
*
* @param clientId 客户端ID
*/
public static void remove(String clientId) {
ClientChannel clientChannel = get(clientId);
if (clientChannel != null) {
channelIdClientChannelDTOConcurrentHashMap.remove(clientId);
} else {
// log warm 无法通过客户ID:[{}]移除客户端
log.warn("Unable to remove client through customer ID: [{}]", clientId);
}
}
/**
* 客户端通道信息
*/
public interface ClientChannel {
/**
* 客户端ID
*/
byte[] getClientId();
/**
* 通道ID
*/
ChannelId getChannelId();
/**
* 通道
*/
Channel getChannel();
}
}
/**
* 客户端通道信息
*/
@Data
class ClientChannelImpl implements ChannelContext.ClientChannel {
/**
* 客户端ID
*/
private byte[] clientId;
/**
* 通道ID
*/
private ChannelId channelId;
/**
* 通道
*/
private Channel channel;
@Override
public String toString() {
return "ClientChannelImpl{" +
"clientId=" + new String(clientId) +
", channelId=" + channelId.asLongText() +
'}';
}
}

View File

@ -0,0 +1,43 @@
package org.framework.smart.agent.network.heartbeat.common;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* describe 内网穿透映射 真实客户端
*
* @author Jia wei Wu
* @date 2023/12/29 05:21 下午
**/
@Builder
@Data
@Accessors(chain = true)
public class InternalNetworkPenetrationRealClient {
/**
* 客户端ID
*/
private String clientId;
/**
* 客户端目标地址
*/
private String clientTargetIp;
/**
* 客户端目标端口
*/
private Integer clientTargetPort;
/**
* 访问端口
*/
private Integer visitorPort;
/**
* 访客ID
*/
private String visitorId;
}

View File

@ -0,0 +1,182 @@
package org.framework.smart.agent.network.heartbeat.common;
import org.framework.smart.agent.network.heartbeat.common.advanced.AbstractHandleChannelHeartbeatTypeAdvanced;
import org.framework.smart.agent.network.heartbeat.common.advanced.client.*;
import org.framework.smart.agent.network.heartbeat.common.advanced.server.*;
import org.framework.smart.agent.network.heartbeat.common.enums.MessageTypeEnums;
/**
* @see MessageTypeEnums
* 数据取值范围 -128~ 127
* 当前约束范围 -100100
*/
public class MessageType {
/**
* 心跳
*
* @see MessageTypeEnums#TYPE_HEARTBEAT
* @see AbstractHandleChannelHeartbeatTypeAdvanced
*/
public static final byte TYPE_HEARTBEAT = 0X00;
/**
* 客户端上报连接成功
*
* @see MessageTypeEnums#REPORT_CLIENT_CONNECT_SUCCESS
* @see AbstractHandleClientConnectSuccessTypeAdvanced
*/
public static final byte REPORT_CLIENT_CONNECT_SUCCESS = 0X01;
/**
* 上报 客户端断开连接
*
* @see MessageTypeEnums#REPORT_CLIENT_DISCONNECTION
* @see AbstractHandleReportDisconnectTypeAdvanced
*/
public static final byte REPORT_CLIENT_DISCONNECTION = 0X02;
/**
* 客户端上报暂存开启
*
* @see MessageTypeEnums#REPORT_CLIENT_STAGING_OPENED
* @see AbstractHandleReportStagingOpenedTypeAdvanced
*/
public static final byte REPORT_CLIENT_STAGING_OPENED = 0X03;
/**
* 客户端上报暂存关闭
*
* @see MessageTypeEnums#REPORT_CLIENT_STAGING_CLOSED
* @see AbstractHandleReportStagingClosedTypeAdvanced
*/
public static final byte REPORT_CLIENT_STAGING_CLOSED = 0X04;
/**
* 上报 客户端数据传输(内网穿透数据回传)
*
* @see MessageTypeEnums#REPORT_CLIENT_TRANSFER
* @see AbstractHandleReportHandleChannelTransferTypeAdvanced
*/
public static final byte REPORT_CLIENT_TRANSFER = 0X05;
/**
* 上报 客户端创建需要代理的真实端口成功
*
* @see MessageTypeEnums#REPORT_SINGLE_CLIENT_REAL_CONNECT
* @see AbstractHandleReportSingleClientRealConnectTypeAdvanced
*/
public static final byte REPORT_SINGLE_CLIENT_REAL_CONNECT = 0X06;
/**
* 上报 客户端关闭一个访客通道
*
* @see MessageTypeEnums#REPORT_SINGLE_CLIENT_CLOSE_VISITOR
* @see AbstractHandleReportSingleClientCloseVisitorTypeAdvanced
*/
public static final byte REPORT_SINGLE_CLIENT_CLOSE_VISITOR = 0X08;
/**
* 上报 客户端消息到另一个客户端
*
* @see MessageTypeEnums#REPORT_SINGLE_CLIENT_MESSAGE
* @see AbstractHandleReportSingleClientMessage2OtherClientTypeAdvanced
*/
public static final byte REPORT_SINGLE_CLIENT_MESSAGE = 0X09;
/**
* 服务端通道 is active
*
* @see MessageTypeEnums#SERVER_CHANNEL_ACTIVE
* @see AbstractHandleServerChannelActiveTypeAdvanced
*/
public static final byte SERVER_CHANNEL_ACTIVE = 0X10;
/**
* 上报 集群注册
*
* @see MessageTypeEnums#REPORT_CLUSTER_NODE_REGISTER_MESSAGE
* @see AbstractHandleReportClusterNodeRegisterTypeAdvanced
*/
public static final byte REPORT_CLUSTER_NODE_REGISTER_MESSAGE = 0X11;
/**
* 下发 客户端接收连接成功通知
*
* @see MessageTypeEnums#DISTRIBUTE_CLIENT_CONNECTION_SUCCESS_NOTIFICATION
* @see AbstractHandleDistributeConnectSuccessNotificationTypeAdvancedHandle
*/
public static final byte DISTRIBUTE_CLIENT_CONNECTION_SUCCESS_NOTIFICATION = -0X01;
/**
* 下发 客户端断开连接通知
*
* @see MessageTypeEnums#DISTRIBUTE_CLIENT_DISCONNECTION_NOTIFICATION
* @see AbstractHandleDistributeDisconnectTypeAdvancedHandle
*/
public static final byte DISTRIBUTE_CLIENT_DISCONNECTION_NOTIFICATION = -0X02;
/**
* 下发 客户端暂存开启通知
*
* @see MessageTypeEnums#DISTRIBUTE_CLIENT_STAGING_OPENED_NOTIFICATION
* @see AbstractHandleDistributeStagingOpenedTypeAdvanced
*/
public static final byte DISTRIBUTE_CLIENT_STAGING_OPENED_NOTIFICATION = -0X03;
/**
* 下发 客户端暂存关闭通知
*
* @see MessageTypeEnums#DISTRIBUTE_CLIENT_STAGING_CLOSED_NOTIFICATION
* @see AbstractHandleDistributeStagingClosedTypeAdvanced
*/
public static final byte DISTRIBUTE_CLIENT_STAGING_CLOSED_NOTIFICATION = -0X04;
/**
* 下发 客户端数据传输(内网穿透数据发送)
*
* @see MessageTypeEnums#DISTRIBUTE_CLIENT_TRANSFER
* @see AbstractHandleDistributeChannelTransferTypeAdvanced
*/
public static final byte DISTRIBUTE_CLIENT_TRANSFER = -0X05;
/**
* 下发 客户端创建需要代理的真实端口
*
* @see MessageTypeEnums#DISTRIBUTE_SINGLE_CLIENT_REAL_CONNECT
* @see AbstractHandleDistributeSingleClientRealConnectTypeAdvanced
*/
public static final byte DISTRIBUTE_SINGLE_CLIENT_REAL_CONNECT = -0X06;
/**
* 下发 客户端代理的真实端口自动读写
*
* @see MessageTypeEnums#DISTRIBUTE_SINGLE_CLIENT_REAL_CONNECT_AUTO_READ
* @see AbstractHandleDistributeSingleClientRealAutoReadConnectTypeAdvanced
*/
public static final byte DISTRIBUTE_SINGLE_CLIENT_REAL_CONNECT_AUTO_READ = -0X07;
/**
* 下发 客户端关闭代理服务通道
*
* @see MessageTypeEnums#DISTRIBUTE_SINGLE_CLIENT_REAL_CLOSE_VISITOR
* @see AbstractHandleDistributeSingleClientRealCloseVisitorTypeAdvanced
*/
public static final byte DISTRIBUTE_SINGLE_CLIENT_REAL_CLOSE_VISITOR = -0X08;
/**
* 下发 客户端消息
*
* @see MessageTypeEnums#DISTRIBUTE_SINGLE_CLIENT_MESSAGE
* @see AbstractHandleDistributeSingleClientMessageTypeAdvanced
*/
public static final byte DISTRIBUTE_SINGLE_CLIENT_MESSAGE = -0X09;
/**
* 客户端通道 is active
*
* @see MessageTypeEnums#CLIENT_CHANNEL_ACTIVE
* @see AbstractHandleClientChannelActiveAdvanced
*/
public static final byte CLIENT_CHANNEL_ACTIVE = -0X10;
/**
* 下发 集群注册
*
* @see MessageTypeEnums#DISTRIBUTE_CLUSTER_NODE_REGISTER_MESSAGE
* @see AbstractHandleDistributeClusterNodeRegisterTypeAdvanced
*/
public static final byte DISTRIBUTE_CLUSTER_NODE_REGISTER_MESSAGE = -0X11;
}

View File

@ -0,0 +1,27 @@
package org.framework.smart.agent.network.heartbeat.common;
import io.netty.channel.Channel;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 客户端连接服务端 通道
*/
@AllArgsConstructor
@Data
public class NettyClientChannel {
/**
* 客户端ID
*/
private String clientId;
/**
* 客户端通道
*/
private Channel channel;
/**
* 服务端ID
*/
private String serverId;
}

View File

@ -0,0 +1,73 @@
package org.framework.smart.agent.network.heartbeat.common;
import org.wu.framework.core.utils.ObjectUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 访客端口对应访客上下文
*/
public class NettyClientVisitorContext {
protected static final ConcurrentHashMap<String/*clientId*/, List<Object>/*NettyVisitorSocket*/> VISITOR_SOCKET = new ConcurrentHashMap<>();
/**
* 添加访客
*
* @param clientId 客户端ID
* @param visitorSocket 客户端访客socket
*/
public static <T> void pushVisitorSocket(String clientId, T visitorSocket) {
List<Object> visitors = getVisitorSockets(clientId);
visitors.add(visitorSocket);
VISITOR_SOCKET.put(clientId, visitors);
}
/**
* 通过客户端ID获取客户端使用的访客socket
*
* @param <T> 访客范型
* @param clientId 客户端ID
* @return 访客
*/
public static <T> List<T> getVisitorSockets(String clientId) {
return (List<T>) VISITOR_SOCKET.getOrDefault(clientId, new ArrayList<>());
}
/**
* 移除 客户端 访客信息
*
* @param clientId 客户端ID
*/
public static void removeVisitorSockets(String clientId) {
VISITOR_SOCKET.remove(clientId);
}
/**
* 移除 客户端 访客信息
*
* @param clientId 客户端ID
* @param visitorSocket 访客socket
*/
public static <T> void removeVisitorSocket(String clientId, T visitorSocket) {
List<Object> visitorSocketList = VISITOR_SOCKET.get(clientId);
if(!ObjectUtils.isEmpty(visitorSocketList)){
visitorSocketList.remove(visitorSocket);
VISITOR_SOCKET.put(clientId,visitorSocketList);
}
}
/**
* 关闭客户端访客socket
*
* @param clientId 客户端ID
*/
public static void close(String clientId) {
// getVisitorSockets(clientId)
}
}

View File

@ -0,0 +1,71 @@
package org.framework.smart.agent.network.heartbeat.common;
import io.netty.channel.Channel;
import java.util.concurrent.ConcurrentHashMap;
/**
* 通信通道对应上下文
*/
public class NettyCommunicationIdContext {
protected static final ConcurrentHashMap<String, Object> COMMUNICATION = new ConcurrentHashMap<>();
/**
* 添加访客
*
* @param visitorId 访客id
* @param visitor 访客
*/
public static <T> void pushVisitor(T visitor, String visitorId) {
COMMUNICATION.put(visitorId, visitor);
}
/**
* 通过访客端口获取访客
*
* @param visitorId 访客id
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getVisitor(String visitorId) {
return (T) COMMUNICATION.get(visitorId);
}
/**
* 通过访客端口获取访客
*
* @param visitorId 访客id
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getVisitor(byte[] visitorId) {
return getVisitor(new String(visitorId));
}
/**
* 移除访客
*
* @param visitorId 访客ID
*/
public static void clear(String visitorId) {
Channel visitor = getVisitor(visitorId);
if (visitor != null) {
COMMUNICATION.remove(visitorId);
visitor.close();
}
}
/**
* 移除访客
*
* @param visitorId 访客ID
*/
public static void clear(byte[] visitorId) {
clear(new String(visitorId));
}
}

View File

@ -0,0 +1,39 @@
package org.framework.smart.agent.network.heartbeat.common;
import lombok.Getter;
import lombok.Setter;
import java.util.Arrays;
@Setter
@Getter
public class NettyMsg {
// body 长度 type 1 clientId 4 data 4
public static final int bodyLength = 9;
/**
* 数据类型
*
* @see MessageType
* byte长度 1
*/
private byte type;
/**
* 客户端ID
* byte[] 长度 4
*/
private byte[] clientId;
/**
* 消息传输数据
* byte[] 长度 4
*/
private byte[] data;
@Override
public String toString() {
return "NettyProxyMsg [type=" + type + ", data=" + Arrays.toString(data) + ",clientId=" + Arrays.toString(clientId) + "]";
}
}

View File

@ -0,0 +1,113 @@
package org.framework.smart.agent.network.heartbeat.common;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.nio.charset.StandardCharsets;
/**
* netty 代理请求数据
*/
@NoArgsConstructor
@Setter
@Getter
public class NettyProxyMsg {
// body 长度 type 1 clientId 4 clientTargetIp 4 clientTargetPort 4 visitorPort 4 visitorId 4 data 4
public static final int bodyLength = 1 + 4 + 4 + 4 + 4 + 4 + 4;
/**
* 数据类型
*
* @see MessageType
* byte长度 1
*/
private byte type;
/**
* 客户端ID
* byte[] 长度 4
*/
private byte[] clientId;
/**
* 客户端目标地址
* byte[] 长度 4
*/
private byte[] clientTargetIp;
/**
* 客户端目标端口
* byte[] 长度 4
*/
private byte[] clientTargetPort;
/**
* 客户端目使用的代理端口
* byte[] 长度 4
*/
private byte[] visitorPort;
/**
* 访客ID
* byte[] 长度 4
*/
private byte[] visitorId;
/**
* 消息传输数据
* byte[] 长度 4
*/
private byte[] data;
@Override
public String toString() {
return "NettyProxyMsg [type=" + type +
",clientId=" + (clientId == null ? null : new String(clientId)) +
",clientTargetIp=" + (clientTargetIp == null ? null : new String(clientTargetIp)) +
",clientTargetPort=" + (clientTargetPort == null ? null : new String(clientTargetPort)) +
",visitorPort=" + (visitorPort == null ? null : new String(visitorPort)) +
"]";
}
public void setClientId(byte[] clientId) {
this.clientId = clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId.getBytes(StandardCharsets.UTF_8);
}
public void setClientTargetIp(byte[] clientTargetIp) {
this.clientTargetIp = clientTargetIp;
}
public void setClientTargetIp(String clientTargetIp) {
this.clientTargetIp = clientTargetIp.getBytes(StandardCharsets.UTF_8);
}
public void setClientTargetPort(Integer clientTargetPort) {
this.clientTargetPort = String.valueOf(clientTargetPort).getBytes(StandardCharsets.UTF_8);
}
public void setClientTargetPort(byte[] clientTargetPort) {
this.clientTargetPort = clientTargetPort;
}
public void setVisitorPort(byte[] visitorPort) {
this.visitorPort = visitorPort;
}
public void setVisitorPort(Integer visitorPort) {
this.visitorPort = String.valueOf(visitorPort).getBytes(StandardCharsets.UTF_8);
}
public void setVisitorId(String visitorId) {
this.visitorId = visitorId.getBytes(StandardCharsets.UTF_8);
}
public void setVisitorId(byte[] visitorId) {
this.visitorId = visitorId;
}
}

View File

@ -0,0 +1,71 @@
package org.framework.smart.agent.network.heartbeat.common;
import io.netty.channel.Channel;
import java.util.concurrent.ConcurrentHashMap;
/**
* 真实通道对应上下文 客户端、服务端真实代理通道
*/
public class NettyRealIdContext {
protected static final ConcurrentHashMap<String, Object> REAL = new ConcurrentHashMap<>();
/**
* 添加真实通道
*
* @param visitorId 访客id
* @param visitor 访客真实通道
*/
public static <T> void pushReal(T visitor, String visitorId) {
REAL.put(visitorId, visitor);
}
/**
* 通过访客端口获取访客
*
* @param visitorId 访客id
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getReal(String visitorId) {
return (T) REAL.get(visitorId);
}
/**
* 通过访客端口获取访客
*
* @param visitorId 访客id
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getReal(byte[] visitorId) {
return getReal(new String(visitorId));
}
/**
* 移除访客
*
* @param visitorId 访客ID
*/
public static void clear(String visitorId) {
Channel visitor = getReal(visitorId);
if (visitor != null) {
REAL.remove(visitorId);
visitor.close();
}
}
/**
* 移除访客
*
* @param visitorId 访客ID
*/
public static void clear(byte[] visitorId) {
clear(new String(visitorId));
}
}

View File

@ -0,0 +1,109 @@
package org.framework.smart.agent.network.heartbeat.common;
import io.netty.channel.Channel;
import org.wu.framework.core.utils.ObjectUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 服务端存储 channel 上下文
*/
public class NettyServerContext {
protected static final ConcurrentHashMap<String/*serverId*/, List<NettyClientChannel>/*NettyClientChannel*/>
NETTY_CLIENT_CHANNEL_SOCKET = new ConcurrentHashMap<>();
/**
* 添加访客
*
* @param serverId 服务端ID
* @param clientId 客户端ID
* @param channel channel
*/
public static <T> void pushServerEndpointChannel(String serverId, String clientId, Channel channel) {
List<NettyClientChannel> nettyClientChannelList = getServerEndpointChannels(serverId);
// 关闭旧的通道
nettyClientChannelList.stream().filter(nettyClientChannel -> nettyClientChannel.getClientId().equals(clientId) && nettyClientChannel.getServerId().equals(serverId)).forEach(nettyClientChannel -> {
Channel oldChannel = nettyClientChannel.getChannel();
if (oldChannel != null && oldChannel.isActive()) {
oldChannel.close();
}
});
List<NettyClientChannel> activeNettyClientChannelList = nettyClientChannelList
.stream()
.filter(nettyClientChannel ->
!nettyClientChannel.getClientId().equals(clientId) && !nettyClientChannel.getServerId().equals(serverId))
.collect(Collectors.toList());
NettyClientChannel nettyClientChannel = new NettyClientChannel(clientId, channel, serverId);
activeNettyClientChannelList.add(nettyClientChannel);
NETTY_CLIENT_CHANNEL_SOCKET.put(serverId, activeNettyClientChannelList);
}
/**
* 通过客户端ID获取客户端使用的访客socket
*
* @param serverId 服务端ID
* @return 客户端通道
*/
public static List<NettyClientChannel> getServerEndpointChannels(String serverId) {
return NETTY_CLIENT_CHANNEL_SOCKET.getOrDefault(serverId, new ArrayList<>());
}
/**
* 通过客户端ID获取客户端使用的访客socket
* @return 客户端通道
*/
public static List<NettyClientChannel> getServerEndpointChannels() {
return NETTY_CLIENT_CHANNEL_SOCKET
.values()
.stream()
.collect(Collectors.flatMapping(Collection::stream,Collectors.toList()));
}
/**
* 移除 客户端通道
*
* @param serverId 服务端ID
*/
public static void removeServerEndpointChannels(String serverId) {
for (NettyClientChannel nettyClientChannel : getServerEndpointChannels(serverId)) {
if (nettyClientChannel.getChannel() != null && nettyClientChannel.getChannel().isActive()) {
nettyClientChannel.getChannel().close();
}
}
NETTY_CLIENT_CHANNEL_SOCKET.remove(serverId);
}
/**
* 移除 客户端通道
*
* @param serverId 服务端ID
* @param clientId 客户端ID
*/
public static <T> void removeServerEndpointChannels(String serverId, String clientId) {
List<NettyClientChannel> nettyClientChannelList = NETTY_CLIENT_CHANNEL_SOCKET.get(serverId);
if (!ObjectUtils.isEmpty(nettyClientChannelList)) {
// 关闭指定服务端对应客户端通道
nettyClientChannelList.stream().filter(nettyClientChannel -> nettyClientChannel.getClientId().equals(clientId))
.forEach(nettyClientChannel -> {
if (nettyClientChannel.getChannel() != null && nettyClientChannel.getChannel().isActive()) {
nettyClientChannel.getChannel().close();
}
});
// 过滤后数据
List<NettyClientChannel> clientChannelList = nettyClientChannelList.stream().filter(nettyClientChannel -> !nettyClientChannel.getClientId().equals(clientId))
.collect(Collectors.toList());
NETTY_CLIENT_CHANNEL_SOCKET.put(serverId, clientChannelList);
}
}
}

View File

@ -0,0 +1,72 @@
package org.framework.smart.agent.network.heartbeat.common;
import io.netty.channel.Channel;
import java.util.concurrent.ConcurrentHashMap;
/**
* 访客通信通道上下文(服务端、客户端 通信)
*/
@Deprecated
public class NettyVisitorIdContext {
protected static final ConcurrentHashMap<String, Object> VISITOR_ID = new ConcurrentHashMap<>();
/**
* 添加访客
*
* @param visitorId 访客id
* @param visitor 访客
*/
public static <T> void pushVisitor(T visitor, String visitorId) {
VISITOR_ID.put(visitorId, visitor);
}
/**
* 通过访客端口获取访客
*
* @param visitorId 访客id
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getVisitor(String visitorId) {
return (T) VISITOR_ID.get(visitorId);
}
/**
* 通过访客端口获取访客
*
* @param visitorId 访客id
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getVisitor(byte[] visitorId) {
return getVisitor(new String(visitorId));
}
/**
* 移除访客
*
* @param visitorId 访客ID
*/
public static void clear(String visitorId) {
Channel visitor = getVisitor(visitorId);
if (visitor != null) {
VISITOR_ID.remove(visitorId);
visitor.close();
}
}
/**
* 移除访客
*
* @param visitorId 访客ID
*/
public static void clear(byte[] visitorId) {
clear(new String(visitorId));
}
}

View File

@ -0,0 +1,49 @@
package org.framework.smart.agent.network.heartbeat.common;
import java.util.concurrent.ConcurrentHashMap;
/**
* 访客端口对应上下文
*/
public class NettyVisitorPortContext {
protected static final ConcurrentHashMap<Integer, Object> VISITOR_PORT = new ConcurrentHashMap<>();
/**
* 添加访客
*
* @param visitorPort 访客端口
* @param visitor 访客
*/
public static <T> void pushVisitor(Integer visitorPort, T visitor) {
VISITOR_PORT.put(visitorPort, visitor);
}
/**
* 通过访客端口获取访客
*
* @param visitorPort 访客端口
* @param <T> 访客范型
* @return 访客
*/
public static <T> T getVisitor(Integer visitorPort) {
return (T) VISITOR_PORT.get(visitorPort);
}
/**
* 删除访客
* @param visitorPort 访客通道
* @return 删除的访客通道
* @param <T> 访客通道范型
*/
public static <T> T removeVisitor(Integer visitorPort){
T visitor = getVisitor(visitorPort);
if(visitor!=null){
VISITOR_PORT.remove(visitorPort);
}
return visitor;
}
}

View File

@ -0,0 +1,41 @@
package org.framework.smart.agent.network.heartbeat.common.adapter;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.framework.smart.agent.network.heartbeat.common.advanced.flow.ChannelFlow;
import org.framework.smart.agent.network.heartbeat.common.advanced.flow.HandleChannelFlowAdvanced;
import java.util.List;
/**
* 通道流量适配器
*
* @see HandleChannelFlowAdvanced
*/
@Slf4j
public class ChannelFlowAdapter {
protected final List<HandleChannelFlowAdvanced> handleChannelFlowAdvancedList;
public ChannelFlowAdapter(List<HandleChannelFlowAdvanced> handleChannelFlowAdvancedList) {
this.handleChannelFlowAdvancedList = handleChannelFlowAdvancedList;
}
/**
* 处理当前数据
*
* @param channelFlow 通道数据
*/
public void handler(Channel channel, ChannelFlow channelFlow) {
for (HandleChannelFlowAdvanced handleChannelTypeAdvanced : handleChannelFlowAdvancedList) {
if (handleChannelTypeAdvanced.support(channelFlow)) {
try {
handleChannelTypeAdvanced.handler(channel, channelFlow);
} catch (Exception e) {
log.error("流量统计失败:{}", e.getMessage());
}
return;
}
}
}
}

View File

@ -0,0 +1,46 @@
package org.framework.smart.agent.network.heartbeat.common.adapter;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.framework.smart.agent.network.heartbeat.common.advanced.HandleChannelTypeAdvanced;
import java.util.Comparator;
import java.util.List;
/**
* 通道类型适配器
*/
@Slf4j
public class ChannelTypeAdapter {
protected final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList;
public ChannelTypeAdapter(List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
}
/**
* 处理当前数据
*
* @param msg 通道数据
*/
public void handler(Channel channel, Object msg) {
// 升序 处理器
List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedSortedList =
handleChannelTypeAdvancedList.
stream().
sorted(Comparator.comparing(Ordered::getOrder)).
toList();
for (HandleChannelTypeAdvanced handleChannelTypeAdvanced : handleChannelTypeAdvancedSortedList) {
if (handleChannelTypeAdvanced.support(msg)) {
// log.info("处理器:{},客户端:{}, 处理类型:{}",handleChannelTypeAdvanced.getClass(),new String(msg.getClientId()),msg.getMysqlType());
handleChannelTypeAdvanced.handler(channel, msg);
return;
}
}
}
}

View File

@ -0,0 +1,25 @@
package org.framework.smart.agent.network.heartbeat.common.advanced;
import org.framework.smart.agent.network.heartbeat.common.NettyProxyMsg;
import org.framework.smart.agent.network.heartbeat.common.enums.MessageTypeEnums;
/**
* 服务端 处理客户端心跳
* TYPE_HEARTBEAT
*/
public abstract class AbstractHandleChannelHeartbeatTypeAdvanced<MSG> extends AbstractHandleChannelTypeAdvanced<NettyProxyMsg> implements HandleChannelTypeAdvanced {
/**
* 是否支持当前类型
*
* @param msg 通道数据
* @return 布尔类型 是、否
*/
@Override
public boolean doSupport(NettyProxyMsg msg) {
return MessageTypeEnums.TYPE_HEARTBEAT.getTypeByte() == msg.getType();
}
}

View File

@ -0,0 +1,93 @@
package org.framework.smart.agent.network.heartbeat.common.advanced;
import io.netty.channel.Channel;
import org.springframework.core.Ordered;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;
public abstract class AbstractHandleChannelTypeAdvanced<MSG> implements HandleChannelTypeAdvanced {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
protected abstract void doHandler(Channel channel, MSG msg);
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
@Override
public void handler(Channel channel, Object msg) {
doHandler(channel, (MSG) msg);
}
/**
* 是否支持当前类型
*
* @param msg 通道数据
* @return 布尔类型 是、否
*/
protected abstract boolean doSupport(MSG msg);
/**
* 是否支持当前类型
*
* @param msg 通道数据
* @return 布尔类型 是、否
*/
@Override
public boolean support(Object msg) {
if (msg == null) return false;
if (!msg.getClass().isAssignableFrom(Objects.requireNonNull(getMsgTypes()))) {
return false;
}
return doSupport((MSG) msg);
}
/**
* 获取当前处理范型
*
* @return 范型
*/
private Class<?> getMsgTypes() {
Type superClassType = this.getClass().getGenericSuperclass();
if (superClassType instanceof ParameterizedType parameterizedType) {
Type[] actualTypes = parameterizedType.getActualTypeArguments();
// for (Type type : actualTypes) {
// System.out.println("范型类型:" + ((Class<?>) type).getName());
// }
return (Class<?>) actualTypes[0];
} else {
// System.out.println("未能获取到范型类型");
return null;
}
}
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
*
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
* 越小越靠前
*/
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@ -0,0 +1,35 @@
package org.framework.smart.agent.network.heartbeat.common.advanced;
import io.netty.channel.Channel;
import org.springframework.core.Ordered;
import org.framework.smart.agent.network.heartbeat.common.MessageType;
import org.framework.smart.agent.network.heartbeat.common.enums.MessageTypeEnums;
/**
* 通道不同数据类型处理器
*
* @see MessageType
* @see MessageTypeEnums
*/
public interface HandleChannelTypeAdvanced extends Ordered {
/**
* 处理当前数据
*
* @param channel 当前通道
* @param msg 通道数据
*/
void handler(Channel channel, Object msg);
/**
* 是否支持当前类型
*
* @param msg 通道数据
* @return 布尔类型 是、否
*/
boolean support(Object msg);
}

Some files were not shown because too many files have changed in this diff Show More