diff --git a/README.md b/README.md index c4a7c7c7904fbccf84361c8d85ad11d5efb7352a..b41128112e869eb96e9706316c4b4b25d90009b6 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,8 @@ DeviceRemoteConfig 类做了设备映射关系;该关系是通过服务启动 SDK 已经发布到 maven 仓库中,https://mvnrepository.com/artifact/me.hekr.iotos.softgateway +[API 文档](https://apidoc.gitee.com/geekhekr/iotos-soft-gateway/) + 添加必须依赖: ```xml @@ -179,6 +181,8 @@ snapshot 版本 需要添加仓库: ``` +最新版本: 3.2.1-SNAPSHOT + 示例 demo 可以参考 [iotos-soft-gateway-demo](https://gitee.com/geekhekr/iotos-soft-gateway-demo) 项目。 ### 发布 diff --git a/deploy.sh b/deploy.sh index bc9e49ca7924b5f43c25a5b36ca7a95ad09ed2e3..85cc8b84137733da35e4a7d3868e2aa56b3b63ad 100755 --- a/deploy.sh +++ b/deploy.sh @@ -3,7 +3,7 @@ # ./deploy deploy 生成 maven jar包 # ./deploy upload 生成 maven jar 包并 commit -VERSION="3.1.0" +VERSION="3.2.1-SNAPSHOT" echo "版本号:$VERSION" updateVersion(){ diff --git a/example/pom.xml b/example/pom.xml index fcb192477d44c333fb07b153e0c7cc01a72545d4..fc1a3aa945ad3439603965d345d414cad62475c4 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -5,7 +5,7 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 8 @@ -27,6 +27,10 @@ me.hekr.iotos.softgateway framework-network-mqtt + + me.hekr.iotos.softgateway + framework-network-http + ch.qos.logback diff --git a/example/src/main/java/me/hekr/iotos/softgateway/sample/Device.java b/example/src/main/java/me/hekr/iotos/softgateway/sample/Device.java new file mode 100644 index 0000000000000000000000000000000000000000..9b4586115b397a531b6c11d6c5e6a8eed7026279 --- /dev/null +++ b/example/src/main/java/me/hekr/iotos/softgateway/sample/Device.java @@ -0,0 +1,9 @@ +package me.hekr.iotos.softgateway.sample; + +import lombok.Data; + +@Data +public class Device { + private String name; + private int temId; +} diff --git a/example/src/main/java/me/hekr/iotos/softgateway/sample/DeviceResponse.java b/example/src/main/java/me/hekr/iotos/softgateway/sample/DeviceResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..23768019f126e905b0732cf7c2fb82f19e7ac33e --- /dev/null +++ b/example/src/main/java/me/hekr/iotos/softgateway/sample/DeviceResponse.java @@ -0,0 +1,25 @@ +package me.hekr.iotos.softgateway.sample; + +import java.util.List; +import lombok.Data; +import me.hekr.iotos.softgateway.network.http.PageableResponse; + +@Data +public class DeviceResponse implements PageableResponse { + List devices; + + public DeviceResponse() {} + + public DeviceResponse(List devices) { + this.devices = devices; + } + + public boolean hasMore() { + return devices != null && !devices.isEmpty(); + } + + @Override + public List getItems() { + return devices; + } +} diff --git a/example/src/main/java/me/hekr/iotos/softgateway/sample/HttpClientSample.java b/example/src/main/java/me/hekr/iotos/softgateway/sample/HttpClientSample.java new file mode 100644 index 0000000000000000000000000000000000000000..ba6e22f23d582060eff19b3cd922c2bc1150aedf --- /dev/null +++ b/example/src/main/java/me/hekr/iotos/softgateway/sample/HttpClientSample.java @@ -0,0 +1,101 @@ +package me.hekr.iotos.softgateway.sample; + +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.List; +import me.hekr.iotos.softgateway.common.utils.JsonUtil; +import me.hekr.iotos.softgateway.network.http.HttpClient; +import me.hekr.iotos.softgateway.network.http.HttpPageResponse; +import me.hekr.iotos.softgateway.network.http.HttpRequest; +import me.hekr.iotos.softgateway.network.http.HttpRequestPageable; +import me.hekr.iotos.softgateway.network.http.HttpResponse; +import me.hekr.iotos.softgateway.network.http.HttpResponseChecker; +import me.hekr.iotos.softgateway.network.http.PageableResponseParser; + +/** + * 运行前先启动 HttpServerSample + * + * @author iotos + */ +public class HttpClientSample { + public static void main(String[] args) { + testRequestPageable(); + testRequestPageableItems(); + testChecker(); + } + + /** 测试分页 */ + public static void testRequestPageable() { + HttpClient client = HttpClient.newInstance("http://localhost:8080/"); + client.setHttpResponseChecker( + response -> { + // 200才算成功 + return response.getStatusCode() == 200; + }); + HttpRequestPageable request = + new HttpRequestPageable(0, 10) { + @Override + public HttpRequest buildRequest(int curPage, int pageSize) { + return HttpRequest.builder() + .addParam("curPage", curPage) + .addParam("pageSize", pageSize) + .build(); + } + + @Override + public boolean hasMore(DeviceResponse resp) { + return resp.hasMore(); + } + }; + PageableResponseParser parser = + r -> HttpPageResponse.wrap(JsonUtil.fromBytes(r.getBytes(), DeviceResponse.class)); + List list = client.exec(request, parser, 0, 1); + System.out.println(list); + } + + /** 测试分页 ,紧紧返回列表的情况 */ + public static void testRequestPageableItems() { + HttpClient client = HttpClient.newInstance("http://localhost:8080/"); + HttpRequestPageable request = + new HttpRequestPageable(0, 10) { + @Override + public HttpRequest buildRequest(int curPage, int pageSize) { + return HttpRequest.builder() + .path("items") + .addParam("curPage", curPage) + .addParam("pageSize", pageSize) + .build(); + } + + @Override + public boolean hasMore(DeviceResponse resp) { + return resp.hasMore(); + } + }; + PageableResponseParser parser = + r -> { + List devices = + JsonUtil.fromBytes(r.getBytes(), new TypeReference>() {}); + return new HttpPageResponse<>(new DeviceResponse(devices)); + }; + List list = client.exec(request, parser, 0, 1); + System.out.println(list); + } + + /** 测试自定义校验 */ + public static void testChecker() { + HttpClient client = HttpClient.newInstance("http://localhost:8080/"); + client.setHttpResponseChecker( + new HttpResponseChecker() { + @Override + public boolean isSuccess(HttpResponse response) { + return response.getStatusCode() == 201; + } + + @Override + public String desc() { + return "不是201!!"; + } + }); + client.exec(HttpRequest.builder().path("").build()); + } +} diff --git a/example/src/main/java/me/hekr/iotos/softgateway/sample/HttpServerSample.java b/example/src/main/java/me/hekr/iotos/softgateway/sample/HttpServerSample.java new file mode 100644 index 0000000000000000000000000000000000000000..7bc5fa28fff177b681eb16e8fca3cdd6cb91fec2 --- /dev/null +++ b/example/src/main/java/me/hekr/iotos/softgateway/sample/HttpServerSample.java @@ -0,0 +1,42 @@ +package me.hekr.iotos.softgateway.sample; + +import java.util.Collections; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * http server + * + *

端口配置在 resources/application.yml 中 + * + * @author iotos + */ +@SpringBootApplication +@RestController +public class HttpServerSample { + public static void main(String[] args) { + SpringApplication.run(HttpServerSample.class); + } + + @GetMapping("/") + public Object get(Integer curPage, Integer pageSize) { + DeviceResponse response = new DeviceResponse(); + + if (curPage >= 3) { + response.setDevices(Collections.emptyList()); + } else { + response.setDevices(Collections.singletonList(new Device())); + } + return response; + } + + @GetMapping("/items") + public Object items(Integer curPage, Integer pageSize) { + if (curPage >= 3) { + return Collections.emptyList(); + } + return Collections.singletonList(new Device()); + } +} diff --git a/example/src/main/resources/application.yml b/example/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..04ea648477df4098d6f33b28a53561913b5b871e --- /dev/null +++ b/example/src/main/resources/application.yml @@ -0,0 +1,2 @@ +# http server port +server.port: 8080 diff --git a/framework-common-utils/pom.xml b/framework-common-utils/pom.xml index 06dba71fe17fb81c5515e23007096c9608caa05f..4e790bb1265fd1247290b4d86d22b56ad34e3cf1 100644 --- a/framework-common-utils/pom.xml +++ b/framework-common-utils/pom.xml @@ -5,7 +5,7 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 4.0.0 diff --git a/framework-common-utils/src/main/java/me/hekr/iotos/softgateway/common/utils/ThreadPoolUtil.java b/framework-common-utils/src/main/java/me/hekr/iotos/softgateway/common/utils/ThreadPoolUtil.java index be67393670139da08fc75fd8a47d0b283ee23e44..8274391582ce8ef7a12c65d2961b67caf1dfea5d 100644 --- a/framework-common-utils/src/main/java/me/hekr/iotos/softgateway/common/utils/ThreadPoolUtil.java +++ b/framework-common-utils/src/main/java/me/hekr/iotos/softgateway/common/utils/ThreadPoolUtil.java @@ -32,7 +32,7 @@ public class ThreadPoolUtil implements Closeable { public static final ScheduledExecutorService DEFAULT_SCHEDULED = (ScheduledExecutorService) new Builder() - .setPrefix("spring-schedule") + .setPrefix("default-scheduled") .setCore(CORES * 4) .setMax(32) .setQueueSize(1000) diff --git a/framework-core/pom.xml b/framework-core/pom.xml index 59609321cf39a975d278650f6cc33bfbab83b814..699b48acd92af6d0318dae319f9986ffa0370c25 100644 --- a/framework-core/pom.xml +++ b/framework-core/pom.xml @@ -5,7 +5,7 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 4.0.0 jar diff --git a/framework-core/src/main/java/me/hekr/iotos/softgateway/core/config/IotOsConfig.java b/framework-core/src/main/java/me/hekr/iotos/softgateway/core/config/IotOsConfig.java index 8f345c25f8ae9352bf063111d86f08d3c3334378..2514073a8b548ea5297fd2e1f165b3d414328c04 100644 --- a/framework-core/src/main/java/me/hekr/iotos/softgateway/core/config/IotOsConfig.java +++ b/framework-core/src/main/java/me/hekr/iotos/softgateway/core/config/IotOsConfig.java @@ -17,15 +17,21 @@ public class IotOsConfig { @Getter private MqttConfig mqttConfig; @Getter private GatewayConfig gatewayConfig; + /** mqtt 连接地址 */ @Value("${connect.mqtt.endpoint}") private String endpoint; - @Value("${connect.mqtt.connectionTimeout}") + /** mqtt 连接超时时间 秒 */ + @Value("${connect.mqtt.connectionTimeout:10}") private int connectionTimeout; @Value("${connect.mqtt.keepAliveTime}") private int keepAliveTime; + @Value("${connect.mqtt.klink.queue.size:10}") + @Getter + private int klinkQueueSize; + @Value("${gateway.pk}") private String gatewayPk; diff --git a/framework-core/src/main/java/me/hekr/iotos/softgateway/core/network/mqtt/MqttService.java b/framework-core/src/main/java/me/hekr/iotos/softgateway/core/network/mqtt/MqttService.java index 8ac2ec3fc412450bda7d83aed6245288a2511ef7..347a749c4abf70b43eb15f97368417125f35fd05 100644 --- a/framework-core/src/main/java/me/hekr/iotos/softgateway/core/network/mqtt/MqttService.java +++ b/framework-core/src/main/java/me/hekr/iotos/softgateway/core/network/mqtt/MqttService.java @@ -11,6 +11,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import me.hekr.iotos.softgateway.common.utils.JsonUtil; +import me.hekr.iotos.softgateway.common.utils.ThreadPoolUtil; import me.hekr.iotos.softgateway.core.config.DeviceRemoteConfig; import me.hekr.iotos.softgateway.core.config.IotOsConfig; import me.hekr.iotos.softgateway.core.config.MqttConfig; @@ -18,7 +20,6 @@ import me.hekr.iotos.softgateway.core.klink.DevLogin; import me.hekr.iotos.softgateway.core.klink.DevLogout; import me.hekr.iotos.softgateway.core.klink.KlinkDev; import me.hekr.iotos.softgateway.core.listener.MqttConnectedListener; -import me.hekr.iotos.softgateway.common.utils.JsonUtil; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttException; @@ -52,6 +53,20 @@ public class MqttService { this.iotOsConfig = iotOsConfig; initConfig(iotOsConfig); publishExecutor.execute(this::startPublishTask); + ThreadPoolUtil.DEFAULT_SCHEDULED.scheduleAtFixedRate( + () -> { + int size = queue.size(); + if (log.isDebugEnabled()) { + log.debug("klink 队列还有 {} 个", size); + } + + if (size > iotOsConfig.getKlinkQueueSize()) { + log.warn("Klink 队列未及时消费,还有: {} 个记录", size); + } + }, + 0, + 3, + TimeUnit.SECONDS); } private void initConfig(IotOsConfig iotOsConfig) throws MqttException { @@ -137,6 +152,10 @@ public class MqttService { /** @param klink 消息,发送的时候会被 toJson */ @SneakyThrows public void publish(KlinkDev klink) { + if (log.isDebugEnabled()) { + log.debug("发送 klink: {}", JsonUtil.toJson(klink)); + } + String pk = klink.getPk(); String devId = klink.getDevId(); Optional byPkAndDevId = DeviceRemoteConfig.getByPkAndDevId(pk, devId); diff --git a/framework-network-common/pom.xml b/framework-network-common/pom.xml index cdf6749ba033c9c48101fa1ea6f3f4f867ac9bd3..b9280d0ea85cafb14b9cac73e33999d906c489f1 100644 --- a/framework-network-common/pom.xml +++ b/framework-network-common/pom.xml @@ -5,7 +5,7 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 4.0.0 diff --git a/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/AbstractClient.java b/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/AbstractClient.java index 7d932302112d9806005759236b402205b5aaa10c..43139960aef50696d8140cd814bbc2ba3b9653fe 100644 --- a/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/AbstractClient.java +++ b/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/AbstractClient.java @@ -14,6 +14,7 @@ import io.netty.channel.socket.DatagramChannel; import io.netty.handler.logging.LoggingHandler; import java.net.InetSocketAddress; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import lombok.Getter; import lombok.Setter; @@ -71,6 +72,7 @@ public abstract class AbstractClient { protected void init() {} public void close() { + log.info("准备关闭服务 " + this.getClass().getName()); try { preDestroy(); } catch (Exception e) { @@ -78,7 +80,8 @@ public abstract class AbstractClient { } if (eventLoop != null) { try { - eventLoop.shutdownGracefully().syncUninterruptibly(); + log.info("关闭 eventLoop"); + eventLoop.shutdownGracefully(0, 10, TimeUnit.SECONDS); log.info(" 成功关闭 client"); } catch (Exception e) { log.error(e.getMessage(), e); @@ -216,6 +219,10 @@ public abstract class AbstractClient { /** 直到连接成功 */ protected void loopConnect() { + if (eventLoop.isShuttingDown() || eventLoop.isShutdown() || eventLoop.isTerminated()) { + log.error("服务关闭,不能重连"); + return; + } while (true) { try { connect(); diff --git a/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/ClientMessageHandler.java b/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/ClientMessageHandler.java index db6b78e67cdf5cdf27e25dbeb30f688edcd07d48..80897a6acf996bf62706899cc308dcab635da7fc 100644 --- a/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/ClientMessageHandler.java +++ b/framework-network-common/src/main/java/me/hekr/iotos/softgateway/network/common/client/ClientMessageHandler.java @@ -8,8 +8,8 @@ import java.net.InetSocketAddress; import lombok.extern.slf4j.Slf4j; import me.hekr.iotos.softgateway.network.common.CloseReason; import me.hekr.iotos.softgateway.network.common.InternalPacket; -import me.hekr.iotos.softgateway.network.common.listener.MessageListener; import me.hekr.iotos.softgateway.network.common.PacketContext; +import me.hekr.iotos.softgateway.network.common.listener.MessageListener; /** @author iotos */ @Sharable @@ -35,6 +35,10 @@ public class ClientMessageHandler extends SimpleChannelInboundHandler packet) { + if (log.isDebugEnabled()) { + log.debug("收到消息:{}", packet); + } + // 如果不是同步,调用消息回调接口 if (!sync) { messageListener.onMessage(PacketContext.wrap(packet.getAddress(), packet.getMessage())); diff --git a/framework-network-http/pom.xml b/framework-network-http/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a4c43d83c5d4a1c760880805fee139f8afaddd9c --- /dev/null +++ b/framework-network-http/pom.xml @@ -0,0 +1,46 @@ + + + + iotos-soft-gateway + me.hekr.iotos.softgateway + 3.2.1-SNAPSHOT + + 4.0.0 + framework-network-http + + + + org.springframework.boot + spring-boot-parent + 2.2.12.RELEASE + pom + import + + + + + + + me.hekr.iotos.softgateway + framework-common-utils + + + org.springframework.boot + spring-boot-starter-web + + + + com.squareup.okhttp3 + logging-interceptor + 3.13.1 + + + + + 8 + 8 + + + \ No newline at end of file diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpClient.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpClient.java new file mode 100644 index 0000000000000000000000000000000000000000..862d81f53861fa37d3a444aa157e11e8b82db163 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpClient.java @@ -0,0 +1,136 @@ +package me.hekr.iotos.softgateway.network.http; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import lombok.Data; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import me.hekr.iotos.softgateway.common.utils.JsonUtil; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; + +/** @author du */ +@Data +@Slf4j +public class HttpClient { + + private OkHttpClient okHttpClient; + private String baseUrl; + /** http response 结果校验,如果不通过则抛出异常 */ + @Setter private HttpResponseChecker httpResponseChecker = HttpResponseChecker.DEFAULT; + + private HttpClient() {} + + @SneakyThrows + public static HttpClient newInstance(String baseUrl, int timeoutOfSecs, Level level) { + ConnectionPool connectionPool = new ConnectionPool(1, 30, TimeUnit.SECONDS); + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); + interceptor.setLevel(level); + OkHttpClient client = + new OkHttpClient() + .newBuilder() + // 读超时 + .readTimeout(Duration.ofSeconds(timeoutOfSecs)) + // 写超时 + .writeTimeout(Duration.ofSeconds(timeoutOfSecs)) + // + .callTimeout(Duration.ofSeconds(timeoutOfSecs)) + // 连接池 + .connectionPool(connectionPool) + // 连接超时 + .connectTimeout(Duration.ofSeconds(timeoutOfSecs)) + // 不重试 + .retryOnConnectionFailure(false) + .addInterceptor(interceptor) + .build(); + HttpClient httpClient = new HttpClient(); + httpClient.okHttpClient = client; + httpClient.baseUrl = baseUrl; + return httpClient; + } + + @SneakyThrows + public static HttpClient newInstance(String url) { + return newInstance(url, 3, Level.BASIC); + } + + @SneakyThrows + public static HttpClient newInstance(String url, Level level) { + return newInstance(url, 3, level); + } + + @SneakyThrows + public HttpResponse exec(HttpRequest request) { + HttpResponse httpResponse; + request.baseUrl = baseUrl; + try { + Response response = okHttpClient.newCall(request.getOkHttpRequest()).execute(); + ResponseBody body = response.body(); + byte[] bytes = body == null ? null : body.bytes(); + httpResponse = new HttpResponse(response, bytes); + } catch (Exception e) { + throw new HttpException(request, e); + } + + boolean success = httpResponseChecker.isSuccess(httpResponse); + if (!success) { + throw new HttpException(request, httpResponse, httpResponseChecker.desc()); + } + + return httpResponse; + } + + /** + * 执行请求,并自动从 json 对象解析成对象 + * + * @param request 请求 + * @param clazz 映射的对象 + * @param 参数类型 + * @return 结果 + */ + @SneakyThrows + public T exec(HttpRequest request, Class clazz) { + return exec(request, response -> JsonUtil.fromBytes(response.bytes, clazz)); + } + + @SneakyThrows + public T exec(HttpRequest request, ResponseParser parser) { + HttpResponse response = exec(request); + return parser.parse(response); + } + + /** + * @param request 请求 + * @param parser response解析 + * @param curPage 初始化当前页码 + * @param pageSize 初始化每页大小 + * @param response + * @param response 中的元素 + * @return 元素列表 + */ + public , T> List exec( + HttpRequestPageable request, + PageableResponseParser parser, + int curPage, + int pageSize) { + List resultList = new ArrayList<>(); + boolean hasMore = true; + int page = curPage; + while (hasMore) { + HttpResponse resp = exec(request.buildRequest(page, pageSize)); + HttpPageResponse parse = parser.parse(resp); + resultList.addAll(parse.getItems()); + hasMore = request.hasMore(parse.getResult()); + page++; + } + + return resultList; + } +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpException.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpException.java new file mode 100644 index 0000000000000000000000000000000000000000..fff0806508eb0e65de1cf2e0cd4269a73794556d --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpException.java @@ -0,0 +1,36 @@ +package me.hekr.iotos.softgateway.network.http; + +/** + * http 异常 + * + * @author IoTOS + */ +public class HttpException extends RuntimeException { + + public HttpException(HttpRequest request, Throwable t) {} + + public HttpException(String message) { + super(message); + } + + public HttpException(String message, Throwable cause) { + super(message, cause); + } + + public HttpException(Throwable cause) { + super(cause); + } + + public HttpException( + String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public HttpException(HttpRequest request, HttpResponse response, Throwable throwable) { + super("request:" + request + ", response:" + response, throwable); + } + + public HttpException(HttpRequest request, HttpResponse response, String desc) { + super(desc + ", request:" + request + ", response:" + response); + } +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpExceptionHandler.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..544796e99e21c7cf0abb967ae2dd6d8f4b5e6d88 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpExceptionHandler.java @@ -0,0 +1,33 @@ +package me.hekr.iotos.softgateway.network.http; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 异常处理器 + * + * @author IoTOS + */ +public interface HttpExceptionHandler { + Logger log = LoggerFactory.getLogger(HttpExceptionHandler.class); + /** 抛出异常 */ + HttpExceptionHandler THROW_HANDLER = + (request, t) -> { + throw new HttpException(request, t); + }; + + /** 默认打印异常 */ + HttpExceptionHandler LOG_HANDLER = + (request, t) -> { + log.error("request:" + request + ", error:" + t.getMessage(), t); + return new HttpResponse(); + }; + /** + * 异常发生的时候 + * + * @param request 请求 + * @param t 异常 + * @return 自定义 response + */ + HttpResponse onException(HttpRequest request, Throwable t); +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpMethod.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..c99a57a7f48cafe2504c779976b550225505837f --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpMethod.java @@ -0,0 +1,14 @@ +package me.hekr.iotos.softgateway.network.http; + +/** @author iotos */ +public enum HttpMethod { + OPTIONS, + GET, + POST, + HEAD, + PUT, + PATCH, + DELETE, + TRACE, + CONNECT +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpPageRequest.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpPageRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..4fe46d9dc2181046bc9183ed3e3f38d19bce6f7d --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpPageRequest.java @@ -0,0 +1,12 @@ +package me.hekr.iotos.softgateway.network.http; + +/** @author iotos */ +public abstract class HttpPageRequest extends HttpRequest { + private Builder builder; + + public HttpPageRequest(Builder builder) { + super(builder); + } + + public abstract HttpPageRequest build(); +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpPageResponse.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpPageResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..01831edee6fbe58fe96bca5ae9d13b4c4a8d7cd7 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpPageResponse.java @@ -0,0 +1,31 @@ +package me.hekr.iotos.softgateway.network.http; + +import java.util.Collection; +import java.util.Collections; +import lombok.Getter; +import lombok.Setter; + +/** + * 分页请求结果 + * + * @author iotos + */ +public class HttpPageResponse, T> { + + /** response 整体结果 */ + @Setter @Getter private R result; + + public HttpPageResponse() {} + + public HttpPageResponse(R r) { + this.result = r; + } + + public static , T> HttpPageResponse wrap(R r) { + return new HttpPageResponse<>(r); + } + + public Collection getItems() { + return result == null ? Collections.emptyList() : result.getItems(); + } +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpRequest.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..8c5d8088717a22862986d63cb29813587b5386f4 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpRequest.java @@ -0,0 +1,113 @@ +package me.hekr.iotos.softgateway.network.http; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.RequestBody; +import org.springframework.http.MediaType; + +/** @author iotos */ +public class HttpRequest { + protected Request request; + String baseUrl; + private Builder requestBuilder; + + public HttpRequest(Builder builder) { + this.requestBuilder = builder; + } + + public HttpRequest(Request request) { + this.request = request; + } + + public static Builder builder() { + return new Builder(); + } + + public Request getOkHttpRequest() { + HttpUrl httpUrl = HttpUrl.parse(requestBuilder.path); + // 为 null,非完整的 url,拼接 baseUrl + if (httpUrl == null) { + httpUrl = HttpUrl.parse(baseUrl + requestBuilder.path); + Objects.requireNonNull(httpUrl, "需要填写baseUrl 或者 request 的 path 需要填写完整的 url 地址"); + } + HttpUrl.Builder httpUrlBuilder = httpUrl.newBuilder(); + requestBuilder.queryParams.forEach( + (k, v) -> httpUrlBuilder.addQueryParameter(k, String.valueOf(v))); + + return requestBuilder.okHttpRequestBuilder.url(httpUrlBuilder.build()).build(); + } + + @Override + public String toString() { + return "HttpRequest{" + "request=" + getOkHttpRequest() + '}'; + } + + public static class Builder { + private final Map queryParams = new HashMap<>(10); + private final Headers.Builder headerBuilder = new Headers.Builder(); + Request.Builder okHttpRequestBuilder; + private byte[] body; + private HttpMethod method = HttpMethod.GET; + private MediaType mediaType = MediaType.APPLICATION_JSON; + private String path = ""; + + public HttpRequest build() { + okHttpRequestBuilder = new Request.Builder(); + handleHeaders(); + handleMethodAndBody(); + return new HttpRequest(this); + } + + private void handleUrl() { + HttpUrl.Builder httpUrlBuilder = HttpUrl.get(path).newBuilder(); + queryParams.forEach((k, v) -> httpUrlBuilder.addQueryParameter(k, String.valueOf(v))); + okHttpRequestBuilder.url(httpUrlBuilder.build()); + } + + private void handleMethodAndBody() { + RequestBody requestBody = null; + if (okhttp3.internal.http.HttpMethod.requiresRequestBody(method.name())) { + requestBody = RequestBody.create(okhttp3.MediaType.parse(mediaType.toString()), body); + } + okHttpRequestBuilder.method(method.name(), requestBody); + } + + private void handleHeaders() { + okHttpRequestBuilder.headers(headerBuilder.build()); + } + + public Builder addHeader(String name, String value) { + headerBuilder.add(name, value); + return this; + } + + public Builder body(byte[] body) { + this.body = body; + return this; + } + + public Builder addParam(String name, Object value) { + queryParams.put(name, value); + return this; + } + + public Builder path(String path) { + this.path = path; + return this; + } + + public Builder method(HttpMethod method) { + this.method = method; + return this; + } + + public Builder mediaType(MediaType mediaType) { + this.mediaType = mediaType; + return this; + } + } +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpRequestPageable.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpRequestPageable.java new file mode 100644 index 0000000000000000000000000000000000000000..c7a17c3e98823a4d3e894f637a96de592a294c93 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpRequestPageable.java @@ -0,0 +1,36 @@ +package me.hekr.iotos.softgateway.network.http; + +/** + * 分页请求 + * + * @author iotos + */ +public abstract class HttpRequestPageable { + int curPage = 0; + int pageSize = 10; + + /** 默认分页 curPage=0; pageSize=10; */ + public HttpRequestPageable() {} + + public HttpRequestPageable(int curPage, int pageSize) { + this.curPage = curPage; + this.pageSize = pageSize; + } + + /** + * 通过分页信息构造一个 httpRequest + * + * @param curPage 当前页码, 下次查询的页码,第一次传入是 初始化的页面,后面每次调用会+1 + * @param pageSize 每页大小 + * @return 构造的 HttpRequest + */ + public abstract HttpRequest buildRequest(int curPage, int pageSize); + + /** + * 是否还有更多元素 + * + * @param resp 结果 + * @return true 还有剩余分页数据;false 没有分页数据 + */ + public abstract boolean hasMore(R resp); +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpResponse.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..fd6533571fa9a201b0f1bc3f43c1c8d07edbacad --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpResponse.java @@ -0,0 +1,24 @@ +package me.hekr.iotos.softgateway.network.http; + +import lombok.Getter; +import okhttp3.Response; + +/** @author IoTOS */ +public class HttpResponse { + Response response; + @Getter byte[] bytes; + @Getter int statusCode; + + public HttpResponse(Response response, byte[] bytes) { + this.response = response; + this.statusCode = response.code(); + this.bytes = bytes; + } + + public HttpResponse() {} + + @Override + public String toString() { + return "HttpResponse{" + "response=" + response + '}'; + } +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpResponseChecker.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpResponseChecker.java new file mode 100644 index 0000000000000000000000000000000000000000..dbb1777d1fe01bd7b8b38a6d1316a6e141b27c99 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/HttpResponseChecker.java @@ -0,0 +1,40 @@ +package me.hekr.iotos.softgateway.network.http; + +/** + * 是否成功校验 + * + * @author iotos + */ +public interface HttpResponseChecker { + + /** 默认实现, http status 200 <=code<300 */ + HttpResponseChecker DEFAULT = + new HttpResponseChecker() { + @Override + public boolean isSuccess(HttpResponse response) { + return response.response.isSuccessful(); + } + + @Override + public String desc() { + return "status code 不是2xx"; + } + }; + /** + * 是否成功 + * + * @param response response + * @return 成功 true,否则 false + */ + boolean isSuccess(HttpResponse response); + + /** + * 校验失败描述 + * + * @return 描述 + */ + default String desc() { + return "response 校验失败"; + } + ; +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/PageableResponse.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/PageableResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..cf3df7055640ed5a84f2d47e982f668319958410 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/PageableResponse.java @@ -0,0 +1,14 @@ +package me.hekr.iotos.softgateway.network.http; + +import java.util.List; + +/** @author iotos */ +public interface PageableResponse { + + /** + * 获取分页的元素列表 + * + * @return 分页的元素列表 + */ + List getItems(); +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/PageableResponseParser.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/PageableResponseParser.java new file mode 100644 index 0000000000000000000000000000000000000000..ba044adbc45db7e0a488c4ba2f69fb5df4c46fc8 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/PageableResponseParser.java @@ -0,0 +1,12 @@ +package me.hekr.iotos.softgateway.network.http; + +/** @author iotos */ +public interface PageableResponseParser, T> { + /** + * 解析 response + * + * @param response 请求结果 + * @return 请求结果 + */ + HttpPageResponse parse(HttpResponse response); +} diff --git a/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/ResponseParser.java b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/ResponseParser.java new file mode 100644 index 0000000000000000000000000000000000000000..0feaa4d1e66582fbb691ca549d8b9f05263b1198 --- /dev/null +++ b/framework-network-http/src/main/java/me/hekr/iotos/softgateway/network/http/ResponseParser.java @@ -0,0 +1,13 @@ +package me.hekr.iotos.softgateway.network.http; + +/** @author iotos */ +public interface ResponseParser { + + /** + * 从 response 解析成对象 + * + * @param response response + * @return 解析结果 + */ + T parse(HttpResponse response); +} diff --git a/framework-network-mqtt/pom.xml b/framework-network-mqtt/pom.xml index 00b9a6979a641dee81c0407f8248e5ccf5e1aba0..25abf8af7b05107417033ff340a709d928a4ef06 100644 --- a/framework-network-mqtt/pom.xml +++ b/framework-network-mqtt/pom.xml @@ -5,7 +5,7 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 4.0.0 @@ -25,7 +25,7 @@ me.hekr.iotos.softgateway framework-network-common - 3.1.0 + 3.2.1-SNAPSHOT \ No newline at end of file diff --git a/framework-network-tcp/pom.xml b/framework-network-tcp/pom.xml index 53b25ca864a75fa5a52d061ccb47160f709e5d05..8d546fb0b0f3a2fe695d20e54dbf61e60b712b12 100644 --- a/framework-network-tcp/pom.xml +++ b/framework-network-tcp/pom.xml @@ -5,12 +5,11 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 4.0.0 framework-network-tcp - 3.1.0 8 diff --git a/framework-network-udp/pom.xml b/framework-network-udp/pom.xml index 269d00993fdef9ab367f89c084e2f8dbcade4eab..fbe6af6c2f33612d1d5b6461eddc8a26bc3848af 100644 --- a/framework-network-udp/pom.xml +++ b/framework-network-udp/pom.xml @@ -5,12 +5,11 @@ iotos-soft-gateway me.hekr.iotos.softgateway - 3.1.0 + 3.2.1-SNAPSHOT 4.0.0 framework-network-udp - 3.1.0 8 diff --git a/pom.xml b/pom.xml index f3c220967307823f3f1427e01d77627fae8211a5..75b996607345473760ecbf81177c4c7826d5a176 100644 --- a/pom.xml +++ b/pom.xml @@ -12,12 +12,13 @@ example framework-common-utils framework-network-mqtt + framework-network-http me.hekr.iotos.softgateway iotos-soft-gateway - 3.1.0 + 3.2.1-SNAPSHOT ${project.groupId}:${project.artifactId} IoTOS 软网关 https://gitee.com/geekhekr/iotos-soft-gateway @@ -29,7 +30,7 @@ UTF-8 UTF-8 UTF-8 - 3.1.0 + 3.2.1-SNAPSHOT @@ -50,7 +51,8 @@ scm:git:git://https://gitee.com/geekhekr/iotos-soft-gateway.git - scm:git:ssh://gitee.com:geekhekr/iotos-soft-gateway.git + scm:git:ssh://gitee.com:geekhekr/iotos-soft-gateway.git + https://gitee.com/geekhekr/iotos-soft-gateway @@ -61,6 +63,11 @@ netty-all 4.1.60.Final + + me.hekr.iotos.softgateway + framework-network-common + ${iotos.softgateway.version} + me.hekr.iotos.softgateway framework-core @@ -88,7 +95,7 @@ me.hekr.iotos.softgateway - framework-network-common + framework-network-http ${iotos.softgateway.version} @@ -148,22 +155,22 @@ - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - - jar - - - - - false - - + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + false + + org.apache.maven.plugins maven-gpg-plugin