From dea6774942a5b643b69cb65e28e2bd1cc8c12230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+zfine_0@user.noreply.gitee.com> Date: Wed, 18 Sep 2024 09:40:34 +0000 Subject: [PATCH 001/102] Initial commit --- .gitee/ISSUE_TEMPLATE.zh-CN.md | 13 ++ .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md | 12 ++ .gitignore | 23 +++ LICENSE | 201 ++++++++++++++++++++++++++ README.en.md | 36 +++++ README.md | 37 +++++ 6 files changed, 322 insertions(+) create mode 100644 .gitee/ISSUE_TEMPLATE.zh-CN.md create mode 100644 .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.en.md create mode 100644 README.md diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md new file mode 100644 index 0000000..f09d98d --- /dev/null +++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md @@ -0,0 +1,13 @@ +### 该问题是怎么引起的? + + + +### 重现步骤 + + + +### 报错信息 + + + + diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md new file mode 100644 index 0000000..6199c1e --- /dev/null +++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md @@ -0,0 +1,12 @@ +### 相关的Issue + + +### 原因(目的、解决的问题等) + + +### 描述(做了什么,变更了什么) + + +### 测试用例(新增、改动、可能影响的功能) + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1c2a23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..cee5c4c --- /dev/null +++ b/README.en.md @@ -0,0 +1,36 @@ +# JChargePointProtocol + +#### Description +JAVA 充电桩协议库 + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md new file mode 100644 index 0000000..b255b63 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# JChargePointProtocol + +#### 介绍 +JAVA 充电桩协议库 + +#### 软件架构 +软件架构说明 + + +#### 安装教程 + +1. xxxx +2. xxxx +3. xxxx + +#### 使用说明 + +1. xxxx +2. xxxx +3. xxxx + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 +4. 新建 Pull Request + + +#### 特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 +5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) From cb19b459196971ca869b7298275cdf3babd0f468 Mon Sep 17 00:00:00 2001 From: 3god Date: Tue, 8 Oct 2024 09:38:54 +0800 Subject: [PATCH 002/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 33 ++ README.md | 46 +- docker/Dockerfile-App | 40 ++ docker/Dockerfile-Base | 11 + docker/Dockerfile-Protocol | 40 ++ docker/docker-compose.kafka.yml | 57 ++ docker/docker-compose.redis-cluster.yml | 90 +++ docker/docker-compose.redis-sentinel.yml | 49 ++ docker/docker-compose.redis-standalone.yml | 23 + docker/kafka.env | 14 + docker/queue-kafka.env | 2 + docker/schema/schema-postgres.sql | 121 ++++ docker/start.sh | 21 + jcpp-app-bootstrap/pom.xml | 84 +++ jcpp-app-bootstrap/src/layers.xml | 33 ++ .../sanbing/jcpp/JCPPServerApplication.java | 39 ++ .../src/main/resources/app-service.yml | 214 +++++++ .../src/main/resources/banner.txt | 12 + .../src/main/resources/log4j2.xml | 56 ++ .../java/sanbing/jcpp/AbstractTestBase.java | 27 + .../jcpp/app/dal/mapper/GunMapperTest.java | 72 +++ .../jcpp/app/dal/mapper/OrderMapperTest.java | 66 +++ .../jcpp/app/dal/mapper/PileMapperTest.java | 74 +++ .../app/dal/mapper/StationMapperTest.java | 55 ++ .../jcpp/app/dal/mapper/UserMapperTest.java | 44 ++ .../cache/RedisCacheConfigurationTest.java | 98 ++++ .../resources/app-service-test.properties | 2 + jcpp-app/pom.xml | 56 ++ .../jcpp/app/dal/config/DalConfig.java | 76 +++ .../config/ibatis/enums/GunOptStatusEnum.java | 22 + .../config/ibatis/enums/GunRunStatusEnum.java | 27 + .../config/ibatis/enums/OrderStatusEnum.java | 24 + .../config/ibatis/enums/OrderTypeEnum.java | 21 + .../config/ibatis/enums/OwnerTypeEnum.java | 21 + .../config/ibatis/enums/PileStatusEnum.java | 24 + .../dal/config/ibatis/enums/PileTypeEnum.java | 21 + .../ibatis/enums/StationStatusEnum.java | 26 + .../config/ibatis/enums/UserStatusEnum.java | 20 + .../ibatis/typehandlers/JsonbTypeHandler.java | 40 ++ .../ibatis/typehandlers/UUIDTypeHandler.java | 40 ++ .../java/sanbing/jcpp/app/dal/entity/Gun.java | 61 ++ .../sanbing/jcpp/app/dal/entity/Order.java | 69 +++ .../sanbing/jcpp/app/dal/entity/Pile.java | 61 ++ .../sanbing/jcpp/app/dal/entity/Station.java | 62 +++ .../sanbing/jcpp/app/dal/entity/User.java | 45 ++ .../jcpp/app/dal/mapper/GunMapper.java | 14 + .../jcpp/app/dal/mapper/OrderMapper.java | 14 + .../jcpp/app/dal/mapper/PileMapper.java | 23 + .../jcpp/app/dal/mapper/StationMapper.java | 14 + .../jcpp/app/dal/mapper/UserMapper.java | 14 + .../sanbing/jcpp/app/data/PileSession.java | 57 ++ .../AbstractCachedEntityRepository.java | 23 + .../repository/AbstractEntityRepository.java | 17 + .../CachedVersionedEntityRepository.java | 19 + .../jcpp/app/repository/PileRepository.java | 15 + .../app/repository/PileRepositoryImpl.java | 47 ++ .../jcpp/app/service/DownlinkCallService.java | 15 + .../jcpp/app/service/PileProtocolService.java | 66 +++ .../cache/pile/PileCacheEvictEvent.java | 17 + .../app/service/cache/pile/PileCacheKey.java | 47 ++ .../service/cache/pile/PileCaffeineCache.java | 22 + .../service/cache/pile/PileRedisCache.java | 33 ++ .../cache/session/PileSessionCacheKey.java | 41 ++ .../session/PileSessionCaffeineCache.java | 24 + .../cache/session/PileSessionRedisCache.java | 36 ++ .../DownlinkRestTemplateConfiguration.java | 32 ++ .../impl/DefaultDownlinkCallService.java | 93 ++++ .../impl/DefaultPileProtocolService.java | 264 +++++++++ .../queue/AbstractConsumerService.java | 66 +++ .../app/service/queue/AppConsumerStats.java | 85 +++ .../queue/AppQueueConsumerManager.java | 296 ++++++++++ .../queue/QueueConsumerManagerTask.java | 37 ++ .../app/service/queue/QueueConsumerTask.java | 78 +++ .../jcpp/app/service/queue/QueueEvent.java | 13 + .../ProtocolUplinkConsumerService.java | 236 ++++++++ jcpp-infrastructure-cache/pom.xml | 51 ++ .../infrastructure/cache/CacheConstants.java | 13 + .../jcpp/infrastructure/cache/CacheSpecs.java | 13 + .../infrastructure/cache/CacheSpecsMap.java | 22 + .../cache/CacheTransaction.java | 15 + .../cache/CacheValueWrapper.java | 11 + .../cache/CaffeineCacheTransaction.java | 48 ++ .../cache/CaffeineTransactionalCache.java | 180 ++++++ .../jcpp/infrastructure/cache/HasVersion.java | 14 + .../cache/JCPPCaffeineCacheConfiguration.java | 83 +++ .../JCPPJCPPRedisClusterConfiguration.java | 54 ++ .../JCPPJCPPRedisSentinelConfiguration.java | 59 ++ .../JCPPJCPPRedisStandaloneConfiguration.java | 78 +++ .../cache/JCPPRedisCacheConfiguration.java | 168 ++++++ .../cache/JCPPRedisSerializer.java | 18 + .../cache/RedisCacheTransaction.java | 45 ++ .../cache/RedisTransactionalCache.java | 246 ++++++++ .../cache/SimpleCacheValueWrapper.java | 35 ++ .../cache/TransactionalCache.java | 86 +++ .../infrastructure/cache/VersionedCache.java | 51 ++ .../cache/VersionedCacheKey.java | 15 + .../cache/VersionedCaffeineCache.java | 86 +++ .../cache/VersionedRedisCache.java | 155 ++++++ jcpp-infrastructure-proto/pom.xml | 50 ++ .../infrastructure/proto/ProtoConverter.java | 57 ++ .../proto/model/PricingModel.java | 78 +++ .../src/main/proto/cluster.proto | 25 + .../src/main/proto/protocol.proto | 242 ++++++++ jcpp-infrastructure-queue/pom.xml | 52 ++ .../queue/AbstractQueueConsumerTemplate.java | 190 +++++++ .../jcpp/infrastructure/queue/Callback.java | 26 + .../infrastructure/queue/DefaultQueueMsg.java | 23 + .../queue/DefaultQueueMsgHeaders.java | 29 + .../infrastructure/queue/KafkaQueueMsg.java | 38 ++ .../infrastructure/queue/PackCallback.java | 32 ++ .../queue/PackProcessingContext.java | 75 +++ .../infrastructure/queue/ProtoQueueMsg.java | 40 ++ .../jcpp/infrastructure/queue/QueueAdmin.java | 17 + .../infrastructure/queue/QueueCallback.java | 12 + .../infrastructure/queue/QueueConsumer.java | 34 ++ .../jcpp/infrastructure/queue/QueueMsg.java | 14 + .../infrastructure/queue/QueueMsgHeaders.java | 16 + .../queue/QueueMsgMetadata.java | 8 + .../infrastructure/queue/QueueProducer.java | 19 + .../queue/common/QueueConfig.java | 13 + .../queue/common/QueueConstants.java | 17 + .../queue/common/TopicPartitionInfo.java | 59 ++ .../discovery/DefaultServiceInfoProvider.java | 109 ++++ .../queue/discovery/DiscoveryProvider.java | 15 + .../discovery/DummyDiscoveryProvider.java | 41 ++ .../discovery/HashPartitionProvider.java | 199 +++++++ .../queue/discovery/PartitionProvider.java | 22 + .../queue/discovery/QueueKey.java | 33 ++ .../queue/discovery/ServiceInfoProvider.java | 26 + .../queue/discovery/ServiceType.java | 23 + .../queue/discovery/ZkDiscoveryProvider.java | 328 +++++++++++ .../discovery/event/JCPPApplicationEvent.java | 26 + .../event/JCPPApplicationEventListener.java | 54 ++ .../event/OtherServiceShutdownEvent.java | 18 + .../discovery/event/PartitionChangeEvent.java | 45 ++ .../queue/kafka/KafkaAdmin.java | 94 ++++ .../kafka/KafkaConsumerStatisticConfig.java | 29 + .../kafka/KafkaConsumerStatsService.java | 158 ++++++ .../queue/kafka/KafkaConsumerTemplate.java | 117 ++++ .../queue/kafka/KafkaDecoder.java | 16 + .../queue/kafka/KafkaProducerTemplate.java | 133 +++++ .../queue/kafka/KafkaQueueMsgMetadata.java | 17 + .../queue/kafka/KafkaSettings.java | 210 +++++++ .../queue/kafka/KafkaTopicConfigs.java | 32 ++ .../queue/memory/DefaultInMemoryStorage.java | 66 +++ .../queue/memory/InMemoryQueueConsumer.java | 106 ++++ .../queue/memory/InMemoryQueueProducer.java | 48 ++ .../queue/memory/InMemoryStorage.java | 24 + .../queue/processing/IdMsgPair.java | 22 + .../queue/provider/AppQueueFactory.java | 18 + .../provider/InMemoryAppQueueFactory.java | 48 ++ .../queue/provider/KafkaAppQueueFactory.java | 83 +++ .../queue/settings/QueueAppSettings.java | 30 + jcpp-infrastructure-stats/pom.xml | 57 ++ .../infrastructure/stats/DefaultCounter.java | 37 ++ .../stats/DefaultMessagesStats.java | 54 ++ .../stats/DefaultStatsFactory.java | 124 +++++ .../infrastructure/stats/MessagesStats.java | 33 ++ .../infrastructure/stats/StatsCounter.java | 22 + .../infrastructure/stats/StatsFactory.java | 59 ++ .../jcpp/infrastructure/stats/StatsTimer.java | 44 ++ jcpp-infrastructure-util/pom.xml | 81 +++ .../infrastructure/util/JCPPHashUtil.java | 34 ++ .../jcpp/infrastructure/util/JCPPPair.java | 19 + .../jcpp/infrastructure/util/SystemUtil.java | 95 ++++ .../util/annotation/AfterStartUp.java | 28 + .../util/annotation/AppComponent.java | 25 + .../util/annotation/ProtocolComponent.java | 49 ++ .../util/async/JCPPAsynchron.java | 54 ++ .../util/async/JCPPExecutors.java | 27 + .../JCPPForkJoinWorkerThreadFactory.java | 30 + .../util/async/JCPPThreadFactory.java | 46 ++ .../util/async/JCPPVirtualThreadFactory.java | 24 + .../infrastructure/util/codec/BCDUtil.java | 172 ++++++ .../infrastructure/util/codec/ByteUtil.java | 81 +++ .../util/codec/CP56Time2aUtil.java | 65 +++ .../util/config/ConstraintValidator.java | 97 ++++ .../util/config/ShardingThreadPool.java | 82 +++ .../util/config/ThreadPoolConfiguration.java | 43 ++ .../exception/DataValidationException.java | 16 + .../util/exception/DownlinkException.java | 19 + .../IncorrectParameterException.java | 17 + .../util/jackson/BigNumberSerializer.java | 35 ++ .../util/jackson/DataTypeModule.java | 56 ++ .../util/jackson/DateDeserializer.java | 38 ++ .../util/jackson/DateSerializer.java | 32 ++ .../util/jackson/InstantDeserializer.java | 41 ++ .../util/jackson/InstantSerializer.java | 21 + .../util/jackson/JacksonUtil.java | 215 +++++++ .../jackson/LocalDateTimeDeserializer.java | 39 ++ .../util/jackson/LocalDateTimeSerializer.java | 32 ++ .../util/jackson/LocalTimeDeserializer.java | 42 ++ .../util/jackson/LocalTimeSerializer.java | 32 ++ .../jackson/LongTimestampDeserializer.java | 33 ++ .../util/jackson/SqlDateDeserializer.java | 39 ++ .../util/jackson/SqlDateSerializer.java | 33 ++ .../util/jackson/TimestampDeserializer.java | 38 ++ .../util/jackson/TimestampSerializer.java | 33 ++ .../infrastructure/util/mdc/MDCUtils.java | 43 ++ .../util/property/JCPPProperty.java | 14 + .../util/property/PropertyUtils.java | 44 ++ .../util/trace/TraceIdGenerator.java | 69 +++ .../infrastructure/util/trace/Tracer.java | 38 ++ .../util/trace/TracerCallable.java | 41 ++ .../util/trace/TracerContextUtil.java | 71 +++ .../util/trace/TracerRunnable.java | 38 ++ .../util/validation/Length.java | 28 + .../validation/StringLengthValidator.java | 35 ++ .../util/validation/Validator.java | 55 ++ .../util/codec/BCDUtilTest.java | 28 + .../util/codec/CP56Time2aUtilTest.java | 27 + jcpp-protocol-api/pom.xml | 50 ++ .../jcpp/protocol/ProtocolBootstrap.java | 125 +++++ .../jcpp/protocol/ProtocolContext.java | 64 +++ .../protocol/ProtocolMessageProcessor.java | 66 +++ .../protocol/adapter/DownlinkController.java | 78 +++ .../adapter/config/TracerInterceptor.java | 41 ++ .../UndertowServletWebServerCustomizer.java | 24 + .../adapter/config/WebMvcConfiguration.java | 45 ++ .../jcpp/protocol/cfg/ForwarderCfg.java | 24 + .../sanbing/jcpp/protocol/cfg/KafkaCfg.java | 49 ++ .../jcpp/protocol/cfg/ListenerCfg.java | 17 + .../sanbing/jcpp/protocol/cfg/MemoryCfg.java | 18 + .../jcpp/protocol/cfg/ProtocolCfg.java | 25 + .../sanbing/jcpp/protocol/cfg/TcpCfg.java | 45 ++ .../jcpp/protocol/cfg/TcpHandlerCfg.java | 55 ++ .../protocol/cfg/enums/ForwarderType.java | 12 + .../protocol/cfg/enums/TcpHandlerType.java | 14 + .../jcpp/protocol/domain/DownlinkCmdEnum.java | 23 + .../protocol/domain/ListenerToHandlerMsg.java | 11 + .../jcpp/protocol/domain/ProtocolSession.java | 87 +++ .../protocol/domain/ProtocolUplinkMsg.java | 22 + .../protocol/domain/SessionCloseReason.java | 14 + .../protocol/domain/SessionToHandlerMsg.java | 13 + .../jcpp/protocol/forwarder/Forwarder.java | 110 ++++ .../protocol/forwarder/KafkaForwarder.java | 204 +++++++ .../protocol/forwarder/MemoryForwarder.java | 84 +++ .../listener/ChannelHandlerInitializer.java | 142 +++++ .../listener/ChannelHandlerParameter.java | 24 + .../jcpp/protocol/listener/Listener.java | 45 ++ .../listener/tcp/TcpChannelHandler.java | 238 ++++++++ .../protocol/listener/tcp/TcpListener.java | 114 ++++ .../protocol/listener/tcp/TcpSession.java | 90 +++ .../configs/BinaryHandlerConfiguration.java | 75 +++ .../tcp/configs/HandlerConfiguration.java | 37 ++ .../tcp/configs/JsonHandlerConfiguration.java | 22 + .../tcp/configs/TextHandlerConfiguration.java | 32 ++ .../tcp/decoder/JCPPHeadTailFrameDecoder.java | 145 +++++ .../JCPPLengthFieldBasedFrameDecoder.java | 217 ++++++++ .../listener/tcp/decoder/TcpMsgDecoder.java | 51 ++ .../protocol/listener/tcp/enums/ReadAct.java | 15 + .../tcp/enums/SequenceNumberLength.java | 21 + .../tcp/handler/ConnectionLimitHandler.java | 60 ++ .../tcp/handler/IdleEventHandler.java | 35 ++ .../listener/tcp/handler/TracerHandler.java | 25 + .../ProtocolSessionRegistryProvider.java | 30 + .../provider/ProtocolsConfigProvider.java | 15 + ...efaultProtocolSessionRegistryProvider.java | 115 ++++ .../impl/DefaultProtocolsConfigProvider.java | 37 ++ jcpp-protocol-bootstrap/pom.xml | 75 +++ jcpp-protocol-bootstrap/src/layers.xml | 33 ++ .../JCPPProtocolServiceApplication.java | 42 ++ .../src/main/resources/banner.txt | 12 + .../src/main/resources/log4j2.xml | 56 ++ .../src/main/resources/protocol-service.yml | 152 +++++ .../protocol/AbstractProtocolTestBase.java | 38 ++ .../adapter/DownlinkControllerTest.java | 147 +++++ jcpp-protocol-yunkuaichong/pom.xml | 35 ++ .../AbstractYunKuaiChongCmdExe.java | 154 +++++ .../YunKuaiChongDownlinkCmdExe.java | 17 + .../YunKuaiChongDwonlinkMessage.java | 40 ++ .../YunKuaiChongUplinkCmdExe.java | 33 ++ .../YunKuaiChongUplinkMessage.java | 51 ++ .../annotation/YunKuaiChongCmd.java | 19 + ...nKuaiChongV15ProtocolMessageProcessor.java | 224 ++++++++ .../YunkuaichongV150ProtocolBootstrap.java | 45 ++ .../YunKuaiChongV150BmsHandshakeULCmd.java | 118 ++++ .../cmd/YunKuaiChongV150HeartbeatULCmd.java | 78 +++ .../cmd/YunKuaiChongV150LoginAckDLCmd.java | 118 ++++ .../v150/cmd/YunKuaiChongV150LoginULCmd.java | 77 +++ ...uaiChongV150QueryPricingModelAckDLCmd.java | 93 ++++ ...unKuaiChongV150QueryPricingModelULCmd.java | 50 ++ .../YunKuaiChongV150RealTimeDataULCmd.java | 236 ++++++++ .../cmd/YunKuaiChongV150RemoteStartDLCmd.java | 73 +++ ...unKuaiChongV150RemoteStartResultULCmd.java | 93 ++++ .../cmd/YunKuaiChongV150RemoteStopDLCmd.java | 49 ++ ...YunKuaiChongV150RemoteStopResultULCmd.java | 82 +++ ...nKuaiChongV150SetPricingModelAckULCmd.java | 60 ++ .../YunKuaiChongV150SetPricingModelDLCmd.java | 91 +++ ...uaiChongV150TransactionRecordAckDLCmd.java | 53 ++ ...unKuaiChongV150TransactionRecordULCmd.java | 290 ++++++++++ ...aiChongV150VerifyPricingModelAckDLCmd.java | 62 +++ ...nKuaiChongV150VerifyPricingModelULCmd.java | 59 ++ .../YunKuaiChongV150DownlinkCmdEnum.java | 38 ++ jcpp-testing/pom.xml | 100 ++++ license-header-template.txt | 2 + pom.xml | 525 ++++++++++++++++++ 297 files changed, 18020 insertions(+), 28 deletions(-) create mode 100644 docker/Dockerfile-App create mode 100644 docker/Dockerfile-Base create mode 100644 docker/Dockerfile-Protocol create mode 100644 docker/docker-compose.kafka.yml create mode 100644 docker/docker-compose.redis-cluster.yml create mode 100644 docker/docker-compose.redis-sentinel.yml create mode 100644 docker/docker-compose.redis-standalone.yml create mode 100644 docker/kafka.env create mode 100644 docker/queue-kafka.env create mode 100644 docker/schema/schema-postgres.sql create mode 100644 docker/start.sh create mode 100644 jcpp-app-bootstrap/pom.xml create mode 100644 jcpp-app-bootstrap/src/layers.xml create mode 100644 jcpp-app-bootstrap/src/main/java/sanbing/jcpp/JCPPServerApplication.java create mode 100644 jcpp-app-bootstrap/src/main/resources/app-service.yml create mode 100644 jcpp-app-bootstrap/src/main/resources/banner.txt create mode 100644 jcpp-app-bootstrap/src/main/resources/log4j2.xml create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/AbstractTestBase.java create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java create mode 100644 jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java create mode 100644 jcpp-app-bootstrap/src/test/resources/app-service-test.properties create mode 100644 jcpp-app/pom.xml create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/DalConfig.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunOptStatusEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderStatusEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderTypeEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OwnerTypeEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileStatusEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/StationStatusEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/UserStatusEnum.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/JsonbTypeHandler.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/UUIDTypeHandler.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/User.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/GunMapper.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/OrderMapper.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/PileMapper.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/UserMapper.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractCachedEntityRepository.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractEntityRepository.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/repository/CachedVersionedEntityRepository.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepository.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepositoryImpl.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheEvictEvent.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheKey.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCaffeineCache.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileRedisCache.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCaffeineCache.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionRedisCache.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/config/DownlinkRestTemplateConfiguration.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppConsumerStats.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppQueueConsumerManager.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerManagerTask.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerTask.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueEvent.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java create mode 100644 jcpp-infrastructure-cache/pom.xml create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheConstants.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecs.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecsMap.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheTransaction.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheValueWrapper.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineCacheTransaction.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineTransactionalCache.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/HasVersion.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPCaffeineCacheConfiguration.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisClusterConfiguration.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisSentinelConfiguration.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisCacheConfiguration.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisSerializer.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisCacheTransaction.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisTransactionalCache.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/SimpleCacheValueWrapper.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/TransactionalCache.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCache.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCacheKey.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java create mode 100644 jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedRedisCache.java create mode 100644 jcpp-infrastructure-proto/pom.xml create mode 100644 jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java create mode 100644 jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java create mode 100644 jcpp-infrastructure-proto/src/main/proto/cluster.proto create mode 100644 jcpp-infrastructure-proto/src/main/proto/protocol.proto create mode 100644 jcpp-infrastructure-queue/pom.xml create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/Callback.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsg.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsgHeaders.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/KafkaQueueMsg.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackCallback.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackProcessingContext.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueAdmin.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueCallback.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueConsumer.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsg.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgHeaders.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgMetadata.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueProducer.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConfig.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/TopicPartitionInfo.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DiscoveryProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DummyDiscoveryProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/PartitionProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/QueueKey.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceType.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ZkDiscoveryProvider.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEvent.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEventListener.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/OtherServiceShutdownEvent.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/PartitionChangeEvent.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaAdmin.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatisticConfig.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatsService.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerTemplate.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaDecoder.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaProducerTemplate.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaQueueMsgMetadata.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaSettings.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaTopicConfigs.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueConsumer.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueProducer.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryStorage.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/AppQueueFactory.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java create mode 100644 jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/settings/QueueAppSettings.java create mode 100644 jcpp-infrastructure-stats/pom.xml create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultCounter.java create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultMessagesStats.java create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/MessagesStats.java create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsCounter.java create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsFactory.java create mode 100644 jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsTimer.java create mode 100644 jcpp-infrastructure-util/pom.xml create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPHashUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPPair.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/SystemUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AfterStartUp.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AppComponent.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/ProtocolComponent.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPAsynchron.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutors.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPForkJoinWorkerThreadFactory.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPThreadFactory.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/BCDUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/ByteUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ConstraintValidator.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ThreadPoolConfiguration.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DataValidationException.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DownlinkException.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/IncorrectParameterException.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/BigNumberSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DataTypeModule.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/JacksonUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LongTimestampDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampDeserializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampSerializer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/JCPPProperty.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/PropertyUtils.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TraceIdGenerator.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/Tracer.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerCallable.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerRunnable.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Length.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/StringLengthValidator.java create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java create mode 100644 jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/BCDUtilTest.java create mode 100644 jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtilTest.java create mode 100644 jcpp-protocol-api/pom.xml create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolContext.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/TracerInterceptor.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/UndertowServletWebServerCustomizer.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/WebMvcConfiguration.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ForwarderCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/KafkaCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ListenerCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/MemoryCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ProtocolCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpHandlerCfg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/ForwarderType.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/TcpHandlerType.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ListenerToHandlerMsg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionCloseReason.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/MemoryForwarder.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/BinaryHandlerConfiguration.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/HandlerConfiguration.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/JsonHandlerConfiguration.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/TextHandlerConfiguration.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPHeadTailFrameDecoder.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPLengthFieldBasedFrameDecoder.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/TcpMsgDecoder.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/ReadAct.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/SequenceNumberLength.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/IdleEventHandler.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/TracerHandler.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolsConfigProvider.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolsConfigProvider.java create mode 100644 jcpp-protocol-bootstrap/pom.xml create mode 100644 jcpp-protocol-bootstrap/src/layers.xml create mode 100644 jcpp-protocol-bootstrap/src/main/java/sanbing/jcpp/protocol/JCPPProtocolServiceApplication.java create mode 100644 jcpp-protocol-bootstrap/src/main/resources/banner.txt create mode 100644 jcpp-protocol-bootstrap/src/main/resources/log4j2.xml create mode 100644 jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml create mode 100644 jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java create mode 100644 jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java create mode 100644 jcpp-protocol-yunkuaichong/pom.xml create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkMessage.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java create mode 100644 jcpp-testing/pom.xml create mode 100644 license-header-template.txt create mode 100644 pom.xml diff --git a/.gitignore b/.gitignore index a1c2a23..c7b1a41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ # Compiled class file *.class +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ # Log file *.log @@ -21,3 +26,31 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/README.md b/README.md index b255b63..b7a044a 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,27 @@ # JChargePointProtocol -#### 介绍 -JAVA 充电桩协议库 +###### 一个高性能、分布式、支持海量并发量的充电桩JAVA服务端,计划支持100种协议,为充电应用提供基础能力。 -#### 软件架构 -软件架构说明 +

+ + GitHub License + + + Static Badge + +

- -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx +------------------------------ #### 参与贡献 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 +4. 加入社群 +5. 新建 Pull Request -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) + \ No newline at end of file diff --git a/docker/Dockerfile-App b/docker/Dockerfile-App new file mode 100644 index 0000000..144da60 --- /dev/null +++ b/docker/Dockerfile-App @@ -0,0 +1,40 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/jcpp-base:latest AS base +WORKDIR /app +COPY . . +RUN mvn -U -B -T 0.8C clean install -DskipTests + +#分层 +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/openjdk:21-jdk-slim-bullseye AS builder +WORKDIR /app +COPY --from=base /app/jcpp-app-bootstrap/target/application.jar application.jar +RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted + +# 执行 +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/openjdk:21-jdk-slim-bullseye +WORKDIR /app +COPY --from=builder /app/extracted/dependencies/ ./ +COPY --from=builder /app/extracted/spring-boot-loader/ ./ +COPY --from=builder /app/extracted/snapshot-dependencies/ ./ +COPY --from=builder /app/extracted/application/ ./ +COPY --from=base /app/jcpp-app-bootstrap/target/conf ./config +COPY --from=base /app/docker/start.sh . + +RUN mkdir -p /var/log/sanbing && \ + mkdir -p /var/log/sanbing/jcpp && \ + mkdir -p /var/log/sanbing/accesslog && \ + mkdir -p /var/log/sanbing/gc && \ + mkdir -p /var/log/sanbing/heapdump && \ + chmod 700 -R /var/log/* + +RUN chmod a+x *.sh && mv start.sh /usr/bin + +EXPOSE 8080 8080 + +CMD ["start.sh"] + + diff --git a/docker/Dockerfile-Base b/docker/Dockerfile-Base new file mode 100644 index 0000000..770e4e3 --- /dev/null +++ b/docker/Dockerfile-Base @@ -0,0 +1,11 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/mvn:3.9.9-jdk21 AS base +WORKDIR /app +COPY . . +RUN mvn -U -B -T 0.8C clean install -DskipTests +RUN rm -rf /app + diff --git a/docker/Dockerfile-Protocol b/docker/Dockerfile-Protocol new file mode 100644 index 0000000..7aa342a --- /dev/null +++ b/docker/Dockerfile-Protocol @@ -0,0 +1,40 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/jcpp-base:latest AS base +WORKDIR /app +COPY . . +RUN mvn -U -B -T 0.8C clean install -DskipTests + +#分层 +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/openjdk:21-jdk-slim-bullseye AS builder +WORKDIR /app +COPY --from=base /app/jcpp-protocol-bootstrap/target/application.jar application.jar +RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted + +# 执行 +FROM registry.cn-hangzhou.aliyuncs.com/sanbing/openjdk:21-jdk-slim-bullseye +WORKDIR /app +COPY --from=builder /app/extracted/dependencies/ ./ +COPY --from=builder /app/extracted/spring-boot-loader/ ./ +COPY --from=builder /app/extracted/snapshot-dependencies/ ./ +COPY --from=builder /app/extracted/application/ ./ +COPY --from=base /app/jcpp-protocol-bootstrap/target/conf ./config +COPY --from=base /app/docker/start.sh . + +RUN mkdir -p /var/log/sanbing && \ + mkdir -p /var/log/sanbing/jcpp && \ + mkdir -p /var/log/sanbing/accesslog && \ + mkdir -p /var/log/sanbing/gc && \ + mkdir -p /var/log/sanbing/heapdump && \ + chmod 700 -R /var/log/* + +RUN chmod a+x *.sh && mv start.sh /usr/bin + +EXPOSE 8081 8081 + +CMD ["start.sh"] + + diff --git a/docker/docker-compose.kafka.yml b/docker/docker-compose.kafka.yml new file mode 100644 index 0000000..26b3478 --- /dev/null +++ b/docker/docker-compose.kafka.yml @@ -0,0 +1,57 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +networks: + sanbing-network: + driver: bridge + name: sanbing-network + ipam: + config: + - subnet: 10.10.0.0/24 + +services: + zookeeper: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/zookeeper:3.9 + restart: always + networks: + - sanbing-network + ports: + - "2181:2181" + environment: + ALLOW_ANONYMOUS_LOGIN: true + kafka: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/kafka:3.7.1 + restart: always + depends_on: + - zookeeper + networks: + - sanbing-network + ports: + - "9092:9092" + env_file: + - kafka.env + kafka-exporter: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/kafka-exporter:latest + restart: always + depends_on: + - kafka + networks: + - sanbing-network + ports: + - "9308:9308" + command: + - '--kafka.server=kafka:9092' + # 切换示例项目的队列类型为kafka + example: + restart: always + image: example:latest + depends_on: + - kafka + networks: + - sanbing-network + ports: + - "8080:8080" + env_file: + - queue-kafka.env diff --git a/docker/docker-compose.redis-cluster.yml b/docker/docker-compose.redis-cluster.yml new file mode 100644 index 0000000..eea1dfe --- /dev/null +++ b/docker/docker-compose.redis-cluster.yml @@ -0,0 +1,90 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +networks: + sanbing-network: + driver: bridge + name: sanbing-network + ipam: + config: + - subnet: 10.10.0.0/24 + +services: +# Redis cluster + redis-node-0: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-cluster:7.4 + restart: always + networks: + - sanbing-network + environment: + - 'REDIS_PASSWORD=sanbing' + - 'REDISCLI_AUTH=sanbing' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-1: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-cluster:7.4 + restart: always + networks: + - sanbing-network + depends_on: + - redis-node-0 + environment: + - 'REDIS_PASSWORD=sanbing' + - 'REDISCLI_AUTH=sanbing' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-2: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-cluster:7.4 + restart: always + networks: + - sanbing-network + depends_on: + - redis-node-1 + environment: + - 'REDIS_PASSWORD=sanbing' + - 'REDISCLI_AUTH=sanbing' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-3: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-cluster:7.4 + restart: always + networks: + - sanbing-network + depends_on: + - redis-node-2 + environment: + - 'REDIS_PASSWORD=sanbing' + - 'REDISCLI_AUTH=sanbing' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-4: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-cluster:7.4 + restart: always + networks: + - sanbing-network + depends_on: + - redis-node-3 + environment: + - 'REDIS_PASSWORD=sanbing' + - 'REDISCLI_AUTH=sanbing' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + + redis-node-5: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-cluster:7.4 + restart: always + networks: + - sanbing-network + depends_on: + - redis-node-0 + - redis-node-1 + - redis-node-2 + - redis-node-3 + - redis-node-4 + environment: + - 'REDIS_PASSWORD=sanbing' + - 'REDISCLI_AUTH=sanbing' + - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5' + - 'REDIS_CLUSTER_REPLICAS=1' + - 'REDIS_CLUSTER_CREATOR=yes' diff --git a/docker/docker-compose.redis-sentinel.yml b/docker/docker-compose.redis-sentinel.yml new file mode 100644 index 0000000..d769b33 --- /dev/null +++ b/docker/docker-compose.redis-sentinel.yml @@ -0,0 +1,49 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +networks: + sanbing-network: + driver: bridge + name: sanbing-network + ipam: + config: + - subnet: 10.10.0.0/24 + +services: + redis-master: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis:7.4 + restart: always + networks: + - sanbing-network + environment: + - 'REDIS_REPLICATION_MODE=master' + - 'REDIS_PASSWORD=sanbing' + + redis-slave: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis:7.4 + restart: always + networks: + - sanbing-network + environment: + - 'REDIS_REPLICATION_MODE=slave' + - 'REDIS_MASTER_HOST=redis-master' + - 'REDIS_MASTER_PASSWORD=sanbing' + - 'REDIS_PASSWORD=sanbing' + depends_on: + - redis-master + + redis-sentinel: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis-sentinel:7.4 + restart: always + networks: + - sanbing-network + environment: + - 'REDIS_MASTER_HOST=redis-master' + - 'REDIS_MASTER_SET=mymaster' + - 'REDIS_SENTINEL_PASSWORD=sanbing' + - 'REDIS_MASTER_PASSWORD=sanbing' + depends_on: + - redis-master + - redis-slave diff --git a/docker/docker-compose.redis-standalone.yml b/docker/docker-compose.redis-standalone.yml new file mode 100644 index 0000000..8f118a5 --- /dev/null +++ b/docker/docker-compose.redis-standalone.yml @@ -0,0 +1,23 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +networks: + sanbing-network: + driver: bridge + name: sanbing-network + ipam: + config: + - subnet: 10.10.0.0/24 + +services: + redis: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/redis:7.4 + restart: always + networks: + - sanbing-network + ports: + - '6379:6379' + environment: + - 'REDIS_PASSWORD=sanbing' diff --git a/docker/kafka.env b/docker/kafka.env new file mode 100644 index 0000000..08123fd --- /dev/null +++ b/docker/kafka.env @@ -0,0 +1,14 @@ +KAFKA_CFG_NODE_ID=0 +ALLOW_PLAINTEXT_LISTENER=yes +KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 +KAFKA_CFG_LISTENERS=INSIDE://:9093,OUTSIDE://:9092 +KAFKA_CFG_ADVERTISED_LISTENERS=INSIDE://:9093,OUTSIDE://kafka:9092 +KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT +KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true +KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INSIDE +KAFKA_CFG_LOG_RETENTION_BYTES=1073741824 +KAFKA_CFG_SEGMENT_BYTES=268435456 +KAFKA_CFG_LOG_RETENTION_MS=300000 +KAFKA_CFG_LOG_CLEANUP_POLICY=delete + + diff --git a/docker/queue-kafka.env b/docker/queue-kafka.env new file mode 100644 index 0000000..1294636 --- /dev/null +++ b/docker/queue-kafka.env @@ -0,0 +1,2 @@ +QUEUE_TYPE=kafka +KAFKA_SERVERS=kafka:9092 diff --git a/docker/schema/schema-postgres.sql b/docker/schema/schema-postgres.sql new file mode 100644 index 0000000..ee2c0be --- /dev/null +++ b/docker/schema/schema-postgres.sql @@ -0,0 +1,121 @@ +-- +-- 抖音关注:程序员三丙 +-- 知识星球:https://t.zsxq.com/j9b21 +-- + +CREATE TABLE IF NOT EXISTS jcpp_user +( + id uuid not null + constraint owner_pkey + primary key, + created_time timestamp default CURRENT_TIMESTAMP not null, + additional_info jsonb, + status varchar(16) not null, + user_name varchar(255) not null, + user_credentials jsonb not null, + version int default 1 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uni_user_name + on jcpp_user (user_name); + +CREATE TABLE IF NOT EXISTS jcpp_station +( + id uuid not null + constraint station_pkey + primary key, + created_time timestamp default CURRENT_TIMESTAMP not null, + additional_info jsonb, + station_name varchar(255) not null, + station_code varchar(255) not null, + owner_id uuid not null, + longitude double precision not null, + latitude double precision not null, + owner_type varchar(16) not null, + province varchar(255), + city varchar(255), + county varchar(255), + address varchar(255), + status varchar(16) not null, + version int default 1 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uni_station_code + on jcpp_station (station_code); + +CREATE TABLE IF NOT EXISTS jcpp_pile +( + id uuid not null + constraint pile_pkey + primary key, + created_time timestamp default CURRENT_TIMESTAMP not null, + additional_info jsonb, + pile_name varchar(255) not null, + pile_code varchar(255) not null, + protocol varchar(255) not null, + station_id uuid not null, + owner_id uuid not null, + owner_type varchar(16) not null, + brand varchar(255), + model varchar(255), + manufacturer varchar(255), + status varchar(16) not null, + type varchar(16) not null, + version int default 1 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uni_pile_code + on jcpp_pile (pile_code); + + +CREATE TABLE IF NOT EXISTS jcpp_gun +( + id uuid not null + primary key, + created_time timestamp default CURRENT_TIMESTAMP not null, + additional_info varchar(255), + gun_no varchar(255) not null, + gun_name varchar(255) not null, + gun_code varchar(255) not null, + station_id uuid not null, + pile_id uuid not null, + owner_id uuid not null, + owner_type varchar(16) not null, + run_status varchar(16) not null, + run_status_updated_time timestamp not null, + opt_status varchar(16) not null, + version int default 1 +); + +CREATE UNIQUE INDEX IF NOT EXISTS uni_gun_code + on jcpp_gun (gun_code); + +CREATE TABLE IF NOT EXISTS jcpp_order +( + id uuid not null + primary key, + internal_order_no varchar(255) not null, + external_order_no varchar(255) not null, + pile_order_No varchar(255) not null, + created_time timestamp default CURRENT_TIMESTAMP not null, + additional_info jsonb, + updated_time timestamp, + cancelled_time timestamp, + status varchar(16) not null, + type varchar(16) not null, + creator_id uuid not null, + station_id uuid not null, + pile_id uuid not null, + gun_id uuid not null, + plate_no varchar(64), + settlement_amount bigint default 0 not null, + settlement_details jsonb, + electricity_quantity numeric(16, 9) default 0 not null +); + +CREATE UNIQUE INDEX IF NOT EXISTS uni_internal_order_no + on jcpp_order (internal_order_no); + +CREATE UNIQUE INDEX IF NOT EXISTS uni_external_order_no + on jcpp_order (external_order_no); + diff --git a/docker/start.sh b/docker/start.sh new file mode 100644 index 0000000..33ea7fa --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +echo "Starting Server ..." + +export JAVA_APP_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=10 -XX:MaxRAMPercentage=70 \ + -Xlog:gc*,heap*,age*,safepoint=debug:file=/var/log/sanbing/gc/gc.log:time,uptime,level,tags:filecount=10,filesize=10M \ + -XX:+HeapDumpOnOutOfMemoryError \ + -XX:HeapDumpPath=/var/log/sanbing/heapdump/ \ + -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark \ + -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10 \ + -Xss512k -XX:MaxDirectMemorySize=128M -XX:G1ReservePercent=20 \ + -XX:-OmitStackTraceInFastThrow \ + -Dlogging.config=/app/config/log4j2.xml" + +#export JAVA_OPTS_EXTEND="-Xdebug -Xrunjdwp:transport=dt_socket,address=0.0.0.0:8000,server=y,suspend=n" + +exec java $JAVA_APP_OPTS $JAVA_OPTS_EXTEND $JAVA_OPTS -Dnetworkaddress.cache.ttl=60 -jar /app/application.jar diff --git a/jcpp-app-bootstrap/pom.xml b/jcpp-app-bootstrap/pom.xml new file mode 100644 index 0000000..1297db4 --- /dev/null +++ b/jcpp-app-bootstrap/pom.xml @@ -0,0 +1,84 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-app-bootstrap + jar + JChargePointProtocol Application Bootstrap Module + App引导程序 + + + ${basedir}/.. + 3.4.4 + + + + + sanbing + jcpp-app + + + sanbing + jcpp-protocol-yunkuaichong + + + org.testcontainers + junit-jupiter + test + + + + + application + + + org.springframework.boot + spring-boot-maven-plugin + + false + ZIP + sanbing.jcpp.JCPPServerApplication + true + + true + ${project.basedir}/src/layers.xml + + + + + + repackage + build-info + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + diff --git a/jcpp-app-bootstrap/src/layers.xml b/jcpp-app-bootstrap/src/layers.xml new file mode 100644 index 0000000..ebf721a --- /dev/null +++ b/jcpp-app-bootstrap/src/layers.xml @@ -0,0 +1,33 @@ + + + + + + org/springframework/boot/loader/** + + + + + + + + + *:*:*SNAPSHOT + + + + + dependencies + spring-boot-loader + snapshot-dependencies + application + + diff --git a/jcpp-app-bootstrap/src/main/java/sanbing/jcpp/JCPPServerApplication.java b/jcpp-app-bootstrap/src/main/java/sanbing/jcpp/JCPPServerApplication.java new file mode 100644 index 0000000..4631858 --- /dev/null +++ b/jcpp-app-bootstrap/src/main/java/sanbing/jcpp/JCPPServerApplication.java @@ -0,0 +1,39 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp; + +import org.springframework.boot.Banner; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.util.Arrays; + +/** + * @author baigod + */ +@SpringBootApplication +@EnableAsync +@EnableScheduling +public class JCPPServerApplication { + + private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name"; + private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "app-service"; + + public static void main(String[] args) { + new SpringApplicationBuilder(JCPPServerApplication.class).bannerMode(Banner.Mode.LOG).run(updateArguments(args)); + } + + private static String[] updateArguments(String[] args) { + if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) { + String[] modifiedArgs = new String[args.length + 1]; + System.arraycopy(args, 0, modifiedArgs, 0, args.length); + modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM; + return modifiedArgs; + } + return args; + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml new file mode 100644 index 0000000..175b136 --- /dev/null +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -0,0 +1,214 @@ +server: + address: "${HTTP_BIND_ADDRESS:0.0.0.0}" + port: "${HTTP_BIND_PORT:8080}" + undertow: + buffer-size: "${SERVER_UNDERTOW_BUFFER_SIZE:16384}" + directBuffers: "${SERVER_UNDERTOW_DIRECT_BUFFERS:true}" + threads: + io: "${SERVER_UNDERTOW_THREADS_IO:4}" + worker: "${SERVER_UNDERTOW_THREADS_WORKER:128}" + max-http-post-size: "${SERVER_UNDERTOW_MAX_HTTP_POST_SIZE:10MB}" + no-request-timeout: "${SERVER_UNDERTOW_NO_REQUEST_TIMEOUT:10000}" + accesslog: + enabled: true + pattern: "%t %a %r %s (%D ms)" + dir: /var/log/sanbing/accesslog + options: + server: + record-request-start-time: true + +spring: + application: + name: "${SPRING_APPLICATION_NAME:java-charge-point-server}" + datasource: + driver-class-name: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" + url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://10.102.12.102:30135/jcpp}" + username: "${SPRING_DATASOURCE_USERNAME:postgres}" + password: "${SPRING_DATASOURCE_PASSWORD:postgres}" + hikari: + leak-detection-threshold: "${SPRING_DATASOURCE_HIKARI_LEAK_DETECTION_THRESHOLD:0}" + maximum-pool-size: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:16}" + register-mbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}" + +mybatis-plus: + type-handlers-package: sanbing.jcpp.app.dal.config.ibatis.typehandlers + +management: + endpoints: + web: + exposure: + include: '${METRICS_ENDPOINTS_EXPOSE:prometheus,health}' + endpoint: + health: + show-details: always + +metrics: + enabled: "${METRICS_ENABLED:true}" + timer: + percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" + +# 应用程序服务注册中心配置 +zk: + enabled: "${ZOOKEEPER_ENABLED:true}" + url: "${ZOOKEEPER_URL:zookeeper:2181}" + retry-interval-ms: "${ZOOKEEPER_RETRY_INTERVAL_MS:3000}" + connection-timeout-ms: "${ZOOKEEPER_CONNECTION_TIMEOUT_MS:3000}" + session-timeout-ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" + zk-dir: "${ZOOKEEPER_NODES_DIR:/jcpp}" + recalculate-delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" + +# 队列配置 +queue: + # 可选 kafka、memory + type: "${QUEUE_TYPE:memory}" + partitions: + hash_function_name: "${QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + in_memory: + stats: + print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" + kafka: + bootstrap-servers: "${KAFKA_SERVERS:kafka:9092}" + ssl: + enabled: "${KAFKA_SSL_ENABLED:false}" + truststore-location: "${KAFKA_SSL_TRUSTSTORE_LOCATION:}" + truststore-password: "${KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + keystore-location: "${KAFKA_SSL_KEYSTORE_LOCATION:}" + keystore-password: "${KAFKA_SSL_KEYSTORE_PASSWORD:}" + key-password: "${KAFKA_SSL_KEY_PASSWORD:}" + acks: "${KAFKA_ACKS:1}" + retries: "${KAFKA_RETRIES:1}" + compression-type: "${KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + batch-size: "${KAFKA_BATCH_SIZE:1048576}" + linger-ms: "${KAFKA_LINGER_MS:1}" + max-request-size: "${KAFKA_MAX_REQUEST_SIZE:1048576}" + max-in-flight-requests-per-connection: "${KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + buffer-memory: "${BUFFER_MEMORY:33554432}" + replication-factor: "${QUEUE_KAFKA_REPLICATION_FACTOR:1}" + max-poll-interval-ms: "${QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + max-poll-records: "${QUEUE_KAFKA_MAX_POLL_RECORDS:10240}" + max-partition-fetch-bytes: "${QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + fetch-max-bytes: "${QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" + request-timeout-ms: "${QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" + session-timeout-ms: "${QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" + auto-offset-reset: "${QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" + other-inline: "${QUEUE_KAFKA_OTHER_PROPERTIES:}" + topic-properties: + app: "${QUEUE_KAFKA_APP_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + consumer-stats: + enabled: "${QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" + print-interval-ms: "${QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" + kafka-response-timeout-ms: "${QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" + app: + topic: "${QUEUE_APP_TOPIC:protocol_uplink}" + poll-interval: "${QUEUE_APP_POLL_INTERVAL_MS:5}" + pack-processing-timeout: "${QUEUE_APP_PACK_PROCESSING_TIMEOUT_MS:2000}" + consumer-per-partition: "${QUEUE_APP_CONSUMER_PER_PARTITION:true}" + partitions: "${QUEUE_APP_PARTITIONS:10}" + # 可选 protobuf(推荐)、json,需要跟..forwarder.kafka.encoder保持一致 + decoder: "${QUEUE_APP_DECODER:protobuf}" + stats: + enabled: "${QUEUE_APP_STATS_ENABLED:true}" + print-interval-ms: "${QUEUE_APP_STATS_PRINT_INTERVAL_MS:60000}" + +# 应用程序缓存配置 +cache: + type: "${CACHE_TYPE:caffeine}" # caffeine or redis + specs: + piles: + timeToLiveInMinutes: "${CACHE_SPECS_PILES_TTL:15}" + maxSize: "${CACHE_SPECS_PILES_MAX_SIZE:1000}" + pileSessions: + timeToLiveInMinutes: "${CACHE_SPECS_PILE_SESSIONS_TTL:1440}" + maxSize: "${CACHE_SPECS_PILE_SESSIONS_MAX_SIZE:100000}" + +redis: + connection: + type: "${REDIS_CONNECTION_TYPE:standalone}" + standalone: + host: "${REDIS_HOST:redis}" + port: "${REDIS_PORT:6379}" + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" + clientName: "${REDIS_CLIENT_NAME:standalone}" + commandTimeout: "${REDIS_CLIENT_COMMAND_TIMEOUT:30000}" + shutdownTimeout: "${REDIS_CLIENT_SHUTDOWN_TIMEOUT:1000}" + readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}" + usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}" + cluster: + nodes: "${REDIS_NODES:redis-node-0:6379,redis-node-1:6379,redis-node-2:6379,redis-node-3:6379,redis-node-4:6379,redis-node-5:6379}" + max-redirects: "${REDIS_MAX_REDIRECTS:12}" + useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:false}" + sentinel: + master: "${REDIS_MASTER:mymaster}" + sentinels: "${REDIS_SENTINELS:redis-sentinel:26379}" + password: "${REDIS_SENTINEL_PASSWORD:sanbing}" + useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:false}" + db: "${REDIS_DB:0}" + password: "${REDIS_PASSWORD:sanbing}" + pool_config: + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:false}" + testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:false}" + testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" + evictTtlInMs: "${REDIS_EVICT_TTL_MS:60000}" + +service: + # 服务类型:纯协议解析前置 - protocol,纯应用后端 - app,单体服务(包含protocol和app) - monolith + type: "${SERVICE_TYPE:monolith}" + # 可自定义的服务ID,如果不指定,则默认为HOSTNAME + id: "${SERVICE_ID:}" + protocols: + sessions: + default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" + default-state-check-interval-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" + yunkuaichongV150: + enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" + listener: + tcp: + bind-address: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BIND_ADDRESS:0.0.0.0}" + bind-port: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BIND_PORT:38001}" + boss-group-thread_count: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BOSS_GROUP_THREADS:4}" + worker-group-thread-count: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_WORKER_GROUP_THREADS:16}" + so-keep-alive: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_KEEPALIVE:true}" + so-backlog: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_BACKLOG:128}" + so-rcvbuf: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_RCVBUF:65536}" + so-sndbuf: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_SNDBUF:65536}" + nodelay: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_NODELAY:true}" + handler: + idle-timeout-seconds: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_HANDLER_IDLE_TIMEOUT_SECONDS:600}" + max_connections: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_HANDLER_MAX_CONNECTIONS:100000}" + # 默认为二进制类型的拆包器 + # 可选JSON类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV150_NETTY_HANDLER_BINARY_CONFIGURATION:type:JSON}" + # 可选纯文本类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV150_NETTY_HANDLER_BINARY_CONFIGURATION:type:TEXT;maxFrameLength:128;stripDelimiter:true;messageSeparator:null;charsetName:UTF-8}" + configuration: "${PROTOCOLS_YUNKUAICHONGV150_NETTY_HANDLER_BINARY_CONFIGURATION:type:BINARY;decoder:sanbing.jcpp.protocol.listener.tcp.decoder.JCPPLengthFieldBasedFrameDecoder;byteOrder:LITTLE_ENDIAN;head:68;lengthFieldOffset:1;lengthFieldLength:1;lengthAdjustment:2;initialBytesToStrip:0}" + forwarder: + # 如果是单体服务,可选kafka、memory,未来计划扩展RocketMQ, GRpc、REST + type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_TYPE:memory}" + memory: + topic: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_MEMORY_TOPIC:protocol_uplink}" + kafka: + topic: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_TOPIC:protocol_uplink}" + jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 + # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + acks: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ACKS:1}" + # # 可选 protobuf(推荐)、json + encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" + retries: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_RETRIES:1}" + compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + batch-size: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_BATCH_SIZE:16384}" + linger-ms: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_LINGER_MS:0}" + buffer-memory: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_BUFFER_MEMORY:33554432}" + other-properties: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" + +thread-pool: + sharding: + hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:128}" + stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-app-bootstrap/src/main/resources/banner.txt b/jcpp-app-bootstrap/src/main/resources/banner.txt new file mode 100644 index 0000000..0c37cd9 --- /dev/null +++ b/jcpp-app-bootstrap/src/main/resources/banner.txt @@ -0,0 +1,12 @@ + + ___ ________ ________ ________ + |\ \|\ ____\|\ __ \|\ __ \ + \ \ \ \ \___|\ \ \|\ \ \ \|\ \ + __ \ \ \ \ \ \ \ ____\ \ ____\ +|\ \\_\ \ \ \____\ \ \___|\ \ \___| +\ \________\ \_______\ \__\ \ \__\ + \|________|\|_______|\|__| \|__| + +=================================================== +:: ${application.title} :: ${application.formatted-version} +=================================================== \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/main/resources/log4j2.xml b/jcpp-app-bootstrap/src/main/resources/log4j2.xml new file mode 100644 index 0000000..082044b --- /dev/null +++ b/jcpp-app-bootstrap/src/main/resources/log4j2.xml @@ -0,0 +1,56 @@ + + + + + /var/log/sanbing/jcpp + %d{yyyy-MM-dd HH:mm:ss:SSS} [%X{TRACE_ID}] [%t] %p %c{1} %m%n%throwable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/AbstractTestBase.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/AbstractTestBase.java new file mode 100644 index 0000000..42f038e --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/AbstractTestBase.java @@ -0,0 +1,27 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp; + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +/** + * @author baigod + */ +@ActiveProfiles("test") +@SpringBootTest(classes = JCPPServerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class AbstractTestBase { + + static { + System.setProperty("spring.config.name", "app-service"); + } + + protected final Logger log = LoggerFactory.getLogger(this.getClass()); +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java new file mode 100644 index 0000000..592c781 --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java @@ -0,0 +1,72 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import sanbing.jcpp.AbstractTestBase; +import sanbing.jcpp.app.dal.config.ibatis.enums.GunOptStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.GunRunStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; +import sanbing.jcpp.app.dal.entity.Gun; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +import java.time.LocalDateTime; +import java.util.UUID; + +import static sanbing.jcpp.app.dal.mapper.PileMapperTest.NORMAL_PILE_ID; +import static sanbing.jcpp.app.dal.mapper.StationMapperTest.NORMAL_STATION_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; + +/** + * @author baigod + */ +public class GunMapperTest extends AbstractTestBase { + static final UUID[] NORMAL_GUN_ID = new UUID[]{ + UUID.fromString("8f1ffb5b-e536-4f2b-8cd0-31f7d0348a44"), + UUID.fromString("ae256617-b747-4110-b27a-00773e03bed1"), + UUID.fromString("d15dbb29-ea2f-4094-b448-dff853e9275f"), + UUID.fromString("b4a2de24-d7ff-4828-a0d8-2429a6253f9c"), + UUID.fromString("f505f7e2-9e1c-4251-8f7f-9a8eae84372a"), + UUID.fromString("0c5bab7b-786b-4e05-ab26-618c3f5a6086"), + UUID.fromString("2db4ad92-e353-4ac2-a2b0-942cb778eca6"), + UUID.fromString("203833e7-0a44-4f1c-935e-cd43e6dbbf46"), + UUID.fromString("3f3a61e9-de55-4177-9b4e-3a1d8c529890"), + UUID.fromString("cf1a8970-5aa9-4636-a76e-d6bcf98b4a07") + }; + @Resource + GunMapper gunMapper; + + @Test + void curdTest() { + gunMapper.delete(Wrappers.lambdaQuery()); + + for (int i = 0; i < NORMAL_PILE_ID.length; i++) { + UUID pileId = NORMAL_PILE_ID[i]; + UUID gunId = NORMAL_GUN_ID[i]; + Gun gun = Gun.builder() + .id(gunId) + .createdTime(LocalDateTime.now()) + .additionalInfo(JacksonUtil.newObjectNode()) + .gunNo("01") + .gunName("三丙的1号枪") + .gunCode("20231212000001-" + (i + 1)) + .stationId(NORMAL_STATION_ID) + .pileId(pileId) + .ownerId(NORMAL_USER_ID) + .ownerType(OwnerTypeEnum.C) + .runStatus(GunRunStatusEnum.IDLE) + .runStatusUpdatedTime(LocalDateTime.now()) + .optStatus(GunOptStatusEnum.AVAILABLE) + .build(); + + gunMapper.insertOrUpdate(gun); + + log.info("{}", gunMapper.selectById(gunId)); + } + + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java new file mode 100644 index 0000000..eb2a24f --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java @@ -0,0 +1,66 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import cn.hutool.core.math.Money; +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.jupiter.api.Test; +import sanbing.jcpp.AbstractTestBase; +import sanbing.jcpp.app.dal.config.ibatis.enums.OrderStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.OrderTypeEnum; +import sanbing.jcpp.app.dal.entity.Order; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +import static sanbing.jcpp.app.dal.mapper.GunMapperTest.NORMAL_GUN_ID; +import static sanbing.jcpp.app.dal.mapper.PileMapperTest.NORMAL_PILE_ID; +import static sanbing.jcpp.app.dal.mapper.StationMapperTest.NORMAL_STATION_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; + +/** + * @author baigod + */ +public class OrderMapperTest extends AbstractTestBase { + + @Resource + OrderMapper orderMapper; + + @Test + void testOrderMapper() { + orderMapper.delete(Wrappers.lambdaQuery()); + + Order order = Order.builder() + .id(UUID.randomUUID()) + .internalOrderNo(IdUtil.getSnowflake(1, 1).nextIdStr()) + .externalOrderNo(IdUtil.getSnowflake(1, 1).nextIdStr()) + .pileOrderNo(RandomStringUtils.randomNumeric(16)) + .createdTime(LocalDateTime.now()) + .additionalInfo(JacksonUtil.newObjectNode()) + .updatedTime(LocalDateTime.now()) + .cancelledTime(null) + .status(OrderStatusEnum.IN_CHARGING) + .type(OrderTypeEnum.CHARGE) + .creatorId(NORMAL_USER_ID) + .stationId(NORMAL_STATION_ID) + .pileId(NORMAL_PILE_ID[0]) + .gunId(NORMAL_GUN_ID[0]) + .plateNo("浙A88888") + .settlementAmount(new Money(100D).getCent()) + .settlementDetails(JacksonUtil.newObjectNode()) + .electricityQuantity(new BigDecimal("100")) + .build(); + + orderMapper.insertOrUpdate(order); + + log.info("{}", orderMapper.selectById(order.getId())); + + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java new file mode 100644 index 0000000..b3afb7b --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java @@ -0,0 +1,74 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import sanbing.jcpp.AbstractTestBase; +import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.PileStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.PileTypeEnum; +import sanbing.jcpp.app.dal.entity.Pile; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.util.UUID; + +import static sanbing.jcpp.app.dal.mapper.StationMapperTest.NORMAL_STATION_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; + +/** + * @author baigod + */ +public class PileMapperTest extends AbstractTestBase { + static final UUID[] NORMAL_PILE_ID = new UUID[]{ + UUID.fromString("fd7b3f60-db6c-4347-bff3-3c922985b95c"), + UUID.fromString("fa621927-6458-4e09-9666-99c52230db2b"), + UUID.fromString("afec0b0a-ad82-4923-97da-70e4a5d5e2c6"), + UUID.fromString("3e45ae30-2848-4d5a-a7b8-bd8504a6713d"), + UUID.fromString("349ff65e-ce8e-435a-928b-52fdef2828f2"), + UUID.fromString("e60d5b2d-8014-4f8f-b828-e207e6cf4a8f"), + UUID.fromString("8f010829-b505-4e57-8b93-6bdf981ac4e1"), + UUID.fromString("081842e2-9e74-4abb-aeab-b2cbfeb7a335"), + UUID.fromString("f04cf40a-0fbe-40f7-a07c-5b663ad68e98"), + UUID.fromString("ec522751-e1d3-4117-a887-3bdae7892369") + }; + + @Resource + PileMapper pileMapper; + + @Test + void curdTest() { + pileMapper.delete(Wrappers.lambdaQuery()); + + for (int i = 0; i < 10; i++) { + UUID pileId = NORMAL_PILE_ID[i]; + Pile pile = Pile.builder() + .id(pileId) + .createdTime(LocalDateTime.now()) + .additionalInfo(JacksonUtil.newObjectNode()) + .pileName(String.format("三丙家的%d号充电桩", i + 1)) + .pileCode("202312120000" + new DecimalFormat("00").format(i + 1)) + .protocol("yunkuaichongV150") + .stationId(NORMAL_STATION_ID) + .ownerId(NORMAL_USER_ID) + .ownerType(OwnerTypeEnum.C) + .brand("星星") + .model("10A") + .manufacturer("星星") + .status(PileStatusEnum.IDLE) + .type(PileTypeEnum.AC) + .build(); + + pileMapper.insertOrUpdate(pile); + + log.info("{}", pileMapper.selectById(pileId)); + + } + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java new file mode 100644 index 0000000..63a6f02 --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java @@ -0,0 +1,55 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import sanbing.jcpp.AbstractTestBase; +import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.StationStatusEnum; +import sanbing.jcpp.app.dal.entity.Station; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +import java.time.LocalDateTime; +import java.util.UUID; + +import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; + +/** + * @author baigod + */ +class StationMapperTest extends AbstractTestBase { + static final UUID NORMAL_STATION_ID = UUID.fromString("07d80c81-fe99-4a1f-a6aa-dc4d798b5626"); + + @Resource + StationMapper stationMapper; + + @Test + void curdTest() { + stationMapper.delete(Wrappers.lambdaQuery()); + + Station station = Station.builder() + .id(NORMAL_STATION_ID) + .createdTime(LocalDateTime.now()) + .additionalInfo(JacksonUtil.newObjectNode()) + .stationName("三丙家专属充电站") + .stationCode("S20241001001") + .ownerId(NORMAL_USER_ID) + .longitude(120.107936F) + .latitude(30.267014F) + .ownerType(OwnerTypeEnum.C) + .province("浙江省") + .city("杭州市") + .county("西湖区") + .address("西溪路552-1号") + .status(StationStatusEnum.OPERATIONAL) + .build(); + + stationMapper.insertOrUpdate(station); + + log.info("{}", stationMapper.selectById(NORMAL_STATION_ID)); + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java new file mode 100644 index 0000000..be710a4 --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java @@ -0,0 +1,44 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import sanbing.jcpp.AbstractTestBase; +import sanbing.jcpp.app.dal.config.ibatis.enums.UserStatusEnum; +import sanbing.jcpp.app.dal.entity.User; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +import java.time.LocalDateTime; +import java.util.UUID; + +/** + * @author baigod + */ +class UserMapperTest extends AbstractTestBase { + static final UUID NORMAL_USER_ID = UUID.fromString("21cbf909-a23a-4396-840a-f34061f59f95"); + + @Resource + private UserMapper userMapper; + + @Test + void curdTest() { + userMapper.delete(Wrappers.lambdaQuery()); + + User user = User.builder() + .id(NORMAL_USER_ID) + .createdTime(LocalDateTime.now()) + .additionalInfo(JacksonUtil.newObjectNode()) + .status(UserStatusEnum.ENABLE) + .userName("sanbing") + .userCredentials(JacksonUtil.newObjectNode()) + .build(); + + userMapper.insertOrUpdate(user); + + log.info("{}", userMapper.selectById(NORMAL_USER_ID)); + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java new file mode 100644 index 0000000..1c41e34 --- /dev/null +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java @@ -0,0 +1,98 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.springframework.data.redis.core.*; +import sanbing.jcpp.AbstractTestBase; + +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.stream.IntStream; + +class RedisCacheConfigurationTest extends AbstractTestBase { + + @Resource + RedisTemplate redisTemplate; + + @Resource + ReactiveRedisTemplate reactiveRedisTemplate; + + final static int testTimes = 10_000; + final static String hashKey = "hashKey"; + + @Test + @Order(1) + void kvTest() { + ValueOperations valueOperations = redisTemplate.opsForValue(); + + IntStream.range(0, testTimes).forEach(i -> { + String key = "field:" + i; + String value = "value:" + i; + valueOperations.set(key, value, Duration.ofMinutes(1)); + }); + + Object o = valueOperations.get("field:1000"); + System.out.println(Objects.requireNonNull(o).getClass() + " : " + o); + } + + @Test + @Order(2) + void hashTest() { + HashOperations hashOperations = redisTemplate.opsForHash(); + + IntStream.range(0, testTimes).forEach(i -> { + String key = "field:" + i; + String value = "value:" + i; + hashOperations.put(hashKey, key, value); + }); + + redisTemplate.expire(hashKey, Duration.ofMinutes(1)); + + Map slowKey = hashOperations.entries(hashKey); + System.out.println("map size:" + slowKey.size()); + } + + + @Test + @Order(3) + void reactiveKVTest() { + ReactiveValueOperations valueOperations = reactiveRedisTemplate.opsForValue(); + + IntStream.range(0, testTimes).forEach(i -> { + String key = "field:" + i; + String value = "value:" + i; + valueOperations.set(key, value, Duration.ofMinutes(1)).block(); + }); + + Object o = valueOperations.get("field:1000").block(); + System.out.println(Objects.requireNonNull(o).getClass() + " : " + o); + } + + @Test + @Order(4) + void reactiveHashTest() throws InterruptedException { + ReactiveHashOperations hashOperations = reactiveRedisTemplate.opsForHash(); + + IntStream.range(0, testTimes).forEach(i -> { + String key = "field:" + i; + String value = "value:" + i; + hashOperations.put(hashKey, key, value).block(); + }); + + redisTemplate.expire(hashKey, Duration.ofMinutes(1)); + + CountDownLatch latch = new CountDownLatch(1); + hashOperations.entries(hashKey).collectList().subscribe(entries -> { + System.out.println("size:" + entries.size()); + latch.countDown(); + }); + latch.await(); + } +} \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties new file mode 100644 index 0000000..9e61ffc --- /dev/null +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -0,0 +1,2 @@ +redis.connection.type=cluster +redis.cluster.nodes=10.102.12.101:30700,10.102.12.101:32027,10.102.12.101:30767,10.102.12.101:30250,10.102.12.101:30612,10.102.12.101:32303 diff --git a/jcpp-app/pom.xml b/jcpp-app/pom.xml new file mode 100644 index 0000000..03b28dc --- /dev/null +++ b/jcpp-app/pom.xml @@ -0,0 +1,56 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-app + jar + JChargePointProtocol App Module + 应用模块 + + + ${basedir}/.. + + + + + sanbing + jcpp-protocol-api + + + sanbing + jcpp-infrastructure-queue + + + sanbing + jcpp-infrastructure-cache + + + org.postgresql + postgresql + + + org.springframework.boot + spring-boot-starter-jdbc + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + + diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/DalConfig.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/DalConfig.java new file mode 100644 index 0000000..56de407 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/DalConfig.java @@ -0,0 +1,76 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.zaxxer.hikari.HikariDataSource; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; + +@Configuration +@EnableAutoConfiguration(exclude = {RedisAutoConfiguration.class}) +@MapperScan({"sanbing.jcpp.app.dal.mapper"}) +public class DalConfig { + + @Bean + @ConfigurationProperties("spring.datasource") + public DataSourceProperties dataSourceProperties() { + return new DataSourceProperties(); + } + + @Primary + @ConfigurationProperties(prefix = "spring.datasource.hikari") + @Bean + public DataSource dataSource(@Qualifier("dataSourceProperties") DataSourceProperties dataSourceProperties) { + return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); + } + + @Primary + @Bean + public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource dataSource) { + return new JdbcTemplate(dataSource); + } + + @Primary + @Bean + public NamedParameterJdbcTemplate namedParameterJdbcTemplate(@Qualifier("dataSource") DataSource dataSource) { + return new NamedParameterJdbcTemplate(dataSource); + } + + @Primary + @Bean + public TransactionTemplate transactionTemplate(DataSourceTransactionManager transactionManager) { + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED); + transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); + return transactionTemplate; + } + + @Bean + @Primary + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL); + mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor); + return mybatisPlusInterceptor; + } + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunOptStatusEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunOptStatusEnum.java new file mode 100644 index 0000000..f3caa6a --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunOptStatusEnum.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum GunOptStatusEnum implements IEnum { + AVAILABLE, // 可用状态 + IN_MAINTENANCE, // 维护中状态 + OUT_OF_SERVICE, // 停用状态 + RESERVED; // 已预约状态 + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java new file mode 100644 index 0000000..d2d0e92 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/GunRunStatusEnum.java @@ -0,0 +1,27 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum GunRunStatusEnum implements IEnum { + IDLE, // 空闲 + INSERTED, // 已插枪 + CHARGING, // 充电中 + CHARGE_COMPLETE, // 充电完成 + DISCHARGE_READY, // 放电准备 + DISCHARGING, // 放电中 + DISCHARGE_COMPLETE, // 放电完成 + RESERVED, // 预约 + FAULT; // 故障 + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderStatusEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderStatusEnum.java new file mode 100644 index 0000000..11aa6d6 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderStatusEnum.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +public enum OrderStatusEnum implements IEnum { + PENDING, + IN_CHARGING, + COMPLETED, + CANCELLED, + TERMINATED, + FAILED, + REFUNDED; + + + @Override + public String getValue() { + return name(); + } + +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderTypeEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderTypeEnum.java new file mode 100644 index 0000000..b72a85d --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OrderTypeEnum.java @@ -0,0 +1,21 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum OrderTypeEnum implements IEnum { + CHARGE, + + DISCHARGE; + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OwnerTypeEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OwnerTypeEnum.java new file mode 100644 index 0000000..7c142b9 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/OwnerTypeEnum.java @@ -0,0 +1,21 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum OwnerTypeEnum implements IEnum { + C, + B, + G; + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileStatusEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileStatusEnum.java new file mode 100644 index 0000000..f8e20a6 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileStatusEnum.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum PileStatusEnum implements IEnum { + IDLE, // 空闲 + WORKING, // 工作中 + FAULT, // 故障 + MAINTENANCE, // 维护中 + OFFLINE, // 离线 + ; + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java new file mode 100644 index 0000000..11a1cef --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/PileTypeEnum.java @@ -0,0 +1,21 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum PileTypeEnum implements IEnum { + AC, // 交流充电桩 + DC, // 直流充电桩 + ; + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/StationStatusEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/StationStatusEnum.java new file mode 100644 index 0000000..665e522 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/StationStatusEnum.java @@ -0,0 +1,26 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum StationStatusEnum implements IEnum { + OPERATIONAL, // 正常运营 + PARTIAL_FAILURE, // 部分故障 + FULLY_LOADED, // 满载 + MAINTENANCE, // 维护中 + CLOSED, // 关闭 + WAITING_FOR_OPEN; // 待开放 + + @Override + public String getValue() { + return name(); + } + + +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/UserStatusEnum.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/UserStatusEnum.java new file mode 100644 index 0000000..6b1ac06 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/enums/UserStatusEnum.java @@ -0,0 +1,20 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author baigod + */ +public enum UserStatusEnum implements IEnum { + ENABLE, + DISABLE; + + @Override + public String getValue() { + return name(); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/JsonbTypeHandler.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/JsonbTypeHandler.java new file mode 100644 index 0000000..85faad5 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/JsonbTypeHandler.java @@ -0,0 +1,40 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.typehandlers; + +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedTypes; +import org.postgresql.util.PGobject; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +import java.lang.reflect.Field; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +@Slf4j +@MappedTypes({JsonNode.class}) +public class JsonbTypeHandler extends JacksonTypeHandler { + + public JsonbTypeHandler(Class type) { + super(type); + } + + public JsonbTypeHandler(Class type, Field field) { + super(type, field); + } + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { + if (ps != null) { + PGobject jsonObject = new PGobject(); + jsonObject.setType("jsonb"); + jsonObject.setValue(JacksonUtil.toString(parameter)); + ps.setObject(i, jsonObject); + } + } +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/UUIDTypeHandler.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/UUIDTypeHandler.java new file mode 100644 index 0000000..df0a08a --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/config/ibatis/typehandlers/UUIDTypeHandler.java @@ -0,0 +1,40 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.config.ibatis.typehandlers; + +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +/** + * mysql UUID 类型转 varchar + */ +public class UUIDTypeHandler extends BaseTypeHandler { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException { + ps.setObject(i, parameter); + } + + @Override + public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException { + return rs.getObject(columnName, UUID.class); + } + + @Override + public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return rs.getObject(columnIndex, UUID.class); + } + + @Override + public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return cs.getObject(columnIndex, UUID.class); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java new file mode 100644 index 0000000..d8c15b8 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Gun.java @@ -0,0 +1,61 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import sanbing.jcpp.app.dal.config.ibatis.enums.GunOptStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.GunRunStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; +import sanbing.jcpp.infrastructure.cache.HasVersion; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.UUID; + + +@Data +@TableName("jcpp_gun") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Gun implements Serializable, HasVersion { + + @TableId(type = IdType.INPUT) + private UUID id; + + private LocalDateTime createdTime; + + private JsonNode additionalInfo; + + private String gunNo; + + private String gunName; + + private String gunCode; + + private UUID stationId; + + private UUID pileId; + + private UUID ownerId; + + private OwnerTypeEnum ownerType; + + private GunRunStatusEnum runStatus; + + private LocalDateTime runStatusUpdatedTime; + + private GunOptStatusEnum optStatus; + + private Integer version; + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java new file mode 100644 index 0000000..e86b1b1 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java @@ -0,0 +1,69 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import sanbing.jcpp.app.dal.config.ibatis.enums.OrderStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.OrderTypeEnum; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + + +@Data +@TableName("jcpp_order") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Order implements Serializable { + + @TableId(type = IdType.INPUT) + private UUID id; + + private String internalOrderNo; + + private String externalOrderNo; + + private String pileOrderNo; + + private LocalDateTime createdTime; + + private JsonNode additionalInfo; + + private LocalDateTime updatedTime; + + private LocalDateTime cancelledTime; + + private OrderStatusEnum status; + + private OrderTypeEnum type; + + private UUID creatorId; + + private UUID stationId; + + private UUID pileId; + + private UUID gunId; + + private String plateNo; + + private Long settlementAmount; + + private JsonNode settlementDetails; + + private BigDecimal electricityQuantity; + + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java new file mode 100644 index 0000000..e70a7ec --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Pile.java @@ -0,0 +1,61 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.PileStatusEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.PileTypeEnum; +import sanbing.jcpp.infrastructure.cache.HasVersion; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +@TableName(value = "jcpp_pile", autoResultMap = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Pile implements Serializable, HasVersion { + + @TableId(type = IdType.INPUT) + private UUID id; + + private LocalDateTime createdTime; + + private JsonNode additionalInfo; + + private String pileName; + + private String pileCode; + + private String protocol; + + private UUID stationId; + + private UUID ownerId; + + private OwnerTypeEnum ownerType; + + private String brand; + + private String model; + + private String manufacturer; + + private PileStatusEnum status; + + private PileTypeEnum type; + + private Integer version; +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java new file mode 100644 index 0000000..e8b9c42 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Station.java @@ -0,0 +1,62 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; +import sanbing.jcpp.app.dal.config.ibatis.enums.StationStatusEnum; +import sanbing.jcpp.infrastructure.cache.HasVersion; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.UUID; + + +@Data +@TableName("jcpp_station") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Station implements Serializable, HasVersion { + + @TableId(type = IdType.INPUT) + private UUID id; + + private LocalDateTime createdTime; + + private JsonNode additionalInfo; + + private String stationName; + + private String stationCode; + + private UUID ownerId; + + private Float longitude; + + private Float latitude; + + private OwnerTypeEnum ownerType; + + private String province; + + private String city; + + private String county; + + private String address; + + private StationStatusEnum status; + + private Integer version; + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/User.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/User.java new file mode 100644 index 0000000..7350bde --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/User.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import sanbing.jcpp.app.dal.config.ibatis.enums.UserStatusEnum; +import sanbing.jcpp.infrastructure.cache.HasVersion; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.UUID; + + +@Data +@TableName("jcpp_user") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class User implements Serializable, HasVersion { + + @TableId(type = IdType.INPUT) + private UUID id; + + private LocalDateTime createdTime; + + private JsonNode additionalInfo; + + private UserStatusEnum status; + + private String userName; + + private JsonNode userCredentials; + + private Integer version; + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/GunMapper.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/GunMapper.java new file mode 100644 index 0000000..b44c31f --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/GunMapper.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import sanbing.jcpp.app.dal.entity.Gun; + +/** + * @author baigod + */ +public interface GunMapper extends BaseMapper { +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/OrderMapper.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/OrderMapper.java new file mode 100644 index 0000000..ce68e25 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/OrderMapper.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import sanbing.jcpp.app.dal.entity.Order; + +/** + * @author baigod + */ +public interface OrderMapper extends BaseMapper { +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/PileMapper.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/PileMapper.java new file mode 100644 index 0000000..78e65ce --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/PileMapper.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; +import sanbing.jcpp.app.dal.entity.Pile; + +/** + * @author baigod + */ +public interface PileMapper extends BaseMapper { + + @Select("SELECT " + + " * " + + "FROM " + + " jcpp_pile " + + "WHERE " + + " pile_code = #{pileCode}") + Pile selectByCode(String pileCode); +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java new file mode 100644 index 0000000..136010b --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/StationMapper.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import sanbing.jcpp.app.dal.entity.Station; + +/** + * @author baigod + */ +public interface StationMapper extends BaseMapper { +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/UserMapper.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/UserMapper.java new file mode 100644 index 0000000..e49d639 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/mapper/UserMapper.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.dal.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import sanbing.jcpp.app.dal.entity.User; + +/** + * @author baigod + */ +public interface UserMapper extends BaseMapper { +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java b/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java new file mode 100644 index 0000000..1dbb5cc --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java @@ -0,0 +1,57 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.data; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.UUID; + +/** + * @author baigod + */ +@Data +public class PileSession implements Serializable { + + private final UUID pileId; + + private final String pileCode; + + private final String protocolName; + + private UUID protocolSessionId; + + private String remoteAddress; + + private String nodeId; + + private String nodeWebapiIpPort; + + public PileSession(UUID pileId, String pileCode, String protocolName) { + this.pileId = pileId; + this.pileCode = pileCode; + this.protocolName = protocolName; + } + + @JsonCreator + public PileSession( + @JsonProperty("pileId") UUID pileId, + @JsonProperty("pileCode") String pileCode, + @JsonProperty("protocolName") String protocolName, + @JsonProperty("protocolSessionId") UUID protocolSessionId, + @JsonProperty("remoteAddress") String remoteAddress, + @JsonProperty("nodeId") String nodeId, + @JsonProperty("nodeWebapiIpPort") String nodeWebapiIpPort) { + this.pileId = pileId; + this.pileCode = pileCode; + this.protocolName = protocolName; + this.protocolSessionId = protocolSessionId; + this.remoteAddress = remoteAddress; + this.nodeId = nodeId; + this.nodeWebapiIpPort = nodeWebapiIpPort; + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractCachedEntityRepository.java b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractCachedEntityRepository.java new file mode 100644 index 0000000..d192f65 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractCachedEntityRepository.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.repository; + +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.io.Serializable; + +public abstract class AbstractCachedEntityRepository extends AbstractEntityRepository { + + protected void publishEvictEvent(E event) { + if (TransactionSynchronizationManager.isActualTransactionActive()) { + eventPublisher.publishEvent(event); + } else { + handleEvictEvent(event); + } + } + + public abstract void handleEvictEvent(E event); + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractEntityRepository.java b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractEntityRepository.java new file mode 100644 index 0000000..4bdf6bb --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/AbstractEntityRepository.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.repository; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; + +@Slf4j +public abstract class AbstractEntityRepository { + + @Resource + protected ApplicationEventPublisher eventPublisher; + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/repository/CachedVersionedEntityRepository.java b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/CachedVersionedEntityRepository.java new file mode 100644 index 0000000..157b928 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/CachedVersionedEntityRepository.java @@ -0,0 +1,19 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.repository; + +import jakarta.annotation.Resource; +import sanbing.jcpp.infrastructure.cache.HasVersion; +import sanbing.jcpp.infrastructure.cache.VersionedCache; +import sanbing.jcpp.infrastructure.cache.VersionedCacheKey; + +import java.io.Serializable; + +public abstract class CachedVersionedEntityRepository extends AbstractCachedEntityRepository { + + @Resource + protected VersionedCache cache; + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepository.java b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepository.java new file mode 100644 index 0000000..183bb0a --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepository.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.repository; + +import sanbing.jcpp.app.dal.entity.Pile; + +/** + * @author baigod + */ +public interface PileRepository { + + Pile findPileByCode(String pileCode); +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepositoryImpl.java b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepositoryImpl.java new file mode 100644 index 0000000..309d383 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/repository/PileRepositoryImpl.java @@ -0,0 +1,47 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.repository; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.event.TransactionalEventListener; +import sanbing.jcpp.app.dal.entity.Pile; +import sanbing.jcpp.app.dal.mapper.PileMapper; +import sanbing.jcpp.app.service.cache.pile.PileCacheEvictEvent; +import sanbing.jcpp.app.service.cache.pile.PileCacheKey; + +import java.util.ArrayList; +import java.util.List; + +import static sanbing.jcpp.infrastructure.util.validation.Validator.validateString; + +/** + * @author baigod + */ +@Repository +@Slf4j +public class PileRepositoryImpl extends CachedVersionedEntityRepository implements PileRepository { + + @Resource + PileMapper pileMapper; + + @TransactionalEventListener(classes = PileCacheEvictEvent.class) + @Override + public void handleEvictEvent(PileCacheEvictEvent event) { + // 如果修改或删除充电桩,需要在这里消费删除事件 + List toEvict = new ArrayList<>(3); + toEvict.add(new PileCacheKey(event.getPileId())); + toEvict.add(new PileCacheKey(event.getPileCode())); + cache.evict(toEvict); + } + + @Override + public Pile findPileByCode(String pileCode) { + validateString(pileCode, code -> "无效的桩编号" + pileCode); + return cache.get(new PileCacheKey(pileCode), + () -> pileMapper.selectByCode(pileCode)); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java new file mode 100644 index 0000000..3dc6c78 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service; + +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; + +/** + * @author baigod + */ +public interface DownlinkCallService { + + void sendDownlinkMessage(DownlinkRestMessage.Builder downlinkMessageBuilder, String pileCode); +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java new file mode 100644 index 0000000..76c34ed --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java @@ -0,0 +1,66 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service; + +import sanbing.jcpp.infrastructure.queue.Callback; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +/** + * @author baigod + */ +public interface PileProtocolService { + /** + * 桩登录 + */ + void pileLogin(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 充电桩心跳 + */ + void heartBeat(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 校验计费模型 + */ + void verifyPricing(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 查询计费策略 + */ + void queryPricing(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 上报电桩运行状态 + */ + void postGunRunStatus(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 上报充电进度 + */ + void postChargingProgress(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 费率下发反馈 + */ + void onSetPricingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 远程启动反馈 + * + * @param uplinkQueueMessage + * @param callback + */ + void onRemoteStartChargingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 远程停止反馈 + */ + void onRemoteStopChargingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 交易记录上报 + */ + void onTransactionRecord(UplinkQueueMessage uplinkQueueMessage, Callback callback); +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheEvictEvent.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheEvictEvent.java new file mode 100644 index 0000000..4948e03 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheEvictEvent.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.pile; + +import lombok.Data; + +import java.util.UUID; + +@Data +public class PileCacheEvictEvent { + + private UUID pileId; + private String pileCode; + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheKey.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheKey.java new file mode 100644 index 0000000..94f7d56 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCacheKey.java @@ -0,0 +1,47 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.pile; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import sanbing.jcpp.infrastructure.cache.VersionedCacheKey; + +import java.io.Serial; +import java.util.Optional; +import java.util.UUID; + +@Getter +@EqualsAndHashCode +@RequiredArgsConstructor +@Builder +public class PileCacheKey implements VersionedCacheKey { + + @Serial + private static final long serialVersionUID = 6366389552842340207L; + + private final UUID pileId; + private final String pileCode; + + public PileCacheKey(UUID pileId) { + this(pileId, null); + } + + public PileCacheKey(String pileCode) { + this(null, pileCode); + } + + @Override + public String toString() { + return Optional.ofNullable(pileId).map(UUID::toString).orElse(pileCode); + } + + @Override + public boolean isVersioned() { + return pileId != null; + } + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCaffeineCache.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCaffeineCache.java new file mode 100644 index 0000000..baa8690 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileCaffeineCache.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.pile; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.dal.entity.Pile; +import sanbing.jcpp.infrastructure.cache.CacheConstants; +import sanbing.jcpp.infrastructure.cache.VersionedCaffeineCache; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("PileCache") +public class PileCaffeineCache extends VersionedCaffeineCache { + + public PileCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.PILE_CACHE); + } + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileRedisCache.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileRedisCache.java new file mode 100644 index 0000000..9209bb7 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/pile/PileRedisCache.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.pile; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.serializer.SerializationException; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.dal.entity.Pile; +import sanbing.jcpp.infrastructure.cache.*; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("PileCache") +public class PileRedisCache extends VersionedRedisCache { + + public PileRedisCache(JCPPRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, LettuceConnectionFactory connectionFactory) { + super(CacheConstants.PILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new JCPPRedisSerializer<>() { + + @Override + public byte[] serialize(Pile pile) throws SerializationException { + return JacksonUtil.writeValueAsBytes(pile); + } + + @Override + public Pile deserialize(PileCacheKey key, byte[] bytes) throws SerializationException { + return JacksonUtil.fromBytes(bytes, Pile.class); + } + }); + } +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java new file mode 100644 index 0000000..e18f0e4 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java @@ -0,0 +1,41 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.session; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.io.Serializable; +import java.util.Optional; +import java.util.UUID; + +/** + * @author baigod + */ +@Getter +@EqualsAndHashCode +@RequiredArgsConstructor +@Builder +public class PileSessionCacheKey implements Serializable { + + private final UUID pileId; + private final String pileCode; + + public PileSessionCacheKey(UUID pileId) { + this(pileId, null); + } + + public PileSessionCacheKey(String pileCode) { + this(null, pileCode); + } + + @Override + public String toString() { + return Optional.ofNullable(pileId).map(UUID::toString).orElse(pileCode); + } + +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCaffeineCache.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCaffeineCache.java new file mode 100644 index 0000000..fec98a3 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCaffeineCache.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.session; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.infrastructure.cache.CacheConstants; +import sanbing.jcpp.infrastructure.cache.CaffeineTransactionalCache; + +/** + * @author baigod + */ +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("PileSessionCache") +public class PileSessionCaffeineCache extends CaffeineTransactionalCache { + + public PileSessionCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.PILE_SESSION_CACHE); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionRedisCache.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionRedisCache.java new file mode 100644 index 0000000..77c193d --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionRedisCache.java @@ -0,0 +1,36 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.cache.session; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.serializer.SerializationException; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.infrastructure.cache.*; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; + +/** + * @author baigod + */ +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("PileSessionCache") +public class PileSessionRedisCache extends RedisTransactionalCache { + + public PileSessionRedisCache(JCPPRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, LettuceConnectionFactory connectionFactory) { + super(CacheConstants.PILE_SESSION_CACHE, cacheSpecsMap, connectionFactory, configuration, new JCPPRedisSerializer<>() { + + @Override + public byte[] serialize(PileSession pileSession) throws SerializationException { + return JacksonUtil.writeValueAsBytes(pileSession); + } + + @Override + public PileSession deserialize(PileSessionCacheKey key, byte[] bytes) throws SerializationException { + return JacksonUtil.fromBytes(bytes, PileSession.class); + } + }); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/config/DownlinkRestTemplateConfiguration.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/config/DownlinkRestTemplateConfiguration.java new file mode 100644 index 0000000..af36e86 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/config/DownlinkRestTemplateConfiguration.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collections; + +/** + * @author baigod + */ +@Configuration +public class DownlinkRestTemplateConfiguration { + + @Bean("downlinkRestTemplate") + public RestTemplate downlinkRestTemplate() { + RestTemplate restTemplate = new RestTemplateBuilder() + .setConnectTimeout(Duration.of(3, ChronoUnit.SECONDS)) + .setReadTimeout(Duration.of(3, ChronoUnit.SECONDS)) + .build(); + restTemplate.setMessageConverters(Collections.singletonList(new ProtobufHttpMessageConverter())); + return restTemplate; + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java new file mode 100644 index 0000000..90c1f76 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java @@ -0,0 +1,93 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.impl; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.app.service.DownlinkCallService; +import sanbing.jcpp.app.service.cache.session.PileSessionCacheKey; +import sanbing.jcpp.infrastructure.cache.CacheValueWrapper; +import sanbing.jcpp.infrastructure.cache.TransactionalCache; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.adapter.DownlinkController; + +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; + +/** + * @author baigod + */ +@Service +@Slf4j +public class DefaultDownlinkCallService implements DownlinkCallService { + + @Resource + RestTemplate downlinkRestTemplate; + + @Resource + ServiceInfoProvider serviceInfoProvider; + + @Resource + DownlinkController downlinkController; + + @Resource + TransactionalCache pileSessionCache; + + @Override + public void sendDownlinkMessage(DownlinkRestMessage.Builder downlinkMessageBuilder, String pileCode) { + if (serviceInfoProvider.isMonolith()) { + + downlinkController.onDownlink(downlinkMessageBuilder.build()) + .setResultHandler(result -> log.info("下行消息发送完成")); + + } else { + try { + CacheValueWrapper pileSessionCacheValueWrapper = pileSessionCache.get(new PileSessionCacheKey(pileCode)); + + if (pileSessionCacheValueWrapper == null) { + log.warn("充电桩会话不存在 {}", pileCode); + return; + } + + PileSession pileSession = pileSessionCacheValueWrapper.get(); + + invokeDownlinkRestApi(downlinkMessageBuilder.build(), pileSession.getNodeWebapiIpPort()); + + + } catch (RestClientException e) { + log.error("下行消息发送异常", e); + } + } + } + + private void invokeDownlinkRestApi(DownlinkRestMessage downlinkRestMessage, String nodeWebapiIpPort) { + HttpHeaders headers = new HttpHeaders(); + headers.add(JCPP_TRACER_ID, TracerContextUtil.getCurrentTracer().getTraceId()); + headers.add(JCPP_TRACER_ORIGIN, TracerContextUtil.getCurrentTracer().getOrigin()); + headers.add(JCPP_TRACER_TS, String.valueOf(TracerContextUtil.getCurrentTracer().getTracerTs())); + headers.setContentType(MediaType.parseMediaType("application/x-protobuf")); + + HttpEntity entity = new HttpEntity<>(downlinkRestMessage, headers); + + try { + ResponseEntity response = downlinkRestTemplate.postForEntity("http://" + nodeWebapiIpPort + "/api/onDownlink", + entity, ResponseEntity.class); + log.info("下行消息发送成功 {}", response); + } catch (RestClientException e) { + log.error("下行消息发送失败 {}", downlinkRestMessage, e); + throw new RuntimeException(e); + } + + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java new file mode 100644 index 0000000..6699c10 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -0,0 +1,264 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.impl; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.dal.entity.Pile; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.app.repository.PileRepository; +import sanbing.jcpp.app.service.DownlinkCallService; +import sanbing.jcpp.app.service.PileProtocolService; +import sanbing.jcpp.app.service.cache.session.PileSessionCacheKey; +import sanbing.jcpp.infrastructure.cache.TransactionalCache; +import sanbing.jcpp.infrastructure.proto.ProtoConverter; +import sanbing.jcpp.infrastructure.proto.model.PricingModel; +import sanbing.jcpp.infrastructure.proto.model.PricingModel.FlagPrice; +import sanbing.jcpp.infrastructure.proto.model.PricingModel.Period; +import sanbing.jcpp.infrastructure.queue.Callback; +import sanbing.jcpp.proto.gen.ProtocolProto.*; +import sanbing.jcpp.protocol.domain.DownlinkCmdEnum; + +import java.time.LocalTime; +import java.util.*; + +import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag.*; +import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelRule.SPLIT_TIME; +import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelType.CHARGE; + +/** + * @author baigod + */ +@Service +@Slf4j +public class DefaultPileProtocolService implements PileProtocolService { + + @Resource + PileRepository pileRepository; + + @Resource + TransactionalCache pileSessionCache; + + @Resource + DownlinkCallService downlinkCallService; + + @Override + public void pileLogin(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到桩登录事件 {}", uplinkQueueMessage); + + LoginRequest loginRequest = uplinkQueueMessage.getLoginRequest(); + + Pile pile = pileRepository.findPileByCode(loginRequest.getPileCode()); + + String pileCode = loginRequest.getPileCode(); + + log.info("查询到充电桩信息 {}", pile); + + // 构造下行回复 + DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, loginRequest.getPileCode()); + downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.LOGIN_ACK.name()); + + if (pile != null) { + // 保存到缓存 + cacheSession(uplinkQueueMessage, pile, + loginRequest.getRemoteAddress(), + loginRequest.getNodeId(), + loginRequest.getNodeWebapiIpPort()); + + downlinkMessageBuilder.setLoginResponse(LoginResponse.newBuilder() + .setSuccess(true) + .setPileCode(loginRequest.getPileCode()) + .build()); + } else { + downlinkMessageBuilder.setLoginResponse(LoginResponse.newBuilder() + .setSuccess(false) + .setPileCode(loginRequest.getPileCode()) + .build()); + } + + downlinkCallService.sendDownlinkMessage(downlinkMessageBuilder, pileCode); + + callback.onSuccess(); + } + + @Override + public void heartBeat(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到桩心跳事件 {}", uplinkQueueMessage); + + HeartBeatRequest heartBeatRequest = uplinkQueueMessage.getHeartBeatRequest(); + + Pile pile = pileRepository.findPileByCode(heartBeatRequest.getPileCode()); + + if (pile != null) { + // 重新保存到缓存 + cacheSession(uplinkQueueMessage, pile, + heartBeatRequest.getRemoteAddress(), + heartBeatRequest.getNodeId(), + heartBeatRequest.getNodeWebapiIpPort()); + } + } + + private void cacheSession(UplinkQueueMessage uplinkQueueMessage, Pile pile, String remoteAddress, String nodeId, String nodeWebapiIpPort) { + PileSession pileSession = new PileSession(pile.getId(), pile.getPileCode(), uplinkQueueMessage.getProtocolName()); + pileSession.setProtocolSessionId(new UUID(uplinkQueueMessage.getSessionIdMSB(), uplinkQueueMessage.getSessionIdLSB())); + pileSession.setRemoteAddress(remoteAddress); + pileSession.setNodeId(nodeId); + pileSession.setNodeWebapiIpPort(nodeWebapiIpPort); + pileSessionCache.put(new PileSessionCacheKey(pile.getId()), pileSession); + pileSessionCache.put(new PileSessionCacheKey(pile.getPileCode()), pileSession); + } + + @Override + public void verifyPricing(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到计费模型验证请求 {}", uplinkQueueMessage); + + VerifyPricingRequest verifyPricingRequest = uplinkQueueMessage.getVerifyPricingRequest(); + String pileCode = verifyPricingRequest.getPileCode(); + + long pricingId = verifyPricingRequest.getPricingId(); + // todo 默认校验成功,后续查库校验 + assert pricingId > 0; + + DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); + downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.VERIFY_PRICING_ACK.name()); + downlinkMessageBuilder.setVerifyPricingResponse(VerifyPricingResponse.newBuilder() + .setSuccess(true) + .setPricingId(pricingId) + .build()); + + downlinkCallService.sendDownlinkMessage(downlinkMessageBuilder, pileCode); + + callback.onSuccess(); + } + + @Override + public void queryPricing(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩计费模型请求 {}", uplinkQueueMessage); + + QueryPricingRequest queryPricingRequest = uplinkQueueMessage.getQueryPricingRequest(); + String pileCode = queryPricingRequest.getPileCode(); + + // TODO 先构造一个通用的计费模型,后续根据业务做库查询 + List periods = new ArrayList<>(); + + periods.add(createPeriod(1, LocalTime.parse("00:00"), LocalTime.parse("06:00"), TOP)); + periods.add(createPeriod(2, LocalTime.parse("06:00"), LocalTime.parse("12:00"), PEAK)); + periods.add(createPeriod(3, LocalTime.parse("12:00"), LocalTime.parse("18:00"), FLAT)); + periods.add(createPeriod(4, LocalTime.parse("18:00"), LocalTime.parse("00:00"), VALLEY)); + + Map flagPriceMap = new HashMap<>(); + flagPriceMap.put(TOP, new FlagPrice(75, 45)); + flagPriceMap.put(PEAK, new FlagPrice(75, 45)); + flagPriceMap.put(FLAT, new FlagPrice(75, 45)); + flagPriceMap.put(VALLEY, new FlagPrice(75, 45)); + + PricingModel model = new PricingModel(); + model.setId(UUID.randomUUID()); + model.setSequenceNumber(1); + model.setPileCode(pileCode); + model.setType(CHARGE); + model.setRule(SPLIT_TIME); + model.setStandardElec(75); + model.setStandardServ(45); + model.setFlagPriceList(flagPriceMap); + model.setPeriodsList(periods); + + // 构造下行计费 + DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); + downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.QUERY_PRICING_ACK.name()); + downlinkMessageBuilder.setQueryPricingResponse(QueryPricingResponse.newBuilder() + .setPileCode(pileCode) + .setPricingId(model.getSequenceNumber()) + .setPricingModel(ProtoConverter.toPricingModel(model)) + .build()); + + downlinkCallService.sendDownlinkMessage(downlinkMessageBuilder, pileCode); + + callback.onSuccess(); + } + + @Override + public void postGunRunStatus(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩上报的电桩状态 {}", uplinkQueueMessage); + + callback.onSuccess(); + } + + @Override + public void postChargingProgress(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩上报的充电进度 {}", uplinkQueueMessage); + + callback.onSuccess(); + } + + @Override + public void onSetPricingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩上费率下发反馈 {}", uplinkQueueMessage); + + callback.onSuccess(); + } + + @Override + public void onRemoteStartChargingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩启动结果反馈 {}", uplinkQueueMessage); + + callback.onSuccess(); + } + + @Override + public void onRemoteStopChargingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩停止结果反馈 {}", uplinkQueueMessage); + + callback.onSuccess(); + } + + @Override + public void onTransactionRecord(UplinkQueueMessage uplinkQueueMessage, Callback callback) { + log.info("接收到充电桩交易记录上报 {}", uplinkQueueMessage); + + // todo 毛都不敢先给个回复 + TransactionRecord transactionRecord = uplinkQueueMessage.getTransactionRecord(); + + String tradeNo = transactionRecord.getTradeNo(); + String pileCode = transactionRecord.getPileCode(); + + // 构造下行计费 + DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); + downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.TRANSACTION_RECORD.name()); + downlinkMessageBuilder.setTransactionRecordAck(TransactionRecordAck.newBuilder() + .setTradeNo(tradeNo) + .setSuccess(true) + .build()); + + downlinkCallService.sendDownlinkMessage(downlinkMessageBuilder, pileCode); + + callback.onSuccess(); + } + + private static Period createPeriod(int sn, LocalTime beginTime, LocalTime endTime, PricingModelFlag flag) { + Period period = new Period(); + period.setSn(sn); + period.setBegin(beginTime); + period.setEnd(endTime); + period.setFlag(flag); + return period; + } + + private DownlinkRestMessage.Builder createDownlinkMessageBuilder(UplinkQueueMessage uplinkQueueMessage, String pileCode) { + UUID messageId = UUID.randomUUID(); + DownlinkRestMessage.Builder builder = DownlinkRestMessage.newBuilder(); + builder.setMessageIdMSB(messageId.getLeastSignificantBits()); + builder.setMessageIdLSB(messageId.getLeastSignificantBits()); + builder.setPileCode(pileCode); + builder.setSessionIdMSB(uplinkQueueMessage.getSessionIdMSB()); + builder.setSessionIdLSB(uplinkQueueMessage.getSessionIdLSB()); + builder.setProtocolName(uplinkQueueMessage.getProtocolName()); + builder.setRequestIdMSB(uplinkQueueMessage.getMessageIdMSB()); + builder.setRequestIdLSB(uplinkQueueMessage.getMessageIdLSB()); + builder.setRequestData(uplinkQueueMessage.getRequestData()); + return builder; + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java new file mode 100644 index 0000000..cff3ebd --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java @@ -0,0 +1,66 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue; + +import jakarta.annotation.PreDestroy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import sanbing.jcpp.infrastructure.queue.discovery.PartitionProvider; +import sanbing.jcpp.infrastructure.queue.discovery.event.JCPPApplicationEventListener; +import sanbing.jcpp.infrastructure.queue.discovery.event.PartitionChangeEvent; +import sanbing.jcpp.infrastructure.util.annotation.AfterStartUp; +import sanbing.jcpp.infrastructure.util.async.JCPPExecutors; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Slf4j +@RequiredArgsConstructor +public abstract class AbstractConsumerService extends JCPPApplicationEventListener { + + protected final PartitionProvider partitionProvider; + protected final ApplicationEventPublisher eventPublisher; + + protected ExecutorService consumersExecutor; + protected ExecutorService mgmtExecutor; + protected ScheduledExecutorService scheduler; + + public void init(String prefix) { + this.consumersExecutor = Executors.newCachedThreadPool(JCPPThreadFactory.forName(prefix + "-consumer")); + this.mgmtExecutor = JCPPExecutors.newWorkStealingPool(getMgmtThreadPoolSize(), prefix + "-mgmt"); + this.scheduler = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName(prefix + "-consumer-scheduler")); + } + + @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) + public void afterStartUp() { + startConsumers(); + } + + protected void startConsumers() { + } + + protected void stopConsumers() { + } + + protected abstract int getMgmtThreadPoolSize(); + + @PreDestroy + public void destroy() { + stopConsumers(); + if (consumersExecutor != null) { + consumersExecutor.shutdownNow(); + } + if (mgmtExecutor != null) { + mgmtExecutor.shutdownNow(); + } + if (scheduler != null) { + scheduler.shutdownNow(); + } + } + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppConsumerStats.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppConsumerStats.java new file mode 100644 index 0000000..3acab87 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppConsumerStats.java @@ -0,0 +1,85 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue; + +import io.micrometer.core.instrument.Timer; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.stats.StatsCounter; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class AppConsumerStats { + public static final String TOTAL_MSGS = "totalMsgs"; + public static final String LOGIN_EVENTS = "loginEvents"; + public static final String HEARTBEAT_EVENTS = "heartBeatEvents"; + public static final String GUN_RUN_STATUS_EVENTS = "gunRunStatusEvents"; + public static final String CHARGING_PROGRESS_EVENTS = "chargingProgressEvents"; + public static final String TRANSACTION_RECORD_EVENTS = "transactionRecordEvents"; + + private final StatsCounter totalCounter; + private final StatsCounter loginCounter; + private final StatsCounter heartBeatCounter; + private final StatsCounter gunRunStatusCounter; + private final StatsCounter chargingProgressCounter; + private final StatsCounter transactionRecordCounter; + private final Timer appConsumerTimer; + + private final List counters = new ArrayList<>(); + + public AppConsumerStats(StatsFactory statsFactory) { + String statsKey = "appConsumer"; + + this.totalCounter = register(statsFactory.createStatsCounter(statsKey, TOTAL_MSGS)); + this.loginCounter = register(statsFactory.createStatsCounter(statsKey, LOGIN_EVENTS)); + this.heartBeatCounter = register(statsFactory.createStatsCounter(statsKey, HEARTBEAT_EVENTS)); + this.gunRunStatusCounter = register(statsFactory.createStatsCounter(statsKey, GUN_RUN_STATUS_EVENTS)); + this.chargingProgressCounter = register(statsFactory.createStatsCounter(statsKey, CHARGING_PROGRESS_EVENTS)); + this.transactionRecordCounter = register(statsFactory.createStatsCounter(statsKey, TRANSACTION_RECORD_EVENTS)); + this.appConsumerTimer = statsFactory.createTimer(statsKey); + } + + private StatsCounter register(StatsCounter counter) { + counters.add(counter); + return counter; + } + + public void log(UplinkQueueMessage msg) { + totalCounter.increment(); + if (msg.hasLoginRequest()) { + loginCounter.increment(); + } else if (msg.hasHeartBeatRequest()) { + heartBeatCounter.increment(); + } else if (msg.hasGunRunStatusProto()) { + gunRunStatusCounter.increment(); + } else if (msg.hasChargingProgressProto()) { + chargingProgressCounter.increment(); + } else if (msg.hasTransactionRecord()) { + transactionRecordCounter.increment(); + } + + appConsumerTimer.record(Duration.ofMillis(System.currentTimeMillis() - TracerContextUtil.getCurrentTracer().getTracerTs())); + } + + public void printStats() { + int total = totalCounter.get(); + if (total > 0) { + StringBuilder stats = new StringBuilder(); + counters.forEach(counter -> { + stats.append(counter.getName()).append(" = [").append(counter.get()).append("] "); + }); + log.info("App Queue Consumer Stats: {}", stats); + } + } + + public void reset() { + counters.forEach(StatsCounter::clear); + } +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppQueueConsumerManager.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppQueueConsumerManager.java new file mode 100644 index 0000000..62b7bff --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AppQueueConsumerManager.java @@ -0,0 +1,296 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue; + +import lombok.Builder; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.queue.QueueConsumer; +import sanbing.jcpp.infrastructure.queue.QueueMsg; +import sanbing.jcpp.infrastructure.queue.common.QueueConfig; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +@Slf4j +public class AppQueueConsumerManager { + + protected final String queueName; + @Getter + protected C config; + protected final MsgPackProcessor msgPackProcessor; + protected final BiFunction> consumerCreator; + protected final ExecutorService consumerExecutor; + protected final ScheduledExecutorService scheduler; + protected final ExecutorService taskExecutor; + + private final Queue tasks = new ConcurrentLinkedQueue<>(); + private final ReentrantLock lock = new ReentrantLock(); + + @Getter + private volatile Set partitions; + protected volatile ConsumerWrapper consumerWrapper; + protected volatile boolean stopped; + + @Builder + public AppQueueConsumerManager(String queueName, C config, + MsgPackProcessor msgPackProcessor, + BiFunction> consumerCreator, + ExecutorService consumerExecutor, + ScheduledExecutorService scheduler, + ExecutorService taskExecutor) { + this.queueName = queueName; + this.config = config; + this.msgPackProcessor = msgPackProcessor; + this.consumerCreator = consumerCreator; + this.consumerExecutor = consumerExecutor; + this.scheduler = scheduler; + this.taskExecutor = taskExecutor; + if (config != null) { + init(config); + } + } + + public void init(C config) { + this.config = config; + if (config.isConsumerPerPartition()) { + this.consumerWrapper = new ConsumerPerPartitionWrapper(); + } else { + this.consumerWrapper = new SingleConsumerWrapper(); + } + log.debug("[{}] Initialized consumer for queue: {}", queueName, config); + } + + public void update(C config) { + addTask(QueueConsumerManagerTask.configUpdate(config)); + } + + public void update(Set partitions) { + addTask(QueueConsumerManagerTask.partitionChange(partitions)); + } + + protected void addTask(QueueConsumerManagerTask todo) { + if (stopped) { + return; + } + tasks.add(todo); + log.info("[{}] Added task: {}", queueName, todo); + tryProcessTasks(); + } + + @SuppressWarnings("unchecked") + private void tryProcessTasks() { + taskExecutor.submit(() -> { + if (lock.tryLock()) { + try { + C newConfig = null; + Set newPartitions = null; + while (!stopped) { + QueueConsumerManagerTask task = tasks.poll(); + if (task == null) { + break; + } + log.info("[{}] Processing task: {}", queueName, task); + + if (task.getEvent() == QueueEvent.PARTITION_CHANGE) { + newPartitions = task.getPartitions(); + } else if (task.getEvent() == QueueEvent.CONFIG_UPDATE) { + newConfig = (C) task.getConfig(); + } else { + processTask(task); + } + } + if (stopped) { + return; + } + if (newConfig != null) { + doUpdate(newConfig); + } + if (newPartitions != null) { + doUpdate(newPartitions); + } + } catch (Exception e) { + log.error("[{}] Failed to process tasks", queueName, e); + } finally { + lock.unlock(); + } + } else { + log.trace("[{}] Failed to acquire lock", queueName); + scheduler.schedule(this::tryProcessTasks, 1, TimeUnit.SECONDS); + } + }); + } + + protected void processTask(QueueConsumerManagerTask task) { + } + + private void doUpdate(C newConfig) { + log.info("[{}] Processing queue update: {}", queueName, newConfig); + var oldConfig = this.config; + this.config = newConfig; + if (log.isTraceEnabled()) { + log.trace("[{}] Old queue configuration: {}", queueName, oldConfig); + log.trace("[{}] New queue configuration: {}", queueName, newConfig); + } + + if (oldConfig == null) { + init(config); + } else if (newConfig.isConsumerPerPartition() != oldConfig.isConsumerPerPartition()) { + consumerWrapper.getConsumers().forEach(QueueConsumerTask::initiateStop); + consumerWrapper.getConsumers().forEach(QueueConsumerTask::awaitCompletion); + + init(config); + if (partitions != null) { + doUpdate(partitions); + } + } else { + log.trace("[{}] Silently applied new config, because consumer-per-partition not changed", queueName); + } + } + + private void doUpdate(Set partitions) { + this.partitions = partitions; + consumerWrapper.updatePartitions(partitions); + } + + private void launchConsumer(QueueConsumerTask consumerTask) { + log.info("[{}] Launching consumer", consumerTask.getKey()); + Future consumerLoop = consumerExecutor.submit(() -> { + JCPPThreadFactory.updateCurrentThreadName(consumerTask.getKey().toString()); + try { + consumerLoop(consumerTask.getConsumer()); + } catch (Throwable e) { + log.error("Failure in consumer loop", e); + } + log.info("[{}] Consumer stopped", consumerTask.getKey()); + }); + consumerTask.setTask(consumerLoop); + } + + private void consumerLoop(QueueConsumer consumer) { + while (!stopped && !consumer.isStopped()) { + try { + List msgs = consumer.poll(config.getPollInterval()); + if (msgs.isEmpty()) { + continue; + } + processMsgs(msgs, consumer, config); + } catch (Exception e) { + if (!consumer.isStopped()) { + log.warn("Failed to process messages from queue", e); + try { + Thread.sleep(config.getPollInterval()); + } catch (InterruptedException e2) { + log.trace("Failed to wait until the server has capacity to handle new requests", e2); + } + } + } + } + if (consumer.isStopped()) { + consumer.unsubscribe(); + } + } + + protected void processMsgs(List msgs, QueueConsumer consumer, C config) throws Exception { + msgPackProcessor.process(msgs, consumer, config); + } + + public void stop() { + log.debug("[{}] Stopping consumers", queueName); + consumerWrapper.getConsumers().forEach(QueueConsumerTask::initiateStop); + stopped = true; + } + + public void awaitStop() { + log.debug("[{}] Waiting for consumers to stop", queueName); + consumerWrapper.getConsumers().forEach(QueueConsumerTask::awaitCompletion); + log.debug("[{}] Unsubscribed and stopped consumers", queueName); + } + + private static String partitionsToString(Collection partitions) { + return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.joining(", ", "[", "]")); + } + + public interface MsgPackProcessor { + void process(List msgs, QueueConsumer consumer, C config) throws Exception; + } + + public interface ConsumerWrapper { + + void updatePartitions(Set partitions); + + Collection> getConsumers(); + + } + + class ConsumerPerPartitionWrapper implements ConsumerWrapper { + private final Map> consumers = new HashMap<>(); + + @Override + public void updatePartitions(Set partitions) { + Set addedPartitions = new HashSet<>(partitions); + addedPartitions.removeAll(consumers.keySet()); + + Set removedPartitions = new HashSet<>(consumers.keySet()); + removedPartitions.removeAll(partitions); + log.info("[{}] Added partitions: {}, removed partitions: {}", queueName, partitionsToString(addedPartitions), partitionsToString(removedPartitions)); + + removedPartitions.forEach((tpi) -> consumers.get(tpi).initiateStop()); + removedPartitions.forEach((tpi) -> consumers.remove(tpi).awaitCompletion()); + + addedPartitions.forEach((tpi) -> { + Integer partitionId = tpi.getPartition().orElse(-1); + String key = queueName + "-" + partitionId; + QueueConsumerTask consumer = new QueueConsumerTask<>(key, () -> consumerCreator.apply(config, partitionId)); + consumers.put(tpi, consumer); + consumer.subscribe(Set.of(tpi)); + launchConsumer(consumer); + }); + } + + @Override + public Collection> getConsumers() { + return consumers.values(); + } + } + + class SingleConsumerWrapper implements ConsumerWrapper { + private QueueConsumerTask consumer; + + @Override + public void updatePartitions(Set partitions) { + log.info("[{}] New partitions: {}", queueName, partitionsToString(partitions)); + if (partitions.isEmpty()) { + if (consumer != null && consumer.isRunning()) { + consumer.initiateStop(); + consumer.awaitCompletion(); + } + consumer = null; + return; + } + + if (consumer == null) { + consumer = new QueueConsumerTask<>(queueName, () -> consumerCreator.apply(config, null)); // no partitionId passed + } + consumer.subscribe(partitions); + if (!consumer.isRunning()) { + launchConsumer(consumer); + } + } + + @Override + public Collection> getConsumers() { + if (consumer == null) { + return Collections.emptyList(); + } + return List.of(consumer); + } + } +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerManagerTask.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerManagerTask.java new file mode 100644 index 0000000..9a26bcf --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerManagerTask.java @@ -0,0 +1,37 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import sanbing.jcpp.infrastructure.queue.common.QueueConfig; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +import java.util.Set; + +@Getter +@ToString +@AllArgsConstructor +public class QueueConsumerManagerTask { + + private final QueueEvent event; + private QueueConfig config; + private Set partitions; + private boolean drainQueue; + + public static QueueConsumerManagerTask delete(boolean drainQueue) { + return new QueueConsumerManagerTask(QueueEvent.DELETE, null, null, drainQueue); + } + + public static QueueConsumerManagerTask configUpdate(QueueConfig config) { + return new QueueConsumerManagerTask(QueueEvent.CONFIG_UPDATE, config, null, false); + } + + public static QueueConsumerManagerTask partitionChange(Set partitions) { + return new QueueConsumerManagerTask(QueueEvent.PARTITION_CHANGE, null, partitions, false); + } + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerTask.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerTask.java new file mode 100644 index 0000000..7e5cad7 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueConsumerTask.java @@ -0,0 +1,78 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.queue.QueueConsumer; +import sanbing.jcpp.infrastructure.queue.QueueMsg; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +@Slf4j +public class QueueConsumerTask { + + @Getter + private final Object key; + private volatile QueueConsumer consumer; + private volatile Supplier> consumerSupplier; + + @Setter + private Future task; + + public QueueConsumerTask(Object key, Supplier> consumerSupplier) { + this.key = key; + this.consumer = null; + this.consumerSupplier = consumerSupplier; + } + + public QueueConsumer getConsumer() { + if (consumer == null) { + synchronized (this) { + if (consumer == null) { + Objects.requireNonNull(consumerSupplier, "consumerSupplier for key [" + key + "] is null"); + consumer = consumerSupplier.get(); + Objects.requireNonNull(consumer, "consumer for key [" + key + "] is null"); + consumerSupplier = null; + } + } + } + return consumer; + } + + public void subscribe(Set partitions) { + log.info("[{}] Subscribing to partitions: {}", key, partitions); + getConsumer().subscribe(partitions); + } + + public void initiateStop() { + log.debug("[{}] Initiating stop", key); + getConsumer().stop(); + } + + public void awaitCompletion() { + log.trace("[{}] Awaiting finish", key); + if (isRunning()) { + try { + task.get(30, TimeUnit.SECONDS); + log.trace("[{}] Awaited finish", key); + } catch (Exception e) { + log.warn("[{}] Failed to await for consumer to stop", key, e); + } + task = null; + } + } + + public boolean isRunning() { + return task != null; + } + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueEvent.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueEvent.java new file mode 100644 index 0000000..0f17161 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/QueueEvent.java @@ -0,0 +1,13 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue; + +import java.io.Serializable; + +public enum QueueEvent implements Serializable { + + PARTITION_CHANGE, CONFIG_UPDATE, DELETE + +} diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java new file mode 100644 index 0000000..e7ab6d0 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java @@ -0,0 +1,236 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.queue.consumer; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationListener; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.service.PileProtocolService; +import sanbing.jcpp.app.service.queue.AbstractConsumerService; +import sanbing.jcpp.app.service.queue.AppConsumerStats; +import sanbing.jcpp.app.service.queue.AppQueueConsumerManager; +import sanbing.jcpp.infrastructure.queue.*; +import sanbing.jcpp.infrastructure.queue.common.QueueConfig; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; +import sanbing.jcpp.infrastructure.queue.discovery.PartitionProvider; +import sanbing.jcpp.infrastructure.queue.discovery.event.PartitionChangeEvent; +import sanbing.jcpp.infrastructure.queue.processing.IdMsgPair; +import sanbing.jcpp.infrastructure.queue.provider.AppQueueFactory; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.infrastructure.util.annotation.AppComponent; +import sanbing.jcpp.infrastructure.util.codec.ByteUtil; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_PREFIX; +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_TS; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ID; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ORIGIN; + + +/** + * @author baigod + */ +@Service +@AppComponent +@Slf4j +public class ProtocolUplinkConsumerService extends AbstractConsumerService implements ApplicationListener { + + @Value("${queue.app.poll-interval}") + private int pollInterval; + @Value("${queue.app.pack-processing-timeout}") + private long packProcessingTimeout; + @Value("${queue.app.consumer-per-partition}") + private boolean consumerPerPartition; + @Value("${queue.app.stats.enabled}") + private boolean statsEnabled; + + private final PileProtocolService pileProtocolService; + + private final AppQueueFactory appQueueFactory; + + private AppQueueConsumerManager, AppQueueConfig> appConsumer; + + private final AppConsumerStats stats; + + public ProtocolUplinkConsumerService(PartitionProvider partitionProvider, + ApplicationEventPublisher eventPublisher, + PileProtocolService pileProtocolService, + AppQueueFactory appQueueFactory, + StatsFactory statsFactory) { + super(partitionProvider, eventPublisher); + this.pileProtocolService = pileProtocolService; + this.appQueueFactory = appQueueFactory; + this.stats = new AppConsumerStats(statsFactory); + } + + @PostConstruct + public void init() { + super.init("jcpp-app"); + + log.info("Initializing Protocol Uplink Messages Queue Subscriptions."); + + this.appConsumer = AppQueueConsumerManager., AppQueueConfig>builder() + .queueName("protocol uplink") + .config(AppQueueConfig.of(consumerPerPartition, pollInterval)) + .msgPackProcessor(this::processMsgs) + .consumerCreator((config, partitionId) -> appQueueFactory.createProtocolUplinkMsgConsumer()) + .consumerExecutor(consumersExecutor) + .scheduler(scheduler) + .taskExecutor(mgmtExecutor) + .build(); + } + + + @PreDestroy + public void destroy() { + super.destroy(); + } + + + @Override + protected void stopConsumers() { + super.stopConsumers(); + appConsumer.stop(); + appConsumer.awaitStop(); + } + + + @Scheduled(fixedDelayString = "${queue.app.stats.print-interval-ms}") + public void printStats() { + if (statsEnabled) { + stats.printStats(); + stats.reset(); + } + } + + private void processMsgs(List> msgs, QueueConsumer> consumer, AppQueueConfig config) throws Exception { + List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList(); + ConcurrentMap> pendingMap = orderedMsgList.stream().collect( + Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg)); + CountDownLatch processingTimeoutLatch = new CountDownLatch(1); + PackProcessingContext> ctx = new PackProcessingContext<>( + processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>()); + PendingMsgHolder pendingMsgHolder = new PendingMsgHolder(); + Future packSubmitFuture = consumersExecutor.submit(new TracerRunnable(() -> + orderedMsgList.forEach((element) -> { + UUID id = element.getUuid(); + ProtoQueueMsg msg = element.getMsg(); + tracer(msg); + log.trace("[{}] Creating main callback for message: {}", id, msg.getValue()); + Callback callback = new PackCallback<>(id, ctx); + try { + UplinkQueueMessage uplinkQueueMsg = msg.getValue(); + pendingMsgHolder.setUplinkQueueMessage(uplinkQueueMsg); + if (uplinkQueueMsg.hasLoginRequest()) { + pileProtocolService.pileLogin(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasHeartBeatRequest()) { + pileProtocolService.heartBeat(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasVerifyPricingRequest()) { + pileProtocolService.verifyPricing(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasQueryPricingRequest()) { + pileProtocolService.queryPricing(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasGunRunStatusProto()) { + pileProtocolService.postGunRunStatus(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasChargingProgressProto()) { + pileProtocolService.postChargingProgress(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasSetPricingResponse()) { + pileProtocolService.onSetPricingResponse(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasRemoteStartChargingResponse()) { + pileProtocolService.onRemoteStartChargingResponse(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasRemoteStopChargingResponse()) { + pileProtocolService.onRemoteStopChargingResponse(uplinkQueueMsg, callback); + } else if(uplinkQueueMsg.hasTransactionRecord()){ + pileProtocolService.onTransactionRecord(uplinkQueueMsg, callback); + }else { + callback.onSuccess(); + } + + if (statsEnabled) { + stats.log(uplinkQueueMsg); + } + } catch (Throwable e) { + log.warn("[{}] Failed to process message: {}", id, msg, e); + callback.onFailure(e); + } + })) + ); + if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) { + if (!packSubmitFuture.isDone()) { + packSubmitFuture.cancel(true); + UplinkQueueMessage lastSubmitMsg = pendingMsgHolder.getUplinkQueueMessage(); + log.warn("Timeout to process message: {}", lastSubmitMsg); + } + if (log.isDebugEnabled()) { + ctx.getAckMap().forEach((id, msg) -> log.debug("[{}] Timeout to process message: {}", id, msg.getValue())); + } + ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue())); + } + consumer.commit(); + } + + private void tracer(ProtoQueueMsg msg) { + Optional.ofNullable(msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ID)) + .map(tracerId -> { + String origin = null; + byte[] tracerOrigin = msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN); + if (tracerOrigin != null) { + origin = ByteUtil.bytesToString(tracerOrigin); + } + + long ts = System.currentTimeMillis(); + byte[] tracerTs = msg.getHeaders().get(MSG_MD_PREFIX + MSG_MD_TS); + if (tracerTs != null) { + ts = ByteUtil.bytesToLong(tracerTs); + } + + return TracerContextUtil.newTracer(ByteUtil.bytesToString(tracerId), origin, ts); + }) + .orElseGet(TracerContextUtil::newTracer); + + MDCUtils.recordTracer(); + } + + @Override + protected int getMgmtThreadPoolSize() { + return Math.max(Runtime.getRuntime().availableProcessors(), 4); + } + + @Override + protected void onJCPPApplicationEvent(PartitionChangeEvent event) { + Set appPartitions = event.getAppPartitions(); + log.info("Subscribing to partitions: {}", appPartitions); + appConsumer.update(appPartitions); + } + + @Data(staticConstructor = "of") + public static class AppQueueConfig implements QueueConfig { + private final boolean consumerPerPartition; + private final int pollInterval; + } + + @Setter + @Getter + private static class PendingMsgHolder { + private volatile UplinkQueueMessage uplinkQueueMessage; + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-cache/pom.xml b/jcpp-infrastructure-cache/pom.xml new file mode 100644 index 0000000..2019e50 --- /dev/null +++ b/jcpp-infrastructure-cache/pom.xml @@ -0,0 +1,51 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-infrastructure-cache + jar + JChargePointProtocol Infrastructure Cache Module + 基础缓存管理模块 + + + ${basedir}/.. + + + + + org.springframework.data + spring-data-redis + + + io.lettuce + lettuce-core + + + org.apache.commons + commons-pool2 + + + sanbing + jcpp-infrastructure-util + + + com.github.ben-manes.caffeine + caffeine + + + + diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheConstants.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheConstants.java new file mode 100644 index 0000000..7e632ae --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheConstants.java @@ -0,0 +1,13 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +public final class CacheConstants { + + public static final String PILE_CACHE = "piles"; + + public static final String PILE_SESSION_CACHE = "pileSessions"; + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecs.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecs.java new file mode 100644 index 0000000..f582448 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecs.java @@ -0,0 +1,13 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.Data; + +@Data +public class CacheSpecs { + private Integer timeToLiveInMinutes; + private Integer maxSize; +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecsMap.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecsMap.java new file mode 100644 index 0000000..e0af10d --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheSpecsMap.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.Data; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +@Configuration +@ConfigurationProperties(prefix = "cache") +@Data +public class CacheSpecsMap { + + @Getter + private Map specs; + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheTransaction.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheTransaction.java new file mode 100644 index 0000000..a90ab57 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheTransaction.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +public interface CacheTransaction { + + void put(K key, V value); + + boolean commit(); + + void rollback(); + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheValueWrapper.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheValueWrapper.java new file mode 100644 index 0000000..f12f122 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CacheValueWrapper.java @@ -0,0 +1,11 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +public interface CacheValueWrapper { + + T get(); + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineCacheTransaction.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineCacheTransaction.java new file mode 100644 index 0000000..c58d79c --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineCacheTransaction.java @@ -0,0 +1,48 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@RequiredArgsConstructor +public class CaffeineCacheTransaction implements CacheTransaction { + @Getter + private final UUID id = UUID.randomUUID(); + private final CaffeineTransactionalCache cache; + @Getter + private final List keys; + @Getter + @Setter + private boolean failed; + + private final Map pendingPuts = new LinkedHashMap<>(); + + @Override + public void put(K key, V value) { + pendingPuts.put(key, value); + } + + @Override + public boolean commit() { + return cache.commit(id, pendingPuts); + } + + @Override + public void rollback() { + cache.rollback(id); + } + + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineTransactionalCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineTransactionalCache.java new file mode 100644 index 0000000..475d64a --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/CaffeineTransactionalCache.java @@ -0,0 +1,180 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +@RequiredArgsConstructor +public abstract class CaffeineTransactionalCache implements TransactionalCache { + + @Getter + protected final String cacheName; + protected final Cache cache; + protected final Lock lock = new ReentrantLock(); + private final Map> objectTransactions = new HashMap<>(); + private final Map> transactions = new HashMap<>(); + + public CaffeineTransactionalCache(CacheManager cacheManager, String cacheName) { + this.cacheName = cacheName; + this.cache = Optional.ofNullable(cacheManager.getCache(cacheName)) + .orElseThrow(() -> new IllegalArgumentException("Cache '" + cacheName + "' is not configured")); + } + + @Override + public CacheValueWrapper get(K key) { + return SimpleCacheValueWrapper.wrap(cache.get(key)); + } + + @Override + public void put(K key, V value) { + lock.lock(); + try { + failAllTransactionsByKey(key); + cache.put(key, value); + } finally { + lock.unlock(); + } + } + + @Override + public void putIfAbsent(K key, V value) { + lock.lock(); + try { + failAllTransactionsByKey(key); + doPutIfAbsent(key, value); + } finally { + lock.unlock(); + } + } + + @Override + public void evict(K key) { + lock.lock(); + try { + failAllTransactionsByKey(key); + doEvict(key); + } finally { + lock.unlock(); + } + } + + @Override + public void evict(Collection keys) { + lock.lock(); + try { + keys.forEach(key -> { + failAllTransactionsByKey(key); + doEvict(key); + }); + } finally { + lock.unlock(); + } + } + + @Override + public void evictOrPut(K key, V value) { + evict(key); + } + + @Override + public CacheTransaction newTransactionForKey(K key) { + return newTransaction(Collections.singletonList(key)); + } + + @Override + public CacheTransaction newTransactionForKeys(List keys) { + return newTransaction(keys); + } + + void doPutIfAbsent(K key, V value) { + cache.putIfAbsent(key, value); + } + + void doEvict(K key) { + cache.evict(key); + } + + CacheTransaction newTransaction(List keys) { + lock.lock(); + try { + var transaction = new CaffeineCacheTransaction<>(this, keys); + var transactionId = transaction.getId(); + for (K key : keys) { + objectTransactions.computeIfAbsent(key, k -> new HashSet<>()).add(transactionId); + } + transactions.put(transactionId, transaction); + return transaction; + } finally { + lock.unlock(); + } + } + + public boolean commit(UUID trId, Map pendingPuts) { + lock.lock(); + try { + var tr = transactions.get(trId); + var success = !tr.isFailed(); + if (success) { + for (K key : tr.getKeys()) { + Set otherTransactions = objectTransactions.get(key); + if (otherTransactions != null) { + for (UUID otherTrId : otherTransactions) { + if (trId == null || !trId.equals(otherTrId)) { + transactions.get(otherTrId).setFailed(true); + } + } + } + } + pendingPuts.forEach(this::doPutIfAbsent); + } + removeTransaction(trId); + return success; + } finally { + lock.unlock(); + } + } + + void rollback(UUID id) { + lock.lock(); + try { + removeTransaction(id); + } finally { + lock.unlock(); + } + } + + private void removeTransaction(UUID id) { + CaffeineCacheTransaction transaction = transactions.remove(id); + if (transaction != null) { + for (var key : transaction.getKeys()) { + Set transactions = objectTransactions.get(key); + if (transactions != null) { + transactions.remove(id); + if (transactions.isEmpty()) { + objectTransactions.remove(key); + } + } + } + } + } + + protected void failAllTransactionsByKey(K key) { + Set transactionsIds = objectTransactions.get(key); + if (transactionsIds != null) { + for (UUID otherTrId : transactionsIds) { + transactions.get(otherTrId).setFailed(true); + } + } + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/HasVersion.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/HasVersion.java new file mode 100644 index 0000000..b6dbf38 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/HasVersion.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +public interface HasVersion { + + Integer getVersion(); + + default void setVersion(Integer version) { + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPCaffeineCacheConfiguration.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPCaffeineCacheConfiguration.java new file mode 100644 index 0000000..e1f37b7 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPCaffeineCacheConfiguration.java @@ -0,0 +1,83 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; +import com.github.benmanes.caffeine.cache.Ticker; +import com.github.benmanes.caffeine.cache.Weigher; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Configuration +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@EnableCaching +@Slf4j +public class JCPPCaffeineCacheConfiguration { + + private final CacheSpecsMap configuration; + + public JCPPCaffeineCacheConfiguration(CacheSpecsMap configuration) { + this.configuration = configuration; + } + + @Bean + public CacheManager cacheManager() { + log.info("Initializing cache: {} specs {}", Arrays.toString(RemovalCause.values()), configuration.getSpecs()); + SimpleCacheManager manager = new SimpleCacheManager(); + if (configuration.getSpecs() != null) { + List caches = + configuration.getSpecs().entrySet().stream() + .map(entry -> buildCache(entry.getKey(), + entry.getValue())) + .collect(Collectors.toList()); + manager.setCaches(caches); + } + + manager.initializeCaches(); + + return manager; + } + + private CaffeineCache buildCache(String name, CacheSpecs cacheSpec) { + + final Caffeine caffeineBuilder + = Caffeine.newBuilder() + .weigher(collectionSafeWeigher()) + .maximumWeight(cacheSpec.getMaxSize()) + .ticker(ticker()); + if (!cacheSpec.getTimeToLiveInMinutes().equals(0)) { + caffeineBuilder.expireAfterWrite(cacheSpec.getTimeToLiveInMinutes(), TimeUnit.MINUTES); + } + return new CaffeineCache(name, caffeineBuilder.build()); + } + + @Bean + public Ticker ticker() { + return Ticker.systemTicker(); + } + + private Weigher collectionSafeWeigher() { + return (Weigher) (key, value) -> { + if (value instanceof Collection) { + return ((Collection) value).size(); + } + return 1; + }; + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisClusterConfiguration.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisClusterConfiguration.java new file mode 100644 index 0000000..217efe8 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisClusterConfiguration.java @@ -0,0 +1,54 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; + +@Configuration +@ConditionalOnExpression("'${cache.type:null}'=='redis' && '${redis.connection.type:null}'=='cluster'") +@Slf4j +public class JCPPJCPPRedisClusterConfiguration extends JCPPRedisCacheConfiguration { + + @Value("${redis.cluster.nodes:}") + private String clusterNodes; + + @Value("${redis.cluster.max-redirects:12}") + private Integer maxRedirects; + + @Value("${redis.cluster.useDefaultPoolConfig:true}") + private boolean useDefaultPoolConfig; + + @Value("${redis.password:}") + private String password; + + + @Override + public LettuceConnectionFactory loadFactory() { + log.info("Initializing Redis Cluster on {}", clusterNodes); + RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(); + clusterConfiguration.setClusterNodes(getNodes(clusterNodes)); + clusterConfiguration.setMaxRedirects(maxRedirects); + clusterConfiguration.setPassword(password); + return new LettuceConnectionFactory(clusterConfiguration, buildClientConfig()); + } + + private LettucePoolingClientConfiguration buildClientConfig() { + + var clientConfigurationBuilder = LettucePoolingClientConfiguration.builder(); + + if (!useDefaultPoolConfig) { + clientConfigurationBuilder + .poolConfig(buildPoolConfig()); + } + return clientConfigurationBuilder + .build(); + } +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisSentinelConfiguration.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisSentinelConfiguration.java new file mode 100644 index 0000000..a08e813 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisSentinelConfiguration.java @@ -0,0 +1,59 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisSentinelConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; + +@Configuration +@ConditionalOnMissingBean(JCPPCaffeineCacheConfiguration.class) +@ConditionalOnProperty(prefix = "redis.connection", value = "type", havingValue = "sentinel") +@Slf4j +public class JCPPJCPPRedisSentinelConfiguration extends JCPPRedisCacheConfiguration { + + @Value("${redis.sentinel.master:}") + private String master; + + @Value("${redis.sentinel.sentinels:}") + private String sentinels; + + @Value("${redis.sentinel.password:}") + private String sentinelPassword; + + @Value("${redis.sentinel.useDefaultPoolConfig:true}") + private boolean useDefaultPoolConfig; + + @Value("${redis.db:}") + private Integer database; + + @Value("${redis.password:}") + private String password; + + @Override + public LettuceConnectionFactory loadFactory() { + log.info("Initializing Redis Sentinel on {}, sentinels: {}", master, sentinels); + RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(); + redisSentinelConfiguration.setMaster(master); + redisSentinelConfiguration.setSentinels(getNodes(sentinels)); + redisSentinelConfiguration.setSentinelPassword(sentinelPassword); + redisSentinelConfiguration.setPassword(password); + redisSentinelConfiguration.setDatabase(database); + return new LettuceConnectionFactory(redisSentinelConfiguration, buildClientConfig()); + } + + private LettucePoolingClientConfiguration buildClientConfig() { + var clientConfigurationBuilder = LettucePoolingClientConfiguration.builder(); + if (!useDefaultPoolConfig) { + clientConfigurationBuilder.poolConfig(buildPoolConfig()); + } + return clientConfigurationBuilder.build(); + } +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java new file mode 100644 index 0000000..bdfd115 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java @@ -0,0 +1,78 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; + +import java.time.Duration; + +@Configuration +@ConditionalOnMissingBean(JCPPCaffeineCacheConfiguration.class) +@ConditionalOnProperty(prefix = "redis.connection", value = "type", havingValue = "standalone") +@Slf4j +public class JCPPJCPPRedisStandaloneConfiguration extends JCPPRedisCacheConfiguration { + + @Value("${redis.standalone.host:localhost}") + private String host; + + @Value("${redis.standalone.port:6379}") + private Integer port; + + @Value("${redis.standalone.clientName:standalone}") + private String clientName; + + @Value("${redis.standalone.commandTimeout:30000}") + private Long commandTimeout; + + @Value("${redis.standalone.shutdownTimeout:5000}") + private Long shutdownTimeout; + + @Value("${redis.standalone.useDefaultClientConfig:true}") + private boolean useDefaultClientConfig; + + @Value("${redis.standalone.usePoolConfig:false}") + private boolean usePoolConfig; + + @Value("${redis.db:0}") + private Integer db; + + @Value("${redis.password:}") + private String password; + + @Override + public LettuceConnectionFactory loadFactory() { + log.info("Initializing Redis Standalone on {}:{}", host, port); + RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(); + standaloneConfiguration.setHostName(host); + standaloneConfiguration.setPort(port); + standaloneConfiguration.setDatabase(db); + standaloneConfiguration.setPassword(password); + return new LettuceConnectionFactory(standaloneConfiguration, buildClientConfig()); + } + + private LettucePoolingClientConfiguration buildClientConfig() { + + var clientConfigurationBuilder = LettucePoolingClientConfiguration.builder(); + + if (!useDefaultClientConfig) { + clientConfigurationBuilder + .clientName(clientName) + .commandTimeout(Duration.ofMillis(commandTimeout)) + .shutdownTimeout(Duration.ofMillis(shutdownTimeout)); + } + + if (usePoolConfig) { + clientConfigurationBuilder.poolConfig(buildPoolConfig()); + } + return clientConfigurationBuilder.build(); + } +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisCacheConfiguration.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisCacheConfiguration.java new file mode 100644 index 0000000..926368d --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisCacheConfiguration.java @@ -0,0 +1,168 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import io.lettuce.core.api.StatefulRedisConnection; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.ConverterRegistry; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisNode; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.format.support.DefaultFormattingConversionService; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +@Configuration +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Data +@Slf4j +public abstract class JCPPRedisCacheConfiguration { + + private static final String COMMA = ","; + private static final String COLON = ":"; + + @Value("${redis.evictTtlInMs:60000}") + private int evictTtlInMs; + + @Value("${redis.pool_config.maxTotal:128}") + private int maxTotal; + + @Value("${redis.pool_config.maxIdle:128}") + private int maxIdle; + + @Value("${redis.pool_config.minIdle:16}") + private int minIdle; + + @Value("${redis.pool_config.testOnBorrow:true}") + private boolean testOnBorrow; + + @Value("${redis.pool_config.testOnReturn:true}") + private boolean testOnReturn; + + @Value("${redis.pool_config.testWhileIdle:true}") + private boolean testWhileIdle; + + @Value("${redis.pool_config.minEvictableMs:60000}") + private long minEvictableMs; + + @Value("${redis.pool_config.evictionRunsMs:30000}") + private long evictionRunsMs; + + @Value("${redis.pool_config.maxWaitMills:60000}") + private long maxWaitMills; + + @Value("${redis.pool_config.numberTestsPerEvictionRun:3}") + private int numberTestsPerEvictionRun; + + @Value("${redis.pool_config.blockWhenExhausted:true}") + private boolean blockWhenExhausted; + + @Bean + public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory(LettuceConnectionFactory loadFactory) { + return loadFactory; + } + + @Bean + public RedisConnectionFactory redisConnectionFactory(LettuceConnectionFactory loadFactory) { + return loadFactory; + } + + @Bean + protected abstract LettuceConnectionFactory loadFactory(); + + @Bean + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { + DefaultFormattingConversionService redisConversionService = new DefaultFormattingConversionService(); + RedisCacheConfiguration.registerDefaultConverters(redisConversionService); + registerDefaultConverters(redisConversionService); + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig().withConversionService(redisConversionService); + return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(configuration) + .transactionAware() + .build(); + } + + @Bean + public ReactiveRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) { + RedisSerializationContext serializationContext = RedisSerializationContext + .newSerializationContext() + .key(new StringRedisSerializer()) + .value(new GenericJackson2JsonRedisSerializer()) + .hashKey(new StringRedisSerializer()) + .hashValue(new GenericJackson2JsonRedisSerializer()) + .build(); + + return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, serializationContext); + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); + template.setConnectionFactory(redisConnectionFactory); + return template; + } + + private static void registerDefaultConverters(ConverterRegistry registry) { + Assert.notNull(registry, "ConverterRegistry must not be null!"); + registry.addConverter(UUID.class, String.class, UUID::toString); + } + + protected GenericObjectPoolConfig> buildPoolConfig() { + GenericObjectPoolConfig> poolConfig = new GenericObjectPoolConfig<>(); + poolConfig.setMaxTotal(maxTotal); + poolConfig.setMaxIdle(maxIdle); + poolConfig.setMinIdle(minIdle); + poolConfig.setTestOnBorrow(testOnBorrow); + poolConfig.setTestOnReturn(testOnReturn); + poolConfig.setTestWhileIdle(testWhileIdle); + poolConfig.setSoftMinEvictableIdleDuration(Duration.ofMillis(minEvictableMs)); + poolConfig.setTimeBetweenEvictionRuns(Duration.ofMillis(evictionRunsMs)); + poolConfig.setMaxWait(Duration.ofMillis(maxWaitMills)); + poolConfig.setNumTestsPerEvictionRun(numberTestsPerEvictionRun); + poolConfig.setBlockWhenExhausted(blockWhenExhausted); + return poolConfig; + } + + protected List getNodes(String nodes) { + List result; + if (!StringUtils.hasText(nodes)) { + result = Collections.emptyList(); + } else { + result = new ArrayList<>(); + for (String hostPort : nodes.split(COMMA)) { + String host = hostPort.split(COLON)[0]; + int port = Integer.parseInt(hostPort.split(COLON)[1]); + result.add(new RedisNode(host, port)); + } + } + return result; + } + + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisSerializer.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisSerializer.java new file mode 100644 index 0000000..fd1d3f6 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPRedisSerializer.java @@ -0,0 +1,18 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import org.springframework.data.redis.serializer.SerializationException; +import org.springframework.lang.Nullable; + +public interface JCPPRedisSerializer { + + @Nullable + byte[] serialize(@Nullable T t) throws SerializationException; + + @Nullable + T deserialize(K key, @Nullable byte[] bytes) throws SerializationException; + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisCacheTransaction.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisCacheTransaction.java new file mode 100644 index 0000000..64dbf60 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisCacheTransaction.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.connection.RedisConnection; + +import java.io.Serializable; +import java.util.Objects; + +@Slf4j +@RequiredArgsConstructor +public class RedisCacheTransaction implements CacheTransaction { + + private final RedisTransactionalCache cache; + private final RedisConnection connection; + + @Override + public void put(K key, V value) { + cache.put(key, value, connection); + } + + @Override + public boolean commit() { + try { + var execResult = connection.exec(); + return execResult.stream().anyMatch(Objects::nonNull); + } finally { + connection.close(); + } + } + + @Override + public void rollback() { + try { + connection.discard(); + } finally { + connection.close(); + } + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisTransactionalCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisTransactionalCache.java new file mode 100644 index 0000000..7ba1e8f --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/RedisTransactionalCache.java @@ -0,0 +1,246 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import io.lettuce.core.RedisAsyncCommandsImpl; +import io.lettuce.core.RedisClient; +import io.lettuce.core.cluster.RedisAdvancedClusterAsyncCommandsImpl; +import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.support.NullValue; +import org.springframework.data.redis.connection.RedisClusterNode; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.connection.lettuce.LettuceConnection; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Supplier; + +@Slf4j +public abstract class RedisTransactionalCache implements TransactionalCache { + + static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE); + + @Getter + private final String cacheName; + @Getter + private final LettuceConnectionFactory connectionFactory; + private final RedisSerializer keySerializer = StringRedisSerializer.UTF_8; + private final JCPPRedisSerializer valueSerializer; + protected final Expiration evictExpiration; + protected final Expiration cacheTtl; + protected final boolean cacheEnabled; + + public RedisTransactionalCache(String cacheName, + CacheSpecsMap cacheSpecsMap, + LettuceConnectionFactory connectionFactory, + JCPPRedisCacheConfiguration configuration, + JCPPRedisSerializer valueSerializer) { + this.cacheName = cacheName; + this.connectionFactory = connectionFactory; + this.valueSerializer = valueSerializer; + this.evictExpiration = Expiration.from(configuration.getEvictTtlInMs(), TimeUnit.MILLISECONDS); + this.cacheTtl = Optional.ofNullable(cacheSpecsMap) + .map(CacheSpecsMap::getSpecs) + .map(specs -> specs.get(cacheName)) + .map(CacheSpecs::getTimeToLiveInMinutes) + .filter(ttl -> !ttl.equals(0)) + .map(ttl -> Expiration.from(ttl, TimeUnit.MINUTES)) + .orElseGet(Expiration::persistent); + this.cacheEnabled = Optional.ofNullable(cacheSpecsMap) + .map(CacheSpecsMap::getSpecs) + .map(x -> x.get(cacheName)) + .map(CacheSpecs::getMaxSize) + .map(size -> size > 0) + .orElse(false); + } + + @Override + public CacheValueWrapper get(K key) { + if (!cacheEnabled) { + return null; + } + try (var connection = connectionFactory.getConnection()) { + byte[] rawValue = doGet(key, connection); + if (rawValue == null || rawValue.length == 0) { + return null; + } else if (Arrays.equals(rawValue, BINARY_NULL_VALUE)) { + return SimpleCacheValueWrapper.empty(); + } else { + V value = valueSerializer.deserialize(key, rawValue); + return SimpleCacheValueWrapper.wrap(value); + } + } + } + + protected byte[] doGet(K key, RedisConnection connection) { + return connection.stringCommands().get(getRawKey(key)); + } + + @Override + public void put(K key, V value) { + if (!cacheEnabled) { + return; + } + try (var connection = connectionFactory.getConnection()) { + put(key, value, connection); + } + } + + public void put(K key, V value, RedisConnection connection) { + put(connection, key, value, RedisStringCommands.SetOption.UPSERT); + } + + @Override + public void putIfAbsent(K key, V value) { + if (!cacheEnabled) { + return; + } + try (var connection = connectionFactory.getConnection()) { + put(connection, key, value, RedisStringCommands.SetOption.SET_IF_ABSENT); + } + } + + @Override + public void evict(K key) { + if (!cacheEnabled) { + return; + } + try (var connection = connectionFactory.getConnection()) { + connection.keyCommands().del(getRawKey(key)); + } + } + + @Override + public void evict(Collection keys) { + if (!cacheEnabled) { + return; + } + if (keys.isEmpty()) { + return; + } + try (var connection = connectionFactory.getConnection()) { + connection.keyCommands().del(keys.stream().map(this::getRawKey).toArray(byte[][]::new)); + } + } + + @Override + public void evictOrPut(K key, V value) { + if (!cacheEnabled) { + return; + } + try (var connection = connectionFactory.getConnection()) { + var rawKey = getRawKey(key); + var records = connection.keyCommands().del(rawKey); + if (records == null || records == 0) { + //We need to put the value in case of Redis, because evict will NOT cancel concurrent transaction used to "get" the missing value from cache. + connection.stringCommands().set(rawKey, getRawValue(value), evictExpiration, RedisStringCommands.SetOption.UPSERT); + } + } + } + + @Override + public CacheTransaction newTransactionForKey(K key) { + byte[][] rawKey = new byte[][]{getRawKey(key)}; + RedisConnection connection = watch(rawKey); + return new RedisCacheTransaction<>(this, connection); + } + + @Override + public CacheTransaction newTransactionForKeys(List keys) { + RedisConnection connection = watch(keys.stream().map(this::getRawKey).toArray(byte[][]::new)); + return new RedisCacheTransaction<>(this, connection); + } + + @Override + public R getAndPutInTransaction(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue) { + if (!cacheEnabled) { + return dbCall.get(); + } + return TransactionalCache.super.getAndPutInTransaction(key, dbCall, cacheValueToResult, dbValueToCacheValue, cacheNullValue); + } + + @SuppressWarnings("unchecked") + protected RedisConnection getConnection(byte[] rawKey) { + if (!connectionFactory.isClusterAware()) { + return connectionFactory.getConnection(); + } + + RedisClusterNode redisClusterNode = connectionFactory.getClusterConnection().clusterGetNodeForKey(rawKey); + Object nativeConnection = connectionFactory.getConnection().getNativeConnection(); + RedisClusterAsyncCommands connection = ((RedisAdvancedClusterAsyncCommandsImpl) nativeConnection).getConnection(redisClusterNode.getId()); + LettuceConnection lettuceConnection = new LettuceConnection(((RedisAsyncCommandsImpl) connection).getStatefulConnection(), + connectionFactory.getTimeout(), + RedisClient.create()); + lettuceConnection.setConvertPipelineAndTxResults(connectionFactory.getConvertPipelineAndTxResults()); + return lettuceConnection; + } + + protected RedisConnection watch(byte[][] rawKeysList) { + RedisConnection connection = getConnection(rawKeysList[0]); + try { + connection.watch(rawKeysList); + connection.multi(); + } catch (Exception e) { + connection.close(); + throw e; + } + return connection; + } + + protected byte[] getRawKey(K key) { + String keyString = cacheName + key.toString(); + byte[] rawKey; + try { + rawKey = keySerializer.serialize(keyString); + } catch (Exception e) { + log.warn("Failed to serialize the cache key: {}", key, e); + throw new RuntimeException(e); + } + if (rawKey == null) { + log.warn("Failed to serialize the cache key: {}", key); + throw new IllegalArgumentException("Failed to serialize the cache key!"); + } + return rawKey; + } + + protected byte[] getRawValue(V value) { + if (value == null) { + return BINARY_NULL_VALUE; + } else { + try { + return valueSerializer.serialize(value); + } catch (Exception e) { + log.warn("Failed to serialize the cache value: {}", value, e); + throw new RuntimeException(e); + } + } + } + + public void put(RedisConnection connection, K key, V value, RedisStringCommands.SetOption setOption) { + if (!cacheEnabled) { + return; + } + byte[] rawKey = getRawKey(key); + put(connection, rawKey, value, setOption); + } + + public void put(RedisConnection connection, byte[] rawKey, V value, RedisStringCommands.SetOption setOption) { + byte[] rawValue = getRawValue(value); + connection.stringCommands().set(rawKey, rawValue, this.cacheTtl, setOption); + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/SimpleCacheValueWrapper.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/SimpleCacheValueWrapper.java new file mode 100644 index 0000000..477f747 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/SimpleCacheValueWrapper.java @@ -0,0 +1,35 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.springframework.cache.Cache; + +@ToString +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class SimpleCacheValueWrapper implements CacheValueWrapper { + + private final T value; + + @Override + public T get() { + return value; + } + + public static SimpleCacheValueWrapper empty() { + return new SimpleCacheValueWrapper<>(null); + } + + public static SimpleCacheValueWrapper wrap(T value) { + return new SimpleCacheValueWrapper<>(value); + } + + @SuppressWarnings("unchecked") + public static SimpleCacheValueWrapper wrap(Cache.ValueWrapper source) { + return source == null ? null : new SimpleCacheValueWrapper<>((T) source.get()); + } +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/TransactionalCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/TransactionalCache.java new file mode 100644 index 0000000..f3b27f0 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/TransactionalCache.java @@ -0,0 +1,86 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +public interface TransactionalCache { + + String getCacheName(); + + CacheValueWrapper get(K key); + + void put(K key, V value); + + void putIfAbsent(K key, V value); + + void evict(K key); + + void evict(Collection keys); + + void evictOrPut(K key, V value); + + CacheTransaction newTransactionForKey(K key); + + + CacheTransaction newTransactionForKeys(List keys); + + default V getOrFetchFromDB(K key, Supplier dbCall, boolean cacheNullValue, boolean putToCache) { + if (putToCache) { + return getAndPutInTransaction(key, dbCall, cacheNullValue); + } else { + CacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + return cacheValueWrapper.get(); + } + return dbCall.get(); + } + } + + default V getAndPutInTransaction(K key, Supplier dbCall, boolean cacheNullValue) { + return getAndPutInTransaction(key, dbCall, Function.identity(), Function.identity(), cacheNullValue); + } + + default R getAndPutInTransaction(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue) { + CacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + V cacheValue = cacheValueWrapper.get(); + return cacheValue != null ? cacheValueToResult.apply(cacheValue) : null; + } + var cacheTransaction = newTransactionForKey(key); + try { + R dbValue = dbCall.get(); + if (dbValue != null || cacheNullValue) { + cacheTransaction.put(key, dbValueToCacheValue.apply(dbValue)); + cacheTransaction.commit(); + return dbValue; + } else { + cacheTransaction.rollback(); + return null; + } + } catch (Throwable e) { + cacheTransaction.rollback(); + throw e; + } + } + + default R getOrFetchFromDB(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue, boolean putToCache) { + if (putToCache) { + return getAndPutInTransaction(key, dbCall, cacheValueToResult, dbValueToCacheValue, cacheNullValue); + } else { + CacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + var cacheValue = cacheValueWrapper.get(); + return cacheValue == null ? null : cacheValueToResult.apply(cacheValue); + } + return dbCall.get(); + } + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCache.java new file mode 100644 index 0000000..2297d4d --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCache.java @@ -0,0 +1,51 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + + +import java.io.Serializable; +import java.util.Collection; +import java.util.Optional; +import java.util.function.Supplier; + +public interface VersionedCache extends TransactionalCache { + + CacheValueWrapper get(K key); + + default V get(K key, Supplier supplier) { + return get(key, supplier, true); + } + + default V get(K key, Supplier supplier, boolean putToCache) { + return Optional.ofNullable(get(key)) + .map(CacheValueWrapper::get) + .orElseGet(() -> { + V value = supplier.get(); + if (putToCache) { + put(key, value); + } + return value; + }); + } + + void put(K key, V value); + + void evict(K key); + + void evict(Collection keys); + + void evict(K key, Integer version); + + default Integer getVersion(V value) { + if (value == null) { + return 0; + } else if (value.getVersion() != null) { + return value.getVersion(); + } else { + return null; + } + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCacheKey.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCacheKey.java new file mode 100644 index 0000000..2529c17 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCacheKey.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import java.io.Serializable; + +public interface VersionedCacheKey extends Serializable { + + default boolean isVersioned() { + return false; + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java new file mode 100644 index 0000000..9e7ede6 --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java @@ -0,0 +1,86 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import sanbing.jcpp.infrastructure.util.JCPPPair; + +import java.io.Serializable; + +public abstract class VersionedCaffeineCache extends CaffeineTransactionalCache implements VersionedCache { + + public VersionedCaffeineCache(CacheManager cacheManager, String cacheName) { + super(cacheManager, cacheName); + } + + @Override + public CacheValueWrapper get(K key) { + JCPPPair versionValuePair = doGet(key); + if (versionValuePair != null) { + return SimpleCacheValueWrapper.wrap(versionValuePair.getSecond()); + } + return null; + } + + @Override + public void put(K key, V value) { + Integer version = getVersion(value); + if (version == null) { + return; + } + doPut(key, value, version); + } + + private void doPut(K key, V value, Integer version) { + lock.lock(); + try { + JCPPPair versionValuePair = doGet(key); + if (versionValuePair == null || version > versionValuePair.getFirst()) { + failAllTransactionsByKey(key); + cache.put(key, wrapValue(value, version)); + } + } finally { + lock.unlock(); + } + } + + private JCPPPair doGet(K key) { + Cache.ValueWrapper source = cache.get(key); + if (source != null && source.get() instanceof JCPPPair pair) { + return pair; + } + return null; + } + + @Override + public void evict(K key) { + lock.lock(); + try { + failAllTransactionsByKey(key); + cache.evict(key); + } finally { + lock.unlock(); + } + } + + @Override + public void evict(K key, Integer version) { + if (version == null) { + return; + } + doPut(key, null, version); + } + + @Override + void doPutIfAbsent(K key, V value) { + cache.putIfAbsent(key, wrapValue(value, getVersion(value))); + } + + private JCPPPair wrapValue(V value, Integer version) { + return JCPPPair.of(version, value); + } + +} diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedRedisCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedRedisCache.java new file mode 100644 index 0000000..bfc5a1e --- /dev/null +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedRedisCache.java @@ -0,0 +1,155 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.cache; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.NotImplementedException; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.ReturnType; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.types.Expiration; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.io.Serializable; +import java.util.Arrays; + +@Slf4j +public abstract class VersionedRedisCache extends RedisTransactionalCache implements VersionedCache { + + private static final int VERSION_SIZE = 8; + private static final int VALUE_END_OFFSET = -1; + + static final byte[] SET_VERSIONED_VALUE_LUA_SCRIPT = StringRedisSerializer.UTF_8.serialize(""" + local key = KEYS[1] + local newValue = ARGV[1] + local newVersion = tonumber(ARGV[2]) + local expiration = tonumber(ARGV[3]) + + local function setNewValue() + local newValueWithVersion = struct.pack(">I8", newVersion) .. newValue + redis.call('SET', key, newValueWithVersion, 'EX', expiration) + end + + -- Get the current version (first 8 bytes) of the current value + local currentVersionBytes = redis.call('GETRANGE', key, 0, 7) + + if currentVersionBytes and #currentVersionBytes == 8 then + local currentVersion = struct.unpack(">I8", currentVersionBytes) + if newVersion > currentVersion then + setNewValue() + end + else + -- If the current value is absent or the current version is not found, set the new value + setNewValue() + end + """); + static final byte[] SET_VERSIONED_VALUE_SHA = StringRedisSerializer.UTF_8.serialize("0453cb1814135b706b4198b09a09f43c9f67bbfe"); + + public VersionedRedisCache(String cacheName, CacheSpecsMap cacheSpecsMap, LettuceConnectionFactory connectionFactory, JCPPRedisCacheConfiguration configuration, JCPPRedisSerializer valueSerializer) { + super(cacheName, cacheSpecsMap, connectionFactory, configuration, valueSerializer); + } + + @PostConstruct + public void init() { + try (var connection = getConnection(SET_VERSIONED_VALUE_SHA)) { + log.debug("Loading LUA with expected SHA[{}], connection [{}]", new String(SET_VERSIONED_VALUE_SHA), connection.getNativeConnection()); + String sha = connection.scriptingCommands().scriptLoad(SET_VERSIONED_VALUE_LUA_SCRIPT); + if (!Arrays.equals(SET_VERSIONED_VALUE_SHA, StringRedisSerializer.UTF_8.serialize(sha))) { + log.error("SHA for SET_VERSIONED_VALUE_LUA_SCRIPT wrong! Expected [{}], but actual [{}], connection [{}]", new String(SET_VERSIONED_VALUE_SHA), sha, connection.getNativeConnection()); + } + } catch (Throwable t) { + log.error("Error on Redis versioned cache init", t); + } + } + + @Override + protected byte[] doGet(K key, RedisConnection connection) { + if (!key.isVersioned()) { + return super.doGet(key, connection); + } + byte[] rawKey = getRawKey(key); + return connection.stringCommands().getRange(rawKey, VERSION_SIZE, VALUE_END_OFFSET); + } + + @Override + public void put(K key, V value) { + if (!key.isVersioned()) { + super.put(key, value); + return; + } + Integer version = getVersion(value); + if (version == null) { + return; + } + doPut(key, value, version, cacheTtl); + } + + @Override + public void put(K key, V value, RedisConnection connection) { + if (!key.isVersioned()) { + super.put(key, value, connection); // because scripting commands are not supported in transaction mode + return; + } + Integer version = getVersion(value); + if (version == null) { + return; + } + byte[] rawKey = getRawKey(key); + doPut(rawKey, value, version, cacheTtl, connection); + } + + private void doPut(K key, V value, Integer version, Expiration expiration) { + if (!cacheEnabled) { + return; + } + log.trace("put [{}][{}][{}]", key, value, version); + final byte[] rawKey = getRawKey(key); + try (var connection = getConnection(rawKey)) { + doPut(rawKey, value, version, expiration, connection); + } + } + + private void doPut(byte[] rawKey, V value, Integer version, Expiration expiration, RedisConnection connection) { + byte[] rawValue = getRawValue(value); + byte[] rawVersion = StringRedisSerializer.UTF_8.serialize(String.valueOf(version)); + byte[] rawExpiration = StringRedisSerializer.UTF_8.serialize(String.valueOf(expiration.getExpirationTimeInSeconds())); + try { + connection.scriptingCommands().evalSha(SET_VERSIONED_VALUE_SHA, ReturnType.VALUE, 1, rawKey, rawValue, rawVersion, rawExpiration); + } catch (InvalidDataAccessApiUsageException e) { + log.debug("loading LUA [{}]", connection.getNativeConnection()); + String sha = connection.scriptingCommands().scriptLoad(SET_VERSIONED_VALUE_LUA_SCRIPT); + if (!Arrays.equals(SET_VERSIONED_VALUE_SHA, StringRedisSerializer.UTF_8.serialize(sha))) { + log.error("SHA for SET_VERSIONED_VALUE_LUA_SCRIPT wrong! Expected [{}], but actual [{}]", new String(SET_VERSIONED_VALUE_SHA), sha); + } + try { + connection.scriptingCommands().evalSha(SET_VERSIONED_VALUE_SHA, ReturnType.VALUE, 1, rawKey, rawValue, rawVersion, rawExpiration); + } catch (InvalidDataAccessApiUsageException ignored) { + log.debug("Slowly executing eval instead of fast evalsha"); + connection.scriptingCommands().eval(SET_VERSIONED_VALUE_LUA_SCRIPT, ReturnType.VALUE, 1, rawKey, rawValue, rawVersion, rawExpiration); + } + } + } + + @Override + public void evict(K key, Integer version) { + log.trace("evict [{}][{}]", key, version); + if (version != null) { + doPut(key, null, version, evictExpiration); + } + } + + @Override + public void putIfAbsent(K key, V value) { + throw new NotImplementedException("putIfAbsent is not supported by versioned cache"); + } + + @Override + public void evictOrPut(K key, V value) { + throw new NotImplementedException("evictOrPut is not supported by versioned cache"); + } + +} diff --git a/jcpp-infrastructure-proto/pom.xml b/jcpp-infrastructure-proto/pom.xml new file mode 100644 index 0000000..2583d91 --- /dev/null +++ b/jcpp-infrastructure-proto/pom.xml @@ -0,0 +1,50 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-infrastructure-proto + jar + JChargePointProtocol Infrastructure Proto Module + 基础Protobuf模块 + + + ${basedir}/.. + + + + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java new file mode 100644 index 0000000..6d91913 --- /dev/null +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java @@ -0,0 +1,57 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.proto; + + +import sanbing.jcpp.infrastructure.proto.model.PricingModel; +import sanbing.jcpp.infrastructure.proto.model.PricingModel.FlagPrice; +import sanbing.jcpp.infrastructure.proto.model.PricingModel.Period; +import sanbing.jcpp.proto.gen.ProtocolProto.*; + +import java.util.Map; + +/** + * @author baigod + */ +public class ProtoConverter { + + public static PricingModelProto toPricingModel(PricingModel pricingModel) { + // 创建 PricingModelProto 实例 + PricingModelProto.Builder builder = PricingModelProto.newBuilder(); + + // 设置字段 + builder.setType(PricingModelType.valueOf(pricingModel.getType().name())); + builder.setRule(PricingModelRule.valueOf(pricingModel.getRule().name())); + builder.setStandardElec(pricingModel.getStandardElec()); + builder.setStandardServ(pricingModel.getStandardServ()); + + // 转换 flagPriceList + for (Map.Entry entry : pricingModel.getFlagPriceList().entrySet()) { + PricingModelFlag flag = entry.getKey(); + FlagPrice flagPrice = entry.getValue(); + + FlagPriceProto flagPriceProto = FlagPriceProto.newBuilder() + .setFlag(PricingModelFlag.valueOf(flag.name())) // 枚举转换 + .setElec(flagPrice.getElec()) + .setServ(flagPrice.getServ()) + .build(); + + builder.putFlagPrice(flag.ordinal(), flagPriceProto); // 按 ordinal 值作为 key 存入 + } + + // 转换 PeriodsList + for (Period period : pricingModel.getPeriodsList()) { + PeriodProto periodProto = PeriodProto.newBuilder() + .setSn(period.getSn()) + .setBegin(period.getBegin().toString()) // 假设 begin 是 LocalTime, 转换为字符串 + .setEnd(period.getEnd().toString()) // 假设 end 是 LocalTime, 转换为字符串 + .setFlag(PricingModelFlag.valueOf(period.getFlag().name())) + .build(); + builder.addPeriod(periodProto); + } + + return builder.build(); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java new file mode 100644 index 0000000..c80631f --- /dev/null +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java @@ -0,0 +1,78 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.proto.model; + +import lombok.*; +import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag; +import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelRule; +import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelType; + +import java.time.LocalTime; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Data +public class PricingModel { + + private UUID id; + + // 计数器,供充电桩协议使用 + private int sequenceNumber; + + private String pileCode; + + private PricingModelType type; + + private PricingModelRule rule; + + /** + * 标准电价(单位分) + */ + private int standardElec; + + /** + * 标准服务费(单位分) + */ + private int standardServ; + + /** + * 分时电价 + */ + private Map flagPriceList; + + /** + * 分时时段 + */ + private List periodsList; + + @Setter + @Getter + public static class Period { + private int sn; + + // 起始时间 + private LocalTime begin; + + // 结束时间 + private LocalTime end; + + // 尖峰平谷标识 + private PricingModelFlag flag; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class FlagPrice { + + // 分时电价,单位分 + private int elec; + + // 分时服务费,单位分 + private int serv; + } + +} \ No newline at end of file diff --git a/jcpp-infrastructure-proto/src/main/proto/cluster.proto b/jcpp-infrastructure-proto/src/main/proto/cluster.proto new file mode 100644 index 0000000..c2455fc --- /dev/null +++ b/jcpp-infrastructure-proto/src/main/proto/cluster.proto @@ -0,0 +1,25 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +syntax = "proto3"; + +package infrastructureProto; + +option java_package = "sanbing.jcpp.proto.gen"; +option java_outer_classname = "ClusterProto"; + +message ServiceInfo { + string serviceId = 1; + repeated string serviceTypes = 2; + SystemInfoProto systemInfo = 10; +} + +message SystemInfoProto { + int64 cpuUsage = 1; + int64 cpuCount = 2; + int64 memoryUsage = 3; + int64 totalMemory = 4; + int64 diskUsage = 5; + int64 totalDiscSpace = 6; +} diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto new file mode 100644 index 0000000..bd3ee09 --- /dev/null +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -0,0 +1,242 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +syntax = "proto3"; + +package infrastructureProto; + +option java_package = "sanbing.jcpp.proto.gen"; +option java_outer_classname = "ProtocolProto"; + +message UplinkQueueMessage { + int64 messageIdMSB = 1; + int64 messageIdLSB = 2; + int64 sessionIdMSB = 3; + int64 sessionIdLSB = 4; + string messageKey = 5; + string protocolName = 6; + bytes requestData = 10; + LoginRequest loginRequest = 21; + HeartBeatRequest heartBeatRequest = 22; + VerifyPricingRequest verifyPricingRequest = 23; + QueryPricingRequest queryPricingRequest = 24; + GunRunStatusProto gunRunStatusProto = 25; + ChargingProgressProto chargingProgressProto = 26; + SetPricingResponse setPricingResponse = 27; + RemoteStartChargingResponse remoteStartChargingResponse = 28; + RemoteStopChargingResponse remoteStopChargingResponse = 29; + TransactionRecord transactionRecord = 30; +} + +message DownlinkRestMessage { + int64 messageIdMSB = 1; + int64 messageIdLSB = 2; + int64 sessionIdMSB = 3; + int64 sessionIdLSB = 4; + string protocolName = 6; + string pileCode = 7; + optional int64 requestIdMSB = 8; + optional int64 requestIdLSB = 9; + optional bytes requestData = 10; + string downlinkCmd = 11; + LoginResponse loginResponse = 20; + VerifyPricingResponse verifyPricingResponse = 21; + QueryPricingResponse queryPricingResponse = 22; + SetPricingRequest setPricingRequest = 23; + RemoteStartChargingRequest remoteStartChargingRequest = 24; + RemoteStopChargingRequest remoteStopChargingRequest = 25; + TransactionRecordAck transactionRecordAck = 26; +} + +message LoginRequest { + string pileCode = 3; + string remoteAddress = 4; + string nodeId = 10; + string nodeWebapiIpPort = 11; + optional string additionalInfo = 20; +} + +message LoginResponse { + bool success = 1; + string pileCode = 2; +} + +message HeartBeatRequest { + string pileCode = 3; + string remoteAddress = 4; + string nodeId = 10; + string nodeWebapiIpPort = 11; + optional string additionalInfo = 20; +} + +message VerifyPricingRequest { + string pileCode = 4; + int64 pricingId = 30; + optional string pricingModel = 31; + optional string additionalInfo = 20; +} + +message VerifyPricingResponse { + bool success = 1; + int64 pricingId = 30; +} + +message QueryPricingRequest { + string pileCode = 4; + optional string additionalInfo = 20; +} + +message QueryPricingResponse { + string pileCode = 4; + int64 pricingId = 30; + PricingModelProto pricingModel = 1; +} + +message PricingModelProto { + PricingModelType type = 3; + PricingModelRule rule = 4; + int32 standardElec = 5; + int32 standardServ = 6; + map flagPrice = 8; + repeated PeriodProto period = 9; +} + +message PeriodProto { + int32 sn = 1; + string begin = 2; + string end = 3; + PricingModelFlag flag = 4; +} + +message FlagPriceProto { + PricingModelFlag flag = 1; + int32 elec = 2; + int32 serv = 3; +} + +enum PricingModelType { + CHARGE = 0; // 充电费率模型 + DISCHARGE = 1; // 放电费率模型 +} + +enum PricingModelRule { + STANDARD = 0; + SPLIT_TIME = 1; +} + +enum PricingModelFlag { + TOP = 0; // 尖峰 + PEAK = 1; // 峰 + FLAT = 2; // 平 + VALLEY = 3; // 谷 + DEEP = 4; // 深谷 +} + +enum GunRunStatus { + IDLE = 0; // 空闲 + INSERTED = 1; // 已插枪 + CHARGING = 2; // 充电中 + CHARGE_COMPLETE = 3; // 充电完成 + DISCHARGE_READY = 4; // 放电准备 + DISCHARGING = 5; // 放电中 + DISCHARGE_COMPLETE = 6; // 放电完成 + RESERVED = 7; // 预约 + FAULT = 8; // 故障 + UNKNOWN = 9; // 未知 +} + +message GunRunStatusProto { + int64 ts = 1; + string pileCode = 4; + string gunCode = 5; + GunRunStatus GunRunStatus = 41; + repeated string faultMessages = 6; + optional string additionalInfo = 20; +} + +message ChargingProgressProto { + int64 ts = 1; + string pileCode = 4; + string gunCode = 5; + string tradeNo = 6; + float outputVoltage = 7; + float outputCurrent = 8; + float soc = 9; + int32 totalChargingDurationMin = 10; + float totalChargingEnergyKWh = 11; + int64 totalChargingCostCent = 12; + optional string additionalInfo = 20; +} + +message SetPricingRequest { + string pileCode = 4; + int64 pricingId = 30; + PricingModelProto pricingModel = 1; +} + +message SetPricingResponse { + bool success = 1; + string pileCode = 4; + int64 pricingId = 30; +} + +message RemoteStartChargingRequest { + string pileCode = 4; + string gunCode = 5; + string tradeNo = 6; + int32 limitCent = 7; + optional string additionalInfo = 20; +} + +message RemoteStartChargingResponse { + int64 ts = 1; + string pileCode = 4; + string gunCode = 5; + string tradeNo = 6; + bool success = 7; + string failReason = 8; + optional string additionalInfo = 20; +} + +message RemoteStopChargingRequest { + string pileCode = 4; + string gunCode = 5; +} + +message RemoteStopChargingResponse { + int64 ts = 1; + string pileCode = 4; + string gunCode = 5; + bool success = 7; + string failReason = 8; + optional string additionalInfo = 20; +} + +message TransactionRecord { + string pileCode = 4; + string gunCode = 5; + string tradeNo = 6; + int64 startTs = 51; + int64 endTs = 52; + float topEnergyKWh = 53; + int64 topAmountCent = 54; + float peakEnergyKWh = 55; + int64 peakAmountCent = 56; + float flatEnergyKWh = 57; + int64 flatAmountCent = 58; + float valleyEnergyKWh = 59; + int64 valleyAmountCent = 60; + float deepEnergyKWh = 61; + int64 deepAmountCent = 62; + float totalEnergyKWh = 63; + int64 totalAmountCent = 64; + int64 tradeTs = 65; + string stopReason = 66; + optional string additionalInfo = 20; +} + +message TransactionRecordAck { + string tradeNo = 6; + bool success = 7; +} \ No newline at end of file diff --git a/jcpp-infrastructure-queue/pom.xml b/jcpp-infrastructure-queue/pom.xml new file mode 100644 index 0000000..b068db7 --- /dev/null +++ b/jcpp-infrastructure-queue/pom.xml @@ -0,0 +1,52 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-infrastructure-queue + jar + JChargePointProtocol Infrastructure Queue Module + 基础MQ管理模块 + + + ${basedir}/.. + + + + + org.apache.kafka + kafka-clients + + + sanbing + jcpp-infrastructure-util + + + sanbing + jcpp-infrastructure-proto + + + sanbing + jcpp-infrastructure-stats + + + org.apache.curator + curator-recipes + + + + + diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java new file mode 100644 index 0000000..88914e8 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java @@ -0,0 +1,190 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import jakarta.annotation.Nonnull; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyList; + +@Slf4j +public abstract class AbstractQueueConsumerTemplate implements QueueConsumer{ + + public static final long ONE_MILLISECOND_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(1); + private volatile boolean subscribed; + protected volatile boolean stopped = false; + protected volatile Set partitions; + protected final ReentrantLock consumerLock = new ReentrantLock(); //NonfairSync + final Queue> subscribeQueue = new ConcurrentLinkedQueue<>(); + + @Getter + private final String topic; + + public AbstractQueueConsumerTemplate(String topic) { + this.topic = topic; + } + + @Override + public void subscribe() { + log.debug("enqueue topic subscribe {} ", topic); + if (stopped) { + log.error("trying subscribe, but consumer stopped for topic {}", topic); + return; + } + subscribeQueue.add(Collections.singleton(new TopicPartitionInfo(topic, null, true))); + } + + @Override + public void subscribe(Set partitions) { + log.debug("enqueue topics subscribe {} ", partitions); + if (stopped) { + log.error("trying subscribe, but consumer stopped for topic {}", topic); + return; + } + subscribeQueue.add(partitions); + } + + @Override + public List poll(long durationInMillis) { + List records; + long startNanos = System.nanoTime(); + if (stopped) { + log.error("poll invoked but consumer stopped for topic " + topic, new RuntimeException("stacktrace")); + return emptyList(); + } + if (!subscribed && partitions == null && subscribeQueue.isEmpty()) { + return sleepAndReturnEmpty(startNanos, durationInMillis); + } + + if (consumerLock.isLocked()) { + log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); + } + + consumerLock.lock(); + try { + while (!subscribeQueue.isEmpty()) { + subscribed = false; + partitions = subscribeQueue.poll(); + } + if (!subscribed) { + List topicNames = getFullTopicNames(); + log.info("Subscribing to topics {}", topicNames); + doSubscribe(topicNames); + subscribed = true; + } + records = partitions.isEmpty() ? emptyList() : doPoll(durationInMillis); + } finally { + consumerLock.unlock(); + } + + if (records.isEmpty() && !isLongPollingSupported()) { + return sleepAndReturnEmpty(startNanos, durationInMillis); + } + + return decodeRecords(records); + } + + @Nonnull + List decodeRecords(@Nonnull List records) { + List result = new ArrayList<>(records.size()); + records.forEach(record -> { + try { + if (record != null) { + result.add(decode(record)); + } + } catch (IOException e) { + log.error("Failed decode record: [{}]", record); + throw new RuntimeException("Failed to decode record: ", e); + } + }); + return result; + } + + List sleepAndReturnEmpty(final long startNanos, final long durationInMillis) { + long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis); + long spentNanos = System.nanoTime() - startNanos; + long nanosLeft = durationNanos - spentNanos; + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) { + try { + long sleepMs = TimeUnit.NANOSECONDS.toMillis(nanosLeft); + log.trace("Going to sleep after poll: topic {} for {}ms", topic, sleepMs); + Thread.sleep(sleepMs); + } catch (InterruptedException e) { + if (!stopped) { + log.error("Failed to wait", e); + } + } + } + return emptyList(); + } + + @Override + public void commit() { + if (consumerLock.isLocked()) { + log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); + } + consumerLock.lock(); + try { + doCommit(); + } finally { + consumerLock.unlock(); + } + } + + @Override + public void stop() { + stopped = true; + } + + @Override + public void unsubscribe() { + log.info("Unsubscribing and stopping consumer for topics {}", getFullTopicNames()); + stopped = true; + consumerLock.lock(); + try { + if (subscribed) { + doUnsubscribe(); + } + } finally { + consumerLock.unlock(); + } + } + + @Override + public boolean isStopped() { + return stopped; + } + + abstract protected List doPoll(long durationInMillis); + + abstract protected T decode(R record) throws IOException; + + abstract protected void doSubscribe(List topicNames); + + abstract protected void doCommit(); + + abstract protected void doUnsubscribe(); + + @Override + public List getFullTopicNames() { + if (partitions == null) { + return Collections.emptyList(); + } + return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); + } + + protected boolean isLongPollingSupported() { + return false; + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/Callback.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/Callback.java new file mode 100644 index 0000000..9e97701 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/Callback.java @@ -0,0 +1,26 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +public interface Callback { + + Callback EMPTY = new Callback() { + + @Override + public void onSuccess() { + + } + + @Override + public void onFailure(Throwable t) { + + } + }; + + void onSuccess(); + + void onFailure(Throwable t); + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsg.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsg.java new file mode 100644 index 0000000..9e27287 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsg.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import lombok.Data; + +@Data +public class DefaultQueueMsg implements QueueMsg { + private final String key; + private final byte[] data; + private final DefaultQueueMsgHeaders headers; + + public DefaultQueueMsg(QueueMsg msg) { + this.key = msg.getKey(); + this.data = msg.getData(); + DefaultQueueMsgHeaders headers = new DefaultQueueMsgHeaders(); + msg.getHeaders().getData().forEach(headers::put); + this.headers = headers; + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsgHeaders.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsgHeaders.java new file mode 100644 index 0000000..d93ac8c --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/DefaultQueueMsgHeaders.java @@ -0,0 +1,29 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + + +import java.util.HashMap; +import java.util.Map; + +public class DefaultQueueMsgHeaders implements QueueMsgHeaders { + + protected final Map data = new HashMap<>(); + + @Override + public byte[] put(String key, byte[] value) { + return data.put(key, value); + } + + @Override + public byte[] get(String key) { + return data.get(key); + } + + @Override + public Map getData() { + return data; + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/KafkaQueueMsg.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/KafkaQueueMsg.java new file mode 100644 index 0000000..81c59ca --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/KafkaQueueMsg.java @@ -0,0 +1,38 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import org.apache.kafka.clients.consumer.ConsumerRecord; + +public class KafkaQueueMsg implements QueueMsg { + private final String key; + private final QueueMsgHeaders headers; + private final byte[] data; + + public KafkaQueueMsg(ConsumerRecord record) { + this.key = record.key(); + QueueMsgHeaders headers = new DefaultQueueMsgHeaders(); + record.headers().forEach(header -> { + headers.put(header.key(), header.value()); + }); + this.headers = headers; + this.data = record.value(); + } + + @Override + public String getKey() { + return key; + } + + @Override + public QueueMsgHeaders getHeaders() { + return headers; + } + + @Override + public byte[] getData() { + return data; + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackCallback.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackCallback.java new file mode 100644 index 0000000..f5bb075 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackCallback.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import lombok.extern.slf4j.Slf4j; + +import java.util.UUID; + +@Slf4j +public class PackCallback implements Callback { + private final PackProcessingContext ctx; + private final UUID id; + + public PackCallback(UUID id, PackProcessingContext ctx) { + this.id = id; + this.ctx = ctx; + } + + @Override + public void onSuccess() { + log.trace("[{}] ON SUCCESS", id); + ctx.onSuccess(id); + } + + @Override + public void onFailure(Throwable t) { + log.trace("[{}] ON FAILURE", id, t); + ctx.onFailure(id, t); + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackProcessingContext.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackProcessingContext.java new file mode 100644 index 0000000..6ea6e10 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/PackProcessingContext.java @@ -0,0 +1,75 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.UUID; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j +public class PackProcessingContext { + + private final AtomicInteger pendingCount; + private final CountDownLatch processingTimeoutLatch; + @Getter + private final ConcurrentMap ackMap; + @Getter + private final ConcurrentMap failedMap; + + public PackProcessingContext(CountDownLatch processingTimeoutLatch, + ConcurrentMap ackMap, + ConcurrentMap failedMap) { + this.processingTimeoutLatch = processingTimeoutLatch; + this.pendingCount = new AtomicInteger(ackMap.size()); + this.ackMap = ackMap; + this.failedMap = failedMap; + } + + public boolean await(long packProcessingTimeout, TimeUnit milliseconds) throws InterruptedException { + return processingTimeoutLatch.await(packProcessingTimeout, milliseconds); + } + + public void onSuccess(UUID id) { + boolean empty = false; + T msg = ackMap.remove(id); + if (msg != null) { + empty = pendingCount.decrementAndGet() == 0; + } + if (empty) { + processingTimeoutLatch.countDown(); + } else { + if (log.isTraceEnabled()) { + log.trace("Items left: {}", ackMap.size()); + for (T t : ackMap.values()) { + log.trace("left item: {}", t); + } + } + } + } + + public void onFailure(UUID id, Throwable t) { + boolean empty = false; + T msg = ackMap.remove(id); + if (msg != null) { + empty = pendingCount.decrementAndGet() == 0; + failedMap.put(id, msg); + if (log.isTraceEnabled()) { + log.trace("Items left: {}", ackMap.size()); + for (T v : ackMap.values()) { + log.trace("left item: {}", v); + } + } + } + if (empty) { + processingTimeoutLatch.countDown(); + } + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java new file mode 100644 index 0000000..3667dfc --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java @@ -0,0 +1,40 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import lombok.Data; + +@Data +public class ProtoQueueMsg implements QueueMsg { + + private final String key; + protected final T value; + private final QueueMsgHeaders headers; + + public ProtoQueueMsg(String key, T value) { + this(key, value, new DefaultQueueMsgHeaders()); + } + + public ProtoQueueMsg(String key, T value, QueueMsgHeaders headers) { + this.key = key; + this.value = value; + this.headers = headers; + } + + @Override + public String getKey() { + return key; + } + + @Override + public QueueMsgHeaders getHeaders() { + return headers; + } + + @Override + public byte[] getData() { + return value.toByteArray(); + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueAdmin.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueAdmin.java new file mode 100644 index 0000000..1e3688a --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueAdmin.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +public interface QueueAdmin { + + default void createTopicIfNotExists(String topic) { + createTopicIfNotExists(topic, null); + } + + void createTopicIfNotExists(String topic, String properties); + + void destroy(); + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueCallback.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueCallback.java new file mode 100644 index 0000000..f2dcc91 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueCallback.java @@ -0,0 +1,12 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +public interface QueueCallback { + + void onSuccess(QueueMsgMetadata metadata); + + void onFailure(Throwable t); +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueConsumer.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueConsumer.java new file mode 100644 index 0000000..7b68b34 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueConsumer.java @@ -0,0 +1,34 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + + + +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +import java.util.List; +import java.util.Set; + +public interface QueueConsumer { + + String getTopic(); + + void subscribe(); + + void subscribe(Set partitions); + + void stop(); + + void unsubscribe(); + + List poll(long durationInMillis); + + void commit(); + + boolean isStopped(); + + List getFullTopicNames(); + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsg.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsg.java new file mode 100644 index 0000000..828d756 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsg.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +public interface QueueMsg { + + String getKey(); + + QueueMsgHeaders getHeaders(); + + byte[] getData(); +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgHeaders.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgHeaders.java new file mode 100644 index 0000000..d3fe798 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgHeaders.java @@ -0,0 +1,16 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +import java.util.Map; + +public interface QueueMsgHeaders { + + byte[] put(String key, byte[] value); + + byte[] get(String key); + + Map getData(); +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgMetadata.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgMetadata.java new file mode 100644 index 0000000..b137435 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueMsgMetadata.java @@ -0,0 +1,8 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + +public interface QueueMsgMetadata { +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueProducer.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueProducer.java new file mode 100644 index 0000000..aae4422 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/QueueProducer.java @@ -0,0 +1,19 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue; + + +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +public interface QueueProducer { + + void init(); + + String getTopic(); + + void send(TopicPartitionInfo tpi, T msg, QueueCallback callback); + + void stop(); +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConfig.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConfig.java new file mode 100644 index 0000000..a3667e8 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConfig.java @@ -0,0 +1,13 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.common; + +public interface QueueConfig { + + boolean isConsumerPerPartition(); + + int getPollInterval(); + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java new file mode 100644 index 0000000..c2ac400 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.common; + +/** + * @author baigod + */ +public final class QueueConstants { + + public static final String MSG_MD_PREFIX = "jcpp_"; + + public static final String MSG_MD_TS = "ts"; + + public static final String MSG_MD_PROTOCOL_SESSION_ID = "protocol_session_id"; +} \ No newline at end of file diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/TopicPartitionInfo.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/TopicPartitionInfo.java new file mode 100644 index 0000000..c777928 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/TopicPartitionInfo.java @@ -0,0 +1,59 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.common; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +import java.util.Objects; +import java.util.Optional; + +@ToString +public class TopicPartitionInfo { + + @Getter + private final String topic; + private final Integer partition; + @Getter + private final String fullTopicName; + @Getter + private final boolean myPartition; + + @Builder + public TopicPartitionInfo(String topic, Integer partition, boolean myPartition) { + this.topic = topic; + this.partition = partition; + this.myPartition = myPartition; + String tmp = topic; + if (partition != null) { + tmp += "." + partition; + } + this.fullTopicName = tmp; + } + + public TopicPartitionInfo newByTopic(String topic) { + return new TopicPartitionInfo(topic, this.partition, this.myPartition); + } + + public Optional getPartition() { + return Optional.ofNullable(partition); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TopicPartitionInfo that = (TopicPartitionInfo) o; + return topic.equals(that.topic) && + Objects.equals(partition, that.partition) && + fullTopicName.equals(that.fullTopicName); + } + + @Override + public int hashCode() { + return Objects.hash(fullTopicName); + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java new file mode 100644 index 0000000..055610f --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java @@ -0,0 +1,109 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import sanbing.jcpp.infrastructure.util.SystemUtil; +import sanbing.jcpp.proto.gen.ClusterProto.ServiceInfo; +import sanbing.jcpp.proto.gen.ClusterProto.SystemInfoProto; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * @author baigod + */ +@Component +@Slf4j +public class DefaultServiceInfoProvider implements ServiceInfoProvider { + + @Value("${service.id:#{null}}") + @Getter + private String serviceId; + + @Getter + @Value("${service.type:monolith}") + private String serviceType; + + private List serviceTypes; + + private ServiceInfo serviceInfo; + + @Getter + private String serviceWebapiEndpoint; + + @Value("${server.port}") + private String webapiPort; + + @PostConstruct + public void init() throws UnknownHostException { + + + if (!StringUtils.hasText(this.serviceId)) { + try { + this.serviceId = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + this.serviceId = RandomStringUtils.randomAlphabetic(10); + } + } + log.info("Current Service ID: {}", this.serviceId); + + serviceWebapiEndpoint = InetAddress.getLocalHost().getHostAddress() + ":" + webapiPort; + log.info("Current Service HostAddress: {}", this.serviceWebapiEndpoint); + if (serviceType.equalsIgnoreCase("monolith")) { + serviceTypes = List.of(ServiceType.values()); + } else { + serviceTypes = Collections.singletonList(ServiceType.of(serviceType)); + } + + generateNewServiceInfoWithCurrentSystemInfo(); + } + + + @Override + public boolean isMonolith() { + return "monolith".equals(getServiceType()); + } + + @Override + public ServiceInfo getServiceInfo() { + return serviceInfo; + } + + @Override + public ServiceInfo generateNewServiceInfoWithCurrentSystemInfo() { + ServiceInfo.Builder builder = ServiceInfo.newBuilder() + .setServiceId(serviceId) + .addAllServiceTypes(serviceTypes.stream().map(ServiceType::name).collect(Collectors.toList())) + .setSystemInfo(getCurrentSystemInfoProto()); + return serviceInfo = builder.build(); + } + + private SystemInfoProto getCurrentSystemInfoProto() { + SystemInfoProto.Builder builder = SystemInfoProto.newBuilder(); + + SystemUtil.getCpuUsage().ifPresent(builder::setCpuUsage); + SystemUtil.getMemoryUsage().ifPresent(builder::setMemoryUsage); + SystemUtil.getDiscSpaceUsage().ifPresent(builder::setDiskUsage); + + SystemUtil.getCpuCount().ifPresent(builder::setCpuCount); + SystemUtil.getTotalMemory().ifPresent(builder::setTotalMemory); + SystemUtil.getTotalDiscSpace().ifPresent(builder::setTotalDiscSpace); + + return builder.build(); + } + +} + diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DiscoveryProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DiscoveryProvider.java new file mode 100644 index 0000000..89b0fb0 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DiscoveryProvider.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import sanbing.jcpp.proto.gen.ClusterProto.ServiceInfo; + +import java.util.List; + +public interface DiscoveryProvider { + + List getOtherServers(); + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DummyDiscoveryProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DummyDiscoveryProvider.java new file mode 100644 index 0000000..36d2cd8 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DummyDiscoveryProvider.java @@ -0,0 +1,41 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.stereotype.Service; +import sanbing.jcpp.infrastructure.util.annotation.AfterStartUp; +import sanbing.jcpp.proto.gen.ClusterProto.ServiceInfo; + +import java.util.Collections; +import java.util.List; + +@Service +@ConditionalOnProperty(prefix = "zk", value = "enabled", havingValue = "false", matchIfMissing = true) +@Slf4j +public class DummyDiscoveryProvider implements DiscoveryProvider { + + private final ServiceInfoProvider serviceInfoProvider; + private final PartitionProvider partitionProvider; + + public DummyDiscoveryProvider(ServiceInfoProvider serviceInfoProvider, PartitionProvider partitionProvider) { + this.serviceInfoProvider = serviceInfoProvider; + this.partitionProvider = partitionProvider; + } + + + @AfterStartUp(order = AfterStartUp.DISCOVERY_SERVICE) + public void onApplicationEvent(ApplicationReadyEvent event) { + partitionProvider.recalculatePartitions(serviceInfoProvider.getServiceInfo(), Collections.emptyList()); + } + + @Override + public List getOtherServers() { + return Collections.emptyList(); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java new file mode 100644 index 0000000..87db05b --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java @@ -0,0 +1,199 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import com.google.common.hash.HashFunction; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; +import sanbing.jcpp.infrastructure.queue.discovery.event.PartitionChangeEvent; +import sanbing.jcpp.proto.gen.ClusterProto.ServiceInfo; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.forName; +import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.hash; + +/** + * @author baigod + */ +@Component +@Slf4j +@ConfigurationProperties("queue.partitions") +public class HashPartitionProvider implements PartitionProvider { + + @Value("${queue.app.topic}") + private String appTopic; + @Value("${queue.app.partitions:10}") + private Integer appPartitions; + @Value("${queue.partitions.hash_function_name:murmur3_128}") + private String hashFunctionName; + + private final ConcurrentMap partitionTopicsMap = new ConcurrentHashMap<>(); + private final ConcurrentMap partitionSizesMap = new ConcurrentHashMap<>(); + + private HashFunction hashFunction; + + protected volatile ConcurrentMap> myPartitions = new ConcurrentHashMap<>(); + + @Resource + private ApplicationEventPublisher applicationEventPublisher; + + @PostConstruct + public void init() { + this.hashFunction = forName(hashFunctionName); + + QueueKey appKey = new QueueKey(ServiceType.APP); + partitionTopicsMap.put(appKey, appTopic); + partitionSizesMap.put(appKey, appPartitions); + } + + private TopicPartitionInfo resolve(QueueKey queueKey, int hash) { + Integer partitionSize = partitionSizesMap.get(queueKey); + if (partitionSize == null) { + throw new IllegalStateException("Partitions info for queue " + queueKey + " is missing"); + } + + int partition = Math.abs(hash % partitionSize); + + return buildTopicPartitionInfo(queueKey, partition); + } + + @Override + public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, UUID entityId) { + QueueKey queueKey = getQueueKey(serviceType, queueName); + return resolve(queueKey, hash(hashFunction, entityId)); + } + + @Override + public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, String pileCode) { + QueueKey queueKey = getQueueKey(serviceType, queueName); + return resolve(queueKey, hash(hashFunction, pileCode)); + } + + private QueueKey getQueueKey(ServiceType serviceType, String queueName) { + QueueKey queueKey = new QueueKey(serviceType, queueName); + if (!partitionSizesMap.containsKey(queueKey)) { + queueKey = new QueueKey(serviceType); + } + return queueKey; + } + + @Override + public synchronized void recalculatePartitions(ServiceInfo currentService, List otherServices) { + log.info("Recalculating partitions"); + logServiceInfo(currentService); + otherServices.forEach(this::logServiceInfo); + + Map> queueServicesMap = new HashMap<>(); + addNode(currentService, queueServicesMap); + for (ServiceInfo other : otherServices) { + addNode(other, queueServicesMap); + } + queueServicesMap.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId))); + + final ConcurrentMap> newPartitions = new ConcurrentHashMap<>(); + partitionSizesMap.forEach((queueKey, size) -> { + for (int i = 0; i < size; i++) { + try { + List servers = queueServicesMap.get(queueKey); + ServiceInfo serviceInfo = servers == null || servers.isEmpty() ? null : servers.get(i % servers.size()); + log.info("Server responsible for {}[{}] - {}", queueKey, i, serviceInfo != null ? serviceInfo.getServiceId() : "none"); + if (currentService.equals(serviceInfo)) { + newPartitions.computeIfAbsent(queueKey, key -> new ArrayList<>()).add(i); + } + } catch (Exception e) { + log.warn("Failed to resolve server responsible for {}[{}]", queueKey, i, e); + } + } + }); + + final ConcurrentMap> oldPartitions = myPartitions; + myPartitions = newPartitions; + + log.info("Current Server responsible partitions: {}", myPartitions); + + Map> changedPartitionsMap = new HashMap<>(); + + Set removed = new HashSet<>(); + oldPartitions.forEach((queueKey, partitions) -> { + if (!newPartitions.containsKey(queueKey)) { + removed.add(queueKey); + } + }); + + removed.forEach(queueKey -> { + changedPartitionsMap.put(queueKey, Collections.emptySet()); + }); + + myPartitions.forEach((queueKey, partitions) -> { + if (!partitions.equals(oldPartitions.get(queueKey))) { + Set tpiList = partitions.stream() + .map(partition -> buildTopicPartitionInfo(queueKey, partition)) + .collect(Collectors.toSet()); + changedPartitionsMap.put(queueKey, tpiList); + } + }); + + if (!changedPartitionsMap.isEmpty()) { + Map>> partitionsByServiceType = new HashMap<>(); + changedPartitionsMap.forEach((queueKey, partitions) -> { + partitionsByServiceType.computeIfAbsent(queueKey.getType(), serviceType -> new HashMap<>()) + .put(queueKey, partitions); + }); + partitionsByServiceType.forEach(this::publishPartitionChangeEvent); + } + + } + + private void publishPartitionChangeEvent(ServiceType serviceType, Map> partitionsMap) { + log.info("Partitions changed: {}", System.lineSeparator() + partitionsMap.entrySet().stream() + .map(entry -> "[" + entry.getKey() + "] - [" + entry.getValue().stream() + .map(tpi -> tpi.getPartition().orElse(-1).toString()).sorted() + .collect(Collectors.joining(", ")) + "]") + .collect(Collectors.joining(System.lineSeparator()))); + PartitionChangeEvent event = new PartitionChangeEvent(this, serviceType, partitionsMap); + try { + applicationEventPublisher.publishEvent(event); + } catch (Exception e) { + log.error("Failed to publish partition change event {}", event, e); + } + } + + private void logServiceInfo(ServiceInfo server) { + log.info("Found server: {}", server.getServiceId()); + } + + private void addNode(ServiceInfo instance, Map> queueServiceList) { + for (String serviceTypeStr : instance.getServiceTypesList()) { + ServiceType serviceType = ServiceType.of(serviceTypeStr); + if (ServiceType.APP.equals(serviceType)) { + queueServiceList.computeIfAbsent(new QueueKey(serviceType), key -> new ArrayList<>()).add(instance); + } + } + } + + private TopicPartitionInfo buildTopicPartitionInfo(QueueKey queueKey, int partition) { + List partitions = myPartitions.get(queueKey); + return buildTopicPartitionInfo(queueKey, partition, partitions != null && partitions.contains(partition)); + } + + private TopicPartitionInfo buildTopicPartitionInfo(QueueKey queueKey, int partition, boolean myPartition) { + return TopicPartitionInfo.builder() + .topic(partitionTopicsMap.get(queueKey)) + .partition(partition) + .myPartition(myPartition) + .build(); + } + +} \ No newline at end of file diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/PartitionProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/PartitionProvider.java new file mode 100644 index 0000000..908707d --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/PartitionProvider.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + + +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; +import sanbing.jcpp.proto.gen.ClusterProto.ServiceInfo; + +import java.util.List; +import java.util.UUID; + +public interface PartitionProvider { + + TopicPartitionInfo resolve(ServiceType serviceType,String queueName, UUID entityId); + + TopicPartitionInfo resolve(ServiceType serviceType,String queueName, String pileCode); + + void recalculatePartitions(ServiceInfo currentService, List otherServices); + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/QueueKey.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/QueueKey.java new file mode 100644 index 0000000..d847479 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/QueueKey.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import lombok.Data; +import lombok.With; + +@Data +public class QueueKey { + public static final String MAIN_QUEUE_NAME = "Main"; + + private final ServiceType type; + @With + private final String queueName; + + public QueueKey(ServiceType type, String queueName) { + this.type = type; + this.queueName = queueName; + } + + public QueueKey(ServiceType type) { + this.type = type; + this.queueName = MAIN_QUEUE_NAME; + } + + @Override + public String toString() { + return "QK(" + queueName + "," + type + ")"; + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java new file mode 100644 index 0000000..294e2a6 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java @@ -0,0 +1,26 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + + +import sanbing.jcpp.proto.gen.ClusterProto; + +/** + * @author baigod + */ +public interface ServiceInfoProvider { + String getServiceId(); + + String getServiceWebapiEndpoint(); + + String getServiceType(); + + boolean isMonolith(); + + ClusterProto.ServiceInfo getServiceInfo(); + + ClusterProto.ServiceInfo generateNewServiceInfoWithCurrentSystemInfo(); + +} \ No newline at end of file diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceType.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceType.java new file mode 100644 index 0000000..b4f6050 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceType.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum ServiceType { + + APP("app"), + PROTOCOL("protocol"); + + private final String label; + + public static ServiceType of(String serviceType) { + return ServiceType.valueOf(serviceType.toUpperCase()); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ZkDiscoveryProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ZkDiscoveryProvider.java new file mode 100644 index 0000000..4ec563d --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ZkDiscoveryProvider.java @@ -0,0 +1,328 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery; + +import com.google.protobuf.InvalidProtocolBufferException; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.imps.CuratorFrameworkState; +import org.apache.curator.framework.recipes.cache.ChildData; +import org.apache.curator.framework.recipes.cache.CuratorCache; +import org.apache.curator.framework.recipes.cache.CuratorCacheListener; +import org.apache.curator.framework.state.ConnectionState; +import org.apache.curator.framework.state.ConnectionStateListener; +import org.apache.curator.retry.RetryForever; +import org.apache.curator.utils.CloseableUtils; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import sanbing.jcpp.infrastructure.queue.discovery.event.OtherServiceShutdownEvent; +import sanbing.jcpp.infrastructure.util.annotation.AfterStartUp; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; +import sanbing.jcpp.proto.gen.ClusterProto.ServiceInfo; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +@Service +@ConditionalOnProperty(prefix = "zk", value = "enabled", havingValue = "true") +@Slf4j +public class ZkDiscoveryProvider implements DiscoveryProvider, CuratorCacheListener { + + @Value("${zk.url}") + private String zkUrl; + @Value("${zk.retry-interval-ms}") + private Integer zkRetryInterval; + @Value("${zk.connection-timeout-ms}") + private Integer zkConnectionTimeout; + @Value("${zk.session-timeout-ms}") + private Integer zkSessionTimeout; + @Value("${zk.zk-dir}") + private String zkDir; + @Value("${zk.recalculate-delay:0}") + private Long recalculateDelay; + + protected final ConcurrentHashMap> delayedTasks; + + private final ApplicationEventPublisher applicationEventPublisher; + private final ServiceInfoProvider serviceInfoProvider; + private final PartitionProvider partitionProvider; + + private ScheduledExecutorService zkExecutorService; + private CuratorFramework client; + private CuratorCache cache; + private String nodePath; + private String zkNodesDir; + + private volatile boolean stopped = true; + + public ZkDiscoveryProvider(ApplicationEventPublisher applicationEventPublisher, + ServiceInfoProvider serviceInfoProvider, + PartitionProvider partitionProvider) { + this.applicationEventPublisher = applicationEventPublisher; + this.serviceInfoProvider = serviceInfoProvider; + this.partitionProvider = partitionProvider; + delayedTasks = new ConcurrentHashMap<>(); + } + + @PostConstruct + public void init() { + log.info("Discovery Provider Initializing..."); + Assert.hasLength(zkUrl, missingProperty("zk.url")); + Assert.notNull(zkRetryInterval, missingProperty("zk.retry-interval-ms")); + Assert.notNull(zkConnectionTimeout, missingProperty("zk.connection-timeout-ms")); + Assert.notNull(zkSessionTimeout, missingProperty("zk-session-timeout-ms")); + + zkExecutorService = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("zk-discovery")); + + zkNodesDir = zkDir + "/nodes"; + initZkClient(); + + log.info("Initialization completed, using ZK connect string: {}", zkUrl); + } + + @Override + public List getOtherServers() { + return cache.stream() + .filter(cd -> !cd.getPath().equals(nodePath) && !cd.getPath().equals(zkNodesDir)) + .map(cd -> { + try { + return ServiceInfo.parseFrom(cd.getData()); + } catch (NoSuchElementException | InvalidProtocolBufferException e) { + log.error("Failed to decode ZK node", e); + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()); + } + + @AfterStartUp(order = AfterStartUp.DISCOVERY_SERVICE) + public void onApplicationEvent(ApplicationReadyEvent event) { + if (stopped) { + log.debug("Ignoring application ready event. Service is stopped."); + return; + } else { + log.info("Received application ready event. Starting current ZK node."); + } + if (client.getState() != CuratorFrameworkState.STARTED) { + log.debug("Ignoring application ready event, ZK client is not started, ZK client state [{}]", client.getState()); + return; + } + log.info("Going to publish current server..."); + publishCurrentServer(); + log.info("Going to recalculate partitions..."); + recalculatePartitions(); + + zkExecutorService.scheduleAtFixedRate(this::publishCurrentServer, 1, 1, TimeUnit.MINUTES); + } + + @SneakyThrows + public synchronized void publishCurrentServer() { + ServiceInfo self = serviceInfoProvider.getServiceInfo(); + if (currentServerExists()) { + log.trace("[{}] Updating ZK node for current instance: {}", self.getServiceId(), nodePath); + client.setData().forPath(nodePath, serviceInfoProvider.generateNewServiceInfoWithCurrentSystemInfo().toByteArray()); + } else { + try { + log.info("[{}] Creating ZK node for current instance", self.getServiceId()); + nodePath = client.create() + .creatingParentsIfNeeded() + .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) + .forPath(zkNodesDir + "/node-", self.toByteArray()); + log.info("[{}] Created ZK node for current instance: {}", self.getServiceId(), nodePath); + client.getConnectionStateListenable().addListener(checkReconnect(self)); + } catch (Exception e) { + log.error("Failed to create ZK node", e); + throw new RuntimeException(e); + } + } + } + + private boolean currentServerExists() { + if (nodePath == null) { + return false; + } + try { + ServiceInfo self = serviceInfoProvider.getServiceInfo(); + ServiceInfo registeredServerInfo = ServiceInfo.parseFrom(client.getData().forPath(nodePath)); + if (self.equals(registeredServerInfo)) { + return true; + } + } catch (KeeperException.NoNodeException e) { + log.info("ZK node does not exist: {}", nodePath); + } catch (Exception e) { + log.error("Couldn't check if ZK node exists", e); + } + return false; + } + + private ConnectionStateListener checkReconnect(ServiceInfo self) { + return (client, newState) -> { + log.info("[{}] ZK state changed: {}", self.getServiceId(), newState); + if (newState == ConnectionState.LOST) { + zkExecutorService.submit(this::reconnect); + } + }; + } + + private volatile boolean reconnectInProgress = false; + + private synchronized void reconnect() { + if (!reconnectInProgress) { + reconnectInProgress = true; + try { + destroyZkClient(); + initZkClient(); + publishCurrentServer(); + } catch (Exception e) { + log.error("Failed to reconnect to ZK: {}", e.getMessage(), e); + } finally { + reconnectInProgress = false; + } + } + } + + private void initZkClient() { + try { + client = CuratorFrameworkFactory.newClient(zkUrl, zkSessionTimeout, zkConnectionTimeout, new RetryForever(zkRetryInterval)); + client.start(); + client.blockUntilConnected(); + cache = CuratorCache.builder(client, zkNodesDir).build(); + cache.listenable().addListener(this); + cache.start(); + stopped = false; + log.info("ZK client connected"); + } catch (Exception e) { + log.error("Failed to connect to ZK: {}", e.getMessage(), e); + CloseableUtils.closeQuietly(cache); + CloseableUtils.closeQuietly(client); + throw new RuntimeException(e); + } + } + + private void unpublishCurrentServer() { + try { + if (nodePath != null) { + client.delete().forPath(nodePath); + } + } catch (Exception e) { + log.error("Failed to delete ZK node {}", nodePath, e); + throw new RuntimeException(e); + } + } + + private void destroyZkClient() { + stopped = true; + try { + unpublishCurrentServer(); + } catch (Exception ignored) { + } + CloseableUtils.closeQuietly(cache); + CloseableUtils.closeQuietly(client); + log.info("ZK client disconnected"); + } + + @PreDestroy + public void destroy() { + destroyZkClient(); + zkExecutorService.shutdownNow(); + log.info("Stopped zk discovery service"); + } + + public static String missingProperty(String propertyName) { + return "The " + propertyName + " property need to be set!"; + } + + @Override + public void event(Type type, ChildData oldData, ChildData data) { + if (stopped) { + log.info("Ignoring {}. Service is stopped.", type); + return; + } + if (client.getState() != CuratorFrameworkState.STARTED) { + log.info("Ignoring {}, ZK client is not started, ZK client state [{}]", type, client.getState()); + return; + } + + switch (type) { + case NODE_CREATED -> { + if (data == null || data.getData() == null) { + log.info("Ignoring {} due to empty created data", type); + return; + } + String serviceId = getServiceId(type, data); + + ScheduledFuture task = delayedTasks.remove(serviceId); + if (task != null) { + if (task.cancel(false)) { + log.info("[{}] Recalculate partitions ignored. Service was restarted in time.", serviceId); + } else { + log.info("[{}] Going to recalculate partitions. Service was not restarted in time!", serviceId); + recalculatePartitions(); + } + } else { + log.info("[{}] Going to recalculate partitions due to adding new node.", + serviceId); + recalculatePartitions(); + } + } + case NODE_DELETED -> { + if (oldData == null || oldData.getData() == null) { + log.info("Ignoring {} due to empty delete data", type); + return; + } else if (nodePath != null && nodePath.equals(oldData.getPath())) { + log.info("ZK node for current instance is somehow deleted."); + publishCurrentServer(); + return; + } + String serviceId = getServiceId(type, oldData); + + zkExecutorService.submit(() -> applicationEventPublisher.publishEvent(new OtherServiceShutdownEvent(this, serviceId))); + ScheduledFuture future = zkExecutorService.schedule(() -> { + log.info("[{}] Going to recalculate partitions due to removed node", serviceId); + ScheduledFuture removedTask = delayedTasks.remove(serviceId); + if (removedTask != null) { + recalculatePartitions(); + } + }, recalculateDelay, TimeUnit.MILLISECONDS); + delayedTasks.put(serviceId, future); + } + default -> { + } + } + } + + private static String getServiceId(Type type, ChildData data) { + ServiceInfo instance; + try { + instance = ServiceInfo.parseFrom(data.getData()); + } catch (InvalidProtocolBufferException e) { + log.error("Failed to decode server instance for node {}", data.getPath(), e); + throw new RuntimeException(e); + } + + String serviceId = instance.getServiceId(); + + log.info("Processing [{}] event for [{}]", type, serviceId); + return serviceId; + } + + synchronized void recalculatePartitions() { + delayedTasks.values().forEach(future -> future.cancel(false)); + delayedTasks.clear(); + partitionProvider.recalculatePartitions(serviceInfoProvider.getServiceInfo(), getOtherServers()); + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEvent.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEvent.java new file mode 100644 index 0000000..1bcee8a --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEvent.java @@ -0,0 +1,26 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery.event; + +import lombok.Getter; +import lombok.ToString; +import org.springframework.context.ApplicationEvent; + +import java.util.concurrent.atomic.AtomicInteger; + +@ToString +public class JCPPApplicationEvent extends ApplicationEvent { + + private static final AtomicInteger sequence = new AtomicInteger(); + + @Getter + private final int sequenceNumber; + + public JCPPApplicationEvent(Object source) { + super(source); + sequenceNumber = sequence.incrementAndGet(); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEventListener.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEventListener.java new file mode 100644 index 0000000..8541521 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/JCPPApplicationEventListener.java @@ -0,0 +1,54 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public abstract class JCPPApplicationEventListener implements ApplicationListener { + + private int lastProcessedSequenceNumber = Integer.MIN_VALUE; + private final Lock seqNumberLock = new ReentrantLock(); + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Override + public void onApplicationEvent(T event) { + if (!filterApplicationEvent(event)) { + log.trace("Skipping event due to filter: {}", event); + return; + } + boolean validUpdate = false; + seqNumberLock.lock(); + try { + if (event.getSequenceNumber() > lastProcessedSequenceNumber) { + validUpdate = true; + lastProcessedSequenceNumber = event.getSequenceNumber(); + } + } finally { + seqNumberLock.unlock(); + } + if (validUpdate) { + try { + onJCPPApplicationEvent(event); + } catch (Exception e) { + log.error("Failed to handle partition change event: {}", event, e); + } + } else { + log.info("Application event ignored due to invalid sequence number ({} > {}). Event: {}", lastProcessedSequenceNumber, event.getSequenceNumber(), event); + } + } + + protected abstract void onJCPPApplicationEvent(T event); + + protected boolean filterApplicationEvent(T event) { + return true; + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/OtherServiceShutdownEvent.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/OtherServiceShutdownEvent.java new file mode 100644 index 0000000..b334e3c --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/OtherServiceShutdownEvent.java @@ -0,0 +1,18 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery.event; + +import lombok.Getter; + +public class OtherServiceShutdownEvent extends JCPPApplicationEvent { + + @Getter + private final String serviceId; + + public OtherServiceShutdownEvent(Object source, String serviceId) { + super(source); + this.serviceId = serviceId; + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/PartitionChangeEvent.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/PartitionChangeEvent.java new file mode 100644 index 0000000..7a4b37a --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/event/PartitionChangeEvent.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.discovery.event; + +import lombok.Getter; +import lombok.ToString; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; +import sanbing.jcpp.infrastructure.queue.discovery.QueueKey; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceType; + +import java.io.Serial; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static sanbing.jcpp.infrastructure.queue.discovery.QueueKey.MAIN_QUEUE_NAME; + +@ToString(callSuper = true) +public class PartitionChangeEvent extends JCPPApplicationEvent { + + @Serial + private static final long serialVersionUID = -8731788167026510559L; + + @Getter + private final Map> partitionsMap; + + public PartitionChangeEvent(Object source, ServiceType serviceType, Map> partitionsMap) { + super(source); + this.partitionsMap = partitionsMap; + } + + public Set getAppPartitions() { + return getPartitionsByServiceTypeAndQueueName(ServiceType.APP, MAIN_QUEUE_NAME); + } + + private Set getPartitionsByServiceTypeAndQueueName(ServiceType serviceType, String queueName) { + return partitionsMap.entrySet() + .stream() + .filter(entry -> serviceType.equals(entry.getKey().getType()) && queueName.equals(entry.getKey().getQueueName())) + .flatMap(entry -> entry.getValue().stream()) + .collect(Collectors.toSet()); + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaAdmin.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaAdmin.java new file mode 100644 index 0000000..385634b --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaAdmin.java @@ -0,0 +1,94 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.admin.CreateTopicsResult; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.common.errors.TopicExistsException; +import sanbing.jcpp.infrastructure.queue.QueueAdmin; +import sanbing.jcpp.infrastructure.util.property.PropertyUtils; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; + +@Slf4j +public class KafkaAdmin implements QueueAdmin { + + private final KafkaSettings settings; + private final Map topicConfigs; + private final int numPartitions; + private volatile Set topics; + + private final short replicationFactor; + + public KafkaAdmin(KafkaSettings settings, Map topicConfigs) { + this.settings = settings; + this.topicConfigs = topicConfigs; + + String numPartitionsStr = topicConfigs.get(KafkaTopicConfigs.NUM_PARTITIONS_SETTING); + if (numPartitionsStr != null) { + numPartitions = Integer.parseInt(numPartitionsStr); + topicConfigs.remove("partitions"); + } else { + numPartitions = 1; + } + replicationFactor = settings.getReplicationFactor(); + } + + @Override + public void createTopicIfNotExists(String topic, String properties) { + Set topics = getTopics(); + if (topics.contains(topic)) { + return; + } + try { + NewTopic newTopic = new NewTopic(topic, numPartitions, replicationFactor).configs(PropertyUtils.getProps(topicConfigs, properties)); + createTopic(newTopic).values().get(topic).get(); + topics.add(topic); + } catch (ExecutionException ee) { + switch (ee.getCause()) { + case TopicExistsException ignored -> { + //do nothing + } + case null, default -> { + log.warn("[{}] Failed to create topic", topic, ee); + throw new RuntimeException(ee); + } + } + } catch (Exception e) { + log.warn("[{}] Failed to create topic", topic, e); + throw new RuntimeException(e); + } + } + + private Set getTopics() { + if (topics == null) { + synchronized (this) { + if (topics == null) { + topics = ConcurrentHashMap.newKeySet(); + try { + topics.addAll(settings.getAdminClient().listTopics().names().get()); + } catch (InterruptedException | ExecutionException e) { + log.error("Failed to get all topics.", e); + } + } + } + } + return topics; + } + + public CreateTopicsResult createTopic(NewTopic topic) { + return settings.getAdminClient().createTopics(Collections.singletonList(topic)); + } + + @Override + public void destroy() { + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatisticConfig.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatisticConfig.java new file mode 100644 index 0000000..8b6aebc --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatisticConfig.java @@ -0,0 +1,29 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class KafkaConsumerStatisticConfig { + + @Value("${queue.kafka.consumer-stats.enabled:true}") + private Boolean enabled; + + @Value("${queue.kafka.consumer-stats.print-interval-ms:60000}") + private Long printIntervalMs; + + @Value("${queue.kafka.consumer-stats.kafka-response-timeout-ms:1000}") + private Long kafkaResponseTimeoutMs; +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatsService.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatsService.java new file mode 100644 index 0000000..a145ed9 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerStatsService.java @@ -0,0 +1,158 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.Builder; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.consumer.OffsetAndMetadata; +import org.apache.kafka.common.TopicPartition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") +public class KafkaConsumerStatsService { + + private final Set monitoredGroups = ConcurrentHashMap.newKeySet(); + + private final KafkaSettings kafkaSettings; + private final KafkaConsumerStatisticConfig statsConfig; + + private Consumer consumer; + private ScheduledExecutorService statsPrintScheduler; + + @PostConstruct + public void init() { + if (!statsConfig.getEnabled()) { + return; + } + this.statsPrintScheduler = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("kafka-consumer-stats", Thread.MAX_PRIORITY)); + + Properties consumerProps = kafkaSettings.toConsumerProps(null); + consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumer-stats-loader-client"); + consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer-stats-loader-client-group"); + this.consumer = new KafkaConsumer<>(consumerProps); + + startLogScheduling(); + } + + private void startLogScheduling() { + Duration timeoutDuration = Duration.ofMillis(statsConfig.getKafkaResponseTimeoutMs()); + statsPrintScheduler.scheduleWithFixedDelay(() -> { + if (!isStatsPrintRequired()) { + return; + } + for (String groupId : monitoredGroups) { + try { + Map groupOffsets = kafkaSettings.getAdminClient().listConsumerGroupOffsets(groupId).partitionsToOffsetAndMetadata() + .get(statsConfig.getKafkaResponseTimeoutMs(), TimeUnit.MILLISECONDS); + Map endOffsets = consumer.endOffsets(groupOffsets.keySet(), timeoutDuration); + + List lagTopicsStats = getTopicsStatsWithLag(groupOffsets, endOffsets); + if (!lagTopicsStats.isEmpty()) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < lagTopicsStats.size(); i++) { + builder.append(lagTopicsStats.get(i).toString()); + if (i != lagTopicsStats.size() - 1) { + builder.append(", "); + } + } + log.info("[{}] Topic partitions with lag: [{}].", groupId, builder); + } + } catch (Exception e) { + log.warn("[{}] Failed to get consumer group stats. Reason - {}.", groupId, e.getMessage()); + log.trace("Detailed error: ", e); + } + } + + }, statsConfig.getPrintIntervalMs(), statsConfig.getPrintIntervalMs(), TimeUnit.MILLISECONDS); + } + + private boolean isStatsPrintRequired() { + return log.isInfoEnabled() ; + } + + private List getTopicsStatsWithLag(Map groupOffsets, Map endOffsets) { + List consumerGroupStats = new ArrayList<>(); + for (TopicPartition topicPartition : groupOffsets.keySet()) { + long endOffset = endOffsets.get(topicPartition); + long committedOffset = groupOffsets.get(topicPartition).offset(); + long lag = endOffset - committedOffset; + if (lag != 0) { + GroupTopicStats groupTopicStats = GroupTopicStats.builder() + .topic(topicPartition.topic()) + .partition(topicPartition.partition()) + .committedOffset(committedOffset) + .endOffset(endOffset) + .lag(lag) + .build(); + consumerGroupStats.add(groupTopicStats); + } + } + return consumerGroupStats; + } + + public void registerClientGroup(String groupId) { + if (statsConfig.getEnabled() && !StringUtils.isEmpty(groupId)) { + monitoredGroups.add(groupId); + } + } + + public void unregisterClientGroup(String groupId) { + if (statsConfig.getEnabled() && !StringUtils.isEmpty(groupId)) { + monitoredGroups.remove(groupId); + } + } + + @PreDestroy + public void destroy() { + if (statsPrintScheduler != null) { + statsPrintScheduler.shutdownNow(); + } + if (consumer != null) { + consumer.close(); + } + } + + + @Builder + @Data + private static class GroupTopicStats { + private String topic; + private int partition; + private long committedOffset; + private long endOffset; + private long lag; + + @Override + public String toString() { + return "[" + + "topic=[" + topic + ']' + + ", partition=[" + partition + "]" + + ", committedOffset=[" + committedOffset + "]" + + ", endOffset=[" + endOffset + "]" + + ", lag=[" + lag + "]" + + "]"; + } + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerTemplate.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerTemplate.java new file mode 100644 index 0000000..22818d1 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaConsumerTemplate.java @@ -0,0 +1,117 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import lombok.Builder; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.springframework.util.StopWatch; +import sanbing.jcpp.infrastructure.queue.AbstractQueueConsumerTemplate; +import sanbing.jcpp.infrastructure.queue.KafkaQueueMsg; +import sanbing.jcpp.infrastructure.queue.QueueAdmin; +import sanbing.jcpp.infrastructure.queue.QueueMsg; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +@Slf4j +public class KafkaConsumerTemplate extends AbstractQueueConsumerTemplate, T> { + + private final QueueAdmin admin; + private final KafkaConsumer consumer; + private final KafkaDecoder decoder; + + private final KafkaConsumerStatsService statsService; + private final String groupId; + + @Builder + private KafkaConsumerTemplate(KafkaSettings settings, KafkaDecoder decoder, + String clientId, String groupId, String topic, + QueueAdmin admin, KafkaConsumerStatsService statsService) { + super(topic); + Properties props = settings.toConsumerProps(topic); + props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); + if (groupId != null) { + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + } + + this.statsService = statsService; + this.groupId = groupId; + + if (statsService != null) { + statsService.registerClientGroup(groupId); + } + + this.admin = admin; + this.consumer = new KafkaConsumer<>(props); + this.decoder = decoder; + } + + @Override + protected void doSubscribe(List topicNames) { + if (!topicNames.isEmpty()) { + topicNames.forEach(admin::createTopicIfNotExists); + consumer.subscribe(topicNames); + } else { + log.info("unsubscribe due to empty topic list"); + consumer.unsubscribe(); + } + } + + @Override + protected List> doPoll(long durationInMillis) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + log.trace("poll topic {} maxDuration {}", getTopic(), durationInMillis); + + ConsumerRecords records = consumer.poll(Duration.ofMillis(durationInMillis)); + + stopWatch.stop(); + log.trace("poll topic {} took {}ms", getTopic(), stopWatch.getTotalTimeMillis()); + + if (records.isEmpty()) { + return Collections.emptyList(); + } else { + List> recordList = new ArrayList<>(256); + records.forEach(recordList::add); + return recordList; + } + } + + @Override + public T decode(ConsumerRecord record) throws IOException { + return decoder.decode(new KafkaQueueMsg(record)); + } + + @Override + protected void doCommit() { + consumer.commitSync(); + } + + @Override + protected void doUnsubscribe() { + if (consumer != null) { + consumer.unsubscribe(); + consumer.close(); + } + if (statsService != null) { + statsService.unregisterClientGroup(groupId); + } + } + + @Override + public boolean isLongPollingSupported() { + return true; + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaDecoder.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaDecoder.java new file mode 100644 index 0000000..e4ad32a --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaDecoder.java @@ -0,0 +1,16 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + + +import sanbing.jcpp.infrastructure.queue.QueueMsg; + +import java.io.IOException; + +public interface KafkaDecoder { + + T decode(QueueMsg msg) throws IOException; + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaProducerTemplate.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaProducerTemplate.java new file mode 100644 index 0000000..60fc4d0 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaProducerTemplate.java @@ -0,0 +1,133 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import lombok.Builder; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.header.Header; +import org.apache.kafka.common.header.internals.RecordHeader; +import sanbing.jcpp.infrastructure.queue.QueueAdmin; +import sanbing.jcpp.infrastructure.queue.QueueCallback; +import sanbing.jcpp.infrastructure.queue.QueueMsg; +import sanbing.jcpp.infrastructure.queue.QueueProducer; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Slf4j +public class KafkaProducerTemplate implements QueueProducer { + + private final KafkaProducer producer; + + @Getter + private final String topic; + + @Getter + private final KafkaSettings settings; + + private final QueueAdmin admin; + + private final Set topics; + + @Getter + private final String clientId; + + @Builder + private KafkaProducerTemplate(KafkaSettings settings, String topic, String clientId, QueueAdmin admin) { + Properties props = settings.toProducerProps(topic); + + this.clientId = Objects.requireNonNull(clientId, "Kafka producer client.id is null"); + if (!StringUtils.isEmpty(clientId)) { + props.put(ProducerConfig.CLIENT_ID_CONFIG, clientId); + } + this.settings = settings; + + this.producer = new KafkaProducer<>(props); + this.topic = topic; + this.admin = admin; + topics = ConcurrentHashMap.newKeySet(); + } + + @Override + public void init() { + } + + void addAnalyticHeaders(List
headers) { + headers.add(new RecordHeader("_producerId", getClientId().getBytes(StandardCharsets.UTF_8))); + headers.add(new RecordHeader("_threadName", Thread.currentThread().getName().getBytes(StandardCharsets.UTF_8))); + if (log.isTraceEnabled()) { + try { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + int maxLevel = Math.min(stackTrace.length, 20); + for (int i = 2; i < maxLevel; i++) { // ignore two levels: getStackTrace and addAnalyticHeaders + headers.add(new RecordHeader("_stackTrace" + i, stackTrace[i].toString().getBytes(StandardCharsets.UTF_8))); + } + } catch (Throwable t) { + log.trace("Failed to add stacktrace headers in Kafka producer {}", getClientId(), t); + } + } + } + + @Override + public void send(TopicPartitionInfo tpi, T msg, QueueCallback callback) { + try { + createTopicIfNotExist(tpi); + String key = msg.getKey(); + byte[] data = msg.getData(); + ProducerRecord record; + List
headers = msg.getHeaders().getData().entrySet().stream().map(e -> new RecordHeader(e.getKey(), e.getValue())).collect(Collectors.toList()); + if (log.isDebugEnabled()) { + addAnalyticHeaders(headers); + } + record = new ProducerRecord<>(tpi.getFullTopicName(), null, key, data, headers); + producer.send(record, (metadata, exception) -> { + if (exception == null) { + if (callback != null) { + callback.onSuccess(new KafkaQueueMsgMetadata(metadata)); + } + } else { + if (callback != null) { + callback.onFailure(exception); + } else { + log.warn("Producer template failure: {}", exception.getMessage(), exception); + } + } + }); + } catch (Exception e) { + if (callback != null) { + callback.onFailure(e); + } else { + log.warn("Producer template failure (send method wrapper): {}", e.getMessage(), e); + } + throw e; + } + } + + private void createTopicIfNotExist(TopicPartitionInfo tpi) { + if (topics.contains(tpi)) { + return; + } + admin.createTopicIfNotExists(tpi.getFullTopicName()); + topics.add(tpi); + } + + @Override + public void stop() { + if (producer != null) { + producer.close(); + } + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaQueueMsgMetadata.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaQueueMsgMetadata.java new file mode 100644 index 0000000..02bd2c7 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaQueueMsgMetadata.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.kafka.clients.producer.RecordMetadata; +import sanbing.jcpp.infrastructure.queue.QueueMsgMetadata; + +@Data +@AllArgsConstructor +public class KafkaQueueMsgMetadata implements QueueMsgMetadata { + + private RecordMetadata metadata; +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaSettings.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaSettings.java new file mode 100644 index 0000000..fc5f3cf --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaSettings.java @@ -0,0 +1,210 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import jakarta.annotation.PreDestroy; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.CommonClientConfigs; +import org.apache.kafka.clients.admin.AdminClient; +import org.apache.kafka.clients.admin.AdminClientConfig; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.config.SslConfigs; +import org.apache.kafka.common.serialization.ByteArrayDeserializer; +import org.apache.kafka.common.serialization.ByteArraySerializer; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.util.property.JCPPProperty; +import sanbing.jcpp.infrastructure.util.property.PropertyUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +@Slf4j +@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") +@ConfigurationProperties(prefix = "queue.kafka") +@Component +public class KafkaSettings { + + @Value("${queue.kafka.bootstrap-servers}") + private String servers; + + @Value("${queue.kafka.ssl.enabled:false}") + private boolean sslEnabled; + + @Value("${queue.kafka.ssl.truststore-location:}") + private String sslTruststoreLocation; + + @Value("${queue.kafka.ssl.truststore-password:}") + private String sslTruststorePassword; + + @Value("${queue.kafka.ssl.keystore-location:}") + private String sslKeystoreLocation; + + @Value("${queue.kafka.ssl.keystore-password:}") + private String sslKeystorePassword; + + @Value("${queue.kafka.ssl.key-password:}") + private String sslKeyPassword; + + @Value("${queue.kafka.acks:all}") + private String acks; + + @Value("${queue.kafka.retries:1}") + private int retries; + + @Value("${queue.kafka.compression-type:none}") + private String compressionType; + + @Value("${queue.kafka.batch-size:16384}") + private int batchSize; + + @Value("${queue.kafka.linger-ms:1}") + private long lingerMs; + + @Value("${queue.kafka.max-request-size:1048576}") + private int maxRequestSize; + + @Value("${queue.kafka.max-in-flight-requests-per-connection:5}") + private int maxInFlightRequestsPerConnection; + + @Value("${queue.kafka.buffer-memory:33554432}") + private long bufferMemory; + + @Value("${queue.kafka.replication-factor:1}") + @Getter + private short replicationFactor; + + @Value("${queue.kafka.max-poll-records:8192}") + private int maxPollRecords; + + @Value("${queue.kafka.max-poll-interval-ms:300000}") + private int maxPollIntervalMs; + + @Value("${queue.kafka.max-partition-fetch-bytes:16777216}") + private int maxPartitionFetchBytes; + + @Value("${queue.kafka.fetch-max-bytes:134217728}") + private int fetchMaxBytes; + + @Value("${queue.kafka.request-timeout-ms:30000}") + private int requestTimeoutMs; + + @Value("${queue.kafka.session-timeout-ms:10000}") + private int sessionTimeoutMs; + + @Value("${queue.kafka.auto-offset-reset:earliest}") + private String autoOffsetReset; + + @Value("${queue.kafka.other-inline:}") + private String otherInline; + + + @Setter + private Map> consumerPropertiesPerTopic = Collections.emptyMap(); + @Setter + private Map> producerPropertiesPerTopic = Collections.emptyMap(); + + private volatile AdminClient adminClient; + + public Properties toConsumerProps(String topic) { + Properties props = toProps(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); + props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); + props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, maxPartitionFetchBytes); + props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, fetchMaxBytes); + props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPollIntervalMs); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset); + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); + + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class); + + consumerPropertiesPerTopic + .getOrDefault(topic, Collections.emptyList()) + .forEach(kv -> props.put(kv.getKey(), kv.getValue())); + + return props; + } + + public Properties toProducerProps(String topic) { + Properties props = toProps(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); + props.put(ProducerConfig.RETRIES_CONFIG, retries); + props.put(ProducerConfig.ACKS_CONFIG, acks); + props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize); + props.put(ProducerConfig.LINGER_MS_CONFIG, lingerMs); + props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class); + props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, compressionType); + props.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, maxRequestSize); + props.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, maxInFlightRequestsPerConnection); + + producerPropertiesPerTopic + .getOrDefault(topic, Collections.emptyList()) + .forEach(kv -> props.put(kv.getKey(), kv.getValue())); + + return props; + } + + Properties toProps() { + Properties props = new Properties(); + + props.put(CommonClientConfigs.REQUEST_TIMEOUT_MS_CONFIG, requestTimeoutMs); + props.put(CommonClientConfigs.SESSION_TIMEOUT_MS_CONFIG, sessionTimeoutMs); + + props.putAll(PropertyUtils.getProps(otherInline)); + + configureSSL(props); + + return props; + } + + void configureSSL(Properties props) { + if (sslEnabled) { + props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SSL"); + props.put(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG, sslTruststoreLocation); + props.put(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG, sslTruststorePassword); + props.put(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG, sslKeystoreLocation); + props.put(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG, sslKeystorePassword); + props.put(SslConfigs.SSL_KEY_PASSWORD_CONFIG, sslKeyPassword); + } + } + + public AdminClient getAdminClient() { + if (adminClient == null) { + synchronized (this) { + if (adminClient == null) { + adminClient = AdminClient.create(toAdminProps()); + } + } + } + return adminClient; + } + + protected Properties toAdminProps() { + Properties props = toProps(); + props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, servers); + props.put(AdminClientConfig.RETRIES_CONFIG, retries); + return props; + } + + @PreDestroy + private void destroy() { + if (adminClient != null) { + adminClient.close(); + } + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaTopicConfigs.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaTopicConfigs.java new file mode 100644 index 0000000..111e76e --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/kafka/KafkaTopicConfigs.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.kafka; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.util.property.PropertyUtils; + +import java.util.Map; + +@Component +@ConditionalOnProperty(prefix = "queue", value = "type", havingValue = "kafka") +public class KafkaTopicConfigs { + public static final String NUM_PARTITIONS_SETTING = "partitions"; + + @Value("${queue.kafka.topic-properties.app:}") + private String appProperties; + + @Getter + private Map appConfigs; + + @PostConstruct + private void init() { + this.appConfigs = PropertyUtils.getProps(appProperties); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java new file mode 100644 index 0000000..f61f905 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -0,0 +1,66 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.memory; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.queue.QueueMsg; + +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; + +@Component +@Slf4j +public final class DefaultInMemoryStorage implements InMemoryStorage { + private final ConcurrentHashMap> storage = new ConcurrentHashMap<>(); + + @Override + public void printStats() { + if (log.isDebugEnabled()) { + storage.forEach((topic, queue) -> { + if (!queue.isEmpty()) { + log.debug("[{}] Queue Size [{}]", topic, queue.size()); + } + }); + } + } + + @Override + public int getLagTotal() { + return storage.values().stream().map(BlockingQueue::size).reduce(0, Integer::sum); + } + + @Override + public int getLag(String topic) { + return Optional.ofNullable(storage.get(topic)).map(Collection::size).orElse(0); + } + + @Override + public boolean put(String topic, QueueMsg msg) { + return storage.computeIfAbsent(topic, (t) -> new LinkedBlockingQueue<>()).add(msg); + } + + @Override + public List get(String topic) throws InterruptedException { + final BlockingQueue queue = storage.get(topic); + if (queue != null) { + final QueueMsg firstMsg = queue.poll(); + if (firstMsg != null) { + final int queueSize = queue.size(); + if (queueSize > 0) { + final List entities = new ArrayList<>(Math.min(queueSize, 999) + 1); + entities.add(firstMsg); + queue.drainTo(entities, 999); + return entities; + } + return Collections.singletonList(firstMsg); + } + } + return Collections.emptyList(); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueConsumer.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueConsumer.java new file mode 100644 index 0000000..181c12f --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueConsumer.java @@ -0,0 +1,106 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.memory; + +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.queue.QueueConsumer; +import sanbing.jcpp.infrastructure.queue.QueueMsg; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +public class InMemoryQueueConsumer implements QueueConsumer { + private final InMemoryStorage storage; + private volatile Set partitions; + private volatile boolean stopped; + private volatile boolean subscribed; + + public InMemoryQueueConsumer(InMemoryStorage storage, String topic) { + this.storage = storage; + this.topic = topic; + stopped = false; + } + + private final String topic; + + @Override + public String getTopic() { + return topic; + } + + @Override + public void subscribe() { + partitions = Collections.singleton(new TopicPartitionInfo(topic, null, true)); + subscribed = true; + } + + @Override + public void subscribe(Set partitions) { + this.partitions = partitions; + subscribed = true; + } + + @Override + public void stop() { + stopped = true; + } + + @Override + public void unsubscribe() { + stopped = true; + subscribed = false; + } + + @Override + public List poll(long durationInMillis) { + if (subscribed) { + @SuppressWarnings("unchecked") + List messages = partitions + .stream() + .map(tpi -> { + try { + return storage.get(tpi.getFullTopicName()); + } catch (InterruptedException e) { + if (!stopped) { + log.error("Queue was interrupted.", e); + } + return Collections.emptyList(); + } + }) + .flatMap(List::stream) + .map(msg -> (T) msg).collect(Collectors.toList()); + if (!messages.isEmpty()) { + return messages; + } + try { + Thread.sleep(durationInMillis); + } catch (InterruptedException e) { + if (!stopped) { + log.error("Failed to sleep.", e); + } + } + } + return Collections.emptyList(); + } + + @Override + public void commit() { + } + + @Override + public boolean isStopped() { + return stopped; + } + + @Override + public List getFullTopicNames() { + return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueProducer.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueProducer.java new file mode 100644 index 0000000..594b26d --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryQueueProducer.java @@ -0,0 +1,48 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.memory; + +import lombok.Data; +import sanbing.jcpp.infrastructure.queue.QueueCallback; +import sanbing.jcpp.infrastructure.queue.QueueMsg; +import sanbing.jcpp.infrastructure.queue.QueueProducer; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; + +@Data +public class InMemoryQueueProducer implements QueueProducer { + private final InMemoryStorage storage; + + private final String topic; + + public InMemoryQueueProducer(InMemoryStorage storage, String topic) { + this.storage = storage; + this.topic = topic; + } + + @Override + public void init() { + + } + + @Override + public void send(TopicPartitionInfo tpi, T msg, QueueCallback callback) { + boolean result = storage.put(tpi.getFullTopicName(), msg); + if (result) { + if (callback != null) { + callback.onSuccess(null); + } + } else { + if (callback != null) { + callback.onFailure(new RuntimeException("Failure add msg to InMemoryQueue")); + } + } + } + + @Override + public void stop() { + + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryStorage.java new file mode 100644 index 0000000..cc6b60a --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/InMemoryStorage.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.memory; + + +import sanbing.jcpp.infrastructure.queue.QueueMsg; + +import java.util.List; + +public interface InMemoryStorage { + + void printStats(); + + int getLagTotal(); + + int getLag(String topic); + + boolean put(String topic, QueueMsg msg); + + List get(String topic) throws InterruptedException; + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java new file mode 100644 index 0000000..8a6eec5 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.processing; + +import lombok.Getter; +import sanbing.jcpp.infrastructure.queue.ProtoQueueMsg; + +import java.util.UUID; + +public class IdMsgPair { + @Getter + final UUID uuid; + @Getter + final ProtoQueueMsg msg; + + public IdMsgPair(UUID uuid, ProtoQueueMsg msg) { + this.uuid = uuid; + this.msg = msg; + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/AppQueueFactory.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/AppQueueFactory.java new file mode 100644 index 0000000..ce80fe5 --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/AppQueueFactory.java @@ -0,0 +1,18 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.provider; + + +import sanbing.jcpp.infrastructure.queue.ProtoQueueMsg; +import sanbing.jcpp.infrastructure.queue.QueueConsumer; +import sanbing.jcpp.infrastructure.queue.QueueProducer; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +public interface AppQueueFactory { + + QueueConsumer> createProtocolUplinkMsgConsumer(); + + QueueProducer> createProtocolUplinkMsgProducer(String topic); +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java new file mode 100644 index 0000000..82cb1ad --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java @@ -0,0 +1,48 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.provider; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.queue.ProtoQueueMsg; +import sanbing.jcpp.infrastructure.queue.QueueConsumer; +import sanbing.jcpp.infrastructure.queue.QueueProducer; +import sanbing.jcpp.infrastructure.queue.memory.InMemoryQueueConsumer; +import sanbing.jcpp.infrastructure.queue.memory.InMemoryQueueProducer; +import sanbing.jcpp.infrastructure.queue.memory.InMemoryStorage; +import sanbing.jcpp.infrastructure.queue.settings.QueueAppSettings; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +@Slf4j +@Component +@ConditionalOnExpression("'${queue.type:null}'=='memory' && '${service.type:null}'=='monolith'") +public class InMemoryAppQueueFactory implements AppQueueFactory { + + private final InMemoryStorage storage; + private final QueueAppSettings appSettings; + + public InMemoryAppQueueFactory(InMemoryStorage storage, QueueAppSettings appSettings) { + this.storage = storage; + this.appSettings = appSettings; + } + + @Override + public QueueConsumer> createProtocolUplinkMsgConsumer() { + return new InMemoryQueueConsumer<>(storage, appSettings.getTopic()); + } + + @Override + public QueueProducer> createProtocolUplinkMsgProducer(String topic) { + return new InMemoryQueueProducer<>(storage, topic); + } + + @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") + private void printInMemoryStats() { + storage.printStats(); + } + +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java new file mode 100644 index 0000000..155e15b --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java @@ -0,0 +1,83 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.provider; + +import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.queue.ProtoQueueMsg; +import sanbing.jcpp.infrastructure.queue.QueueAdmin; +import sanbing.jcpp.infrastructure.queue.QueueConsumer; +import sanbing.jcpp.infrastructure.queue.QueueProducer; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.queue.kafka.*; +import sanbing.jcpp.infrastructure.queue.settings.QueueAppSettings; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +@Component +@ConditionalOnExpression("'${queue.type:null}'=='kafka'") +public class KafkaAppQueueFactory implements AppQueueFactory { + + private final KafkaSettings kafkaSettings; + private final QueueAppSettings appSettings; + private final KafkaConsumerStatsService consumerStatsService; + private final ServiceInfoProvider serviceInfoProvider; + + private final QueueAdmin appAdmin; + + public KafkaAppQueueFactory(KafkaSettings kafkaSettings, + ServiceInfoProvider serviceInfoProvider, + QueueAppSettings appSettings, + KafkaConsumerStatsService consumerStatsService, + KafkaTopicConfigs kafkaTopicConfigs) { + this.kafkaSettings = kafkaSettings; + this.serviceInfoProvider = serviceInfoProvider; + this.appSettings = appSettings; + this.consumerStatsService = consumerStatsService; + + this.appAdmin = new KafkaAdmin(kafkaSettings, kafkaTopicConfigs.getAppConfigs()); + } + + + @Override + public QueueConsumer> createProtocolUplinkMsgConsumer() { + KafkaConsumerTemplate.KafkaConsumerTemplateBuilder> consumerBuilder = KafkaConsumerTemplate.builder(); + consumerBuilder.settings(kafkaSettings); + consumerBuilder.topic(appSettings.getTopic()); + consumerBuilder.clientId("protocol-uplink-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId("protocol-uplink-consumer"); + if (appSettings.getDecoder() == QueueAppSettings.DecoderType.protobuf) { + consumerBuilder.decoder(msg -> new ProtoQueueMsg<>(msg.getKey(), UplinkQueueMessage.parseFrom(msg.getData()), msg.getHeaders())); + } else { + consumerBuilder.decoder(msg -> { + UplinkQueueMessage.Builder builder = UplinkQueueMessage.newBuilder(); + JsonFormat.parser().merge(new String(msg.getData()), builder); + return new ProtoQueueMsg<>(msg.getKey(), builder.build(), msg.getHeaders()); + }); + } + consumerBuilder.admin(appAdmin); + consumerBuilder.statsService(consumerStatsService); + return consumerBuilder.build(); + } + + @Override + public QueueProducer> createProtocolUplinkMsgProducer(String topic) { + KafkaProducerTemplate.KafkaProducerTemplateBuilder> requestBuilder = KafkaProducerTemplate.builder(); + requestBuilder.settings(kafkaSettings); + requestBuilder.clientId("protocol-to-app-" + serviceInfoProvider.getServiceId()); + requestBuilder.topic(topic); + requestBuilder.admin(appAdmin); + return requestBuilder.build(); + } + + + @PreDestroy + private void destroy() { + if (appAdmin != null) { + appAdmin.destroy(); + } + } +} diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/settings/QueueAppSettings.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/settings/QueueAppSettings.java new file mode 100644 index 0000000..74415fb --- /dev/null +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/settings/QueueAppSettings.java @@ -0,0 +1,30 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.queue.settings; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +@Lazy +@Data +@Component +public class QueueAppSettings { + + @Value("${queue.app.topic}") + private String topic; + + @Value("${queue.app.partitions}") + private int partitions; + + @Value("${queue.app.decoder:protobuf}") + private DecoderType decoder; + + public enum DecoderType { + protobuf, + json + } +} diff --git a/jcpp-infrastructure-stats/pom.xml b/jcpp-infrastructure-stats/pom.xml new file mode 100644 index 0000000..bf27d95 --- /dev/null +++ b/jcpp-infrastructure-stats/pom.xml @@ -0,0 +1,57 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-infrastructure-stats + jar + JChargePointProtocol Infrastructure Stats Module + 基础监控模块 + + + ${basedir}/.. + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-undertow + + + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + org.apache.commons + commons-lang3 + + + + diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultCounter.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultCounter.java new file mode 100644 index 0000000..83f7b36 --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultCounter.java @@ -0,0 +1,37 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +import io.micrometer.core.instrument.Counter; + +import java.util.concurrent.atomic.AtomicInteger; + +public class DefaultCounter { + private final AtomicInteger aiCounter; + private final Counter micrometerCounter; + + public DefaultCounter(AtomicInteger aiCounter, Counter micrometerCounter) { + this.aiCounter = aiCounter; + this.micrometerCounter = micrometerCounter; + } + + public void increment() { + aiCounter.incrementAndGet(); + micrometerCounter.increment(); + } + + public void clear() { + aiCounter.set(0); + } + + public int get() { + return aiCounter.get(); + } + + public void add(int delta){ + aiCounter.addAndGet(delta); + micrometerCounter.increment(delta); + } +} diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultMessagesStats.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultMessagesStats.java new file mode 100644 index 0000000..d0ea727 --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultMessagesStats.java @@ -0,0 +1,54 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +public class DefaultMessagesStats implements MessagesStats { + private final StatsCounter totalCounter; + private final StatsCounter successfulCounter; + private final StatsCounter failedCounter; + + public DefaultMessagesStats(StatsCounter totalCounter, StatsCounter successfulCounter, StatsCounter failedCounter) { + this.totalCounter = totalCounter; + this.successfulCounter = successfulCounter; + this.failedCounter = failedCounter; + } + + @Override + public void incrementTotal(int amount) { + totalCounter.add(amount); + } + + @Override + public void incrementSuccessful(int amount) { + successfulCounter.add(amount); + } + + @Override + public void incrementFailed(int amount) { + failedCounter.add(amount); + } + + @Override + public int getTotal() { + return totalCounter.get(); + } + + @Override + public int getSuccessful() { + return successfulCounter.get(); + } + + @Override + public int getFailed() { + return failedCounter.get(); + } + + @Override + public void reset() { + totalCounter.clear(); + successfulCounter.clear(); + failedCounter.clear(); + } +} diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java new file mode 100644 index 0000000..8126f85 --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java @@ -0,0 +1,124 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +import io.micrometer.common.util.StringUtils; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.concurrent.atomic.AtomicInteger; + +@Service +public class DefaultStatsFactory implements StatsFactory { + private static final String TOTAL_MSGS = "totalMsgs"; + private static final String SUCCESSFUL_MSGS = "successfulMsgs"; + private static final String FAILED_MSGS = "failedMsgs"; + + private static final String STATS_NAME_TAG = "statsName"; + + private static final Counter STUB_COUNTER = new StubCounter(); + + @Resource + private MeterRegistry meterRegistry; + + @Value("${metrics.enabled:true}") + private Boolean metricsEnabled; + + @Value("${metrics.timer.percentiles:1.0}") + private String timerPercentilesStr; + + private double[] timerPercentiles; + + @PostConstruct + public void init() { + if (StringUtils.isNotEmpty(timerPercentilesStr)) { + String[] split = timerPercentilesStr.split(","); + timerPercentiles = new double[split.length]; + for (int i = 0; i < split.length; i++) { + timerPercentiles[i] = Double.parseDouble(split[i]); + } + } + } + + + @Override + public StatsCounter createStatsCounter(String key, String statsName, String... otherTags) { + String[] tags = getTags(statsName, otherTags); + return new StatsCounter( + new AtomicInteger(0), + metricsEnabled ? meterRegistry.counter(key, tags) : STUB_COUNTER, + statsName + ); + } + + @Override + public DefaultCounter createDefaultCounter(String key, String... tags) { + return new DefaultCounter( + new AtomicInteger(0), + metricsEnabled ? + meterRegistry.counter(key, tags) + : STUB_COUNTER + ); + } + + @Override + public MessagesStats createMessagesStats(String key, String... tags) { + StatsCounter totalCounter = createStatsCounter(key, TOTAL_MSGS, tags); + StatsCounter successfulCounter = createStatsCounter(key, SUCCESSFUL_MSGS, tags); + StatsCounter failedCounter = createStatsCounter(key, FAILED_MSGS, tags); + return new DefaultMessagesStats(totalCounter, successfulCounter, failedCounter); + } + + @Override + public Timer createTimer(String key, String... tags) { + Timer.Builder timerBuilder = Timer.builder(key) + .tags(tags) + .publishPercentiles(); + if (timerPercentiles != null && timerPercentiles.length > 0) { + timerBuilder.publishPercentiles(timerPercentiles); + } + return timerBuilder.register(meterRegistry); + } + + @Override + public T createGauge(String key, T number, String... tags) { + return meterRegistry.gauge(key, Tags.of(tags), number); + } + + private static String[] getTags(String statsName, String[] otherTags) { + String[] tags = new String[]{STATS_NAME_TAG, statsName}; + if (otherTags.length > 0) { + if (otherTags.length % 2 != 0) { + throw new IllegalArgumentException("Invalid tags array size"); + } + tags = ArrayUtils.addAll(tags, otherTags); + } + return tags; + } + + + private static class StubCounter implements Counter { + @Override + public void increment(double amount) { + } + + @Override + public double count() { + return 0; + } + + @Override + public Id getId() { + return null; + } + } +} diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/MessagesStats.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/MessagesStats.java new file mode 100644 index 0000000..83d9632 --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/MessagesStats.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +public interface MessagesStats { + default void incrementTotal() { + incrementTotal(1); + } + + void incrementTotal(int amount); + + default void incrementSuccessful() { + incrementSuccessful(1); + } + + void incrementSuccessful(int amount); + + default void incrementFailed() { + incrementFailed(1); + } + + void incrementFailed(int amount); + + int getTotal(); + + int getSuccessful(); + + int getFailed(); + + void reset(); +} diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsCounter.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsCounter.java new file mode 100644 index 0000000..2e32e84 --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsCounter.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +import io.micrometer.core.instrument.Counter; + +import java.util.concurrent.atomic.AtomicInteger; + +public class StatsCounter extends DefaultCounter { + private final String name; + + public StatsCounter(AtomicInteger aiCounter, Counter micrometerCounter, String name) { + super(aiCounter, micrometerCounter); + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsFactory.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsFactory.java new file mode 100644 index 0000000..970c9bf --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsFactory.java @@ -0,0 +1,59 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +import io.micrometer.core.instrument.Timer; + +public interface StatsFactory { + + /** + * 创建状态计数器,默认带一个statsName的Tag,并可以自定义扩展其他Tag + * + * @param key 指标名 + * @param statsName statsName的标签值 + * @param otherTags 其他Tag键值对,参数个数需要是偶数 + * @return + */ + StatsCounter createStatsCounter(String key, String statsName, String... otherTags); + + /** + * 创建计数器,可自定义Tag + * + * @param key 指标名 + * @param tags 自定义Tag键值对,参数个数需要是偶数 + * @return + */ + DefaultCounter createDefaultCounter(String key, String... tags); + + /** + * 创建消息计数器,消息计数器默认包含三种状态(总数、成功数、失败数) + * + * @param key 指标名 + * @param tags 自定义Tag键值对,参数个数需要是偶数 + * @return + */ + MessagesStats createMessagesStats(String key, String... tags); + + /** + * 创建计时器 + * + * @param key 指标名 + * @param tags 自定义Tag键值对,参数个数需要是偶数 + * @return + */ + Timer createTimer(String key, String... tags); + + /** + * 创建计量器,用于记录某个值的当前状态,可以是瞬时数值 + * + * @param key 指标名 + * @param number 初始值 + * @param tags 自定义Tag键值对,参数个数需要是偶数 + * @return + * @param + */ + T createGauge(String key, T number, String... tags); + +} diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsTimer.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsTimer.java new file mode 100644 index 0000000..c5de588 --- /dev/null +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/StatsTimer.java @@ -0,0 +1,44 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.stats; + +import io.micrometer.core.instrument.Timer; +import lombok.Getter; + +import java.util.concurrent.TimeUnit; + +public class StatsTimer { + + @Getter + private final String name; + private final Timer timer; + + private int count; + private long totalTime; + + public StatsTimer(String name, Timer micrometerTimer) { + this.name = name; + this.timer = micrometerTimer; + } + + public void record(long timeMs) { + count++; + totalTime += timeMs; + timer.record(timeMs, TimeUnit.MILLISECONDS); + } + + public double getAvg() { + if (count == 0) { + return 0.0; + } + return (double) totalTime / count; + } + + public void reset() { + count = 0; + totalTime = 0; + } + +} diff --git a/jcpp-infrastructure-util/pom.xml b/jcpp-infrastructure-util/pom.xml new file mode 100644 index 0000000..cbfbe6c --- /dev/null +++ b/jcpp-infrastructure-util/pom.xml @@ -0,0 +1,81 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-infrastructure-util + jar + JChargePointProtocol Infrastructure Util Module + 基础工具模块 + + + ${basedir}/.. + + + + + org.springframework.boot + spring-boot-starter-json + + + org.springframework.boot + spring-boot-starter-log4j2 + + + com.lmax + disruptor + + + com.google.guava + guava + ${guava.version} + + + org.apache.commons + commons-lang3 + + + cn.hutool + hutool-core + + + + jakarta.validation + jakarta.validation-api + + + org.hibernate.validator + hibernate-validator + + + org.glassfish + jakarta.el + + + jakarta.annotation + jakarta.annotation-api + + + com.github.oshi + oshi-core + + + io.netty + netty-buffer + + + + diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPHashUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPHashUtil.java new file mode 100644 index 0000000..f296ec8 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPHashUtil.java @@ -0,0 +1,34 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util; + +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +/** + * @author baigod + */ +public class JCPPHashUtil { + public static HashFunction forName(String name) { + return switch (name) { + case "murmur3_32" -> Hashing.murmur3_32_fixed(); + case "murmur3_128" -> Hashing.murmur3_128(); + case "sha256" -> Hashing.sha256(); + default -> throw new IllegalArgumentException("Can't find hash function with name " + name); + }; + } + + public static int hash(HashFunction hashFunction, String key) { + return hashFunction.hashString(key, StandardCharsets.UTF_8).asInt(); + } + + public static int hash(HashFunction hashFunction, UUID key) { + return hashFunction.hashString(key.toString(), StandardCharsets.UTF_8).asInt(); + } + +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPPair.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPPair.java new file mode 100644 index 0000000..32c2622 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/JCPPPair.java @@ -0,0 +1,19 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class JCPPPair { + private S first; + private T second; + + public static JCPPPair of(S first, T second) { + return new JCPPPair<>(first, second); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/SystemUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/SystemUtil.java new file mode 100644 index 0000000..571e8f5 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/SystemUtil.java @@ -0,0 +1,95 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util; + +import lombok.extern.slf4j.Slf4j; +import oshi.SystemInfo; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; + +@Slf4j +public class SystemUtil { + + private static final HardwareAbstractionLayer HARDWARE; + + static { + HARDWARE = new SystemInfo().getHardware(); + } + + public static Optional getMemoryUsage() { + try { + GlobalMemory memory = HARDWARE.getMemory(); + long total = memory.getTotal(); + long available = memory.getAvailable(); + return Optional.of(toPercent(total - available, total)); + } catch (Throwable e) { + log.debug("Failed to get memory usage!!!", e); + } + return Optional.empty(); + } + + public static Optional getTotalMemory() { + try { + return Optional.of(HARDWARE.getMemory().getTotal()); + } catch (Throwable e) { + log.debug("Failed to get total memory!!!", e); + } + return Optional.empty(); + } + + public static Optional getCpuUsage() { + try { + return Optional.of((int) (HARDWARE.getProcessor().getSystemCpuLoad(1000) * 100.0)); + } catch (Throwable e) { + log.debug("Failed to get cpu usage!!!", e); + } + return Optional.empty(); + } + + public static Optional getCpuCount() { + try { + return Optional.of(HARDWARE.getProcessor().getLogicalProcessorCount()); + } catch (Throwable e) { + log.debug("Failed to get total cpu count!!!", e); + } + return Optional.empty(); + } + + public static Optional getDiscSpaceUsage() { + try { + FileStore store = Files.getFileStore(Paths.get("/")); + long total = store.getTotalSpace(); + long available = store.getUsableSpace(); + return Optional.of(toPercent(total - available, total)); + } catch (Throwable e) { + log.debug("Failed to get free disc space!!!", e); + } + return Optional.empty(); + } + + public static Optional getTotalDiscSpace() { + try { + FileStore store = Files.getFileStore(Paths.get("/")); + return Optional.of(store.getTotalSpace()); + } catch (Throwable e) { + log.debug("Failed to get total disc space!!!", e); + } + return Optional.empty(); + } + + private static int toPercent(long used, long total) { + BigDecimal u = new BigDecimal(used); + BigDecimal t = new BigDecimal(total); + BigDecimal i = new BigDecimal(100); + return u.multiply(i).divide(t, RoundingMode.HALF_UP).intValue(); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AfterStartUp.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AfterStartUp.java new file mode 100644 index 0000000..afa3bef --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AfterStartUp.java @@ -0,0 +1,28 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.annotation; + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.AliasFor; +import org.springframework.core.annotation.Order; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@EventListener(ApplicationReadyEvent.class) +@Order +public @interface AfterStartUp { + + int DISCOVERY_SERVICE = 1; + int REGULAR_SERVICE = 2; + + @AliasFor(annotation = Order.class, attribute = "value") + int order(); +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AppComponent.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AppComponent.java new file mode 100644 index 0000000..c6f6fae --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/AppComponent.java @@ -0,0 +1,25 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.annotation; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author baigod + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='app'") +@Component +public @interface AppComponent { + + +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/ProtocolComponent.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/ProtocolComponent.java new file mode 100644 index 0000000..1b56966 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/annotation/ProtocolComponent.java @@ -0,0 +1,49 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.annotation; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.annotation.AliasFor; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.util.annotation.ProtocolComponent.ProtocolCondition; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author baigod + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Conditional(ProtocolCondition.class) +@Component +public @interface ProtocolComponent { + + @AliasFor(annotation = Component.class) + String value() default ""; + + class ProtocolCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + if (!metadata.isAnnotated(ProtocolComponent.class.getName())) { + return true; + } + + String serviceType = context.getEnvironment().getProperty("service.type", "null"); + + String protocolName = (String) metadata.getAnnotationAttributes(ProtocolComponent.class.getName()).get("value"); + + String enabled = context.getEnvironment().getProperty("service.protocols." + protocolName + ".enabled", "false"); + + return ("monolith".equals(serviceType) || "protocol".equals(serviceType)) && "true".equals(enabled); + } + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPAsynchron.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPAsynchron.java new file mode 100644 index 0000000..04a37ce --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPAsynchron.java @@ -0,0 +1,54 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.async; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; + +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +public class JCPPAsynchron { + + public static void withCallback(ListenableFuture future, Consumer onSuccess, + Consumer onFailure) { + withCallback(future, onSuccess, onFailure, null); + } + + public static void withCallback(ListenableFuture future, Consumer onSuccess, + Consumer onFailure, Executor executor) { + FutureCallback callback = new FutureCallback<>() { + @Override + public void onSuccess(T result) { + try { + onSuccess.accept(result); + } catch (Throwable th) { + onFailure(th); + } + } + + @Override + public void onFailure(Throwable t) { + onFailure.accept(t); + } + }; + Futures.addCallback(future, callback, Objects.requireNonNullElseGet(executor, MoreExecutors::directExecutor)); + } + + public static ListenableFuture submit(Callable task, Consumer onSuccess, Consumer onFailure, Executor executor) { + return submit(task, onSuccess, onFailure, executor, null); + } + + public static ListenableFuture submit(Callable task, Consumer onSuccess, Consumer onFailure, Executor executor, Executor callbackExecutor) { + ListenableFuture future = Futures.submit(task, executor); + withCallback(future, onSuccess, onFailure, callbackExecutor); + return future; + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutors.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutors.java new file mode 100644 index 0000000..7a92486 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutors.java @@ -0,0 +1,27 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.async; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; + +public class JCPPExecutors { + + public static ExecutorService newWorkStealingPool(int parallelism, String namePrefix) { + return new ForkJoinPool(parallelism, + new JCPPForkJoinWorkerThreadFactory(namePrefix), + null, true); + } + + public static ExecutorService newWorkStealingPool(int parallelism, Class clazz) { + return newWorkStealingPool(parallelism, clazz.getSimpleName()); + } + + public static ExecutorService newVirtualThreadPool(String namePrefix) { + return Executors.newThreadPerTaskExecutor(new JCPPVirtualThreadFactory(namePrefix)); + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPForkJoinWorkerThreadFactory.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPForkJoinWorkerThreadFactory.java new file mode 100644 index 0000000..c2758c7 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPForkJoinWorkerThreadFactory.java @@ -0,0 +1,30 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.async; + +import lombok.NonNull; +import lombok.ToString; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.atomic.AtomicLong; + +@ToString +public class JCPPForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private final String namePrefix; + private final AtomicLong threadNumber = new AtomicLong(1); + + public JCPPForkJoinWorkerThreadFactory(@NonNull String namePrefix) { + this.namePrefix = namePrefix; + } + + @Override + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setContextClassLoader(this.getClass().getClassLoader()); + thread.setName(namePrefix + "-" + thread.getPoolIndex() + "-" + threadNumber.getAndIncrement()); + return thread; + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPThreadFactory.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPThreadFactory.java new file mode 100644 index 0000000..d6b02c3 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPThreadFactory.java @@ -0,0 +1,46 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.async; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import java.util.concurrent.ThreadFactory; + +public class JCPPThreadFactory { + public static final String THREAD_TOPIC_SEPARATOR = " | "; + + public static ThreadFactory forName(String name) { + return new ThreadFactoryBuilder() + .setNameFormat(name) + .setDaemon(true) + .setPriority(Thread.NORM_PRIORITY) + .build(); + } + + public static ThreadFactory forName(String name, int priority) { + return new ThreadFactoryBuilder() + .setNameFormat(name) + .setDaemon(true) + .setPriority(priority) + .build(); + } + public static void updateCurrentThreadName(String threadSuffix) { + String name = Thread.currentThread().getName(); + int spliteratorIndex = name.indexOf(THREAD_TOPIC_SEPARATOR); + if (spliteratorIndex > 0) { + name = name.substring(0, spliteratorIndex); + } + name = name + THREAD_TOPIC_SEPARATOR + threadSuffix; + Thread.currentThread().setName(name); + } + + public static void addThreadNamePrefix(String prefix) { + String name = Thread.currentThread().getName(); + name = prefix + "-" + name; + Thread.currentThread().setName(name); + } + + +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java new file mode 100644 index 0000000..2c88144 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.async; + +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +public class JCPPVirtualThreadFactory implements ThreadFactory { + private final String namePrefix; + private final AtomicLong threadNumber = new AtomicLong(1); + + public JCPPVirtualThreadFactory(String namePrefix) { + this.namePrefix = namePrefix; + } + + @Override + public Thread newThread(Runnable r) { + return Thread.ofVirtual().name(namePrefix + "-" + threadNumber.getAndIncrement()).unstarted(new TracerRunnable(r)); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/BCDUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/BCDUtil.java new file mode 100644 index 0000000..0fde9eb --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/BCDUtil.java @@ -0,0 +1,172 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.codec; + + +public class BCDUtil { + private static final String HEX = "0123456789ABCDEF"; + + /** + * 十进制 转 BCD字节数组 + * + * @param num long (8字节) + * @return byte[] + */ + public static byte[] longToBcdBytes(long num) { + int digits = 0; + long temp = num; + while (temp != 0) { + digits++; + temp /= 10; + } + int byteLen = digits % 2 == 0 ? digits / 2 : (digits + 1) / 2; + byte[] bcd = new byte[byteLen]; + for (int i = 0; i < digits; i++) { + byte tmp = (byte) (num % 10); + if (i % 2 == 0) { + bcd[i / 2] = tmp; + } else { + bcd[i / 2] |= (byte) (tmp << 4); + } + num /= 10; + } + for (int i = 0; i < byteLen / 2; i++) { + byte tmp = bcd[i]; + bcd[i] = bcd[byteLen - i - 1]; + bcd[byteLen - i - 1] = tmp; + } + return bcd; + } + + /** + * BCD字节数组 转 十进制 + * + * @param bcd byte[] + * @return long + */ + public static long bcdBytesToLong(byte[] bcd) { + return Long.parseLong(BCDUtil.toString(bcd)); + } + + /** + * bcd字节数组 转 数字字符串 + * + * @param bcd byte[] + * @return String + */ + public static String toString(byte[] bcd) { + StringBuilder sb = new StringBuilder(); + for (byte b : bcd) { + sb.append(toString(b)); + } + return sb.toString(); + } + + /** + * 单个字节BCD 转 数字字符串 + * + * @param bcd byte + * @return String + */ + public static String toString(byte bcd) { + StringBuilder sb = new StringBuilder(); + byte high = (byte) (bcd & 0xf0); + high >>>= (byte) 4; + high = (byte) (high & 0x0f); + byte low = (byte) (bcd & 0x0f); + + sb.append(high); + sb.append(low); + return sb.toString(); + } + + /** + * 数字字符串 转 BCD字节数组 + * + * @param str 数字字符串 + * @return BCD字节数组 + */ + public static byte[] numStrToBcdBytes(String str) { + //若为奇数,补0为偶 + if ((str.length() & 0x1) == 1) { + str = "0" + str; + } + byte[] ret = new byte[str.length() / 2]; + byte[] bs = str.getBytes(); + for (int i = 0; i < ret.length; i++) { + byte high = ascII2Bcd(bs[2 * i]); + byte low = ascII2Bcd(bs[2 * i + 1]); + ret[i] = (byte) ((high << 4) | low); + } + return ret; + } + + public static byte ascII2Bcd(byte asc) { + if ((asc >= '0') && (asc <= '9')) + return (byte) (asc - '0'); + else if ((asc >= 'A') && (asc <= 'F')) + return (byte) (asc - 'A' + 10); + else if ((asc >= 'a') && (asc <= 'f')) + return (byte) (asc - 'a' + 10); + else + return (byte) (asc - 48); + } + + /** + * BCD 转 数字 + * + * @param bcd byte + * @return int + */ + public static int bcdByteToInt(byte bcd) { + return ((bcd & 0xF0) >>> 4) * 10 + (bcd & 0x0F); + } + + + /** + * char to byte + * + * @param c char + * @return byte + */ + private static byte charToByte(char c) { + return (byte) HEX.indexOf(c); + } + + /** + * Hex 转 BCD字节数组 + * + * @param hex String + * @return BCD字节数组 + */ + public static byte[] toBytes(String hex) { + int len = (hex.length() / 2); + byte[] result = new byte[len]; + char[] cr = hex.toCharArray(); + for (int i = 0; i < len; i++) { + int pos = i * 2; + result[i] = (byte) (charToByte(cr[pos]) << 4 | charToByte(cr[pos + 1])); + } + return result; + } + + /** + * BCD字节数组 转 Hex + * + * @param bcd BCD字节数组 + * @return Hex + */ + public static String bcdBytesToHex(byte[] bcd) { + StringBuilder sb = new StringBuilder(); + for (byte b : bcd) { + int highNibble = (b >> 4) & 0x0F; + int lowNibble = b & 0x0F; + sb.append(Integer.toHexString(highNibble)); + sb.append(Integer.toHexString(lowNibble)); + } + return sb.toString().toUpperCase(); + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/ByteUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/ByteUtil.java new file mode 100644 index 0000000..3575809 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/ByteUtil.java @@ -0,0 +1,81 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.codec; + +import cn.hutool.core.io.checksum.crc16.CRC16Modbus; +import io.netty.buffer.ByteBuf; +import sanbing.jcpp.infrastructure.util.JCPPPair; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +/** + * @author baigod + */ +public class ByteUtil { + + public static byte[] uuidToBytes(UUID uuid) { + ByteBuffer buf = ByteBuffer.allocate(16); + buf.putLong(uuid.getMostSignificantBits()); + buf.putLong(uuid.getLeastSignificantBits()); + return buf.array(); + } + + public static UUID bytesToUuid(byte[] bytes) { + ByteBuffer bb = ByteBuffer.wrap(bytes); + long firstLong = bb.getLong(); + long secondLong = bb.getLong(); + return new UUID(firstLong, secondLong); + } + + public static byte[] stringToBytes(String string) { + return string.getBytes(StandardCharsets.UTF_8); + } + + public static String bytesToString(byte[] data) { + return new String(data, StandardCharsets.UTF_8); + } + + public static byte[] longToBytes(long x) { + ByteBuffer longBuffer = ByteBuffer.allocate(Long.BYTES); + longBuffer.putLong(0, x); + return longBuffer.array(); + } + + public static long bytesToLong(byte[] bytes) { + return ByteBuffer.wrap(bytes).getLong(); + } + + /** + * 计算校验和 + */ + public static int crcSum(byte[] data) { + CRC16Modbus crc16Modbus = new CRC16Modbus(); + crc16Modbus.update(data); + return (int) crc16Modbus.getValue(); + } + + /** + * 验证校验和 + */ + public static JCPPPair checkCrcSum(byte[] data, int checkSum) { + int expectedCs = crcSum(data); + return JCPPPair.of(expectedCs == checkSum, expectedCs); + } + + /** + * ByteBuf转byte数组 + * + * @param byteBuf + * @return + */ + public static byte[] toBytes(ByteBuf byteBuf) { + int msgLength = byteBuf.readableBytes(); + byte[] bytes = new byte[msgLength]; + byteBuf.readBytes(bytes); + return bytes; + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtil.java new file mode 100644 index 0000000..83c02fc --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtil.java @@ -0,0 +1,65 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.codec; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; + +public class CP56Time2aUtil { + /** + * 解码 CP56Time2a 字节数组为 Instant 对象 + * + * @param bytes 字节数组 + * @return Instant 对象 + */ + public static Instant decode(byte[] bytes) { + // 将字节数组解释为各个时间部分 + int milliseconds = ((bytes[0] & 0xFF) + ((bytes[1] & 0xFF) << 8)); // 处理字节的无符号值 + int minutes = bytes[2] & 0x3F; + int hours = bytes[3] & 0x1F; + int days = bytes[4] & 0x1F; + int months = bytes[5] & 0x0F; + int years = bytes[6] & 0x7F; + + // 将 CP56Time2a 转换为 LocalDateTime + LocalDateTime dateTime = LocalDateTime.of( + years + 2000, + months, + days, + hours, + minutes, + milliseconds / 1000 // 秒数 + ); + + // 返回对应的 Instant 对象 + return dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + /** + * 编码 Instant 对象为 CP56Time2a 字节数组 + * + * @param instant Instant 对象 + * @return 字节数组 + */ + public static byte[] encode(Instant instant) { + // 将 Instant 转换到 LocalDateTime + LocalDateTime aTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + byte[] result = new byte[7]; + int milliseconds = aTime.getSecond() * 1000; // 获取毫秒部分 + + // 填充字节数组 + result[0] = (byte) (milliseconds % 256); + result[1] = (byte) (milliseconds / 256); + result[2] = (byte) aTime.getMinute(); + result[3] = (byte) aTime.getHour(); + result[4] = (byte) aTime.getDayOfMonth(); + result[5] = (byte) aTime.getMonthValue(); // 1-12 + result[6] = (byte) (aTime.getYear() % 100); // 00-99 + + return result; + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ConstraintValidator.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ConstraintValidator.java new file mode 100644 index 0000000..8d40b7f --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ConstraintValidator.java @@ -0,0 +1,97 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.config; + +import com.google.common.collect.Iterators; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.metadata.ConstraintDescriptor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping; +import org.hibernate.validator.internal.engine.ConfigurationImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import sanbing.jcpp.infrastructure.util.exception.DataValidationException; +import sanbing.jcpp.infrastructure.util.validation.Length; +import sanbing.jcpp.infrastructure.util.validation.StringLengthValidator; + +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@Configuration +public class ConstraintValidator { + + private static Validator fieldsValidator; + + static { + initializeValidators(); + } + + public static void validateFields(Object data) { + validateFields(data, "Validation error: "); + } + + public static void validateFields(Object data, String errorPrefix) { + Set> constraintsViolations = fieldsValidator.validate(data); + if (!constraintsViolations.isEmpty()) { + throw new DataValidationException(errorPrefix + getErrorMessage(constraintsViolations)); + } + } + + public static String getErrorMessage(Collection> constraintsViolations) { + return constraintsViolations.stream() + .map(ConstraintValidator::getErrorMessage) + .distinct().sorted().collect(Collectors.joining(", ")); + } + + public static String getErrorMessage(ConstraintViolation constraintViolation) { + ConstraintDescriptor constraintDescriptor = constraintViolation.getConstraintDescriptor(); + String property = (String) constraintDescriptor.getAttributes().get("fieldName"); + if (StringUtils.isEmpty(property) && !(constraintDescriptor.getAnnotation() instanceof AssertTrue)) { + property = Iterators.getLast(constraintViolation.getPropertyPath().iterator()).toString(); + } + + String error = ""; + if (StringUtils.isNotEmpty(property)) { + error += property + " "; + } + error += constraintViolation.getMessage(); + return error; + } + + private static void initializeValidators() { + HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure(); + + ConstraintMapping constraintMapping = getCustomConstraintMapping(); + validatorConfiguration.addMapping(constraintMapping); + + fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator(); + } + + @Bean + public LocalValidatorFactoryBean validatorFactoryBean() { + LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); + localValidatorFactoryBean.setConfigurationInitializer(configuration -> { + ((ConfigurationImpl) configuration).addMapping(getCustomConstraintMapping()); + }); + return localValidatorFactoryBean; + } + + private static ConstraintMapping getCustomConstraintMapping() { + ConstraintMapping constraintMapping = new DefaultConstraintMapping(null); + constraintMapping.constraintDefinition(Length.class).validatedBy(StringLengthValidator.class); + return constraintMapping; + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java new file mode 100644 index 0000000..4d5338b --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -0,0 +1,82 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.config; + +import com.google.common.hash.HashFunction; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; + +import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.forName; +import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.hash; + +/** + * @author baigod + */ +@Component +@Slf4j +public class ShardingThreadPool { + @Value("${thread-pool.sharding.hash_function_name:murmur3_128}") + private String hashFunctionName; + + @Value("${thread-pool.sharding.parallelism:128}") + private int parallelism; + + private HashFunction hashFunction; + + private final Map EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(128); + + @PostConstruct + public void init() { + this.hashFunction = forName(hashFunctionName); + } + + @PreDestroy + public void destroy() { + for (ExecutorService executorService : EXECUTOR_SERVICE_MAP.values()) { + executorService.shutdownNow(); + log.info("Sharding Thread [{}] Shutdown completed.", executorService); + } + } + + @Scheduled(fixedDelayString = "${thread-pool.sharding.stats-print-interval-ms:10000}") + public void printStats() { + EXECUTOR_SERVICE_MAP.forEach((k, v) -> { + + ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) v; + + log.info("分区 {}/{} 的线程池中剩余 {} 条待执行任务,当前正在执行的线程数 {}, 已完成任务 {} / {}", + k, + EXECUTOR_SERVICE_MAP.size(), + threadPoolExecutor.getQueue().size(), + threadPoolExecutor.getActiveCount(), + threadPoolExecutor.getCompletedTaskCount(), + threadPoolExecutor.getTaskCount()); + }); + } + + /** + * 提交分片任务 + */ + public void execute(UUID hashKey, TracerRunnable runnable) { + int partition = hash(hashFunction, hashKey); + + EXECUTOR_SERVICE_MAP.computeIfAbsent(partition % parallelism, + p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads-" + p))) + .execute(runnable); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ThreadPoolConfiguration.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ThreadPoolConfiguration.java new file mode 100644 index 0000000..40daa49 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ThreadPoolConfiguration.java @@ -0,0 +1,43 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.config; + +import jakarta.annotation.PreDestroy; +import org.springframework.context.annotation.Configuration; +import sanbing.jcpp.infrastructure.util.async.JCPPExecutors; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * @author baigod + */ +@Configuration +public class ThreadPoolConfiguration { + + public static final ExecutorService JCPP_COMMON_THREAD_POOL = JCPPExecutors.newVirtualThreadPool("jcpp-common-virtual"); + + public static final ScheduledExecutorService PROTOCOL_SESSION_SCHEDULED = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("protocol-session-schedule")); + + @PreDestroy + public void destroy() { + PROTOCOL_SESSION_SCHEDULED.shutdownNow(); + + JCPP_COMMON_THREAD_POOL.shutdown(); + + try { + if (!JCPP_COMMON_THREAD_POOL.awaitTermination(5, TimeUnit.SECONDS)) { + JCPP_COMMON_THREAD_POOL.shutdownNow(); + } + } catch (InterruptedException e) { + JCPP_COMMON_THREAD_POOL.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DataValidationException.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DataValidationException.java new file mode 100644 index 0000000..4fbcdb1 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DataValidationException.java @@ -0,0 +1,16 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.exception; + +public class DataValidationException extends RuntimeException { + + public DataValidationException(String message) { + super(message); + } + + public DataValidationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DownlinkException.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DownlinkException.java new file mode 100644 index 0000000..02342ad --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/DownlinkException.java @@ -0,0 +1,19 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.exception; + +/** + * @author baigod + */ +public class DownlinkException extends RuntimeException { + + public DownlinkException(String message) { + super(message); + } + + public DownlinkException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/IncorrectParameterException.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/IncorrectParameterException.java new file mode 100644 index 0000000..21af4a2 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/exception/IncorrectParameterException.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.exception; + + +public class IncorrectParameterException extends RuntimeException { + + public IncorrectParameterException(String message) { + super(message); + } + + public IncorrectParameterException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/BigNumberSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/BigNumberSerializer.java new file mode 100644 index 0000000..3271327 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/BigNumberSerializer.java @@ -0,0 +1,35 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.ser.std.NumberSerializer; + +import java.io.IOException; + +@JacksonStdImpl +public class BigNumberSerializer extends NumberSerializer { + + private static final long JS_NUM_MAX = 9007199254740992L; + private static final long JS_NUM_MIN = -9007199254740992L; + public static final BigNumberSerializer instance = new BigNumberSerializer(Number.class); + + public BigNumberSerializer(Class rawType) { + super(rawType); + } + + @Override + public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { + long longValue = value.longValue(); + if (longValue >= JS_NUM_MIN && longValue <= JS_NUM_MAX) { + super.serialize(value, gen, provider); + } else { + gen.writeString(value.toString()); + } + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DataTypeModule.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DataTypeModule.java new file mode 100644 index 0000000..3bd9fb3 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DataTypeModule.java @@ -0,0 +1,56 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; + + +/** + * 类型转换 + * + * @author baigod + */ +public class DataTypeModule extends SimpleModule { + public static final DataTypeModule INSTANCE = new DataTypeModule(); + + private DataTypeModule() { + super(DataTypeModule.class.getName()); + + // number + this.addSerializer(Long.class, BigNumberSerializer.instance); + this.addSerializer(Long.TYPE, BigNumberSerializer.instance); + this.addSerializer(BigInteger.class, BigNumberSerializer.instance); + this.addSerializer(BigDecimal.class, BigNumberSerializer.instance); + + // time + this.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE); + this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + this.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE); + this.addSerializer(Instant.class, InstantSerializer.INSTANCE); + this.addSerializer(Date.class, DateSerializer.INSTANCE); + this.addSerializer(java.sql.Date.class, SqlDateSerializer.INSTANCE); + this.addSerializer(Timestamp.class, TimestampSerializer.INSTANCE); + + this.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE); + this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + this.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE); + this.addDeserializer(Instant.class, InstantDeserializer.INSTANCE); + this.addDeserializer(Date.class, DateDeserializer.INSTANCE); + this.addDeserializer(java.sql.Date.class, SqlDateDeserializer.INSTANCE); + this.addDeserializer(Timestamp.class, TimestampDeserializer.INSTANCE); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateDeserializer.java new file mode 100644 index 0000000..5608a28 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateDeserializer.java @@ -0,0 +1,38 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +/** + * 时间反序列化 + * + * @author baigod + */ +public class DateDeserializer extends JsonDeserializer { + public static final DateDeserializer INSTANCE = new DateDeserializer(); + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private DateDeserializer() { + } + + @Override + public Date deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + String dateString = p.getText(); + + return dateString.length() > 19 + ? Date.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS).atZone(ZoneOffset.systemDefault()).toInstant()) + : Date.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER).atZone(ZoneOffset.systemDefault()).toInstant()); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateSerializer.java new file mode 100644 index 0000000..961d5e5 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/DateSerializer.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.apache.commons.lang3.time.FastDateFormat; + +import java.io.IOException; +import java.util.Date; + +/** + * 时间序列化 + * + * @author baigod + */ +public class DateSerializer extends StdSerializer { + public static final DateSerializer INSTANCE = new DateSerializer(); + private static final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS"); + + private DateSerializer() { + super(Date.class); + } + + @Override + public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(FAST_DATE_FORMAT_MS.format(value)); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantDeserializer.java new file mode 100644 index 0000000..8b3ad52 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantDeserializer.java @@ -0,0 +1,41 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import lombok.SneakyThrows; +import org.apache.commons.lang3.time.FastDateFormat; + +import java.time.Instant; +import java.time.format.DateTimeFormatter; + +/** + * Instant 反序列化 + * + * @author baigod + */ +public class InstantDeserializer extends com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer { + public static final InstantDeserializer INSTANCE = new InstantDeserializer(); + + private final FastDateFormat FAST_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); + private final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS"); + + private InstantDeserializer() { + super(InstantDeserializer.INSTANT, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); + } + + @SneakyThrows + @Override + public Instant deserialize(JsonParser parser, DeserializationContext context) { + String timestamp = parser.getText(); + + return timestamp.length() > 19 + ? FAST_DATE_FORMAT_MS.parse(timestamp).toInstant() + : FAST_DATE_FORMAT.parse(timestamp).toInstant(); + + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantSerializer.java new file mode 100644 index 0000000..11ceb2a --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/InstantSerializer.java @@ -0,0 +1,21 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import java.time.format.DateTimeFormatter; + +/** + * Instant 序列化 + * + * @author baigod + */ +public class InstantSerializer extends com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer { + public static final InstantSerializer INSTANCE = new InstantSerializer(); + + private InstantSerializer() { + super(com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer.INSTANCE, true,false, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/JacksonUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/JacksonUtil.java new file mode 100644 index 0000000..e523eab --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/JacksonUtil.java @@ -0,0 +1,215 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonWriteFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.io.IOException; +import java.util.Arrays; +import java.util.TimeZone; + +/** + * @author baigod + */ +public class JacksonUtil { + + public static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder() + .configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) + .configure(Feature.ALLOW_SINGLE_QUOTES, true) + .configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true) + .configure(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS, false) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .defaultTimeZone(TimeZone.getTimeZone("GMT+8")) + .build() + .registerModules(DataTypeModule.INSTANCE); + + public static final ObjectMapper PRETTY_SORTED_JSON_MAPPER = JsonMapper.builder() + .enable(SerializationFeature.INDENT_OUTPUT) + .configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true) + .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) + .configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true) + .serializationInclusion(Include.NON_NULL) + .defaultTimeZone(TimeZone.getTimeZone("GMT+8")) + .build() + .registerModules(DataTypeModule.INSTANCE); + + + public static T convertValue(Object fromValue, Class toValueType) { + try { + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("The given object value: " + + fromValue + " cannot be converted to " + toValueType, e); + } + } + + public static T convertValue(Object fromValue, TypeReference toValueTypeRef) { + try { + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef) : null; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("The given object value: " + + fromValue + " cannot be converted to " + toValueTypeRef, e); + } + } + + public static T fromString(String string, Class clazz) { + try { + return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value: " + + string + " cannot be transformed to Json object", e); + } + } + + public static T fromString(String string, TypeReference valueTypeRef) { + try { + return string != null ? OBJECT_MAPPER.readValue(string, valueTypeRef) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value: " + + string + " cannot be transformed to Json object", e); + } + } + + public static T fromBytes(byte[] bytes, Class clazz) { + try { + return bytes != null ? OBJECT_MAPPER.readValue(bytes, clazz) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value: " + + Arrays.toString(bytes) + " cannot be transformed to Json object", e); + } + } + + public static JsonNode fromBytes(byte[] bytes) { + try { + return OBJECT_MAPPER.readTree(bytes); + } catch (IOException e) { + throw new IllegalArgumentException("The given byte[] value: " + + Arrays.toString(bytes) + " cannot be transformed to Json object", e); + } + } + + public static String toString(Object value) { + try { + return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null; + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("The given Json object value: " + + value + " cannot be transformed to a String", e); + } + } + + public static String toPrettyString(Object o) { + try { + return PRETTY_SORTED_JSON_MAPPER.writeValueAsString(o); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static T treeToValue(JsonNode node, Class clazz) { + try { + return OBJECT_MAPPER.treeToValue(node, clazz); + } catch (IOException e) { + throw new IllegalArgumentException("Can't convert value: " + node.toString(), e); + } + } + + public static JsonNode toJsonNode(String value) { + return toJsonNode(value, OBJECT_MAPPER); + } + + public static JsonNode toJsonNode(String value, ObjectMapper mapper) { + if (value == null || value.isEmpty()) { + return null; + } + try { + return mapper.readTree(value); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + public static ObjectNode newObjectNode() { + return newObjectNode(OBJECT_MAPPER); + } + + public static ObjectNode newObjectNode(ObjectMapper mapper) { + return mapper.createObjectNode(); + } + + public static ArrayNode newArrayNode() { + return newArrayNode(OBJECT_MAPPER); + } + + public static ArrayNode newArrayNode(ObjectMapper mapper) { + return mapper.createArrayNode(); + } + + public static T clone(T value) { + @SuppressWarnings("unchecked") + Class valueClass = (Class) value.getClass(); + return fromString(toString(value), valueClass); + } + + public static JsonNode valueToTree(T value) { + return OBJECT_MAPPER.valueToTree(value); + } + + public static byte[] writeValueAsBytes(T value) { + try { + return OBJECT_MAPPER.writeValueAsBytes(value); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("The given Json object value: " + + value + " cannot be transformed to a String", e); + } + } + + public static JsonNode getSafely(JsonNode node, String... path) { + if (node == null) { + return null; + } + for (String p : path) { + if (!node.has(p)) { + return null; + } else { + node = node.get(p); + } + } + return node; + } + + /** + * 合并两个ObjectNode. + * 如果存在相同的字段,优先保留第二个ObjectNode中的值。 + * + * @param node1 the first ObjectNode + * @param node2 the second ObjectNode + * @return 合并后的结果 + */ + public static ObjectNode merge(ObjectNode node1, ObjectNode node2) { + ObjectNode mergedNode = OBJECT_MAPPER.createObjectNode(); + + // 把第一个节点的所有字段添加到mergedNode中 + node1.fields().forEachRemaining(entry -> { + mergedNode.set(entry.getKey(), entry.getValue()); + }); + + // 把第二个节点的所有字段添加到mergedNode中,覆盖相同字段 + node2.fields().forEachRemaining(entry -> { + mergedNode.set(entry.getKey(), entry.getValue()); + }); + + return mergedNode; + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeDeserializer.java new file mode 100644 index 0000000..95f5d03 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeDeserializer.java @@ -0,0 +1,39 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * LocalDateTime类型反序列化 + * 需要用到的字段上加 @JsonDeserialize(using = LocalDateTimeDeserializer.class) + */ +public class LocalDateTimeDeserializer extends StdDeserializer { + public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer(); + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private LocalDateTimeDeserializer() { + super(LocalDateTime.class); + } + + @Override + public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext context) + throws IOException { + + String dateString = jsonParser.getText(); + return dateString.length() > 19 + ? LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS) + : LocalDateTime.parse(dateString, DATE_TIME_FORMATTER); + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeSerializer.java new file mode 100644 index 0000000..5c7d790 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalDateTimeSerializer.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 时间类型序列化工具 + * + * @author baigod + */ +public class LocalDateTimeSerializer extends JsonSerializer { + public static final LocalDateTimeSerializer INSTANCE = new LocalDateTimeSerializer(); + + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private LocalDateTimeSerializer() { + } + + @Override + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(value.format(DATE_TIME_FORMATTER_MS)); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeDeserializer.java new file mode 100644 index 0000000..c4e8f1e --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeDeserializer.java @@ -0,0 +1,42 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * LocalDateTime类型反序列化 + * 需要用到的字段上加 @JsonDeserialize(using = EnergyLocalTimeDeserializer.class) + */ +public class LocalTimeDeserializer extends StdDeserializer { + public static final LocalTimeDeserializer INSTANCE = new LocalTimeDeserializer(); + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss"); + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS"); + + + private LocalTimeDeserializer() { + super(LocalDateTime.class); + } + + @Override + public LocalTime deserialize(JsonParser jsonParser, DeserializationContext context) + throws IOException { + + String dateString = jsonParser.getText(); + return dateString.length() > 8 + ? LocalTime.parse(dateString, DATE_TIME_FORMATTER_MS) + : LocalTime.parse(dateString, DATE_TIME_FORMATTER); + + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeSerializer.java new file mode 100644 index 0000000..3fde5cc --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LocalTimeSerializer.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * 时间类型序列化工具 + * + * @author baigod + */ +public class LocalTimeSerializer extends JsonSerializer { + public static final LocalTimeSerializer INSTANCE = new LocalTimeSerializer(); + + private LocalTimeSerializer() { + } + + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("HH:mm:ss.SSS"); + + @Override + public void serialize(LocalTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(value.format(DATE_TIME_FORMATTER_MS)); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LongTimestampDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LongTimestampDeserializer.java new file mode 100644 index 0000000..a60a19c --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/LongTimestampDeserializer.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; + +/** + * 13位时间戳反序列化器 + * @author baigod + */ +public class LongTimestampDeserializer extends JsonDeserializer { + + @Override + public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + + // 判定是否是long类型 + if ("LONG".equals(jsonParser.getNumberType().name())) { + return jsonParser.getLongValue(); + } + LocalDateTime localDateTime = LocalDateTime.parse(jsonParser.getValueAsString().replace(" ", "T").replace("Z", "")); + ZoneId systemDefaultZone = ZoneId.systemDefault(); + return localDateTime.atZone(systemDefaultZone).toInstant().toEpochMilli(); + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateDeserializer.java new file mode 100644 index 0000000..5b891cf --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateDeserializer.java @@ -0,0 +1,39 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.sql.Date; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +/** + * sqlDate 反序列化 + * + * @author baigod + */ +public class SqlDateDeserializer extends JsonDeserializer { + + public static final SqlDateDeserializer INSTANCE = new SqlDateDeserializer(); + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private SqlDateDeserializer() { + } + + @Override + public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String dateString = p.getText(); + + return dateString.length() > 19 + ? new Date(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli()) + : new Date(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli()); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateSerializer.java new file mode 100644 index 0000000..0749d53 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/SqlDateSerializer.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.apache.commons.lang3.time.FastDateFormat; + +import java.io.IOException; +import java.sql.Date; + +/** + * sqlDate序列化 + * + * @author baigod + */ +public class SqlDateSerializer extends StdSerializer { + public static final SqlDateSerializer INSTANCE = new SqlDateSerializer(); + + private static final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS"); + + private SqlDateSerializer() { + super(Date.class); + } + + @Override + public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(FAST_DATE_FORMAT_MS.format(value)); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampDeserializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampDeserializer.java new file mode 100644 index 0000000..4ce6b8a --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampDeserializer.java @@ -0,0 +1,38 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +/** + * timestamp 反序列化 + * @author baigod + */ +public class TimestampDeserializer extends JsonDeserializer { + + public static final TimestampDeserializer INSTANCE = new TimestampDeserializer(); + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DATE_TIME_FORMATTER_MS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + + private TimestampDeserializer() { + } + + @Override + public Timestamp deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + String dateString = p.getText(); + + return dateString.length() > 19 + ? Timestamp.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER_MS).atZone(ZoneOffset.systemDefault()).toInstant()) + : Timestamp.from(LocalDateTime.parse(dateString, DATE_TIME_FORMATTER).atZone(ZoneOffset.systemDefault()).toInstant()); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampSerializer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampSerializer.java new file mode 100644 index 0000000..fcf9fcb --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/jackson/TimestampSerializer.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.apache.commons.lang3.time.FastDateFormat; + +import java.io.IOException; +import java.sql.Timestamp; + +/** + * timestamp 序列化 + * + * @author baigod + */ +public class TimestampSerializer extends StdSerializer { + public static final TimestampSerializer INSTANCE = new TimestampSerializer(); + + private static final FastDateFormat FAST_DATE_FORMAT_MS = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS"); + + private TimestampSerializer() { + super(Timestamp.class); + } + + @Override + public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(FAST_DATE_FORMAT_MS.format(value)); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java new file mode 100644 index 0000000..7f22912 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java @@ -0,0 +1,43 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.mdc; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.MDC; +import sanbing.jcpp.infrastructure.util.trace.Tracer; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; + + +public class MDCUtils { + + private static final String TRACE_ID = "TRACE_ID"; + + public static String putIfAbsentTracer() { + String traceId = MDC.get(TRACE_ID); + + if (StringUtils.isEmpty(traceId)) { + return recordTracer(); + } + + return traceId; + } + + public static String recordTracer() { + Tracer tracer = TracerContextUtil.getCurrentTracer(); + + if (!StringUtils.isEmpty(tracer.getTraceId())) { + MDC.put(TRACE_ID, tracer.getTraceId()); + } else { + MDC.remove(TRACE_ID); + } + + return tracer.getTraceId(); + + } + + public static void cleanTracer() { + MDC.remove(TRACE_ID); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/JCPPProperty.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/JCPPProperty.java new file mode 100644 index 0000000..a3c0903 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/JCPPProperty.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.property; + +import lombok.Data; + +@Data +public class JCPPProperty { + + private String key; + private String value; +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/PropertyUtils.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/PropertyUtils.java new file mode 100644 index 0000000..bf3ed82 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/property/PropertyUtils.java @@ -0,0 +1,44 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.property; + + +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public class PropertyUtils { + + public static Map getProps(String properties) { + Map configs = new HashMap<>(); + if (StringUtils.isNotEmpty(properties)) { + for (String property : properties.split(";")) { + if (StringUtils.isNotEmpty(property)) { + int delimiterPosition = property.indexOf(":"); + String key = property.substring(0, delimiterPosition); + String value = property.substring(delimiterPosition + 1); + configs.put(key, value); + } + } + } + return configs; + } + + public static Map getProps(Map defaultProperties, String propertiesStr) { + return getProps(defaultProperties, propertiesStr, PropertyUtils::getProps); + } + + public static Map getProps(Map defaultProperties, String propertiesStr, Function> parser) { + Map properties = defaultProperties; + if (StringUtils.isNotBlank(propertiesStr)) { + properties = new HashMap<>(properties); + properties.putAll(parser.apply(propertiesStr)); + } + return properties; + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TraceIdGenerator.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TraceIdGenerator.java new file mode 100644 index 0000000..6baf94a --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TraceIdGenerator.java @@ -0,0 +1,69 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.trace; + +import java.net.InetAddress; +import java.util.concurrent.atomic.AtomicInteger; + +public class TraceIdGenerator { + + //127.0.0.1 + private static String IP_16 = "7F000001"; + + private static final int MAX_COUNT_INDEX = 9000; + + private static final AtomicInteger COUNT = new AtomicInteger(1000); + + static { + try { + String ipAddress = InetAddress.getLocalHost().getHostAddress(); + if (ipAddress != null) { + IP_16 = getIP_16(ipAddress); + } + } catch (Throwable ignored) { + } + } + + public static String generate() { + return getTraceId(IP_16, System.currentTimeMillis(), getNextId()); + } + + private static String getIP_16(String ip) { + String[] ips = ip.split("\\."); + StringBuilder sb = new StringBuilder(); + for (String column : ips) { + String hex = Integer.toHexString(Integer.parseInt(column)).toUpperCase(); + if (hex.length() == 1) { + sb.append('0').append(hex); + } else { + sb.append(hex); + } + + } + return sb.toString(); + } + + private static String getTraceId(String ip, long timestamp, String nextId) { + return ip + timestamp + nextId; + } + + private static String getNextId() { + + int count = COUNT.incrementAndGet(); + + if (count > 9000) { + synchronized (TraceIdGenerator.class) { + if (COUNT.get() > MAX_COUNT_INDEX) { + COUNT.set(1000); + } + } + + return String.valueOf(COUNT.incrementAndGet()); + } else { + return String.valueOf(count); + } + + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/Tracer.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/Tracer.java new file mode 100644 index 0000000..e5a3105 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/Tracer.java @@ -0,0 +1,38 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.trace; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class Tracer implements Serializable { + + private String traceId; + + private String origin; + + private final long tracerTs; + + public Tracer(String traceId, String origin) { + this.traceId = traceId; + this.origin = origin; + this.tracerTs = System.currentTimeMillis(); + } + + public Tracer(String traceId, String origin, long tracerTs) { + this.traceId = traceId; + this.origin = origin; + this.tracerTs = tracerTs; + } + + public Tracer(String traceId, long tracerTs) { + this.traceId = traceId; + this.origin = "JCPP"; + this.tracerTs = tracerTs; + + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerCallable.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerCallable.java new file mode 100644 index 0000000..9978493 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerCallable.java @@ -0,0 +1,41 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.trace; + + +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; + +import java.util.concurrent.Callable; + +public class TracerCallable implements Callable { + + private Tracer tracer; + private final Callable callable; + + public TracerCallable(Callable callable) { + this.tracer = TracerContextUtil.getCurrentTracer(); + this.callable = callable; + } + + @Override + public T call() throws Exception { + try { + if (this.tracer != null) { + TracerContextUtil.newTracer(tracer.getTraceId(), tracer.getOrigin(), tracer.getTracerTs()); + + MDCUtils.recordTracer(); + } + + return this.callable.call(); + } finally { + TracerContextUtil.cleanTracer(); + + MDCUtils.cleanTracer(); + + this.tracer = null; + } + } + +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java new file mode 100644 index 0000000..5921c7c --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java @@ -0,0 +1,71 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.trace; + +import org.apache.commons.lang3.StringUtils; + +/** + * Tracer上下文工具类 + */ +public class TracerContextUtil { + + public static final String JCPP_TRACER_ID = "jcpp_tracer_id"; + public static final String JCPP_TRACER_ORIGIN = "jcpp_tracer_origin"; + public static final String JCPP_TRACER_TS = "jcpp_tracer_ts"; + + private static final ThreadLocal TRACE_ID_CONTAINER = new ThreadLocal<>(); + + public static Tracer newTracer(String traceId, String origin) { + Tracer tracer; + + if (StringUtils.isEmpty(traceId)) { + tracer = new Tracer(TraceIdGenerator.generate(), origin); + } else { + tracer = new Tracer(traceId, origin); + } + + TRACE_ID_CONTAINER.set(tracer); + + return tracer; + } + + public static Tracer newTracer(String traceId, String origin, long ts) { + final Tracer tracer; + + if (StringUtils.isEmpty(traceId)) { + tracer = new Tracer(TraceIdGenerator.generate(), origin, ts); + } else { + tracer = new Tracer(traceId, origin, ts); + } + + + TRACE_ID_CONTAINER.set(tracer); + + return tracer; + } + + public static Tracer newTracer(String origin) { + return newTracer(TraceIdGenerator.generate(), origin); + } + + public static Tracer newTracer() { + return newTracer(TraceIdGenerator.generate(), null); + } + + public static Tracer getCurrentTracer() { + Tracer tracer = TRACE_ID_CONTAINER.get(); + + if (tracer == null) { + return newTracer(); + } + + return tracer; + } + + public static void cleanTracer() { + TRACE_ID_CONTAINER.remove(); + } + +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerRunnable.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerRunnable.java new file mode 100644 index 0000000..779daa4 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerRunnable.java @@ -0,0 +1,38 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.trace; + + +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; + +public class TracerRunnable implements Runnable { + + private Tracer tracer; + private final Runnable runnable; + + public TracerRunnable(Runnable runnable) { + this.tracer = TracerContextUtil.getCurrentTracer(); + this.runnable = runnable; + } + + @Override + public void run() { + try { + if (this.tracer != null) { + TracerContextUtil.newTracer(tracer.getTraceId(), tracer.getOrigin(), tracer.getTracerTs()); + + MDCUtils.recordTracer(); + } + + this.runnable.run(); + } finally { + TracerContextUtil.cleanTracer(); + + MDCUtils.cleanTracer(); + + this.tracer = null; + } + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Length.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Length.java new file mode 100644 index 0000000..ae32266 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Length.java @@ -0,0 +1,28 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Constraint(validatedBy = {}) +public @interface Length { + String message() default "length must be equal or less than {max}"; + + String fieldName() default ""; + + int max() default 255; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/StringLengthValidator.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/StringLengthValidator.java new file mode 100644 index 0000000..98948d6 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/StringLengthValidator.java @@ -0,0 +1,35 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.validation; + +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +@Slf4j +public class StringLengthValidator implements ConstraintValidator { + private int max; + + @Override + public boolean isValid(Object value, ConstraintValidatorContext context) { + String stringValue; + if (value instanceof CharSequence || value instanceof JsonNode) { + stringValue = value.toString(); + } else { + return true; + } + if (StringUtils.isEmpty(stringValue)) { + return true; + } + return stringValue.length() <= max; + } + + @Override + public void initialize(Length constraintAnnotation) { + this.max = constraintAnnotation.max(); + } +} diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java new file mode 100644 index 0000000..a13aab0 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java @@ -0,0 +1,55 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.validation; + +import sanbing.jcpp.infrastructure.util.exception.IncorrectParameterException; + +import java.util.UUID; +import java.util.function.Function; + +public class Validator { + + + public static void validateString(String val, String errorMessage) { + if (val == null || val.isEmpty()) { + throw new IncorrectParameterException(errorMessage); + } + } + + + public static void validateString(String val, Function errorMessageFunction) { + if (val == null || val.isEmpty()) { + throw new IncorrectParameterException(errorMessageFunction.apply(val)); + } + } + + public static void validatePositiveNumber(long val, String errorMessage) { + if (val <= 0) { + throw new IncorrectParameterException(errorMessage); + } + } + + @Deprecated + public static void validateId(UUID id, String errorMessage) { + if (id == null) { + throw new IncorrectParameterException(errorMessage); + } + } + + public static void validateId(UUID id, Function errorMessageFunction) { + if (id == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(id)); + } + } + + + public static void checkNotNull(Object reference, String errorMessage) { + if (reference == null) { + throw new IncorrectParameterException(errorMessage); + } + } + + +} diff --git a/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/BCDUtilTest.java b/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/BCDUtilTest.java new file mode 100644 index 0000000..195da2c --- /dev/null +++ b/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/BCDUtilTest.java @@ -0,0 +1,28 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.codec; + +import cn.hutool.core.util.HexUtil; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +class BCDUtilTest { + + @Test + void toBytesTest() { + String pileCodeHex = "20231212000010"; + + byte[] bytes = HexUtil.decodeHex(pileCodeHex); + + String pileCode = BCDUtil.toString(bytes); + + assert pileCodeHex.equals(pileCode); + + byte[] pileCodeBytes = BCDUtil.toBytes(pileCodeHex); + + assertArrayEquals(pileCodeBytes, bytes); + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtilTest.java b/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtilTest.java new file mode 100644 index 0000000..6542f33 --- /dev/null +++ b/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/codec/CP56Time2aUtilTest.java @@ -0,0 +1,27 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.codec; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Arrays; + +class CP56Time2aUtilTest { + + @Test + void encodeTest() { + Instant time = Instant.ofEpochMilli(1727798453000L); + + byte[] bytes = CP56Time2aUtil.encode(time); + + System.out.println(Arrays.toString(bytes)); + + Instant decode = CP56Time2aUtil.decode(bytes); + + assert time.equals(decode); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-api/pom.xml b/jcpp-protocol-api/pom.xml new file mode 100644 index 0000000..6935c02 --- /dev/null +++ b/jcpp-protocol-api/pom.xml @@ -0,0 +1,50 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-protocol-api + jar + JChargePointProtocol Protocol Api Module + 协议API + + + ${basedir}/.. + + + + + sanbing + jcpp-infrastructure-util + + + sanbing + jcpp-infrastructure-queue + + + org.springframework.boot + spring-boot-starter-reactor-netty + + + com.github.ben-manes.caffeine + caffeine + + + + + + + diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java new file mode 100644 index 0000000..6a8ffd6 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java @@ -0,0 +1,125 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import sanbing.jcpp.protocol.cfg.ForwarderCfg; +import sanbing.jcpp.protocol.cfg.ProtocolCfg; +import sanbing.jcpp.protocol.cfg.TcpCfg; +import sanbing.jcpp.protocol.cfg.enums.ForwarderType; +import sanbing.jcpp.protocol.forwarder.Forwarder; +import sanbing.jcpp.protocol.forwarder.KafkaForwarder; +import sanbing.jcpp.protocol.forwarder.MemoryForwarder; +import sanbing.jcpp.protocol.listener.Listener; +import sanbing.jcpp.protocol.listener.tcp.TcpListener; + +import static org.springframework.boot.actuate.health.Status.UP; + +/** + * @author baigod + */ +@Slf4j +public abstract class ProtocolBootstrap implements HealthIndicator { + + @Resource + protected ProtocolContext protocolContext; + + protected ProtocolCfg protocolCfg; + + protected Listener listener; + + protected Forwarder forwarder; + + @PostConstruct + public void init() throws InterruptedException { + String protocolName = getProtocolName(); + + log.info("Protocol Service [{}] Initializing...", protocolName); + + protocolCfg = protocolContext.getProtocolsConfigProvider().loadConfig(protocolName); + + ForwarderCfg forwarderCfg = protocolCfg.getForwarder(); + + if (protocolContext.getServiceInfoProvider().isMonolith() && forwarderCfg.getType() == ForwarderType.memory) { + + forwarder = new MemoryForwarder(getProtocolName(), forwarderCfg, + protocolContext.getStatsFactory(), + protocolContext.getAppQueueFactory(), + protocolContext.getPartitionProvider(), + protocolContext.getServiceInfoProvider()); + + } else if (forwarderCfg.getType() == ForwarderType.kafka) { + + forwarder = new KafkaForwarder(getProtocolName(), forwarderCfg, + protocolContext.getStatsFactory(), + protocolContext.getAppQueueFactory(), + protocolContext.getPartitionProvider(), + protocolContext.getServiceInfoProvider()); + } else { + throw new IllegalArgumentException("Unknown Forwarder type: " + forwarderCfg.getType()); + } + + TcpCfg tcpCfg = protocolCfg.getListener().getTcp(); + + if (tcpCfg != null) { + + listener = new TcpListener<>(protocolName, tcpCfg, messageProcessor(), protocolContext.getStatsFactory()); + } + + _init(); + } + + @PreDestroy + public void destroy() throws InterruptedException { + log.info("{} destroy...", getProtocolName()); + + if (listener != null) { + listener.destroy(); + } + + if (forwarder != null) { + forwarder.destroy(); + } + + _destroy(); + } + + + @Override + public Health health() { + Health.Builder healthBuilder; + + if (listener != null && listener.health().getStatus() == UP && forwarder != null && forwarder.health().getStatus() == UP) { + healthBuilder = Health.up(); + } else { + healthBuilder = Health.down(); + } + + if (listener != null) { + healthBuilder.withDetail("listener", listener.health().getStatus()); + } + + if (forwarder != null) { + healthBuilder.withDetail("forwarder", forwarder.health().getStatus()); + } + + return healthBuilder.build(); + } + + + protected abstract String getProtocolName(); + + protected abstract void _init(); + + protected abstract void _destroy(); + + protected abstract ProtocolMessageProcessor messageProcessor(); + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolContext.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolContext.java new file mode 100644 index 0000000..0bad837 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolContext.java @@ -0,0 +1,64 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol; + +import io.netty.util.ResourceLeakDetector; +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.queue.discovery.PartitionProvider; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.queue.provider.AppQueueFactory; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.infrastructure.util.config.ShardingThreadPool; +import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; +import sanbing.jcpp.protocol.provider.ProtocolsConfigProvider; + +/** + * @author baigod + */ +@Component +@Getter +@Slf4j +public class ProtocolContext { + + private final StatsFactory statsFactory; + + private final ProtocolsConfigProvider protocolsConfigProvider; + + private final ProtocolSessionRegistryProvider protocolSessionRegistryProvider; + + private final ServiceInfoProvider serviceInfoProvider; + + private final PartitionProvider partitionProvider; + + private final AppQueueFactory appQueueFactory; + + private final ShardingThreadPool shardingThreadPool; + + public ProtocolContext(StatsFactory statsFactory, + ProtocolsConfigProvider protocolsConfigProvider, + ProtocolSessionRegistryProvider protocolSessionRegistryProvider, + ServiceInfoProvider serviceInfoProvider, + @Autowired(required = false) PartitionProvider partitionProvider, + @Autowired(required = false) AppQueueFactory appQueueFactory, + ShardingThreadPool shardingThreadPool) { + this.statsFactory = statsFactory; + this.protocolsConfigProvider = protocolsConfigProvider; + this.protocolSessionRegistryProvider = protocolSessionRegistryProvider; + this.serviceInfoProvider = serviceInfoProvider; + this.partitionProvider = partitionProvider; + this.appQueueFactory = appQueueFactory; + this.shardingThreadPool = shardingThreadPool; + } + + @PostConstruct + public void init() { + ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); + log.info("Setting resource leak detector level to {}", ResourceLeakDetector.Level.DISABLED); + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java new file mode 100644 index 0000000..848cfd3 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java @@ -0,0 +1,66 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol; + +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.stats.MessagesStats; +import sanbing.jcpp.infrastructure.util.exception.DownlinkException; +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; +import sanbing.jcpp.protocol.domain.ListenerToHandlerMsg; +import sanbing.jcpp.protocol.domain.SessionToHandlerMsg; +import sanbing.jcpp.protocol.forwarder.Forwarder; + +import java.util.UUID; + +/** + * @author baigod + */ +@Slf4j +public abstract class ProtocolMessageProcessor { + protected final Forwarder forwarder; + protected final ProtocolContext protocolContext; + + protected ProtocolMessageProcessor(Forwarder forwarder, ProtocolContext protocolContext) { + this.forwarder = forwarder; + this.protocolContext = protocolContext; + } + + public void uplinkHandleAsync(ListenerToHandlerMsg listenerToHandlerMsg, MessagesStats uplinkMsgStats) { + + UUID id = listenerToHandlerMsg.session().getId(); + + protocolContext.getShardingThreadPool().execute(id, new TracerRunnable(() -> { + try { + + listenerToHandlerMsg.session().setForwarder(forwarder); + + uplinkHandle(listenerToHandlerMsg); + + } catch (Exception e) { + + uplinkMsgStats.incrementFailed(); + + log.error("{} 消息处理器处理报文异常", listenerToHandlerMsg.session(), e); + } + })); + } + + protected abstract void uplinkHandle(ListenerToHandlerMsg listenerToHandlerMsg) throws Exception; + + public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg, MessagesStats downlinkMsgStats) throws DownlinkException { + try { + + downlinkHandle(sessionToHandlerMsg); + + } catch (Exception e) { + + downlinkMsgStats.incrementFailed(); + + throw new DownlinkException(e.getMessage(), e); + } + } + + protected abstract void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) throws Exception; +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java new file mode 100644 index 0000000..94b6906 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java @@ -0,0 +1,78 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.adapter; + +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.domain.ProtocolSession; +import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * @author baigod + */ +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class DownlinkController { + + @Value("${api.timeout.onDownlink:3000}") + public long onDownlinkTimeout; + + + @Resource + ProtocolSessionRegistryProvider protocolSessionRegistryProvider; + + @PostMapping(value = "/onDownlink", consumes = "application/x-protobuf", produces = "application/x-protobuf") + public DeferredResult> onDownlink(@RequestBody DownlinkRestMessage downlinkMsg) { + log.info("收到REST下行请求 {}", downlinkMsg); + + final DeferredResult> response = new DeferredResult<>(onDownlinkTimeout, + ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).build()); + + UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(),downlinkMsg.getSessionIdLSB()) ; + + CompletableFuture protocolSessionCompletableFuture = protocolSessionRegistryProvider.get(protocolSessionId); + + protocolSessionCompletableFuture.thenAccept(session -> { + if (session != null) { + + session.onDownlink(downlinkMsg); + + response.setResult(ResponseEntity.status(HttpStatus.OK).build()); + } else { + + log.warn("下发报文时Session未找到 sessionId: {}", protocolSessionId); + + response.setResult(ResponseEntity.status(HttpStatus.NOT_FOUND).body("Protocol Session not found for ID:" + protocolSessionId)); + } + }).whenComplete((unused, throwable) -> { + if (throwable != null) { + + log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, throwable); + + if (!response.isSetOrExpired()) { + + response.setResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(throwable.getMessage())); + } + } + }); + + return response; + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/TracerInterceptor.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/TracerInterceptor.java new file mode 100644 index 0000000..90a68c3 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/TracerInterceptor.java @@ -0,0 +1,41 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.adapter.config; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; + +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; + +@Component +public class TracerInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String tracerId = request.getHeader(JCPP_TRACER_ID); + String tracerOrigin = request.getHeader(JCPP_TRACER_ORIGIN); + String tracerTsStr = request.getHeader(JCPP_TRACER_TS); + + long tracerTs; + if (tracerTsStr != null) { + try { + tracerTs = Long.parseLong(tracerTsStr); + } catch (NumberFormatException e) { + tracerTs = System.currentTimeMillis(); + } + } else { + tracerTs = System.currentTimeMillis(); + } + + TracerContextUtil.newTracer(tracerId, tracerOrigin, tracerTs); + MDCUtils.recordTracer(); + + return true; + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/UndertowServletWebServerCustomizer.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/UndertowServletWebServerCustomizer.java new file mode 100644 index 0000000..21830f8 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/UndertowServletWebServerCustomizer.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.adapter.config; + +import io.undertow.server.DefaultByteBufferPool; +import io.undertow.websockets.jsr.WebSocketDeploymentInfo; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.stereotype.Component; + + +@Component +public class UndertowServletWebServerCustomizer implements WebServerFactoryCustomizer { + @Override + public void customize(UndertowServletWebServerFactory factory) { + factory.addDeploymentInfoCustomizers(deploymentInfo -> { + WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); + webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 128 * 1024 * 1024)); + deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); + }); + } +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/WebMvcConfiguration.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/WebMvcConfiguration.java new file mode 100644 index 0000000..94e4f3a --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/config/WebMvcConfiguration.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.adapter.config; + +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +@Configuration +public class WebMvcConfiguration implements WebMvcConfigurer { + + @Resource + private TracerInterceptor tracerInterceptor; + + @Override + public void configureMessageConverters(List> converters) { + for (HttpMessageConverter converter : converters) { + if (converter instanceof StringHttpMessageConverter) { + ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8); + } + + if (converter instanceof MappingJackson2HttpMessageConverter) { + ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8); + } + } + + // protobuf 序列化 + converters.add( new ProtobufHttpMessageConverter()); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(tracerInterceptor).addPathPatterns("/**"); + } +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ForwarderCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ForwarderCfg.java new file mode 100644 index 0000000..b6feb8e --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ForwarderCfg.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import sanbing.jcpp.protocol.cfg.enums.ForwarderType; + +@Setter +@Getter +public class ForwarderCfg { + + @NotNull + private ForwarderType type; + + private MemoryCfg memory; + + @Valid + private KafkaCfg kafka; +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/KafkaCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/KafkaCfg.java new file mode 100644 index 0000000..6b9885e --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/KafkaCfg.java @@ -0,0 +1,49 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import lombok.Getter; +import lombok.Setter; +import sanbing.jcpp.infrastructure.util.property.PropertyUtils; + +import java.util.Map; + +@Getter +@Setter +public class KafkaCfg { + + private String topic; + + private boolean jcppPartition; + + private String bootstrapServers; + + private String acks; + + private EncoderType encoder; + + private int retries; + + private String compressionType; // none, gzip, snappy, lz4, zstd + + private int batchSize; + + private int lingerMs; + + private long bufferMemory; + + private Map otherProperties; // Other inline properties if necessary + + private String topicProperties; + + public void setOtherProperties(String otherProperties) { + this.otherProperties = PropertyUtils.getProps(otherProperties); + } + + public enum EncoderType { + protobuf, + json + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ListenerCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ListenerCfg.java new file mode 100644 index 0000000..7baf6b6 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ListenerCfg.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import jakarta.validation.Valid; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ListenerCfg { + + @Valid + private TcpCfg tcp; +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/MemoryCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/MemoryCfg.java new file mode 100644 index 0000000..203f474 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/MemoryCfg.java @@ -0,0 +1,18 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author baigod + */ +@Getter +@Setter +public class MemoryCfg { + + private String topic; +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ProtocolCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ProtocolCfg.java new file mode 100644 index 0000000..f482522 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ProtocolCfg.java @@ -0,0 +1,25 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ProtocolCfg { + + private boolean enabled; + + @NotNull + @Valid + private ListenerCfg listener; + + @NotNull + @Valid + private ForwarderCfg forwarder; +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpCfg.java new file mode 100644 index 0000000..7312c82 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpCfg.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TcpCfg { + + private String bindAddress; + + @Max(65000) + private int bindPort; + + @Min(1) + private int bossGroupThreadCount; + + @Min(1) + private int workerGroupThreadCount; + + private boolean soKeepAlive; + + @Min(1) + @Max(65500) + private int soBacklog; + + @Min(1) + private int soRcvbuf; + + @Min(1) + private int soSndbuf; + + private boolean nodelay; + + @Valid + private TcpHandlerCfg handler; + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpHandlerCfg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpHandlerCfg.java new file mode 100644 index 0000000..d752a46 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/TcpHandlerCfg.java @@ -0,0 +1,55 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg; + +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.validation.constraints.Min; +import lombok.Getter; +import lombok.Setter; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.property.PropertyUtils; +import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; +import sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration; +import sanbing.jcpp.protocol.listener.tcp.configs.HandlerConfiguration; +import sanbing.jcpp.protocol.listener.tcp.configs.JsonHandlerConfiguration; +import sanbing.jcpp.protocol.listener.tcp.configs.TextHandlerConfiguration; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class TcpHandlerCfg { + + @Getter + private TcpHandlerType type; + + @Min(1) + @Setter + @Getter + private int idleTimeoutSeconds; + + @Min(1) + @Setter + @Getter + private int maxConnections; + + private final Map HANDLER_MAP = new ConcurrentHashMap<>(); + + public HandlerConfiguration getConfiguration(TcpHandlerType type) { + return HANDLER_MAP.get(type); + } + + public void setConfiguration(String configuration) { + final JsonNode cfgJson = JacksonUtil.valueToTree(PropertyUtils.getProps(configuration)); + + type = TcpHandlerType.valueOf(cfgJson.get("type").asText()); + + switch (type) { + case TEXT -> HANDLER_MAP.put(type, JacksonUtil.treeToValue(cfgJson, TextHandlerConfiguration.class)); + case JSON -> HANDLER_MAP.put(type, JacksonUtil.treeToValue(cfgJson, JsonHandlerConfiguration.class)); + case BINARY -> HANDLER_MAP.put(type, JacksonUtil.treeToValue(cfgJson, BinaryHandlerConfiguration.class)); + default -> throw new IllegalArgumentException("Unknown TCP handler type: " + type); + } + } +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/ForwarderType.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/ForwarderType.java new file mode 100644 index 0000000..e18adb2 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/ForwarderType.java @@ -0,0 +1,12 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg.enums; + +public enum ForwarderType { + + memory, // 本地队列模式 + + kafka // Kafka模式 - 发送到外部 +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/TcpHandlerType.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/TcpHandlerType.java new file mode 100644 index 0000000..4c1a882 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/enums/TcpHandlerType.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.cfg.enums; + +/** + * @author baigod + */ +public enum TcpHandlerType { + TEXT, + BINARY, + JSON +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java new file mode 100644 index 0000000..4fbe3f5 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.domain; + +/** + * @author baigod + */ +public enum DownlinkCmdEnum { + + LOGIN_ACK, + + VERIFY_PRICING_ACK, + + QUERY_PRICING_ACK, + + SET_PRICING, + + REMOTE_START_CHARGING, + + TRANSACTION_RECORD, +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ListenerToHandlerMsg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ListenerToHandlerMsg.java new file mode 100644 index 0000000..0b560bf --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ListenerToHandlerMsg.java @@ -0,0 +1,11 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.domain; + +import java.util.UUID; + +public record ListenerToHandlerMsg(UUID id, byte[] msg, ProtocolSession session) { + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java new file mode 100644 index 0000000..b8ead56 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java @@ -0,0 +1,87 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.domain; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.forwarder.Forwarder; + +import java.io.Closeable; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.function.Function; + +/** + * @author baigod + */ +@Getter +@Slf4j +public abstract class ProtocolSession implements Closeable { + + private static final int REQUEST_CACHE_LIMIT = 1000; + + protected final String protocolName; + + protected final UUID id; + + @Setter + protected LocalDateTime lastActivityTime; + + protected final Set pileCodeSet; + + private final Map> scheduledFutures = new ConcurrentHashMap<>(); + + private final Cache requestCache = Caffeine.newBuilder() + .initialCapacity(REQUEST_CACHE_LIMIT) + .maximumSize(REQUEST_CACHE_LIMIT) + .expireAfterAccess(Duration.ofMinutes(1)) + .build(); + + @Setter + private Forwarder forwarder; + + public ProtocolSession(String protocolName) { + this.protocolName = protocolName; + this.pileCodeSet = new LinkedHashSet<>(); + this.id = UUID.randomUUID(); + this.lastActivityTime = LocalDateTime.now(); + } + + public abstract void onDownlink(DownlinkRestMessage downlinkMsg); + + public void close() { + close(SessionCloseReason.DESTRUCTION); + } + + public void close(SessionCloseReason reason) { + log.info("[{}] Protocol会话关闭,原因: {}", this, reason); + + scheduledFutures.values().forEach(scheduledFuture -> scheduledFuture.cancel(true)); + scheduledFutures.clear(); + } + + @Override + public String toString() { + return "[" + id + "]" + pileCodeSet; + } + + public void addPileCode(String pileCode) { + this.pileCodeSet.add(pileCode); + } + + public void addSchedule(String name, Function> scheduledFutureFunction) { + scheduledFutures.computeIfAbsent(name, scheduledFutureFunction); + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java new file mode 100644 index 0000000..cdeabf8 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.domain; + +import io.netty.buffer.ByteBufUtil; + +import java.net.SocketAddress; +import java.util.UUID; + +public record ProtocolUplinkMsg(SocketAddress address, UUID id, T data, int size) { + + @Override + public String toString() { + if (data instanceof byte[]) { + return ByteBufUtil.hexDump((byte[]) data); + } else { + return data.toString(); + } + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionCloseReason.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionCloseReason.java new file mode 100644 index 0000000..7c7fd58 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionCloseReason.java @@ -0,0 +1,14 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.domain; + +/** + * @author baigod + */ +public enum SessionCloseReason { + DESTRUCTION, + INACTIVE, + MANUALLY +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java new file mode 100644 index 0000000..fdf5a59 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java @@ -0,0 +1,13 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.domain; + +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; + +/** + * @author baigod + */ +public record SessionToHandlerMsg(DownlinkRestMessage downlinkMsg, ProtocolSession session) { +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java new file mode 100644 index 0000000..7cd2c22 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java @@ -0,0 +1,110 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.forwarder; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import sanbing.jcpp.infrastructure.queue.*; +import sanbing.jcpp.infrastructure.queue.common.TopicPartitionInfo; +import sanbing.jcpp.infrastructure.queue.discovery.PartitionProvider; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceType; +import sanbing.jcpp.infrastructure.stats.MessagesStats; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.infrastructure.util.codec.ByteUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.Tracer; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; + +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_PREFIX; +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_TS; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ID; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ORIGIN; + +/** + * @author baigod + */ +@Slf4j +public abstract class Forwarder { + protected static final String ERROR = "error"; + + AtomicBoolean healthy = new AtomicBoolean(true); + + @Getter + private final String protocolName; + + protected MessagesStats forwarderMessagesStats; + + + protected final PartitionProvider partitionProvider; + protected final ServiceInfoProvider serviceInfoProvider; + + protected final boolean isMonolith; + protected QueueProducer> producer; + + public Forwarder(String protocolName, StatsFactory statsFactory, PartitionProvider partitionProvider, ServiceInfoProvider serviceInfoProvider) { + this.protocolName = protocolName; + this.partitionProvider = partitionProvider; + this.serviceInfoProvider = serviceInfoProvider; + + this.forwarderMessagesStats = statsFactory.createMessagesStats("forwarderMessages", "protocol", protocolName); + + this.isMonolith = serviceInfoProvider.isMonolith(); + } + + public abstract Health health(); + + public abstract void destroy(); + + protected void jcppForward(String topic, String key, UplinkQueueMessage msg, BiConsumer consumer) { + QueueMsgHeaders headers = new DefaultQueueMsgHeaders(); + + Tracer currentTracer = TracerContextUtil.getCurrentTracer(); + headers.put(MSG_MD_PREFIX + JCPP_TRACER_ID, ByteUtil.stringToBytes(currentTracer.getTraceId())); + headers.put(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN, ByteUtil.stringToBytes(currentTracer.getOrigin())); + headers.put(MSG_MD_PREFIX + MSG_MD_TS, ByteUtil.longToBytes(currentTracer.getTracerTs())); + + TopicPartitionInfo tpi = partitionProvider.resolve(ServiceType.APP, topic, key); + producer.send(tpi, new ProtoQueueMsg<>(key, msg, headers), new QueueCallback() { + @Override + public void onSuccess(QueueMsgMetadata metadata) { + + TracerContextUtil.newTracer(currentTracer.getTraceId(), currentTracer.getOrigin(), currentTracer.getTracerTs()); + MDCUtils.recordTracer(); + log.trace("单体消息转发成功 key:{}", key); + + if (consumer != null) { + consumer.accept(true, JacksonUtil.newObjectNode()); + } + } + + @Override + public void onFailure(Throwable t) { + + TracerContextUtil.newTracer(currentTracer.getTraceId(), currentTracer.getOrigin(), currentTracer.getTracerTs()); + MDCUtils.recordTracer(); + log.warn("单体消息转发异常", t); + + if (consumer != null) { + ObjectNode objectNode = JacksonUtil.newObjectNode(); + objectNode.put(ERROR, t.getClass() + ": " + t.getMessage()); + consumer.accept(true, objectNode); + } + } + }); + } + + public abstract void sendMessage(UplinkQueueMessage msg, BiConsumer consumer); + + public abstract void sendMessage(UplinkQueueMessage msg); + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java new file mode 100644 index 0000000..ae1ebf3 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java @@ -0,0 +1,204 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.forwarder; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.config.SslConfigs; +import org.apache.kafka.common.header.Headers; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.apache.kafka.common.header.internals.RecordHeaders; +import org.apache.kafka.common.serialization.ByteArraySerializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.boot.actuate.health.Health; +import sanbing.jcpp.infrastructure.queue.discovery.PartitionProvider; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.queue.provider.AppQueueFactory; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.infrastructure.util.codec.ByteUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.Tracer; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.cfg.ForwarderCfg; +import sanbing.jcpp.protocol.cfg.KafkaCfg; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; + +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_PREFIX; +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_TS; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ID; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ORIGIN; + +/** + * @author baigod + */ +@Slf4j +public class KafkaForwarder extends Forwarder { + AtomicBoolean healthy = new AtomicBoolean(true); + + private static final String OFFSET = "offset"; + private static final String PARTITION = "partition"; + private static final String TOPIC = "topic"; + + private final KafkaCfg kafkaCfg; + protected final boolean jcppPartition; + + private KafkaProducer kafkaProducer; + + public KafkaForwarder(String protocolName, + ForwarderCfg forwarderCfg, + StatsFactory statsFactory, + AppQueueFactory appQueueFactory, + PartitionProvider partitionProvider, + ServiceInfoProvider serviceInfoProvider) { + super(protocolName, statsFactory, partitionProvider, serviceInfoProvider); + + this.kafkaCfg = forwarderCfg.getKafka(); + this.jcppPartition = kafkaCfg.isJcppPartition(); + + if (this.isMonolith || jcppPartition) { + + this.producer = appQueueFactory.createProtocolUplinkMsgProducer(kafkaCfg.getTopic()); + + } else { + Properties properties = new Properties(); + properties.put(ProducerConfig.CLIENT_ID_CONFIG, "kafka-forwarder-" + getProtocolName() + "-" + serviceInfoProvider.getServiceId()); + properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaCfg.getBootstrapServers()); + properties.put(ProducerConfig.ACKS_CONFIG, kafkaCfg.getAcks()); + properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ByteArraySerializer.class); + properties.put(ProducerConfig.RETRIES_CONFIG, kafkaCfg.getRetries()); + properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, kafkaCfg.getCompressionType()); + properties.put(ProducerConfig.BATCH_SIZE_CONFIG, kafkaCfg.getBatchSize()); + properties.put(ProducerConfig.LINGER_MS_CONFIG, kafkaCfg.getLingerMs()); + properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, kafkaCfg.getBufferMemory()); + if (this.kafkaCfg.getOtherProperties() != null) { + this.kafkaCfg.getOtherProperties().forEach((k, v) -> { + if (SslConfigs.SSL_KEYSTORE_CERTIFICATE_CHAIN_CONFIG.equals(k) + || SslConfigs.SSL_KEYSTORE_KEY_CONFIG.equals(k) + || SslConfigs.SSL_TRUSTSTORE_CERTIFICATES_CONFIG.equals(k)) { + v = v.replace("\\n", "\n"); + } + properties.put(k, v); + }); + } + + this.kafkaProducer = new KafkaProducer<>(properties); + } + } + + + @Override + public Health health() { + if (healthy.get()) { + return Health.up().withDetail("producer", "Kafka producer is healthy").build(); + } else { + return Health.down().withDetail("producer", "Kafka producer is unhealthy").build(); + } + } + + @Override + public void destroy() { + healthy.set(false); + + if (this.kafkaProducer != null) { + try { + this.kafkaProducer.close(); + } catch (Exception e) { + log.error("Failed to close producer during destroy()", e); + } + } + } + + @Override + public void sendMessage(UplinkQueueMessage msg, BiConsumer consumer) { + String topic = kafkaCfg.getTopic(); + + try { + + String messageKey = msg.getMessageKey(); + + if (isMonolith || jcppPartition) { + + jcppForward(topic, messageKey, msg, consumer); + + } else { + + kafkaForward(topic, messageKey, msg, consumer); + } + + } catch (Exception e) { + log.debug("[{}] Failed to forward Kafka message: {}", getProtocolName(), msg, e); + } + } + + @Override + public void sendMessage(UplinkQueueMessage msg) { + sendMessage(msg, null); + } + + private void kafkaForward(String topic, String key, UplinkQueueMessage msg, BiConsumer consumer) throws InvalidProtocolBufferException { + Headers headers = new RecordHeaders(); + + Tracer currentTracer = TracerContextUtil.getCurrentTracer(); + headers.add(new RecordHeader(MSG_MD_PREFIX + JCPP_TRACER_ID, ByteUtil.stringToBytes(currentTracer.getTraceId()))); + headers.add(new RecordHeader(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN, ByteUtil.stringToBytes(currentTracer.getOrigin()))); + headers.add(new RecordHeader(MSG_MD_PREFIX + MSG_MD_TS, ByteUtil.longToBytes(currentTracer.getTracerTs()))); + + if (kafkaCfg.getEncoder() == KafkaCfg.EncoderType.json) { + + String protoJson = JsonFormat.printer().print(msg); + + log.info("[{}] Kafka forwarder send json headers:{}, message:{}", getProtocolName(), headers, protoJson); + + kafkaProducer.send(new ProducerRecord<>(topic, null, null, key, ByteUtil.stringToBytes(protoJson), headers), + (metadata, e) -> logAndDoConsumer(consumer, metadata, e, currentTracer)); + } else { + + log.info("[{}] Kafka forwarder send protobuf headers:{}, message:{}", getProtocolName(), headers, msg); + + kafkaProducer.send(new ProducerRecord<>(topic, null, null, key, msg.toByteArray(), headers), + (metadata, e) -> logAndDoConsumer(consumer, metadata, e, currentTracer)); + } + } + + private void logAndDoConsumer(BiConsumer consumer, RecordMetadata metadata, Exception e, Tracer currentTracer) { + TracerContextUtil.newTracer(currentTracer.getTraceId(), currentTracer.getOrigin(), currentTracer.getTracerTs()); + MDCUtils.recordTracer(); + log.debug("Kafka 消息转发完成, success:{}", e == null); + + if (consumer != null) { + onComplete(metadata, e, consumer); + } + } + + private void onComplete(RecordMetadata metadata, Exception e, BiConsumer consumer) { + if (consumer == null) { + return; + } + + ObjectNode objectNode = JacksonUtil.newObjectNode(); + objectNode.put(OFFSET, String.valueOf(metadata.offset())); + objectNode.put(PARTITION, String.valueOf(metadata.partition())); + objectNode.put(TOPIC, metadata.topic()); + + if (e != null) { + objectNode.put(ERROR, e.getClass() + ": " + e.getMessage()); + } + + consumer.accept(e == null, objectNode); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/MemoryForwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/MemoryForwarder.java new file mode 100644 index 0000000..cbbbb17 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/MemoryForwarder.java @@ -0,0 +1,84 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.forwarder; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import sanbing.jcpp.infrastructure.queue.discovery.PartitionProvider; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.queue.provider.AppQueueFactory; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.cfg.ForwarderCfg; +import sanbing.jcpp.protocol.cfg.MemoryCfg; + +import java.util.function.BiConsumer; + +/** + * @author baigod + */ +@Slf4j +public class MemoryForwarder extends Forwarder { + + private final MemoryCfg memoryCfg; + + + public MemoryForwarder(String protocolName, + ForwarderCfg forwarderCfg, + StatsFactory statsFactory, + AppQueueFactory appQueueFactory, + PartitionProvider partitionProvider, + ServiceInfoProvider serviceInfoProvider) { + super(protocolName, statsFactory, partitionProvider, serviceInfoProvider); + + this.memoryCfg = forwarderCfg.getMemory(); + + super.producer = appQueueFactory.createProtocolUplinkMsgProducer(memoryCfg.getTopic()); + } + + @Override + public Health health() { + if (healthy.get()) { + return Health.up().withDetail("producer", "Memory producer is healthy").build(); + } else { + return Health.down().withDetail("producer", "Memory producer is unhealthy").build(); + } + } + + @Override + public void destroy() { + healthy.set(false); + + if (this.producer != null) { + try { + this.producer.stop(); + } catch (Exception e) { + log.error("Failed to close producer during destroy()", e); + } + } + } + + @Override + public void sendMessage(UplinkQueueMessage msg, BiConsumer consumer) { + String topic = memoryCfg.getTopic(); + + String key = msg.getMessageKey(); + + try { + + jcppForward(topic, key, msg, consumer); + + } catch (Exception e) { + + log.warn("[{}] Failed to forward Memory message: {}", getProtocolName(), msg, e); + } + } + + @Override + public void sendMessage(UplinkQueueMessage msg) { + sendMessage(msg, null); + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java new file mode 100644 index 0000000..e2c1f62 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java @@ -0,0 +1,142 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.codec.Delimiters; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.json.JsonObjectDecoder; +import io.netty.handler.timeout.IdleStateHandler; +import io.netty.util.concurrent.GlobalEventExecutor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; +import sanbing.jcpp.protocol.listener.tcp.TcpChannelHandler; +import sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration; +import sanbing.jcpp.protocol.listener.tcp.configs.TextHandlerConfiguration; +import sanbing.jcpp.protocol.listener.tcp.decoder.JCPPHeadTailFrameDecoder; +import sanbing.jcpp.protocol.listener.tcp.decoder.JCPPLengthFieldBasedFrameDecoder; +import sanbing.jcpp.protocol.listener.tcp.decoder.TcpMsgDecoder; +import sanbing.jcpp.protocol.listener.tcp.handler.ConnectionLimitHandler; +import sanbing.jcpp.protocol.listener.tcp.handler.IdleEventHandler; +import sanbing.jcpp.protocol.listener.tcp.handler.TracerHandler; + +import java.nio.ByteOrder; + +import static sanbing.jcpp.protocol.cfg.enums.TcpHandlerType.BINARY; +import static sanbing.jcpp.protocol.cfg.enums.TcpHandlerType.TEXT; +import static sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration.LITTLE_ENDIAN_BYTE_ORDER; +import static sanbing.jcpp.protocol.listener.tcp.configs.TextHandlerConfiguration.SYSTEM_LINE_SEPARATOR; + +/** + * @author baigod + */ +@Slf4j +@RequiredArgsConstructor +public abstract class ChannelHandlerInitializer extends ChannelInitializer { + + protected final ChannelGroup CHANNEL_GROUP = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); + + @Override + protected abstract void initChannel(C ch); + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + super.exceptionCaught(ctx, cause); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + super.channelUnregistered(ctx); + } + + public static ChannelHandlerInitializer createTcpChannelHandler(ChannelHandlerParameter parameter) { + TcpHandlerType type = parameter.handlerCfg().getType(); + + return switch (type) { + case TEXT -> new ChannelHandlerInitializer<>() { + @Override + protected void initChannel(SocketChannel socketChannel) { + TextHandlerConfiguration textHandlerConfig = (TextHandlerConfiguration) parameter.handlerCfg().getConfiguration(TEXT); + ByteBuf[] delimiters = SYSTEM_LINE_SEPARATOR.equals(textHandlerConfig.getMessageSeparator()) + ? Delimiters.lineDelimiter() : Delimiters.nulDelimiter(); + DelimiterBasedFrameDecoder framer = new DelimiterBasedFrameDecoder(textHandlerConfig.getMaxFrameLength(), + textHandlerConfig.isStripDelimiter(), delimiters); + socketChannel.pipeline() + .addLast("tracerHandler", new TracerHandler()) + .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), parameter.handlerCfg().getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) + .addLast("idleStateHandler", new IdleStateHandler(parameter.handlerCfg().getIdleTimeoutSeconds(), 0, 0)) + .addLast("idleEventHandler", new IdleEventHandler(parameter.protocolName())) + .addLast("framer", framer) + .addLast("tcpTextDecoder", new TcpMsgDecoder<>(parameter.protocolName(), msg -> TcpMsgDecoder.toString(msg, textHandlerConfig.getCharsetName()))) + .addLast("tcpStringInHandler", new TcpChannelHandler<>(parameter)); + + } + }; + case JSON -> new ChannelHandlerInitializer<>() { + @Override + protected void initChannel(SocketChannel socketChannel) { + socketChannel.pipeline() + .addLast("tracerHandler", new TracerHandler()) + .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), parameter.handlerCfg().getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) + .addLast("idleStateHandler", + new IdleStateHandler(parameter.handlerCfg().getIdleTimeoutSeconds(), 0, 0)) + .addLast("idleEventHandler", new IdleEventHandler(parameter.protocolName())) + .addLast("datagramToJsonDecoder", new JsonObjectDecoder()) + .addLast("tcpJsonDecoder", new TcpMsgDecoder<>(parameter.protocolName(), TcpMsgDecoder::toJson)) + .addLast("tcpJsonInHandler", new TcpChannelHandler<>(parameter)); + } + }; + case BINARY -> new ChannelHandlerInitializer<>() { + @Override + protected void initChannel(SocketChannel socketChannel) { + BinaryHandlerConfiguration binaryHandlerConfig = (BinaryHandlerConfiguration) parameter.handlerCfg().getConfiguration(BINARY); + + ByteOrder byteOrder = LITTLE_ENDIAN_BYTE_ORDER.equalsIgnoreCase(binaryHandlerConfig.getByteOrder()) + ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; + + socketChannel.pipeline() + .addLast("tracerHandler", new TracerHandler()) + .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), parameter.handlerCfg().getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) + .addLast("idleStateHandler", new IdleStateHandler(parameter.handlerCfg().getIdleTimeoutSeconds(), 0, 0)) + .addLast("idleEventHandler", new IdleEventHandler(parameter.protocolName())); + + if (LengthFieldBasedFrameDecoder.class.isAssignableFrom(binaryHandlerConfig.getDecoder())) { + LengthFieldBasedFrameDecoder framer = new LengthFieldBasedFrameDecoder(byteOrder, + binaryHandlerConfig.getMaxFrameLength(), binaryHandlerConfig.getLengthFieldOffset(), + binaryHandlerConfig.getLengthFieldLength(), binaryHandlerConfig.getLengthAdjustment(), + binaryHandlerConfig.getInitialBytesToStrip(), binaryHandlerConfig.isFailFast()); + socketChannel.pipeline().addLast("LengthFieldBasedFrameDecoder", framer); + } else if (JCPPLengthFieldBasedFrameDecoder.class.isAssignableFrom(binaryHandlerConfig.getDecoder())) { + JCPPLengthFieldBasedFrameDecoder framer = new JCPPLengthFieldBasedFrameDecoder(binaryHandlerConfig.getHead(), byteOrder, + binaryHandlerConfig.getLengthFieldOffset(), binaryHandlerConfig.getLengthFieldLength(), + binaryHandlerConfig.getLengthAdjustment(), binaryHandlerConfig.getInitialBytesToStrip()); + socketChannel.pipeline().addLast("JCPPLengthFieldBasedFrameDecoder", framer); + } else if (JCPPHeadTailFrameDecoder.class.isAssignableFrom(binaryHandlerConfig.getDecoder())) { + JCPPHeadTailFrameDecoder framer = new JCPPHeadTailFrameDecoder(binaryHandlerConfig.getHead(), + binaryHandlerConfig.getTail()); + socketChannel.pipeline().addLast("JCPPHeadTailFrameDecoder", framer); + } else { + throw new IllegalArgumentException("Unknown binary decoder"); + } + + socketChannel.pipeline() + .addLast("tcpByteDecoderOverride", new TcpMsgDecoder<>(parameter.protocolName(), TcpMsgDecoder::toByteArray)) + .addLast("tcpByteHandler", new TcpChannelHandler<>(parameter)); + } + }; + case null -> throw new IllegalArgumentException("Unknown: " + parameter.handlerCfg()); + }; + } + + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java new file mode 100644 index 0000000..dc5509d --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java @@ -0,0 +1,24 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener; + +import io.micrometer.core.instrument.Timer; +import sanbing.jcpp.infrastructure.stats.DefaultCounter; +import sanbing.jcpp.infrastructure.stats.MessagesStats; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; +import sanbing.jcpp.protocol.cfg.TcpHandlerCfg; + +import java.util.concurrent.atomic.AtomicInteger; + +public record ChannelHandlerParameter(String protocolName, + TcpHandlerCfg handlerCfg, + ProtocolMessageProcessor protocolMessageProcessor, + AtomicInteger connectionsGauge, + MessagesStats uplinkMsgStats, + MessagesStats downlinkMsgStats, + DefaultCounter uplinkTrafficCounter, + DefaultCounter downlinkTrafficCounter, + Timer downlinkTimer) { +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java new file mode 100644 index 0000000..b438b6f --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener; + +import io.micrometer.core.instrument.Timer; +import lombok.Getter; +import org.springframework.boot.actuate.health.Health; +import sanbing.jcpp.infrastructure.stats.DefaultCounter; +import sanbing.jcpp.infrastructure.stats.MessagesStats; +import sanbing.jcpp.infrastructure.stats.StatsFactory; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author baigod + */ +public abstract class Listener { + + @Getter + private final String protocolName; + + protected AtomicInteger connectionsGauge = new AtomicInteger(); + protected MessagesStats uplinkMsgStats; + protected MessagesStats downlinkMsgStats; + protected DefaultCounter uplinkTrafficCounter; + protected DefaultCounter downlinkTrafficCounter; + protected Timer downlinkTimer; + + public Listener(String protocolName, StatsFactory statsFactory) { + this.protocolName = protocolName; + + statsFactory.createGauge("openConnections", connectionsGauge, "protocol", protocolName); + this.uplinkMsgStats = statsFactory.createMessagesStats("listenerUplinkMessage", "protocol", protocolName); + this.downlinkMsgStats = statsFactory.createMessagesStats("listenerDownlinkMessage", "protocol", protocolName); + this.uplinkTrafficCounter = statsFactory.createDefaultCounter("listenerUplinkTraffic", "protocol", protocolName); + this.downlinkTrafficCounter = statsFactory.createDefaultCounter("listenerDownlinkTraffic", "protocol", protocolName); + this.downlinkTimer = statsFactory.createTimer("listenerDownlink", "protocol", protocolName); + } + + public abstract Health health(); + + public abstract void destroy() throws InterruptedException; +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java new file mode 100644 index 0000000..03d5d24 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java @@ -0,0 +1,238 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp; + +import com.fasterxml.jackson.databind.JsonNode; +import io.micrometer.core.instrument.Timer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.concurrent.Future; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.stats.DefaultCounter; +import sanbing.jcpp.infrastructure.stats.MessagesStats; +import sanbing.jcpp.infrastructure.util.exception.DownlinkException; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; +import sanbing.jcpp.protocol.domain.ListenerToHandlerMsg; +import sanbing.jcpp.protocol.domain.ProtocolUplinkMsg; +import sanbing.jcpp.protocol.domain.SessionCloseReason; +import sanbing.jcpp.protocol.domain.SessionToHandlerMsg; +import sanbing.jcpp.protocol.listener.ChannelHandlerParameter; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.function.Supplier; + +@Slf4j +public class TcpChannelHandler extends SimpleChannelInboundHandler> { + private final String protocolName; + private final ProtocolMessageProcessor protocolMessageProcessor; + + private final MessagesStats uplinkMsgStats; + private final DefaultCounter uplinkTrafficCounter; + private final MessagesStats downlinkMsgStats; + private final DefaultCounter downlinkTrafficCounter; + private final Timer downlinkTimer; + + private final TcpSession tcpSession; + + @SneakyThrows + public TcpChannelHandler(ChannelHandlerParameter parameter) { + this.protocolName = parameter.protocolName(); + this.protocolMessageProcessor = parameter.protocolMessageProcessor(); + + this.uplinkMsgStats = parameter.uplinkMsgStats(); + this.uplinkTrafficCounter = parameter.uplinkTrafficCounter(); + this.downlinkMsgStats = parameter.downlinkMsgStats(); + this.downlinkTrafficCounter = parameter.downlinkTrafficCounter(); + this.downlinkTimer = parameter.downlinkTimer(); + + tcpSession = new TcpSession(protocolName, this::onDownlink, this::writeAndFlush); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ProtocolUplinkMsg msg) { + + if (log.isDebugEnabled()) { + + log.debug("[{}]{}{} Netty拆出到上行报文:{}", protocolName, ctx.channel(), tcpSession, msg); + } + + uplinkMsgStats.incrementTotal(); + + uplinkTrafficCounter.add(msg.size()); + + tcpSession.setLastActivityTime(LocalDateTime.now()); + + if (tcpSession.getAddress() == null) { + + tcpSession.setAddress(msg.address()); + } + + if (tcpSession.getCtx() == null) { + + tcpSession.setCtx(ctx); + } + + T data = msg.data(); + + if (Objects.isNull(data)) { + + log.debug("[{}]{}{} 上行报文为空被过滤 [{}]", protocolName, ctx.channel(), tcpSession, msg); + + return; + } + + try { + + process(msg, ctx); + + uplinkMsgStats.incrementSuccessful(); + + } catch (Exception e) { + + uplinkMsgStats.incrementFailed(); + + log.error("[{}]{}{} TCP管道处理报文异常", protocolName, ctx.channel(), tcpSession, e); + } + } + + private void process(ProtocolUplinkMsg msg, ChannelHandlerContext ctx) { + switch (msg.data()) { + case byte[] bytes -> + protocolMessageProcessor.uplinkHandleAsync(new ListenerToHandlerMsg(msg.id(), bytes, tcpSession), uplinkMsgStats); + case JsonNode json -> + protocolMessageProcessor.uplinkHandleAsync(new ListenerToHandlerMsg(msg.id(), JacksonUtil.writeValueAsBytes(json), tcpSession), uplinkMsgStats); + case String text -> + protocolMessageProcessor.uplinkHandleAsync(new ListenerToHandlerMsg(msg.id(), JacksonUtil.writeValueAsBytes(text.getBytes()), tcpSession), uplinkMsgStats); + case null, default -> { + assert msg.data() != null; + log.warn("[{}]{}{} 不支持的TCP上行报文类型:{}", protocolName, ctx.channel(), tcpSession, msg.data().getClass()); + } + } + } + + protected void onDownlink(ChannelHandlerContext ctx, DownlinkRestMessage downlinkMsg) throws DownlinkException { + protocolMessageProcessor.downlinkHandle(new SessionToHandlerMsg(downlinkMsg, tcpSession), downlinkMsgStats); + } + + protected void writeAndFlush(ChannelHandlerContext ctx, ByteBuf... byteBufList) { + if (byteBufList == null || byteBufList.length == 0) { + + return; + } + + if (ctx.isRemoved()) { + + tcpSession.close(SessionCloseReason.INACTIVE); + + log.warn("[{}]{}{} TCP会话已失效,因此删除会话", protocolName, ctx.channel(), tcpSession); + + return; + } + + downlinkMsgStats.incrementTotal(byteBufList.length); + + for (ByteBuf byteBuf : byteBufList) { + + try { + + if (Objects.isNull(byteBuf)) { + log.warn("[{}]{}{} 下发空报文被拦截", protocolName, ctx.channel(), tcpSession); + continue; + } + + logDownlinkStart(ctx, byteBuf.readableBytes(), () -> ByteBufUtil.hexDump(byteBuf)); + + ctx.writeAndFlush(Unpooled.wrappedBuffer(byteBuf)) + .addListener(channelFuture -> logDownlinkUnsuccessful(ctx, channelFuture)); + + + downlinkMsgStats.incrementSuccessful(); + + } catch (Exception e) { + + downlinkMsgStats.incrementFailed(); + + throw e; + } + } + + } + + private void logDownlinkStart(ChannelHandlerContext ctx, int payloadSize, Supplier logTransform) { + + downlinkTrafficCounter.add(payloadSize); + + if (log.isDebugEnabled()) { + log.debug("[{}]{}{} 开始发送下行报文:{}", protocolName, ctx.channel(), tcpSession, logTransform.get()); + } + } + + private void logDownlinkUnsuccessful(ChannelHandlerContext ctx, Future channelFuture) { + + downlinkTimer.record(Duration.ofMillis(System.currentTimeMillis() - TracerContextUtil.getCurrentTracer().getTracerTs())); + + if (channelFuture.isDone() && !channelFuture.isSuccess()) { + log.info("[{}]{}{} 下行报文发送未成功", protocolName, ctx.channel(), tcpSession); + } + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + + ctx.flush(); + + if (log.isTraceEnabled()) { + log.trace("[{}]{}{} Channel Read Complete [{}]", protocolName, ctx.channel(), tcpSession, ctx.name()); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + log.error("[{}]{}{} Invalid message received, Exception caught", protocolName, ctx.channel(), tcpSession, cause); + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + + super.channelRegistered(ctx); + + log.info("[{}]{} 打开通道", protocolName, ctx.channel()); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + + super.channelUnregistered(ctx); + + log.info("[{}]{}{} 关闭通道", protocolName, ctx.channel(), tcpSession); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + + super.channelActive(ctx); + + log.info("[{}]{} 通道活跃", protocolName, ctx.channel()); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + + super.channelInactive(ctx); + + log.info("[{}]{}{} 通道不活跃", protocolName, ctx.channel(), tcpSession); + } + + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java new file mode 100644 index 0000000..8f99074 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java @@ -0,0 +1,114 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.util.concurrent.Future; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.actuate.health.Health; +import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; +import sanbing.jcpp.protocol.cfg.TcpCfg; +import sanbing.jcpp.protocol.listener.ChannelHandlerInitializer; +import sanbing.jcpp.protocol.listener.ChannelHandlerParameter; +import sanbing.jcpp.protocol.listener.Listener; + +/** + * @author baigod + */ +@Slf4j +public class TcpListener extends Listener { + + private Channel serverChannel; + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; + + private final ChannelHandlerParameter parameter; + + public TcpListener(String protocolName, TcpCfg tcpCfg, ProtocolMessageProcessor protocolMessageProcessor, StatsFactory statsFactory) throws InterruptedException { + super(protocolName, statsFactory); + + parameter = new ChannelHandlerParameter(protocolName, tcpCfg.getHandler(), protocolMessageProcessor, connectionsGauge, uplinkMsgStats, downlinkMsgStats, uplinkTrafficCounter, downlinkTrafficCounter, downlinkTimer); + + tcpServerBootstrap(tcpCfg, getProtocolName()); + } + + private void tcpServerBootstrap(TcpCfg tcpCfg, String protocolName) throws InterruptedException { + bossGroup = new NioEventLoopGroup(tcpCfg.getBossGroupThreadCount(), JCPPThreadFactory.forName("tcp-boss")); + workerGroup = new NioEventLoopGroup(tcpCfg.getWorkerGroupThreadCount(), JCPPThreadFactory.forName("tcp-worker")); + + ChannelHandlerInitializer channelHandler = ChannelHandlerInitializer.createTcpChannelHandler(parameter); + + ServerBootstrap server = new ServerBootstrap() + .group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, tcpCfg.getSoBacklog()) + .option(ChannelOption.SO_REUSEADDR, true) + .option(ChannelOption.SO_RCVBUF, tcpCfg.getSoRcvbuf()) + .childOption(ChannelOption.SO_KEEPALIVE, tcpCfg.isSoKeepAlive()) + .childOption(ChannelOption.TCP_NODELAY, tcpCfg.isNodelay()) + .childOption(ChannelOption.SO_SNDBUF, tcpCfg.getSoSndbuf()) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(channelHandler); + serverChannel = server.bind(tcpCfg.getBindAddress(), tcpCfg.getBindPort()).sync().channel(); + log.info("Tcp server [{}] started, BindAddress:[{}], BindPort: [{}]", protocolName, tcpCfg.getBindAddress(), tcpCfg.getBindPort()); + } + + + private void tcpServerShutdown() throws InterruptedException { + if (this.serverChannel != null) { + ChannelFuture cf = this.serverChannel.close().sync(); + cf.awaitUninterruptibly(); + } + + Future bossFuture = null; + Future workerFuture = null; + + if (bossGroup != null) { + bossFuture = bossGroup.shutdownGracefully(); + } + if (workerGroup != null) { + workerFuture = workerGroup.shutdownGracefully(); + } + + log.info("[{}] Awaiting shutdown gracefully boss and worker groups...", getProtocolName()); + + if (bossFuture != null) { + bossFuture.sync(); + } + if (workerFuture != null) { + workerFuture.sync(); + } + + log.info("[{}] Protocol server stopped!", getProtocolName()); + } + + @Override + public Health health() { + if (serverChannel != null) { + if (serverChannel.isActive()) { + return Health.up().withDetail("TcpServer", "Active").build(); + } else { + return Health.down().withDetail("TcpServer", "Inactive").build(); + } + } + return Health.down().build(); + } + + @Override + public void destroy() throws InterruptedException { + tcpServerShutdown(); + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java new file mode 100644 index 0000000..7071521 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java @@ -0,0 +1,90 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.domain.ProtocolSession; +import sanbing.jcpp.protocol.domain.SessionCloseReason; +import sanbing.jcpp.protocol.listener.tcp.enums.SequenceNumberLength; + +import java.net.SocketAddress; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; + +/** + * 设备会话 + * + * @author baigod + */ +@EqualsAndHashCode(callSuper = true) +@Getter +@Setter +public class TcpSession extends ProtocolSession { + + private SocketAddress address; + + private ChannelHandlerContext ctx; + + private final BiConsumer sendDownlinkConsumer; + + private final BiConsumer writeAndFlushConsumer; + + private final AtomicInteger sequenceNumber = new AtomicInteger(0); + + public int nextSeqNo(SequenceNumberLength sequenceNumberLength) { + synchronized (sequenceNumber) { + int result = sequenceNumber.incrementAndGet(); + switch (sequenceNumberLength) { + case BYTE -> { + if (result == 0xFF) { + sequenceNumber.set(0); + } + } + case SHORT -> { + if (result == Short.MAX_VALUE) { + sequenceNumber.set(0); + } + } + default -> { + if (result == Integer.MAX_VALUE) { + sequenceNumber.set(0); + } + } + } + + return result; + } + } + + public TcpSession(String protocolName, + BiConsumer sendDownlinkConsumer, + BiConsumer writeAndFlushConsumer) { + super(protocolName); + this.sendDownlinkConsumer = sendDownlinkConsumer; + this.writeAndFlushConsumer = writeAndFlushConsumer; + } + + @Override + public void onDownlink(DownlinkRestMessage downlinkMsg) { + sendDownlinkConsumer.accept(ctx, downlinkMsg); + } + + @Override + public void close(SessionCloseReason reason) { + super.close(reason); + + ctx.flush(); + ctx.close(); + } + + public void writeAndFlush(ByteBuf byteBuf) { + writeAndFlushConsumer.accept(ctx, byteBuf); + } +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/BinaryHandlerConfiguration.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/BinaryHandlerConfiguration.java new file mode 100644 index 0000000..39606ef --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/BinaryHandlerConfiguration.java @@ -0,0 +1,75 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.configs; + +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; + +import static sanbing.jcpp.protocol.cfg.enums.TcpHandlerType.BINARY; + +@Data +@ToString +@EqualsAndHashCode +public class BinaryHandlerConfiguration implements HandlerConfiguration { + public static final String LITTLE_ENDIAN_BYTE_ORDER = "LITTLE_ENDIAN"; + + /** + * 拆包器 + */ + private Class decoder; + + /** + * 大小端(共用) + */ + private String byteOrder; + + /** + * 起始域HEX字符串 + */ + private String head; + + /** + * 结束域(HeadTailFrameDecoder) + */ + private String tail; + + /** + * 最大帧长(LengthFieldBasedFrameDecoder) + */ + private int maxFrameLength; + + /** + * 长度域位置(共用) + */ + private int lengthFieldOffset; + + /** + * 长度域长度(共用) + */ + private int lengthFieldLength; + + /** + * 长度调整(共用) + */ + private int lengthAdjustment; + + /** + * 初始跳过字节数(共用) + */ + private int initialBytesToStrip; + + /** + * 快速失败(LengthFieldBasedFrameDecoder) + */ + private boolean failFast; + + public TcpHandlerType getType() { + return BINARY; + } + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/HandlerConfiguration.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/HandlerConfiguration.java new file mode 100644 index 0000000..251db93 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/HandlerConfiguration.java @@ -0,0 +1,37 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.configs; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; + +@JsonTypeInfo( + use = Id.NAME, + property = "type" +) +@JsonSubTypes({ + @Type( + value = TextHandlerConfiguration.class, + name = "TEXT" + ), + @Type( + value = BinaryHandlerConfiguration.class, + name = "BINARY" + ), + @Type( + value = JsonHandlerConfiguration.class, + name = "JSON" + ) +}) +@JsonIgnoreProperties( + ignoreUnknown = true +) +public interface HandlerConfiguration { + TcpHandlerType getType(); +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/JsonHandlerConfiguration.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/JsonHandlerConfiguration.java new file mode 100644 index 0000000..fb68f07 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/JsonHandlerConfiguration.java @@ -0,0 +1,22 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.configs; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; + +import static sanbing.jcpp.protocol.cfg.enums.TcpHandlerType.JSON; + +@Data +@ToString +@EqualsAndHashCode +public class JsonHandlerConfiguration implements HandlerConfiguration { + public TcpHandlerType getType() { + return JSON; + } + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/TextHandlerConfiguration.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/TextHandlerConfiguration.java new file mode 100644 index 0000000..af92ecf --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/configs/TextHandlerConfiguration.java @@ -0,0 +1,32 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.configs; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; + +import static sanbing.jcpp.protocol.cfg.enums.TcpHandlerType.TEXT; + +@Data +@ToString +@EqualsAndHashCode +public class TextHandlerConfiguration implements HandlerConfiguration { + public static final String SYSTEM_LINE_SEPARATOR = "SYSTEM_LINE_SEPARATOR"; + + private int maxFrameLength; + + private boolean stripDelimiter; + + private String messageSeparator; + + private String charsetName; + + public TcpHandlerType getType() { + return TEXT; + } + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPHeadTailFrameDecoder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPHeadTailFrameDecoder.java new file mode 100644 index 0000000..a06f9d6 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPHeadTailFrameDecoder.java @@ -0,0 +1,145 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.decoder; + +import cn.hutool.core.util.HexUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; + +import java.util.Arrays; +import java.util.List; + +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static sanbing.jcpp.protocol.listener.tcp.enums.ReadAct.BREAK; +import static sanbing.jcpp.protocol.listener.tcp.enums.ReadAct.CONTINUE; + +/** + * 起始域结束域拆包 + * + * @author baigod + */ +@Slf4j +public class JCPPHeadTailFrameDecoder extends ByteToMessageDecoder { + /** + * 起始域 + */ + private final byte[] headBytes; + + /** + * 结束域 + */ + private final byte[] tailBytes; + + public JCPPHeadTailFrameDecoder(String head, String tail) { + checkNotNull(head, "head"); + checkNotNull(head, "tail"); + + this.headBytes = HexUtil.decodeHex(head); + this.tailBytes = HexUtil.decodeHex(tail); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + while (in.isReadable()) { + Object decoded = decode(ctx, in); + + if (decoded == null || decoded == BREAK) { + break; + } + + if (decoded == CONTINUE) { + continue; + } + + out.add(decoded); + } + } + + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) { + if (log.isTraceEnabled()) { + String hexDump = ByteBufUtil.hexDump(in); + log.trace("{} 开始解析16进制报文:{}", ctx.channel(), hexDump); + } + // 剩余可读长度 + int readableBytes = in.readableBytes(); + + // 读取到的字节长度小于起始域+结束语长度,则跳过先不处理 + if (readableBytes < headBytes.length + tailBytes.length) { + log.debug("{} 可读长度过短,因此跳过,可读长度:{}", ctx.channel(), readableBytes); + return BREAK; + } + + // byteBuf当前的读索引 + int buffIndex = in.readerIndex(); + + // 查看前n个字节判断消息头 + byte[] firstBytes = new byte[headBytes.length]; + for (int i = 0; i < headBytes.length; i++, buffIndex++) { + firstBytes[i] = in.getByte(buffIndex); + } + + // 校验起始域,如果不符,则丢弃1字节,直到读取到正确的起始域为止 + if (!Arrays.equals(firstBytes, headBytes)) { + byte aByte = in.readByte(); + if (log.isDebugEnabled()) { + log.debug("{} 丢弃1字节 {}", ctx.channel(), String.format("%02X", aByte & 0xFF)); + } + return CONTINUE; + } + + // 记住起始字节的位置 + int startIndex = in.readerIndex(); + + // 找到结束字节序列 + int endIndex = indexOf(in, tailBytes, headBytes.length); + + if (endIndex < 0) { + log.debug("{} 未找到结束域索引,因此先跳过", ctx.channel()); + return BREAK; + } + + // 提取报文 + int length = endIndex + tailBytes.length; + ByteBuf frame = in.retainedSlice(startIndex, length); + in.readerIndex(startIndex + length); + + return frame; + } + + public static int indexOf(ByteBuf in, byte[] bytes, int fromIndex) { + if (bytes.length == 0) { + return 0; + } + if (in.readableBytes() == 0) { + return -1; + } + + int targetCount = bytes.length; + int readerIndex = in.readerIndex() + fromIndex; + int writerIndex = in.writerIndex(); + + int lastIndex = in.indexOf(readerIndex, writerIndex, bytes[0]); + while (lastIndex != -1) { + if (lastIndex + targetCount <= writerIndex) { + boolean matched = true; + for (int i = 1; i < targetCount; i++) { + if (in.getByte(lastIndex + i) != bytes[i]) { + matched = false; + break; + } + } + if (matched) { + return lastIndex - in.readerIndex(); + } + } + lastIndex = in.indexOf(lastIndex + 1, writerIndex, bytes[0]); + } + + return -1; + } +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPLengthFieldBasedFrameDecoder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPLengthFieldBasedFrameDecoder.java new file mode 100644 index 0000000..cd3073c --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/JCPPLengthFieldBasedFrameDecoder.java @@ -0,0 +1,217 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.decoder; + +import cn.hutool.core.util.HexUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.CorruptedFrameException; +import io.netty.handler.codec.DecoderException; +import lombok.extern.slf4j.Slf4j; + +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.List; + +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; +import static sanbing.jcpp.protocol.listener.tcp.enums.ReadAct.BREAK; +import static sanbing.jcpp.protocol.listener.tcp.enums.ReadAct.CONTINUE; + +/** + * JCPP长度域拆包 + * + * @author baigod + */ +@Slf4j +public class JCPPLengthFieldBasedFrameDecoder extends ByteToMessageDecoder { + /** + * 起始域 + */ + private final byte[] headBytes; + private final ByteOrder byteOrder; + private final int lengthFieldOffset; + private final int lengthFieldLength; + private final int lengthFieldEndOffset; + private final int lengthAdjustment; + private final int initialBytesToStrip; + private int frameLengthInt = -1; + + public JCPPLengthFieldBasedFrameDecoder(String head, ByteOrder byteOrder, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, + int initialBytesToStrip) { + checkNotNull(head, "head"); + + this.headBytes = HexUtil.decodeHex(head); + + this.byteOrder = checkNotNull(byteOrder, "byteOrder"); + + checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset"); + + checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip"); + + this.lengthFieldOffset = lengthFieldOffset; + this.lengthFieldLength = lengthFieldLength; + this.lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength; + this.lengthAdjustment = lengthAdjustment; + this.initialBytesToStrip = initialBytesToStrip; + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + while (in.isReadable()) { + Object decoded = decode(ctx, in); + + if (decoded == null || decoded == BREAK) { + break; + } + + if (decoded == CONTINUE) { + continue; + } + + out.add(decoded); + } + } + + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) { + if (log.isDebugEnabled()) { + String hexDump = ByteBufUtil.hexDump(in); + log.debug("{} 开始解析16进制报文:{}", ctx.channel(), hexDump); + } + // 帧长 + long frameLength = 0; + // new frame + if (frameLengthInt == -1) { + // 剩余可读长度 + int readableBytes = in.readableBytes(); + + // 读取到的字节长度小于长度域结束位置,则跳过先不处理 + if (readableBytes < lengthFieldEndOffset) { + log.debug("{} 读取到的字节长度小于长度域结束位置,则跳过先不处理 readableBytes:{}", ctx.channel(), readableBytes); + return BREAK; + } + + // byteBuf当前的读索引 + int buffIndex = in.readerIndex(); + + // 查看前n个字节判断消息头 + byte[] firstBytes = new byte[headBytes.length]; + for (int i = 0; i < headBytes.length; i++, buffIndex++) { + firstBytes[i] = in.getByte(buffIndex); + } + + // 校验起始域,如果不符,则丢弃1字节,直到读取到正确的起始域为止 + if (!Arrays.equals(firstBytes, headBytes)) { + in.skipBytes(1); + return CONTINUE; + } + + // 实际长度域位置 + int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset; + frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset); + + // 如果帧长<0,则跳过buf并抛出异常 + if (frameLength < 0) { + failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset); + } + + // 帧长 = 调整长度 + 长度域结束位置 + frameLength += lengthAdjustment + lengthFieldEndOffset; + + // 如果帧长<长度与结束位置,则跳过并抛出异常 + if (frameLength < lengthFieldEndOffset) { + failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset); + } + + frameLengthInt = (int) frameLength; + } + + // frameLengthInt exist , just check buf + if (in.readableBytes() < frameLengthInt) { + log.debug("{} 可读长度小于帧长,因此跳过 {}", ctx.channel(), frameLengthInt); + return BREAK; + } + + // 初始跳过长度如果大于帧长,则跳过并抛出异常 + if (initialBytesToStrip > frameLengthInt) { + failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip); + } + in.skipBytes(initialBytesToStrip); + + // extract frame + int readerIndex = in.readerIndex(); + int actualFrameLength = frameLengthInt - initialBytesToStrip; + ByteBuf frame = extractFrame(in, readerIndex, actualFrameLength); + in.readerIndex(readerIndex + actualFrameLength); + frameLengthInt = -1; // start processing the next frame + + return frame; + } + + /** + * 获取未调整的帧长度 + */ + protected long getUnadjustedFrameLength(ByteBuf buf, int offset) { + long frameLength; + switch (lengthFieldLength) { + case 1 -> frameLength = buf.getUnsignedByte(offset); + case 2 -> { + if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + frameLength = buf.getUnsignedShortLE(offset); + } else { + frameLength = buf.getUnsignedShort(offset); + } + } + case 3 -> { + if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + frameLength = buf.getUnsignedMediumLE(offset); + } else { + frameLength = buf.getUnsignedMedium(offset); + } + } + case 4 -> { + if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + frameLength = buf.getUnsignedIntLE(offset); + } else { + frameLength = buf.getUnsignedInt(offset); + } + } + case 8 -> { + if (byteOrder == ByteOrder.LITTLE_ENDIAN) { + frameLength = buf.getLongLE(offset); + } else { + frameLength = buf.getLong(offset); + } + } + default -> + throw new DecoderException("unsupported lengthFieldLength: " + lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)"); + } + return frameLength; + } + + private static void failOnNegativeLengthField(ByteBuf in, long frameLength, int lengthFieldEndOffset) { + in.skipBytes(lengthFieldEndOffset); + throw new CorruptedFrameException("negative pre-adjustment length field: " + frameLength); + } + + private static void failOnFrameLengthLessThanLengthFieldEndOffset(ByteBuf in, long frameLength, int lengthFieldEndOffset) { + in.skipBytes(lengthFieldEndOffset); + throw new CorruptedFrameException( + "Adjusted frame length (" + frameLength + ") is less " + "than lengthFieldEndOffset: " + lengthFieldEndOffset); + } + + private static void failOnFrameLengthLessThanInitialBytesToStrip(ByteBuf in, long frameLength, int initialBytesToStrip) { + in.skipBytes((int) frameLength); + throw new CorruptedFrameException( + "Adjusted frame length (" + frameLength + ") is less " + "than initialBytesToStrip: " + initialBytesToStrip); + } + + protected ByteBuf extractFrame(ByteBuf buffer, int index, int length) { + return buffer.retainedSlice(index, length); + } + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/TcpMsgDecoder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/TcpMsgDecoder.java new file mode 100644 index 0000000..e1ae127 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/decoder/TcpMsgDecoder.java @@ -0,0 +1,51 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.decoder; + +import com.fasterxml.jackson.databind.JsonNode; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.protocol.domain.ProtocolUplinkMsg; + +import java.nio.charset.Charset; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; + +@RequiredArgsConstructor +@Slf4j +public class TcpMsgDecoder extends MessageToMessageDecoder { + private final String protocolName; + private final Function transformer; + + @Override + public void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) { + try { + out.add(new ProtocolUplinkMsg<>(ctx.pipeline().channel().remoteAddress(), UUID.randomUUID(), this.transformer.apply(msg), msg.readableBytes())); + } catch (Exception e) { + log.error("[{}][{}] Exception during of decoding message", protocolName, ctx.channel(), e); + throw new RuntimeException(e); + } + } + + public static byte[] toByteArray(ByteBuf buffer) { + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + return bytes; + } + + public static String toString(ByteBuf buffer, String charsetName) { + return buffer.toString(Charset.forName(charsetName)); + } + + public static JsonNode toJson(ByteBuf buffer) { + return JacksonUtil.fromBytes(toByteArray(buffer)); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/ReadAct.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/ReadAct.java new file mode 100644 index 0000000..25a51ed --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/ReadAct.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.enums; + +/** + * 读取动作,辅助枚举 + * + * @author baigod + */ +public enum ReadAct { + BREAK, + CONTINUE +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/SequenceNumberLength.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/SequenceNumberLength.java new file mode 100644 index 0000000..3a48681 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/enums/SequenceNumberLength.java @@ -0,0 +1,21 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.enums; + +/** + * @author baigod + */ +public enum SequenceNumberLength { + + // 1字节 + BYTE, + + // 2字节 + SHORT, + + // 4字节 + INT, + +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java new file mode 100644 index 0000000..9508e26 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java @@ -0,0 +1,60 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.group.ChannelGroup; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j +@RequiredArgsConstructor +public class ConnectionLimitHandler extends ChannelInboundHandlerAdapter { + private final String protocolName; + private final int maxConnections; + private final ChannelGroup channelGroup; + private final AtomicInteger connectionsGauge; + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + if (connectionsGauge.incrementAndGet() > maxConnections) { + ctx.close(); + log.info("[{}]{} channelRegistered超过最大连接数 {},因此关闭连接 {}",protocolName, ctx.channel(), maxConnections, ctx.channel()); + } else { + super.channelRegistered(ctx); + log.info("[{}]{} channelRegistered 当前连接数 {} / {}",protocolName, ctx.channel(), connectionsGauge.get(), maxConnections); + } + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + connectionsGauge.decrementAndGet(); + super.channelUnregistered(ctx); + log.info("[{}]{} channelUnregistered 当前连接数 {} / {}",protocolName, ctx.channel(), connectionsGauge.get(), maxConnections); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + channelGroup.add(ctx.channel()); + log.info("[{}]{} channelActive 当前连接数管道数 {} / {}",protocolName, ctx.channel(), channelGroup.size(), maxConnections); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + super.channelInactive(ctx); + channelGroup.remove(ctx.channel()); + log.info("[{}]{} channelInactive 当前连接数管道数 {} / {}",protocolName, ctx.channel(), channelGroup.size(), maxConnections); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + log.error("[{}]{} ConnectionLimitHandler exceptionCaught",protocolName, ctx.channel(), cause); + ctx.close(); + } +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/IdleEventHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/IdleEventHandler.java new file mode 100644 index 0000000..469db25 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/IdleEventHandler.java @@ -0,0 +1,35 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 心跳检测 + * + * @author baigod + */ +@Slf4j +@RequiredArgsConstructor +public class IdleEventHandler extends ChannelInboundHandlerAdapter { + + private final String protocolName; + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { + if (evt instanceof IdleStateEvent event) { + if (event.state() == IdleState.READER_IDLE) { + ctx.close(); + log.info("[{}]{} 检测到空闲连接,连接关闭", protocolName, ctx.channel()); + } + } + } + +} diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/TracerHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/TracerHandler.java new file mode 100644 index 0000000..cd44e81 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/TracerHandler.java @@ -0,0 +1,25 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.listener.tcp.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; + +/** + * @author baigod + */ +public class TracerHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + + TracerContextUtil.newTracer("jcpp-protocol"); + + MDCUtils.recordTracer(); + + super.channelRead(ctx, msg); + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java new file mode 100644 index 0000000..be674f8 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java @@ -0,0 +1,30 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.provider; + +import sanbing.jcpp.protocol.domain.ProtocolSession; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * @author baigod + */ +public interface ProtocolSessionRegistryProvider { + + /** + * 注册会话 + */ + void register(ProtocolSession protocolSession); + + void unregister(UUID sessionId); + + CompletableFuture get(UUID sessionId); + + /** + * 活跃会话 + */ + void activate(ProtocolSession protocolSession); +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolsConfigProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolsConfigProvider.java new file mode 100644 index 0000000..aafa9d9 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolsConfigProvider.java @@ -0,0 +1,15 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.provider; + +import sanbing.jcpp.protocol.cfg.ProtocolCfg; + +/** + * @author baigod + */ +public interface ProtocolsConfigProvider { + + ProtocolCfg loadConfig(String protocol); +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java new file mode 100644 index 0000000..c32ef51 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java @@ -0,0 +1,115 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.provider.impl; + +import com.github.benmanes.caffeine.cache.AsyncCache; +import com.github.benmanes.caffeine.cache.Caffeine; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; +import sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration; +import sanbing.jcpp.protocol.domain.ProtocolSession; +import sanbing.jcpp.protocol.domain.SessionCloseReason; +import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; + +import java.time.LocalDateTime; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + + +/** + * @author baigod + */ +@Service +@Slf4j +public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRegistryProvider { + private static final int INIT_CACHE_LIMIT = 100_000; + + @Value("${service.protocols.sessions.default-inactivity-timeout-in-sec}") + private int defaultInactivityTimeoutInSec; + + @Value("${service.protocols.sessions.default-state-check-interval-in-sec}") + private int defaultStateCheckIntervalInSec; + + @Getter + private final AsyncCache SESSION_CACHE = buildCache(); + + private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("session-state-checker")); + + @PostConstruct + public void init() { + scheduledExecutorService.scheduleAtFixedRate(() -> + SESSION_CACHE.asMap().forEach((id, sessionCompletableFuture) -> + sessionCompletableFuture.whenComplete((protocolSession, throwable) -> { + if (throwable == null && protocolSession != null) { + if (protocolSession.getLastActivityTime().isBefore(LocalDateTime.now().minusSeconds(defaultInactivityTimeoutInSec))) { + protocolSession.close(SessionCloseReason.INACTIVE); + unregister(protocolSession.getId()); + } + } + }) + ), defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); + } + + @PreDestroy + public void destroy() { + scheduledExecutorService.shutdownNow(); + } + + @Override + public void register(ProtocolSession protocolSession) { + + if (log.isDebugEnabled()) { + log.debug("Registering session {}", protocolSession); + } + + SESSION_CACHE.put(protocolSession.getId(), CompletableFuture.supplyAsync(() -> protocolSession, ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL)); + + } + + @Override + public void unregister(UUID sessionId) { + + log.info("Unregistering session {}", sessionId); + + SESSION_CACHE.synchronous().invalidate(sessionId); + } + + @Override + public CompletableFuture get(UUID sessionId) { + + log.debug("Get session {}", sessionId); + + return SESSION_CACHE.get(sessionId, uuid -> null); + + } + + @Override + public void activate(ProtocolSession protocolSession) { + + if (log.isDebugEnabled()) { + log.debug("Activating session {}", protocolSession); + } + + protocolSession.setLastActivityTime(LocalDateTime.now()); + + SESSION_CACHE.put(protocolSession.getId(), CompletableFuture.supplyAsync(() -> protocolSession, ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL)); + } + + private AsyncCache buildCache() { + return Caffeine.newBuilder() + .initialCapacity(INIT_CACHE_LIMIT) + .maximumSize(INIT_CACHE_LIMIT * 10) + .executor(ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL) + .buildAsync(); + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolsConfigProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolsConfigProvider.java new file mode 100644 index 0000000..774d239 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolsConfigProvider.java @@ -0,0 +1,37 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.provider.impl; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Service; +import sanbing.jcpp.infrastructure.util.config.ConstraintValidator; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.protocol.cfg.ProtocolCfg; +import sanbing.jcpp.protocol.provider.ProtocolsConfigProvider; + +import java.util.Map; + +@Setter +@Service +@Slf4j +@ConfigurationProperties("service") +public class DefaultProtocolsConfigProvider implements ProtocolsConfigProvider { + + private Map protocols; + + @Override + public ProtocolCfg loadConfig(String protocol) { + + ProtocolCfg protocolCfg = protocols.get(protocol); + + log.info("load {}'s configuration: \n{}", protocol, JacksonUtil.toPrettyString(protocolCfg)); + + ConstraintValidator.validateFields(protocolCfg, "'" + protocol + "' configuration is invalid:"); + + return protocolCfg; + } +} \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/pom.xml b/jcpp-protocol-bootstrap/pom.xml new file mode 100644 index 0000000..d2fdb39 --- /dev/null +++ b/jcpp-protocol-bootstrap/pom.xml @@ -0,0 +1,75 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-protocol-bootstrap + jar + JChargePointProtocol Protocol Bootstrap Module + 前置协议服务引导程序 + + + ${basedir}/.. + 3.4.4 + + + + + sanbing + jcpp-protocol-yunkuaichong + + + + + application + + + org.springframework.boot + spring-boot-maven-plugin + + false + ZIP + sanbing.jcpp.protocol.JCPPProtocolServiceApplication + true + + true + ${project.basedir}/src/layers.xml + + + + + + repackage + build-info + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + diff --git a/jcpp-protocol-bootstrap/src/layers.xml b/jcpp-protocol-bootstrap/src/layers.xml new file mode 100644 index 0000000..ebf721a --- /dev/null +++ b/jcpp-protocol-bootstrap/src/layers.xml @@ -0,0 +1,33 @@ + + + + + + org/springframework/boot/loader/** + + + + + + + + + *:*:*SNAPSHOT + + + + + dependencies + spring-boot-loader + snapshot-dependencies + application + + diff --git a/jcpp-protocol-bootstrap/src/main/java/sanbing/jcpp/protocol/JCPPProtocolServiceApplication.java b/jcpp-protocol-bootstrap/src/main/java/sanbing/jcpp/protocol/JCPPProtocolServiceApplication.java new file mode 100644 index 0000000..7c381f8 --- /dev/null +++ b/jcpp-protocol-bootstrap/src/main/java/sanbing/jcpp/protocol/JCPPProtocolServiceApplication.java @@ -0,0 +1,42 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol; + +import org.springframework.boot.Banner; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.util.Arrays; + +/** + * @author baigod + */ +@SpringBootApplication(scanBasePackages = {"sanbing.jcpp.protocol", + "sanbing.jcpp.infrastructure.stats", + "sanbing.jcpp.infrastructure.queue", + "sanbing.jcpp.infrastructure.util"}) +@EnableAsync +@EnableScheduling +public class JCPPProtocolServiceApplication { + + private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name"; + private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "protocol-service"; + + public static void main(String[] args) { + new SpringApplicationBuilder(JCPPProtocolServiceApplication.class).bannerMode(Banner.Mode.LOG).run(updateArguments(args)); + } + + private static String[] updateArguments(String[] args) { + if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) { + String[] modifiedArgs = new String[args.length + 1]; + System.arraycopy(args, 0, modifiedArgs, 0, args.length); + modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM; + return modifiedArgs; + } + return args; + } +} \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/banner.txt b/jcpp-protocol-bootstrap/src/main/resources/banner.txt new file mode 100644 index 0000000..0c37cd9 --- /dev/null +++ b/jcpp-protocol-bootstrap/src/main/resources/banner.txt @@ -0,0 +1,12 @@ + + ___ ________ ________ ________ + |\ \|\ ____\|\ __ \|\ __ \ + \ \ \ \ \___|\ \ \|\ \ \ \|\ \ + __ \ \ \ \ \ \ \ ____\ \ ____\ +|\ \\_\ \ \ \____\ \ \___|\ \ \___| +\ \________\ \_______\ \__\ \ \__\ + \|________|\|_______|\|__| \|__| + +=================================================== +:: ${application.title} :: ${application.formatted-version} +=================================================== \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml b/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml new file mode 100644 index 0000000..34d9fdf --- /dev/null +++ b/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml @@ -0,0 +1,56 @@ + + + + + /var/log/sanbing/jcpp + %d{yyyy-MM-dd HH:mm:ss:SSS} [%X{TRACE_ID}] [%t] %p %c{1} %m%n%throwable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml new file mode 100644 index 0000000..4baf214 --- /dev/null +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -0,0 +1,152 @@ +server: + address: "${HTTP_BIND_ADDRESS:0.0.0.0}" + port: "${HTTP_BIND_PORT:8081}" + undertow: + buffer-size: "${SERVER_UNDERTOW_BUFFER_SIZE:16384}" + directBuffers: "${SERVER_UNDERTOW_DIRECT_BUFFERS:true}" + threads: + io: "${SERVER_UNDERTOW_THREADS_IO:4}" + worker: "${SERVER_UNDERTOW_THREADS_WORKER:128}" + max-http-post-size: "${SERVER_UNDERTOW_MAX_HTTP_POST_SIZE:10MB}" + no-request-timeout: "${SERVER_UNDERTOW_NO_REQUEST_TIMEOUT:10000}" + accesslog: + enabled: true + pattern: "%t %a %r %s (%D ms)" + dir: /var/log/sanbing/accesslog + options: + server: + record-request-start-time: true + +spring: + application: + name: "${SPRING_APPLICATION_NAME:java-charge-point-protocol-server}" + +management: + endpoints: + web: + exposure: + include: '${METRICS_ENDPOINTS_EXPOSE:prometheus,health}' + endpoint: + health: + show-details: always + +metrics: + enabled: "${METRICS_ENABLED:true}" + timer: + percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" + +service: + # 服务类型:纯协议解析前置 - protocol,纯应用后端 - app,单体服务(包含protocol和app) - monolith + type: "${SERVICE_TYPE:protocol}" + # 可自定义的服务ID,如果不指定,则默认为HOSTNAME + id: "${SERVICE_ID:}" + protocols: + sessions: + default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" + default-state-check-interval-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" + yunkuaichongV150: + enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" + listener: + tcp: + bind-address: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BIND_ADDRESS:0.0.0.0}" + bind-port: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BIND_PORT:38001}" + boss-group-thread_count: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BOSS_GROUP_THREADS:4}" + worker-group-thread-count: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_WORKER_GROUP_THREADS:16}" + so-keep-alive: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_KEEPALIVE:true}" + so-backlog: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_BACKLOG:128}" + so-rcvbuf: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_RCVBUF:65536}" + so-sndbuf: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_SO_SNDBUF:65536}" + nodelay: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_NODELAY:true}" + handler: + idle-timeout-seconds: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_HANDLER_IDLE_TIMEOUT_SECONDS:600}" + max_connections: "${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_HANDLER_MAX_CONNECTIONS:100000}" + # 默认为二进制类型的拆包器 + # 可选JSON类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV150_NETTY_HANDLER_BINARY_CONFIGURATION:type:JSON}" + # 可选纯文本类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV150_NETTY_HANDLER_BINARY_CONFIGURATION:type:TEXT;maxFrameLength:128;stripDelimiter:true;messageSeparator:null;charsetName:UTF-8}" + configuration: "${PROTOCOLS_YUNKUAICHONGV150_NETTY_HANDLER_BINARY_CONFIGURATION:type:BINARY;decoder:sanbing.jcpp.protocol.listener.tcp.decoder.JCPPLengthFieldBasedFrameDecoder;byteOrder:LITTLE_ENDIAN;head:68;lengthFieldOffset:1;lengthFieldLength:1;lengthAdjustment:2;initialBytesToStrip:0}" + forwarder: + # 作为前置服务单独启时可选:kafka、kafka-sharding,未来计划扩展RocketMQ, GRpc、REST + type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_TYPE:kafka}" + kafka: + topic: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_TOPIC:protocol_uplink}" + jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 + # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + acks: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ACKS:1}" + # # 可选 protobuf(推荐)、json + encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" + retries: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_RETRIES:1}" + compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + batch-size: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_BATCH_SIZE:16384}" + linger-ms: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_LINGER_MS:0}" + buffer-memory: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_BUFFER_MEMORY:33554432}" + other-properties: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" + +# 应用程序服务注册中心配置 +zk: + enabled: "${ZOOKEEPER_ENABLED:true}" + url: "${ZOOKEEPER_URL:zookeeper:2181}" + retry-interval-ms: "${ZOOKEEPER_RETRY_INTERVAL_MS:3000}" + connection-timeout-ms: "${ZOOKEEPER_CONNECTION_TIMEOUT_MS:3000}" + session-timeout-ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" + zk-dir: "${ZOOKEEPER_NODES_DIR:/jcpp}" + recalculate-delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" + +# 队列配置 +queue: + # 在protocol服务中只能选择 kafka + type: "${QUEUE_TYPE:kafka}" + partitions: + hash_function_name: "${QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + in_memory: + stats: + print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" + kafka: + bootstrap-servers: "${KAFKA_SERVERS:kafka:9092}" + ssl: + enabled: "${KAFKA_SSL_ENABLED:false}" + truststore-location: "${KAFKA_SSL_TRUSTSTORE_LOCATION:}" + truststore-password: "${KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + keystore-location: "${KAFKA_SSL_KEYSTORE_LOCATION:}" + keystore-password: "${KAFKA_SSL_KEYSTORE_PASSWORD:}" + key-password: "${KAFKA_SSL_KEY_PASSWORD:}" + acks: "${KAFKA_ACKS:1}" + retries: "${KAFKA_RETRIES:1}" + compression-type: "${KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + batch-size: "${KAFKA_BATCH_SIZE:1048576}" + linger-ms: "${KAFKA_LINGER_MS:1}" + max-request-size: "${KAFKA_MAX_REQUEST_SIZE:1048576}" + max-in-flight-requests-per-connection: "${KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + buffer-memory: "${BUFFER_MEMORY:33554432}" + replication-factor: "${QUEUE_KAFKA_REPLICATION_FACTOR:1}" + max-poll-interval-ms: "${QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + max-poll-records: "${QUEUE_KAFKA_MAX_POLL_RECORDS:10240}" + max-partition-fetch-bytes: "${QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + fetch-max-bytes: "${QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" + request-timeout-ms: "${QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" + session-timeout-ms: "${QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" + auto-offset-reset: "${QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" + other-inline: "${QUEUE_KAFKA_OTHER_PROPERTIES:}" + topic-properties: + app: "${QUEUE_KAFKA_APP_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + consumer-stats: + enabled: "${QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" + print-interval-ms: "${QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" + kafka-response-timeout-ms: "${QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" + app: + topic: "${QUEUE_APP_TOPIC:protocol_uplink}" + poll-interval: "${QUEUE_APP_POLL_INTERVAL_MS:5}" + pack-processing-timeout: "${QUEUE_APP_PACK_PROCESSING_TIMEOUT_MS:2000}" + consumer-per-partition: "${QUEUE_APP_CONSUMER_PER_PARTITION:true}" + partitions: "${QUEUE_APP_PARTITIONS:10}" + # 可选 protobuf(推荐)、json,需要跟..forwarder.kafka.encoder保持一致 + decoder: "${QUEUE_APP_DECODER:protobuf}" + stats: + enabled: "${QUEUE_APP_STATS_ENABLED:true}" + print-interval-ms: "${QUEUE_APP_STATS_PRINT_INTERVAL_MS:60000}" + +thread-pool: + sharding: + hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:128}" + stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java new file mode 100644 index 0000000..a9ad896 --- /dev/null +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java @@ -0,0 +1,38 @@ +package sanbing.jcpp.protocol; /** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ + +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; + +/** + * @author baigod + */ +@ActiveProfiles("test") +@SpringBootTest(classes = JCPPProtocolServiceApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@AutoConfigureMockMvc +public class AbstractProtocolTestBase { + + static { + System.setProperty("spring.config.name", "protocol-service"); + } + + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected Environment environment; +} \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java new file mode 100644 index 0000000..fb5c959 --- /dev/null +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java @@ -0,0 +1,147 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.adapter; + +import cn.hutool.core.util.HexUtil; +import com.fasterxml.jackson.databind.JsonNode; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import jakarta.annotation.Resource; +import org.junit.After; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.property.PropertyUtils; +import sanbing.jcpp.proto.gen.ProtocolProto; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.AbstractProtocolTestBase; +import sanbing.jcpp.protocol.domain.DownlinkCmdEnum; +import sanbing.jcpp.protocol.domain.ProtocolSession; +import sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration; +import sanbing.jcpp.protocol.listener.tcp.decoder.JCPPLengthFieldBasedFrameDecoder; +import sanbing.jcpp.protocol.listener.tcp.decoder.TcpMsgDecoder; +import sanbing.jcpp.protocol.provider.impl.DefaultProtocolSessionRegistryProvider; + +import java.nio.ByteOrder; +import java.util.UUID; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration.LITTLE_ENDIAN_BYTE_ORDER; + +class DownlinkControllerTest extends AbstractProtocolTestBase { + final String PROTOCOL_NAME = "yunkuaichongV150"; + + @Value("${service.protocols.yunkuaichongV150.listener.tcp.handler.configuration}") + private String yunkuaichongV150TcpHandler; + + @Value("${service.protocols.yunkuaichongV150.listener.tcp.bind-port}") + private int yunkuaichongV150TcpPort; + + @Resource + DefaultProtocolSessionRegistryProvider sessionRegistryProvider; + + private EventLoopGroup group; + private Channel channel; + + @BeforeEach + void setUp() throws InterruptedException { + final JsonNode cfgJson = JacksonUtil.valueToTree(PropertyUtils.getProps(yunkuaichongV150TcpHandler)); + + BinaryHandlerConfiguration binaryHandlerConfig = JacksonUtil.treeToValue(cfgJson, BinaryHandlerConfiguration.class); + + ByteOrder byteOrder = LITTLE_ENDIAN_BYTE_ORDER.equalsIgnoreCase(binaryHandlerConfig.getByteOrder()) + ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; + + JCPPLengthFieldBasedFrameDecoder framer = new JCPPLengthFieldBasedFrameDecoder(binaryHandlerConfig.getHead(), byteOrder, + binaryHandlerConfig.getLengthFieldOffset(), binaryHandlerConfig.getLengthFieldLength(), + binaryHandlerConfig.getLengthAdjustment(), binaryHandlerConfig.getInitialBytesToStrip()); + + + group = new NioEventLoopGroup(); + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + ch.pipeline() + .addLast(framer) + .addLast("tcpByteDecoderOverride", new TcpMsgDecoder<>(PROTOCOL_NAME, TcpMsgDecoder::toByteArray)) + .addLast(new SimpleChannelInboundHandler<>() { + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + + log.info("接收到字节码:{}", msg); + } + }); + } + }); + + // 连接到服务器 + ChannelFuture f = b.connect("127.0.0.1", yunkuaichongV150TcpPort).sync(); + channel = f.channel(); + } + + @After + public void tearDown() { + if (channel != null) { + channel.close(); + } + group.shutdownGracefully(); + } + + @Test + void remoteStartCharging() throws Exception { + // 先发送一段登录 + channel.writeAndFlush(Unpooled.wrappedBuffer(HexUtil.decodeHex("6822001900012023121200001001011047562e393572313300898604d11722d0348606024E87"))).sync(); + + // 停一会等注册完成 todo 也可以读下行消息判断是否登录成功 + Thread.sleep(1000); + + UUID messageId = UUID.randomUUID(); + ProtocolSession protocolSession = sessionRegistryProvider.getSESSION_CACHE().asMap().values().stream().findFirst().get().get(); + UUID sessionId = protocolSession.getId(); + UUID requestId = UUID.randomUUID(); + + // 创建 DownlinkRestMessage 实例 + String pileCode = "20231212000010"; + DownlinkRestMessage downlinkMsg = DownlinkRestMessage.newBuilder() + .setMessageIdMSB(messageId.getMostSignificantBits()) + .setMessageIdLSB(messageId.getLeastSignificantBits()) + .setSessionIdMSB(sessionId.getMostSignificantBits()) + .setSessionIdLSB(sessionId.getLeastSignificantBits()) + .setProtocolName(PROTOCOL_NAME) + .setPileCode(pileCode) + .setRequestIdMSB(requestId.getMostSignificantBits()) + .setRequestIdLSB(requestId.getLeastSignificantBits()) + .setDownlinkCmd(DownlinkCmdEnum.REMOTE_START_CHARGING.name()) + .setRemoteStartChargingRequest(ProtocolProto.RemoteStartChargingRequest.newBuilder() + .setPileCode(pileCode) + .setGunCode("01") + .setLimitCent(10000) + .setTradeNo("12345678901234567890") + .build()) + .build(); + + // 序列化为 Protobuf 字节流 + byte[] protobufContent = downlinkMsg.toByteArray(); + + // 调用 POST 接口 + mockMvc.perform(post("/api/onDownlink") + .contentType("application/x-protobuf") + .content(protobufContent)) + .andDo(print()) // 打印请求和响应信息 + .andExpect(status().is(HttpStatus.OK.value())); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/pom.xml b/jcpp-protocol-yunkuaichong/pom.xml new file mode 100644 index 0000000..b702510 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/pom.xml @@ -0,0 +1,35 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-protocol-yunkuaichong + jar + JChargePointProtocol Yunkuaichong Protocol Module + 云快充1.5 + + + ${basedir}/.. + + + + + sanbing + jcpp-protocol-api + + + + diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java new file mode 100644 index 0000000..f38e54a --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java @@ -0,0 +1,154 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong; + +import cn.hutool.core.text.CharSequenceUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.apache.commons.lang3.StringUtils; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.proto.gen.ProtocolProto; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.listener.tcp.enums.SequenceNumberLength; +import sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.time.LocalTime; +import java.util.List; + +import static sanbing.jcpp.infrastructure.util.codec.ByteUtil.crcSum; +import static sanbing.jcpp.infrastructure.util.codec.ByteUtil.toBytes; + +/** + * @author baigod + */ +public class AbstractYunKuaiChongCmdExe { + + private static final byte TOP_BYTE = 0x00; + private static final byte PEAK_BYTE = 0x01; + private static final byte FLAT_BYTE = 0x02; + private static final byte VALLEY_BYTE = 0x03; + + protected static final byte YUNKUAICHONG_HEAD = 0x68; + protected static final int YUNKUAICHONG_NORMAL_ENCRYPTION_FLAG = 0; + + private static final DecimalFormat PRICING_ID_DECIMAL_FORMAT = new DecimalFormat("0000"); + + protected static String decodeTradeNo(byte[] tradeNo) { + String tradeNoStr = BCDUtil.toString(tradeNo); + return CharSequenceUtil.strip(tradeNoStr, "0", null); + } + + protected byte[] encodePricingId(long pricingId) { + return BCDUtil.toBytes(PRICING_ID_DECIMAL_FORMAT.format(pricingId % 10000)); + } + + protected static byte getFlagForCurrentTime(List periodList, LocalTime currentTime) { + for (ProtocolProto.PeriodProto period : periodList) { + LocalTime beginLt = LocalTime.parse(period.getBegin()); + LocalTime endLt = "00:00".equals(period.getEnd()) ? LocalTime.MAX : LocalTime.parse(period.getEnd()); + if ((currentTime.equals(beginLt) || currentTime.isAfter(beginLt)) && currentTime.isBefore(endLt)) { + switch (period.getFlag()) { + case TOP: + return TOP_BYTE; + case PEAK: + return PEAK_BYTE; + case FLAT: + return FLAT_BYTE; + case VALLEY: + return VALLEY_BYTE; + } + } + } + return FLAT_BYTE; // 默认情况下返回平价 + } + + protected static byte[] encodePileCode(String pileCode) { + if (StringUtils.length(pileCode) > 32) { + throw new IllegalArgumentException("云快充1.5可接受最大桩编号为14位"); + } + + String pileCodeStr = StringUtils.leftPad(pileCode, 14, '0'); + + return BCDUtil.toBytes(pileCodeStr); + } + + protected static byte[] encodeGunCode(String gunCode) { + if (StringUtils.length(gunCode) > 2) { + throw new IllegalArgumentException("云快充1.5可接受最大枪编号为2位"); + } + + String gunCodeStr = StringUtils.leftPad(gunCode, 2, '0'); + + return BCDUtil.toBytes(gunCodeStr); + } + + protected static byte[] encodeTradeNo(String tradeNo) { + if (StringUtils.length(tradeNo) > 32) { + throw new IllegalArgumentException("云快充1.5可接受最大交易流水号为32位"); + } + + String tradeNoStr = StringUtils.leftPad(tradeNo, 32, '0'); + + return BCDUtil.toBytes(tradeNoStr); + } + + + protected byte[] encode(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, + int seqNo, + int encryptionFlag, + ByteBuf msgBody) { + int msgBodyLength = msgBody.readableBytes(); + ByteBuf response = Unpooled.buffer(msgBodyLength + 6); + response.writeByte(YUNKUAICHONG_HEAD); + response.writeByte(msgBodyLength + 4); + response.writeShortLE(seqNo); + response.writeByte(encryptionFlag); + response.writeByte(downlinkCmd.getCmd()); + response.writeBytes(msgBody); + + // 帧校验域:从序列号域到数据域的 CRC 校验,校验多项式为 0x180D,低字节在前,高字节在后 + byte[] checkArr = new byte[msgBodyLength + 4]; + System.arraycopy(response.array(), 2, checkArr, 0, checkArr.length); + + response.writeShortLE(crcSum(checkArr)); + + return toBytes(response); + } + + protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, + int seqNo, + int encryptionFlag, + ByteBuf msgBody, + TcpSession tcpSession) { + + byte[] encode = encode(downlinkCmd, seqNo, encryptionFlag, msgBody); + + tcpSession.writeAndFlush(Unpooled.copiedBuffer(encode)); + } + + protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, + ByteBuf msgBody, + TcpSession tcpSession) { + + byte[] encode = encode(downlinkCmd, + tcpSession.nextSeqNo(SequenceNumberLength.SHORT), + YUNKUAICHONG_NORMAL_ENCRYPTION_FLAG, + msgBody); + + tcpSession.writeAndFlush(Unpooled.copiedBuffer(encode)); + } + + protected static BigDecimal reduceMagnification(long value, int magnification) { + return new BigDecimal(value).divide(new BigDecimal(magnification), 4, RoundingMode.HALF_UP); + } + + protected static BigDecimal reduceMagnification(long value, int magnification, int scale) { + return new BigDecimal(value).divide(new BigDecimal(magnification), scale, RoundingMode.HALF_UP); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java new file mode 100644 index 0000000..41c5f97 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java @@ -0,0 +1,17 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong; + +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; + +/** + * @author baigod + */ +public abstract class YunKuaiChongDownlinkCmdExe extends AbstractYunKuaiChongCmdExe{ + + public abstract void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx); + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java new file mode 100644 index 0000000..83db1a7 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java @@ -0,0 +1,40 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; + +import java.io.Serializable; +import java.util.UUID; + +/** + * @author baigod + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +public class YunKuaiChongDwonlinkMessage implements Serializable { + public static final byte SUCCESS_BYTE = 0x00; + public static final byte FAILURE_BYTE = 0x01; + + // 消息ID + private UUID id; + + // 请求ID(如有) + private UUID requestId; + + // 指令 + private int cmd; + + // 消息体 + private DownlinkRestMessage msg; + + // 上行消息 + private YunKuaiChongUplinkMessage requestData; + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java new file mode 100644 index 0000000..450dd81 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java @@ -0,0 +1,33 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong; + +import com.google.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; + +/** + * @author baigod + */ +@Slf4j +public abstract class YunKuaiChongUplinkCmdExe extends AbstractYunKuaiChongCmdExe{ + + public abstract void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx); + + protected static UplinkQueueMessage.Builder uplinkMessageBuilder(String messageKey, TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage) { + return UplinkQueueMessage.newBuilder() + .setMessageIdMSB(yunKuaiChongUplinkMessage.getId().getMostSignificantBits()) + .setMessageIdLSB(yunKuaiChongUplinkMessage.getId().getLeastSignificantBits()) + .setSessionIdMSB(tcpSession.getId().getMostSignificantBits()) + .setSessionIdLSB(tcpSession.getId().getLeastSignificantBits()) + .setRequestData(ByteString.copyFrom(JacksonUtil.writeValueAsBytes(yunKuaiChongUplinkMessage))) + .setMessageKey(messageKey) + .setProtocolName(tcpSession.getProtocolName()); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkMessage.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkMessage.java new file mode 100644 index 0000000..b9a9aeb --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkMessage.java @@ -0,0 +1,51 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.UUID; + +@Data +@Accessors(chain = true) +public class YunKuaiChongUplinkMessage implements Serializable { + // 消息ID + private final UUID id; + + // 起始域 + private int head; + + // 数据长度 + private int dataLength; + + // 序列号 + private int sequenceNumber; + + // 加密标识 + private int encryptionFlag; + + // 指令 + private int cmd; + + // 消息体 + private byte[] msgBody; + + // 校验和 + private int checkSum; + + // 真实报文 + private byte[] rawFrame; + + public YunKuaiChongUplinkMessage(UUID id) { + this.id = id; + } + + public YunKuaiChongUplinkMessage() { + this(UUID.randomUUID()); + } + +} diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java new file mode 100644 index 0000000..d86321b --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java @@ -0,0 +1,19 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.annotation; + +import java.lang.annotation.*; + +/** + * @author baigod + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface YunKuaiChongCmd { + + byte value(); + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java new file mode 100644 index 0000000..43164b8 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java @@ -0,0 +1,224 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150; + +import cn.hutool.core.util.ClassUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.JCPPPair; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; +import sanbing.jcpp.protocol.domain.ListenerToHandlerMsg; +import sanbing.jcpp.protocol.domain.SessionToHandlerMsg; +import sanbing.jcpp.protocol.forwarder.Forwarder; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; +import sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import static sanbing.jcpp.infrastructure.util.codec.ByteUtil.checkCrcSum; + +@Slf4j +public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProcessor { + private final Map UPLINK_CMD_EXE_MAP = new ConcurrentHashMap<>(); + private final Map DOWNLINK_CMD_EXE_MAP = new ConcurrentHashMap<>(); + + public YunKuaiChongV15ProtocolMessageProcessor(Forwarder forwarder, ProtocolContext protocolContext) { + super(forwarder, protocolContext); + + Set> cmdClasses = ClassUtil.scanPackageByAnnotation(ClassUtil.getPackage(this.getClass()), YunKuaiChongCmd.class); + cmdClasses.stream().filter(YunKuaiChongUplinkCmdExe.class::isAssignableFrom) + .forEach(clazz -> { + byte cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); + try { + YunKuaiChongUplinkCmdExe yunKuaiChongUplinkCmdExe = (YunKuaiChongUplinkCmdExe) clazz.getDeclaredConstructor().newInstance(); + UPLINK_CMD_EXE_MAP.put(cmd, yunKuaiChongUplinkCmdExe); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new RuntimeException(e); + } + }); + + cmdClasses.stream().filter(YunKuaiChongDownlinkCmdExe.class::isAssignableFrom) + .forEach(clazz -> { + byte cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); + try { + YunKuaiChongDownlinkCmdExe yunKuaiChongDownlinkCmdExe = (YunKuaiChongDownlinkCmdExe) clazz.getDeclaredConstructor().newInstance(); + DOWNLINK_CMD_EXE_MAP.put(cmd, yunKuaiChongDownlinkCmdExe); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new RuntimeException(e); + } + }); + } + + @Override + public void uplinkHandle(ListenerToHandlerMsg listenerToHandlerMsg) { + UUID msgId = listenerToHandlerMsg.id(); + byte[] msg = listenerToHandlerMsg.msg(); + TcpSession session = (TcpSession) listenerToHandlerMsg.session(); + + ByteBuf in = Unpooled.copiedBuffer(msg); + + in.markReaderIndex(); + + findStartFlag(in); + + // 判断是否可以读取报头,8个字节 + if (in.readableBytes() < 6) { + in.resetReaderIndex(); + return; + } + + // 起始标识, 固定为0x68 + int startFlag = in.readUnsignedByte(); + if (startFlag != 0x68) { + in.resetReaderIndex(); + return; + } + + // 数据长度 = 序列号域+加密标志+帧类型标志+消息体 + int dataLength = in.readUnsignedByte(); + + // 报文的流水号 + int seqNo = in.readUnsignedShortLE(); + + // 加密标志 + int encrpyFlag = in.readUnsignedByte(); + + // 帧类型标志 + int frameType = in.readUnsignedByte(); + + // 判断是否可以读取消息体,N-4个字节 + int msgBodyLength = dataLength - 4; + if (in.readableBytes() < msgBodyLength) { + in.resetReaderIndex(); + return; + } + + // 消息体 + byte[] msgBody = new byte[msgBodyLength]; + in.readBytes(msgBody); + + // 判断是否可以读取校验和, 2个字节 + if (in.readableBytes() < 2) { + in.resetReaderIndex(); + return; + } + + byte[] byCheckSum = new byte[2]; + in.readBytes(byCheckSum); + ByteBuf csTemp = Unpooled.buffer(); + csTemp.writeBytes(byCheckSum); + + // 校验校验和 + int checkSum = csTemp.readUnsignedShort(); + + byte[] checkData = new byte[dataLength]; + + System.arraycopy(msg, 2, checkData, 0, dataLength); + + JCPPPair checkResult = checkCrcSum(checkData, checkSum); + + if (!checkResult.getFirst()) { + csTemp.writeBytes(byCheckSum); + checkSum = csTemp.readUnsignedShortLE(); + checkResult = checkCrcSum(checkData, checkSum); + log.info("云快充V1.5检验和 第二次检查: checkResult:{}, checkSum:{}", checkResult, checkSum); + } + + if (!checkResult.getFirst()) { + log.info("云快充V1.5检验和不一致两次不通过 不处理! CMD:{},校验域:{},正确校验和:{}", frameType, checkSum, checkResult.getSecond()); + return; + } + + YunKuaiChongUplinkMessage message = new YunKuaiChongUplinkMessage(msgId); + message.setHead(startFlag); + message.setDataLength(dataLength); + message.setSequenceNumber(seqNo); + message.setEncryptionFlag(encrpyFlag); + message.setCmd(frameType); + message.setMsgBody(msgBody); + message.setCheckSum(checkSum); + message.setRawFrame(msg); + + exeCmd(message, session); + } + + @Override + public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) throws Exception { + TcpSession session = (TcpSession) sessionToHandlerMsg.session(); + + DownlinkRestMessage protocolDownlinkMsg = sessionToHandlerMsg.downlinkMsg(); + + int cmd = YunKuaiChongV150DownlinkCmdEnum.valueOf(protocolDownlinkMsg.getDownlinkCmd()).getCmd(); + + YunKuaiChongDwonlinkMessage message = new YunKuaiChongDwonlinkMessage(); + message.setId(new UUID(protocolDownlinkMsg.getMessageIdMSB(), protocolDownlinkMsg.getMessageIdLSB())); + message.setCmd(cmd); + message.setMsg(protocolDownlinkMsg); + + if (protocolDownlinkMsg.hasRequestIdMSB() && protocolDownlinkMsg.hasRequestIdLSB()) { + message.setRequestId(new UUID(protocolDownlinkMsg.getRequestIdMSB(), protocolDownlinkMsg.getRequestIdLSB())); + } + + if (protocolDownlinkMsg.hasRequestData()) { + message.setRequestData(JacksonUtil.fromBytes(protocolDownlinkMsg.getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class)); + } + + exeCmd(message, session); + } + + private void exeCmd(YunKuaiChongUplinkMessage message, TcpSession session) { + YunKuaiChongUplinkCmdExe uplinkCmdExe = UPLINK_CMD_EXE_MAP.get((byte) message.getCmd()); + + if (uplinkCmdExe == null) { + + log.info("[{}] 云快充V1.5协议接收到未知的上行指令 {}", session, message.getCmd()); + + return; + } + + uplinkCmdExe.execute(session, message, protocolContext); + } + + private void exeCmd(YunKuaiChongDwonlinkMessage message, TcpSession session) { + YunKuaiChongDownlinkCmdExe downlinkCmdExe = DOWNLINK_CMD_EXE_MAP.get((byte) message.getCmd()); + + if (downlinkCmdExe == null) { + + log.info("[{}] 云快充V1.5协议接收到未知的下行指令 {}", session, message.getCmd()); + + return; + } + + downlinkCmdExe.execute(session, message, protocolContext); + } + + private static void findStartFlag(ByteBuf buf) { + int count = buf.readableBytes(); + for (int index = buf.readerIndex(); index < count - 1; index++) { + if (buf.getByte(index) == (byte) 0x68) { + buf.readerIndex(index); + return; + } + } + buf.resetReaderIndex(); + } + + +} diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java new file mode 100644 index 0000000..6fb79d9 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java @@ -0,0 +1,45 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150; + +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.annotation.ProtocolComponent; +import sanbing.jcpp.protocol.ProtocolBootstrap; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; + +import static sanbing.jcpp.protocol.yunkuaichong.v150.YunkuaichongV150ProtocolBootstrap.PROTOCOL_NAME; + +/** + * @author baigod + */ + +@ProtocolComponent(PROTOCOL_NAME) +@Slf4j +public class YunkuaichongV150ProtocolBootstrap extends ProtocolBootstrap { + + public static final String PROTOCOL_NAME = "yunkuaichongV150"; + + @Override + protected String getProtocolName() { + return PROTOCOL_NAME; + } + + @Override + protected void _init() { + // do nothing + } + + @Override + protected void _destroy() { + // do nothing + } + + @Override + protected ProtocolMessageProcessor messageProcessor() { + return new YunKuaiChongV15ProtocolMessageProcessor(forwarder, protocolContext); + } + + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java new file mode 100644 index 0000000..b5126a3 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java @@ -0,0 +1,118 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import cn.hutool.core.util.HexUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.nio.charset.StandardCharsets; + +/** + * 云快充1.5.0 充电握手 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x15) +public class YunKuaiChongV150BmsHandshakeULCmd extends YunKuaiChongUplinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.debug("{} 云快充1.5.0充电握手", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + // 1.交易流水号 + byte[] tradeNoBytes = new byte[16]; + byteBuf.readBytes(tradeNoBytes); + String tradeNo = BCDUtil.toString(tradeNoBytes); + additionalInfo.put("交易流水号", tradeNo); + + // 2.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + additionalInfo.put("桩编号", tradeNo); + + // 3.抢号 + byte gunCodeByte = byteBuf.readByte(); + String gunCode = BCDUtil.toString(gunCodeByte); + additionalInfo.put("抢号", tradeNo); + + // 4.BMS 通信协议版本号 + byte[] bmsConnectVersionBytes = new byte[3]; + byteBuf.readBytes(bmsConnectVersionBytes); + additionalInfo.put("BMS 通信协议版本号", HexUtil.encodeHexStr(bmsConnectVersionBytes)); + + // 5.BMS 电池类型 + int bmsBatteryType = byteBuf.readUnsignedByte(); + additionalInfo.put("BMS电池类型", bmsBatteryType); + + // 6.BMS 整车动力蓄电池系统额定容量 + int bmsPowerCapacity = byteBuf.readUnsignedShortLE(); + additionalInfo.put("BMS整车动力蓄电池系统额定容量", bmsPowerCapacity); + + // 7.BMS 整车动力蓄电池系统额定总电压 + int bmsPowerMaxVoltage = byteBuf.readUnsignedShortLE(); + additionalInfo.put("BMS整车动力蓄电池系统额定总电压", bmsPowerMaxVoltage); + + // 8.BMS 电池生产厂商名称 + byte[] bmsFactoryBytes = new byte[4]; + byteBuf.readBytes(bmsFactoryBytes); + String bmsFactory = new String(bmsFactoryBytes, StandardCharsets.US_ASCII); + additionalInfo.put("BMS电池生产厂商名称", bmsFactory); + + // 9.BMS 电池组序号 + int bmsSerialNo = byteBuf.readIntLE(); + additionalInfo.put("BMS 电池组序号", bmsSerialNo); + + // 10.BMS 电池组生产日期年 + int bmsCreateYear = byteBuf.readUnsignedByte(); + additionalInfo.put("BMS 电池组生产日期年", bmsCreateYear); + + // 11.BMS 电池组生产日期月 + int bmsCreateMonth = byteBuf.readUnsignedByte(); + additionalInfo.put("BMS 电池组生产日期月", bmsCreateMonth); + + // 12.BMS 电池组生产日期日 + int bmsCreateDay = byteBuf.readUnsignedByte(); + additionalInfo.put("BMS 电池组生产日期日", bmsCreateDay); + + // 13.BMS 电池组充电次数 + int bmsChargeCount = byteBuf.readUnsignedMedium(); + additionalInfo.put("BMS 电池组充电次数", bmsChargeCount); + + // 14.BMS 电池组产权标识 + int bmsPropertyRightLabel = byteBuf.readUnsignedByte(); + additionalInfo.put("BMS 电池组产权标识", bmsPropertyRightLabel); + + // 15.预留位 + byteBuf.skipBytes(1); + + // 16.BMS 车辆识别码 + byte[] carVINBytes = new byte[17]; + byteBuf.readBytes(carVINBytes); + String bmsVinCode = new String(carVINBytes, StandardCharsets.US_ASCII); + additionalInfo.put("电动汽车唯一标识", bmsVinCode); + + // 17.BMS 软件版本号 + byte[] bmsSoftVersionBytes = new byte[8]; + byteBuf.readBytes(bmsSoftVersionBytes); + additionalInfo.put("BMS 软件版本号", HexUtil.encodeHexStr(bmsSoftVersionBytes)); + + // TODO 先打印日志,暂不转发 + log.debug("{} 云快充1.5.0充电握手信息解析完成:{}", tcpSession, additionalInfo); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java new file mode 100644 index 0000000..e95a2a4 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java @@ -0,0 +1,78 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.HeartBeatRequest; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.HEARTBEAT; + +/** + * 云快充1.5.0 充电桩心跳包 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x03) +public class YunKuaiChongV150HeartbeatULCmd extends YunKuaiChongUplinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.debug("{} 云快充1.5.0充电桩心跳包", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + byte gunCodeByte = byteBuf.readByte(); + int gunCode = Integer.parseInt(BCDUtil.toString(gunCodeByte)); + additionalInfo.put("枪号", gunCode); + + int gunState = byteBuf.readUnsignedByte(); + additionalInfo.put("枪状态(0正常 1故障)", gunState); + + // 刷新前置会话 + ctx.getProtocolSessionRegistryProvider().activate(tcpSession); + + // 转发到后端 + HeartBeatRequest heartBeatRequest = HeartBeatRequest.newBuilder() + .setPileCode(pileCode) + .setRemoteAddress(tcpSession.getAddress().toString()) + .setNodeId(ctx.getServiceInfoProvider().getServiceId()) + .setNodeWebapiIpPort(ctx.getServiceInfoProvider().getServiceWebapiEndpoint()) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(heartBeatRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) + .setHeartBeatRequest(heartBeatRequest) + .build(); + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + + pingAck(tcpSession, yunKuaiChongUplinkMessage, pileCodeBytes, gunCodeByte); + } + + private void pingAck(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, byte[] pileCodeBytes, byte gunCodeByte) { + ByteBuf pingAckMsgBody = Unpooled.buffer(9); + pingAckMsgBody.writeBytes(pileCodeBytes); + pingAckMsgBody.writeByte(gunCodeByte); + pingAckMsgBody.writeByte(0); + + encodeAndWriteFlush(HEARTBEAT, + pingAckMsgBody, + tcpSession); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java new file mode 100644 index 0000000..d7d0b70 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java @@ -0,0 +1,118 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.CP56Time2aUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.LoginResponse; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.listener.tcp.enums.SequenceNumberLength; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.time.Instant; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.PROTOCOL_SESSION_SCHEDULED; +import static sanbing.jcpp.protocol.domain.SessionCloseReason.MANUALLY; +import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE; +import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.LOGIN_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.SYNC_TIME; + +/** + * 云快充1.5.0登录认证应答 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x02) +public class YunKuaiChongV150LoginAckDLCmd extends YunKuaiChongDownlinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0登录认证应答", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasLoginResponse()) { + return; + } + + LoginResponse loginResponse = yunKuaiChongDwonlinkMessage.getMsg().getLoginResponse(); + + YunKuaiChongUplinkMessage requestData = JacksonUtil.fromBytes(yunKuaiChongDwonlinkMessage.getMsg().getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class); + + // 获取上行报文 + byte[] uplinkRawFrame = requestData.getRawFrame(); + // 从上行报文中取出桩编号字节数组 + byte[] pileCodeBytes = Arrays.copyOfRange(uplinkRawFrame, 6, 13); + + if (loginResponse.getSuccess()) { + + // 构造并下发登录ACK + loginAck(tcpSession, pileCodeBytes, requestData, true); + + // 构造定时对时 + registerSyncTimeTask(tcpSession, pileCodeBytes, requestData); + + } else { + + log.info("云快充V1.5登录认证失败,服务端断开连接。 pileCode:{}", loginResponse.getPileCode()); + + // 构造并下发登录ACK + loginAck(tcpSession, pileCodeBytes, requestData, false); + + // 断开连接 + tcpSession.close(MANUALLY); + } + } + + private void loginAck(TcpSession tcpSession, byte[] pileCodeBytes, YunKuaiChongUplinkMessage requestData, boolean loginSuccess) { + // 创建ACK消息体7字节桩编号+1字节登录结果 + ByteBuf loginAckMsgBody = Unpooled.buffer(8); + loginAckMsgBody.writeBytes(pileCodeBytes); + loginAckMsgBody.writeByte(loginSuccess ? SUCCESS_BYTE : FAILURE_BYTE); + + encodeAndWriteFlush(LOGIN_ACK, + requestData.getSequenceNumber(), + requestData.getEncryptionFlag(), + loginAckMsgBody, + tcpSession); + } + + private void registerSyncTimeTask(TcpSession tcpSession, byte[] pileCodeBytes, YunKuaiChongUplinkMessage requestData) { + tcpSession.addSchedule("auto-sync-time", k -> { + log.info("{} 云快充1.5.0开始注册定时对时任务", tcpSession); + return PROTOCOL_SESSION_SCHEDULED.scheduleAtFixedRate(() -> + syncTime(tcpSession, pileCodeBytes, requestData), + 0, 8, TimeUnit.HOURS); + } + ); + } + + private void syncTime(TcpSession tcpSession, byte[] pileCodeBytes, YunKuaiChongUplinkMessage requestData) { + TracerContextUtil.newTracer(); + MDCUtils.recordTracer(); + log.info("{} 云快充1.5.0开始下发对时报文", tcpSession); + ByteBuf syncTimeMsgBody = Unpooled.buffer(14); + syncTimeMsgBody.writeBytes(pileCodeBytes); + syncTimeMsgBody.writeBytes(CP56Time2aUtil.encode(Instant.now())); + + encodeAndWriteFlush(SYNC_TIME, + tcpSession.nextSeqNo(SequenceNumberLength.SHORT), + requestData.getEncryptionFlag(), + syncTimeMsgBody, + tcpSession); + } + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java new file mode 100644 index 0000000..8bb1991 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java @@ -0,0 +1,77 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.LoginRequest; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.nio.charset.StandardCharsets; + +/** + * 云快充1.5.0充电桩登录认证 + */ +@Slf4j +@YunKuaiChongCmd(0x01) +public class YunKuaiChongV150LoginULCmd extends YunKuaiChongUplinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0登录认证请求", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + int pileType = byteBuf.readUnsignedByte(); + additionalInfo.put("桩类型(0直流1交流)", pileType); + + int gunsNum = byteBuf.readUnsignedByte(); + additionalInfo.put("充电枪数量", gunsNum); + additionalInfo.put("通信协议版本", byteBuf.readUnsignedByte()); + byte[] bytes = new byte[8]; + byteBuf.readBytes(bytes); + additionalInfo.put("程序版本", new String(bytes, StandardCharsets.US_ASCII)); + additionalInfo.put("网络链接类型", byteBuf.readUnsignedByte()); + + byte[] simB = new byte[10]; + byteBuf.readBytes(simB); + String sim = BCDUtil.toString(simB); + additionalInfo.put("Sim卡", sim); + additionalInfo.put("运营商", byteBuf.readUnsignedByte()); + + tcpSession.addPileCode(pileCode); + + // 注册前置会话 + ctx.getProtocolSessionRegistryProvider().register(tcpSession); + + // 转发到后端 + LoginRequest loginRequest = LoginRequest.newBuilder() + .setPileCode(pileCode) + .setRemoteAddress(tcpSession.getAddress().toString()) + .setNodeId(ctx.getServiceInfoProvider().getServiceId()) + .setNodeWebapiIpPort(ctx.getServiceInfoProvider().getServiceWebapiEndpoint()) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(loginRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) + .setLoginRequest(loginRequest) + .build(); + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } + +} diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java new file mode 100644 index 0000000..3094e62 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java @@ -0,0 +1,93 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.FlagPriceProto; +import sanbing.jcpp.proto.gen.ProtocolProto.PeriodProto; +import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelProto; +import sanbing.jcpp.proto.gen.ProtocolProto.QueryPricingResponse; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.math.BigDecimal; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; + +import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag.*; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.QUERY_PRICING_ACK; + +/** + * 计费模型请求应答 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x0A) +public class YunKuaiChongV150QueryPricingModelAckDLCmd extends YunKuaiChongDownlinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0计费模型请求应答", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasQueryPricingResponse()) { + return; + } + + QueryPricingResponse queryPricingResponse = yunKuaiChongDwonlinkMessage.getMsg().getQueryPricingResponse(); + + YunKuaiChongUplinkMessage requestData = JacksonUtil.fromBytes(yunKuaiChongDwonlinkMessage.getMsg().getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class); + + long pricingId = queryPricingResponse.getPricingId(); + String pileCode = queryPricingResponse.getPileCode(); + PricingModelProto pricingModel = queryPricingResponse.getPricingModel(); + Map flagPriceMap = pricingModel.getFlagPriceMap(); + List periodList = pricingModel.getPeriodList(); + + // 从上行报文中取出桩编号字节数组 + byte[] pileCodeBytes = encodePileCode(pileCode); + + // 创建ACK消息体7字节桩编号+2字节计费模型编号+4x4x2字节尖峰平谷电价和服务费+1字节计损比例+48字节时段标识 + ByteBuf queryPricingAckMsgBody = Unpooled.buffer(90); + queryPricingAckMsgBody.writeBytes(pileCodeBytes); + queryPricingAckMsgBody.writeBytes(encodePricingId(pricingId)); + + // 4字节电价+4字节服务费 + BigDecimal accurate = new BigDecimal(1000); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getElec()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getServ()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(PEAK.ordinal()).getElec()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(PEAK.ordinal()).getServ()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(FLAT.ordinal()).getElec()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(FLAT.ordinal()).getServ()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(VALLEY.ordinal()).getElec()).multiply(accurate).intValue()); + queryPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(VALLEY.ordinal()).getServ()).multiply(accurate).intValue()); + + // 计损比例 + queryPricingAckMsgBody.writeByte(0); + + // 48段半小时 + byte[] bytes = new byte[48]; + LocalTime currentTime = LocalTime.MIDNIGHT; + for (int i = 0; i < 48; i++) { + bytes[i] = getFlagForCurrentTime(periodList, currentTime); + currentTime = currentTime.plusMinutes(30); // 每次时间增加30分钟 + } + queryPricingAckMsgBody.writeBytes(bytes); + + encodeAndWriteFlush(QUERY_PRICING_ACK, + queryPricingAckMsgBody, + tcpSession); + + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java new file mode 100644 index 0000000..3741366 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java @@ -0,0 +1,50 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.QueryPricingRequest; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +/** + * 云快充1.5.0充电桩计费模型请求 + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x09) +public class YunKuaiChongV150QueryPricingModelULCmd extends YunKuaiChongUplinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0充电桩计费模型请求", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 转发到后端 + QueryPricingRequest queryPricingRequest = QueryPricingRequest.newBuilder() + .setPileCode(pileCode) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(queryPricingRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) + .setQueryPricingRequest(queryPricingRequest) + .build(); + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java new file mode 100644 index 0000000..4b5db9e --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -0,0 +1,236 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.ChargingProgressProto; +import sanbing.jcpp.proto.gen.ProtocolProto.GunRunStatus; +import sanbing.jcpp.proto.gen.ProtocolProto.GunRunStatusProto; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * 云快充1.5.0上传实时监测数据 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x13) +public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe { + + // 故障说明列表 + private static final String[] faultDescriptions = { + "急停按钮动作故障", // Bit 1 + "无可用整流模块", // Bit 2 + "出风口温度过高", // Bit 3 + "交流防雷故障", // Bit 4 + "交直流模块 DC20 通信中断", // Bit 5 + "绝缘检测模块 FC08 通信中断", // Bit 6 + "电度表通信中断", // Bit 7 + "读卡器通信中断", // Bit 8 + "RC10 通信中断", // Bit 9 + "风扇调速板故障", // Bit 10 + "直流熔断器故障", // Bit 11 + "高压接触器故障", // Bit 12 + "门打开" // Bit 13 + }; + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0上传实时监测数据", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + // 从Tracer总获取当前时间 + long ts = TracerContextUtil.getCurrentTracer().getTracerTs(); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + // 1.交易流水号 + byte[] tradeNoBytes = new byte[16]; + byteBuf.readBytes(tradeNoBytes); + String tradeNo = decodeTradeNo(tradeNoBytes); + + // 2.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 3.抢号 + byte gunCodeByte = byteBuf.readByte(); + String gunCode = BCDUtil.toString(gunCodeByte); + + // 4.状态 0x00:离线 0x01:故障 0x02:空闲 0x03:充电 + int gunStatus = byteBuf.readUnsignedByte(); + + // 5.枪是否归位 0x00:否 0x01:是 0x02:未知 + int gunHoming = byteBuf.readUnsignedByte(); + additionalInfo.put("枪是否归位(0否1是)", gunHoming); + + // 6.是否插枪 0x00:否 0x01:是 + int gunInsert = byteBuf.readUnsignedByte(); + + // 7.输出电压 + BigDecimal outputVoltage = reduceMagnification(byteBuf.readUnsignedShortLE(), 10); + + // 8.输出电流 + BigDecimal outputCurrent = reduceMagnification(byteBuf.readUnsignedShortLE(), 10); + + // 9.枪线温度 + short gunLineTemperature = byteBuf.readUnsignedByte(); + additionalInfo.put("枪线温度", gunLineTemperature); + + // 10.枪线编码 + long gunLineCode = byteBuf.readLongLE(); + additionalInfo.put("枪线编码", gunLineCode); + + // 11.soc + int soc = byteBuf.readUnsignedByte(); + + // 12.电池组最高温度 + short maxBatteryTemperature = byteBuf.readUnsignedByte(); + additionalInfo.put("电池组最高温度", maxBatteryTemperature); + + // 13.累计充电时间(分钟) + int totalChargeTime = byteBuf.readUnsignedShortLE(); + + // 14.剩余时间(分钟) + int remainMin = byteBuf.readUnsignedShortLE(); + additionalInfo.put("剩余时间", remainMin); + + //15.充电度数(kWh) + BigDecimal chargeEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); + additionalInfo.put("充电度数", chargeEnergy); + + //16.计损充电度数(kWh) + BigDecimal loseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); + + // 17.已充金额 (电费+服务费)*计损充电度数 + BigDecimal chargeAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + + // 18.硬件故障 测试发现需要使用小端计算bit, 然后对照故障表查询故障码 + byte[] warnCodeBytes = new byte[2]; + byteBuf.readBytes(warnCodeBytes); + // 解析 14 个比特位 + List faults = getFaultDescriptions(parseFaults(warnCodeBytes)); + + // 抢状态 + GunRunStatus gunRunStatus = parseGUnRunStatus(gunStatus, gunInsert, tradeNo); + GunRunStatusProto.Builder gunRunStatusProtoBuilder = GunRunStatusProto.newBuilder() + .setTs(ts) + .setPileCode(pileCode) + .setGunCode(gunCode) + .setGunRunStatus(gunRunStatus) + .addAllFaultMessages(faults) + .setAdditionalInfo(additionalInfo.toString()); + + // 转发到后端 + UplinkQueueMessage gunRunStatusMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) + .setGunRunStatusProto(gunRunStatusProtoBuilder) + .build(); + + tcpSession.getForwarder().sendMessage(gunRunStatusMessage); + + if (StringUtils.isNotBlank(tradeNo)) { + + // 充电进度 + ChargingProgressProto.Builder chargingProgressProtoBuilder = ChargingProgressProto.newBuilder() + .setTs(ts) + .setPileCode(pileCode) + .setGunCode(gunCode) + .setTradeNo(tradeNo) + .setOutputVoltage(outputVoltage.floatValue()) + .setOutputCurrent(outputCurrent.floatValue()) + .setSoc(soc) + .setTotalChargingDurationMin(totalChargeTime) + .setTotalChargingEnergyKWh(loseEnergy.floatValue()) + .setTotalChargingCostCent(chargeAmount.longValue()) + .setAdditionalInfo(additionalInfo.toString()); + + UplinkQueueMessage chargingProgressMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) + .setChargingProgressProto(chargingProgressProtoBuilder) + .build(); + + tcpSession.getForwarder().sendMessage(chargingProgressMessage); + } + + } + + /** + * 解释枪运行状态 + */ + private static GunRunStatus parseGUnRunStatus(int gunStatus, int gunInsert, String tradeNo) { + GunRunStatus gunRunStatus = GunRunStatus.UNKNOWN; + if (gunStatus == 0) { + gunRunStatus = GunRunStatus.FAULT; + } else if (gunStatus == 1) { + gunRunStatus = GunRunStatus.FAULT; + } else if (gunStatus == 2) { + gunRunStatus = GunRunStatus.IDLE; + if (gunInsert == 1) { + gunRunStatus = GunRunStatus.INSERTED; + } + } else if (gunStatus == 3) { + if (StringUtils.isBlank(tradeNo)) { + gunRunStatus = GunRunStatus.INSERTED; + } else { + gunRunStatus = GunRunStatus.CHARGING; + } + } + return gunRunStatus; + } + + + public static boolean[] parseFaults(byte[] bytes) { + // 确保输入有效 + if (bytes.length != 2) { + throw new IllegalArgumentException("输入 byte 数组长度不为 2"); + } + + // 创建一个布尔数组来存储故障状态 + boolean[] faults = new boolean[14]; + + // 读取每个比特并设置到布尔数组中 + for (int i = 0; i < 14; i++) { + // 计算对应的字节和比特位置 + int byteIndex = i / 8; // 字节索引 + int bitIndex = i % 8; // 比特索引 + + // 使用位运算检查该比特位 + faults[i] = ((bytes[byteIndex] >> bitIndex) & 1) == 1; // 如果为 1 则故障 + } + + return faults; + } + + public static List getFaultDescriptions(boolean[] faults) { + List faultList = new ArrayList<>(); + + // 遍历布尔数组,筛选出有故障的说明 + for (int i = 0; i < faults.length; i++) { + if (faults[i]) { + faultList.add(faultDescriptions[i]); + } + } + + // 转换 List 为数组并返回 + return faultList; + } + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java new file mode 100644 index 0000000..27064f4 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java @@ -0,0 +1,73 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.RemoteStartChargingRequest; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.REMOTE_START_CHARGING; + +/** + * 云快充1.5.0 运营平台远程控制启机 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x34) +public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0运营平台远程控制启机", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasRemoteStartChargingRequest()) { + return; + } + + RemoteStartChargingRequest remoteStartChargingRequest = yunKuaiChongDwonlinkMessage.getMsg().getRemoteStartChargingRequest(); + String pileCode = remoteStartChargingRequest.getPileCode(); + String gunCode = remoteStartChargingRequest.getGunCode(); + String tradeNo = remoteStartChargingRequest.getTradeNo(); + int limitCent = remoteStartChargingRequest.getLimitCent(); + + byte[] cardNo = encodeCardNo(tradeNo); + + ByteBuf msgBody = Unpooled.buffer(44); + // 交易流水号 + msgBody.writeBytes(encodeTradeNo(tradeNo)); + // 桩编码 + msgBody.writeBytes(encodePileCode(pileCode)); + // 枪号 + msgBody.writeBytes(encodeGunCode(gunCode)); + // 逻辑卡号 BCD码 + msgBody.writeBytes(cardNo); + // 物理卡号 + msgBody.writeBytes(cardNo); + // 账户余额 + msgBody.writeIntLE(limitCent); + + encodeAndWriteFlush(REMOTE_START_CHARGING, + msgBody, + tcpSession); + } + + /** + * 用交易流水号做卡号 + */ + private static byte[] encodeCardNo(String tradeNo) { + tradeNo = StringUtils.right(tradeNo, 16); + tradeNo = StringUtils.leftPad(tradeNo, 16, '0'); + return BCDUtil.toBytes(tradeNo); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java new file mode 100644 index 0000000..6dca0e4 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java @@ -0,0 +1,93 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.RemoteStartChargingResponse; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +/** + * 云快充1.5.0 远程启动充电命令回复 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x33) +public class YunKuaiChongV150RemoteStartResultULCmd extends YunKuaiChongUplinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0远程启动充电命令回复", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + // 从Tracer总获取当前时间 + long ts = TracerContextUtil.getCurrentTracer().getTracerTs(); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + // 1.交易流水号 + byte[] tradeNoBytes = new byte[16]; + byteBuf.readBytes(tradeNoBytes); + String tradeNo = decodeTradeNo(tradeNoBytes); + + // 2.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 3.抢号 + byte gunCodeByte = byteBuf.readByte(); + String gunCode = BCDUtil.toString(gunCodeByte); + + // 4.命令执行结果 0x00失败 0x01成功 + boolean isSuccess = (byteBuf.readByte() == 0x01); + + // 5.失败原因 0无 1设备编码不匹配 2枪已在充电 3设备故障 4设备离线 5未插枪 + byte failReasonByte = byteBuf.readByte(); + String failReason = mapFailCode(failReasonByte); + + RemoteStartChargingResponse remoteStartChargingResponse = RemoteStartChargingResponse.newBuilder() + .setTs(ts) + .setPileCode(pileCode) + .setGunCode(gunCode) + .setTradeNo(tradeNo) + .setSuccess(isSuccess) + .setFailReason(failReason) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + + // 转发到后端 + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) + .setRemoteStartChargingResponse(remoteStartChargingResponse) + .build(); + + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } + + public static String mapFailCode(byte failCode) { + return switch (failCode) { + case 0x00 -> "无"; + case 0x01 -> "设备编号不匹配"; + case 0x02 -> "枪已在充电"; + case 0x03 -> "设备故障"; + case 0x04 -> "设备离线"; + case 0x05 -> "未插枪"; + case 0x33 -> "充电失败"; // 充电失败或其他相关信息 + case 0x34 -> "待启充"; // 示例,处理收到充电命令 + default -> "未知错误代码"; + }; + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java new file mode 100644 index 0000000..67c4793 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java @@ -0,0 +1,49 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.proto.gen.ProtocolProto.RemoteStopChargingRequest; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.REMOTE_START_CHARGING; + +/** + * 云快充1.5.0 运营平台远程停机 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x36) +public class YunKuaiChongV150RemoteStopDLCmd extends YunKuaiChongDownlinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0运营平台远程停机", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasRemoteStopChargingRequest()) { + return; + } + + RemoteStopChargingRequest remoteStopChargingRequest = yunKuaiChongDwonlinkMessage.getMsg().getRemoteStopChargingRequest(); + String pileCode = remoteStopChargingRequest.getPileCode(); + String gunCode = remoteStopChargingRequest.getGunCode(); + + ByteBuf msgBody = Unpooled.buffer(44); + // 桩编码 + msgBody.writeBytes(encodePileCode(pileCode)); + // 枪号 + msgBody.writeBytes(encodeGunCode(gunCode)); + + encodeAndWriteFlush(REMOTE_START_CHARGING, + msgBody, + tcpSession); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java new file mode 100644 index 0000000..dbda740 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java @@ -0,0 +1,82 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.RemoteStopChargingResponse; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +/** + * 云快充1.5.0 远程停机命令回复 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x35) +public class YunKuaiChongV150RemoteStopResultULCmd extends YunKuaiChongUplinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0远程停机命令回复", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + // 从Tracer总获取当前时间 + long ts = TracerContextUtil.getCurrentTracer().getTracerTs(); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + // 1.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 2.抢号 + byte gunCodeByte = byteBuf.readByte(); + String gunCode = BCDUtil.toString(gunCodeByte); + + // 3.命令执行结果 0x00失败 0x01成功 + boolean isSuccess = (byteBuf.readByte() == 0x01); + + // 4.失败原因 0无 1设备编码不匹配 2枪已在充电 3设备故障 4设备离线 5未插枪 + byte failReasonByte = byteBuf.readByte(); + String failReason = mapFailCode(failReasonByte); + + RemoteStopChargingResponse remoteStopChargingResponse = RemoteStopChargingResponse.newBuilder() + .setTs(ts) + .setPileCode(pileCode) + .setGunCode(gunCode) + .setSuccess(isSuccess) + .setFailReason(failReason) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + + // 转发到后端 + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) + .setRemoteStopChargingResponse(remoteStopChargingResponse) + .build(); + + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } + + public static String mapFailCode(byte failCode) { + return switch (failCode) { + case 0x00 -> "无"; + case 0x01 -> "设备编号不匹配"; + case 0x02 -> "枪未处于充电状态"; + case 0x03 -> "其他"; + default -> "未知错误"; // 可以根据需求自定义 + }; + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java new file mode 100644 index 0000000..8e04b1b --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java @@ -0,0 +1,60 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.SetPricingResponse; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.QUERY_PRICING_ACK; + +/** + * 云快充1.5.0 计费模型应答 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x57) +public class YunKuaiChongV150SetPricingModelAckULCmd extends YunKuaiChongUplinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0计费模型应答", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + // 1.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 2.设置结果 0x00:失败 0x01:成功 + boolean isSuccess = (byteBuf.readByte() == 0x01); + + // 从缓存取上个请求的pricingId + Object pricingId = tcpSession.getRequestCache().asMap().getOrDefault(pileCode + QUERY_PRICING_ACK.getCmd(), null); + + if (pricingId instanceof Long pricingIdL) { + // 转发到后端 + SetPricingResponse setPricingResponse = SetPricingResponse.newBuilder() + .setPileCode(pileCode) + .setSuccess(isSuccess) + .setPricingId(pricingIdL) + .build(); + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(setPricingResponse.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) + .setSetPricingResponse(setPricingResponse) + .build(); + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } + + + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java new file mode 100644 index 0000000..bd94432 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java @@ -0,0 +1,91 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.proto.gen.ProtocolProto.FlagPriceProto; +import sanbing.jcpp.proto.gen.ProtocolProto.PeriodProto; +import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelProto; +import sanbing.jcpp.proto.gen.ProtocolProto.SetPricingRequest; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.math.BigDecimal; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; + +import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag.*; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.QUERY_PRICING_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.SET_PRICING; + +/** + * 云快充1.5.0 计费模型设置 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x58) +public class YunKuaiChongV150SetPricingModelDLCmd extends YunKuaiChongDownlinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0计费模型设置", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasSetPricingRequest()) { + return; + } + + SetPricingRequest setPricingRequest = yunKuaiChongDwonlinkMessage.getMsg().getSetPricingRequest(); + + long pricingId = setPricingRequest.getPricingId(); + String pileCode = setPricingRequest.getPileCode(); + PricingModelProto pricingModel = setPricingRequest.getPricingModel(); + Map flagPriceMap = pricingModel.getFlagPriceMap(); + List periodList = pricingModel.getPeriodList(); + + // 反转取出桩编号字节数组 + byte[] pileCodeBytes = encodePileCode(pileCode); + + // 创建ACK消息体7字节桩编号+2字节计费模型编号+4x4x2字节尖峰平谷电价和服务费+1字节计损比例+48字节时段标识 + ByteBuf setPricingAckMsgBody = Unpooled.buffer(90); + setPricingAckMsgBody.writeBytes(pileCodeBytes); + setPricingAckMsgBody.writeBytes(encodePricingId(pricingId)); + + // 4字节电价+4字节服务费 + BigDecimal accurate = new BigDecimal(1000); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getElec()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getServ()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(PEAK.ordinal()).getElec()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(PEAK.ordinal()).getServ()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(FLAT.ordinal()).getElec()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(FLAT.ordinal()).getServ()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(VALLEY.ordinal()).getElec()).multiply(accurate).intValue()); + setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(VALLEY.ordinal()).getServ()).multiply(accurate).intValue()); + + // 计损比例 + setPricingAckMsgBody.writeByte(0); + + // 48段半小时 + byte[] bytes = new byte[48]; + LocalTime currentTime = LocalTime.MIDNIGHT; + for (int i = 0; i < 48; i++) { + bytes[i] = getFlagForCurrentTime(periodList, currentTime); + currentTime = currentTime.plusMinutes(30); // 每次时间增加30分钟 + } + setPricingAckMsgBody.writeBytes(bytes); + + // 放进缓存后再下发 + tcpSession.getRequestCache().put(pileCode + QUERY_PRICING_ACK.getCmd(), Long.valueOf(pricingId)); + + encodeAndWriteFlush(SET_PRICING, + setPricingAckMsgBody, + tcpSession); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java new file mode 100644 index 0000000..417db72 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java @@ -0,0 +1,53 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.TransactionRecordAck; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE; +import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.VERIFY_PRICING_ACK; + +/** + * 云快充1.5.0 交易记录确认 + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x40) +public class YunKuaiChongV150TransactionRecordAckDLCmd extends YunKuaiChongDownlinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0交易记录确认", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasTransactionRecordAck()) { + return; + } + + TransactionRecordAck transactionRecordAck = yunKuaiChongDwonlinkMessage.getMsg().getTransactionRecordAck(); + + YunKuaiChongUplinkMessage requestData = JacksonUtil.fromBytes(yunKuaiChongDwonlinkMessage.getMsg().getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class); + + // 创建ACK消息体16字节交易流水号 + 1字节确认结果 + ByteBuf msgBody = Unpooled.buffer(17); + msgBody.writeBytes(encodeTradeNo(transactionRecordAck.getTradeNo())); + msgBody.writeByte(transactionRecordAck.getSuccess() ? SUCCESS_BYTE : FAILURE_BYTE); + + encodeAndWriteFlush(VERIFY_PRICING_ACK, + requestData.getSequenceNumber(), + requestData.getEncryptionFlag(), + msgBody, + tcpSession); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java new file mode 100644 index 0000000..931386b --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java @@ -0,0 +1,290 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.codec.CP56Time2aUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.TransactionRecord; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.time.Instant; + +/** + * 云快充1.5.0 交易记录 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x3B) +public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0交易记录", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + // 1.交易流水号 + byte[] tradeNoBytes = new byte[16]; + byteBuf.readBytes(tradeNoBytes); + String tradeNo = decodeTradeNo(tradeNoBytes); + + // 2.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 3.抢号 + byte gunCodeByte = byteBuf.readByte(); + String gunCode = BCDUtil.toString(gunCodeByte); + + // 4.开始时间 + byte[] startTimeBytes = new byte[7]; + byteBuf.readBytes(startTimeBytes); + Instant startTime = CP56Time2aUtil.decode(startTimeBytes); + + // 5.结束时间 + byte[] endTimeBytes = new byte[7]; + byteBuf.readBytes(endTimeBytes); + Instant endTime = CP56Time2aUtil.decode(endTimeBytes); + + // 6.尖单价 + BigDecimal topPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + additionalInfo.put("尖单价", topPrice); + // 7. 尖电量 + BigDecimal topEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + // 8.计损尖电量 + BigDecimal topLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + additionalInfo.put("计损尖电量", topLoseEnergy); + // 9.尖金额 + BigDecimal topAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + + // 10.峰单价 + BigDecimal peakPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + additionalInfo.put("峰单价", peakPrice); + // 11. 峰电量 + BigDecimal peakEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + // 12.计损峰电量 + BigDecimal peakLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + additionalInfo.put("计损峰电量", peakLoseEnergy); + // 13.峰金额 + BigDecimal peakAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + + // 14.平单价 + BigDecimal flatPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + additionalInfo.put("平单价", flatPrice); + // 15. 平电量 + BigDecimal flatEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + // 16.计损平电量 + BigDecimal flatLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + additionalInfo.put("计损平电量", flatLoseEnergy); + // 17.平金额 + BigDecimal flatAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + + // 18.谷单价 + BigDecimal valleyPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + additionalInfo.put("谷单价", valleyPrice); + // 19. 谷电量 + BigDecimal valleyEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + // 20.计损谷电量 + BigDecimal valleyLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); + additionalInfo.put("计损谷电量", valleyLoseEnergy); + // 21.谷金额 + BigDecimal valleyAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + + // 22.电表总起值 + byte[] meterStartValueBytes = new byte[5]; + byteBuf.readBytes(meterStartValueBytes); + BigDecimal startMeterValue = reduceMagnification(readLongLE5Byte(meterStartValueBytes), 10000, 4); + additionalInfo.put("电表总起值", startMeterValue); + + // 23.电表总止值 + byte[] meterEndValueBytes = new byte[5]; + byteBuf.readBytes(meterEndValueBytes); + BigDecimal endMeterValue = reduceMagnification(readLongLE5Byte(meterEndValueBytes), 10000, 4); + additionalInfo.put("电表总止值", endMeterValue); + + // 24.总电量 + BigDecimal totalEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); + // 25.计损总电量 + BigDecimal totalLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); + additionalInfo.put("计损总电量", totalLoseEnergy); + // 26 .消费金额 + BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + + // 27.电动汽车唯一标识 + byte[] carVINBytes = new byte[17]; + byteBuf.readBytes(carVINBytes); + String bmsVinCode = new String(carVINBytes, StandardCharsets.US_ASCII); + additionalInfo.put("电动汽车唯一标识", bmsVinCode); + + // 28.交易标识 0x01:app 启动0x02:卡启动 0x04:离线卡启动 0x05: vin 码启动充电 + byte tradeFlag = byteBuf.readByte(); + additionalInfo.put("交易标识", mapStartFlag(tradeFlag)); + + // 29.交易日期、时间 + byte[] tradeTimeBytes = new byte[7]; + byteBuf.readBytes(tradeTimeBytes); + Instant tradeTime = CP56Time2aUtil.decode(tradeTimeBytes); + + // 30.停止原因 + byte stopReasonByte = byteBuf.readByte(); + String stopReason = mapStopReason(stopReasonByte); + + //31 物理卡号 + byte[] cardNoBytes = new byte[8]; + byteBuf.readBytes(cardNoBytes); + String cardNo = BCDUtil.toString(cardNoBytes); + additionalInfo.put("物理卡号", cardNo); + + TransactionRecord transactionRecord = TransactionRecord.newBuilder() + .setPileCode(pileCode) + .setGunCode(gunCode) + .setTradeNo(tradeNo) + .setStartTs(startTime.toEpochMilli()) + .setEndTs(endTime.toEpochMilli()) + .setTopEnergyKWh(topEnergy.floatValue()) + .setTopAmountCent(topAmount.longValue()) + .setPeakEnergyKWh(peakEnergy.floatValue()) + .setPeakAmountCent(peakAmount.longValue()) + .setFlatEnergyKWh(flatEnergy.floatValue()) + .setFlatAmountCent(flatAmount.longValue()) + .setValleyEnergyKWh(valleyEnergy.floatValue()) + .setValleyAmountCent(valleyAmount.longValue()) + .setTotalEnergyKWh(totalEnergy.floatValue()) + .setTotalAmountCent(totalAmount.longValue()) + .setTradeTs(tradeTime.toEpochMilli()) + .setStopReason(stopReason) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + + // 转发到后端 + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) + .setTransactionRecord(transactionRecord) + .build(); + + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } + + public static long readLongLE5Byte(byte[] bytes) { + // 确保字节数组的长度至少为 5 + if (bytes.length < 5) { + throw new IllegalArgumentException("Byte array must contain at least 5 bytes."); + } + + // 使用小端字节序读取 5 字节数字 + int byte1 = bytes[0] & 0xFF; + int byte2 = bytes[1] & 0xFF; + int byte3 = bytes[2] & 0xFF; + int byte4 = bytes[3] & 0xFF; + int byte5 = bytes[4] & 0xFF; + + // 将读取的字节合并成一个 long 值 + return ((long) byte1) | + ((long) byte2 << 8) | + ((long) byte3 << 16) | + ((long) byte4 << 24) | + ((long) byte5 << 32); + } + + public static String mapStartFlag(byte startFlag) { + return switch (startFlag) { + case 0x01 -> "app 启动"; + case 0x02 -> "卡启动"; + case 0x04 -> "离线卡启动"; + case 0x05 -> "vin 码启动充电"; + default -> "未知启动方式"; + }; + } + + public static String mapStopReason(byte stopReasonCode) { + return switch (stopReasonCode) { + case (byte) 0x40 -> "结束充电,APP 远程停止"; + case (byte) 0x41 -> "结束充电,SOC 达到 100%"; + case (byte) 0x42 -> "结束充电,充电电量满足设定条件"; + case (byte) 0x43 -> "结束充电,充电金额满足设定条件"; + case (byte) 0x44 -> "结束充电,充电时间满足设定条件"; + case (byte) 0x45 -> "结束充电,手动停止充电"; + case (byte) 0x46, (byte) 0x47, (byte) 0x48, (byte) 0x49 -> "结束充电,其他方式(预留)"; + case (byte) 0x4A -> "充电启动失败,充电桩控制系统故障(需要重启或自动恢复)"; + case (byte) 0x4B -> "充电启动失败,控制导引断开"; + case (byte) 0x4C -> "充电启动失败,断路器跳位"; + case (byte) 0x4D -> "充电启动失败,电表通信中断"; + case (byte) 0x4E -> "充电启动失败,余额不足"; + case (byte) 0x4F -> "充电启动失败,充电模块故障"; + case (byte) 0x50 -> "充电启动失败,急停开入"; + case (byte) 0x51 -> "充电启动失败,防雷器异常"; + case (byte) 0x52 -> "充电启动失败,BMS 未就绪"; + case (byte) 0x53 -> "充电启动失败,温度异常"; + case (byte) 0x54 -> "充电启动失败,电池反接故障"; + case (byte) 0x55 -> "充电启动失败,电子锁异常"; + case (byte) 0x56 -> "充电启动失败,合闸失败"; + case (byte) 0x57 -> "充电启动失败,绝缘异常"; + case (byte) 0x58 -> "充电启动失败,预留"; + case (byte) 0x59 -> "充电启动失败,接收 BMS 握手报文 BHM 超时"; + case (byte) 0x5A -> "充电启动失败,接收 BMS 和车辆的辨识报文超时 BRM"; + case (byte) 0x5B -> "充电启动失败,接收电池充电参数报文超时 BCP"; + case (byte) 0x5C -> "充电启动失败,接收 BMS 完成充电准备报文超时 BRO AA"; + case (byte) 0x5D -> "充电启动失败,接收电池充电总状态报文超时 BCS"; + case (byte) 0x5E -> "充电启动失败,接收电池充电要求报文超时 BCL"; + case (byte) 0x5F -> "充电启动失败,接收电池状态信息报文超时 BSM"; + case (byte) 0x60 -> "充电启动失败,GB2015 电池在 BHM 阶段有电压不允许充电"; + case (byte) 0x61 -> "充电启动失败,GB2015 辨识阶段在 BRO_AA 时候电池实际电压与 BCP 报文电池电压差距大于 5%"; + case (byte) 0x62 -> "充电启动失败,B2015 充电机在预充电阶段从 BRO_AA 变成BRO_00 状态"; + case (byte) 0x63 -> "充电启动失败,接收主机配置报文超时"; + case (byte) 0x64 -> "充电启动失败,充电机未准备就绪,我们没有回 CRO AA,对应老国标"; + case (byte) 0x65, (byte) 0x66, (byte) 0x67, (byte) 0x68, (byte) 0x69 -> "充电启动失败,其他原因(预留)"; + case (byte) 0x6A -> "充电异常中止,系统闭锁"; + case (byte) 0x6B -> "充电异常中止,导引断开"; + case (byte) 0x6C -> "充电异常中止,断路器跳位"; + case (byte) 0x6D -> "充电异常中止,电表通信中断"; + case (byte) 0x6E -> "充电异常中止,余额不足"; + case (byte) 0x6F -> "充电异常中止,交流保护动作"; + case (byte) 0x70 -> "充电异常中止,直流保护动作"; + case (byte) 0x71 -> "充电异常中止,充电模块故障"; + case (byte) 0x72 -> "充电异常中止,急停开入"; + case (byte) 0x73 -> "充电异常中止,防雷器异常"; + case (byte) 0x74 -> "充电异常中止,温度异常"; + case (byte) 0x75 -> "充电异常中止,输出异常"; + case (byte) 0x76 -> "充电异常中止,充电无流"; + case (byte) 0x77 -> "充电异常中止,电子锁异常"; + case (byte) 0x78 -> "充电异常中止,预留"; + case (byte) 0x79 -> "充电异常中止,总充电电压异常"; + case (byte) 0x7A -> "充电异常中止,总充电电流异常"; + case (byte) 0x7B -> "充电异常中止,单体充电电压异常"; + case (byte) 0x7C -> "充电异常中止,电池组过温"; + case (byte) 0x7D -> "充电异常中止,最高单体充电电压异常"; + case (byte) 0x7E -> "充电异常中止,最高电池组过温"; + case (byte) 0x7F -> "充电异常中止,BMV 单体充电电压异常"; + case (byte) 0x80 -> "充电异常中止,BMT 电池组过温"; + case (byte) 0x81 -> "充电异常中止,电池状态异常停止充电"; + case (byte) 0x82 -> "充电异常中止,车辆发报文禁止充电"; + case (byte) 0x83 -> "充电异常中止,充电桩断电"; + case (byte) 0x84 -> "充电异常中止,接收电池充电总状态报文超时"; + case (byte) 0x85 -> "充电异常中止,接收电池充电要求报文超时"; + case (byte) 0x86 -> "充电异常中止,接收电池状态信息报文超时"; + case (byte) 0x87 -> "充电异常中止,接收 BMS 中止充电报文超时"; + case (byte) 0x88 -> "充电异常中止,接收 BMS 充电统计报文超时"; + case (byte) 0x89 -> "充电异常中止,接收对侧 CCS 报文超时"; + case (byte) 0x8A, (byte) 0x8B, (byte) 0x8C, (byte) 0x8D, (byte) 0x8E, (byte) 0x8F -> + "充电异常中止,其他原因(预留)"; + case (byte) 0x90 -> "未知原因停止"; + default -> "无效的错误码"; + }; + } + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java new file mode 100644 index 0000000..fbe0a34 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java @@ -0,0 +1,62 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.VerifyPricingResponse; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.util.Arrays; + +import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE; +import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE; +import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.VERIFY_PRICING_ACK; + +/** + * 云快充1.5.0计费模型验证请求应答 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x06) +public class YunKuaiChongV150VerifyPricingModelAckDLCmd extends YunKuaiChongDownlinkCmdExe { + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0计费模型验证请求应答", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasVerifyPricingResponse()) { + return; + } + + VerifyPricingResponse verifyPricingResponse = yunKuaiChongDwonlinkMessage.getMsg().getVerifyPricingResponse(); + + YunKuaiChongUplinkMessage requestData = JacksonUtil.fromBytes(yunKuaiChongDwonlinkMessage.getMsg().getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class); + + // 获取上行报文 + byte[] uplinkRawFrame = requestData.getRawFrame(); + // 从上行报文中取出桩编号字节数组 + byte[] pileCodeBytes = Arrays.copyOfRange(uplinkRawFrame, 6, 13); + + // 创建ACK消息体7字节桩编号+2字节计费模型编号+1字节验证结果 + ByteBuf verifyPricingAckMsgBody = Unpooled.buffer(10); + verifyPricingAckMsgBody.writeBytes(pileCodeBytes); + verifyPricingAckMsgBody.writeBytes(encodePricingId(verifyPricingResponse.getPricingId())); + verifyPricingAckMsgBody.writeByte(verifyPricingResponse.getSuccess() ? SUCCESS_BYTE : FAILURE_BYTE); + + encodeAndWriteFlush(VERIFY_PRICING_ACK, + requestData.getSequenceNumber(), + requestData.getEncryptionFlag(), + verifyPricingAckMsgBody, + tcpSession); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java new file mode 100644 index 0000000..24a6d31 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java @@ -0,0 +1,59 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.VerifyPricingRequest; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + + +/** + * 云快充1.5.0计费模型验证请求 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0x05) +public class YunKuaiChongV150VerifyPricingModelULCmd extends YunKuaiChongUplinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0计费模型验证请求", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + byte[] pricingModelIdBytes = new byte[2]; + byteBuf.readBytes(pricingModelIdBytes); + long pricingModelId = BCDUtil.bcdBytesToLong(pricingModelIdBytes); + + // 转发到后端 + VerifyPricingRequest heartBeatRequest = VerifyPricingRequest.newBuilder() + .setPileCode(pileCode) + .setPricingId(pricingModelId) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(heartBeatRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) + .setVerifyPricingRequest(heartBeatRequest) + .build(); + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java new file mode 100644 index 0000000..5dab6f1 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java @@ -0,0 +1,38 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author baigod + */ +@AllArgsConstructor +@Getter +public enum YunKuaiChongV150DownlinkCmdEnum { + + LOGIN_ACK(0x02), + + SYNC_TIME(0x56), + + HEARTBEAT(0x04), + + VERIFY_PRICING_ACK(0x06), + + QUERY_PRICING_ACK(0X0A), + + SET_PRICING(0x58), + + REMOTE_START_CHARGING(0x34), + + REMOTE_STOP_CHARGING(0x36), + + TRANSACTION_RECORD(0x40) + ; + + private int cmd; + +} \ No newline at end of file diff --git a/jcpp-testing/pom.xml b/jcpp-testing/pom.xml new file mode 100644 index 0000000..9039b02 --- /dev/null +++ b/jcpp-testing/pom.xml @@ -0,0 +1,100 @@ + + + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + jcpp-testing + jar + JChargePointProtocol Testing Module + 聚合测试模块 + + + ${basedir}/.. + + + + + sanbing + jcpp-app + + + sanbing + jcpp-infrastructure-cache + + + sanbing + jcpp-infrastructure-proto + + + sanbing + jcpp-infrastructure-queue + + + sanbing + jcpp-infrastructure-stats + + + sanbing + jcpp-infrastructure-util + + + sanbing + jcpp-protocol-api + + + sanbing + jcpp-protocol-yunkuaichong + + + + + + org.jacoco + jacoco-maven-plugin + + + report-aggregate + verify + + report-aggregate + + + ${basedir}/../target + + + + merge-results + verify + + merge + + + + + ${basedir}/../ + + **/target/jacoco.exec + + + + ${basedir}/../target/aggregate.exec + + + + + + + diff --git a/license-header-template.txt b/license-header-template.txt new file mode 100644 index 0000000..2047e82 --- /dev/null +++ b/license-header-template.txt @@ -0,0 +1,2 @@ +抖音关注:${owner} +知识星球:https://t.zsxq.com/j9b21 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bd91b22 --- /dev/null +++ b/pom.xml @@ -0,0 +1,525 @@ + + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.3 + + + sanbing + jcpp-parent + 1.0.0-SNAPSHOT + pom + JChargePointProtocol + JChargePointProtocol + https://t.zsxq.com/j9b21 + + + 程序员三丙 + github + https://github.com/sanbing-java + + + + + 21 + UTF-8 + ${basedir} + 33.3.0-jre + 0.8.12 + + 1.7.0 + 3.4.4 + 3.21.9 + 4.0.2 + 1.56.1 + 0.5.1 + 3.13.0 + 5.8.32 + 3.5.7 + 5.7.0 + 6.6.2 + 3.9.2 + 3.8.16.Final + + + + + + dev + + true + + + true + true + + + + + unit-test + + false + true + + + + + integration-test + + true + false + + + + + test-all + + false + false + + + + + + jcpp-app-bootstrap + jcpp-protocol-bootstrap + jcpp-app + jcpp-infrastructure-queue + jcpp-infrastructure-cache + jcpp-infrastructure-util + jcpp-infrastructure-stats + jcpp-infrastructure-proto + jcpp-protocol-api + jcpp-testing + jcpp-protocol-yunkuaichong + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + + + + sanbing + jcpp-app + ${project.version} + + + sanbing + jcpp-protocol-yunkuaichong + ${project.version} + + + sanbing + jcpp-infrastructure-cache + ${project.version} + + + sanbing + jcpp-infrastructure-proto + ${project.version} + + + sanbing + jcpp-infrastructure-queue + ${project.version} + + + sanbing + jcpp-infrastructure-stats + ${project.version} + + + sanbing + jcpp-infrastructure-util + ${project.version} + + + sanbing + jcpp-protocol-api + ${project.version} + + + org.jboss.xnio + xnio-api + ${xnio-api.version} + + + com.google.protobuf + protobuf-java + ${protobuf.version} + + + com.google.protobuf + protobuf-java-util + ${protobuf.version} + + + org.glassfish + jakarta.el + ${jakarta.el.version} + + + com.lmax + disruptor + ${disruptor.version} + + + cn.hutool + hutool-core + ${hutool-all.version} + + + com.github.oshi + oshi-core + ${oshi-core.version} + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus-boot-starter.version} + + + org.apache.curator + curator-recipes + ${curator-recipes.version} + + + org.apache.zookeeper + zookeeper + ${zookeeper.version} + + + + + + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 21 + + -Xlint:deprecation + -Xlint:removal + -Xlint:unchecked + -parameters + + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + copy-conf + process-resources + + copy-resources + + + ${project.build.directory}/conf + + + src/main/resources + + * + + false + + + src/main/resources + + log4j2.xml + application.yml + + true + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + **/log4j2.xml + + + + ${project.name} + ${project.version} + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-protoc + generate-sources + + copy + + + + + com.google.protobuf + protoc + ${protobuf.version} + + ${os.detected.classifier} + exe + true + ${project.build.directory} + + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${protobuf-maven-plugin.version} + + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + + grpc-java + + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + + + + + + compile + compile-custom + test-compile + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + @{argLine} -Xms256m -Xmx1024m -Dfile.encoding=UTF-8 + -XX:+EnableDynamicAgentLoading + -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20 + --add-opens java.base/java.lang.reflect=ALL-UNNAMED + + ${skip.unit.test} + 1 + filesystem + + **/*Tests.java + **/*Test.java + + + **/Abstract*.java + **/*IntegrationTest.java + **/IT*.java + **/*IT.java + **/*ITCase.java + examples/codequality/tests/**/*.java + + true + 1 + + + + org.apache.maven.surefire + surefire-junit47 + ${maven-surefire-plugin.version} + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + integration-tests + + integration-test + verify + + + ${skip.integration.test} + + **/*IntegrationTest.java + **/IT*.java + **/*IT.java + **/*ITCase.java + + true + ${itCoverageAgent} + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + **/*DO.class + **/*DTO.class + **/*VO.class + **/*BO.class + **/*Model.class + + + + + prepare-unit-tests + + prepare-agent + + + + prepare-agent + + prepare-agent + + pre-integration-test + + itCoverageAgent + + + + + + org.apache.maven.plugins + maven-clean-plugin + + + custom-clean + clean + + clean + + + + + ${project.build.directory} + + **/* + + + + + + + + + com.mycila + license-maven-plugin + 3.0 + +
${main.dir}/license-header-template.txt
+ + 程序员三丙 + + + **/.env + **/*.env + **/.gradle/** + **/*.md + **/LICENSE + **/banner.txt + **/*.properties + src/test/resources/** + src/sh/** + **/*.log + src/main/resources/** + **/*.raw + **/*.patch + .run/** + + + JAVADOC_STYLE + DOUBLEDASHES_STYLE + JAVADOC_STYLE + SLASHSTAR_STYLE + SLASHSTAR_STYLE + SCRIPT_STYLE + JAVADOC_STYLE + SCRIPT_STYLE + SCRIPT_STYLE + SCRIPT_STYLE + +
+ + + + format + check + + + +
+
+
+
From 975723a70a45127798cc438a072ed17a6a5bb4cf Mon Sep 17 00:00:00 2001 From: 3god Date: Tue, 8 Oct 2024 09:49:39 +0800 Subject: [PATCH 003/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index b7a044a..d8a94c5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,16 @@

------------------------------ +#### 当前支持的充电桩协议 +| 协议名 | 版本号 | +|---|---| +| 云快充 | 1.5.0 | +------------------------------ +#### 代码编写指南 +TODO + +------------------------------ #### 参与贡献 1. Fork 本仓库 From b5370cee6c80b370d56e5aed3c744df2a8389e95 Mon Sep 17 00:00:00 2001 From: 3god Date: Tue, 8 Oct 2024 10:08:22 +0800 Subject: [PATCH 004/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 36 ------------------------------------ README.md | 4 ++++ 2 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 README.en.md diff --git a/README.en.md b/README.en.md deleted file mode 100644 index cee5c4c..0000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# JChargePointProtocol - -#### Description -JAVA 充电桩协议库 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index d8a94c5..f312fe7 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,10 @@ |---|---| | 云快充 | 1.5.0 | +------------------------------ +#### 充电桩协议文档 +百度网盘: https://pan.baidu.com/s/1xT8xWty1XRUHDzTZM_8aLw?pwd=jcpp + ------------------------------ #### 代码编写指南 TODO From ecda209644fb0e8fc36d7ca8cad12f78dccbb79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 8 Oct 2024 16:56:29 +0800 Subject: [PATCH 005/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E9=87=91=E9=A2=9D=E5=8D=95=E4=BD=8D=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/schema/schema-postgres.sql | 4 +- .../jcpp/app/dal/mapper/OrderMapperTest.java | 3 +- .../sanbing/jcpp/app/dal/entity/Order.java | 2 +- .../impl/DefaultPileProtocolService.java | 13 +++--- .../infrastructure/proto/ProtoConverter.java | 8 ++-- .../proto/model/PricingModel.java | 17 +++---- .../src/main/proto/protocol.proto | 44 +++++++++---------- .../adapter/DownlinkControllerTest.java | 2 +- .../YunKuaiChongV150RealTimeDataULCmd.java | 11 +++-- .../cmd/YunKuaiChongV150RemoteStartDLCmd.java | 4 +- .../YunKuaiChongV150SetPricingModelDLCmd.java | 2 +- ...unKuaiChongV150TransactionRecordULCmd.java | 38 ++++++++-------- 12 files changed, 74 insertions(+), 74 deletions(-) diff --git a/docker/schema/schema-postgres.sql b/docker/schema/schema-postgres.sql index ee2c0be..47d1ce0 100644 --- a/docker/schema/schema-postgres.sql +++ b/docker/schema/schema-postgres.sql @@ -108,9 +108,9 @@ CREATE TABLE IF NOT EXISTS jcpp_order pile_id uuid not null, gun_id uuid not null, plate_no varchar(64), - settlement_amount bigint default 0 not null, + settlement_amount numeric(16, 8) default 0 not null, settlement_details jsonb, - electricity_quantity numeric(16, 9) default 0 not null + electricity_quantity numeric(16, 8) default 0 not null ); CREATE UNIQUE INDEX IF NOT EXISTS uni_internal_order_no diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java index eb2a24f..dcc0aa8 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java @@ -4,7 +4,6 @@ */ package sanbing.jcpp.app.dal.mapper; -import cn.hutool.core.math.Money; import cn.hutool.core.util.IdUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import jakarta.annotation.Resource; @@ -53,7 +52,7 @@ public class OrderMapperTest extends AbstractTestBase { .pileId(NORMAL_PILE_ID[0]) .gunId(NORMAL_GUN_ID[0]) .plateNo("浙A88888") - .settlementAmount(new Money(100D).getCent()) + .settlementAmount(new BigDecimal(100)) .settlementDetails(JacksonUtil.newObjectNode()) .electricityQuantity(new BigDecimal("100")) .build(); diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java index e86b1b1..4e18e40 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java @@ -59,7 +59,7 @@ public class Order implements Serializable { private String plateNo; - private Long settlementAmount; + private BigDecimal settlementAmount; private JsonNode settlementDetails; diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index 6699c10..87d114c 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -22,6 +22,7 @@ import sanbing.jcpp.infrastructure.queue.Callback; import sanbing.jcpp.proto.gen.ProtocolProto.*; import sanbing.jcpp.protocol.domain.DownlinkCmdEnum; +import java.math.BigDecimal; import java.time.LocalTime; import java.util.*; @@ -150,10 +151,10 @@ public class DefaultPileProtocolService implements PileProtocolService { periods.add(createPeriod(4, LocalTime.parse("18:00"), LocalTime.parse("00:00"), VALLEY)); Map flagPriceMap = new HashMap<>(); - flagPriceMap.put(TOP, new FlagPrice(75, 45)); - flagPriceMap.put(PEAK, new FlagPrice(75, 45)); - flagPriceMap.put(FLAT, new FlagPrice(75, 45)); - flagPriceMap.put(VALLEY, new FlagPrice(75, 45)); + flagPriceMap.put(TOP, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); + flagPriceMap.put(PEAK, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); + flagPriceMap.put(FLAT, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); + flagPriceMap.put(VALLEY, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); PricingModel model = new PricingModel(); model.setId(UUID.randomUUID()); @@ -161,8 +162,8 @@ public class DefaultPileProtocolService implements PileProtocolService { model.setPileCode(pileCode); model.setType(CHARGE); model.setRule(SPLIT_TIME); - model.setStandardElec(75); - model.setStandardServ(45); + model.setStandardElec(new BigDecimal("0.75")); + model.setStandardServ(new BigDecimal("0.45")); model.setFlagPriceList(flagPriceMap); model.setPeriodsList(periods); diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java index 6d91913..def0edb 100644 --- a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java @@ -24,8 +24,8 @@ public class ProtoConverter { // 设置字段 builder.setType(PricingModelType.valueOf(pricingModel.getType().name())); builder.setRule(PricingModelRule.valueOf(pricingModel.getRule().name())); - builder.setStandardElec(pricingModel.getStandardElec()); - builder.setStandardServ(pricingModel.getStandardServ()); + builder.setStandardElec(pricingModel.getStandardElec().toPlainString()); + builder.setStandardServ(pricingModel.getStandardServ().toPlainString()); // 转换 flagPriceList for (Map.Entry entry : pricingModel.getFlagPriceList().entrySet()) { @@ -34,8 +34,8 @@ public class ProtoConverter { FlagPriceProto flagPriceProto = FlagPriceProto.newBuilder() .setFlag(PricingModelFlag.valueOf(flag.name())) // 枚举转换 - .setElec(flagPrice.getElec()) - .setServ(flagPrice.getServ()) + .setElec(flagPrice.getElec().toPlainString()) + .setServ(flagPrice.getServ().toPlainString()) .build(); builder.putFlagPrice(flag.ordinal(), flagPriceProto); // 按 ordinal 值作为 key 存入 diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java index c80631f..1a2191a 100644 --- a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java @@ -9,6 +9,7 @@ import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag; import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelRule; import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelType; +import java.math.BigDecimal; import java.time.LocalTime; import java.util.List; import java.util.Map; @@ -29,14 +30,14 @@ public class PricingModel { private PricingModelRule rule; /** - * 标准电价(单位分) + * 标准电价(单位元) */ - private int standardElec; + private BigDecimal standardElec; /** - * 标准服务费(单位分) + * 标准服务费(单位元) */ - private int standardServ; + private BigDecimal standardServ; /** * 分时电价 @@ -68,11 +69,11 @@ public class PricingModel { @NoArgsConstructor public static class FlagPrice { - // 分时电价,单位分 - private int elec; + // 分时电价,单位元 + private BigDecimal elec; - // 分时服务费,单位分 - private int serv; + // 分时服务费,单位元 + private BigDecimal serv; } } \ No newline at end of file diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index bd3ee09..5834455 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -96,8 +96,8 @@ message QueryPricingResponse { message PricingModelProto { PricingModelType type = 3; PricingModelRule rule = 4; - int32 standardElec = 5; - int32 standardServ = 6; + string standardElec = 5; + string standardServ = 6; map flagPrice = 8; repeated PeriodProto period = 9; } @@ -111,8 +111,8 @@ message PeriodProto { message FlagPriceProto { PricingModelFlag flag = 1; - int32 elec = 2; - int32 serv = 3; + string elec = 2; + string serv = 3; } enum PricingModelType { @@ -160,12 +160,12 @@ message ChargingProgressProto { string pileCode = 4; string gunCode = 5; string tradeNo = 6; - float outputVoltage = 7; - float outputCurrent = 8; - float soc = 9; + string outputVoltage = 7; + string outputCurrent = 8; + int32 soc = 9; int32 totalChargingDurationMin = 10; - float totalChargingEnergyKWh = 11; - int64 totalChargingCostCent = 12; + string totalChargingEnergyKWh = 11; + string totalChargingCostYuan = 12; optional string additionalInfo = 20; } @@ -185,7 +185,7 @@ message RemoteStartChargingRequest { string pileCode = 4; string gunCode = 5; string tradeNo = 6; - int32 limitCent = 7; + int32 limitYuan = 7; optional string additionalInfo = 20; } @@ -219,18 +219,18 @@ message TransactionRecord { string tradeNo = 6; int64 startTs = 51; int64 endTs = 52; - float topEnergyKWh = 53; - int64 topAmountCent = 54; - float peakEnergyKWh = 55; - int64 peakAmountCent = 56; - float flatEnergyKWh = 57; - int64 flatAmountCent = 58; - float valleyEnergyKWh = 59; - int64 valleyAmountCent = 60; - float deepEnergyKWh = 61; - int64 deepAmountCent = 62; - float totalEnergyKWh = 63; - int64 totalAmountCent = 64; + string topEnergyKWh = 53; + string topAmountYuan = 54; + string peakEnergyKWh = 55; + string peakAmountYuan = 56; + string flatEnergyKWh = 57; + string flatAmountYuan = 58; + string valleyEnergyKWh = 59; + string valleyAmountYuan = 60; + string deepEnergyKWh = 61; + string deepAmountYuan = 62; + string totalEnergyKWh = 63; + string totalAmountYuan = 64; int64 tradeTs = 65; string stopReason = 66; optional string additionalInfo = 20; diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java index fb5c959..016175b 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java @@ -128,7 +128,7 @@ class DownlinkControllerTest extends AbstractProtocolTestBase { .setRemoteStartChargingRequest(ProtocolProto.RemoteStartChargingRequest.newBuilder() .setPileCode(pileCode) .setGunCode("01") - .setLimitCent(10000) + .setLimitYuan(100) .setTradeNo("12345678901234567890") .build()) .build(); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java index 4b5db9e..0512ae6 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -122,7 +122,7 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe BigDecimal loseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); // 17.已充金额 (电费+服务费)*计损充电度数 - BigDecimal chargeAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal chargeAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 18.硬件故障 测试发现需要使用小端计算bit, 然后对照故障表查询故障码 byte[] warnCodeBytes = new byte[2]; @@ -155,12 +155,12 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe .setPileCode(pileCode) .setGunCode(gunCode) .setTradeNo(tradeNo) - .setOutputVoltage(outputVoltage.floatValue()) - .setOutputCurrent(outputCurrent.floatValue()) + .setOutputVoltage(outputVoltage.toPlainString()) + .setOutputCurrent(outputCurrent.toPlainString()) .setSoc(soc) .setTotalChargingDurationMin(totalChargeTime) - .setTotalChargingEnergyKWh(loseEnergy.floatValue()) - .setTotalChargingCostCent(chargeAmount.longValue()) + .setTotalChargingEnergyKWh(loseEnergy.toPlainString()) + .setTotalChargingCostYuan(chargeAmount.toPlainString()) .setAdditionalInfo(additionalInfo.toString()); UplinkQueueMessage chargingProgressMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) @@ -169,7 +169,6 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe tcpSession.getForwarder().sendMessage(chargingProgressMessage); } - } /** diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java index 27064f4..9846f10 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java @@ -39,7 +39,7 @@ public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe String pileCode = remoteStartChargingRequest.getPileCode(); String gunCode = remoteStartChargingRequest.getGunCode(); String tradeNo = remoteStartChargingRequest.getTradeNo(); - int limitCent = remoteStartChargingRequest.getLimitCent(); + int limitYuan = remoteStartChargingRequest.getLimitYuan(); byte[] cardNo = encodeCardNo(tradeNo); @@ -55,7 +55,7 @@ public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe // 物理卡号 msgBody.writeBytes(cardNo); // 账户余额 - msgBody.writeIntLE(limitCent); + msgBody.writeIntLE(limitYuan); encodeAndWriteFlush(REMOTE_START_CHARGING, msgBody, diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java index bd94432..44b066e 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java @@ -59,7 +59,7 @@ public class YunKuaiChongV150SetPricingModelDLCmd extends YunKuaiChongDownlinkCm setPricingAckMsgBody.writeBytes(encodePricingId(pricingId)); // 4字节电价+4字节服务费 - BigDecimal accurate = new BigDecimal(1000); + BigDecimal accurate = new BigDecimal(100000); setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getElec()).multiply(accurate).intValue()); setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getServ()).multiply(accurate).intValue()); setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(PEAK.ordinal()).getElec()).multiply(accurate).intValue()); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java index 931386b..8fbdf8b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java @@ -63,7 +63,7 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm Instant endTime = CP56Time2aUtil.decode(endTimeBytes); // 6.尖单价 - BigDecimal topPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal topPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("尖单价", topPrice); // 7. 尖电量 BigDecimal topEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -71,10 +71,10 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal topLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损尖电量", topLoseEnergy); // 9.尖金额 - BigDecimal topAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal topAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 10.峰单价 - BigDecimal peakPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal peakPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("峰单价", peakPrice); // 11. 峰电量 BigDecimal peakEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -82,10 +82,10 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal peakLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损峰电量", peakLoseEnergy); // 13.峰金额 - BigDecimal peakAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal peakAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 14.平单价 - BigDecimal flatPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal flatPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("平单价", flatPrice); // 15. 平电量 BigDecimal flatEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -93,10 +93,10 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal flatLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损平电量", flatLoseEnergy); // 17.平金额 - BigDecimal flatAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal flatAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 18.谷单价 - BigDecimal valleyPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal valleyPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("谷单价", valleyPrice); // 19. 谷电量 BigDecimal valleyEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -104,7 +104,7 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal valleyLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损谷电量", valleyLoseEnergy); // 21.谷金额 - BigDecimal valleyAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal valleyAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 22.电表总起值 byte[] meterStartValueBytes = new byte[5]; @@ -124,7 +124,7 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal totalLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); additionalInfo.put("计损总电量", totalLoseEnergy); // 26 .消费金额 - BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 27.电动汽车唯一标识 byte[] carVINBytes = new byte[17]; @@ -157,16 +157,16 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm .setTradeNo(tradeNo) .setStartTs(startTime.toEpochMilli()) .setEndTs(endTime.toEpochMilli()) - .setTopEnergyKWh(topEnergy.floatValue()) - .setTopAmountCent(topAmount.longValue()) - .setPeakEnergyKWh(peakEnergy.floatValue()) - .setPeakAmountCent(peakAmount.longValue()) - .setFlatEnergyKWh(flatEnergy.floatValue()) - .setFlatAmountCent(flatAmount.longValue()) - .setValleyEnergyKWh(valleyEnergy.floatValue()) - .setValleyAmountCent(valleyAmount.longValue()) - .setTotalEnergyKWh(totalEnergy.floatValue()) - .setTotalAmountCent(totalAmount.longValue()) + .setTopEnergyKWh(topEnergy.toPlainString()) + .setTopAmountYuan(topAmount.toPlainString()) + .setPeakEnergyKWh(peakEnergy.toPlainString()) + .setPeakAmountYuan(peakAmount.toPlainString()) + .setFlatEnergyKWh(flatEnergy.toPlainString()) + .setFlatAmountYuan(flatAmount.toPlainString()) + .setValleyEnergyKWh(valleyEnergy.toPlainString()) + .setValleyAmountYuan(valleyAmount.toPlainString()) + .setTotalEnergyKWh(totalEnergy.toPlainString()) + .setTotalAmountYuan(totalAmount.toPlainString()) .setTradeTs(tradeTime.toEpochMilli()) .setStopReason(stopReason) .setAdditionalInfo(additionalInfo.toString()) From 791039fabd098ef26dfc6b151831d8992807e1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 8 Oct 2024 16:56:29 +0800 Subject: [PATCH 006/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E9=87=91=E9=A2=9D=E5=8D=95=E4=BD=8D=E4=BF=AE=E6=AD=A3=E4=B8=BA?= =?UTF-8?q?=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/schema/schema-postgres.sql | 4 +- .../jcpp/app/dal/mapper/OrderMapperTest.java | 3 +- .../sanbing/jcpp/app/dal/entity/Order.java | 2 +- .../impl/DefaultPileProtocolService.java | 13 +++--- .../infrastructure/proto/ProtoConverter.java | 8 ++-- .../proto/model/PricingModel.java | 17 +++---- .../src/main/proto/protocol.proto | 44 +++++++++---------- .../protocol/adapter/DownlinkController.java | 1 - .../adapter/DownlinkControllerTest.java | 2 +- .../YunKuaiChongV150RealTimeDataULCmd.java | 11 +++-- .../cmd/YunKuaiChongV150RemoteStartDLCmd.java | 4 +- .../YunKuaiChongV150SetPricingModelDLCmd.java | 2 +- ...unKuaiChongV150TransactionRecordULCmd.java | 38 ++++++++-------- 13 files changed, 74 insertions(+), 75 deletions(-) diff --git a/docker/schema/schema-postgres.sql b/docker/schema/schema-postgres.sql index ee2c0be..47d1ce0 100644 --- a/docker/schema/schema-postgres.sql +++ b/docker/schema/schema-postgres.sql @@ -108,9 +108,9 @@ CREATE TABLE IF NOT EXISTS jcpp_order pile_id uuid not null, gun_id uuid not null, plate_no varchar(64), - settlement_amount bigint default 0 not null, + settlement_amount numeric(16, 8) default 0 not null, settlement_details jsonb, - electricity_quantity numeric(16, 9) default 0 not null + electricity_quantity numeric(16, 8) default 0 not null ); CREATE UNIQUE INDEX IF NOT EXISTS uni_internal_order_no diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java index eb2a24f..dcc0aa8 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java @@ -4,7 +4,6 @@ */ package sanbing.jcpp.app.dal.mapper; -import cn.hutool.core.math.Money; import cn.hutool.core.util.IdUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import jakarta.annotation.Resource; @@ -53,7 +52,7 @@ public class OrderMapperTest extends AbstractTestBase { .pileId(NORMAL_PILE_ID[0]) .gunId(NORMAL_GUN_ID[0]) .plateNo("浙A88888") - .settlementAmount(new Money(100D).getCent()) + .settlementAmount(new BigDecimal(100)) .settlementDetails(JacksonUtil.newObjectNode()) .electricityQuantity(new BigDecimal("100")) .build(); diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java index e86b1b1..4e18e40 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/dal/entity/Order.java @@ -59,7 +59,7 @@ public class Order implements Serializable { private String plateNo; - private Long settlementAmount; + private BigDecimal settlementAmount; private JsonNode settlementDetails; diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index 6699c10..87d114c 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -22,6 +22,7 @@ import sanbing.jcpp.infrastructure.queue.Callback; import sanbing.jcpp.proto.gen.ProtocolProto.*; import sanbing.jcpp.protocol.domain.DownlinkCmdEnum; +import java.math.BigDecimal; import java.time.LocalTime; import java.util.*; @@ -150,10 +151,10 @@ public class DefaultPileProtocolService implements PileProtocolService { periods.add(createPeriod(4, LocalTime.parse("18:00"), LocalTime.parse("00:00"), VALLEY)); Map flagPriceMap = new HashMap<>(); - flagPriceMap.put(TOP, new FlagPrice(75, 45)); - flagPriceMap.put(PEAK, new FlagPrice(75, 45)); - flagPriceMap.put(FLAT, new FlagPrice(75, 45)); - flagPriceMap.put(VALLEY, new FlagPrice(75, 45)); + flagPriceMap.put(TOP, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); + flagPriceMap.put(PEAK, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); + flagPriceMap.put(FLAT, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); + flagPriceMap.put(VALLEY, new FlagPrice(new BigDecimal("0.75"), new BigDecimal("0.45"))); PricingModel model = new PricingModel(); model.setId(UUID.randomUUID()); @@ -161,8 +162,8 @@ public class DefaultPileProtocolService implements PileProtocolService { model.setPileCode(pileCode); model.setType(CHARGE); model.setRule(SPLIT_TIME); - model.setStandardElec(75); - model.setStandardServ(45); + model.setStandardElec(new BigDecimal("0.75")); + model.setStandardServ(new BigDecimal("0.45")); model.setFlagPriceList(flagPriceMap); model.setPeriodsList(periods); diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java index 6d91913..def0edb 100644 --- a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java @@ -24,8 +24,8 @@ public class ProtoConverter { // 设置字段 builder.setType(PricingModelType.valueOf(pricingModel.getType().name())); builder.setRule(PricingModelRule.valueOf(pricingModel.getRule().name())); - builder.setStandardElec(pricingModel.getStandardElec()); - builder.setStandardServ(pricingModel.getStandardServ()); + builder.setStandardElec(pricingModel.getStandardElec().toPlainString()); + builder.setStandardServ(pricingModel.getStandardServ().toPlainString()); // 转换 flagPriceList for (Map.Entry entry : pricingModel.getFlagPriceList().entrySet()) { @@ -34,8 +34,8 @@ public class ProtoConverter { FlagPriceProto flagPriceProto = FlagPriceProto.newBuilder() .setFlag(PricingModelFlag.valueOf(flag.name())) // 枚举转换 - .setElec(flagPrice.getElec()) - .setServ(flagPrice.getServ()) + .setElec(flagPrice.getElec().toPlainString()) + .setServ(flagPrice.getServ().toPlainString()) .build(); builder.putFlagPrice(flag.ordinal(), flagPriceProto); // 按 ordinal 值作为 key 存入 diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java index c80631f..1a2191a 100644 --- a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/model/PricingModel.java @@ -9,6 +9,7 @@ import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag; import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelRule; import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelType; +import java.math.BigDecimal; import java.time.LocalTime; import java.util.List; import java.util.Map; @@ -29,14 +30,14 @@ public class PricingModel { private PricingModelRule rule; /** - * 标准电价(单位分) + * 标准电价(单位元) */ - private int standardElec; + private BigDecimal standardElec; /** - * 标准服务费(单位分) + * 标准服务费(单位元) */ - private int standardServ; + private BigDecimal standardServ; /** * 分时电价 @@ -68,11 +69,11 @@ public class PricingModel { @NoArgsConstructor public static class FlagPrice { - // 分时电价,单位分 - private int elec; + // 分时电价,单位元 + private BigDecimal elec; - // 分时服务费,单位分 - private int serv; + // 分时服务费,单位元 + private BigDecimal serv; } } \ No newline at end of file diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index bd3ee09..5834455 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -96,8 +96,8 @@ message QueryPricingResponse { message PricingModelProto { PricingModelType type = 3; PricingModelRule rule = 4; - int32 standardElec = 5; - int32 standardServ = 6; + string standardElec = 5; + string standardServ = 6; map flagPrice = 8; repeated PeriodProto period = 9; } @@ -111,8 +111,8 @@ message PeriodProto { message FlagPriceProto { PricingModelFlag flag = 1; - int32 elec = 2; - int32 serv = 3; + string elec = 2; + string serv = 3; } enum PricingModelType { @@ -160,12 +160,12 @@ message ChargingProgressProto { string pileCode = 4; string gunCode = 5; string tradeNo = 6; - float outputVoltage = 7; - float outputCurrent = 8; - float soc = 9; + string outputVoltage = 7; + string outputCurrent = 8; + int32 soc = 9; int32 totalChargingDurationMin = 10; - float totalChargingEnergyKWh = 11; - int64 totalChargingCostCent = 12; + string totalChargingEnergyKWh = 11; + string totalChargingCostYuan = 12; optional string additionalInfo = 20; } @@ -185,7 +185,7 @@ message RemoteStartChargingRequest { string pileCode = 4; string gunCode = 5; string tradeNo = 6; - int32 limitCent = 7; + int32 limitYuan = 7; optional string additionalInfo = 20; } @@ -219,18 +219,18 @@ message TransactionRecord { string tradeNo = 6; int64 startTs = 51; int64 endTs = 52; - float topEnergyKWh = 53; - int64 topAmountCent = 54; - float peakEnergyKWh = 55; - int64 peakAmountCent = 56; - float flatEnergyKWh = 57; - int64 flatAmountCent = 58; - float valleyEnergyKWh = 59; - int64 valleyAmountCent = 60; - float deepEnergyKWh = 61; - int64 deepAmountCent = 62; - float totalEnergyKWh = 63; - int64 totalAmountCent = 64; + string topEnergyKWh = 53; + string topAmountYuan = 54; + string peakEnergyKWh = 55; + string peakAmountYuan = 56; + string flatEnergyKWh = 57; + string flatAmountYuan = 58; + string valleyEnergyKWh = 59; + string valleyAmountYuan = 60; + string deepEnergyKWh = 61; + string deepAmountYuan = 62; + string totalEnergyKWh = 63; + string totalAmountYuan = 64; int64 tradeTs = 65; string stopReason = 66; optional string additionalInfo = 20; diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java index 94b6906..8fe97a3 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java @@ -34,7 +34,6 @@ public class DownlinkController { @Value("${api.timeout.onDownlink:3000}") public long onDownlinkTimeout; - @Resource ProtocolSessionRegistryProvider protocolSessionRegistryProvider; diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java index fb5c959..016175b 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java @@ -128,7 +128,7 @@ class DownlinkControllerTest extends AbstractProtocolTestBase { .setRemoteStartChargingRequest(ProtocolProto.RemoteStartChargingRequest.newBuilder() .setPileCode(pileCode) .setGunCode("01") - .setLimitCent(10000) + .setLimitYuan(100) .setTradeNo("12345678901234567890") .build()) .build(); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java index 4b5db9e..0512ae6 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -122,7 +122,7 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe BigDecimal loseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); // 17.已充金额 (电费+服务费)*计损充电度数 - BigDecimal chargeAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal chargeAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 18.硬件故障 测试发现需要使用小端计算bit, 然后对照故障表查询故障码 byte[] warnCodeBytes = new byte[2]; @@ -155,12 +155,12 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe .setPileCode(pileCode) .setGunCode(gunCode) .setTradeNo(tradeNo) - .setOutputVoltage(outputVoltage.floatValue()) - .setOutputCurrent(outputCurrent.floatValue()) + .setOutputVoltage(outputVoltage.toPlainString()) + .setOutputCurrent(outputCurrent.toPlainString()) .setSoc(soc) .setTotalChargingDurationMin(totalChargeTime) - .setTotalChargingEnergyKWh(loseEnergy.floatValue()) - .setTotalChargingCostCent(chargeAmount.longValue()) + .setTotalChargingEnergyKWh(loseEnergy.toPlainString()) + .setTotalChargingCostYuan(chargeAmount.toPlainString()) .setAdditionalInfo(additionalInfo.toString()); UplinkQueueMessage chargingProgressMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) @@ -169,7 +169,6 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe tcpSession.getForwarder().sendMessage(chargingProgressMessage); } - } /** diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java index 27064f4..9846f10 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java @@ -39,7 +39,7 @@ public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe String pileCode = remoteStartChargingRequest.getPileCode(); String gunCode = remoteStartChargingRequest.getGunCode(); String tradeNo = remoteStartChargingRequest.getTradeNo(); - int limitCent = remoteStartChargingRequest.getLimitCent(); + int limitYuan = remoteStartChargingRequest.getLimitYuan(); byte[] cardNo = encodeCardNo(tradeNo); @@ -55,7 +55,7 @@ public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe // 物理卡号 msgBody.writeBytes(cardNo); // 账户余额 - msgBody.writeIntLE(limitCent); + msgBody.writeIntLE(limitYuan); encodeAndWriteFlush(REMOTE_START_CHARGING, msgBody, diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java index bd94432..44b066e 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java @@ -59,7 +59,7 @@ public class YunKuaiChongV150SetPricingModelDLCmd extends YunKuaiChongDownlinkCm setPricingAckMsgBody.writeBytes(encodePricingId(pricingId)); // 4字节电价+4字节服务费 - BigDecimal accurate = new BigDecimal(1000); + BigDecimal accurate = new BigDecimal(100000); setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getElec()).multiply(accurate).intValue()); setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(TOP.ordinal()).getServ()).multiply(accurate).intValue()); setPricingAckMsgBody.writeIntLE(new BigDecimal(flagPriceMap.get(PEAK.ordinal()).getElec()).multiply(accurate).intValue()); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java index 931386b..8fbdf8b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java @@ -63,7 +63,7 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm Instant endTime = CP56Time2aUtil.decode(endTimeBytes); // 6.尖单价 - BigDecimal topPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal topPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("尖单价", topPrice); // 7. 尖电量 BigDecimal topEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -71,10 +71,10 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal topLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损尖电量", topLoseEnergy); // 9.尖金额 - BigDecimal topAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal topAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 10.峰单价 - BigDecimal peakPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal peakPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("峰单价", peakPrice); // 11. 峰电量 BigDecimal peakEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -82,10 +82,10 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal peakLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损峰电量", peakLoseEnergy); // 13.峰金额 - BigDecimal peakAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal peakAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 14.平单价 - BigDecimal flatPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal flatPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("平单价", flatPrice); // 15. 平电量 BigDecimal flatEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -93,10 +93,10 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal flatLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损平电量", flatLoseEnergy); // 17.平金额 - BigDecimal flatAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal flatAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 18.谷单价 - BigDecimal valleyPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 1000); + BigDecimal valleyPrice = reduceMagnification(byteBuf.readUnsignedIntLE(), 100000); additionalInfo.put("谷单价", valleyPrice); // 19. 谷电量 BigDecimal valleyEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); @@ -104,7 +104,7 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal valleyLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); additionalInfo.put("计损谷电量", valleyLoseEnergy); // 21.谷金额 - BigDecimal valleyAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal valleyAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 22.电表总起值 byte[] meterStartValueBytes = new byte[5]; @@ -124,7 +124,7 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm BigDecimal totalLoseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); additionalInfo.put("计损总电量", totalLoseEnergy); // 26 .消费金额 - BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 100); + BigDecimal totalAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); // 27.电动汽车唯一标识 byte[] carVINBytes = new byte[17]; @@ -157,16 +157,16 @@ public class YunKuaiChongV150TransactionRecordULCmd extends YunKuaiChongUplinkCm .setTradeNo(tradeNo) .setStartTs(startTime.toEpochMilli()) .setEndTs(endTime.toEpochMilli()) - .setTopEnergyKWh(topEnergy.floatValue()) - .setTopAmountCent(topAmount.longValue()) - .setPeakEnergyKWh(peakEnergy.floatValue()) - .setPeakAmountCent(peakAmount.longValue()) - .setFlatEnergyKWh(flatEnergy.floatValue()) - .setFlatAmountCent(flatAmount.longValue()) - .setValleyEnergyKWh(valleyEnergy.floatValue()) - .setValleyAmountCent(valleyAmount.longValue()) - .setTotalEnergyKWh(totalEnergy.floatValue()) - .setTotalAmountCent(totalAmount.longValue()) + .setTopEnergyKWh(topEnergy.toPlainString()) + .setTopAmountYuan(topAmount.toPlainString()) + .setPeakEnergyKWh(peakEnergy.toPlainString()) + .setPeakAmountYuan(peakAmount.toPlainString()) + .setFlatEnergyKWh(flatEnergy.toPlainString()) + .setFlatAmountYuan(flatAmount.toPlainString()) + .setValleyEnergyKWh(valleyEnergy.toPlainString()) + .setValleyAmountYuan(valleyAmount.toPlainString()) + .setTotalEnergyKWh(totalEnergy.toPlainString()) + .setTotalAmountYuan(totalAmount.toPlainString()) .setTradeTs(tradeTime.toEpochMilli()) .setStopReason(stopReason) .setAdditionalInfo(additionalInfo.toString()) From 2f5b9f454a9b43f8db75aa8176c3e9a6e467276e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 8 Oct 2024 17:19:05 +0800 Subject: [PATCH 007/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E5=AE=9E=E6=97=B6=E6=95=B0=E6=8D=AE=E4=B8=AD=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=85=85=E7=94=B5=E5=BA=A6=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java index 0512ae6..9282d8b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -159,7 +159,7 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe .setOutputCurrent(outputCurrent.toPlainString()) .setSoc(soc) .setTotalChargingDurationMin(totalChargeTime) - .setTotalChargingEnergyKWh(loseEnergy.toPlainString()) + .setTotalChargingEnergyKWh(chargeEnergy.toPlainString()) .setTotalChargingCostYuan(chargeAmount.toPlainString()) .setAdditionalInfo(additionalInfo.toString()); From b2b625cd7ac5cf64fca7f597c4b5cb71d39b5b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 8 Oct 2024 17:24:11 +0800 Subject: [PATCH 008/102] =?UTF-8?q?=E4=BA=91=E5=BF=AB=E5=85=851.5.0=20?= =?UTF-8?q?=E5=AE=9E=E6=97=B6=E6=95=B0=E6=8D=AE=E4=B8=AD=EF=BC=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=AE=A1=E6=8D=9F=E5=85=85=E7=94=B5=E5=BA=A6=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java index 9282d8b..8aa463c 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -120,6 +120,7 @@ public class YunKuaiChongV150RealTimeDataULCmd extends YunKuaiChongUplinkCmdExe //16.计损充电度数(kWh) BigDecimal loseEnergy = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000, 4); + additionalInfo.put("计损充电度数", loseEnergy); // 17.已充金额 (电费+服务费)*计损充电度数 BigDecimal chargeAmount = reduceMagnification(byteBuf.readUnsignedIntLE(), 10000); From b7a0d592c20102a13ee44f05d33f5d6410f5d1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 8 Oct 2024 19:30:47 +0800 Subject: [PATCH 009/102] =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=BA=91=E5=BF=AB?= =?UTF-8?q?=E5=85=85=E6=A8=A1=E6=8B=9F=E6=8A=A5=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-protocol-yunkuaichong/READMD.me | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 jcpp-protocol-yunkuaichong/READMD.me diff --git a/jcpp-protocol-yunkuaichong/READMD.me b/jcpp-protocol-yunkuaichong/READMD.me new file mode 100644 index 0000000..f5b765b --- /dev/null +++ b/jcpp-protocol-yunkuaichong/READMD.me @@ -0,0 +1,32 @@ +### 云快充模拟报文 +上行登录(桩编号:20231212000010)HEX:20231212000010 +6822001900012023121200001001011047562e393572313300898604d11722d0348606024E87 +登录应答: +68 08 00 19 00 02 20 23 12 12 00 00 10 00 A1 55 +登录后对时: +68 0E 01 00 00 56 20 23 12 12 00 00 10 08 52 1B 17 02 0A 18 89 87 + +上行心跳: +68 0d 25 d3 00 03 20 23 12 12 00 00 10 01 00 D1 AC +心跳应答 +68 09 25 D3 00 04 20 23 12 12 00 00 10 01 00 1D 0B + +上行计费模型验证 +68 0d 71 ad 00 05 20231212000010 0000 222B +上行计费模型验证应答 +68 08 71 AD 00 06 20 23 12 12 00 00 10 00 6C DB + +上行充电桩计费模型请求 +68 0B 00 07 00 09 20 23 12 12 00 00 10 72 ED +下行计费模型请求应答 +68 5A 00 07 00 0A 20 23 12 12 00 00 10 00 01 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 02 02 02 02 02 02 02 02 02 02 02 02 03 03 03 03 03 03 03 03 03 03 03 03 FE 63 + +上报充电桩状态 +6840bbbd0013 20231212000010 0000000010057136003201060039560002030201940ef4035400000000000000004e5510002000c057010000000000606c010000009373 + +下发启动充电 +68300100003400000000000012345678901234567890202312120000100156789012345678905678901234567890102700005fd9 + +上报交易记录 +68 a2 00 46 00 3b 20231212000010323239000000000000 20231212000010 01b03604116d0c17a08c09116d0c17f0490200000000000000000000000000d0fb0100000000000000000000000000b0ad0100000000000000000000000000905f010072060000720600007805000000000000000000000000720600007206000078050000000000000000000000000000000000000001a08c09116d0c1740000000000000000003E0 + From a1db728be53aa2370e8260bbb88d28b347a4d1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 8 Oct 2024 21:54:30 +0800 Subject: [PATCH 010/102] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=8D=95=E4=BE=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/{GunMapperTest.java => GunMapperIT.java} | 8 ++++---- .../{OrderMapperTest.java => OrderMapperIT.java} | 10 +++++----- .../{PileMapperTest.java => PileMapperIT.java} | 6 +++--- .../{StationMapperTest.java => StationMapperIT.java} | 4 ++-- .../{UserMapperTest.java => UserMapperIT.java} | 2 +- ...ationTest.java => RedisCacheConfigurationIT.java} | 2 +- .../jcpp/protocol/AbstractProtocolTestBase.java | 3 ++- ...ControllerTest.java => DownlinkControllerIT.java} | 12 ++++++------ 8 files changed, 24 insertions(+), 23 deletions(-) rename jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/{GunMapperTest.java => GunMapperIT.java} (90%) rename jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/{OrderMapperTest.java => OrderMapperIT.java} (84%) rename jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/{PileMapperTest.java => PileMapperIT.java} (92%) rename jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/{StationMapperTest.java => StationMapperIT.java} (93%) rename jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/{UserMapperTest.java => UserMapperIT.java} (96%) rename jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/{RedisCacheConfigurationTest.java => RedisCacheConfigurationIT.java} (97%) rename jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/{DownlinkControllerTest.java => DownlinkControllerIT.java} (95%) diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java similarity index 90% rename from jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java rename to jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java index 592c781..5eb5aac 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java @@ -17,14 +17,14 @@ import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import java.time.LocalDateTime; import java.util.UUID; -import static sanbing.jcpp.app.dal.mapper.PileMapperTest.NORMAL_PILE_ID; -import static sanbing.jcpp.app.dal.mapper.StationMapperTest.NORMAL_STATION_ID; -import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; +import static sanbing.jcpp.app.dal.mapper.PileMapperIT.NORMAL_PILE_ID; +import static sanbing.jcpp.app.dal.mapper.StationMapperIT.NORMAL_STATION_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperIT.NORMAL_USER_ID; /** * @author baigod */ -public class GunMapperTest extends AbstractTestBase { +class GunMapperIT extends AbstractTestBase { static final UUID[] NORMAL_GUN_ID = new UUID[]{ UUID.fromString("8f1ffb5b-e536-4f2b-8cd0-31f7d0348a44"), UUID.fromString("ae256617-b747-4110-b27a-00773e03bed1"), diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperIT.java similarity index 84% rename from jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java rename to jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperIT.java index dcc0aa8..beabff7 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/OrderMapperIT.java @@ -19,15 +19,15 @@ import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID; -import static sanbing.jcpp.app.dal.mapper.GunMapperTest.NORMAL_GUN_ID; -import static sanbing.jcpp.app.dal.mapper.PileMapperTest.NORMAL_PILE_ID; -import static sanbing.jcpp.app.dal.mapper.StationMapperTest.NORMAL_STATION_ID; -import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; +import static sanbing.jcpp.app.dal.mapper.GunMapperIT.NORMAL_GUN_ID; +import static sanbing.jcpp.app.dal.mapper.PileMapperIT.NORMAL_PILE_ID; +import static sanbing.jcpp.app.dal.mapper.StationMapperIT.NORMAL_STATION_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperIT.NORMAL_USER_ID; /** * @author baigod */ -public class OrderMapperTest extends AbstractTestBase { +class OrderMapperIT extends AbstractTestBase { @Resource OrderMapper orderMapper; diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java similarity index 92% rename from jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java rename to jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java index b3afb7b..46168eb 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java @@ -19,13 +19,13 @@ import java.text.DecimalFormat; import java.time.LocalDateTime; import java.util.UUID; -import static sanbing.jcpp.app.dal.mapper.StationMapperTest.NORMAL_STATION_ID; -import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; +import static sanbing.jcpp.app.dal.mapper.StationMapperIT.NORMAL_STATION_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperIT.NORMAL_USER_ID; /** * @author baigod */ -public class PileMapperTest extends AbstractTestBase { +class PileMapperIT extends AbstractTestBase { static final UUID[] NORMAL_PILE_ID = new UUID[]{ UUID.fromString("fd7b3f60-db6c-4347-bff3-3c922985b95c"), UUID.fromString("fa621927-6458-4e09-9666-99c52230db2b"), diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperIT.java similarity index 93% rename from jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java rename to jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperIT.java index 63a6f02..5c3b44d 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/StationMapperIT.java @@ -16,12 +16,12 @@ import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import java.time.LocalDateTime; import java.util.UUID; -import static sanbing.jcpp.app.dal.mapper.UserMapperTest.NORMAL_USER_ID; +import static sanbing.jcpp.app.dal.mapper.UserMapperIT.NORMAL_USER_ID; /** * @author baigod */ -class StationMapperTest extends AbstractTestBase { +class StationMapperIT extends AbstractTestBase { static final UUID NORMAL_STATION_ID = UUID.fromString("07d80c81-fe99-4a1f-a6aa-dc4d798b5626"); @Resource diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperIT.java similarity index 96% rename from jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java rename to jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperIT.java index be710a4..5ef9afc 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/UserMapperIT.java @@ -18,7 +18,7 @@ import java.util.UUID; /** * @author baigod */ -class UserMapperTest extends AbstractTestBase { +class UserMapperIT extends AbstractTestBase { static final UUID NORMAL_USER_ID = UUID.fromString("21cbf909-a23a-4396-840a-f34061f59f95"); @Resource diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java similarity index 97% rename from jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java rename to jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java index 1c41e34..edd93f6 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationTest.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java @@ -16,7 +16,7 @@ import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.stream.IntStream; -class RedisCacheConfigurationTest extends AbstractTestBase { +class RedisCacheConfigurationIT extends AbstractTestBase { @Resource RedisTemplate redisTemplate; diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java index a9ad896..1daa805 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/AbstractProtocolTestBase.java @@ -1,7 +1,8 @@ -package sanbing.jcpp.protocol; /** +/** * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ +package sanbing.jcpp.protocol; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.TestMethodOrder; diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java similarity index 95% rename from jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java rename to jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java index 016175b..624f24c 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerTest.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java @@ -13,7 +13,7 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import jakarta.annotation.Resource; -import org.junit.After; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Value; @@ -38,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration.LITTLE_ENDIAN_BYTE_ORDER; -class DownlinkControllerTest extends AbstractProtocolTestBase { +class DownlinkControllerIT extends AbstractProtocolTestBase { final String PROTOCOL_NAME = "yunkuaichongV150"; @Value("${service.protocols.yunkuaichongV150.listener.tcp.handler.configuration}") @@ -81,7 +81,7 @@ class DownlinkControllerTest extends AbstractProtocolTestBase { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - log.info("接收到字节码:{}", msg); + log.info("接收到下行报文:{}", msg); } }); } @@ -92,8 +92,8 @@ class DownlinkControllerTest extends AbstractProtocolTestBase { channel = f.channel(); } - @After - public void tearDown() { + @AfterEach + void tearDown() { if (channel != null) { channel.close(); } @@ -101,7 +101,7 @@ class DownlinkControllerTest extends AbstractProtocolTestBase { } @Test - void remoteStartCharging() throws Exception { + void remoteStartChargingTest() throws Exception { // 先发送一段登录 channel.writeAndFlush(Unpooled.wrappedBuffer(HexUtil.decodeHex("6822001900012023121200001001011047562e393572313300898604d11722d0348606024E87"))).sync(); From 4356eaba85cf1518ecdc695d443fb97672585d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 9 Oct 2024 10:08:32 +0800 Subject: [PATCH 011/102] =?UTF-8?q?protocolSession=E7=9A=84=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=94=B9=E4=B8=BA=E5=90=8C=E6=AD=A5caffeine=EF=BC=8C?= =?UTF-8?q?=E5=9B=A0=E4=B8=BA=E6=B2=A1=E6=9C=89IO=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/app-service.yml | 2 +- .../util/config/ScheduledTaskConfig.java | 23 +++++++++++ .../util/config/ShardingThreadPool.java | 21 +++++----- .../protocol/ProtocolMessageProcessor.java | 4 +- .../protocol/adapter/DownlinkController.java | 23 +++++------ .../ProtocolSessionRegistryProvider.java | 3 +- ...efaultProtocolSessionRegistryProvider.java | 40 ++++++++----------- .../src/main/resources/protocol-service.yml | 2 +- .../AbstractYunKuaiChongCmdExe.java | 12 +++--- ...nKuaiChongV15ProtocolMessageProcessor.java | 2 +- 10 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 175b136..14d120d 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -210,5 +210,5 @@ service: thread-pool: sharding: hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:128}" + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java new file mode 100644 index 0000000..c68c811 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +public class ScheduledTaskConfig { + + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(Runtime.getRuntime().availableProcessors()); + scheduler.setThreadNamePrefix("scheduled-task-"); + scheduler.initialize(); + return scheduler; + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java index 4d5338b..5bf3513 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -33,12 +33,12 @@ public class ShardingThreadPool { @Value("${thread-pool.sharding.hash_function_name:murmur3_128}") private String hashFunctionName; - @Value("${thread-pool.sharding.parallelism:128}") + @Value("${thread-pool.sharding.parallelism:8}") private int parallelism; private HashFunction hashFunction; - private final Map EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(128); + private final Map EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(8); @PostConstruct public void init() { @@ -59,13 +59,16 @@ public class ShardingThreadPool { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) v; - log.info("分区 {}/{} 的线程池中剩余 {} 条待执行任务,当前正在执行的线程数 {}, 已完成任务 {} / {}", - k, - EXECUTOR_SERVICE_MAP.size(), - threadPoolExecutor.getQueue().size(), - threadPoolExecutor.getActiveCount(), - threadPoolExecutor.getCompletedTaskCount(), - threadPoolExecutor.getTaskCount()); + int size = threadPoolExecutor.getQueue().size(); + + if (size > 1) { + log.info("分区 {} 的线程池中剩余 {} 条待执行任务,当前正在执行的线程数 {}, 已完成任务 {} / {}", + k, + size, + threadPoolExecutor.getActiveCount(), + threadPoolExecutor.getCompletedTaskCount(), + threadPoolExecutor.getTaskCount()); + } }); } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java index 848cfd3..506a372 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java @@ -47,7 +47,7 @@ public abstract class ProtocolMessageProcessor { })); } - protected abstract void uplinkHandle(ListenerToHandlerMsg listenerToHandlerMsg) throws Exception; + protected abstract void uplinkHandle(ListenerToHandlerMsg listenerToHandlerMsg); public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg, MessagesStats downlinkMsgStats) throws DownlinkException { try { @@ -62,5 +62,5 @@ public abstract class ProtocolMessageProcessor { } } - protected abstract void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) throws Exception; + protected abstract void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg); } \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java index 8fe97a3..facf5ea 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java @@ -20,7 +20,6 @@ import sanbing.jcpp.protocol.domain.ProtocolSession; import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; import java.util.UUID; -import java.util.concurrent.CompletableFuture; /** * @author baigod @@ -44,14 +43,14 @@ public class DownlinkController { final DeferredResult> response = new DeferredResult<>(onDownlinkTimeout, ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).build()); - UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(),downlinkMsg.getSessionIdLSB()) ; + UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(), downlinkMsg.getSessionIdLSB()); - CompletableFuture protocolSessionCompletableFuture = protocolSessionRegistryProvider.get(protocolSessionId); + ProtocolSession protocolSession = protocolSessionRegistryProvider.get(protocolSessionId); - protocolSessionCompletableFuture.thenAccept(session -> { - if (session != null) { + try { + if (protocolSession != null) { - session.onDownlink(downlinkMsg); + protocolSession.onDownlink(downlinkMsg); response.setResult(ResponseEntity.status(HttpStatus.OK).build()); } else { @@ -60,17 +59,15 @@ public class DownlinkController { response.setResult(ResponseEntity.status(HttpStatus.NOT_FOUND).body("Protocol Session not found for ID:" + protocolSessionId)); } - }).whenComplete((unused, throwable) -> { - if (throwable != null) { + } catch (Exception e) { - log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, throwable); + log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, e); - if (!response.isSetOrExpired()) { + if (!response.isSetOrExpired()) { - response.setResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(throwable.getMessage())); - } + response.setResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage())); } - }); + } return response; } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java index be674f8..b142757 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java @@ -7,7 +7,6 @@ package sanbing.jcpp.protocol.provider; import sanbing.jcpp.protocol.domain.ProtocolSession; import java.util.UUID; -import java.util.concurrent.CompletableFuture; /** * @author baigod @@ -21,7 +20,7 @@ public interface ProtocolSessionRegistryProvider { void unregister(UUID sessionId); - CompletableFuture get(UUID sessionId); + ProtocolSession get(UUID sessionId); /** * 活跃会话 diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java index c32ef51..ae823b0 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java @@ -4,7 +4,7 @@ */ package sanbing.jcpp.protocol.provider.impl; -import com.github.benmanes.caffeine.cache.AsyncCache; +import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -20,7 +20,6 @@ import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; import java.time.LocalDateTime; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -33,6 +32,7 @@ import java.util.concurrent.TimeUnit; @Slf4j public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRegistryProvider { private static final int INIT_CACHE_LIMIT = 100_000; + private static final int MAXIMUM_SIZE = 1_000_000; @Value("${service.protocols.sessions.default-inactivity-timeout-in-sec}") private int defaultInactivityTimeoutInSec; @@ -41,23 +41,18 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe private int defaultStateCheckIntervalInSec; @Getter - private final AsyncCache SESSION_CACHE = buildCache(); + private final Cache sessionCache = buildCache(); private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("session-state-checker")); @PostConstruct public void init() { - scheduledExecutorService.scheduleAtFixedRate(() -> - SESSION_CACHE.asMap().forEach((id, sessionCompletableFuture) -> - sessionCompletableFuture.whenComplete((protocolSession, throwable) -> { - if (throwable == null && protocolSession != null) { - if (protocolSession.getLastActivityTime().isBefore(LocalDateTime.now().minusSeconds(defaultInactivityTimeoutInSec))) { - protocolSession.close(SessionCloseReason.INACTIVE); - unregister(protocolSession.getId()); - } - } - }) - ), defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); + scheduledExecutorService.scheduleAtFixedRate(() -> sessionCache.asMap().forEach((id, session) -> { + if (session.getLastActivityTime().isBefore(LocalDateTime.now().minusSeconds(defaultInactivityTimeoutInSec))) { + session.close(SessionCloseReason.INACTIVE); + unregister(session.getId()); + } + }), defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); } @PreDestroy @@ -72,7 +67,7 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe log.debug("Registering session {}", protocolSession); } - SESSION_CACHE.put(protocolSession.getId(), CompletableFuture.supplyAsync(() -> protocolSession, ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL)); + sessionCache.put(protocolSession.getId(), protocolSession); } @@ -81,16 +76,15 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe log.info("Unregistering session {}", sessionId); - SESSION_CACHE.synchronous().invalidate(sessionId); + sessionCache.invalidate(sessionId); } @Override - public CompletableFuture get(UUID sessionId) { + public ProtocolSession get(UUID sessionId) { log.debug("Get session {}", sessionId); - return SESSION_CACHE.get(sessionId, uuid -> null); - + return sessionCache.get(sessionId, uuid -> null); } @Override @@ -102,14 +96,14 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe protocolSession.setLastActivityTime(LocalDateTime.now()); - SESSION_CACHE.put(protocolSession.getId(), CompletableFuture.supplyAsync(() -> protocolSession, ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL)); + sessionCache.put(protocolSession.getId(), protocolSession); } - private AsyncCache buildCache() { + private Cache buildCache() { return Caffeine.newBuilder() .initialCapacity(INIT_CACHE_LIMIT) - .maximumSize(INIT_CACHE_LIMIT * 10) + .maximumSize(MAXIMUM_SIZE) .executor(ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL) - .buildAsync(); + .build(); } } \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 4baf214..284a6bb 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -148,5 +148,5 @@ queue: thread-pool: sharding: hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:128}" + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java index f38e54a..0d98b61 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java @@ -121,10 +121,10 @@ public class AbstractYunKuaiChongCmdExe { } protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, - int seqNo, - int encryptionFlag, - ByteBuf msgBody, - TcpSession tcpSession) { + int seqNo, + int encryptionFlag, + ByteBuf msgBody, + TcpSession tcpSession) { byte[] encode = encode(downlinkCmd, seqNo, encryptionFlag, msgBody); @@ -132,8 +132,8 @@ public class AbstractYunKuaiChongCmdExe { } protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, - ByteBuf msgBody, - TcpSession tcpSession) { + ByteBuf msgBody, + TcpSession tcpSession) { byte[] encode = encode(downlinkCmd, tcpSession.nextSeqNo(SequenceNumberLength.SHORT), diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java index 43164b8..04d18c8 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java @@ -160,7 +160,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc } @Override - public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) throws Exception { + public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) { TcpSession session = (TcpSession) sessionToHandlerMsg.session(); DownlinkRestMessage protocolDownlinkMsg = sessionToHandlerMsg.downlinkMsg(); From c3295ce01c551ed4075895a06982e226b0487338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 9 Oct 2024 10:08:32 +0800 Subject: [PATCH 012/102] =?UTF-8?q?protocolSession=E7=9A=84=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=94=B9=E4=B8=BA=E5=90=8C=E6=AD=A5caffeine=EF=BC=8C?= =?UTF-8?q?=E5=9B=A0=E4=B8=BA=E6=B2=A1=E6=9C=89IO=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/app-service.yml | 2 +- .../util/config/ScheduledTaskConfig.java | 23 +++++++++++ .../util/config/ShardingThreadPool.java | 21 +++++----- .../protocol/ProtocolMessageProcessor.java | 4 +- .../protocol/adapter/DownlinkController.java | 23 +++++------ .../ProtocolSessionRegistryProvider.java | 3 +- ...efaultProtocolSessionRegistryProvider.java | 40 ++++++++----------- .../src/main/resources/protocol-service.yml | 2 +- .../adapter/DownlinkControllerIT.java | 2 +- .../AbstractYunKuaiChongCmdExe.java | 12 +++--- ...nKuaiChongV15ProtocolMessageProcessor.java | 2 +- 11 files changed, 75 insertions(+), 59 deletions(-) create mode 100644 jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 175b136..14d120d 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -210,5 +210,5 @@ service: thread-pool: sharding: hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:128}" + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java new file mode 100644 index 0000000..c68c811 --- /dev/null +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ScheduledTaskConfig.java @@ -0,0 +1,23 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +public class ScheduledTaskConfig { + + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(Runtime.getRuntime().availableProcessors()); + scheduler.setThreadNamePrefix("scheduled-task-"); + scheduler.initialize(); + return scheduler; + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java index 4d5338b..5bf3513 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -33,12 +33,12 @@ public class ShardingThreadPool { @Value("${thread-pool.sharding.hash_function_name:murmur3_128}") private String hashFunctionName; - @Value("${thread-pool.sharding.parallelism:128}") + @Value("${thread-pool.sharding.parallelism:8}") private int parallelism; private HashFunction hashFunction; - private final Map EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(128); + private final Map EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(8); @PostConstruct public void init() { @@ -59,13 +59,16 @@ public class ShardingThreadPool { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) v; - log.info("分区 {}/{} 的线程池中剩余 {} 条待执行任务,当前正在执行的线程数 {}, 已完成任务 {} / {}", - k, - EXECUTOR_SERVICE_MAP.size(), - threadPoolExecutor.getQueue().size(), - threadPoolExecutor.getActiveCount(), - threadPoolExecutor.getCompletedTaskCount(), - threadPoolExecutor.getTaskCount()); + int size = threadPoolExecutor.getQueue().size(); + + if (size > 1) { + log.info("分区 {} 的线程池中剩余 {} 条待执行任务,当前正在执行的线程数 {}, 已完成任务 {} / {}", + k, + size, + threadPoolExecutor.getActiveCount(), + threadPoolExecutor.getCompletedTaskCount(), + threadPoolExecutor.getTaskCount()); + } }); } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java index 848cfd3..506a372 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java @@ -47,7 +47,7 @@ public abstract class ProtocolMessageProcessor { })); } - protected abstract void uplinkHandle(ListenerToHandlerMsg listenerToHandlerMsg) throws Exception; + protected abstract void uplinkHandle(ListenerToHandlerMsg listenerToHandlerMsg); public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg, MessagesStats downlinkMsgStats) throws DownlinkException { try { @@ -62,5 +62,5 @@ public abstract class ProtocolMessageProcessor { } } - protected abstract void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) throws Exception; + protected abstract void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg); } \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java index 8fe97a3..facf5ea 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java @@ -20,7 +20,6 @@ import sanbing.jcpp.protocol.domain.ProtocolSession; import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; import java.util.UUID; -import java.util.concurrent.CompletableFuture; /** * @author baigod @@ -44,14 +43,14 @@ public class DownlinkController { final DeferredResult> response = new DeferredResult<>(onDownlinkTimeout, ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).build()); - UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(),downlinkMsg.getSessionIdLSB()) ; + UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(), downlinkMsg.getSessionIdLSB()); - CompletableFuture protocolSessionCompletableFuture = protocolSessionRegistryProvider.get(protocolSessionId); + ProtocolSession protocolSession = protocolSessionRegistryProvider.get(protocolSessionId); - protocolSessionCompletableFuture.thenAccept(session -> { - if (session != null) { + try { + if (protocolSession != null) { - session.onDownlink(downlinkMsg); + protocolSession.onDownlink(downlinkMsg); response.setResult(ResponseEntity.status(HttpStatus.OK).build()); } else { @@ -60,17 +59,15 @@ public class DownlinkController { response.setResult(ResponseEntity.status(HttpStatus.NOT_FOUND).body("Protocol Session not found for ID:" + protocolSessionId)); } - }).whenComplete((unused, throwable) -> { - if (throwable != null) { + } catch (Exception e) { - log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, throwable); + log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, e); - if (!response.isSetOrExpired()) { + if (!response.isSetOrExpired()) { - response.setResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(throwable.getMessage())); - } + response.setResult(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage())); } - }); + } return response; } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java index be674f8..b142757 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/ProtocolSessionRegistryProvider.java @@ -7,7 +7,6 @@ package sanbing.jcpp.protocol.provider; import sanbing.jcpp.protocol.domain.ProtocolSession; import java.util.UUID; -import java.util.concurrent.CompletableFuture; /** * @author baigod @@ -21,7 +20,7 @@ public interface ProtocolSessionRegistryProvider { void unregister(UUID sessionId); - CompletableFuture get(UUID sessionId); + ProtocolSession get(UUID sessionId); /** * 活跃会话 diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java index c32ef51..ae823b0 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java @@ -4,7 +4,7 @@ */ package sanbing.jcpp.protocol.provider.impl; -import com.github.benmanes.caffeine.cache.AsyncCache; +import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -20,7 +20,6 @@ import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; import java.time.LocalDateTime; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -33,6 +32,7 @@ import java.util.concurrent.TimeUnit; @Slf4j public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRegistryProvider { private static final int INIT_CACHE_LIMIT = 100_000; + private static final int MAXIMUM_SIZE = 1_000_000; @Value("${service.protocols.sessions.default-inactivity-timeout-in-sec}") private int defaultInactivityTimeoutInSec; @@ -41,23 +41,18 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe private int defaultStateCheckIntervalInSec; @Getter - private final AsyncCache SESSION_CACHE = buildCache(); + private final Cache sessionCache = buildCache(); private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("session-state-checker")); @PostConstruct public void init() { - scheduledExecutorService.scheduleAtFixedRate(() -> - SESSION_CACHE.asMap().forEach((id, sessionCompletableFuture) -> - sessionCompletableFuture.whenComplete((protocolSession, throwable) -> { - if (throwable == null && protocolSession != null) { - if (protocolSession.getLastActivityTime().isBefore(LocalDateTime.now().minusSeconds(defaultInactivityTimeoutInSec))) { - protocolSession.close(SessionCloseReason.INACTIVE); - unregister(protocolSession.getId()); - } - } - }) - ), defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); + scheduledExecutorService.scheduleAtFixedRate(() -> sessionCache.asMap().forEach((id, session) -> { + if (session.getLastActivityTime().isBefore(LocalDateTime.now().minusSeconds(defaultInactivityTimeoutInSec))) { + session.close(SessionCloseReason.INACTIVE); + unregister(session.getId()); + } + }), defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); } @PreDestroy @@ -72,7 +67,7 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe log.debug("Registering session {}", protocolSession); } - SESSION_CACHE.put(protocolSession.getId(), CompletableFuture.supplyAsync(() -> protocolSession, ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL)); + sessionCache.put(protocolSession.getId(), protocolSession); } @@ -81,16 +76,15 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe log.info("Unregistering session {}", sessionId); - SESSION_CACHE.synchronous().invalidate(sessionId); + sessionCache.invalidate(sessionId); } @Override - public CompletableFuture get(UUID sessionId) { + public ProtocolSession get(UUID sessionId) { log.debug("Get session {}", sessionId); - return SESSION_CACHE.get(sessionId, uuid -> null); - + return sessionCache.get(sessionId, uuid -> null); } @Override @@ -102,14 +96,14 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe protocolSession.setLastActivityTime(LocalDateTime.now()); - SESSION_CACHE.put(protocolSession.getId(), CompletableFuture.supplyAsync(() -> protocolSession, ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL)); + sessionCache.put(protocolSession.getId(), protocolSession); } - private AsyncCache buildCache() { + private Cache buildCache() { return Caffeine.newBuilder() .initialCapacity(INIT_CACHE_LIMIT) - .maximumSize(INIT_CACHE_LIMIT * 10) + .maximumSize(MAXIMUM_SIZE) .executor(ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL) - .buildAsync(); + .build(); } } \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 4baf214..284a6bb 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -148,5 +148,5 @@ queue: thread-pool: sharding: hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:128}" + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java index 624f24c..f02982c 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java @@ -109,7 +109,7 @@ class DownlinkControllerIT extends AbstractProtocolTestBase { Thread.sleep(1000); UUID messageId = UUID.randomUUID(); - ProtocolSession protocolSession = sessionRegistryProvider.getSESSION_CACHE().asMap().values().stream().findFirst().get().get(); + ProtocolSession protocolSession = sessionRegistryProvider.getSessionCache().asMap().values().stream().findFirst().get(); UUID sessionId = protocolSession.getId(); UUID requestId = UUID.randomUUID(); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java index f38e54a..0d98b61 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java @@ -121,10 +121,10 @@ public class AbstractYunKuaiChongCmdExe { } protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, - int seqNo, - int encryptionFlag, - ByteBuf msgBody, - TcpSession tcpSession) { + int seqNo, + int encryptionFlag, + ByteBuf msgBody, + TcpSession tcpSession) { byte[] encode = encode(downlinkCmd, seqNo, encryptionFlag, msgBody); @@ -132,8 +132,8 @@ public class AbstractYunKuaiChongCmdExe { } protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, - ByteBuf msgBody, - TcpSession tcpSession) { + ByteBuf msgBody, + TcpSession tcpSession) { byte[] encode = encode(downlinkCmd, tcpSession.nextSeqNo(SequenceNumberLength.SHORT), diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java index 43164b8..04d18c8 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java @@ -160,7 +160,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc } @Override - public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) throws Exception { + public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) { TcpSession session = (TcpSession) sessionToHandlerMsg.session(); DownlinkRestMessage protocolDownlinkMsg = sessionToHandlerMsg.downlinkMsg(); From f6fdc9b5076a3bd76d32bc2cbf2f9976e14c583d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 9 Oct 2024 12:53:18 +0800 Subject: [PATCH 013/102] =?UTF-8?q?=E4=BF=AE=E5=A4=8Djunit5=E5=8D=95?= =?UTF-8?q?=E4=BE=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/RedisCacheConfigurationIT.java | 2 ++ .../resources/app-service-test.properties | 1 + pom.xml | 25 +++++++++++++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java index edd93f6..ae5640e 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/infrastructure/cache/RedisCacheConfigurationIT.java @@ -7,6 +7,7 @@ package sanbing.jcpp.infrastructure.cache; import jakarta.annotation.Resource; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.springframework.data.redis.core.*; import sanbing.jcpp.AbstractTestBase; @@ -16,6 +17,7 @@ import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.stream.IntStream; +@EnabledIfSystemProperty(named = "cache.type", matches = "redis") class RedisCacheConfigurationIT extends AbstractTestBase { @Resource diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties index 9e61ffc..b397d7b 100644 --- a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -1,2 +1,3 @@ redis.connection.type=cluster redis.cluster.nodes=10.102.12.101:30700,10.102.12.101:32027,10.102.12.101:30767,10.102.12.101:30250,10.102.12.101:30612,10.102.12.101:32303 +service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 \ No newline at end of file diff --git a/pom.xml b/pom.xml index bd91b22..37e6903 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,8 @@ 33.3.0-jre 0.8.12 + + 1.7.0 3.4.4 3.21.9 @@ -52,18 +54,18 @@ - + dev true - true - true + true + true - + unit-test @@ -71,7 +73,7 @@ true - + integration-test @@ -79,7 +81,7 @@ false - + test-all @@ -368,7 +370,7 @@ -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20 --add-opens java.base/java.lang.reflect=ALL-UNNAMED - ${skip.unit.test} + ${skip.unit.tests} 1 filesystem @@ -392,11 +394,19 @@ surefire-junit47 ${maven-surefire-plugin.version} + + org.apache.maven.surefire + surefire-junit-platform + ${maven-surefire-plugin.version} + org.apache.maven.plugins maven-failsafe-plugin + + ${skip.integration.tests} + integration-tests @@ -405,7 +415,6 @@ verify - ${skip.integration.test} **/*IntegrationTest.java **/IT*.java From b53cd159a7214659af5e734077fa85f6cbefbbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 9 Oct 2024 20:44:44 +0800 Subject: [PATCH 014/102] =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=8A=A5=E6=96=87?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=87=AD=E6=8D=AE=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E5=BA=94=E5=AF=B9=E5=90=8E=E7=BB=AD=E7=89=B9=E6=AE=8A=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E9=9C=80=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-infrastructure-proto/src/main/proto/protocol.proto | 3 ++- .../yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index 5834455..0a90306 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -50,7 +50,8 @@ message DownlinkRestMessage { } message LoginRequest { - string pileCode = 3; + string pileCode = 2; + string credential = 3; string remoteAddress = 4; string nodeId = 10; string nodeWebapiIpPort = 11; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java index 8bb1991..0971188 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java @@ -63,6 +63,7 @@ public class YunKuaiChongV150LoginULCmd extends YunKuaiChongUplinkCmdExe { // 转发到后端 LoginRequest loginRequest = LoginRequest.newBuilder() .setPileCode(pileCode) + .setCredential(pileCode) .setRemoteAddress(tcpSession.getAddress().toString()) .setNodeId(ctx.getServiceInfoProvider().getServiceId()) .setNodeWebapiIpPort(ctx.getServiceInfoProvider().getServiceWebapiEndpoint()) From 443cf0abe971c3b199a7eec638968f716068f2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 10 Oct 2024 10:29:11 +0800 Subject: [PATCH 015/102] cleanup code --- .../yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java index e95a2a4..d35735b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java @@ -62,10 +62,10 @@ public class YunKuaiChongV150HeartbeatULCmd extends YunKuaiChongUplinkCmdExe { .build(); tcpSession.getForwarder().sendMessage(uplinkQueueMessage); - pingAck(tcpSession, yunKuaiChongUplinkMessage, pileCodeBytes, gunCodeByte); + pingAck(tcpSession, pileCodeBytes, gunCodeByte); } - private void pingAck(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, byte[] pileCodeBytes, byte gunCodeByte) { + private void pingAck(TcpSession tcpSession, byte[] pileCodeBytes, byte gunCodeByte) { ByteBuf pingAckMsgBody = Unpooled.buffer(9); pingAckMsgBody.writeBytes(pileCodeBytes); pingAckMsgBody.writeByte(gunCodeByte); From 966b683b7666c7388ff4eaec2a9338d774eb8191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 10 Oct 2024 10:41:13 +0800 Subject: [PATCH 016/102] cleanup code --- .../jcpp/infrastructure/util/validation/Validator.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java index a13aab0..6467f96 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java @@ -11,14 +11,12 @@ import java.util.function.Function; public class Validator { - public static void validateString(String val, String errorMessage) { if (val == null || val.isEmpty()) { throw new IncorrectParameterException(errorMessage); } } - public static void validateString(String val, Function errorMessageFunction) { if (val == null || val.isEmpty()) { throw new IncorrectParameterException(errorMessageFunction.apply(val)); @@ -31,13 +29,6 @@ public class Validator { } } - @Deprecated - public static void validateId(UUID id, String errorMessage) { - if (id == null) { - throw new IncorrectParameterException(errorMessage); - } - } - public static void validateId(UUID id, Function errorMessageFunction) { if (id == null) { throw new IncorrectParameterException(errorMessageFunction.apply(id)); From 516fe1d09110b93584219bfa6bb9dc2d0c52466c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 10 Oct 2024 10:41:40 +0800 Subject: [PATCH 017/102] cleanup code --- .../sanbing/jcpp/infrastructure/util/validation/Validator.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java index 6467f96..7ff93c1 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/validation/Validator.java @@ -35,12 +35,10 @@ public class Validator { } } - public static void checkNotNull(Object reference, String errorMessage) { if (reference == null) { throw new IncorrectParameterException(errorMessage); } } - } From 344e23d80e5c69e6d2bebd933777d6fe211ec29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 10 Oct 2024 11:08:11 +0800 Subject: [PATCH 018/102] cleanup code --- .../src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java | 2 +- .../java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java index 6a8ffd6..55c894e 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java @@ -70,7 +70,7 @@ public abstract class ProtocolBootstrap implements HealthIndicator { if (tcpCfg != null) { - listener = new TcpListener<>(protocolName, tcpCfg, messageProcessor(), protocolContext.getStatsFactory()); + listener = new TcpListener(protocolName, tcpCfg, messageProcessor(), protocolContext.getStatsFactory()); } _init(); diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java index 8f99074..121e295 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java @@ -29,7 +29,7 @@ import sanbing.jcpp.protocol.listener.Listener; * @author baigod */ @Slf4j -public class TcpListener extends Listener { +public class TcpListener extends Listener { private Channel serverChannel; private EventLoopGroup bossGroup; From 02c399d8dd8f06fa16dc2d17e34dda361bc6dfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 10 Oct 2024 11:19:39 +0800 Subject: [PATCH 019/102] =?UTF-8?q?ProtocolMessageProcessor=20=E5=BA=94?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E5=BF=85=E9=A1=BB=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/sanbing/jcpp/protocol/listener/Listener.java | 7 ++++++- .../sanbing/jcpp/protocol/listener/tcp/TcpListener.java | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java index b438b6f..d3a93da 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java @@ -10,6 +10,7 @@ import org.springframework.boot.actuate.health.Health; import sanbing.jcpp.infrastructure.stats.DefaultCounter; import sanbing.jcpp.infrastructure.stats.MessagesStats; import sanbing.jcpp.infrastructure.stats.StatsFactory; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; import java.util.concurrent.atomic.AtomicInteger; @@ -21,6 +22,9 @@ public abstract class Listener { @Getter private final String protocolName; + @Getter + private final ProtocolMessageProcessor protocolMessageProcessor; + protected AtomicInteger connectionsGauge = new AtomicInteger(); protected MessagesStats uplinkMsgStats; protected MessagesStats downlinkMsgStats; @@ -28,8 +32,9 @@ public abstract class Listener { protected DefaultCounter downlinkTrafficCounter; protected Timer downlinkTimer; - public Listener(String protocolName, StatsFactory statsFactory) { + protected Listener(String protocolName, ProtocolMessageProcessor protocolMessageProcessor, StatsFactory statsFactory) { this.protocolName = protocolName; + this.protocolMessageProcessor = protocolMessageProcessor; statsFactory.createGauge("openConnections", connectionsGauge, "protocol", protocolName); this.uplinkMsgStats = statsFactory.createMessagesStats("listenerUplinkMessage", "protocol", protocolName); diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java index 121e295..4024de0 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java @@ -38,7 +38,7 @@ public class TcpListener extends Listener { private final ChannelHandlerParameter parameter; public TcpListener(String protocolName, TcpCfg tcpCfg, ProtocolMessageProcessor protocolMessageProcessor, StatsFactory statsFactory) throws InterruptedException { - super(protocolName, statsFactory); + super(protocolName, protocolMessageProcessor, statsFactory); parameter = new ChannelHandlerParameter(protocolName, tcpCfg.getHandler(), protocolMessageProcessor, connectionsGauge, uplinkMsgStats, downlinkMsgStats, uplinkTrafficCounter, downlinkTrafficCounter, downlinkTimer); From 278bb9e69eb95c6f62559de89f1baf98e21308fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 10 Oct 2024 11:44:27 +0800 Subject: [PATCH 020/102] =?UTF-8?q?ChannelHandlerParameter=20=E4=B8=8A?= =?UTF-8?q?=E6=B5=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/ChannelHandlerInitializer.java | 27 ++++++++++--------- .../listener/ChannelHandlerParameter.java | 2 -- .../jcpp/protocol/listener/Listener.java | 4 +++ .../protocol/listener/tcp/TcpListener.java | 13 +++------ 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java index e2c1f62..0b9c892 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerInitializer.java @@ -19,6 +19,8 @@ import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.concurrent.GlobalEventExecutor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.protocol.cfg.TcpCfg; +import sanbing.jcpp.protocol.cfg.TcpHandlerCfg; import sanbing.jcpp.protocol.cfg.enums.TcpHandlerType; import sanbing.jcpp.protocol.listener.tcp.TcpChannelHandler; import sanbing.jcpp.protocol.listener.tcp.configs.BinaryHandlerConfiguration; @@ -59,22 +61,23 @@ public abstract class ChannelHandlerInitializer extends Chann super.channelUnregistered(ctx); } - public static ChannelHandlerInitializer createTcpChannelHandler(ChannelHandlerParameter parameter) { - TcpHandlerType type = parameter.handlerCfg().getType(); + public static ChannelHandlerInitializer createTcpChannelHandler(TcpCfg tcpCfg, ChannelHandlerParameter parameter) { + TcpHandlerCfg tcpCfgHandler = tcpCfg.getHandler(); + TcpHandlerType type = tcpCfgHandler.getType(); return switch (type) { case TEXT -> new ChannelHandlerInitializer<>() { @Override protected void initChannel(SocketChannel socketChannel) { - TextHandlerConfiguration textHandlerConfig = (TextHandlerConfiguration) parameter.handlerCfg().getConfiguration(TEXT); + TextHandlerConfiguration textHandlerConfig = (TextHandlerConfiguration) tcpCfgHandler.getConfiguration(TEXT); ByteBuf[] delimiters = SYSTEM_LINE_SEPARATOR.equals(textHandlerConfig.getMessageSeparator()) ? Delimiters.lineDelimiter() : Delimiters.nulDelimiter(); DelimiterBasedFrameDecoder framer = new DelimiterBasedFrameDecoder(textHandlerConfig.getMaxFrameLength(), textHandlerConfig.isStripDelimiter(), delimiters); socketChannel.pipeline() .addLast("tracerHandler", new TracerHandler()) - .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), parameter.handlerCfg().getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) - .addLast("idleStateHandler", new IdleStateHandler(parameter.handlerCfg().getIdleTimeoutSeconds(), 0, 0)) + .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), tcpCfgHandler.getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) + .addLast("idleStateHandler", new IdleStateHandler(tcpCfgHandler.getIdleTimeoutSeconds(), 0, 0)) .addLast("idleEventHandler", new IdleEventHandler(parameter.protocolName())) .addLast("framer", framer) .addLast("tcpTextDecoder", new TcpMsgDecoder<>(parameter.protocolName(), msg -> TcpMsgDecoder.toString(msg, textHandlerConfig.getCharsetName()))) @@ -87,9 +90,9 @@ public abstract class ChannelHandlerInitializer extends Chann protected void initChannel(SocketChannel socketChannel) { socketChannel.pipeline() .addLast("tracerHandler", new TracerHandler()) - .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), parameter.handlerCfg().getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) + .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), tcpCfgHandler.getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) .addLast("idleStateHandler", - new IdleStateHandler(parameter.handlerCfg().getIdleTimeoutSeconds(), 0, 0)) + new IdleStateHandler(tcpCfgHandler.getIdleTimeoutSeconds(), 0, 0)) .addLast("idleEventHandler", new IdleEventHandler(parameter.protocolName())) .addLast("datagramToJsonDecoder", new JsonObjectDecoder()) .addLast("tcpJsonDecoder", new TcpMsgDecoder<>(parameter.protocolName(), TcpMsgDecoder::toJson)) @@ -99,15 +102,15 @@ public abstract class ChannelHandlerInitializer extends Chann case BINARY -> new ChannelHandlerInitializer<>() { @Override protected void initChannel(SocketChannel socketChannel) { - BinaryHandlerConfiguration binaryHandlerConfig = (BinaryHandlerConfiguration) parameter.handlerCfg().getConfiguration(BINARY); + BinaryHandlerConfiguration binaryHandlerConfig = (BinaryHandlerConfiguration) tcpCfgHandler.getConfiguration(BINARY); ByteOrder byteOrder = LITTLE_ENDIAN_BYTE_ORDER.equalsIgnoreCase(binaryHandlerConfig.getByteOrder()) ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; socketChannel.pipeline() .addLast("tracerHandler", new TracerHandler()) - .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), parameter.handlerCfg().getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) - .addLast("idleStateHandler", new IdleStateHandler(parameter.handlerCfg().getIdleTimeoutSeconds(), 0, 0)) + .addLast("connectionLimitHandler", new ConnectionLimitHandler(parameter.protocolName(), tcpCfgHandler.getMaxConnections(), CHANNEL_GROUP, parameter.connectionsGauge())) + .addLast("idleStateHandler", new IdleStateHandler(tcpCfgHandler.getIdleTimeoutSeconds(), 0, 0)) .addLast("idleEventHandler", new IdleEventHandler(parameter.protocolName())); if (LengthFieldBasedFrameDecoder.class.isAssignableFrom(binaryHandlerConfig.getDecoder())) { @@ -134,9 +137,7 @@ public abstract class ChannelHandlerInitializer extends Chann .addLast("tcpByteHandler", new TcpChannelHandler<>(parameter)); } }; - case null -> throw new IllegalArgumentException("Unknown: " + parameter.handlerCfg()); + case null -> throw new IllegalArgumentException("Unknown: " + tcpCfgHandler); }; } - - } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java index dc5509d..b215156 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/ChannelHandlerParameter.java @@ -8,12 +8,10 @@ import io.micrometer.core.instrument.Timer; import sanbing.jcpp.infrastructure.stats.DefaultCounter; import sanbing.jcpp.infrastructure.stats.MessagesStats; import sanbing.jcpp.protocol.ProtocolMessageProcessor; -import sanbing.jcpp.protocol.cfg.TcpHandlerCfg; import java.util.concurrent.atomic.AtomicInteger; public record ChannelHandlerParameter(String protocolName, - TcpHandlerCfg handlerCfg, ProtocolMessageProcessor protocolMessageProcessor, AtomicInteger connectionsGauge, MessagesStats uplinkMsgStats, diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java index d3a93da..918f5df 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/Listener.java @@ -32,6 +32,8 @@ public abstract class Listener { protected DefaultCounter downlinkTrafficCounter; protected Timer downlinkTimer; + protected final ChannelHandlerParameter parameter; + protected Listener(String protocolName, ProtocolMessageProcessor protocolMessageProcessor, StatsFactory statsFactory) { this.protocolName = protocolName; this.protocolMessageProcessor = protocolMessageProcessor; @@ -42,6 +44,8 @@ public abstract class Listener { this.uplinkTrafficCounter = statsFactory.createDefaultCounter("listenerUplinkTraffic", "protocol", protocolName); this.downlinkTrafficCounter = statsFactory.createDefaultCounter("listenerDownlinkTraffic", "protocol", protocolName); this.downlinkTimer = statsFactory.createTimer("listenerDownlink", "protocol", protocolName); + + this.parameter = new ChannelHandlerParameter(protocolName, protocolMessageProcessor, connectionsGauge, uplinkMsgStats, downlinkMsgStats, uplinkTrafficCounter, downlinkTrafficCounter, downlinkTimer); } public abstract Health health(); diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java index 4024de0..91a6cb4 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java @@ -22,7 +22,6 @@ import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; import sanbing.jcpp.protocol.ProtocolMessageProcessor; import sanbing.jcpp.protocol.cfg.TcpCfg; import sanbing.jcpp.protocol.listener.ChannelHandlerInitializer; -import sanbing.jcpp.protocol.listener.ChannelHandlerParameter; import sanbing.jcpp.protocol.listener.Listener; /** @@ -35,21 +34,17 @@ public class TcpListener extends Listener { private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; - private final ChannelHandlerParameter parameter; - public TcpListener(String protocolName, TcpCfg tcpCfg, ProtocolMessageProcessor protocolMessageProcessor, StatsFactory statsFactory) throws InterruptedException { super(protocolName, protocolMessageProcessor, statsFactory); - parameter = new ChannelHandlerParameter(protocolName, tcpCfg.getHandler(), protocolMessageProcessor, connectionsGauge, uplinkMsgStats, downlinkMsgStats, uplinkTrafficCounter, downlinkTrafficCounter, downlinkTimer); - - tcpServerBootstrap(tcpCfg, getProtocolName()); + tcpServerBootstrap(tcpCfg); } - private void tcpServerBootstrap(TcpCfg tcpCfg, String protocolName) throws InterruptedException { + private void tcpServerBootstrap(TcpCfg tcpCfg) throws InterruptedException { bossGroup = new NioEventLoopGroup(tcpCfg.getBossGroupThreadCount(), JCPPThreadFactory.forName("tcp-boss")); workerGroup = new NioEventLoopGroup(tcpCfg.getWorkerGroupThreadCount(), JCPPThreadFactory.forName("tcp-worker")); - ChannelHandlerInitializer channelHandler = ChannelHandlerInitializer.createTcpChannelHandler(parameter); + ChannelHandlerInitializer channelHandler = ChannelHandlerInitializer.createTcpChannelHandler(tcpCfg, parameter); ServerBootstrap server = new ServerBootstrap() .group(bossGroup, workerGroup) @@ -63,7 +58,7 @@ public class TcpListener extends Listener { .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(channelHandler); serverChannel = server.bind(tcpCfg.getBindAddress(), tcpCfg.getBindPort()).sync().channel(); - log.info("Tcp server [{}] started, BindAddress:[{}], BindPort: [{}]", protocolName, tcpCfg.getBindAddress(), tcpCfg.getBindPort()); + log.info("Tcp server [{}] started, BindAddress:[{}], BindPort: [{}]", getProtocolName(), tcpCfg.getBindAddress(), tcpCfg.getBindPort()); } From 3ec2f97933d1ad75993ed9c603728dc191183a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 11 Oct 2024 11:51:32 +0800 Subject: [PATCH 021/102] =?UTF-8?q?session=20=E6=9C=AC=E8=BA=AB=E6=9C=89?= =?UTF-8?q?=E7=8A=B6=E6=80=81=EF=BC=8C=E6=97=A0=E9=9C=80=E4=BC=A0=E9=80=92?= =?UTF-8?q?=E6=97=A0=E7=8A=B6=E6=80=81=E5=8F=82=E6=95=B0=E8=BF=9B=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/tcp/TcpChannelHandler.java | 18 ++++++++++-------- .../jcpp/protocol/listener/tcp/TcpSession.java | 14 +++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java index 03d5d24..c8819e1 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java @@ -121,16 +121,18 @@ public class TcpChannelHandler extends SimpleChannelInboundHandler extends SimpleChannelInboundHandler ByteBufUtil.hexDump(byteBuf)); + logDownlinkStart(byteBuf.readableBytes(), () -> ByteBufUtil.hexDump(byteBuf)); ctx.writeAndFlush(Unpooled.wrappedBuffer(byteBuf)) - .addListener(channelFuture -> logDownlinkUnsuccessful(ctx, channelFuture)); + .addListener(this::logDownlinkUnsuccessful); downlinkMsgStats.incrementSuccessful(); @@ -169,21 +171,21 @@ public class TcpChannelHandler extends SimpleChannelInboundHandler logTransform) { + private void logDownlinkStart(int payloadSize, Supplier logTransform) { downlinkTrafficCounter.add(payloadSize); if (log.isDebugEnabled()) { - log.debug("[{}]{}{} 开始发送下行报文:{}", protocolName, ctx.channel(), tcpSession, logTransform.get()); + log.debug("[{}]{} 开始发送下行报文:{}", protocolName, tcpSession, logTransform.get()); } } - private void logDownlinkUnsuccessful(ChannelHandlerContext ctx, Future channelFuture) { + private void logDownlinkUnsuccessful(Future channelFuture) { downlinkTimer.record(Duration.ofMillis(System.currentTimeMillis() - TracerContextUtil.getCurrentTracer().getTracerTs())); if (channelFuture.isDone() && !channelFuture.isSuccess()) { - log.info("[{}]{}{} 下行报文发送未成功", protocolName, ctx.channel(), tcpSession); + log.info("[{}]{} 下行报文发送未成功", protocolName, tcpSession); } } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java index 7071521..36b20ad 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java @@ -16,7 +16,7 @@ import sanbing.jcpp.protocol.listener.tcp.enums.SequenceNumberLength; import java.net.SocketAddress; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; +import java.util.function.Consumer; /** * 设备会话 @@ -32,9 +32,9 @@ public class TcpSession extends ProtocolSession { private ChannelHandlerContext ctx; - private final BiConsumer sendDownlinkConsumer; + private final Consumer sendDownlinkConsumer; - private final BiConsumer writeAndFlushConsumer; + private final Consumer writeAndFlushConsumer; private final AtomicInteger sequenceNumber = new AtomicInteger(0); @@ -64,8 +64,8 @@ public class TcpSession extends ProtocolSession { } public TcpSession(String protocolName, - BiConsumer sendDownlinkConsumer, - BiConsumer writeAndFlushConsumer) { + Consumer sendDownlinkConsumer, + Consumer writeAndFlushConsumer) { super(protocolName); this.sendDownlinkConsumer = sendDownlinkConsumer; this.writeAndFlushConsumer = writeAndFlushConsumer; @@ -73,7 +73,7 @@ public class TcpSession extends ProtocolSession { @Override public void onDownlink(DownlinkRestMessage downlinkMsg) { - sendDownlinkConsumer.accept(ctx, downlinkMsg); + sendDownlinkConsumer.accept(downlinkMsg); } @Override @@ -85,6 +85,6 @@ public class TcpSession extends ProtocolSession { } public void writeAndFlush(ByteBuf byteBuf) { - writeAndFlushConsumer.accept(ctx, byteBuf); + writeAndFlushConsumer.accept(byteBuf); } } From 698f4f9b6d88263a86510d30321a9f7dde8a6ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 11 Oct 2024 14:37:42 +0800 Subject: [PATCH 022/102] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/infrastructure/util/config/ShardingThreadPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java index 5bf3513..8b5119c 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -79,7 +79,7 @@ public class ShardingThreadPool { int partition = hash(hashFunction, hashKey); EXECUTOR_SERVICE_MAP.computeIfAbsent(partition % parallelism, - p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads-" + p))) + p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads" + p))) .execute(runnable); } } \ No newline at end of file From 62a712ea3651c56b642b17411e0195b9848bd8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 11 Oct 2024 14:40:28 +0800 Subject: [PATCH 023/102] cleanup code --- .../jcpp/infrastructure/queue/common/QueueConstants.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java index c2ac400..509bc52 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java @@ -12,6 +12,4 @@ public final class QueueConstants { public static final String MSG_MD_PREFIX = "jcpp_"; public static final String MSG_MD_TS = "ts"; - - public static final String MSG_MD_PROTOCOL_SESSION_ID = "protocol_session_id"; } \ No newline at end of file From 22c93292d0368a011bdf40d6d7d34b7a25788ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 11 Oct 2024 15:27:42 +0800 Subject: [PATCH 024/102] cleanup code by sonar --- .../cache/VersionedCaffeineCache.java | 2 +- .../queue/AbstractQueueConsumerTemplate.java | 6 +++--- .../AbstractYunKuaiChongCmdExe.java | 4 ++-- ...nKuaiChongV15ProtocolMessageProcessor.java | 20 +++++++++++-------- .../YunKuaiChongV150BmsHandshakeULCmd.java | 4 ++-- ...uaiChongV150QueryPricingModelAckDLCmd.java | 4 ---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java index 9e7ede6..84d25ba 100644 --- a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/VersionedCaffeineCache.java @@ -12,7 +12,7 @@ import java.io.Serializable; public abstract class VersionedCaffeineCache extends CaffeineTransactionalCache implements VersionedCache { - public VersionedCaffeineCache(CacheManager cacheManager, String cacheName) { + protected VersionedCaffeineCache(CacheManager cacheManager, String cacheName) { super(cacheManager, cacheName); } diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java index 88914e8..07375ce 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/AbstractQueueConsumerTemplate.java @@ -60,7 +60,7 @@ public abstract class AbstractQueueConsumerTemplate imple List records; long startNanos = System.nanoTime(); if (stopped) { - log.error("poll invoked but consumer stopped for topic " + topic, new RuntimeException("stacktrace")); + log.error("poll invoked but consumer stopped for topic {}", topic, new RuntimeException("stacktrace")); return emptyList(); } if (!subscribed && partitions == null && subscribeQueue.isEmpty()) { @@ -68,7 +68,7 @@ public abstract class AbstractQueueConsumerTemplate imple } if (consumerLock.isLocked()) { - log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); + log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic {}", topic, new RuntimeException("stacktrace")); } consumerLock.lock(); @@ -132,7 +132,7 @@ public abstract class AbstractQueueConsumerTemplate imple @Override public void commit() { if (consumerLock.isLocked()) { - log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); + log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic {}", topic, new RuntimeException("stacktrace")); } consumerLock.lock(); try { diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java index 0d98b61..84ea84f 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java @@ -57,10 +57,10 @@ public class AbstractYunKuaiChongCmdExe { return TOP_BYTE; case PEAK: return PEAK_BYTE; - case FLAT: - return FLAT_BYTE; case VALLEY: return VALLEY_BYTE; + default: + return FLAT_BYTE; } } } diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java index 04d18c8..66f4b8b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java @@ -34,8 +34,8 @@ import static sanbing.jcpp.infrastructure.util.codec.ByteUtil.checkCrcSum; @Slf4j public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProcessor { - private final Map UPLINK_CMD_EXE_MAP = new ConcurrentHashMap<>(); - private final Map DOWNLINK_CMD_EXE_MAP = new ConcurrentHashMap<>(); + private final Map uplinkCmdExeMap = new ConcurrentHashMap<>(); + private final Map downlinkCmdExeMap = new ConcurrentHashMap<>(); public YunKuaiChongV15ProtocolMessageProcessor(Forwarder forwarder, ProtocolContext protocolContext) { super(forwarder, protocolContext); @@ -46,8 +46,10 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc byte cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); try { YunKuaiChongUplinkCmdExe yunKuaiChongUplinkCmdExe = (YunKuaiChongUplinkCmdExe) clazz.getDeclaredConstructor().newInstance(); - UPLINK_CMD_EXE_MAP.put(cmd, yunKuaiChongUplinkCmdExe); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + uplinkCmdExeMap.put(cmd, yunKuaiChongUplinkCmdExe); + } catch (InstantiationException | + IllegalAccessException | + InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } @@ -58,8 +60,10 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc byte cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); try { YunKuaiChongDownlinkCmdExe yunKuaiChongDownlinkCmdExe = (YunKuaiChongDownlinkCmdExe) clazz.getDeclaredConstructor().newInstance(); - DOWNLINK_CMD_EXE_MAP.put(cmd, yunKuaiChongDownlinkCmdExe); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + downlinkCmdExeMap.put(cmd, yunKuaiChongDownlinkCmdExe); + } catch (InstantiationException | + IllegalAccessException | + InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } @@ -184,7 +188,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc } private void exeCmd(YunKuaiChongUplinkMessage message, TcpSession session) { - YunKuaiChongUplinkCmdExe uplinkCmdExe = UPLINK_CMD_EXE_MAP.get((byte) message.getCmd()); + YunKuaiChongUplinkCmdExe uplinkCmdExe = uplinkCmdExeMap.get((byte) message.getCmd()); if (uplinkCmdExe == null) { @@ -197,7 +201,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc } private void exeCmd(YunKuaiChongDwonlinkMessage message, TcpSession session) { - YunKuaiChongDownlinkCmdExe downlinkCmdExe = DOWNLINK_CMD_EXE_MAP.get((byte) message.getCmd()); + YunKuaiChongDownlinkCmdExe downlinkCmdExe = downlinkCmdExeMap.get((byte) message.getCmd()); if (downlinkCmdExe == null) { diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java index b5126a3..36f35f8 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java @@ -44,12 +44,12 @@ public class YunKuaiChongV150BmsHandshakeULCmd extends YunKuaiChongUplinkCmdExe byte[] pileCodeBytes = new byte[7]; byteBuf.readBytes(pileCodeBytes); String pileCode = BCDUtil.toString(pileCodeBytes); - additionalInfo.put("桩编号", tradeNo); + additionalInfo.put("桩编号", pileCode); // 3.抢号 byte gunCodeByte = byteBuf.readByte(); String gunCode = BCDUtil.toString(gunCodeByte); - additionalInfo.put("抢号", tradeNo); + additionalInfo.put("抢号", gunCode); // 4.BMS 通信协议版本号 byte[] bmsConnectVersionBytes = new byte[3]; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java index 3094e62..8c6a732 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java @@ -7,7 +7,6 @@ package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import lombok.extern.slf4j.Slf4j; -import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import sanbing.jcpp.proto.gen.ProtocolProto.FlagPriceProto; import sanbing.jcpp.proto.gen.ProtocolProto.PeriodProto; import sanbing.jcpp.proto.gen.ProtocolProto.PricingModelProto; @@ -16,7 +15,6 @@ import sanbing.jcpp.protocol.ProtocolContext; import sanbing.jcpp.protocol.listener.tcp.TcpSession; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; -import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; import java.math.BigDecimal; @@ -46,8 +44,6 @@ public class YunKuaiChongV150QueryPricingModelAckDLCmd extends YunKuaiChongDownl QueryPricingResponse queryPricingResponse = yunKuaiChongDwonlinkMessage.getMsg().getQueryPricingResponse(); - YunKuaiChongUplinkMessage requestData = JacksonUtil.fromBytes(yunKuaiChongDwonlinkMessage.getMsg().getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class); - long pricingId = queryPricingResponse.getPricingId(); String pileCode = queryPricingResponse.getPileCode(); PricingModelProto pricingModel = queryPricingResponse.getPricingModel(); From d5166f873e1413eda017087f633bdccf33d777bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 11 Oct 2024 17:57:32 +0800 Subject: [PATCH 025/102] =?UTF-8?q?=E5=8D=95=E4=BD=93=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 14d120d..f09509a 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -49,7 +49,7 @@ metrics: # 应用程序服务注册中心配置 zk: - enabled: "${ZOOKEEPER_ENABLED:true}" + enabled: "${ZOOKEEPER_ENABLED:false}" url: "${ZOOKEEPER_URL:zookeeper:2181}" retry-interval-ms: "${ZOOKEEPER_RETRY_INTERVAL_MS:3000}" connection-timeout-ms: "${ZOOKEEPER_CONNECTION_TIMEOUT_MS:3000}" From 2cd7c9c5736f86c5aba8276e254e243ff9fe5682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 11 Oct 2024 20:47:34 +0800 Subject: [PATCH 026/102] =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f312fe7..64f5f67 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,11 @@ ------------------------------ #### 代码编写指南 -TODO +##### 教程以视频形式在自媒体平台陆续发布,敬请关注 +![抖音二维码](https://foruda.gitee.com/images/1728650678915895016/b2219d50_10604541.png) + +##### 我建了个微信技术交流群 +![微信交流群](https://foruda.gitee.com/images/1728650750348929723/c8b55505_10604541.png) ------------------------------ #### 参与贡献 From c94110d02ae459d185031d2f711509dddecfe08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Sat, 12 Oct 2024 16:47:27 +0800 Subject: [PATCH 027/102] =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BA=91=E5=BF=AB?= =?UTF-8?q?=E5=85=851.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- .../src/main/resources/app-service.yml | 39 +++++++ .../resources/app-service-test.properties | 3 +- .../jcpp/protocol/domain/DownlinkCmdEnum.java | 2 + .../src/main/resources/protocol-service.yml | 37 +++++++ .../AbstractYunKuaiChongCmdExe.java | 8 +- .../YunKuaiChongDownlinkCmdExe.java | 2 +- ...YunKuaiChongProtocolMessageProcessor.java} | 38 +++---- .../YunKuaiChongUplinkCmdExe.java | 2 +- .../annotation/YunKuaiChongCmd.java | 2 +- .../YunKuaiChongV150BmsHandshakeULCmd.java | 2 +- .../YunKuaiChongV150HeartbeatULCmd.java | 4 +- .../YunKuaiChongV150LoginAckDLCmd.java | 6 +- .../YunKuaiChongV150LoginULCmd.java | 2 +- ...uaiChongV150QueryPricingModelAckDLCmd.java | 4 +- ...unKuaiChongV150QueryPricingModelULCmd.java | 2 +- .../YunKuaiChongV150RealTimeDataULCmd.java | 2 +- .../YunKuaiChongV150RemoteStartDLCmd.java | 4 +- ...unKuaiChongV150RemoteStartResultULCmd.java | 2 +- .../YunKuaiChongV150RemoteStopDLCmd.java | 4 +- ...YunKuaiChongV150RemoteStopResultULCmd.java | 2 +- ...nKuaiChongV150SetPricingModelAckULCmd.java | 4 +- .../YunKuaiChongV150SetPricingModelDLCmd.java | 6 +- ...uaiChongV150TransactionRecordAckDLCmd.java | 4 +- ...unKuaiChongV150TransactionRecordULCmd.java | 2 +- ...aiChongV150VerifyPricingModelAckDLCmd.java | 4 +- ...nKuaiChongV150VerifyPricingModelULCmd.java | 2 +- .../YunKuaiChongDownlinkCmdEnum.java} | 11 +- .../YunkuaichongV150ProtocolBootstrap.java | 3 +- .../YunkuaichongV160ProtocolBootstrap.java | 46 ++++++++ ...KuaiChongV160RemoteParallelStartDLCmd.java | 80 ++++++++++++++ ...ongV160RemoteParallelStartResultULCmd.java | 104 ++++++++++++++++++ 32 files changed, 373 insertions(+), 66 deletions(-) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/YunKuaiChongV15ProtocolMessageProcessor.java => YunKuaiChongProtocolMessageProcessor.java} (80%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150BmsHandshakeULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150HeartbeatULCmd.java (95%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150LoginAckDLCmd.java (94%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150LoginULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150QueryPricingModelAckDLCmd.java (96%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150QueryPricingModelULCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150RealTimeDataULCmd.java (99%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150RemoteStartDLCmd.java (93%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150RemoteStartResultULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150RemoteStopDLCmd.java (90%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150RemoteStopResultULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150SetPricingModelAckULCmd.java (93%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150SetPricingModelDLCmd.java (93%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150TransactionRecordAckDLCmd.java (92%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150TransactionRecordULCmd.java (99%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150VerifyPricingModelAckDLCmd.java (94%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/cmd => commoncmd}/YunKuaiChongV150VerifyPricingModelULCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{v150/enums/YunKuaiChongV150DownlinkCmdEnum.java => enums/YunKuaiChongDownlinkCmdEnum.java} (68%) create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/YunkuaichongV160ProtocolBootstrap.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartResultULCmd.java diff --git a/README.md b/README.md index 64f5f67..7c26330 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ ------------------------------ #### 当前支持的充电桩协议 -| 协议名 | 版本号 | -|---|---| -| 云快充 | 1.5.0 | +| 协议名 | 版本号 | +|---|------------| +| 云快充 | 1.5.0、1.6.0 | ------------------------------ #### 充电桩协议文档 diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index f09509a..7eb821b 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -206,6 +206,45 @@ service: linger-ms: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_LINGER_MS:0}" buffer-memory: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_BUFFER_MEMORY:33554432}" other-properties: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" + yunkuaichongV160: + enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" + listener: + tcp: + bind-address: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_BIND_ADDRESS:0.0.0.0}" + bind-port: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_BIND_PORT:38002}" + boss-group-thread_count: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_BOSS_GROUP_THREADS:4}" + worker-group-thread-count: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_WORKER_GROUP_THREADS:16}" + so-keep-alive: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_KEEPALIVE:true}" + so-backlog: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_BACKLOG:128}" + so-rcvbuf: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_RCVBUF:65536}" + so-sndbuf: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_SNDBUF:65536}" + nodelay: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_NODELAY:true}" + handler: + idle-timeout-seconds: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_HANDLER_IDLE_TIMEOUT_SECONDS:600}" + max_connections: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_HANDLER_MAX_CONNECTIONS:100000}" + # 默认为二进制类型的拆包器 + # 可选JSON类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV160_NETTY_HANDLER_BINARY_CONFIGURATION:type:JSON}" + # 可选纯文本类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV160_NETTY_HANDLER_BINARY_CONFIGURATION:type:TEXT;maxFrameLength:128;stripDelimiter:true;messageSeparator:null;charsetName:UTF-8}" + configuration: "${PROTOCOLS_YUNKUAICHONGV160_NETTY_HANDLER_BINARY_CONFIGURATION:type:BINARY;decoder:sanbing.jcpp.protocol.listener.tcp.decoder.JCPPLengthFieldBasedFrameDecoder;byteOrder:LITTLE_ENDIAN;head:68;lengthFieldOffset:1;lengthFieldLength:1;lengthAdjustment:2;initialBytesToStrip:0}" + forwarder: + # 如果是单体服务,可选kafka、memory,未来计划扩展RocketMQ, GRpc、REST + type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_TYPE:memory}" + memory: + topic: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_MEMORY_TOPIC:protocol_uplink}" + kafka: + topic: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_TOPIC:protocol_uplink}" + jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 + # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + acks: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ACKS:1}" + # # 可选 protobuf(推荐)、json + encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" + retries: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_RETRIES:1}" + compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + batch-size: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_BATCH_SIZE:16384}" + linger-ms: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_LINGER_MS:0}" + buffer-memory: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_BUFFER_MEMORY:33554432}" + other-properties: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" thread-pool: sharding: diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties index b397d7b..75de971 100644 --- a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -1,3 +1,4 @@ redis.connection.type=cluster redis.cluster.nodes=10.102.12.101:30700,10.102.12.101:32027,10.102.12.101:30767,10.102.12.101:30250,10.102.12.101:30612,10.102.12.101:32303 -service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 \ No newline at end of file +service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 +service.protocols.yunkuaichongV160.listener.tcp.bind-port=0 \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java index 4fbe3f5..e5407f8 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java @@ -20,4 +20,6 @@ public enum DownlinkCmdEnum { REMOTE_START_CHARGING, TRANSACTION_RECORD, + + REMOTE_PARALLEL_START_CHARGING, } \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 284a6bb..ea92de8 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -81,6 +81,43 @@ service: linger-ms: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_LINGER_MS:0}" buffer-memory: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_BUFFER_MEMORY:33554432}" other-properties: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" + yunkuaichongV160: + enabled: "${PROTOCOLS_YUNKUAICHONGV160_ENABLED:true}" + listener: + tcp: + bind-address: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_BIND_ADDRESS:0.0.0.0}" + bind-port: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_BIND_PORT:38002}" + boss-group-thread_count: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_BOSS_GROUP_THREADS:4}" + worker-group-thread-count: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_WORKER_GROUP_THREADS:16}" + so-keep-alive: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_KEEPALIVE:true}" + so-backlog: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_BACKLOG:128}" + so-rcvbuf: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_RCVBUF:65536}" + so-sndbuf: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_SO_SNDBUF:65536}" + nodelay: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_NODELAY:true}" + handler: + idle-timeout-seconds: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_HANDLER_IDLE_TIMEOUT_SECONDS:600}" + max_connections: "${PROTOCOLS_YUNKUAICHONGV160_LISTENER_TCP_HANDLER_MAX_CONNECTIONS:100000}" + # 默认为二进制类型的拆包器 + # 可选JSON类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV160_NETTY_HANDLER_BINARY_CONFIGURATION:type:JSON}" + # 可选纯文本类型的拆包器 "${PROTOCOLS_YUNKUAICHONGV160_NETTY_HANDLER_BINARY_CONFIGURATION:type:TEXT;maxFrameLength:128;stripDelimiter:true;messageSeparator:null;charsetName:UTF-8}" + configuration: "${PROTOCOLS_YUNKUAICHONGV160_NETTY_HANDLER_BINARY_CONFIGURATION:type:BINARY;decoder:sanbing.jcpp.protocol.listener.tcp.decoder.JCPPLengthFieldBasedFrameDecoder;byteOrder:LITTLE_ENDIAN;head:68;lengthFieldOffset:1;lengthFieldLength:1;lengthAdjustment:2;initialBytesToStrip:0}" + forwarder: + # 作为前置服务单独启时可选:kafka、kafka-sharding,未来计划扩展RocketMQ, GRpc、REST + type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_TYPE:kafka}" + kafka: + topic: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_TOPIC:protocol_uplink}" + jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 + # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + acks: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ACKS:1}" + # # 可选 protobuf(推荐)、json + encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" + retries: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_RETRIES:1}" + compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + batch-size: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_BATCH_SIZE:16384}" + linger-ms: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_LINGER_MS:0}" + buffer-memory: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_BUFFER_MEMORY:33554432}" + other-properties: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" # 应用程序服务注册中心配置 zk: diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java index 84ea84f..c36a94e 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/AbstractYunKuaiChongCmdExe.java @@ -12,7 +12,7 @@ import sanbing.jcpp.infrastructure.util.codec.BCDUtil; import sanbing.jcpp.proto.gen.ProtocolProto; import sanbing.jcpp.protocol.listener.tcp.TcpSession; import sanbing.jcpp.protocol.listener.tcp.enums.SequenceNumberLength; -import sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum; +import sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum; import java.math.BigDecimal; import java.math.RoundingMode; @@ -98,7 +98,7 @@ public class AbstractYunKuaiChongCmdExe { } - protected byte[] encode(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, + protected byte[] encode(YunKuaiChongDownlinkCmdEnum downlinkCmd, int seqNo, int encryptionFlag, ByteBuf msgBody) { @@ -120,7 +120,7 @@ public class AbstractYunKuaiChongCmdExe { return toBytes(response); } - protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, + protected void encodeAndWriteFlush(YunKuaiChongDownlinkCmdEnum downlinkCmd, int seqNo, int encryptionFlag, ByteBuf msgBody, @@ -131,7 +131,7 @@ public class AbstractYunKuaiChongCmdExe { tcpSession.writeAndFlush(Unpooled.copiedBuffer(encode)); } - protected void encodeAndWriteFlush(YunKuaiChongV150DownlinkCmdEnum downlinkCmd, + protected void encodeAndWriteFlush(YunKuaiChongDownlinkCmdEnum downlinkCmd, ByteBuf msgBody, TcpSession tcpSession) { diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java index 41c5f97..4ad8414 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDownlinkCmdExe.java @@ -10,7 +10,7 @@ import sanbing.jcpp.protocol.listener.tcp.TcpSession; /** * @author baigod */ -public abstract class YunKuaiChongDownlinkCmdExe extends AbstractYunKuaiChongCmdExe{ +public abstract class YunKuaiChongDownlinkCmdExe extends AbstractYunKuaiChongCmdExe { public abstract void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java similarity index 80% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java index 66f4b8b..e932c99 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong; import cn.hutool.core.util.ClassUtil; import io.netty.buffer.ByteBuf; @@ -17,12 +17,8 @@ import sanbing.jcpp.protocol.domain.ListenerToHandlerMsg; import sanbing.jcpp.protocol.domain.SessionToHandlerMsg; import sanbing.jcpp.protocol.forwarder.Forwarder; import sanbing.jcpp.protocol.listener.tcp.TcpSession; -import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; -import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; -import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; -import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; -import sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum; +import sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum; import java.lang.reflect.InvocationTargetException; import java.util.Map; @@ -33,17 +29,17 @@ import java.util.concurrent.ConcurrentHashMap; import static sanbing.jcpp.infrastructure.util.codec.ByteUtil.checkCrcSum; @Slf4j -public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProcessor { - private final Map uplinkCmdExeMap = new ConcurrentHashMap<>(); - private final Map downlinkCmdExeMap = new ConcurrentHashMap<>(); +public class YunKuaiChongProtocolMessageProcessor extends ProtocolMessageProcessor { + private final Map uplinkCmdExeMap = new ConcurrentHashMap<>(); + private final Map downlinkCmdExeMap = new ConcurrentHashMap<>(); - public YunKuaiChongV15ProtocolMessageProcessor(Forwarder forwarder, ProtocolContext protocolContext) { + public YunKuaiChongProtocolMessageProcessor(Forwarder forwarder, ProtocolContext protocolContext) { super(forwarder, protocolContext); Set> cmdClasses = ClassUtil.scanPackageByAnnotation(ClassUtil.getPackage(this.getClass()), YunKuaiChongCmd.class); cmdClasses.stream().filter(YunKuaiChongUplinkCmdExe.class::isAssignableFrom) .forEach(clazz -> { - byte cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); + int cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); try { YunKuaiChongUplinkCmdExe yunKuaiChongUplinkCmdExe = (YunKuaiChongUplinkCmdExe) clazz.getDeclaredConstructor().newInstance(); uplinkCmdExeMap.put(cmd, yunKuaiChongUplinkCmdExe); @@ -57,7 +53,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc cmdClasses.stream().filter(YunKuaiChongDownlinkCmdExe.class::isAssignableFrom) .forEach(clazz -> { - byte cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); + int cmd = clazz.getAnnotation(YunKuaiChongCmd.class).value(); try { YunKuaiChongDownlinkCmdExe yunKuaiChongDownlinkCmdExe = (YunKuaiChongDownlinkCmdExe) clazz.getDeclaredConstructor().newInstance(); downlinkCmdExeMap.put(cmd, yunKuaiChongDownlinkCmdExe); @@ -138,15 +134,15 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc JCPPPair checkResult = checkCrcSum(checkData, checkSum); - if (!checkResult.getFirst()) { + if (Boolean.FALSE.equals(checkResult.getFirst())) { csTemp.writeBytes(byCheckSum); checkSum = csTemp.readUnsignedShortLE(); checkResult = checkCrcSum(checkData, checkSum); - log.info("云快充V1.5检验和 第二次检查: checkResult:{}, checkSum:{}", checkResult, checkSum); + log.info("云快充检验和 第二次检查: checkResult:{}, checkSum:{}", checkResult, checkSum); } - if (!checkResult.getFirst()) { - log.info("云快充V1.5检验和不一致两次不通过 不处理! CMD:{},校验域:{},正确校验和:{}", frameType, checkSum, checkResult.getSecond()); + if (Boolean.FALSE.equals(checkResult.getFirst())) { + log.info("云快充检验和不一致两次不通过 不处理! CMD:{},校验域:{},正确校验和:{}", frameType, checkSum, checkResult.getSecond()); return; } @@ -169,7 +165,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc DownlinkRestMessage protocolDownlinkMsg = sessionToHandlerMsg.downlinkMsg(); - int cmd = YunKuaiChongV150DownlinkCmdEnum.valueOf(protocolDownlinkMsg.getDownlinkCmd()).getCmd(); + int cmd = YunKuaiChongDownlinkCmdEnum.valueOf(protocolDownlinkMsg.getDownlinkCmd()).getCmd(); YunKuaiChongDwonlinkMessage message = new YunKuaiChongDwonlinkMessage(); message.setId(new UUID(protocolDownlinkMsg.getMessageIdMSB(), protocolDownlinkMsg.getMessageIdLSB())); @@ -188,11 +184,11 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc } private void exeCmd(YunKuaiChongUplinkMessage message, TcpSession session) { - YunKuaiChongUplinkCmdExe uplinkCmdExe = uplinkCmdExeMap.get((byte) message.getCmd()); + YunKuaiChongUplinkCmdExe uplinkCmdExe = uplinkCmdExeMap.get(message.getCmd()); if (uplinkCmdExe == null) { - log.info("[{}] 云快充V1.5协议接收到未知的上行指令 {}", session, message.getCmd()); + log.info("[{}] 云快充协议接收到未知的上行指令 {}", session, message.getCmd()); return; } @@ -201,11 +197,11 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc } private void exeCmd(YunKuaiChongDwonlinkMessage message, TcpSession session) { - YunKuaiChongDownlinkCmdExe downlinkCmdExe = downlinkCmdExeMap.get((byte) message.getCmd()); + YunKuaiChongDownlinkCmdExe downlinkCmdExe = downlinkCmdExeMap.get(message.getCmd()); if (downlinkCmdExe == null) { - log.info("[{}] 云快充V1.5协议接收到未知的下行指令 {}", session, message.getCmd()); + log.info("[{}] 云快充协议接收到未知的下行指令 {}", session, message.getCmd()); return; } diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java index 450dd81..52cb0d9 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongUplinkCmdExe.java @@ -15,7 +15,7 @@ import sanbing.jcpp.protocol.listener.tcp.TcpSession; * @author baigod */ @Slf4j -public abstract class YunKuaiChongUplinkCmdExe extends AbstractYunKuaiChongCmdExe{ +public abstract class YunKuaiChongUplinkCmdExe extends AbstractYunKuaiChongCmdExe { public abstract void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java index d86321b..515b6b0 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/annotation/YunKuaiChongCmd.java @@ -14,6 +14,6 @@ import java.lang.annotation.*; @Documented public @interface YunKuaiChongCmd { - byte value(); + int value(); } \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150BmsHandshakeULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150BmsHandshakeULCmd.java index 36f35f8..ce9bbe2 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150BmsHandshakeULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import cn.hutool.core.util.HexUtil; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150HeartbeatULCmd.java similarity index 95% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150HeartbeatULCmd.java index d35735b..5d6d331 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150HeartbeatULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; @@ -18,7 +18,7 @@ import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.HEARTBEAT; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.HEARTBEAT; /** * 云快充1.5.0 充电桩心跳包 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginAckDLCmd.java similarity index 94% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginAckDLCmd.java index d7d0b70..8930cae 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -28,8 +28,8 @@ import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.PR import static sanbing.jcpp.protocol.domain.SessionCloseReason.MANUALLY; import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE; import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.LOGIN_ACK; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.SYNC_TIME; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.LOGIN_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.SYNC_TIME; /** * 云快充1.5.0登录认证应答 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginULCmd.java index 0971188..83eb7b5 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java similarity index 96% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java index 8c6a732..36a4a40 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -23,7 +23,7 @@ import java.util.List; import java.util.Map; import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag.*; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.QUERY_PRICING_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.QUERY_PRICING_ACK; /** * 计费模型请求应答 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelULCmd.java index 3741366..779ce3d 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RealTimeDataULCmd.java similarity index 99% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RealTimeDataULCmd.java index 8aa463c..671aa57 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartDLCmd.java similarity index 93% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartDLCmd.java index 9846f10..c37b671 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -16,7 +16,7 @@ import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.REMOTE_START_CHARGING; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.REMOTE_START_CHARGING; /** * 云快充1.5.0 运营平台远程控制启机 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartResultULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartResultULCmd.java index 6dca0e4..6a2eead 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartResultULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopDLCmd.java similarity index 90% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopDLCmd.java index 67c4793..04c53c3 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -14,7 +14,7 @@ import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.REMOTE_START_CHARGING; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.REMOTE_START_CHARGING; /** * 云快充1.5.0 运营平台远程停机 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopResultULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopResultULCmd.java index dbda740..c00a2cf 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopResultULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelAckULCmd.java similarity index 93% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelAckULCmd.java index 8e04b1b..cb74462 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelAckULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -16,7 +16,7 @@ import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.QUERY_PRICING_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.QUERY_PRICING_ACK; /** * 云快充1.5.0 计费模型应答 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelDLCmd.java similarity index 93% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelDLCmd.java index 44b066e..97b8b07 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -23,8 +23,8 @@ import java.util.List; import java.util.Map; import static sanbing.jcpp.proto.gen.ProtocolProto.PricingModelFlag.*; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.QUERY_PRICING_ACK; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.SET_PRICING; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.QUERY_PRICING_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.SET_PRICING; /** * 云快充1.5.0 计费模型设置 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordAckDLCmd.java similarity index 92% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordAckDLCmd.java index 417db72..3e9e845 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -18,7 +18,7 @@ import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE; import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.VERIFY_PRICING_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.VERIFY_PRICING_ACK; /** * 云快充1.5.0 交易记录确认 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordULCmd.java similarity index 99% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordULCmd.java index 8fbdf8b..1661cbf 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java similarity index 94% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java index fbe0a34..f92586a 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -20,7 +20,7 @@ import java.util.Arrays; import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE; import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE; -import static sanbing.jcpp.protocol.yunkuaichong.v150.enums.YunKuaiChongV150DownlinkCmdEnum.VERIFY_PRICING_ACK; +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.VERIFY_PRICING_ACK; /** * 云快充1.5.0计费模型验证请求应答 diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelULCmd.java index 24a6d31..d968999 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +package sanbing.jcpp.protocol.yunkuaichong.commoncmd; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/enums/YunKuaiChongDownlinkCmdEnum.java similarity index 68% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/enums/YunKuaiChongDownlinkCmdEnum.java index 5dab6f1..c6b16e7 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/enums/YunKuaiChongV150DownlinkCmdEnum.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/enums/YunKuaiChongDownlinkCmdEnum.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150.enums; +package sanbing.jcpp.protocol.yunkuaichong.enums; import lombok.AllArgsConstructor; import lombok.Getter; @@ -12,7 +12,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum YunKuaiChongV150DownlinkCmdEnum { +public enum YunKuaiChongDownlinkCmdEnum { LOGIN_ACK(0x02), @@ -30,9 +30,10 @@ public enum YunKuaiChongV150DownlinkCmdEnum { REMOTE_STOP_CHARGING(0x36), - TRANSACTION_RECORD(0x40) - ; + TRANSACTION_RECORD(0x40), - private int cmd; + REMOTE_PARALLEL_START_CHARGING(0xA4); + + private final Integer cmd; } \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java index 6fb79d9..03e497b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java @@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j; import sanbing.jcpp.infrastructure.util.annotation.ProtocolComponent; import sanbing.jcpp.protocol.ProtocolBootstrap; import sanbing.jcpp.protocol.ProtocolMessageProcessor; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongProtocolMessageProcessor; import static sanbing.jcpp.protocol.yunkuaichong.v150.YunkuaichongV150ProtocolBootstrap.PROTOCOL_NAME; @@ -38,7 +39,7 @@ public class YunkuaichongV150ProtocolBootstrap extends ProtocolBootstrap { @Override protected ProtocolMessageProcessor messageProcessor() { - return new YunKuaiChongV15ProtocolMessageProcessor(forwarder, protocolContext); + return new YunKuaiChongProtocolMessageProcessor(forwarder, protocolContext); } diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/YunkuaichongV160ProtocolBootstrap.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/YunkuaichongV160ProtocolBootstrap.java new file mode 100644 index 0000000..85793fb --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/YunkuaichongV160ProtocolBootstrap.java @@ -0,0 +1,46 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v160; + +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.annotation.ProtocolComponent; +import sanbing.jcpp.protocol.ProtocolBootstrap; +import sanbing.jcpp.protocol.ProtocolMessageProcessor; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongProtocolMessageProcessor; + +import static sanbing.jcpp.protocol.yunkuaichong.v160.YunkuaichongV160ProtocolBootstrap.PROTOCOL_NAME; + +/** + * @author baigod + */ + +@ProtocolComponent(PROTOCOL_NAME) +@Slf4j +public class YunkuaichongV160ProtocolBootstrap extends ProtocolBootstrap { + + public static final String PROTOCOL_NAME = "yunkuaichongV160"; + + @Override + protected String getProtocolName() { + return PROTOCOL_NAME; + } + + @Override + protected void _init() { + // do nothing + } + + @Override + protected void _destroy() { + // do nothing + } + + @Override + protected ProtocolMessageProcessor messageProcessor() { + return new YunKuaiChongProtocolMessageProcessor(forwarder, protocolContext); + } + + +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java new file mode 100644 index 0000000..4a2449a --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java @@ -0,0 +1,80 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v160.cmd; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.proto.gen.ProtocolProto; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.REMOTE_START_CHARGING; + +/** + * 云快充1.6.0 运营平台远程控制并充启机 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0xA4) +public class YunKuaiChongV160RemoteParallelStartDLCmd extends YunKuaiChongDownlinkCmdExe { + + static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.6.0运营平台远程控制并充启机", tcpSession); + + if (!yunKuaiChongDwonlinkMessage.getMsg().hasRemoteStartChargingRequest()) { + return; + } + + ProtocolProto.RemoteStartChargingRequest remoteStartChargingRequest = yunKuaiChongDwonlinkMessage.getMsg().getRemoteStartChargingRequest(); + String pileCode = remoteStartChargingRequest.getPileCode(); + String gunCode = remoteStartChargingRequest.getGunCode(); + String tradeNo = remoteStartChargingRequest.getTradeNo(); + int limitYuan = remoteStartChargingRequest.getLimitYuan(); + + byte[] cardNo = encodeCardNo(tradeNo); + + ByteBuf msgBody = Unpooled.buffer(44); + // 交易流水号 + msgBody.writeBytes(encodeTradeNo(tradeNo)); + // 桩编码 + msgBody.writeBytes(encodePileCode(pileCode)); + // 枪号 + msgBody.writeBytes(encodeGunCode(gunCode)); + // 逻辑卡号 BCD码 + msgBody.writeBytes(cardNo); + // 物理卡号 + msgBody.writeBytes(cardNo); + // 账户余额 + msgBody.writeIntLE(limitYuan); + // 并充序号 + msgBody.writeBytes(BCDUtil.toBytes(LocalDateTime.now().format(dateTimeFormatter))); + + encodeAndWriteFlush(REMOTE_START_CHARGING, + msgBody, + tcpSession); + } + + /** + * 用交易流水号做卡号 + */ + private static byte[] encodeCardNo(String tradeNo) { + tradeNo = StringUtils.right(tradeNo, 16); + tradeNo = StringUtils.leftPad(tradeNo, 16, '0'); + return BCDUtil.toBytes(tradeNo); + } +} \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartResultULCmd.java new file mode 100644 index 0000000..cc994c2 --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartResultULCmd.java @@ -0,0 +1,104 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.yunkuaichong.v160.cmd; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.extern.slf4j.Slf4j; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; +import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.RemoteStartChargingResponse; +import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.protocol.ProtocolContext; +import sanbing.jcpp.protocol.listener.tcp.TcpSession; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe; +import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage; +import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; + +/** + * 云快充1.6.0 远程并充启机命令回复 + * + * @author baigod + */ +@Slf4j +@YunKuaiChongCmd(0xA3) +public class YunKuaiChongV160RemoteParallelStartResultULCmd extends YunKuaiChongUplinkCmdExe { + + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.6.远程并充启机命令回复", tcpSession); + ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + + // 从Tracer总获取当前时间 + long ts = TracerContextUtil.getCurrentTracer().getTracerTs(); + + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + + // 1.交易流水号 + byte[] tradeNoBytes = new byte[16]; + byteBuf.readBytes(tradeNoBytes); + String tradeNo = decodeTradeNo(tradeNoBytes); + + // 2.桩编号 + byte[] pileCodeBytes = new byte[7]; + byteBuf.readBytes(pileCodeBytes); + String pileCode = BCDUtil.toString(pileCodeBytes); + + // 3.抢号 + byte gunCodeByte = byteBuf.readByte(); + String gunCode = BCDUtil.toString(gunCodeByte); + + // 4.命令执行结果 0x00失败 0x01成功 + boolean isSuccess = (byteBuf.readByte() == 0x01); + + // 5.失败原因 0无 1设备编码不匹配 2枪已在充电 3设备故障 4设备离线 5未插枪 + byte failReasonByte = byteBuf.readByte(); + String failReason = mapFailCode(failReasonByte); + + // 6.主辅枪标记 0x00 主枪 0x01辅枪 + byte gunFlagByte = byteBuf.readByte(); + String gunFlag = BCDUtil.toString(gunFlagByte); + additionalInfo.put("主辅枪标记", gunFlag); + + // 7.并充序号,0xA4下发的并充序号 + byte[] parallelSeqNoBytes = new byte[6]; + byteBuf.readBytes(parallelSeqNoBytes); + String parallelSeqNo = BCDUtil.toString(parallelSeqNoBytes); + additionalInfo.put("并充序号", parallelSeqNo); + + RemoteStartChargingResponse remoteStartChargingResponse = RemoteStartChargingResponse.newBuilder() + .setTs(ts) + .setPileCode(pileCode) + .setGunCode(gunCode) + .setTradeNo(tradeNo) + .setSuccess(isSuccess) + .setFailReason(failReason) + .setAdditionalInfo(additionalInfo.toString()) + .build(); + + // 转发到后端 + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage) + .setRemoteStartChargingResponse(remoteStartChargingResponse) + .build(); + + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + } + + public static String mapFailCode(byte failCode) { + return switch (failCode) { + case 0x00 -> "无"; + case 0x01 -> "设备编号不匹配"; + case 0x02 -> "枪已在充电"; + case 0x03 -> "设备故障"; + case 0x04 -> "设备离线"; + case 0x05 -> "未插枪"; + case 0x33 -> "充电失败"; // 充电失败或其他相关信息 + case 0x34 -> "待启充"; // 示例,处理收到充电命令 + default -> "未知错误代码"; + }; + } +} \ No newline at end of file From 54fcb66f5ebdf4dc815656eb0362972879b533cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 10:08:03 +0800 Subject: [PATCH 028/102] code cleanup by sonar --- .../queue/discovery/HashPartitionProvider.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java index 87db05b..98001bf 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/HashPartitionProvider.java @@ -39,12 +39,12 @@ public class HashPartitionProvider implements PartitionProvider { @Value("${queue.partitions.hash_function_name:murmur3_128}") private String hashFunctionName; - private final ConcurrentMap partitionTopicsMap = new ConcurrentHashMap<>(); - private final ConcurrentMap partitionSizesMap = new ConcurrentHashMap<>(); + private final Map partitionTopicsMap = new ConcurrentHashMap<>(); + private final Map partitionSizesMap = new ConcurrentHashMap<>(); private HashFunction hashFunction; - protected volatile ConcurrentMap> myPartitions = new ConcurrentHashMap<>(); + protected Map> myPartitions = new ConcurrentHashMap<>(); @Resource private ApplicationEventPublisher applicationEventPublisher; @@ -118,7 +118,7 @@ public class HashPartitionProvider implements PartitionProvider { } }); - final ConcurrentMap> oldPartitions = myPartitions; + final Map> oldPartitions = myPartitions; myPartitions = newPartitions; log.info("Current Server responsible partitions: {}", myPartitions); From f414cc7e131f70caad4bd168dc286ac15ec3e1d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 15:54:55 +0800 Subject: [PATCH 029/102] =?UTF-8?q?=E6=96=B0=E5=A2=9Epostgresql=2017=20?= =?UTF-8?q?=E7=9A=84docker=20compose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.postgres.yml | 33 +++++++++++++++++++ .../src/main/resources/app-service.yml | 4 +-- .../resources/app-service-test.properties | 3 +- .../src/main/resources/protocol-service.yml | 2 +- 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 docker/docker-compose.postgres.yml diff --git a/docker/docker-compose.postgres.yml b/docker/docker-compose.postgres.yml new file mode 100644 index 0000000..be5ac05 --- /dev/null +++ b/docker/docker-compose.postgres.yml @@ -0,0 +1,33 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +volumes: + postgresql_data: {} + +networks: + sanbing-network: + driver: bridge + name: sanbing-network + ipam: + config: + - subnet: 10.10.0.0/24 + +services: + postgres: + image: registry.cn-hangzhou.aliyuncs.com/sanbing/postgresql:17 + restart: always + networks: + - sanbing-network + ports: + - "5432:5432" + environment: + - 'POSTGRES_DB=jcpp' + - 'POSTGRES_PASSWORD=postgres' + - 'POSTGRESQL_MAX_CONNECTIONS=1000' + - 'POSTGRESQL_DEFAULT_TRANSACTION_ISOLATION=read committed' + - 'TZ=Asia/Shanghai' + volumes: + - postgresql_data:/bitnami/postgresql + - ./schema/schema-postgres.sql:/docker-entrypoint-initdb.d/init.sql \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index f09509a..c5f1cc4 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -22,7 +22,7 @@ spring: name: "${SPRING_APPLICATION_NAME:java-charge-point-server}" datasource: driver-class-name: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" - url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://10.102.12.102:30135/jcpp}" + url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://postgres:5432/jcpp}" username: "${SPRING_DATASOURCE_USERNAME:postgres}" password: "${SPRING_DATASOURCE_PASSWORD:postgres}" hikari: @@ -196,7 +196,7 @@ service: topic: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_TOPIC:protocol_uplink}" jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 - bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ACKS:1}" # # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties index b397d7b..13313a4 100644 --- a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -1,3 +1,2 @@ -redis.connection.type=cluster -redis.cluster.nodes=10.102.12.101:30700,10.102.12.101:32027,10.102.12.101:30767,10.102.12.101:30250,10.102.12.101:30612,10.102.12.101:32303 +spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/jcpp service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 284a6bb..596fa0b 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -71,7 +71,7 @@ service: topic: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_TOPIC:protocol_uplink}" jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 - bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ACKS:1}" # # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" From f6bc74c83d47ea30edd9838a563da72760a02ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 16:30:25 +0800 Subject: [PATCH 030/102] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=8B=E8=A1=8C?= =?UTF-8?q?=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/dal/mapper/GunMapperIT.java | 14 +++++++++++--- .../protocol/adapter/DownlinkControllerIT.java | 17 ++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java index 5eb5aac..af650ff 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java @@ -12,6 +12,7 @@ import sanbing.jcpp.app.dal.config.ibatis.enums.GunOptStatusEnum; import sanbing.jcpp.app.dal.config.ibatis.enums.GunRunStatusEnum; import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; import sanbing.jcpp.app.dal.entity.Gun; +import sanbing.jcpp.app.dal.entity.Pile; import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import java.time.LocalDateTime; @@ -37,9 +38,13 @@ class GunMapperIT extends AbstractTestBase { UUID.fromString("3f3a61e9-de55-4177-9b4e-3a1d8c529890"), UUID.fromString("cf1a8970-5aa9-4636-a76e-d6bcf98b4a07") }; + @Resource GunMapper gunMapper; + @Resource + PileMapper pileMapper; + @Test void curdTest() { gunMapper.delete(Wrappers.lambdaQuery()); @@ -47,13 +52,16 @@ class GunMapperIT extends AbstractTestBase { for (int i = 0; i < NORMAL_PILE_ID.length; i++) { UUID pileId = NORMAL_PILE_ID[i]; UUID gunId = NORMAL_GUN_ID[i]; + + Pile pile = pileMapper.selectById(pileId); + Gun gun = Gun.builder() .id(gunId) .createdTime(LocalDateTime.now()) .additionalInfo(JacksonUtil.newObjectNode()) - .gunNo("01") - .gunName("三丙的1号枪") - .gunCode("20231212000001-" + (i + 1)) + .gunNo("02") + .gunName(pile.getPileName() + "的2号枪") + .gunCode(pile.getPileCode() + "-02") .stationId(NORMAL_STATION_ID) .pileId(pileId) .ownerId(NORMAL_USER_ID) diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java index f02982c..8346abc 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java @@ -66,7 +66,6 @@ class DownlinkControllerIT extends AbstractProtocolTestBase { binaryHandlerConfig.getLengthFieldOffset(), binaryHandlerConfig.getLengthFieldLength(), binaryHandlerConfig.getLengthAdjustment(), binaryHandlerConfig.getInitialBytesToStrip()); - group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group) @@ -80,7 +79,6 @@ class DownlinkControllerIT extends AbstractProtocolTestBase { .addLast(new SimpleChannelInboundHandler<>() { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { - log.info("接收到下行报文:{}", msg); } }); @@ -105,14 +103,19 @@ class DownlinkControllerIT extends AbstractProtocolTestBase { // 先发送一段登录 channel.writeAndFlush(Unpooled.wrappedBuffer(HexUtil.decodeHex("6822001900012023121200001001011047562e393572313300898604d11722d0348606024E87"))).sync(); - // 停一会等注册完成 todo 也可以读下行消息判断是否登录成功 - Thread.sleep(1000); + // 一直检查等待会话注册完成 + UUID sessionId; + while (true) { + if (sessionRegistryProvider.getSessionCache().asMap().values().stream().findFirst().isPresent()) { + ProtocolSession protocolSession = sessionRegistryProvider.getSessionCache().asMap().values().stream().findFirst().get(); + sessionId = protocolSession.getId(); + break; + } + Thread.sleep(100); + } UUID messageId = UUID.randomUUID(); - ProtocolSession protocolSession = sessionRegistryProvider.getSessionCache().asMap().values().stream().findFirst().get(); - UUID sessionId = protocolSession.getId(); UUID requestId = UUID.randomUUID(); - // 创建 DownlinkRestMessage 实例 String pileCode = "20231212000010"; DownlinkRestMessage downlinkMsg = DownlinkRestMessage.newBuilder() From 93d8ec9b6e1b28ccf718ff57b635744403f39738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 17:14:28 +0800 Subject: [PATCH 031/102] =?UTF-8?q?=E5=89=8D=E7=BD=AE=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=85=B3=E9=97=ADzk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 596fa0b..2b57f32 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -84,7 +84,7 @@ service: # 应用程序服务注册中心配置 zk: - enabled: "${ZOOKEEPER_ENABLED:true}" + enabled: "${ZOOKEEPER_ENABLED:false}" url: "${ZOOKEEPER_URL:zookeeper:2181}" retry-interval-ms: "${ZOOKEEPER_RETRY_INTERVAL_MS:3000}" connection-timeout-ms: "${ZOOKEEPER_CONNECTION_TIMEOUT_MS:3000}" From 86614a5a929578a25bca819e1de707a7509c757a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 17:30:31 +0800 Subject: [PATCH 032/102] =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/infrastructure/util/config/ShardingThreadPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java index 8b5119c..29b6084 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -79,7 +79,7 @@ public class ShardingThreadPool { int partition = hash(hashFunction, hashKey); EXECUTOR_SERVICE_MAP.computeIfAbsent(partition % parallelism, - p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads" + p))) + p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads-%d" + p))) .execute(runnable); } } \ No newline at end of file From 103f782ddf6f4e8f7b2526d54679e40d2e9c2efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 17:46:22 +0800 Subject: [PATCH 033/102] =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/util/config/ShardingThreadPool.java | 10 +++++----- .../v150/YunKuaiChongV15ProtocolMessageProcessor.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java index 29b6084..c334e7e 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -38,7 +38,7 @@ public class ShardingThreadPool { private HashFunction hashFunction; - private final Map EXECUTOR_SERVICE_MAP = new ConcurrentHashMap<>(8); + private final Map executorServiceMap = new ConcurrentHashMap<>(8); @PostConstruct public void init() { @@ -47,7 +47,7 @@ public class ShardingThreadPool { @PreDestroy public void destroy() { - for (ExecutorService executorService : EXECUTOR_SERVICE_MAP.values()) { + for (ExecutorService executorService : executorServiceMap.values()) { executorService.shutdownNow(); log.info("Sharding Thread [{}] Shutdown completed.", executorService); } @@ -55,7 +55,7 @@ public class ShardingThreadPool { @Scheduled(fixedDelayString = "${thread-pool.sharding.stats-print-interval-ms:10000}") public void printStats() { - EXECUTOR_SERVICE_MAP.forEach((k, v) -> { + executorServiceMap.forEach((k, v) -> { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) v; @@ -78,8 +78,8 @@ public class ShardingThreadPool { public void execute(UUID hashKey, TracerRunnable runnable) { int partition = hash(hashFunction, hashKey); - EXECUTOR_SERVICE_MAP.computeIfAbsent(partition % parallelism, - p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads-%d" + p))) + executorServiceMap.computeIfAbsent(Math.abs(partition % parallelism), + p -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("sharding-threads-" + p))) .execute(runnable); } } \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java index 66f4b8b..c73ad66 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java @@ -102,7 +102,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc int seqNo = in.readUnsignedShortLE(); // 加密标志 - int encrpyFlag = in.readUnsignedByte(); + int encryptFlag = in.readUnsignedByte(); // 帧类型标志 int frameType = in.readUnsignedByte(); @@ -154,7 +154,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc message.setHead(startFlag); message.setDataLength(dataLength); message.setSequenceNumber(seqNo); - message.setEncryptionFlag(encrpyFlag); + message.setEncryptionFlag(encryptFlag); message.setCmd(frameType); message.setMsgBody(msgBody); message.setCheckSum(checkSum); From e75ac9436508c3161747e1fd8f47fcfdeb7178ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 14 Oct 2024 17:53:42 +0800 Subject: [PATCH 034/102] =?UTF-8?q?=E5=A2=9E=E5=8A=A0todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/service/impl/DefaultPileProtocolService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index 87d114c..f8387d3 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -185,6 +185,8 @@ public class DefaultPileProtocolService implements PileProtocolService { public void postGunRunStatus(UplinkQueueMessage uplinkQueueMessage, Callback callback) { log.info("接收到充电桩上报的电桩状态 {}", uplinkQueueMessage); + // TODO 处理相关业务逻辑 + callback.onSuccess(); } @@ -192,6 +194,8 @@ public class DefaultPileProtocolService implements PileProtocolService { public void postChargingProgress(UplinkQueueMessage uplinkQueueMessage, Callback callback) { log.info("接收到充电桩上报的充电进度 {}", uplinkQueueMessage); + // TODO 处理相关业务逻辑 + callback.onSuccess(); } @@ -199,6 +203,8 @@ public class DefaultPileProtocolService implements PileProtocolService { public void onSetPricingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback) { log.info("接收到充电桩上费率下发反馈 {}", uplinkQueueMessage); + // TODO 处理相关业务逻辑 + callback.onSuccess(); } @@ -206,6 +212,8 @@ public class DefaultPileProtocolService implements PileProtocolService { public void onRemoteStartChargingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback) { log.info("接收到充电桩启动结果反馈 {}", uplinkQueueMessage); + // TODO 处理相关业务逻辑 + callback.onSuccess(); } @@ -213,6 +221,8 @@ public class DefaultPileProtocolService implements PileProtocolService { public void onRemoteStopChargingResponse(UplinkQueueMessage uplinkQueueMessage, Callback callback) { log.info("接收到充电桩停止结果反馈 {}", uplinkQueueMessage); + // TODO 处理相关业务逻辑 + callback.onSuccess(); } From 4e18b78edec3e38e685a65a66fd8472243bcc35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 15 Oct 2024 09:47:23 +0800 Subject: [PATCH 035/102] =?UTF-8?q?=E6=89=A9=E5=B1=95=E4=BA=91=E5=BF=AB?= =?UTF-8?q?=E5=85=851.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{commoncmd => v150}/YunKuaiChongV150BmsHandshakeULCmd.java | 2 +- .../{commoncmd => v150}/YunKuaiChongV150HeartbeatULCmd.java | 2 +- .../{commoncmd => v150}/YunKuaiChongV150LoginAckDLCmd.java | 2 +- .../{commoncmd => v150}/YunKuaiChongV150LoginULCmd.java | 2 +- .../YunKuaiChongV150QueryPricingModelAckDLCmd.java | 2 +- .../YunKuaiChongV150QueryPricingModelULCmd.java | 2 +- .../{commoncmd => v150}/YunKuaiChongV150RealTimeDataULCmd.java | 2 +- .../{commoncmd => v150}/YunKuaiChongV150RemoteStartDLCmd.java | 2 +- .../YunKuaiChongV150RemoteStartResultULCmd.java | 2 +- .../{commoncmd => v150}/YunKuaiChongV150RemoteStopDLCmd.java | 2 +- .../YunKuaiChongV150RemoteStopResultULCmd.java | 2 +- .../YunKuaiChongV150SetPricingModelAckULCmd.java | 2 +- .../YunKuaiChongV150SetPricingModelDLCmd.java | 2 +- .../YunKuaiChongV150TransactionRecordAckDLCmd.java | 2 +- .../YunKuaiChongV150TransactionRecordULCmd.java | 2 +- .../YunKuaiChongV150VerifyPricingModelAckDLCmd.java | 2 +- .../YunKuaiChongV150VerifyPricingModelULCmd.java | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150BmsHandshakeULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150HeartbeatULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150LoginAckDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150LoginULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150QueryPricingModelAckDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150QueryPricingModelULCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150RealTimeDataULCmd.java (99%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150RemoteStartDLCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150RemoteStartResultULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150RemoteStopDLCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150RemoteStopResultULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150SetPricingModelAckULCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150SetPricingModelDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150TransactionRecordAckDLCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150TransactionRecordULCmd.java (99%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150VerifyPricingModelAckDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/{commoncmd => v150}/YunKuaiChongV150VerifyPricingModelULCmd.java (97%) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150BmsHandshakeULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150BmsHandshakeULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150BmsHandshakeULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150BmsHandshakeULCmd.java index ce9bbe2..a7058e0 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150BmsHandshakeULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150BmsHandshakeULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import cn.hutool.core.util.HexUtil; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150HeartbeatULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150HeartbeatULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java index 5d6d331..40fc271 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150HeartbeatULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginAckDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginAckDLCmd.java index 8930cae..d52e4d8 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java index 83eb7b5..5ef9cce 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150LoginULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelAckDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelAckDLCmd.java index 36a4a40..3824162 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelULCmd.java index 779ce3d..4863012 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150QueryPricingModelULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RealTimeDataULCmd.java similarity index 99% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RealTimeDataULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RealTimeDataULCmd.java index 671aa57..54e4911 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RealTimeDataULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java index c37b671..9088b49 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartResultULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartResultULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartResultULCmd.java index 6a2eead..798aa85 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStartResultULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartResultULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopDLCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopDLCmd.java index 04c53c3..6b2685a 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopResultULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopResultULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopResultULCmd.java index c00a2cf..5ad8201 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150RemoteStopResultULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopResultULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelAckULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelAckULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelAckULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelAckULCmd.java index cb74462..677fea9 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelAckULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelAckULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelDLCmd.java index 97b8b07..18f97cd 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150SetPricingModelDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordAckDLCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordAckDLCmd.java index 3e9e845..5b2812f 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordULCmd.java similarity index 99% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordULCmd.java index 1661cbf..8d6c6d9 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150TransactionRecordULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelAckDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelAckDLCmd.java index f92586a..e7f33cb 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelULCmd.java index d968999..5a55748 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/commoncmd/YunKuaiChongV150VerifyPricingModelULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.commoncmd; +package sanbing.jcpp.protocol.yunkuaichong.v150; import com.fasterxml.jackson.databind.node.ObjectNode; From 107a0b9b3a38340ce17dffcf089028f9a812379d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 15 Oct 2024 09:50:55 +0800 Subject: [PATCH 036/102] typo --- jcpp-protocol-yunkuaichong/{READMD.me => READMD.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename jcpp-protocol-yunkuaichong/{READMD.me => READMD.md} (100%) diff --git a/jcpp-protocol-yunkuaichong/READMD.me b/jcpp-protocol-yunkuaichong/READMD.md similarity index 100% rename from jcpp-protocol-yunkuaichong/READMD.me rename to jcpp-protocol-yunkuaichong/READMD.md From b6193d2d7a11abde0181c06b73b6da80d9794722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 15 Oct 2024 15:35:03 +0800 Subject: [PATCH 037/102] npe --- .../java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java index af650ff..d2c7099 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/GunMapperIT.java @@ -12,9 +12,9 @@ import sanbing.jcpp.app.dal.config.ibatis.enums.GunOptStatusEnum; import sanbing.jcpp.app.dal.config.ibatis.enums.GunRunStatusEnum; import sanbing.jcpp.app.dal.config.ibatis.enums.OwnerTypeEnum; import sanbing.jcpp.app.dal.entity.Gun; -import sanbing.jcpp.app.dal.entity.Pile; import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; +import java.text.DecimalFormat; import java.time.LocalDateTime; import java.util.UUID; @@ -42,9 +42,6 @@ class GunMapperIT extends AbstractTestBase { @Resource GunMapper gunMapper; - @Resource - PileMapper pileMapper; - @Test void curdTest() { gunMapper.delete(Wrappers.lambdaQuery()); @@ -53,15 +50,13 @@ class GunMapperIT extends AbstractTestBase { UUID pileId = NORMAL_PILE_ID[i]; UUID gunId = NORMAL_GUN_ID[i]; - Pile pile = pileMapper.selectById(pileId); - Gun gun = Gun.builder() .id(gunId) .createdTime(LocalDateTime.now()) .additionalInfo(JacksonUtil.newObjectNode()) .gunNo("02") - .gunName(pile.getPileName() + "的2号枪") - .gunCode(pile.getPileCode() + "-02") + .gunName(String.format("三丙家的%d号充电桩", i + 1) + "的2号枪") + .gunCode("202312120000" + new DecimalFormat("00").format(i + 1) + "-02") .stationId(NORMAL_STATION_ID) .pileId(pileId) .ownerId(NORMAL_USER_ID) From 26588112a371dba54f4ac1a4f4e6891a45080108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 15 Oct 2024 16:18:01 +0800 Subject: [PATCH 038/102] =?UTF-8?q?=E7=94=9F=E6=88=9010=E4=B8=87=E6=A0=B9?= =?UTF-8?q?=E6=A1=A9=E5=81=9A=E5=8E=8B=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/dal/mapper/PileMapperIT.java | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java index 46168eb..e00a37d 100644 --- a/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java +++ b/jcpp-app-bootstrap/src/test/java/sanbing/jcpp/app/dal/mapper/PileMapperIT.java @@ -17,7 +17,10 @@ import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import java.text.DecimalFormat; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import static sanbing.jcpp.app.dal.mapper.StationMapperIT.NORMAL_STATION_ID; import static sanbing.jcpp.app.dal.mapper.UserMapperIT.NORMAL_USER_ID; @@ -68,7 +71,36 @@ class PileMapperIT extends AbstractTestBase { pileMapper.insertOrUpdate(pile); log.info("{}", pileMapper.selectById(pileId)); - } } + + @Test + void generate100KPiles() { + AtomicInteger number = new AtomicInteger(1); + for (int i = 0; i < 100; i++) { + List piles = new ArrayList<>(); + for (int j = 0; j < 1000; j++, number.incrementAndGet()) { + Pile pile = Pile.builder() + .id(UUID.randomUUID()) + .createdTime(LocalDateTime.now()) + .additionalInfo(JacksonUtil.newObjectNode()) + .pileName(String.format("三丙家的%d号充电桩", number.get())) + .pileCode("20241015" + new DecimalFormat("000000").format(number.get())) + .protocol("yunkuaichongV150") + .stationId(NORMAL_STATION_ID) + .ownerId(NORMAL_USER_ID) + .ownerType(OwnerTypeEnum.C) + .brand("星星") + .model("10A") + .manufacturer("星星") + .status(PileStatusEnum.IDLE) + .type(PileTypeEnum.AC) + .build(); + piles.add(pile); + } + pileMapper.insert(piles); + } + + log.info("{}", pileMapper.selectCount(Wrappers.lambdaQuery())); + } } \ No newline at end of file From 2780298b60d0e284571ac55a16d3597365162edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 15 Oct 2024 17:40:45 +0800 Subject: [PATCH 039/102] cleanup --- .../java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java index cdeabf8..c770fc4 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolUplinkMsg.java @@ -13,8 +13,8 @@ public record ProtocolUplinkMsg(SocketAddress address, UUID id, T data, int s @Override public String toString() { - if (data instanceof byte[]) { - return ByteBufUtil.hexDump((byte[]) data); + if (data instanceof byte[] bytes) { + return ByteBufUtil.hexDump(bytes); } else { return data.toString(); } From 72baea61e50df881b943409e2e39eaff575fdf2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 09:37:36 +0800 Subject: [PATCH 040/102] =?UTF-8?q?=E4=BF=AE=E6=94=B9kafka=20topic?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/app-service.yml | 2 +- .../ProtocolUplinkConsumerService.java | 18 +++++++++++------- .../src/main/resources/protocol-service.yml | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index c5f1cc4..9bb5108 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -93,7 +93,7 @@ queue: auto-offset-reset: "${QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" other-inline: "${QUEUE_KAFKA_OTHER_PROPERTIES:}" topic-properties: - app: "${QUEUE_KAFKA_APP_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + app: "${QUEUE_KAFKA_APP_TOPIC_PROPERTIES:retention.ms:86400000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" consumer-stats: enabled: "${QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" print-interval-ms: "${QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java index e7ab6d0..c3c4014 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java @@ -101,6 +101,7 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple } + @Override @PreDestroy public void destroy() { super.destroy(); @@ -132,7 +133,7 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>()); PendingMsgHolder pendingMsgHolder = new PendingMsgHolder(); Future packSubmitFuture = consumersExecutor.submit(new TracerRunnable(() -> - orderedMsgList.forEach((element) -> { + orderedMsgList.forEach(element -> { UUID id = element.getUuid(); ProtoQueueMsg msg = element.getMsg(); tracer(msg); @@ -157,11 +158,11 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple pileProtocolService.onSetPricingResponse(uplinkQueueMsg, callback); } else if (uplinkQueueMsg.hasRemoteStartChargingResponse()) { pileProtocolService.onRemoteStartChargingResponse(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasRemoteStopChargingResponse()) { + } else if (uplinkQueueMsg.hasRemoteStopChargingResponse()) { pileProtocolService.onRemoteStopChargingResponse(uplinkQueueMsg, callback); - } else if(uplinkQueueMsg.hasTransactionRecord()){ + } else if (uplinkQueueMsg.hasTransactionRecord()) { pileProtocolService.onTransactionRecord(uplinkQueueMsg, callback); - }else { + } else { callback.onSuccess(); } @@ -189,7 +190,7 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple } private void tracer(ProtoQueueMsg msg) { - Optional.ofNullable(msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ID)) + if (Optional.ofNullable(msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ID)) .map(tracerId -> { String origin = null; byte[] tracerOrigin = msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN); @@ -205,7 +206,10 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple return TracerContextUtil.newTracer(ByteUtil.bytesToString(tracerId), origin, ts); }) - .orElseGet(TracerContextUtil::newTracer); + .isEmpty()) { + + TracerContextUtil.newTracer(); + } MDCUtils.recordTracer(); } @@ -231,6 +235,6 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple @Setter @Getter private static class PendingMsgHolder { - private volatile UplinkQueueMessage uplinkQueueMessage; + private UplinkQueueMessage uplinkQueueMessage; } } \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 2b57f32..f417977 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -128,7 +128,7 @@ queue: auto-offset-reset: "${QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" other-inline: "${QUEUE_KAFKA_OTHER_PROPERTIES:}" topic-properties: - app: "${QUEUE_KAFKA_APP_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + app: "${QUEUE_KAFKA_APP_TOPIC_PROPERTIES:retention.ms:86400000;segment.bytes:52428800;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" consumer-stats: enabled: "${QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" print-interval-ms: "${QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" From 65084e3269dd961ace3ba5c405687d29cf291c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 09:44:46 +0800 Subject: [PATCH 041/102] =?UTF-8?q?=E4=BF=AE=E6=94=B9log4j2=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/log4j2.xml | 3 ++- jcpp-protocol-bootstrap/src/main/resources/log4j2.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/log4j2.xml b/jcpp-app-bootstrap/src/main/resources/log4j2.xml index 082044b..f53a4fd 100644 --- a/jcpp-app-bootstrap/src/main/resources/log4j2.xml +++ b/jcpp-app-bootstrap/src/main/resources/log4j2.xml @@ -18,7 +18,8 @@ - + + diff --git a/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml b/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml index 34d9fdf..b077425 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml +++ b/jcpp-protocol-bootstrap/src/main/resources/log4j2.xml @@ -18,7 +18,8 @@ - + + From 0122ecf7cd863bcf80589a3e37798763ea4b56fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 09:53:19 +0800 Subject: [PATCH 042/102] =?UTF-8?q?=E6=94=BE=E5=BC=80memory=E9=98=9F?= =?UTF-8?q?=E5=88=97=E6=8C=87=E6=A0=87=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queue/memory/DefaultInMemoryStorage.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java index f61f905..fb02920 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -20,13 +20,11 @@ public final class DefaultInMemoryStorage implements InMemoryStorage { @Override public void printStats() { - if (log.isDebugEnabled()) { - storage.forEach((topic, queue) -> { - if (!queue.isEmpty()) { - log.debug("[{}] Queue Size [{}]", topic, queue.size()); - } - }); - } + storage.forEach((topic, queue) -> { + if (!queue.isEmpty()) { + log.info("[{}] Queue Size [{}]", topic, queue.size()); + } + }); } @Override @@ -45,7 +43,7 @@ public final class DefaultInMemoryStorage implements InMemoryStorage { } @Override - public List get(String topic) throws InterruptedException { + public List get(String topic) throws InterruptedException { final BlockingQueue queue = storage.get(topic); if (queue != null) { final QueueMsg firstMsg = queue.poll(); From 3741273f9b542d88ff8108a36b91304ab68d6a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 10:09:35 +0800 Subject: [PATCH 043/102] =?UTF-8?q?=E6=B6=88=E8=B4=B9=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E6=9B=B4=E6=8D=A2=E4=B8=BA=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sanbing/jcpp/app/service/queue/AbstractConsumerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java index cff3ebd..0b91f94 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/AbstractConsumerService.java @@ -31,7 +31,7 @@ public abstract class AbstractConsumerService extends JCPPApplicationEventListen protected ScheduledExecutorService scheduler; public void init(String prefix) { - this.consumersExecutor = Executors.newCachedThreadPool(JCPPThreadFactory.forName(prefix + "-consumer")); + this.consumersExecutor = JCPPExecutors.newVirtualThreadPool(prefix + "-consumer-virtual"); this.mgmtExecutor = JCPPExecutors.newWorkStealingPool(getMgmtThreadPoolSize(), prefix + "-mgmt"); this.scheduler = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName(prefix + "-consumer-scheduler")); } From a90c94a40c55b2271bdfc2df5f6ad315f305b74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 10:20:35 +0800 Subject: [PATCH 044/102] =?UTF-8?q?=E6=B6=88=E8=B4=B9=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E6=9B=B4=E6=8D=A2=E4=B8=BA=E8=99=9A?= =?UTF-8?q?=E6=8B=9F=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/async/JCPPVirtualThreadFactory.java | 4 +-- .../util/async/JCPPExecutorsTest.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutorsTest.java diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java index 2c88144..4edcc68 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/async/JCPPVirtualThreadFactory.java @@ -4,8 +4,6 @@ */ package sanbing.jcpp.infrastructure.util.async; -import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; - import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; @@ -19,6 +17,6 @@ public class JCPPVirtualThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { - return Thread.ofVirtual().name(namePrefix + "-" + threadNumber.getAndIncrement()).unstarted(new TracerRunnable(r)); + return Thread.ofVirtual().name(namePrefix + "-" + threadNumber.getAndIncrement()).unstarted(r); } } \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutorsTest.java b/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutorsTest.java new file mode 100644 index 0000000..418a630 --- /dev/null +++ b/jcpp-infrastructure-util/src/test/java/sanbing/jcpp/infrastructure/util/async/JCPPExecutorsTest.java @@ -0,0 +1,30 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.infrastructure.util.async; + +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; + +import java.util.concurrent.ExecutorService; + +class JCPPExecutorsTest { + + @Test + void newVirtualThreadPool() { + ExecutorService executorService = JCPPExecutors.newVirtualThreadPool("test-consumer-virtual"); + + TracerContextUtil.newTracer(); + MDCUtils.recordTracer(); + + System.out.println(MDC.get("TRACE_ID")); + + executorService.submit(new TracerRunnable(() -> { + System.out.println(MDC.get("TRACE_ID")); + })); + } +} \ No newline at end of file From 74eeb256e274fb125776d4dd63cd06fb278bf4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 10:57:47 +0800 Subject: [PATCH 045/102] =?UTF-8?q?=E9=99=90=E5=AE=9A=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E9=98=9F=E5=88=97=E5=AE=B9=E9=87=8F=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/queue/memory/DefaultInMemoryStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java index fb02920..05a42ba 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -39,7 +39,7 @@ public final class DefaultInMemoryStorage implements InMemoryStorage { @Override public boolean put(String topic, QueueMsg msg) { - return storage.computeIfAbsent(topic, (t) -> new LinkedBlockingQueue<>()).add(msg); + return storage.computeIfAbsent(topic, t -> new LinkedBlockingQueue<>(100000)).add(msg); } @Override From 5b6583defa44f0adf2120431fae28581362f25fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 11:09:40 +0800 Subject: [PATCH 046/102] =?UTF-8?q?NIO=E7=BA=BF=E7=A8=8B=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java index 91a6cb4..5379903 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpListener.java @@ -41,8 +41,8 @@ public class TcpListener extends Listener { } private void tcpServerBootstrap(TcpCfg tcpCfg) throws InterruptedException { - bossGroup = new NioEventLoopGroup(tcpCfg.getBossGroupThreadCount(), JCPPThreadFactory.forName("tcp-boss")); - workerGroup = new NioEventLoopGroup(tcpCfg.getWorkerGroupThreadCount(), JCPPThreadFactory.forName("tcp-worker")); + bossGroup = new NioEventLoopGroup(tcpCfg.getBossGroupThreadCount(), JCPPThreadFactory.forName("tcp-boss-%d")); + workerGroup = new NioEventLoopGroup(tcpCfg.getWorkerGroupThreadCount(), JCPPThreadFactory.forName("tcp-worker-%d")); ChannelHandlerInitializer channelHandler = ChannelHandlerInitializer.createTcpChannelHandler(tcpCfg, parameter); From e4921faf80d2f685bd2deeced20009bc6d99fe7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 11:36:20 +0800 Subject: [PATCH 047/102] =?UTF-8?q?=E6=97=A5=E5=BF=97=E9=99=8D=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 6 +++--- .../v150/YunKuaiChongV15ProtocolMessageProcessor.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 9bb5108..8d93a32 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -127,12 +127,12 @@ redis: standalone: host: "${REDIS_HOST:redis}" port: "${REDIS_PORT:6379}" - useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:false}" clientName: "${REDIS_CLIENT_NAME:standalone}" commandTimeout: "${REDIS_CLIENT_COMMAND_TIMEOUT:30000}" shutdownTimeout: "${REDIS_CLIENT_SHUTDOWN_TIMEOUT:1000}" readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}" - usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}" + usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:true}" cluster: nodes: "${REDIS_NODES:redis-node-0:6379,redis-node-1:6379,redis-node-2:6379,redis-node-3:6379,redis-node-4:6379,redis-node-5:6379}" max-redirects: "${REDIS_MAX_REDIRECTS:12}" @@ -145,7 +145,7 @@ redis: db: "${REDIS_DB:0}" password: "${REDIS_PASSWORD:sanbing}" pool_config: - maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:256}" maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:false}" diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java index c73ad66..e1719ef 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV15ProtocolMessageProcessor.java @@ -142,7 +142,7 @@ public class YunKuaiChongV15ProtocolMessageProcessor extends ProtocolMessageProc csTemp.writeBytes(byCheckSum); checkSum = csTemp.readUnsignedShortLE(); checkResult = checkCrcSum(checkData, checkSum); - log.info("云快充V1.5检验和 第二次检查: checkResult:{}, checkSum:{}", checkResult, checkSum); + log.debug("云快充V1.5检验和 第二次检查: checkResult:{}, checkSum:{}", checkResult, checkSum); } if (!checkResult.getFirst()) { From 8bde50009202c06a5304b113a620cbd49087ad2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 13:56:37 +0800 Subject: [PATCH 048/102] =?UTF-8?q?=E6=8F=90=E9=AB=98DB=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 8d93a32..f8e82f0 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -27,7 +27,7 @@ spring: password: "${SPRING_DATASOURCE_PASSWORD:postgres}" hikari: leak-detection-threshold: "${SPRING_DATASOURCE_HIKARI_LEAK_DETECTION_THRESHOLD:0}" - maximum-pool-size: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:16}" + maximum-pool-size: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:32}" register-mbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}" mybatis-plus: From 95039219c9c4bcf8018c4a9eafbecf080b281163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 13:58:55 +0800 Subject: [PATCH 049/102] =?UTF-8?q?=E6=A1=A9=E4=BF=A1=E6=81=AF=E7=BC=93?= =?UTF-8?q?=E5=AD=981=E5=A4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index f8e82f0..5d31b18 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -115,7 +115,7 @@ cache: type: "${CACHE_TYPE:caffeine}" # caffeine or redis specs: piles: - timeToLiveInMinutes: "${CACHE_SPECS_PILES_TTL:15}" + timeToLiveInMinutes: "${CACHE_SPECS_PILES_TTL:1440}" maxSize: "${CACHE_SPECS_PILES_MAX_SIZE:1000}" pileSessions: timeToLiveInMinutes: "${CACHE_SPECS_PILE_SESSIONS_TTL:1440}" From f707705bbb9857c65c8d2d02fc5959eac1e8eed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 13:59:17 +0800 Subject: [PATCH 050/102] =?UTF-8?q?=E6=A1=A9=E4=BF=A1=E6=81=AF=E7=BC=93?= =?UTF-8?q?=E5=AD=981=E5=A4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 5d31b18..058b6c1 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -116,7 +116,7 @@ cache: specs: piles: timeToLiveInMinutes: "${CACHE_SPECS_PILES_TTL:1440}" - maxSize: "${CACHE_SPECS_PILES_MAX_SIZE:1000}" + maxSize: "${CACHE_SPECS_PILES_MAX_SIZE:100000}" pileSessions: timeToLiveInMinutes: "${CACHE_SPECS_PILE_SESSIONS_TTL:1440}" maxSize: "${CACHE_SPECS_PILE_SESSIONS_MAX_SIZE:100000}" From 586c338f36f49934961e8ba857adaf0de90de98b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 14:27:10 +0800 Subject: [PATCH 051/102] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 4 ++-- .../infrastructure/queue/memory/DefaultInMemoryStorage.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 058b6c1..7fbfb91 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -145,8 +145,8 @@ redis: db: "${REDIS_DB:0}" password: "${REDIS_PASSWORD:sanbing}" pool_config: - maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:256}" - maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:64}" minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:false}" testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:false}" diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java index 05a42ba..347c88a 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -39,7 +39,7 @@ public final class DefaultInMemoryStorage implements InMemoryStorage { @Override public boolean put(String topic, QueueMsg msg) { - return storage.computeIfAbsent(topic, t -> new LinkedBlockingQueue<>(100000)).add(msg); + return storage.computeIfAbsent(topic, t -> new LinkedBlockingQueue<>(10000)).add(msg); } @Override From 7022dec3d6ec2d6e800ebc9a639d0c8ac187d0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 14:41:47 +0800 Subject: [PATCH 052/102] log --- .../protocol/listener/tcp/handler/ConnectionLimitHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java index 9508e26..6714e96 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/handler/ConnectionLimitHandler.java @@ -42,14 +42,14 @@ public class ConnectionLimitHandler extends ChannelInboundHandlerAdapter { public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); channelGroup.add(ctx.channel()); - log.info("[{}]{} channelActive 当前连接数管道数 {} / {}",protocolName, ctx.channel(), channelGroup.size(), maxConnections); + log.info("[{}]{} channelActive 当前连接数 {} / {}",protocolName, ctx.channel(), channelGroup.size(), maxConnections); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); channelGroup.remove(ctx.channel()); - log.info("[{}]{} channelInactive 当前连接数管道数 {} / {}",protocolName, ctx.channel(), channelGroup.size(), maxConnections); + log.info("[{}]{} channelInactive 当前连接数 {} / {}",protocolName, ctx.channel(), channelGroup.size(), maxConnections); } @Override From 5b7d42da9ebe814faae51b0a42f05d290b204303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 14:59:21 +0800 Subject: [PATCH 053/102] =?UTF-8?q?=E6=97=A5=E5=BF=97=E9=99=8D=E7=BA=A7=20?= =?UTF-8?q?log4j2=20=E6=9C=89=E9=98=BB=E5=A1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/service/impl/DefaultDownlinkCallService.java | 2 +- .../jcpp/app/service/impl/DefaultPileProtocolService.java | 4 ++-- .../sanbing/jcpp/protocol/adapter/DownlinkController.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java index 90c1f76..3c55ada 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java @@ -49,7 +49,7 @@ public class DefaultDownlinkCallService implements DownlinkCallService { if (serviceInfoProvider.isMonolith()) { downlinkController.onDownlink(downlinkMessageBuilder.build()) - .setResultHandler(result -> log.info("下行消息发送完成")); + .setResultHandler(result -> log.debug("下行消息发送完成")); } else { try { diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index f8387d3..e5853ae 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -48,7 +48,7 @@ public class DefaultPileProtocolService implements PileProtocolService { @Override public void pileLogin(UplinkQueueMessage uplinkQueueMessage, Callback callback) { - log.info("接收到桩登录事件 {}", uplinkQueueMessage); + log.debug("接收到桩登录事件 {}", uplinkQueueMessage); LoginRequest loginRequest = uplinkQueueMessage.getLoginRequest(); @@ -56,7 +56,7 @@ public class DefaultPileProtocolService implements PileProtocolService { String pileCode = loginRequest.getPileCode(); - log.info("查询到充电桩信息 {}", pile); + log.debug("查询到充电桩信息 {}", pile); // 构造下行回复 DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, loginRequest.getPileCode()); diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java index facf5ea..ce54453 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java @@ -38,7 +38,7 @@ public class DownlinkController { @PostMapping(value = "/onDownlink", consumes = "application/x-protobuf", produces = "application/x-protobuf") public DeferredResult> onDownlink(@RequestBody DownlinkRestMessage downlinkMsg) { - log.info("收到REST下行请求 {}", downlinkMsg); + log.debug("收到REST下行请求 {}", downlinkMsg); final DeferredResult> response = new DeferredResult<>(onDownlinkTimeout, ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).build()); @@ -55,7 +55,7 @@ public class DownlinkController { response.setResult(ResponseEntity.status(HttpStatus.OK).build()); } else { - log.warn("下发报文时Session未找到 sessionId: {}", protocolSessionId); + log.info("下发报文时Session未找到 sessionId: {}", protocolSessionId); response.setResult(ResponseEntity.status(HttpStatus.NOT_FOUND).body("Protocol Session not found for ID:" + protocolSessionId)); } From 6c3b5c46bfdb75f4f814554e08a04f5242d91375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 15:21:06 +0800 Subject: [PATCH 054/102] =?UTF-8?q?=E4=B8=8D=E4=BF=9D=E5=AD=98pileId?= =?UTF-8?q?=E7=BB=B4=E5=BA=A6=E7=9A=84=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/cache/session/PileSessionCacheKey.java | 13 ++----------- .../service/impl/DefaultPileProtocolService.java | 1 - 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java index e18f0e4..8d6f5a6 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/cache/session/PileSessionCacheKey.java @@ -7,35 +7,26 @@ package sanbing.jcpp.app.service.cache.session; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.RequiredArgsConstructor; import java.io.Serializable; -import java.util.Optional; -import java.util.UUID; /** * @author baigod */ @Getter @EqualsAndHashCode -@RequiredArgsConstructor @Builder public class PileSessionCacheKey implements Serializable { - private final UUID pileId; private final String pileCode; - public PileSessionCacheKey(UUID pileId) { - this(pileId, null); - } - public PileSessionCacheKey(String pileCode) { - this(null, pileCode); + this.pileCode = pileCode; } @Override public String toString() { - return Optional.ofNullable(pileId).map(UUID::toString).orElse(pileCode); + return pileCode; } } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index e5853ae..10fc0dd 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -108,7 +108,6 @@ public class DefaultPileProtocolService implements PileProtocolService { pileSession.setRemoteAddress(remoteAddress); pileSession.setNodeId(nodeId); pileSession.setNodeWebapiIpPort(nodeWebapiIpPort); - pileSessionCache.put(new PileSessionCacheKey(pile.getId()), pileSession); pileSessionCache.put(new PileSessionCacheKey(pile.getPileCode()), pileSession); } From 7d8a072084531ac06f2f474cfa22d9e8aa71b16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 15:27:35 +0800 Subject: [PATCH 055/102] =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=86=8D=E9=99=8D?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java | 2 +- .../yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java index d7d0b70..74874ac 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java @@ -42,7 +42,7 @@ public class YunKuaiChongV150LoginAckDLCmd extends YunKuaiChongDownlinkCmdExe { @Override public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) { - log.info("{} 云快充1.5.0登录认证应答", tcpSession); + log.debug("{} 云快充1.5.0登录认证应答", tcpSession); if (!yunKuaiChongDwonlinkMessage.getMsg().hasLoginResponse()) { return; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java index 0971188..63b5739 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java @@ -29,7 +29,7 @@ public class YunKuaiChongV150LoginULCmd extends YunKuaiChongUplinkCmdExe { @Override public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { - log.info("{} 云快充1.5.0登录认证请求", tcpSession); + log.debug("{} 云快充1.5.0登录认证请求", tcpSession); ByteBuf byteBuf = Unpooled.copiedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); ObjectNode additionalInfo = JacksonUtil.newObjectNode(); From 73bc580a0a281372caa76ab8a6f385ccad80694b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Wed, 16 Oct 2024 16:15:59 +0800 Subject: [PATCH 056/102] =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E9=98=9F=E5=88=97?= =?UTF-8?q?=E5=AE=B9=E9=87=8F=E5=8F=AF=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 3 ++- .../infrastructure/queue/memory/DefaultInMemoryStorage.java | 6 +++++- .../queue/provider/InMemoryAppQueueFactory.java | 2 +- .../src/main/resources/protocol-service.yml | 3 --- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 7fbfb91..7662d41 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -63,7 +63,8 @@ queue: type: "${QUEUE_TYPE:memory}" partitions: hash_function_name: "${QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - in_memory: + in-memory: + queue-capacity: "${QUEUE_IN_MEMORY_QUEUE_CAPACITY:100000}" stats: print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" kafka: diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java index 347c88a..fd06cff 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -5,6 +5,7 @@ package sanbing.jcpp.infrastructure.queue.memory; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import sanbing.jcpp.infrastructure.queue.QueueMsg; @@ -18,6 +19,9 @@ import java.util.concurrent.LinkedBlockingQueue; public final class DefaultInMemoryStorage implements InMemoryStorage { private final ConcurrentHashMap> storage = new ConcurrentHashMap<>(); + @Value("${queue.in-memory.queue-capacity:100000}") + private int queueCapacity; + @Override public void printStats() { storage.forEach((topic, queue) -> { @@ -39,7 +43,7 @@ public final class DefaultInMemoryStorage implements InMemoryStorage { @Override public boolean put(String topic, QueueMsg msg) { - return storage.computeIfAbsent(topic, t -> new LinkedBlockingQueue<>(10000)).add(msg); + return storage.computeIfAbsent(topic, t -> new LinkedBlockingQueue<>(queueCapacity)).add(msg); } @Override diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java index 82cb1ad..21719f9 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java @@ -40,7 +40,7 @@ public class InMemoryAppQueueFactory implements AppQueueFactory { return new InMemoryQueueProducer<>(storage, topic); } - @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") + @Scheduled(fixedRateString = "${queue.in-memory.stats.print-interval-ms:60000}") private void printInMemoryStats() { storage.printStats(); } diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index f417977..7fd56f0 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -98,9 +98,6 @@ queue: type: "${QUEUE_TYPE:kafka}" partitions: hash_function_name: "${QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - in_memory: - stats: - print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" kafka: bootstrap-servers: "${KAFKA_SERVERS:kafka:9092}" ssl: From 2b143f10afc8e5ceab1323d0638251c41ad2a95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Mon, 21 Oct 2024 16:44:06 +0800 Subject: [PATCH 057/102] =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E9=98=9F=E5=88=97?= =?UTF-8?q?=E5=8F=AA=E8=83=BD=E7=9B=B4=E6=8E=A5=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/service/impl/DefaultDownlinkCallService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java index 3c55ada..a0a801d 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java @@ -6,6 +6,7 @@ package sanbing.jcpp.app.service.impl; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -44,9 +45,12 @@ public class DefaultDownlinkCallService implements DownlinkCallService { @Resource TransactionalCache pileSessionCache; + @Value("${cache.type}") + private String cacheType; + @Override public void sendDownlinkMessage(DownlinkRestMessage.Builder downlinkMessageBuilder, String pileCode) { - if (serviceInfoProvider.isMonolith()) { + if (serviceInfoProvider.isMonolith() && "caffeine".equalsIgnoreCase(cacheType)) { downlinkController.onDownlink(downlinkMessageBuilder.build()) .setResultHandler(result -> log.debug("下行消息发送完成")); From fb0834ce1d47561fe3966c21b15c76a5d3ceed9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 22 Oct 2024 10:58:31 +0800 Subject: [PATCH 058/102] =?UTF-8?q?=E4=B8=8B=E5=8F=91=E5=AF=B9=E6=97=B6?= =?UTF-8?q?=E9=9A=8F=E6=9C=BA7~8=E5=B0=8F=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java index 74874ac..cb5b1cf 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java @@ -4,6 +4,7 @@ */ package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; +import cn.hutool.core.util.RandomUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import lombok.extern.slf4j.Slf4j; @@ -95,7 +96,7 @@ public class YunKuaiChongV150LoginAckDLCmd extends YunKuaiChongDownlinkCmdExe { log.info("{} 云快充1.5.0开始注册定时对时任务", tcpSession); return PROTOCOL_SESSION_SCHEDULED.scheduleAtFixedRate(() -> syncTime(tcpSession, pileCodeBytes, requestData), - 0, 8, TimeUnit.HOURS); + 0, RandomUtil.randomInt(420, 480), TimeUnit.MINUTES); } ); } From 45da17b220bbd524a5a95c7ddcb3f46a31f94d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 22 Oct 2024 11:16:12 +0800 Subject: [PATCH 059/102] =?UTF-8?q?=E8=BD=AC=E5=8F=91=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E9=87=8F=E6=8C=87=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/sanbing/jcpp/protocol/forwarder/Forwarder.java | 7 ++++++- .../sanbing/jcpp/protocol/forwarder/KafkaForwarder.java | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java index 7cd2c22..9aa2e9b 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java @@ -51,7 +51,7 @@ public abstract class Forwarder { protected final boolean isMonolith; protected QueueProducer> producer; - public Forwarder(String protocolName, StatsFactory statsFactory, PartitionProvider partitionProvider, ServiceInfoProvider serviceInfoProvider) { + protected Forwarder(String protocolName, StatsFactory statsFactory, PartitionProvider partitionProvider, ServiceInfoProvider serviceInfoProvider) { this.protocolName = protocolName; this.partitionProvider = partitionProvider; this.serviceInfoProvider = serviceInfoProvider; @@ -66,6 +66,7 @@ public abstract class Forwarder { public abstract void destroy(); protected void jcppForward(String topic, String key, UplinkQueueMessage msg, BiConsumer consumer) { + forwarderMessagesStats.incrementTotal(); QueueMsgHeaders headers = new DefaultQueueMsgHeaders(); Tracer currentTracer = TracerContextUtil.getCurrentTracer(); @@ -80,11 +81,13 @@ public abstract class Forwarder { TracerContextUtil.newTracer(currentTracer.getTraceId(), currentTracer.getOrigin(), currentTracer.getTracerTs()); MDCUtils.recordTracer(); + log.trace("单体消息转发成功 key:{}", key); if (consumer != null) { consumer.accept(true, JacksonUtil.newObjectNode()); } + forwarderMessagesStats.incrementSuccessful(); } @Override @@ -92,6 +95,7 @@ public abstract class Forwarder { TracerContextUtil.newTracer(currentTracer.getTraceId(), currentTracer.getOrigin(), currentTracer.getTracerTs()); MDCUtils.recordTracer(); + log.warn("单体消息转发异常", t); if (consumer != null) { @@ -99,6 +103,7 @@ public abstract class Forwarder { objectNode.put(ERROR, t.getClass() + ": " + t.getMessage()); consumer.accept(true, objectNode); } + forwarderMessagesStats.incrementFailed(); } }); } diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java index ae1ebf3..c81b929 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java @@ -150,6 +150,7 @@ public class KafkaForwarder extends Forwarder { } private void kafkaForward(String topic, String key, UplinkQueueMessage msg, BiConsumer consumer) throws InvalidProtocolBufferException { + forwarderMessagesStats.incrementTotal(); Headers headers = new RecordHeaders(); Tracer currentTracer = TracerContextUtil.getCurrentTracer(); @@ -177,6 +178,7 @@ public class KafkaForwarder extends Forwarder { private void logAndDoConsumer(BiConsumer consumer, RecordMetadata metadata, Exception e, Tracer currentTracer) { TracerContextUtil.newTracer(currentTracer.getTraceId(), currentTracer.getOrigin(), currentTracer.getTracerTs()); MDCUtils.recordTracer(); + log.debug("Kafka 消息转发完成, success:{}", e == null); if (consumer != null) { @@ -196,6 +198,9 @@ public class KafkaForwarder extends Forwarder { if (e != null) { objectNode.put(ERROR, e.getClass() + ": " + e.getMessage()); + forwarderMessagesStats.incrementFailed(); + } else { + forwarderMessagesStats.incrementSuccessful(); } consumer.accept(e == null, objectNode); From 6bb3d3738f4bfd82c388e2c2086ad1c7ad642c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 22 Oct 2024 14:38:56 +0800 Subject: [PATCH 060/102] =?UTF-8?q?=E6=8C=87=E6=A0=87=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProtocolUplinkConsumerService.java | 101 ++++++++++++------ 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java index c3c4014..20ff976 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java @@ -134,58 +134,100 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple PendingMsgHolder pendingMsgHolder = new PendingMsgHolder(); Future packSubmitFuture = consumersExecutor.submit(new TracerRunnable(() -> orderedMsgList.forEach(element -> { + UUID id = element.getUuid(); + ProtoQueueMsg msg = element.getMsg(); + tracer(msg); - log.trace("[{}] Creating main callback for message: {}", id, msg.getValue()); + + log.trace("[{}] Creating PackCallback for message: {}", id, msg.getValue()); + Callback callback = new PackCallback<>(id, ctx); + try { UplinkQueueMessage uplinkQueueMsg = msg.getValue(); - pendingMsgHolder.setUplinkQueueMessage(uplinkQueueMsg); - if (uplinkQueueMsg.hasLoginRequest()) { - pileProtocolService.pileLogin(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasHeartBeatRequest()) { - pileProtocolService.heartBeat(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasVerifyPricingRequest()) { - pileProtocolService.verifyPricing(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasQueryPricingRequest()) { - pileProtocolService.queryPricing(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasGunRunStatusProto()) { - pileProtocolService.postGunRunStatus(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasChargingProgressProto()) { - pileProtocolService.postChargingProgress(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasSetPricingResponse()) { - pileProtocolService.onSetPricingResponse(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasRemoteStartChargingResponse()) { - pileProtocolService.onRemoteStartChargingResponse(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasRemoteStopChargingResponse()) { - pileProtocolService.onRemoteStopChargingResponse(uplinkQueueMsg, callback); - } else if (uplinkQueueMsg.hasTransactionRecord()) { - pileProtocolService.onTransactionRecord(uplinkQueueMsg, callback); - } else { - callback.onSuccess(); - } if (statsEnabled) { stats.log(uplinkQueueMsg); } + + pendingMsgHolder.setUplinkQueueMessage(uplinkQueueMsg); + + if (uplinkQueueMsg.hasLoginRequest()) { + + pileProtocolService.pileLogin(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasHeartBeatRequest()) { + + pileProtocolService.heartBeat(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasVerifyPricingRequest()) { + + pileProtocolService.verifyPricing(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasQueryPricingRequest()) { + + pileProtocolService.queryPricing(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasGunRunStatusProto()) { + + pileProtocolService.postGunRunStatus(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasChargingProgressProto()) { + + pileProtocolService.postChargingProgress(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasSetPricingResponse()) { + + pileProtocolService.onSetPricingResponse(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasRemoteStartChargingResponse()) { + + pileProtocolService.onRemoteStartChargingResponse(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasRemoteStopChargingResponse()) { + + pileProtocolService.onRemoteStopChargingResponse(uplinkQueueMsg, callback); + + } else if (uplinkQueueMsg.hasTransactionRecord()) { + + pileProtocolService.onTransactionRecord(uplinkQueueMsg, callback); + + } else { + + callback.onSuccess(); + } + } catch (Throwable e) { + log.warn("[{}] Failed to process message: {}", id, msg, e); + callback.onFailure(e); } })) ); - if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) { + + if (!ctx.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) { + if (!packSubmitFuture.isDone()) { + packSubmitFuture.cancel(true); + UplinkQueueMessage lastSubmitMsg = pendingMsgHolder.getUplinkQueueMessage(); + log.warn("Timeout to process message: {}", lastSubmitMsg); } + if (log.isDebugEnabled()) { - ctx.getAckMap().forEach((id, msg) -> log.debug("[{}] Timeout to process message: {}", id, msg.getValue())); + + ctx.getAckMap().forEach((id, msg) -> log.info("[{}] Timeout to process message: {}", id, msg.getValue())); + } + ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue())); } + consumer.commit(); } @@ -198,11 +240,8 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple origin = ByteUtil.bytesToString(tracerOrigin); } - long ts = System.currentTimeMillis(); byte[] tracerTs = msg.getHeaders().get(MSG_MD_PREFIX + MSG_MD_TS); - if (tracerTs != null) { - ts = ByteUtil.bytesToLong(tracerTs); - } + long ts = tracerTs != null ? ByteUtil.bytesToLong(tracerTs) : System.currentTimeMillis(); return TracerContextUtil.newTracer(ByteUtil.bytesToString(tracerId), origin, ts); }) From 09281ca3960032a77495c456aa9f3a9602ed24c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 22 Oct 2024 15:04:37 +0800 Subject: [PATCH 061/102] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E5=8E=8B=E6=B5=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=B0=83=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Dockerfile-App | 3 +++ docker/Dockerfile-Protocol | 2 ++ .../src/main/resources/app-service.yml | 6 ++--- .../src/main/resources/log4j2.xml | 6 +++++ .../ProtocolUplinkConsumerService.java | 25 +++---------------- .../queue/common/QueueConstants.java | 9 ++++++- .../infrastructure/util/mdc/MDCUtils.java | 1 - .../util/trace/TracerContextUtil.java | 1 - .../jcpp/protocol/forwarder/Forwarder.java | 11 +++----- .../protocol/forwarder/KafkaForwarder.java | 11 +++----- 10 files changed, 34 insertions(+), 41 deletions(-) diff --git a/docker/Dockerfile-App b/docker/Dockerfile-App index 144da60..f619b3d 100644 --- a/docker/Dockerfile-App +++ b/docker/Dockerfile-App @@ -35,6 +35,9 @@ RUN chmod a+x *.sh && mv start.sh /usr/bin EXPOSE 8080 8080 +ENV APP_LOG_LEVEL=INFO +ENV PROTOCOLS_LOG_LEVEL=INFO + CMD ["start.sh"] diff --git a/docker/Dockerfile-Protocol b/docker/Dockerfile-Protocol index 7aa342a..82aae02 100644 --- a/docker/Dockerfile-Protocol +++ b/docker/Dockerfile-Protocol @@ -35,6 +35,8 @@ RUN chmod a+x *.sh && mv start.sh /usr/bin EXPOSE 8081 8081 +ENV PROTOCOLS_LOG_LEVEL=INFO + CMD ["start.sh"] diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 7662d41..11b7c38 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -27,7 +27,7 @@ spring: password: "${SPRING_DATASOURCE_PASSWORD:postgres}" hikari: leak-detection-threshold: "${SPRING_DATASOURCE_HIKARI_LEAK_DETECTION_THRESHOLD:0}" - maximum-pool-size: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:32}" + maximum-pool-size: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:64}" register-mbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}" mybatis-plus: @@ -66,7 +66,7 @@ queue: in-memory: queue-capacity: "${QUEUE_IN_MEMORY_QUEUE_CAPACITY:100000}" stats: - print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" + print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:10000}" kafka: bootstrap-servers: "${KAFKA_SERVERS:kafka:9092}" ssl: @@ -128,7 +128,7 @@ redis: standalone: host: "${REDIS_HOST:redis}" port: "${REDIS_PORT:6379}" - useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:false}" + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" clientName: "${REDIS_CLIENT_NAME:standalone}" commandTimeout: "${REDIS_CLIENT_COMMAND_TIMEOUT:30000}" shutdownTimeout: "${REDIS_CLIENT_SHUTDOWN_TIMEOUT:1000}" diff --git a/jcpp-app-bootstrap/src/main/resources/log4j2.xml b/jcpp-app-bootstrap/src/main/resources/log4j2.xml index f53a4fd..d5884d8 100644 --- a/jcpp-app-bootstrap/src/main/resources/log4j2.xml +++ b/jcpp-app-bootstrap/src/main/resources/log4j2.xml @@ -46,6 +46,12 @@ + + + + + diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java index 20ff976..0316d58 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java @@ -35,16 +35,12 @@ import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.*; import java.util.stream.Collectors; -import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_PREFIX; -import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_TS; -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ID; -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ORIGIN; +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.*; /** @@ -232,23 +228,10 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple } private void tracer(ProtoQueueMsg msg) { - if (Optional.ofNullable(msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ID)) - .map(tracerId -> { - String origin = null; - byte[] tracerOrigin = msg.getHeaders().get(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN); - if (tracerOrigin != null) { - origin = ByteUtil.bytesToString(tracerOrigin); - } - byte[] tracerTs = msg.getHeaders().get(MSG_MD_PREFIX + MSG_MD_TS); - long ts = tracerTs != null ? ByteUtil.bytesToLong(tracerTs) : System.currentTimeMillis(); - - return TracerContextUtil.newTracer(ByteUtil.bytesToString(tracerId), origin, ts); - }) - .isEmpty()) { - - TracerContextUtil.newTracer(); - } + TracerContextUtil.newTracer(ByteUtil.bytesToString(msg.getHeaders().get(MSG_MD_TRACER_ID)), + ByteUtil.bytesToString(msg.getHeaders().get(MSG_MD_TRACER_ORIGIN)), + ByteUtil.bytesToLong(msg.getHeaders().get(MSG_MD_TRACER_TS))); MDCUtils.recordTracer(); } diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java index 509bc52..6017c93 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/common/QueueConstants.java @@ -4,6 +4,8 @@ */ package sanbing.jcpp.infrastructure.queue.common; +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; + /** * @author baigod */ @@ -11,5 +13,10 @@ public final class QueueConstants { public static final String MSG_MD_PREFIX = "jcpp_"; - public static final String MSG_MD_TS = "ts"; + public static final String MSG_MD_TRACER_ID = MSG_MD_PREFIX + JCPP_TRACER_ID; + + public static final String MSG_MD_TRACER_ORIGIN = MSG_MD_PREFIX + JCPP_TRACER_ORIGIN; + + public static final String MSG_MD_TRACER_TS = MSG_MD_PREFIX + JCPP_TRACER_TS; + } \ No newline at end of file diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java index 7f22912..2af32f0 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/mdc/MDCUtils.java @@ -34,7 +34,6 @@ public class MDCUtils { } return tracer.getTraceId(); - } public static void cleanTracer() { diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java index 5921c7c..589fc13 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java @@ -40,7 +40,6 @@ public class TracerContextUtil { tracer = new Tracer(traceId, origin, ts); } - TRACE_ID_CONTAINER.set(tracer); return tracer; diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java index 9aa2e9b..b4d7fe8 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/Forwarder.java @@ -25,10 +25,7 @@ import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; -import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_PREFIX; -import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_TS; -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ID; -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ORIGIN; +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.*; /** * @author baigod @@ -70,9 +67,9 @@ public abstract class Forwarder { QueueMsgHeaders headers = new DefaultQueueMsgHeaders(); Tracer currentTracer = TracerContextUtil.getCurrentTracer(); - headers.put(MSG_MD_PREFIX + JCPP_TRACER_ID, ByteUtil.stringToBytes(currentTracer.getTraceId())); - headers.put(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN, ByteUtil.stringToBytes(currentTracer.getOrigin())); - headers.put(MSG_MD_PREFIX + MSG_MD_TS, ByteUtil.longToBytes(currentTracer.getTracerTs())); + headers.put(MSG_MD_TRACER_ID, ByteUtil.stringToBytes(currentTracer.getTraceId())); + headers.put(MSG_MD_TRACER_ORIGIN, ByteUtil.stringToBytes(currentTracer.getOrigin())); + headers.put(MSG_MD_TRACER_TS, ByteUtil.longToBytes(currentTracer.getTracerTs())); TopicPartitionInfo tpi = partitionProvider.resolve(ServiceType.APP, topic, key); producer.send(tpi, new ProtoQueueMsg<>(key, msg, headers), new QueueCallback() { diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java index c81b929..56d97a4 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/forwarder/KafkaForwarder.java @@ -36,10 +36,7 @@ import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; -import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_PREFIX; -import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.MSG_MD_TS; -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ID; -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.JCPP_TRACER_ORIGIN; +import static sanbing.jcpp.infrastructure.queue.common.QueueConstants.*; /** * @author baigod @@ -154,9 +151,9 @@ public class KafkaForwarder extends Forwarder { Headers headers = new RecordHeaders(); Tracer currentTracer = TracerContextUtil.getCurrentTracer(); - headers.add(new RecordHeader(MSG_MD_PREFIX + JCPP_TRACER_ID, ByteUtil.stringToBytes(currentTracer.getTraceId()))); - headers.add(new RecordHeader(MSG_MD_PREFIX + JCPP_TRACER_ORIGIN, ByteUtil.stringToBytes(currentTracer.getOrigin()))); - headers.add(new RecordHeader(MSG_MD_PREFIX + MSG_MD_TS, ByteUtil.longToBytes(currentTracer.getTracerTs()))); + headers.add(new RecordHeader(MSG_MD_TRACER_ID, ByteUtil.stringToBytes(currentTracer.getTraceId()))); + headers.add(new RecordHeader(MSG_MD_TRACER_ORIGIN, ByteUtil.stringToBytes(currentTracer.getOrigin()))); + headers.add(new RecordHeader(MSG_MD_TRACER_TS, ByteUtil.longToBytes(currentTracer.getTracerTs()))); if (kafkaCfg.getEncoder() == KafkaCfg.EncoderType.json) { From 8d125f4e19efdd9479de21e4e368955dae223d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Tue, 22 Oct 2024 16:18:50 +0800 Subject: [PATCH 062/102] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E5=8E=8B=E6=B5=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=B0=83=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/service/impl/DefaultDownlinkCallService.java | 2 +- .../infrastructure/queue/provider/KafkaAppQueueFactory.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java index a0a801d..62a6b87 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java @@ -87,7 +87,7 @@ public class DefaultDownlinkCallService implements DownlinkCallService { try { ResponseEntity response = downlinkRestTemplate.postForEntity("http://" + nodeWebapiIpPort + "/api/onDownlink", entity, ResponseEntity.class); - log.info("下行消息发送成功 {}", response); + log.debug("下行消息发送成功 {}", response); } catch (RestClientException e) { log.error("下行消息发送失败 {}", downlinkRestMessage, e); throw new RuntimeException(e); diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java index 155e15b..e884c93 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/KafkaAppQueueFactory.java @@ -41,7 +41,6 @@ public class KafkaAppQueueFactory implements AppQueueFactory { this.appAdmin = new KafkaAdmin(kafkaSettings, kafkaTopicConfigs.getAppConfigs()); } - @Override public QueueConsumer> createProtocolUplinkMsgConsumer() { KafkaConsumerTemplate.KafkaConsumerTemplateBuilder> consumerBuilder = KafkaConsumerTemplate.builder(); From bdd6459d6a8ad1cf4db4b9292be8e29701f90f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 22 Oct 2024 16:41:49 +0800 Subject: [PATCH 063/102] =?UTF-8?q?=E5=BF=BD=E7=95=A5=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/pom.xml | 4 ---- jcpp-protocol-bootstrap/pom.xml | 4 ---- pom.xml | 6 +++++- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/jcpp-app-bootstrap/pom.xml b/jcpp-app-bootstrap/pom.xml index 1297db4..1127c72 100644 --- a/jcpp-app-bootstrap/pom.xml +++ b/jcpp-app-bootstrap/pom.xml @@ -75,10 +75,6 @@ org.apache.maven.plugins maven-jar-plugin - - org.apache.maven.plugins - maven-compiler-plugin - diff --git a/jcpp-protocol-bootstrap/pom.xml b/jcpp-protocol-bootstrap/pom.xml index d2fdb39..f5730a5 100644 --- a/jcpp-protocol-bootstrap/pom.xml +++ b/jcpp-protocol-bootstrap/pom.xml @@ -66,10 +66,6 @@ org.apache.maven.plugins maven-jar-plugin - - org.apache.maven.plugins - maven-compiler-plugin - diff --git a/pom.xml b/pom.xml index 37e6903..a0bb362 100644 --- a/pom.xml +++ b/pom.xml @@ -243,7 +243,7 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - 21 + ${java.version} -Xlint:deprecation -Xlint:removal @@ -361,6 +361,10 @@ + + org.apache.maven.plugins + maven-compiler-plugin + org.apache.maven.plugins maven-surefire-plugin From 4da2bded29b58086f0a42574fe26f47fb10954ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 22 Oct 2024 16:44:16 +0800 Subject: [PATCH 064/102] testenv --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- .../src/test/resources/app-service-test.properties | 2 +- jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 8b791b5..448bf81 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -236,7 +236,7 @@ service: topic: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_TOPIC:protocol_uplink}" jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 - bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ACKS:1}" # # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties index 0259723..d050d7f 100644 --- a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -1,3 +1,3 @@ -spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/jcpp +spring.datasource.url=jdbc:postgresql://testenv:30135/jcpp service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 service.protocols.yunkuaichongV160.listener.tcp.bind-port=0 \ No newline at end of file diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index f11cb16..735f88c 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -108,7 +108,7 @@ service: topic: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_TOPIC:protocol_uplink}" jcpp-partition: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_JCPP_PARTITION:true}" # 是否利用JCPP的分片框架 # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 - bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:10.102.12.102:9092}" + bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ACKS:1}" # # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" From 73de4b28b3f4ac42e6f388775ac0b21e50765af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 22 Oct 2024 16:45:27 +0800 Subject: [PATCH 065/102] ## --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 4 ++-- .../src/main/resources/protocol-service.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 448bf81..187cab9 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -199,7 +199,7 @@ service: # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ACKS:1}" - # # 可选 protobuf(推荐)、json + # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_RETRIES:1}" compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd @@ -238,7 +238,7 @@ service: # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ACKS:1}" - # # 可选 protobuf(推荐)、json + # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_RETRIES:1}" compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 735f88c..f01a893 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -73,7 +73,7 @@ service: # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ACKS:1}" - # # 可选 protobuf(推荐)、json + # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_RETRIES:1}" compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd @@ -110,7 +110,7 @@ service: # 以下配置只有在service.type为protocol时且jcpp-partition为false时才生效 bootstrap-servers: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_SERVERS:kafka:9092}" acks: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ACKS:1}" - # # 可选 protobuf(推荐)、json + # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_RETRIES:1}" compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd From 7445d4e3f08566ee86ca1583a0b2c3ab678dcd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 22 Oct 2024 17:11:05 +0800 Subject: [PATCH 066/102] =?UTF-8?q?=E4=BF=AE=E6=94=B9downlink=20protobuf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/service/DownlinkCallService.java | 4 ++-- .../impl/DefaultDownlinkCallService.java | 10 ++++----- .../impl/DefaultPileProtocolService.java | 12 +++++------ jcpp-infrastructure-proto/pom.xml | 19 +++++++++++++++++ .../src/main/proto/protocol.proto | 11 +++++++++- .../protocol/adapter/DownlinkController.java | 4 ++-- .../jcpp/protocol/domain/ProtocolSession.java | 6 +++--- .../protocol/domain/SessionToHandlerMsg.java | 4 ++-- .../listener/tcp/TcpChannelHandler.java | 4 ++-- .../protocol/listener/tcp/TcpSession.java | 8 +++---- .../adapter/DownlinkControllerIT.java | 6 +++--- .../YunKuaiChongDwonlinkMessage.java | 4 ++-- .../YunKuaiChongProtocolMessageProcessor.java | 4 ++-- pom.xml | 21 +++++++++++++++++++ 14 files changed, 83 insertions(+), 34 deletions(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java index 3dc6c78..15b97a9 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java @@ -4,12 +4,12 @@ */ package sanbing.jcpp.app.service; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; /** * @author baigod */ public interface DownlinkCallService { - void sendDownlinkMessage(DownlinkRestMessage.Builder downlinkMessageBuilder, String pileCode); + void sendDownlinkMessage(DownlinkRequestMessage.Builder downlinkMessageBuilder, String pileCode); } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java index 62a6b87..a29221f 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java @@ -21,7 +21,7 @@ import sanbing.jcpp.infrastructure.cache.CacheValueWrapper; import sanbing.jcpp.infrastructure.cache.TransactionalCache; import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.adapter.DownlinkController; import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; @@ -49,7 +49,7 @@ public class DefaultDownlinkCallService implements DownlinkCallService { private String cacheType; @Override - public void sendDownlinkMessage(DownlinkRestMessage.Builder downlinkMessageBuilder, String pileCode) { + public void sendDownlinkMessage(DownlinkRequestMessage.Builder downlinkMessageBuilder, String pileCode) { if (serviceInfoProvider.isMonolith() && "caffeine".equalsIgnoreCase(cacheType)) { downlinkController.onDownlink(downlinkMessageBuilder.build()) @@ -75,21 +75,21 @@ public class DefaultDownlinkCallService implements DownlinkCallService { } } - private void invokeDownlinkRestApi(DownlinkRestMessage downlinkRestMessage, String nodeWebapiIpPort) { + private void invokeDownlinkRestApi(DownlinkRequestMessage DownlinkRequestMessage, String nodeWebapiIpPort) { HttpHeaders headers = new HttpHeaders(); headers.add(JCPP_TRACER_ID, TracerContextUtil.getCurrentTracer().getTraceId()); headers.add(JCPP_TRACER_ORIGIN, TracerContextUtil.getCurrentTracer().getOrigin()); headers.add(JCPP_TRACER_TS, String.valueOf(TracerContextUtil.getCurrentTracer().getTracerTs())); headers.setContentType(MediaType.parseMediaType("application/x-protobuf")); - HttpEntity entity = new HttpEntity<>(downlinkRestMessage, headers); + HttpEntity entity = new HttpEntity<>(DownlinkRequestMessage, headers); try { ResponseEntity response = downlinkRestTemplate.postForEntity("http://" + nodeWebapiIpPort + "/api/onDownlink", entity, ResponseEntity.class); log.debug("下行消息发送成功 {}", response); } catch (RestClientException e) { - log.error("下行消息发送失败 {}", downlinkRestMessage, e); + log.error("下行消息发送失败 {}", DownlinkRequestMessage, e); throw new RuntimeException(e); } diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index 10fc0dd..f7ccab0 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -59,7 +59,7 @@ public class DefaultPileProtocolService implements PileProtocolService { log.debug("查询到充电桩信息 {}", pile); // 构造下行回复 - DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, loginRequest.getPileCode()); + DownlinkRequestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, loginRequest.getPileCode()); downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.LOGIN_ACK.name()); if (pile != null) { @@ -122,7 +122,7 @@ public class DefaultPileProtocolService implements PileProtocolService { // todo 默认校验成功,后续查库校验 assert pricingId > 0; - DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); + DownlinkRequestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.VERIFY_PRICING_ACK.name()); downlinkMessageBuilder.setVerifyPricingResponse(VerifyPricingResponse.newBuilder() .setSuccess(true) @@ -167,7 +167,7 @@ public class DefaultPileProtocolService implements PileProtocolService { model.setPeriodsList(periods); // 构造下行计费 - DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); + DownlinkRequestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.QUERY_PRICING_ACK.name()); downlinkMessageBuilder.setQueryPricingResponse(QueryPricingResponse.newBuilder() .setPileCode(pileCode) @@ -236,7 +236,7 @@ public class DefaultPileProtocolService implements PileProtocolService { String pileCode = transactionRecord.getPileCode(); // 构造下行计费 - DownlinkRestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); + DownlinkRequestMessage.Builder downlinkMessageBuilder = createDownlinkMessageBuilder(uplinkQueueMessage, pileCode); downlinkMessageBuilder.setDownlinkCmd(DownlinkCmdEnum.TRANSACTION_RECORD.name()); downlinkMessageBuilder.setTransactionRecordAck(TransactionRecordAck.newBuilder() .setTradeNo(tradeNo) @@ -257,9 +257,9 @@ public class DefaultPileProtocolService implements PileProtocolService { return period; } - private DownlinkRestMessage.Builder createDownlinkMessageBuilder(UplinkQueueMessage uplinkQueueMessage, String pileCode) { + private DownlinkRequestMessage.Builder createDownlinkMessageBuilder(UplinkQueueMessage uplinkQueueMessage, String pileCode) { UUID messageId = UUID.randomUUID(); - DownlinkRestMessage.Builder builder = DownlinkRestMessage.newBuilder(); + DownlinkRequestMessage.Builder builder = DownlinkRequestMessage.newBuilder(); builder.setMessageIdMSB(messageId.getLeastSignificantBits()); builder.setMessageIdLSB(messageId.getLeastSignificantBits()); builder.setPileCode(pileCode); diff --git a/jcpp-infrastructure-proto/pom.xml b/jcpp-infrastructure-proto/pom.xml index 2583d91..82e15a5 100644 --- a/jcpp-infrastructure-proto/pom.xml +++ b/jcpp-infrastructure-proto/pom.xml @@ -26,6 +26,10 @@ + + javax.annotation + javax.annotation-api + com.google.protobuf protobuf-java @@ -34,6 +38,21 @@ com.google.protobuf protobuf-java-util + + io.grpc + grpc-netty-shaded + provided + + + io.grpc + grpc-protobuf + provided + + + io.grpc + grpc-stub + provided + diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index 0a90306..2ad68f5 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -9,6 +9,10 @@ package infrastructureProto; option java_package = "sanbing.jcpp.proto.gen"; option java_outer_classname = "ProtocolProto"; +service ProtocolDownlinkInterface { + rpc onDownlink(stream DownlinkRequestMessage) returns (stream DownlinkResponseMessage) {} +} + message UplinkQueueMessage { int64 messageIdMSB = 1; int64 messageIdLSB = 2; @@ -29,7 +33,7 @@ message UplinkQueueMessage { TransactionRecord transactionRecord = 30; } -message DownlinkRestMessage { +message DownlinkRequestMessage { int64 messageIdMSB = 1; int64 messageIdLSB = 2; int64 sessionIdMSB = 3; @@ -49,6 +53,11 @@ message DownlinkRestMessage { TransactionRecordAck transactionRecordAck = 26; } +message DownlinkResponseMessage { + bool success = 1; + optional string error = 2; +} + message LoginRequest { string pileCode = 2; string credential = 3; diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java index ce54453..ffa7948 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.domain.ProtocolSession; import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; @@ -37,7 +37,7 @@ public class DownlinkController { ProtocolSessionRegistryProvider protocolSessionRegistryProvider; @PostMapping(value = "/onDownlink", consumes = "application/x-protobuf", produces = "application/x-protobuf") - public DeferredResult> onDownlink(@RequestBody DownlinkRestMessage downlinkMsg) { + public DeferredResult> onDownlink(@RequestBody DownlinkRequestMessage downlinkMsg) { log.debug("收到REST下行请求 {}", downlinkMsg); final DeferredResult> response = new DeferredResult<>(onDownlinkTimeout, diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java index b8ead56..7b31620 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java @@ -9,7 +9,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.forwarder.Forwarder; import java.io.Closeable; @@ -52,14 +52,14 @@ public abstract class ProtocolSession implements Closeable { @Setter private Forwarder forwarder; - public ProtocolSession(String protocolName) { + protected ProtocolSession(String protocolName) { this.protocolName = protocolName; this.pileCodeSet = new LinkedHashSet<>(); this.id = UUID.randomUUID(); this.lastActivityTime = LocalDateTime.now(); } - public abstract void onDownlink(DownlinkRestMessage downlinkMsg); + public abstract void onDownlink(DownlinkRequestMessage downlinkMsg); public void close() { close(SessionCloseReason.DESTRUCTION); diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java index fdf5a59..291a257 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/SessionToHandlerMsg.java @@ -4,10 +4,10 @@ */ package sanbing.jcpp.protocol.domain; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; /** * @author baigod */ -public record SessionToHandlerMsg(DownlinkRestMessage downlinkMsg, ProtocolSession session) { +public record SessionToHandlerMsg(DownlinkRequestMessage downlinkMsg, ProtocolSession session) { } \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java index c8819e1..abeb467 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpChannelHandler.java @@ -19,7 +19,7 @@ import sanbing.jcpp.infrastructure.stats.MessagesStats; import sanbing.jcpp.infrastructure.util.exception.DownlinkException; import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.ProtocolMessageProcessor; import sanbing.jcpp.protocol.domain.ListenerToHandlerMsg; import sanbing.jcpp.protocol.domain.ProtocolUplinkMsg; @@ -121,7 +121,7 @@ public class TcpChannelHandler extends SimpleChannelInboundHandler sendDownlinkConsumer; + private final Consumer sendDownlinkConsumer; private final Consumer writeAndFlushConsumer; @@ -64,7 +64,7 @@ public class TcpSession extends ProtocolSession { } public TcpSession(String protocolName, - Consumer sendDownlinkConsumer, + Consumer sendDownlinkConsumer, Consumer writeAndFlushConsumer) { super(protocolName); this.sendDownlinkConsumer = sendDownlinkConsumer; @@ -72,7 +72,7 @@ public class TcpSession extends ProtocolSession { } @Override - public void onDownlink(DownlinkRestMessage downlinkMsg) { + public void onDownlink(DownlinkRequestMessage downlinkMsg) { sendDownlinkConsumer.accept(downlinkMsg); } diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java index 8346abc..20ecd62 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java @@ -21,7 +21,7 @@ import org.springframework.http.HttpStatus; import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; import sanbing.jcpp.infrastructure.util.property.PropertyUtils; import sanbing.jcpp.proto.gen.ProtocolProto; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.AbstractProtocolTestBase; import sanbing.jcpp.protocol.domain.DownlinkCmdEnum; import sanbing.jcpp.protocol.domain.ProtocolSession; @@ -116,9 +116,9 @@ class DownlinkControllerIT extends AbstractProtocolTestBase { UUID messageId = UUID.randomUUID(); UUID requestId = UUID.randomUUID(); - // 创建 DownlinkRestMessage 实例 + // 创建 DownlinkRequestMessage 实例 String pileCode = "20231212000010"; - DownlinkRestMessage downlinkMsg = DownlinkRestMessage.newBuilder() + DownlinkRequestMessage downlinkMsg = DownlinkRequestMessage.newBuilder() .setMessageIdMSB(messageId.getMostSignificantBits()) .setMessageIdLSB(messageId.getLeastSignificantBits()) .setSessionIdMSB(sessionId.getMostSignificantBits()) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java index 83db1a7..0e7eb3c 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongDwonlinkMessage.java @@ -7,7 +7,7 @@ package sanbing.jcpp.protocol.yunkuaichong; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import java.io.Serializable; import java.util.UUID; @@ -32,7 +32,7 @@ public class YunKuaiChongDwonlinkMessage implements Serializable { private int cmd; // 消息体 - private DownlinkRestMessage msg; + private DownlinkRequestMessage msg; // 上行消息 private YunKuaiChongUplinkMessage requestData; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java index 665b7b2..7a7f424 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/YunKuaiChongProtocolMessageProcessor.java @@ -10,7 +10,7 @@ import io.netty.buffer.Unpooled; import lombok.extern.slf4j.Slf4j; import sanbing.jcpp.infrastructure.util.JCPPPair; import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.ProtocolContext; import sanbing.jcpp.protocol.ProtocolMessageProcessor; import sanbing.jcpp.protocol.domain.ListenerToHandlerMsg; @@ -163,7 +163,7 @@ public class YunKuaiChongProtocolMessageProcessor extends ProtocolMessageProcess public void downlinkHandle(SessionToHandlerMsg sessionToHandlerMsg) { TcpSession session = (TcpSession) sessionToHandlerMsg.session(); - DownlinkRestMessage protocolDownlinkMsg = sessionToHandlerMsg.downlinkMsg(); + DownlinkRequestMessage protocolDownlinkMsg = sessionToHandlerMsg.downlinkMsg(); int cmd = YunKuaiChongDownlinkCmdEnum.valueOf(protocolDownlinkMsg.getDownlinkCmd()).getCmd(); diff --git a/pom.xml b/pom.xml index a0bb362..3a608a7 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ 6.6.2 3.9.2 3.8.16.Final + 1.3.2 @@ -180,6 +181,11 @@ xnio-api ${xnio-api.version} + + javax.annotation + javax.annotation-api + ${javax.annotation-api.version} + com.google.protobuf protobuf-java @@ -190,6 +196,21 @@ protobuf-java-util ${protobuf.version} + + io.grpc + grpc-netty-shaded + ${grpc.version} + + + io.grpc + grpc-protobuf + ${grpc.version} + + + io.grpc + grpc-stub + ${grpc.version} + org.glassfish jakarta.el From 5a1b4f83037d3aa85b92596622d53667d2970798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 23 Oct 2024 17:07:57 +0800 Subject: [PATCH 067/102] =?UTF-8?q?grpc=20=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/app-service.yml | 19 +- .../resources/app-service-test.properties | 3 +- .../sanbing/jcpp/app/data/PileSession.java | 14 +- .../jcpp/app/service/DownlinkCallService.java | 60 +++- .../app/service/grpc/DownlinkGrpcClient.java | 280 ++++++++++++++++++ .../impl/DefaultDownlinkCallService.java | 97 ------ .../impl/DefaultPileProtocolService.java | 20 +- .../service/impl/GrpcDownlinkCallService.java | 39 +++ .../service/impl/RestDownlinkCallService.java | 64 ++++ jcpp-infrastructure-proto/pom.xml | 3 - .../src/main/proto/protocol.proto | 31 +- .../infrastructure/queue/ProtoQueueMsg.java | 3 +- .../discovery/DefaultServiceInfoProvider.java | 21 +- .../queue/discovery/ServiceInfoProvider.java | 6 +- .../queue/processing/IdMsgPair.java | 3 +- .../protocol/adapter/DownlinkGrpcService.java | 159 ++++++++++ ...efaultProtocolSessionRegistryProvider.java | 4 +- .../src/main/resources/protocol-service.yml | 16 +- .../v150/YunKuaiChongV150HeartbeatULCmd.java | 4 +- .../v150/YunKuaiChongV150LoginULCmd.java | 4 +- pom.xml | 6 +- 21 files changed, 715 insertions(+), 141 deletions(-) create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java delete mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java create mode 100644 jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 187cab9..67cfc18 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -164,10 +164,23 @@ service: type: "${SERVICE_TYPE:monolith}" # 可自定义的服务ID,如果不指定,则默认为HOSTNAME id: "${SERVICE_ID:}" - protocols: + protocol: sessions: default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" default-state-check-interval-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" + rpc: + enabled: "${SERVICE_PROTOCOL_RPC_ENABLED:true}" + port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" + boss: "${SERVICE_PROTOCOL_RPC_BOSS:4}" + worker: "${SERVICE_PROTOCOL_RPC_WORKER:64}" + so-rcvbuf: "${SERVICE_PROTOCOL_RPC_SO_RCVBUF:65535}" + so-sndbuf: "${SERVICE_PROTOCOL_RPC_SO_SNDBUF:65535}" + no-delay: "${SERVICE_PROTOCOL_RPC_NO_DELAY:true}" + user-thread-pool-size: "${SERVICE_PROTOCOL_RPC_USER_THREAD_POOL_SIZE:1024}" + max-inbound-message-size: "${SERVICE_PROTOCOL_RPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" + max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" + client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" + protocols: yunkuaichongV150: enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" listener: @@ -252,3 +265,7 @@ thread-pool: hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" + +downlink: + rpc: + type: "${DOWNLINK_RPC_TYPE:grpc}" # rest or grpc \ No newline at end of file diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties index d050d7f..ce335d7 100644 --- a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -1,3 +1,4 @@ spring.datasource.url=jdbc:postgresql://testenv:30135/jcpp service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 -service.protocols.yunkuaichongV160.listener.tcp.bind-port=0 \ No newline at end of file +service.protocols.yunkuaichongV160.listener.tcp.bind-port=0 +service.protocol.rpc.port=0 \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java b/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java index 1dbb5cc..0efd3d6 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/data/PileSession.java @@ -29,7 +29,11 @@ public class PileSession implements Serializable { private String nodeId; - private String nodeWebapiIpPort; + private String nodeIp; + + private int nodeRestPort; + + private int nodeGrpcPort; public PileSession(UUID pileId, String pileCode, String protocolName) { this.pileId = pileId; @@ -45,13 +49,17 @@ public class PileSession implements Serializable { @JsonProperty("protocolSessionId") UUID protocolSessionId, @JsonProperty("remoteAddress") String remoteAddress, @JsonProperty("nodeId") String nodeId, - @JsonProperty("nodeWebapiIpPort") String nodeWebapiIpPort) { + @JsonProperty("nodeIp") String nodeIp, + @JsonProperty("nodeRestPort") int nodeRestPort, + @JsonProperty("nodeGrpcPort") int nodeGrpcPort) { this.pileId = pileId; this.pileCode = pileCode; this.protocolName = protocolName; this.protocolSessionId = protocolSessionId; this.remoteAddress = remoteAddress; this.nodeId = nodeId; - this.nodeWebapiIpPort = nodeWebapiIpPort; + this.nodeIp = nodeIp; + this.nodeRestPort = nodeRestPort; + this.nodeGrpcPort = nodeGrpcPort; } } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java index 15b97a9..2162c56 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java @@ -4,12 +4,68 @@ */ package sanbing.jcpp.app.service; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.app.service.cache.session.PileSessionCacheKey; +import sanbing.jcpp.infrastructure.cache.CacheValueWrapper; +import sanbing.jcpp.infrastructure.cache.TransactionalCache; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; +import sanbing.jcpp.infrastructure.util.trace.Tracer; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto; import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; +import sanbing.jcpp.protocol.adapter.DownlinkController; /** * @author baigod */ -public interface DownlinkCallService { +@Slf4j +public abstract class DownlinkCallService { - void sendDownlinkMessage(DownlinkRequestMessage.Builder downlinkMessageBuilder, String pileCode); + @Resource + protected ServiceInfoProvider serviceInfoProvider; + + @Resource + protected DownlinkController downlinkController; + + @Resource + protected TransactionalCache pileSessionCache; + + @Value("${cache.type}") + protected String cacheType; + + public void sendDownlinkMessage(DownlinkRequestMessage.Builder downlinkMessageBuilder, String pileCode) { + CacheValueWrapper pileSessionCacheValueWrapper = pileSessionCache.get(new PileSessionCacheKey(pileCode)); + + if (pileSessionCacheValueWrapper == null) { + log.warn("充电桩会话不存在 {}", pileCode); + return; + } + + PileSession pileSession = pileSessionCacheValueWrapper.get(); + + if (serviceInfoProvider.isMonolith() && + ("caffeine".equalsIgnoreCase(cacheType)) || serviceInfoProvider.getServiceId().equalsIgnoreCase(pileSession.getNodeId())) { + + downlinkController.onDownlink(downlinkMessageBuilder.build()) + .setResultHandler(result -> log.debug("下行消息发送完成")); + + } else { + Tracer currentTracer = TracerContextUtil.getCurrentTracer(); + + downlinkMessageBuilder.setTracer(ProtocolProto.TracerProto.newBuilder() + .setId(currentTracer.getTraceId()) + .setOrigin(currentTracer.getOrigin()) + .setTs(currentTracer.getTracerTs()) + .build()); + + + _sendDownlinkMessage(downlinkMessageBuilder.build(), pileSession); + } + } + + + protected abstract void _sendDownlinkMessage(DownlinkRequestMessage downlinkMessage, PileSession pileSession); } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java new file mode 100644 index 0000000..647fd49 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java @@ -0,0 +1,280 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.grpc; + +import com.google.common.net.HostAndPort; +import io.grpc.CompressorRegistry; +import io.grpc.ConnectivityState; +import io.grpc.DecompressorRegistry; +import io.grpc.ManagedChannel; +import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; +import io.grpc.netty.shaded.io.netty.channel.ChannelOption; +import io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoopGroup; +import io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel; +import io.grpc.stub.StreamObserver; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; +import sanbing.jcpp.proto.gen.ProtocolDownlinkInterfaceGrpc; +import sanbing.jcpp.proto.gen.ProtocolDownlinkInterfaceGrpc.ProtocolDownlinkInterfaceStub; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkResponseMessage; + +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author baigod + */ +@Component +@Slf4j +public class DownlinkGrpcClient { + + @Value("${downlink.rpc.grpc.netty.event_loop:}") + private Integer rpcNettyEventLoop; + + @Value("${downlink.rpc.grpc.netty.so_sndbuf:65535}") + private Integer rpcNettySoSndbuf; + + @Value("${downlink.rpc.grpc.netty.so_rcvbuf:65535}") + private Integer rpcNettySoRcvbuf; + + @Value("${downlink.rpc.grpc.netty.no_delay:true}") + private boolean rpcNoDelay; + + @Value("${downlink.rpc.grpc.netty.max_inbound_message_size:33554432}") + private Integer rpcMaxInboundMessageSize; + + @Value("${downlink.rpc.grpc.keep_alive_time_sec:300}") + private int keepAliveTimeSec; + + @Value("${downlink.rpc.grpc.max_records_size:102400}") + private int maxRecordsSize; + + @Value("${downlink.rpc.grpc.batch_records_count:1024}") + private int batchRecordsCount; + + @Value("${downlink.rpc.grpc.no_read_records_sleep:25}") + private long noRecordsSleepInterval; + + @Value("${downlink.rpc.grpc.records_ttl:600000}") + private long recordsTtl; + + private volatile boolean initialized = true; + + private final Map channelMap = new ConcurrentHashMap<>(); + private final Map> inputStreamMap = new ConcurrentHashMap<>(); + private final Map> queueMap = new ConcurrentHashMap<>(); + private final Map msgHandleLocks = new ConcurrentHashMap<>(); + private final Map msgHandleExecutors = new ConcurrentHashMap<>(); + private ExecutorService grpcStarterExecutor; + private ScheduledExecutorService grpcStateCheckExecutor; + private ScheduledExecutorService downlinkMsgsExecutor; + + @PostConstruct + public void init() { + grpcStarterExecutor = Executors.newSingleThreadExecutor(JCPPThreadFactory.forName("grpc-starter-executor")); + grpcStateCheckExecutor = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("grpc-check-executor")); + downlinkMsgsExecutor = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("downlink-msgs-executor")); + + // 每秒进行一次连接检查与线程初始化 + downlinkMsgsExecutor.scheduleWithFixedDelay(() -> { + queueMap.forEach((key, queue) -> { + + if (queue.isEmpty()) { + return; + } + + ManagedChannel managedChannel = channelMap.get(key); + + if (managedChannel == null || managedChannel.getState(false) != ConnectivityState.READY) { + grpcStarterExecutor.submit(new TracerRunnable(() -> connect(key))); + return; + } + + msgHandleExecutors.computeIfAbsent(key, hostAndPort -> + Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("downlink-handle-threads-" + hostAndPort))) + .execute(new TracerRunnable(() -> { + while (initialized) { + try { + handleMsgs(key, queue); + } catch (Exception e) { + log.error("Failed to process messages handling!", e); + } + } + })); + }); + + }, 0, 1, TimeUnit.SECONDS); + + grpcStateCheckExecutor.scheduleWithFixedDelay(() -> { + channelMap.forEach((key, channel) -> { + ConnectivityState state = channel.getState(true); + + if (state == ConnectivityState.SHUTDOWN) { + log.info("Grpc 客户端SHUTDOWN {} {}", key, state); + + LinkedBlockingQueue queue = queueMap.get(key); + if (queue != null) { + queue.clear(); + queueMap.remove(key); + } + + ExecutorService executorService = msgHandleExecutors.get(key); + if (executorService != null) { + executorService.shutdownNow(); + msgHandleExecutors.remove(key); + } + } + }); + }, 0, 1, TimeUnit.SECONDS); + } + + private void handleMsgs(HostAndPort key, LinkedBlockingQueue queue) { + StreamObserver inputStream = inputStreamMap.get(key); + + if (inputStream == null) { + return; + } + + long acceptTs = System.currentTimeMillis() - recordsTtl; + + List downlinkMsgs = new ArrayList<>(batchRecordsCount); + + queue.drainTo(downlinkMsgs, batchRecordsCount); + + for (DownlinkRequestMessage msg : downlinkMsgs) { + + long ts = msg.getTracer().getTs(); + + if (ts > 0 && ts < acceptTs) { + + log.warn("[{}] 消息过期,直接丢弃 {}", key, ts); + + continue; + } + + + ReentrantLock lock = msgHandleLocks.computeIfAbsent(key, hostAndPort -> new ReentrantLock()); + + lock.lock(); + try { + inputStream.onNext(msg); + } finally { + lock.unlock(); + } + } + + + if (downlinkMsgs.isEmpty()) { + try { + Thread.sleep(noRecordsSleepInterval); + } catch (InterruptedException e) { + log.warn("Sleep interrupted!", e); + } + } else { + downlinkMsgs.clear(); + } + } + + @PreDestroy + public void destroy() { + log.info("Starting Grpc destroying process"); + + initialized = false; + + try { + + grpcStarterExecutor.shutdownNow(); + + grpcStateCheckExecutor.shutdownNow(); + + downlinkMsgsExecutor.shutdownNow(); + + msgHandleExecutors.values().forEach(ExecutorService::shutdownNow); + + inputStreamMap.values().forEach(StreamObserver::onCompleted); + + channelMap.values().forEach(ManagedChannel::shutdownNow); + + } catch (Exception e) { + log.error("Exception during disconnect", e); + } + } + + public void connect(HostAndPort hostAndPort) { + + if (channelMap.get(hostAndPort) != null && channelMap.get(hostAndPort).getState(true).ordinal() < 2) { + return; + } + + log.info("[{}] Create new Grpc Client Channel!", hostAndPort); + + ManagedChannel managedChannel = NettyChannelBuilder.forAddress(hostAndPort.getHost(), hostAndPort.getPort()) + .eventLoopGroup(new NioEventLoopGroup(Optional.ofNullable(rpcNettyEventLoop).orElse(Runtime.getRuntime().availableProcessors() * 2))) + .compressorRegistry(CompressorRegistry.getDefaultInstance()) + .decompressorRegistry(DecompressorRegistry.getDefaultInstance()) + .withOption(ChannelOption.SO_SNDBUF, rpcNettySoSndbuf) + .withOption(ChannelOption.SO_RCVBUF, rpcNettySoRcvbuf) + .withOption(ChannelOption.TCP_NODELAY, rpcNoDelay) + .maxInboundMessageSize(rpcMaxInboundMessageSize) + .channelType(NioSocketChannel.class) + .directExecutor() + .keepAliveTime(keepAliveTimeSec, TimeUnit.SECONDS) + .usePlaintext() + .keepAliveTime(5, TimeUnit.MINUTES) // Change to a larger value, e.g. 5min. + .keepAliveTimeout(10, TimeUnit.SECONDS) // Change to a larger value, e.g. 10s. + .keepAliveWithoutCalls(true)// You should normally avoid enabling this. + .defaultLoadBalancingPolicy("round_robin") + .build(); + + log.info("Grpc 客户端READY {} {}", hostAndPort, managedChannel.getState(true)); + + ManagedChannel remove = channelMap.remove(hostAndPort); + + if (remove != null) { + channelMap.get(hostAndPort).shutdownNow(); + } + + channelMap.put(hostAndPort, managedChannel); + + ProtocolDownlinkInterfaceStub stub = ProtocolDownlinkInterfaceGrpc.newStub(managedChannel); + + StreamObserver streamObserver = stub.onDownlink(new StreamObserver<>() { + @Override + public void onNext(DownlinkResponseMessage value) { + log.info("Grpc 接收到通信层反向回复 {}", value); + } + + @Override + public void onError(Throwable t) { + log.warn("Grpc 客户端异常 {}", t.getMessage()); + } + + @Override + public void onCompleted() { + log.info("[{}] The Grpc connection was closed!", hostAndPort); + } + }); + + inputStreamMap.put(hostAndPort, streamObserver); + + } + + /** + * 发送下行请求 + */ + public void sendDownlinkRequest(HostAndPort hostAndPort, DownlinkRequestMessage downlinkRequestMessage) { + queueMap.computeIfAbsent(hostAndPort, k -> new LinkedBlockingQueue<>(maxRecordsSize)).add(downlinkRequestMessage); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java deleted file mode 100644 index a29221f..0000000 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultDownlinkCallService.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * 抖音关注:程序员三丙 - * 知识星球:https://t.zsxq.com/j9b21 - */ -package sanbing.jcpp.app.service.impl; - -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; -import sanbing.jcpp.app.data.PileSession; -import sanbing.jcpp.app.service.DownlinkCallService; -import sanbing.jcpp.app.service.cache.session.PileSessionCacheKey; -import sanbing.jcpp.infrastructure.cache.CacheValueWrapper; -import sanbing.jcpp.infrastructure.cache.TransactionalCache; -import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; -import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; -import sanbing.jcpp.protocol.adapter.DownlinkController; - -import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; - -/** - * @author baigod - */ -@Service -@Slf4j -public class DefaultDownlinkCallService implements DownlinkCallService { - - @Resource - RestTemplate downlinkRestTemplate; - - @Resource - ServiceInfoProvider serviceInfoProvider; - - @Resource - DownlinkController downlinkController; - - @Resource - TransactionalCache pileSessionCache; - - @Value("${cache.type}") - private String cacheType; - - @Override - public void sendDownlinkMessage(DownlinkRequestMessage.Builder downlinkMessageBuilder, String pileCode) { - if (serviceInfoProvider.isMonolith() && "caffeine".equalsIgnoreCase(cacheType)) { - - downlinkController.onDownlink(downlinkMessageBuilder.build()) - .setResultHandler(result -> log.debug("下行消息发送完成")); - - } else { - try { - CacheValueWrapper pileSessionCacheValueWrapper = pileSessionCache.get(new PileSessionCacheKey(pileCode)); - - if (pileSessionCacheValueWrapper == null) { - log.warn("充电桩会话不存在 {}", pileCode); - return; - } - - PileSession pileSession = pileSessionCacheValueWrapper.get(); - - invokeDownlinkRestApi(downlinkMessageBuilder.build(), pileSession.getNodeWebapiIpPort()); - - - } catch (RestClientException e) { - log.error("下行消息发送异常", e); - } - } - } - - private void invokeDownlinkRestApi(DownlinkRequestMessage DownlinkRequestMessage, String nodeWebapiIpPort) { - HttpHeaders headers = new HttpHeaders(); - headers.add(JCPP_TRACER_ID, TracerContextUtil.getCurrentTracer().getTraceId()); - headers.add(JCPP_TRACER_ORIGIN, TracerContextUtil.getCurrentTracer().getOrigin()); - headers.add(JCPP_TRACER_TS, String.valueOf(TracerContextUtil.getCurrentTracer().getTracerTs())); - headers.setContentType(MediaType.parseMediaType("application/x-protobuf")); - - HttpEntity entity = new HttpEntity<>(DownlinkRequestMessage, headers); - - try { - ResponseEntity response = downlinkRestTemplate.postForEntity("http://" + nodeWebapiIpPort + "/api/onDownlink", - entity, ResponseEntity.class); - log.debug("下行消息发送成功 {}", response); - } catch (RestClientException e) { - log.error("下行消息发送失败 {}", DownlinkRequestMessage, e); - throw new RuntimeException(e); - } - - } -} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index f7ccab0..d6207f3 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -67,7 +67,9 @@ public class DefaultPileProtocolService implements PileProtocolService { cacheSession(uplinkQueueMessage, pile, loginRequest.getRemoteAddress(), loginRequest.getNodeId(), - loginRequest.getNodeWebapiIpPort()); + loginRequest.getNodeHostAddress(), + loginRequest.getNodeRestPort(), + loginRequest.getNodeGrpcPort()); downlinkMessageBuilder.setLoginResponse(LoginResponse.newBuilder() .setSuccess(true) @@ -98,16 +100,26 @@ public class DefaultPileProtocolService implements PileProtocolService { cacheSession(uplinkQueueMessage, pile, heartBeatRequest.getRemoteAddress(), heartBeatRequest.getNodeId(), - heartBeatRequest.getNodeWebapiIpPort()); + heartBeatRequest.getNodeHostAddress(), + heartBeatRequest.getNodeRestPort(), + heartBeatRequest.getNodeGrpcPort()); } } - private void cacheSession(UplinkQueueMessage uplinkQueueMessage, Pile pile, String remoteAddress, String nodeId, String nodeWebapiIpPort) { + private void cacheSession(UplinkQueueMessage uplinkQueueMessage, + Pile pile, + String remoteAddress, + String nodeId, + String nodeIp, + int restPort, + int grpcPort) { PileSession pileSession = new PileSession(pile.getId(), pile.getPileCode(), uplinkQueueMessage.getProtocolName()); pileSession.setProtocolSessionId(new UUID(uplinkQueueMessage.getSessionIdMSB(), uplinkQueueMessage.getSessionIdLSB())); pileSession.setRemoteAddress(remoteAddress); pileSession.setNodeId(nodeId); - pileSession.setNodeWebapiIpPort(nodeWebapiIpPort); + pileSession.setNodeIp(nodeIp); + pileSession.setNodeRestPort(restPort); + pileSession.setNodeGrpcPort(grpcPort); pileSessionCache.put(new PileSessionCacheKey(pile.getPileCode()), pileSession); } diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java new file mode 100644 index 0000000..59c6e90 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java @@ -0,0 +1,39 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.impl; + +import com.google.common.net.HostAndPort; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.app.service.DownlinkCallService; +import sanbing.jcpp.app.service.grpc.DownlinkGrpcClient; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; + +/** + * @author baigod + */ +@Service +@Slf4j +@ConditionalOnExpression("'${downlink.rpc.type:null}'=='grpc'") +public class GrpcDownlinkCallService extends DownlinkCallService { + + @Resource + DownlinkGrpcClient downlinkGrpcClient; + + @Override + protected void _sendDownlinkMessage(DownlinkRequestMessage downlinkMessage, PileSession pileSession) { + try { + + downlinkGrpcClient.sendDownlinkRequest(HostAndPort.fromParts(pileSession.getNodeIp(), pileSession.getNodeGrpcPort()), + downlinkMessage); + + } catch (Exception e) { + log.error("下行消息发送异常", e); + } + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java new file mode 100644 index 0000000..795c949 --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java @@ -0,0 +1,64 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.service.impl; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; +import sanbing.jcpp.app.data.PileSession; +import sanbing.jcpp.app.service.DownlinkCallService; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; + +import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; + +/** + * @author baigod + */ +@Service +@Slf4j +@ConditionalOnExpression("'${downlink.rpc.type:null}'=='rest'") +public class RestDownlinkCallService extends DownlinkCallService { + + @Resource + RestTemplate downlinkRestTemplate; + + @Override + protected void _sendDownlinkMessage(DownlinkRequestMessage downlinkMessage, PileSession pileSession) { + try { + + invokeDownlinkRestApi(downlinkMessage, pileSession.getNodeIp(), pileSession.getNodeRestPort()); + + } catch (RestClientException e) { + log.error("下行消息发送异常", e); + } + } + + private void invokeDownlinkRestApi(DownlinkRequestMessage downlinkRequestMessage, String nodeWebapiIpPort, int nodeRestPort) { + HttpHeaders headers = new HttpHeaders(); + headers.add(JCPP_TRACER_ID, TracerContextUtil.getCurrentTracer().getTraceId()); + headers.add(JCPP_TRACER_ORIGIN, TracerContextUtil.getCurrentTracer().getOrigin()); + headers.add(JCPP_TRACER_TS, String.valueOf(TracerContextUtil.getCurrentTracer().getTracerTs())); + headers.setContentType(MediaType.parseMediaType("application/x-protobuf")); + + HttpEntity entity = new HttpEntity<>(downlinkRequestMessage, headers); + + try { + ResponseEntity response = downlinkRestTemplate.postForEntity("http://" + nodeWebapiIpPort + ":" + nodeRestPort + "/api/onDownlink", + entity, ResponseEntity.class); + log.debug("下行消息发送成功 {}", response); + } catch (RestClientException e) { + log.error("下行消息发送失败 {}", downlinkRequestMessage, e); + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/jcpp-infrastructure-proto/pom.xml b/jcpp-infrastructure-proto/pom.xml index 82e15a5..b625d3a 100644 --- a/jcpp-infrastructure-proto/pom.xml +++ b/jcpp-infrastructure-proto/pom.xml @@ -41,17 +41,14 @@ io.grpc grpc-netty-shaded - provided io.grpc grpc-protobuf - provided io.grpc grpc-stub - provided diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index 2ad68f5..a34aa90 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -13,6 +13,12 @@ service ProtocolDownlinkInterface { rpc onDownlink(stream DownlinkRequestMessage) returns (stream DownlinkResponseMessage) {} } +message TracerProto { + string id = 1; + string origin = 2; + int64 ts = 3; +} + message UplinkQueueMessage { int64 messageIdMSB = 1; int64 messageIdLSB = 2; @@ -43,14 +49,15 @@ message DownlinkRequestMessage { optional int64 requestIdMSB = 8; optional int64 requestIdLSB = 9; optional bytes requestData = 10; - string downlinkCmd = 11; - LoginResponse loginResponse = 20; - VerifyPricingResponse verifyPricingResponse = 21; - QueryPricingResponse queryPricingResponse = 22; - SetPricingRequest setPricingRequest = 23; - RemoteStartChargingRequest remoteStartChargingRequest = 24; - RemoteStopChargingRequest remoteStopChargingRequest = 25; - TransactionRecordAck transactionRecordAck = 26; + TracerProto tracer = 12; + string downlinkCmd = 20; + LoginResponse loginResponse = 21; + VerifyPricingResponse verifyPricingResponse = 22; + QueryPricingResponse queryPricingResponse = 23; + SetPricingRequest setPricingRequest = 24; + RemoteStartChargingRequest remoteStartChargingRequest = 25; + RemoteStopChargingRequest remoteStopChargingRequest = 26; + TransactionRecordAck transactionRecordAck = 27; } message DownlinkResponseMessage { @@ -63,7 +70,9 @@ message LoginRequest { string credential = 3; string remoteAddress = 4; string nodeId = 10; - string nodeWebapiIpPort = 11; + string nodeHostAddress = 11; + int32 nodeRestPort = 12; + int32 nodeGrpcPort = 13; optional string additionalInfo = 20; } @@ -76,7 +85,9 @@ message HeartBeatRequest { string pileCode = 3; string remoteAddress = 4; string nodeId = 10; - string nodeWebapiIpPort = 11; + string nodeHostAddress = 11; + int32 nodeRestPort = 12; + int32 nodeGrpcPort = 13; optional string additionalInfo = 20; } diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java index 3667dfc..da7d47d 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/ProtoQueueMsg.java @@ -4,10 +4,11 @@ */ package sanbing.jcpp.infrastructure.queue; +import com.google.protobuf.GeneratedMessage; import lombok.Data; @Data -public class ProtoQueueMsg implements QueueMsg { +public class ProtoQueueMsg implements QueueMsg { private final String key; protected final T value; diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java index 055610f..2c8a1f9 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java @@ -19,7 +19,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; /** @@ -42,15 +41,18 @@ public class DefaultServiceInfoProvider implements ServiceInfoProvider { private ServiceInfo serviceInfo; @Getter - private String serviceWebapiEndpoint; + private String hostAddress; + @Getter @Value("${server.port}") - private String webapiPort; + private int restPort; + + @Getter + @Value("${service.protocol.rpc.port:9090}") + private int grpcPort; @PostConstruct public void init() throws UnknownHostException { - - if (!StringUtils.hasText(this.serviceId)) { try { this.serviceId = InetAddress.getLocalHost().getHostName(); @@ -58,10 +60,11 @@ public class DefaultServiceInfoProvider implements ServiceInfoProvider { this.serviceId = RandomStringUtils.randomAlphabetic(10); } } - log.info("Current Service ID: {}", this.serviceId); + log.info("Current Service ID: {}", serviceId); - serviceWebapiEndpoint = InetAddress.getLocalHost().getHostAddress() + ":" + webapiPort; - log.info("Current Service HostAddress: {}", this.serviceWebapiEndpoint); + hostAddress = InetAddress.getLocalHost().getHostAddress(); + + log.info("Current Service HostAddress: {}, RestPort:{}, GrpcPort:{}", hostAddress, restPort, grpcPort); if (serviceType.equalsIgnoreCase("monolith")) { serviceTypes = List.of(ServiceType.values()); } else { @@ -86,7 +89,7 @@ public class DefaultServiceInfoProvider implements ServiceInfoProvider { public ServiceInfo generateNewServiceInfoWithCurrentSystemInfo() { ServiceInfo.Builder builder = ServiceInfo.newBuilder() .setServiceId(serviceId) - .addAllServiceTypes(serviceTypes.stream().map(ServiceType::name).collect(Collectors.toList())) + .addAllServiceTypes(serviceTypes.stream().map(ServiceType::name).toList()) .setSystemInfo(getCurrentSystemInfoProto()); return serviceInfo = builder.build(); } diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java index 294e2a6..dc22196 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/ServiceInfoProvider.java @@ -13,7 +13,11 @@ import sanbing.jcpp.proto.gen.ClusterProto; public interface ServiceInfoProvider { String getServiceId(); - String getServiceWebapiEndpoint(); + String getHostAddress(); + + int getRestPort(); + + int getGrpcPort(); String getServiceType(); diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java index 8a6eec5..a2ea23f 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/processing/IdMsgPair.java @@ -4,12 +4,13 @@ */ package sanbing.jcpp.infrastructure.queue.processing; +import com.google.protobuf.GeneratedMessage; import lombok.Getter; import sanbing.jcpp.infrastructure.queue.ProtoQueueMsg; import java.util.UUID; -public class IdMsgPair { +public class IdMsgPair { @Getter final UUID uuid; @Getter diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java new file mode 100644 index 0000000..82107d5 --- /dev/null +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java @@ -0,0 +1,159 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.protocol.adapter; + +import io.grpc.CompressorRegistry; +import io.grpc.DecompressorRegistry; +import io.grpc.Server; +import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder; +import io.grpc.netty.shaded.io.netty.channel.ChannelOption; +import io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoopGroup; +import io.grpc.netty.shaded.io.netty.channel.socket.nio.NioServerSocketChannel; +import io.grpc.stub.StreamObserver; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; +import sanbing.jcpp.proto.gen.ProtocolDownlinkInterfaceGrpc.ProtocolDownlinkInterfaceImplBase; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkResponseMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.TracerProto; +import sanbing.jcpp.protocol.domain.ProtocolSession; +import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; + +import javax.annotation.PreDestroy; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL; + +/** + * @author baigod + */ +@Service +@Slf4j +@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='protocol'") +public class DownlinkGrpcService extends ProtocolDownlinkInterfaceImplBase { + @Value("${service.protocol.rpc.port}") + private int rpcPort; + @Value("${service.protocol.rpc.boss}") + private int rpcBoss; + @Value("${service.protocol.rpc.worker}") + private int rpcWorker; + @Value("${service.protocol.rpc.so-rcvbuf}") + private int rpcNettySoRcvbuf; + @Value("${service.protocol.rpc.so-sndbuf}") + private int rpcNettySoSndbuf; + @Value("${service.protocol.rpc.no-delay}") + private boolean rpcNettyNoDelay; + @Value("${service.protocol.rpc.max-inbound-message-size}") + private int maxInboundMessageSize; + @Value("${service.protocol.rpc.max-concurrent-calls-per-connection}") + private int maxConcurrentCallsPerConnection; + @Value("${service.protocol.rpc.client-max-keep-alive-time-sec}") + private int clientMaxKeepAliveTimeSec; + + @Resource + ProtocolSessionRegistryProvider protocolSessionRegistryProvider; + + private Server server; + + @PostConstruct + public void init() throws Exception { + log.info("Initializing Protocol Downlink Grpc service!"); + + NettyServerBuilder builder = NettyServerBuilder.forPort(this.rpcPort) + .bossEventLoopGroup(new NioEventLoopGroup(this.rpcBoss)) + .workerEventLoopGroup(new NioEventLoopGroup(this.rpcWorker)) + .withOption(ChannelOption.SO_RCVBUF, rpcNettySoRcvbuf) + .withChildOption(ChannelOption.SO_SNDBUF, rpcNettySoSndbuf) + .withChildOption(ChannelOption.TCP_NODELAY, rpcNettyNoDelay) + .compressorRegistry(CompressorRegistry.getDefaultInstance()) + .decompressorRegistry(DecompressorRegistry.getDefaultInstance()) + .channelType(NioServerSocketChannel.class) + .permitKeepAliveTime(this.clientMaxKeepAliveTimeSec, TimeUnit.SECONDS) + .maxInboundMessageSize(maxInboundMessageSize) + .maxConcurrentCallsPerConnection(maxConcurrentCallsPerConnection) + .directExecutor() + .keepAliveTime(5, TimeUnit.MINUTES) + .keepAliveTimeout(10, TimeUnit.SECONDS) + .permitKeepAliveWithoutCalls(true) + .addService(this); + + this.server = builder.build(); + log.info("Going to start RPC server using port: {}", this.rpcPort); + + try { + this.server.start(); + } catch (Exception e) { + log.error("Failed to start RPC server!", e); + throw e; + } + + log.info("Protocol Downlink Grpc service initialized!"); + } + + @PreDestroy + public void destroy() { + if (this.server != null) { + this.server.shutdownNow(); + } + } + + @Override + public StreamObserver onDownlink(StreamObserver responseObserver) { + return new StreamObserver<>() { + @Override + public void onNext(DownlinkRequestMessage downlinkMsg) { + TracerProto tracerProto = downlinkMsg.getTracer(); + TracerContextUtil.newTracer(tracerProto.getId(), tracerProto.getOrigin(), tracerProto.getTs()); + MDCUtils.recordTracer(); + + log.debug("收到Grpc下行请求 {}", downlinkMsg); + + JCPP_COMMON_THREAD_POOL.execute(new TracerRunnable(() -> { + UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(), downlinkMsg.getSessionIdLSB()); + + ProtocolSession protocolSession = protocolSessionRegistryProvider.get(protocolSessionId); + + try { + if (protocolSession != null) { + + protocolSession.onDownlink(downlinkMsg); + + } else { + + log.info("下发报文时Session未找到 sessionId: {}", protocolSessionId); + + } + } catch (Exception e) { + + log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, e); + + } + })); + } + + @Override + public void onError(Throwable t) { + log.error("Failed to deliver message from client!", t); + } + + @Override + public void onCompleted() { + try { + responseObserver.onCompleted(); + } catch (Exception e) { + log.error("onCompleted error ", e); + } + } + }; + } +} \ No newline at end of file diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java index ae823b0..65df293 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolSessionRegistryProvider.java @@ -34,10 +34,10 @@ public class DefaultProtocolSessionRegistryProvider implements ProtocolSessionRe private static final int INIT_CACHE_LIMIT = 100_000; private static final int MAXIMUM_SIZE = 1_000_000; - @Value("${service.protocols.sessions.default-inactivity-timeout-in-sec}") + @Value("${service.protocol.sessions.default-inactivity-timeout-in-sec}") private int defaultInactivityTimeoutInSec; - @Value("${service.protocols.sessions.default-state-check-interval-in-sec}") + @Value("${service.protocol.sessions.default-state-check-interval-in-sec}") private int defaultStateCheckIntervalInSec; @Getter diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index f01a893..7d14108 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -40,10 +40,23 @@ service: type: "${SERVICE_TYPE:protocol}" # 可自定义的服务ID,如果不指定,则默认为HOSTNAME id: "${SERVICE_ID:}" - protocols: + protocol: sessions: default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" default-state-check-interval-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" + rpc: + enabled: "${SERVICE_PROTOCOL_RPC_ENABLED:true}" + port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" + boss: "${SERVICE_PROTOCOL_RPC_BOSS:4}" + worker: "${SERVICE_PROTOCOL_RPC_WORKER:64}" + so-rcvbuf: "${SERVICE_PROTOCOL_RPC_SO_RCVBUF:65535}" + so-sndbuf: "${SERVICE_PROTOCOL_RPC_SO_SNDBUF:65535}" + no-delay: "${SERVICE_PROTOCOL_RPC_NO_DELAY:true}" + user-thread-pool-size: "${SERVICE_PROTOCOL_RPC_USER_THREAD_POOL_SIZE:1024}" + max-inbound-message-size: "${SERVICE_PROTOCOL_RPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" + max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" + client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" + protocols: yunkuaichongV150: enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" listener: @@ -184,3 +197,4 @@ thread-pool: hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" + diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java index 40fc271..183c3fb 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java @@ -54,7 +54,9 @@ public class YunKuaiChongV150HeartbeatULCmd extends YunKuaiChongUplinkCmdExe { .setPileCode(pileCode) .setRemoteAddress(tcpSession.getAddress().toString()) .setNodeId(ctx.getServiceInfoProvider().getServiceId()) - .setNodeWebapiIpPort(ctx.getServiceInfoProvider().getServiceWebapiEndpoint()) + .setNodeHostAddress(ctx.getServiceInfoProvider().getHostAddress()) + .setNodeRestPort(ctx.getServiceInfoProvider().getRestPort()) + .setNodeGrpcPort(ctx.getServiceInfoProvider().getGrpcPort()) .setAdditionalInfo(additionalInfo.toString()) .build(); UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(heartBeatRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java index 99fe876..e311860 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java @@ -66,7 +66,9 @@ public class YunKuaiChongV150LoginULCmd extends YunKuaiChongUplinkCmdExe { .setCredential(pileCode) .setRemoteAddress(tcpSession.getAddress().toString()) .setNodeId(ctx.getServiceInfoProvider().getServiceId()) - .setNodeWebapiIpPort(ctx.getServiceInfoProvider().getServiceWebapiEndpoint()) + .setNodeHostAddress(ctx.getServiceInfoProvider().getHostAddress()) + .setNodeRestPort(ctx.getServiceInfoProvider().getRestPort()) + .setNodeGrpcPort(ctx.getServiceInfoProvider().getGrpcPort()) .setAdditionalInfo(additionalInfo.toString()) .build(); UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(loginRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) diff --git a/pom.xml b/pom.xml index 3a608a7..3fcf2fd 100644 --- a/pom.xml +++ b/pom.xml @@ -40,10 +40,10 @@ 1.7.0 3.4.4 - 3.21.9 4.0.2 - 1.56.1 - 0.5.1 + 4.28.2 + 1.68.0 + 0.6.1 3.13.0 5.8.32 3.5.7 From 60295822cd08bc6b38395238edf41cfe3a5f645f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 23 Oct 2024 17:24:44 +0800 Subject: [PATCH 068/102] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4red?= =?UTF-8?q?is=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 67cfc18..a26fae3 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -146,9 +146,9 @@ redis: db: "${REDIS_DB:0}" password: "${REDIS_PASSWORD:sanbing}" pool_config: - maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" - maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:64}" - minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:256}" + maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:64}" testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:false}" testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:false}" testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" From 4edfc8dfe2f73ddebf329e8561bedc12e56d99d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 23 Oct 2024 11:38:03 +0000 Subject: [PATCH 069/102] update README.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 三丙 <10604541+sanbing-os@user.noreply.gitee.com> --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 64f5f67..b6babc6 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ ![抖音二维码](https://foruda.gitee.com/images/1728650678915895016/b2219d50_10604541.png) ##### 我建了个微信技术交流群 -![微信交流群](https://foruda.gitee.com/images/1728650750348929723/c8b55505_10604541.png) +如过期请加个人微信 +![微信交流群](https://foruda.gitee.com/images/1729683365750232573/8b2574af_10604541.png "微信交流群") +![博主个人微信](https://foruda.gitee.com/images/1729683391329244317/bfefcca3_10604541.jpeg "博主个人微信") ------------------------------ #### 参与贡献 From 76f9d5d3dcaea45636151a2c9f4719aca4199c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 24 Oct 2024 15:41:26 +0800 Subject: [PATCH 070/102] =?UTF-8?q?grpc=20=E5=A2=9E=E5=8A=A0=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/adapter/TestController.java | 31 ++++ .../jcpp/app/service/DownlinkCallService.java | 25 +-- .../jcpp/app/service/PileProtocolService.java | 7 + .../app/service/grpc/DownlinkGrpcClient.java | 157 ++++++++++-------- .../impl/DefaultPileProtocolService.java | 25 +++ .../service/impl/GrpcDownlinkCallService.java | 11 +- jcpp-infrastructure-proto/pom.xml | 4 + .../infrastructure/proto/ProtoConverter.java | 11 ++ .../src/main/proto/protocol.proto | 34 +++- .../util/trace/TracerContextUtil.java | 8 + .../protocol/adapter/DownlinkGrpcService.java | 67 +++++--- .../adapter/DownlinkControllerIT.java | 2 +- .../YunKuaiChongV150RemoteStartDLCmd.java | 6 +- ...KuaiChongV160RemoteParallelStartDLCmd.java | 5 +- 14 files changed, 278 insertions(+), 115 deletions(-) create mode 100644 jcpp-app/src/main/java/sanbing/jcpp/app/adapter/TestController.java diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/adapter/TestController.java b/jcpp-app/src/main/java/sanbing/jcpp/app/adapter/TestController.java new file mode 100644 index 0000000..074fcdf --- /dev/null +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/adapter/TestController.java @@ -0,0 +1,31 @@ +/** + * 抖音关注:程序员三丙 + * 知识星球:https://t.zsxq.com/j9b21 + */ +package sanbing.jcpp.app.adapter; + +import jakarta.annotation.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import sanbing.jcpp.app.service.PileProtocolService; + +import java.math.BigDecimal; + +/** + * @author baigod + */ +@RestController +public class TestController { + + @Resource + private PileProtocolService pileProtocolService; + + @GetMapping("/api/startCharge") + public ResponseEntity startCharge() { + + pileProtocolService.startCharge("20231212000010", "01", new BigDecimal("50"), "12345678901234567890"); + + return ResponseEntity.ok("success"); + } +} \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java index 2162c56..61990d1 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/DownlinkCallService.java @@ -12,12 +12,11 @@ import sanbing.jcpp.app.service.cache.session.PileSessionCacheKey; import sanbing.jcpp.infrastructure.cache.CacheValueWrapper; import sanbing.jcpp.infrastructure.cache.TransactionalCache; import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; -import sanbing.jcpp.infrastructure.util.trace.Tracer; -import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; -import sanbing.jcpp.proto.gen.ProtocolProto; import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; import sanbing.jcpp.protocol.adapter.DownlinkController; +import java.util.UUID; + /** * @author baigod */ @@ -46,6 +45,18 @@ public abstract class DownlinkCallService { PileSession pileSession = pileSessionCacheValueWrapper.get(); + UUID protocolSessionId = pileSession.getProtocolSessionId(); + + if (downlinkMessageBuilder.getSessionIdMSB() == 0) { + downlinkMessageBuilder.setSessionIdMSB(protocolSessionId.getMostSignificantBits()); + } + if (downlinkMessageBuilder.getSessionIdLSB() == 0) { + downlinkMessageBuilder.setSessionIdLSB(protocolSessionId.getLeastSignificantBits()); + } + if(downlinkMessageBuilder.getProtocolName() == null){ + downlinkMessageBuilder.setProtocolName(pileSession.getProtocolName()); + } + if (serviceInfoProvider.isMonolith() && ("caffeine".equalsIgnoreCase(cacheType)) || serviceInfoProvider.getServiceId().equalsIgnoreCase(pileSession.getNodeId())) { @@ -53,14 +64,6 @@ public abstract class DownlinkCallService { .setResultHandler(result -> log.debug("下行消息发送完成")); } else { - Tracer currentTracer = TracerContextUtil.getCurrentTracer(); - - downlinkMessageBuilder.setTracer(ProtocolProto.TracerProto.newBuilder() - .setId(currentTracer.getTraceId()) - .setOrigin(currentTracer.getOrigin()) - .setTs(currentTracer.getTracerTs()) - .build()); - _sendDownlinkMessage(downlinkMessageBuilder.build(), pileSession); } diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java index 76c34ed..3290270 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java @@ -7,6 +7,8 @@ package sanbing.jcpp.app.service; import sanbing.jcpp.infrastructure.queue.Callback; import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import java.math.BigDecimal; + /** * @author baigod */ @@ -63,4 +65,9 @@ public interface PileProtocolService { * 交易记录上报 */ void onTransactionRecord(UplinkQueueMessage uplinkQueueMessage, Callback callback); + + /** + * 启动充电 + */ + void startCharge(String pileCode, String gunCode, BigDecimal limitYuan, String orderNo); } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java index 647fd49..dee6bc3 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java @@ -6,7 +6,6 @@ package sanbing.jcpp.app.service.grpc; import com.google.common.net.HostAndPort; import io.grpc.CompressorRegistry; -import io.grpc.ConnectivityState; import io.grpc.DecompressorRegistry; import io.grpc.ManagedChannel; import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; @@ -18,21 +17,27 @@ import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import sanbing.jcpp.infrastructure.queue.discovery.ServiceInfoProvider; import sanbing.jcpp.infrastructure.util.async.JCPPThreadFactory; +import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; -import sanbing.jcpp.proto.gen.ProtocolDownlinkInterfaceGrpc; -import sanbing.jcpp.proto.gen.ProtocolDownlinkInterfaceGrpc.ProtocolDownlinkInterfaceStub; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkResponseMessage; +import sanbing.jcpp.proto.gen.ProtocolInterfaceGrpc; +import sanbing.jcpp.proto.gen.ProtocolInterfaceGrpc.ProtocolInterfaceStub; +import sanbing.jcpp.proto.gen.ProtocolProto.*; import javax.annotation.PreDestroy; +import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; +import static sanbing.jcpp.infrastructure.proto.ProtoConverter.toTracerProto; + /** * @author baigod */ @@ -70,42 +75,39 @@ public class DownlinkGrpcClient { @Value("${downlink.rpc.grpc.records_ttl:600000}") private long recordsTtl; - private volatile boolean initialized = true; + @Value("${downlink.rpc.grpc.max_reconnect_times:10}") + private int maxReconnectTimes; + + @Resource + ServiceInfoProvider serviceInfoProvider; private final Map channelMap = new ConcurrentHashMap<>(); - private final Map> inputStreamMap = new ConcurrentHashMap<>(); - private final Map> queueMap = new ConcurrentHashMap<>(); - private final Map msgHandleLocks = new ConcurrentHashMap<>(); - private final Map msgHandleExecutors = new ConcurrentHashMap<>(); - private ExecutorService grpcStarterExecutor; - private ScheduledExecutorService grpcStateCheckExecutor; + private final Map> inputStreamMap = new ConcurrentHashMap<>(); + private final Map> queueMap = new ConcurrentHashMap<>(); + private final Map msgHandleLocksMap = new ConcurrentHashMap<>(); + private final Map msgHandleExecutorMap = new ConcurrentHashMap<>(); + private final Map connectErrTimesMap = new ConcurrentHashMap<>(); + private final Map initializedMap = new ConcurrentHashMap<>(); private ScheduledExecutorService downlinkMsgsExecutor; @PostConstruct public void init() { - grpcStarterExecutor = Executors.newSingleThreadExecutor(JCPPThreadFactory.forName("grpc-starter-executor")); - grpcStateCheckExecutor = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("grpc-check-executor")); downlinkMsgsExecutor = Executors.newSingleThreadScheduledExecutor(JCPPThreadFactory.forName("downlink-msgs-executor")); // 每秒进行一次连接检查与线程初始化 downlinkMsgsExecutor.scheduleWithFixedDelay(() -> { queueMap.forEach((key, queue) -> { - if (queue.isEmpty()) { - return; - } - ManagedChannel managedChannel = channelMap.get(key); - if (managedChannel == null || managedChannel.getState(false) != ConnectivityState.READY) { - grpcStarterExecutor.submit(new TracerRunnable(() -> connect(key))); - return; + if (managedChannel == null) { + connect(key); } - msgHandleExecutors.computeIfAbsent(key, hostAndPort -> + msgHandleExecutorMap.computeIfAbsent(key, hostAndPort -> Executors.newFixedThreadPool(1, JCPPThreadFactory.forName("downlink-handle-threads-" + hostAndPort))) .execute(new TracerRunnable(() -> { - while (initialized) { + while (Boolean.TRUE.equals(initializedMap.computeIfAbsent(key, k -> Boolean.FALSE))) { try { handleMsgs(key, queue); } catch (Exception e) { @@ -116,32 +118,10 @@ public class DownlinkGrpcClient { }); }, 0, 1, TimeUnit.SECONDS); - - grpcStateCheckExecutor.scheduleWithFixedDelay(() -> { - channelMap.forEach((key, channel) -> { - ConnectivityState state = channel.getState(true); - - if (state == ConnectivityState.SHUTDOWN) { - log.info("Grpc 客户端SHUTDOWN {} {}", key, state); - - LinkedBlockingQueue queue = queueMap.get(key); - if (queue != null) { - queue.clear(); - queueMap.remove(key); - } - - ExecutorService executorService = msgHandleExecutors.get(key); - if (executorService != null) { - executorService.shutdownNow(); - msgHandleExecutors.remove(key); - } - } - }); - }, 0, 1, TimeUnit.SECONDS); } - private void handleMsgs(HostAndPort key, LinkedBlockingQueue queue) { - StreamObserver inputStream = inputStreamMap.get(key); + private void handleMsgs(HostAndPort key, LinkedBlockingQueue queue) { + StreamObserver inputStream = inputStreamMap.get(key); if (inputStream == null) { return; @@ -149,13 +129,13 @@ public class DownlinkGrpcClient { long acceptTs = System.currentTimeMillis() - recordsTtl; - List downlinkMsgs = new ArrayList<>(batchRecordsCount); + List downlinkMsgs = new ArrayList<>(batchRecordsCount); queue.drainTo(downlinkMsgs, batchRecordsCount); - for (DownlinkRequestMessage msg : downlinkMsgs) { + for (RequestMsg msg : downlinkMsgs) { - long ts = msg.getTracer().getTs(); + long ts = msg.getTs(); if (ts > 0 && ts < acceptTs) { @@ -165,7 +145,7 @@ public class DownlinkGrpcClient { } - ReentrantLock lock = msgHandleLocks.computeIfAbsent(key, hostAndPort -> new ReentrantLock()); + ReentrantLock lock = msgHandleLocksMap.computeIfAbsent(key, hostAndPort -> new ReentrantLock()); lock.lock(); try { @@ -175,7 +155,6 @@ public class DownlinkGrpcClient { } } - if (downlinkMsgs.isEmpty()) { try { Thread.sleep(noRecordsSleepInterval); @@ -191,17 +170,12 @@ public class DownlinkGrpcClient { public void destroy() { log.info("Starting Grpc destroying process"); - initialized = false; + initializedMap.replaceAll((hostAndPort, aBoolean) -> Boolean.FALSE); try { - - grpcStarterExecutor.shutdownNow(); - - grpcStateCheckExecutor.shutdownNow(); - downlinkMsgsExecutor.shutdownNow(); - msgHandleExecutors.values().forEach(ExecutorService::shutdownNow); + msgHandleExecutorMap.values().forEach(ExecutorService::shutdownNow); inputStreamMap.values().forEach(StreamObserver::onCompleted); @@ -214,10 +188,6 @@ public class DownlinkGrpcClient { public void connect(HostAndPort hostAndPort) { - if (channelMap.get(hostAndPort) != null && channelMap.get(hostAndPort).getState(true).ordinal() < 2) { - return; - } - log.info("[{}] Create new Grpc Client Channel!", hostAndPort); ManagedChannel managedChannel = NettyChannelBuilder.forAddress(hostAndPort.getHost(), hostAndPort.getPort()) @@ -238,8 +208,6 @@ public class DownlinkGrpcClient { .defaultLoadBalancingPolicy("round_robin") .build(); - log.info("Grpc 客户端READY {} {}", hostAndPort, managedChannel.getState(true)); - ManagedChannel remove = channelMap.remove(hostAndPort); if (remove != null) { @@ -248,17 +216,55 @@ public class DownlinkGrpcClient { channelMap.put(hostAndPort, managedChannel); - ProtocolDownlinkInterfaceStub stub = ProtocolDownlinkInterfaceGrpc.newStub(managedChannel); + ProtocolInterfaceStub stub = ProtocolInterfaceGrpc.newStub(managedChannel); - StreamObserver streamObserver = stub.onDownlink(new StreamObserver<>() { + StreamObserver streamObserver = stub.onDownlink(new StreamObserver<>() { @Override - public void onNext(DownlinkResponseMessage value) { - log.info("Grpc 接收到通信层反向回复 {}", value); + public void onNext(ResponseMsg responseMsg) { + TracerProto tracerProto = responseMsg.getTracer(); + TracerContextUtil.newTracer(tracerProto.getId(), tracerProto.getOrigin(), tracerProto.getTs()); + MDCUtils.recordTracer(); + + if (responseMsg.hasConnectResponseMsg()) { + log.info("[{}] Grpc 接收到通信层连接反馈 {}", hostAndPort, responseMsg.getConnectResponseMsg()); + if (ConnectResponseCode.ACCEPTED.equals(responseMsg.getConnectResponseMsg().getResponseCode())) { + + initializedMap.put(hostAndPort, Boolean.TRUE); + + } else { + onError(new RuntimeException(responseMsg.getConnectResponseMsg().getErrorMsg())); + } + } + + if (responseMsg.hasDownlinkResponseMsg()) { + DownlinkResponseMessage downlinkResponseMsg = responseMsg.getDownlinkResponseMsg(); + if (!downlinkResponseMsg.getSuccess()) { + log.info("[{}] Grpc 下行数据发生错误回复 {}", hostAndPort, downlinkResponseMsg); + } + } } @Override public void onError(Throwable t) { - log.warn("Grpc 客户端异常 {}", t.getMessage()); + log.warn("[{}] Grpc 客户端异常 {}", hostAndPort, t.getMessage()); + + ExecutorService executorService = msgHandleExecutorMap.get(hostAndPort); + if (executorService != null) { + executorService.shutdownNow(); + msgHandleExecutorMap.remove(hostAndPort); + } + + ManagedChannel remove = channelMap.remove(hostAndPort); + + if (remove != null) { + remove.shutdownNow(); + } + + if (connectErrTimesMap.computeIfAbsent(hostAndPort, k -> new AtomicInteger()).incrementAndGet() >= maxReconnectTimes) { + queueMap.remove(hostAndPort); + connectErrTimesMap.remove(hostAndPort); + log.info("[{}] Grpc 客户端重连异常超过{}次,不再重连", hostAndPort, maxReconnectTimes); + } } @Override @@ -267,6 +273,13 @@ public class DownlinkGrpcClient { } }); + streamObserver.onNext(RequestMsg.newBuilder() + .setTracer(toTracerProto()) + .setConnectRequestMsg(ConnectRequestMsg.newBuilder() + .setNodeId(serviceInfoProvider.getServiceId()) + .build()) + .build()); + inputStreamMap.put(hostAndPort, streamObserver); } @@ -274,7 +287,7 @@ public class DownlinkGrpcClient { /** * 发送下行请求 */ - public void sendDownlinkRequest(HostAndPort hostAndPort, DownlinkRequestMessage downlinkRequestMessage) { - queueMap.computeIfAbsent(hostAndPort, k -> new LinkedBlockingQueue<>(maxRecordsSize)).add(downlinkRequestMessage); + public void sendDownlinkRequest(HostAndPort hostAndPort, RequestMsg requestMsg) { + queueMap.computeIfAbsent(hostAndPort, k -> new LinkedBlockingQueue<>(maxRecordsSize)).add(requestMsg); } } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index d6207f3..760b373 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -260,6 +260,31 @@ public class DefaultPileProtocolService implements PileProtocolService { callback.onSuccess(); } + @Override + public void startCharge(String pileCode, String gunCode, BigDecimal limitYuan, String orderNo) { + + + UUID messageId = UUID.randomUUID(); + UUID requestId = UUID.randomUUID(); + + DownlinkRequestMessage.Builder downlinkRequestMessageBuilder = DownlinkRequestMessage.newBuilder() + .setMessageIdMSB(messageId.getMostSignificantBits()) + .setMessageIdLSB(messageId.getLeastSignificantBits()) + .setPileCode(pileCode) + .setRequestIdMSB(requestId.getMostSignificantBits()) + .setRequestIdLSB(requestId.getLeastSignificantBits()) + .setDownlinkCmd(DownlinkCmdEnum.REMOTE_START_CHARGING.name()) + .setRemoteStartChargingRequest(RemoteStartChargingRequest.newBuilder() + .setPileCode(pileCode) + .setGunCode(gunCode) + .setLimitYuan(limitYuan.toPlainString()) + .setTradeNo(orderNo) + .build()); + + + downlinkCallService.sendDownlinkMessage(downlinkRequestMessageBuilder, pileCode); + } + private static Period createPeriod(int sn, LocalTime beginTime, LocalTime endTime, PricingModelFlag flag) { Period period = new Period(); period.setSn(sn); diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java index 59c6e90..2152b24 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java @@ -13,6 +13,9 @@ import sanbing.jcpp.app.data.PileSession; import sanbing.jcpp.app.service.DownlinkCallService; import sanbing.jcpp.app.service.grpc.DownlinkGrpcClient; import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; +import sanbing.jcpp.proto.gen.ProtocolProto.RequestMsg; + +import static sanbing.jcpp.infrastructure.proto.ProtoConverter.toTracerProto; /** * @author baigod @@ -29,8 +32,14 @@ public class GrpcDownlinkCallService extends DownlinkCallService { protected void _sendDownlinkMessage(DownlinkRequestMessage downlinkMessage, PileSession pileSession) { try { + RequestMsg requestMsg = RequestMsg.newBuilder() + .setTs(System.currentTimeMillis()) + .setTracer(toTracerProto()) + .setDownlinkRequestMessage(downlinkMessage) + .build(); + downlinkGrpcClient.sendDownlinkRequest(HostAndPort.fromParts(pileSession.getNodeIp(), pileSession.getNodeGrpcPort()), - downlinkMessage); + requestMsg); } catch (Exception e) { log.error("下行消息发送异常", e); diff --git a/jcpp-infrastructure-proto/pom.xml b/jcpp-infrastructure-proto/pom.xml index b625d3a..e704e3e 100644 --- a/jcpp-infrastructure-proto/pom.xml +++ b/jcpp-infrastructure-proto/pom.xml @@ -26,6 +26,10 @@ + + sanbing + jcpp-infrastructure-util + javax.annotation javax.annotation-api diff --git a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java index def0edb..f9bd59d 100644 --- a/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java +++ b/jcpp-infrastructure-proto/src/main/java/sanbing/jcpp/infrastructure/proto/ProtoConverter.java @@ -8,6 +8,8 @@ package sanbing.jcpp.infrastructure.proto; import sanbing.jcpp.infrastructure.proto.model.PricingModel; import sanbing.jcpp.infrastructure.proto.model.PricingModel.FlagPrice; import sanbing.jcpp.infrastructure.proto.model.PricingModel.Period; +import sanbing.jcpp.infrastructure.util.trace.Tracer; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; import sanbing.jcpp.proto.gen.ProtocolProto.*; import java.util.Map; @@ -17,6 +19,15 @@ import java.util.Map; */ public class ProtoConverter { + public static TracerProto toTracerProto() { + Tracer currentTracer = TracerContextUtil.getCurrentTracer(); + return TracerProto.newBuilder() + .setId(currentTracer.getTraceId()) + .setOrigin(currentTracer.getOrigin()) + .setTs(currentTracer.getTracerTs()) + .build(); + } + public static PricingModelProto toPricingModel(PricingModel pricingModel) { // 创建 PricingModelProto 实例 PricingModelProto.Builder builder = PricingModelProto.newBuilder(); diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index a34aa90..93195fc 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -9,8 +9,35 @@ package infrastructureProto; option java_package = "sanbing.jcpp.proto.gen"; option java_outer_classname = "ProtocolProto"; -service ProtocolDownlinkInterface { - rpc onDownlink(stream DownlinkRequestMessage) returns (stream DownlinkResponseMessage) {} +service ProtocolInterface { + rpc onDownlink(stream RequestMsg) returns (stream ResponseMsg) {} +} + +message RequestMsg { + int64 ts = 1; + TracerProto tracer = 2; + ConnectRequestMsg connectRequestMsg = 10; + DownlinkRequestMessage downlinkRequestMessage = 11; +} + +message ResponseMsg { + TracerProto tracer = 2; + ConnectResponseMsg connectResponseMsg = 12; + DownlinkResponseMessage downlinkResponseMsg = 13; +} + +message ConnectResponseMsg { + ConnectResponseCode responseCode = 1; + string errorMsg = 2; +} + +message ConnectRequestMsg { + string nodeId = 1; +} + +enum ConnectResponseCode { + ACCEPTED = 0; + REFUSE = 1; } message TracerProto { @@ -49,7 +76,6 @@ message DownlinkRequestMessage { optional int64 requestIdMSB = 8; optional int64 requestIdLSB = 9; optional bytes requestData = 10; - TracerProto tracer = 12; string downlinkCmd = 20; LoginResponse loginResponse = 21; VerifyPricingResponse verifyPricingResponse = 22; @@ -206,7 +232,7 @@ message RemoteStartChargingRequest { string pileCode = 4; string gunCode = 5; string tradeNo = 6; - int32 limitYuan = 7; + string limitYuan = 7; optional string additionalInfo = 20; } diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java index 589fc13..4b2597e 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/trace/TracerContextUtil.java @@ -6,11 +6,15 @@ package sanbing.jcpp.infrastructure.util.trace; import org.apache.commons.lang3.StringUtils; +import java.util.Optional; + /** * Tracer上下文工具类 */ public class TracerContextUtil { + public static final String DEFAULT_ORIGIN = "jcpp"; + public static final String JCPP_TRACER_ID = "jcpp_tracer_id"; public static final String JCPP_TRACER_ORIGIN = "jcpp_tracer_origin"; public static final String JCPP_TRACER_TS = "jcpp_tracer_ts"; @@ -20,6 +24,8 @@ public class TracerContextUtil { public static Tracer newTracer(String traceId, String origin) { Tracer tracer; + origin = Optional.ofNullable(origin).orElse(DEFAULT_ORIGIN); + if (StringUtils.isEmpty(traceId)) { tracer = new Tracer(TraceIdGenerator.generate(), origin); } else { @@ -34,6 +40,8 @@ public class TracerContextUtil { public static Tracer newTracer(String traceId, String origin, long ts) { final Tracer tracer; + origin = Optional.ofNullable(origin).orElse(DEFAULT_ORIGIN); + if (StringUtils.isEmpty(traceId)) { tracer = new Tracer(TraceIdGenerator.generate(), origin, ts); } else { diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java index 82107d5..e9a7d0e 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java @@ -21,17 +21,17 @@ import org.springframework.stereotype.Service; import sanbing.jcpp.infrastructure.util.mdc.MDCUtils; import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; import sanbing.jcpp.infrastructure.util.trace.TracerRunnable; -import sanbing.jcpp.proto.gen.ProtocolDownlinkInterfaceGrpc.ProtocolDownlinkInterfaceImplBase; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkRequestMessage; -import sanbing.jcpp.proto.gen.ProtocolProto.DownlinkResponseMessage; -import sanbing.jcpp.proto.gen.ProtocolProto.TracerProto; +import sanbing.jcpp.proto.gen.ProtocolInterfaceGrpc.ProtocolInterfaceImplBase; +import sanbing.jcpp.proto.gen.ProtocolProto.*; import sanbing.jcpp.protocol.domain.ProtocolSession; import sanbing.jcpp.protocol.provider.ProtocolSessionRegistryProvider; import javax.annotation.PreDestroy; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import static sanbing.jcpp.infrastructure.proto.ProtoConverter.toTracerProto; import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.JCPP_COMMON_THREAD_POOL; /** @@ -40,7 +40,7 @@ import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.JC @Service @Slf4j @ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='protocol'") -public class DownlinkGrpcService extends ProtocolDownlinkInterfaceImplBase { +public class DownlinkGrpcService extends ProtocolInterfaceImplBase { @Value("${service.protocol.rpc.port}") private int rpcPort; @Value("${service.protocol.rpc.boss}") @@ -64,6 +64,7 @@ public class DownlinkGrpcService extends ProtocolDownlinkInterfaceImplBase { ProtocolSessionRegistryProvider protocolSessionRegistryProvider; private Server server; + private static final ReentrantLock replyLock = new ReentrantLock(); @PostConstruct public void init() throws Exception { @@ -108,37 +109,59 @@ public class DownlinkGrpcService extends ProtocolDownlinkInterfaceImplBase { } @Override - public StreamObserver onDownlink(StreamObserver responseObserver) { + public StreamObserver onDownlink(StreamObserver responseObserver) { return new StreamObserver<>() { + @Override - public void onNext(DownlinkRequestMessage downlinkMsg) { - TracerProto tracerProto = downlinkMsg.getTracer(); + public void onNext(RequestMsg requestMsg) { + TracerProto tracerProto = requestMsg.getTracer(); TracerContextUtil.newTracer(tracerProto.getId(), tracerProto.getOrigin(), tracerProto.getTs()); MDCUtils.recordTracer(); - log.debug("收到Grpc下行请求 {}", downlinkMsg); - - JCPP_COMMON_THREAD_POOL.execute(new TracerRunnable(() -> { - UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(), downlinkMsg.getSessionIdLSB()); - - ProtocolSession protocolSession = protocolSessionRegistryProvider.get(protocolSessionId); + log.debug("通信层收到Grpc下行请求 {}", requestMsg); + if (requestMsg.hasConnectRequestMsg()) { + replyLock.lock(); try { - if (protocolSession != null) { + responseObserver.onNext( + ResponseMsg.newBuilder() + .setTracer(toTracerProto()) + .setConnectResponseMsg(ConnectResponseMsg.newBuilder() + .setResponseCode(ConnectResponseCode.ACCEPTED) + .setErrorMsg("") + .build()) + .build()); + } finally { + replyLock.unlock(); + } + } - protocolSession.onDownlink(downlinkMsg); + if(requestMsg.hasDownlinkRequestMessage()){ + DownlinkRequestMessage downlinkMsg = requestMsg.getDownlinkRequestMessage(); + JCPP_COMMON_THREAD_POOL.execute(new TracerRunnable(() -> { + UUID protocolSessionId = new UUID(downlinkMsg.getSessionIdMSB(), downlinkMsg.getSessionIdLSB()); - } else { + ProtocolSession protocolSession = protocolSessionRegistryProvider.get(protocolSessionId); - log.info("下发报文时Session未找到 sessionId: {}", protocolSessionId); + try { + if (protocolSession != null) { + + protocolSession.onDownlink(downlinkMsg); + + } else { + + log.info("下发报文时Session未找到 sessionId: {}", protocolSessionId); + + } + } catch (Exception e) { + + log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, e); } - } catch (Exception e) { + })); + } - log.warn("下发报文时处理失败 sessionId: {}", protocolSessionId, e); - } - })); } @Override diff --git a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java index 20ecd62..1e4bc8e 100644 --- a/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java +++ b/jcpp-protocol-bootstrap/src/test/java/sanbing/jcpp/protocol/adapter/DownlinkControllerIT.java @@ -131,7 +131,7 @@ class DownlinkControllerIT extends AbstractProtocolTestBase { .setRemoteStartChargingRequest(ProtocolProto.RemoteStartChargingRequest.newBuilder() .setPileCode(pileCode) .setGunCode("01") - .setLimitYuan(100) + .setLimitYuan("100") .setTradeNo("12345678901234567890") .build()) .build(); diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java index 9088b49..d5cc7d4 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java @@ -16,6 +16,8 @@ import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; +import java.math.BigDecimal; + import static sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongDownlinkCmdEnum.REMOTE_START_CHARGING; /** @@ -39,7 +41,7 @@ public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe String pileCode = remoteStartChargingRequest.getPileCode(); String gunCode = remoteStartChargingRequest.getGunCode(); String tradeNo = remoteStartChargingRequest.getTradeNo(); - int limitYuan = remoteStartChargingRequest.getLimitYuan(); + String limitYuan = remoteStartChargingRequest.getLimitYuan(); byte[] cardNo = encodeCardNo(tradeNo); @@ -55,7 +57,7 @@ public class YunKuaiChongV150RemoteStartDLCmd extends YunKuaiChongDownlinkCmdExe // 物理卡号 msgBody.writeBytes(cardNo); // 账户余额 - msgBody.writeIntLE(limitYuan); + msgBody.writeIntLE(new BigDecimal(limitYuan).intValue()); encodeAndWriteFlush(REMOTE_START_CHARGING, msgBody, diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java index 4a2449a..cd7abca 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/cmd/YunKuaiChongV160RemoteParallelStartDLCmd.java @@ -16,6 +16,7 @@ import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe; import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage; import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -44,7 +45,7 @@ public class YunKuaiChongV160RemoteParallelStartDLCmd extends YunKuaiChongDownli String pileCode = remoteStartChargingRequest.getPileCode(); String gunCode = remoteStartChargingRequest.getGunCode(); String tradeNo = remoteStartChargingRequest.getTradeNo(); - int limitYuan = remoteStartChargingRequest.getLimitYuan(); + String limitYuan = remoteStartChargingRequest.getLimitYuan(); byte[] cardNo = encodeCardNo(tradeNo); @@ -60,7 +61,7 @@ public class YunKuaiChongV160RemoteParallelStartDLCmd extends YunKuaiChongDownli // 物理卡号 msgBody.writeBytes(cardNo); // 账户余额 - msgBody.writeIntLE(limitYuan); + msgBody.writeIntLE(new BigDecimal(limitYuan).intValue()); // 并充序号 msgBody.writeBytes(BCDUtil.toBytes(LocalDateTime.now().format(dateTimeFormatter))); From 210ed399da5579dd6dd50739a7957c4560f69a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Thu, 24 Oct 2024 17:06:09 +0800 Subject: [PATCH 071/102] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queue/discovery/DefaultServiceInfoProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java index 2c8a1f9..75a424d 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java @@ -62,7 +62,7 @@ public class DefaultServiceInfoProvider implements ServiceInfoProvider { } log.info("Current Service ID: {}", serviceId); - hostAddress = InetAddress.getLocalHost().getHostAddress(); + hostAddress = InetAddress.getLocalHost().getHostName(); log.info("Current Service HostAddress: {}, RestPort:{}, GrpcPort:{}", hostAddress, restPort, grpcPort); if (serviceType.equalsIgnoreCase("monolith")) { From 4198c46c01aebd3884a3703f1558955eb3cbe31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 25 Oct 2024 09:27:20 +0800 Subject: [PATCH 072/102] =?UTF-8?q?=E8=B0=83=E6=95=B4kafka=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index a26fae3..99f8c70 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -78,15 +78,15 @@ queue: key-password: "${KAFKA_SSL_KEY_PASSWORD:}" acks: "${KAFKA_ACKS:1}" retries: "${KAFKA_RETRIES:1}" - compression-type: "${KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd - batch-size: "${KAFKA_BATCH_SIZE:1048576}" + compression-type: "${KAFKA_COMPRESSION_TYPE:none}" # none, gzip, snappy, lz4, zstd + batch-size: "${KAFKA_BATCH_SIZE:16384}" linger-ms: "${KAFKA_LINGER_MS:1}" max-request-size: "${KAFKA_MAX_REQUEST_SIZE:1048576}" max-in-flight-requests-per-connection: "${KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" buffer-memory: "${BUFFER_MEMORY:33554432}" replication-factor: "${QUEUE_KAFKA_REPLICATION_FACTOR:1}" max-poll-interval-ms: "${QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" - max-poll-records: "${QUEUE_KAFKA_MAX_POLL_RECORDS:10240}" + max-poll-records: "${QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" max-partition-fetch-bytes: "${QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" fetch-max-bytes: "${QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request-timeout-ms: "${QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" From 490e9baecef84f79d5d4dd1607fc830ad5835afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 25 Oct 2024 09:49:24 +0800 Subject: [PATCH 073/102] =?UTF-8?q?=E8=B0=83=E6=95=B4kafka=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/start.sh | 2 +- .../sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docker/start.sh b/docker/start.sh index 33ea7fa..d16c041 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -12,7 +12,7 @@ export JAVA_APP_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=10 -XX:M -XX:HeapDumpPath=/var/log/sanbing/heapdump/ \ -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark \ -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10 \ - -Xss512k -XX:MaxDirectMemorySize=128M -XX:G1ReservePercent=20 \ + -Xss512k -XX:MaxDirectMemorySize=256M -XX:G1ReservePercent=20 \ -XX:-OmitStackTraceInFastThrow \ -Dlogging.config=/app/config/log4j2.xml" diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java index dee6bc3..445093b 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java @@ -261,7 +261,10 @@ public class DownlinkGrpcClient { } if (connectErrTimesMap.computeIfAbsent(hostAndPort, k -> new AtomicInteger()).incrementAndGet() >= maxReconnectTimes) { - queueMap.remove(hostAndPort); + LinkedBlockingQueue queue = queueMap.remove(hostAndPort); + if (queue != null) { + queue.clear(); + } connectErrTimesMap.remove(hostAndPort); log.info("[{}] Grpc 客户端重连异常超过{}次,不再重连", hostAndPort, maxReconnectTimes); } From 7410f5021a416fb956ce64b99702607d0344f876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Fri, 25 Oct 2024 11:16:53 +0800 Subject: [PATCH 074/102] =?UTF-8?q?hostname=E8=A7=A3=E6=9E=90=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queue/discovery/DefaultServiceInfoProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java index 75a424d..2c8a1f9 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/discovery/DefaultServiceInfoProvider.java @@ -62,7 +62,7 @@ public class DefaultServiceInfoProvider implements ServiceInfoProvider { } log.info("Current Service ID: {}", serviceId); - hostAddress = InetAddress.getLocalHost().getHostName(); + hostAddress = InetAddress.getLocalHost().getHostAddress(); log.info("Current Service HostAddress: {}, RestPort:{}, GrpcPort:{}", hostAddress, restPort, grpcPort); if (serviceType.equalsIgnoreCase("monolith")) { From 6988aac64ba801827c9d487e1c112d19d8c2c314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Mon, 28 Oct 2024 15:24:41 +0800 Subject: [PATCH 075/102] =?UTF-8?q?=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/app-service-test.properties | 1 - jcpp-protocol-yunkuaichong/READMD.md | 66 ++++++++++++------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties index ce335d7..3964ff8 100644 --- a/jcpp-app-bootstrap/src/test/resources/app-service-test.properties +++ b/jcpp-app-bootstrap/src/test/resources/app-service-test.properties @@ -1,4 +1,3 @@ -spring.datasource.url=jdbc:postgresql://testenv:30135/jcpp service.protocols.yunkuaichongV150.listener.tcp.bind-port=0 service.protocols.yunkuaichongV160.listener.tcp.bind-port=0 service.protocol.rpc.port=0 \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/READMD.md b/jcpp-protocol-yunkuaichong/READMD.md index f5b765b..b44b5cf 100644 --- a/jcpp-protocol-yunkuaichong/READMD.md +++ b/jcpp-protocol-yunkuaichong/READMD.md @@ -1,32 +1,48 @@ -### 云快充模拟报文 -上行登录(桩编号:20231212000010)HEX:20231212000010 -6822001900012023121200001001011047562e393572313300898604d11722d0348606024E87 -登录应答: -68 08 00 19 00 02 20 23 12 12 00 00 10 00 A1 55 -登录后对时: -68 0E 01 00 00 56 20 23 12 12 00 00 10 08 52 1B 17 02 0A 18 89 87 +### 云快充1.5模拟报文 -上行心跳: -68 0d 25 d3 00 03 20 23 12 12 00 00 10 01 00 D1 AC -心跳应答 -68 09 25 D3 00 04 20 23 12 12 00 00 10 01 00 1D 0B +--- -上行计费模型验证 -68 0d 71 ad 00 05 20231212000010 0000 222B -上行计费模型验证应答 -68 08 71 AD 00 06 20 23 12 12 00 00 10 00 6C DB +#### 上行登录 +> (桩编号:20231212000010)HEX:20231212000010 -上行充电桩计费模型请求 -68 0B 00 07 00 09 20 23 12 12 00 00 10 72 ED -下行计费模型请求应答 -68 5A 00 07 00 0A 20 23 12 12 00 00 10 00 01 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 02 02 02 02 02 02 02 02 02 02 02 02 03 03 03 03 03 03 03 03 03 03 03 03 FE 63 +`6822001900012023121200001001011047562e393572313300898604d11722d0348606024E87` +#### 下行登录应答 +`68 08 00 19 00 02 20 23 12 12 00 00 10 00 A1 55` +#### 下行登录后对时 +`68 0E 01 00 00 56 20 23 12 12 00 00 10 08 52 1B 17 02 0A 18 89 87` -上报充电桩状态 -6840bbbd0013 20231212000010 0000000010057136003201060039560002030201940ef4035400000000000000004e5510002000c057010000000000606c010000009373 +--- -下发启动充电 -68300100003400000000000012345678901234567890202312120000100156789012345678905678901234567890102700005fd9 +#### 上行心跳 +`68 0d 25 d3 00 03 20 23 12 12 00 00 10 01 00 D1 AC` +#### 下行心跳应答 +`68 09 25 D3 00 04 20 23 12 12 00 00 10 01 00 1D 0B` -上报交易记录 -68 a2 00 46 00 3b 20231212000010323239000000000000 20231212000010 01b03604116d0c17a08c09116d0c17f0490200000000000000000000000000d0fb0100000000000000000000000000b0ad0100000000000000000000000000905f010072060000720600007805000000000000000000000000720600007206000078050000000000000000000000000000000000000001a08c09116d0c1740000000000000000003E0 +--- +#### 上行计费模型验证 +`68 0d 71 ad 00 05 20231212000010 0000 222B` +#### 上行计费模型验证应答 +`68 08 71 AD 00 06 20 23 12 12 00 00 10 00 6C DB` + +--- + +#### 上行充电桩计费模型请求 +`68 0B 00 07 00 09 20 23 12 12 00 00 10 72 ED` +#### 下行计费模型请求应答 +`68 5A 00 07 00 0A 20 23 12 12 00 00 10 00 01 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 F8 24 01 00 C8 AF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 02 02 02 02 02 02 02 02 02 02 02 02 03 03 03 03 03 03 03 03 03 03 03 03 FE 63` + +--- + +#### 上报充电桩状态 +`6840bbbd0013 20231212000010 0000000010057136003201060039560002030201940ef4035400000000000000004e5510002000c057010000000000606c010000009373` + +#### 下发启动充电 +`68300100003400000000000012345678901234567890202312120000100156789012345678905678901234567890102700005fd9` + +--- + +#### 上行交易记录 +`68 a2 00 46 00 3b 20231212000010323239000000000000 20231212000010 01b03604116d0c17a08c09116d0c17f0490200000000000000000000000000d0fb0100000000000000000000000000b0ad0100000000000000000000000000905f010072060000720600007805000000000000000000000000720600007206000078050000000000000000000000000000000000000001a08c09116d0c1740000000000000000003E0` +#### 下行交易就应答 +`68 15 00 46 00 06 20 23 12 12 00 00 10 32 32 39 00 00 00 00 00 00 00 C6 2D ` \ No newline at end of file From ea02a838a376ef3f322faa641decf97df5d1f36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Mon, 28 Oct 2024 15:39:51 +0800 Subject: [PATCH 076/102] =?UTF-8?q?=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-protocol-yunkuaichong/READMD.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jcpp-protocol-yunkuaichong/READMD.md b/jcpp-protocol-yunkuaichong/READMD.md index b44b5cf..4f56330 100644 --- a/jcpp-protocol-yunkuaichong/READMD.md +++ b/jcpp-protocol-yunkuaichong/READMD.md @@ -37,8 +37,12 @@ #### 上报充电桩状态 `6840bbbd0013 20231212000010 0000000010057136003201060039560002030201940ef4035400000000000000004e5510002000c057010000000000606c010000009373` +--- + #### 下发启动充电 `68300100003400000000000012345678901234567890202312120000100156789012345678905678901234567890102700005fd9` +#### 上行启动应答 +`68 1E 0002 00 33 00000000000012345678901234567890 20231212000010 01 01 00 411D` --- From 75ffa8a425af12f21cbeee6da40c0723002d3373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Mon, 28 Oct 2024 16:02:13 +0800 Subject: [PATCH 077/102] =?UTF-8?q?=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.kafka.yml | 12 ------------ docker/queue-kafka.env | 2 -- 2 files changed, 14 deletions(-) delete mode 100644 docker/queue-kafka.env diff --git a/docker/docker-compose.kafka.yml b/docker/docker-compose.kafka.yml index 26b3478..5c071ad 100644 --- a/docker/docker-compose.kafka.yml +++ b/docker/docker-compose.kafka.yml @@ -43,15 +43,3 @@ services: - "9308:9308" command: - '--kafka.server=kafka:9092' - # 切换示例项目的队列类型为kafka - example: - restart: always - image: example:latest - depends_on: - - kafka - networks: - - sanbing-network - ports: - - "8080:8080" - env_file: - - queue-kafka.env diff --git a/docker/queue-kafka.env b/docker/queue-kafka.env deleted file mode 100644 index 1294636..0000000 --- a/docker/queue-kafka.env +++ /dev/null @@ -1,2 +0,0 @@ -QUEUE_TYPE=kafka -KAFKA_SERVERS=kafka:9092 From 246565f236a6168d1c331d2063edb65b8af7d5b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Mon, 28 Oct 2024 16:18:46 +0800 Subject: [PATCH 078/102] =?UTF-8?q?=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/protocol-service.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 7d14108..381692b 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -159,15 +159,15 @@ queue: key-password: "${KAFKA_SSL_KEY_PASSWORD:}" acks: "${KAFKA_ACKS:1}" retries: "${KAFKA_RETRIES:1}" - compression-type: "${KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd - batch-size: "${KAFKA_BATCH_SIZE:1048576}" + compression-type: "${KAFKA_COMPRESSION_TYPE:none}" # none, gzip, snappy, lz4, zstd + batch-size: "${KAFKA_BATCH_SIZE:16384}" linger-ms: "${KAFKA_LINGER_MS:1}" max-request-size: "${KAFKA_MAX_REQUEST_SIZE:1048576}" max-in-flight-requests-per-connection: "${KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" buffer-memory: "${BUFFER_MEMORY:33554432}" replication-factor: "${QUEUE_KAFKA_REPLICATION_FACTOR:1}" max-poll-interval-ms: "${QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" - max-poll-records: "${QUEUE_KAFKA_MAX_POLL_RECORDS:10240}" + max-poll-records: "${QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" max-partition-fetch-bytes: "${QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" fetch-max-bytes: "${QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request-timeout-ms: "${QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" From b0a1125c5688322960d610ae39a57a2518a66051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Mon, 28 Oct 2024 17:52:30 +0800 Subject: [PATCH 079/102] =?UTF-8?q?=E5=BF=AB=E9=80=9F=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.monolith.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docker/docker-compose.monolith.yml diff --git a/docker/docker-compose.monolith.yml b/docker/docker-compose.monolith.yml new file mode 100644 index 0000000..422f37d --- /dev/null +++ b/docker/docker-compose.monolith.yml @@ -0,0 +1,24 @@ +# +# 抖音关注:程序员三丙 +# 知识星球:https://t.zsxq.com/j9b21 +# + +networks: + sanbing-network: + driver: bridge + name: sanbing-network + ipam: + config: + - subnet: 10.10.0.0/24 + +services: + jcpp-node-0: + restart: always + image: registry.cn-hangzhou.aliyuncs.com/sanbing/jcpp-monolith:latest + networks: + - sanbing-network + ports: + - "8080:8080" + - "38001:38001" + environment: + HTTP_BIND_PORT: 8080 \ No newline at end of file From 293388aa0dafced3f679f9db28291c5611b8346c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 11:51:47 +0800 Subject: [PATCH 080/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- .../infrastructure/queue/memory/DefaultInMemoryStorage.java | 2 +- .../infrastructure/queue/provider/InMemoryAppQueueFactory.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 99f8c70..5c900ad 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -63,7 +63,7 @@ queue: type: "${QUEUE_TYPE:memory}" partitions: hash_function_name: "${QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - in-memory: + memory: queue-capacity: "${QUEUE_IN_MEMORY_QUEUE_CAPACITY:100000}" stats: print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:10000}" diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java index fd06cff..e800140 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -19,7 +19,7 @@ import java.util.concurrent.LinkedBlockingQueue; public final class DefaultInMemoryStorage implements InMemoryStorage { private final ConcurrentHashMap> storage = new ConcurrentHashMap<>(); - @Value("${queue.in-memory.queue-capacity:100000}") + @Value("${queue.memory.queue-capacity:100000}") private int queueCapacity; @Override diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java index 21719f9..177d6c8 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/provider/InMemoryAppQueueFactory.java @@ -40,7 +40,7 @@ public class InMemoryAppQueueFactory implements AppQueueFactory { return new InMemoryQueueProducer<>(storage, topic); } - @Scheduled(fixedRateString = "${queue.in-memory.stats.print-interval-ms:60000}") + @Scheduled(fixedRateString = "${queue.memory.stats.print-interval-ms:60000}") private void printInMemoryStats() { storage.printStats(); } From 1fa9e7aeee33d9fe72cfd70f7dd3138f7e8eef7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 11:52:11 +0800 Subject: [PATCH 081/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 5c900ad..ab2d795 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -64,9 +64,9 @@ queue: partitions: hash_function_name: "${QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 memory: - queue-capacity: "${QUEUE_IN_MEMORY_QUEUE_CAPACITY:100000}" + queue-capacity: "${QUEUE_MEMORY_QUEUE_CAPACITY:100000}" stats: - print-interval-ms: "${QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:10000}" + print-interval-ms: "${QUEUE_MEMORY_STATS_PRINT_INTERVAL_MS:10000}" kafka: bootstrap-servers: "${KAFKA_SERVERS:kafka:9092}" ssl: From d9582b3e13dd41307634b3e1d9c2808fb6f3586f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 16:39:03 +0800 Subject: [PATCH 082/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 2 +- .../cache/JCPPJCPPRedisStandaloneConfiguration.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index ab2d795..70bbdc0 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -128,7 +128,7 @@ redis: standalone: host: "${REDIS_HOST:redis}" port: "${REDIS_PORT:6379}" - useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" + useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:false}" clientName: "${REDIS_CLIENT_NAME:standalone}" commandTimeout: "${REDIS_CLIENT_COMMAND_TIMEOUT:30000}" shutdownTimeout: "${REDIS_CLIENT_SHUTDOWN_TIMEOUT:1000}" diff --git a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java index bdfd115..987b9ec 100644 --- a/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java +++ b/jcpp-infrastructure-cache/src/main/java/sanbing/jcpp/infrastructure/cache/JCPPJCPPRedisStandaloneConfiguration.java @@ -36,10 +36,10 @@ public class JCPPJCPPRedisStandaloneConfiguration extends JCPPRedisCacheConfigur @Value("${redis.standalone.shutdownTimeout:5000}") private Long shutdownTimeout; - @Value("${redis.standalone.useDefaultClientConfig:true}") + @Value("${redis.standalone.useDefaultClientConfig:false}") private boolean useDefaultClientConfig; - @Value("${redis.standalone.usePoolConfig:false}") + @Value("${redis.standalone.usePoolConfig:true}") private boolean usePoolConfig; @Value("${redis.db:0}") From cfd97f53bc928dab876e036aab6f79a4533a2f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 16:50:12 +0800 Subject: [PATCH 083/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/app-service.yml | 17 ++++++++--------- .../service/impl/GrpcDownlinkCallService.java | 2 +- .../service/impl/RestDownlinkCallService.java | 2 +- .../util/config/ShardingThreadPool.java | 6 +++--- .../src/main/resources/protocol-service.yml | 11 +++++------ 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 70bbdc0..8a58bd0 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -164,6 +164,14 @@ service: type: "${SERVICE_TYPE:monolith}" # 可自定义的服务ID,如果不指定,则默认为HOSTNAME id: "${SERVICE_ID:}" + thread-pool: + sharding: + hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" + stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" + downlink: + rpc: + type: "${DOWNLINK_RPC_TYPE:grpc}" # rest or grpc protocol: sessions: default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" @@ -260,12 +268,3 @@ service: buffer-memory: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_BUFFER_MEMORY:33554432}" other-properties: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_QUEUE_KAFKA_OTHER_PROPERTIES:}" -thread-pool: - sharding: - hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" - stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" - -downlink: - rpc: - type: "${DOWNLINK_RPC_TYPE:grpc}" # rest or grpc \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java index 2152b24..7941611 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/GrpcDownlinkCallService.java @@ -22,7 +22,7 @@ import static sanbing.jcpp.infrastructure.proto.ProtoConverter.toTracerProto; */ @Service @Slf4j -@ConditionalOnExpression("'${downlink.rpc.type:null}'=='grpc'") +@ConditionalOnExpression("'${service.downlink.rpc.type:null}'=='grpc'") public class GrpcDownlinkCallService extends DownlinkCallService { @Resource diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java index 795c949..6ad1c80 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/RestDownlinkCallService.java @@ -26,7 +26,7 @@ import static sanbing.jcpp.infrastructure.util.trace.TracerContextUtil.*; */ @Service @Slf4j -@ConditionalOnExpression("'${downlink.rpc.type:null}'=='rest'") +@ConditionalOnExpression("'${service.downlink.rpc.type:null}'=='rest'") public class RestDownlinkCallService extends DownlinkCallService { @Resource diff --git a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java index c334e7e..f92ca6c 100644 --- a/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java +++ b/jcpp-infrastructure-util/src/main/java/sanbing/jcpp/infrastructure/util/config/ShardingThreadPool.java @@ -30,10 +30,10 @@ import static sanbing.jcpp.infrastructure.util.JCPPHashUtil.hash; @Component @Slf4j public class ShardingThreadPool { - @Value("${thread-pool.sharding.hash_function_name:murmur3_128}") + @Value("${service.thread-pool.sharding.hash_function_name:murmur3_128}") private String hashFunctionName; - @Value("${thread-pool.sharding.parallelism:8}") + @Value("${service.thread-pool.sharding.parallelism:8}") private int parallelism; private HashFunction hashFunction; @@ -53,7 +53,7 @@ public class ShardingThreadPool { } } - @Scheduled(fixedDelayString = "${thread-pool.sharding.stats-print-interval-ms:10000}") + @Scheduled(fixedDelayString = "${service.thread-pool.sharding.stats-print-interval-ms:10000}") public void printStats() { executorServiceMap.forEach((k, v) -> { diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 381692b..fc63432 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -40,6 +40,11 @@ service: type: "${SERVICE_TYPE:protocol}" # 可自定义的服务ID,如果不指定,则默认为HOSTNAME id: "${SERVICE_ID:}" + thread-pool: + sharding: + hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" + stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" protocol: sessions: default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" @@ -192,9 +197,3 @@ queue: enabled: "${QUEUE_APP_STATS_ENABLED:true}" print-interval-ms: "${QUEUE_APP_STATS_PRINT_INTERVAL_MS:60000}" -thread-pool: - sharding: - hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" - stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" - From 7f41180819313e8712ca8d076b34625bdcbbd4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 16:59:24 +0800 Subject: [PATCH 084/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/service/grpc/DownlinkGrpcClient.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java index 445093b..641e789 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java @@ -51,31 +51,31 @@ public class DownlinkGrpcClient { @Value("${downlink.rpc.grpc.netty.so_sndbuf:65535}") private Integer rpcNettySoSndbuf; - @Value("${downlink.rpc.grpc.netty.so_rcvbuf:65535}") + @Value("${service.downlink.rpc.grpc.netty.so_rcvbuf:65535}") private Integer rpcNettySoRcvbuf; - @Value("${downlink.rpc.grpc.netty.no_delay:true}") + @Value("${service.downlink.rpc.grpc.netty.no_delay:true}") private boolean rpcNoDelay; - @Value("${downlink.rpc.grpc.netty.max_inbound_message_size:33554432}") + @Value("${service.downlink.rpc.grpc.netty.max_inbound_message_size:33554432}") private Integer rpcMaxInboundMessageSize; - @Value("${downlink.rpc.grpc.keep_alive_time_sec:300}") + @Value("${service.downlink.rpc.grpc.keep_alive_time_sec:300}") private int keepAliveTimeSec; - @Value("${downlink.rpc.grpc.max_records_size:102400}") + @Value("${service.downlink.rpc.grpc.max_records_size:102400}") private int maxRecordsSize; - @Value("${downlink.rpc.grpc.batch_records_count:1024}") + @Value("${service.downlink.rpc.grpc.batch_records_count:1024}") private int batchRecordsCount; - @Value("${downlink.rpc.grpc.no_read_records_sleep:25}") + @Value("${service.downlink.rpc.grpc.no_read_records_sleep:25}") private long noRecordsSleepInterval; - @Value("${downlink.rpc.grpc.records_ttl:600000}") + @Value("${service.downlink.rpc.grpc.records_ttl:600000}") private long recordsTtl; - @Value("${downlink.rpc.grpc.max_reconnect_times:10}") + @Value("${service.downlink.rpc.grpc.max_reconnect_times:10}") private int maxReconnectTimes; @Resource From bad5d8ef1c4b97df1dccdd3695a348f3551a2b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:02:08 +0800 Subject: [PATCH 085/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 8a58bd0..16a29ba 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -166,12 +166,12 @@ service: id: "${SERVICE_ID:}" thread-pool: sharding: - hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" - stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" + hash_function_name: "${SERVICE_THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + parallelism: "${SERVICE_THREAD_POOL_SHARDING_PARALLELISM:8}" + stats-print-interval-ms: "${SERVICE_THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" downlink: rpc: - type: "${DOWNLINK_RPC_TYPE:grpc}" # rest or grpc + type: "${SERVICE_DOWNLINK_RPC_TYPE:grpc}" # rest or grpc protocol: sessions: default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" From b8e4549265cad3e9622fe3f7f15da04dc12017b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:02:41 +0800 Subject: [PATCH 086/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 4 ++-- .../src/main/resources/protocol-service.yml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 16a29ba..88c5c1c 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -174,8 +174,8 @@ service: type: "${SERVICE_DOWNLINK_RPC_TYPE:grpc}" # rest or grpc protocol: sessions: - default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" - default-state-check-interval-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" + default-inactivity-timeout-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" + default-state-check-interval-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" rpc: enabled: "${SERVICE_PROTOCOL_RPC_ENABLED:true}" port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index fc63432..7a25a6f 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -42,13 +42,13 @@ service: id: "${SERVICE_ID:}" thread-pool: sharding: - hash_function_name: "${THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 - parallelism: "${THREAD_POOL_SHARDING_PARALLELISM:8}" - stats-print-interval-ms: "${THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" + hash_function_name: "${SERVICE_THREAD_POOL_SHARDING_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 + parallelism: "${SERVICE_THREAD_POOL_SHARDING_PARALLELISM:8}" + stats-print-interval-ms: "${SERVICE_THREAD_POOL_SHARDING_STATS_PRINT_INTERVAL_MS:10000}" protocol: sessions: - default-inactivity-timeout-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" - default-state-check-interval-in-sec: "${PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" + default-inactivity-timeout-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" + default-state-check-interval-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" rpc: enabled: "${SERVICE_PROTOCOL_RPC_ENABLED:true}" port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" From aa0eb5f568a5ca175ca9010198f4a16ab967e621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:12:10 +0800 Subject: [PATCH 087/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 1 - jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 88c5c1c..0062f58 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -177,7 +177,6 @@ service: default-inactivity-timeout-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" default-state-check-interval-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" rpc: - enabled: "${SERVICE_PROTOCOL_RPC_ENABLED:true}" port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" boss: "${SERVICE_PROTOCOL_RPC_BOSS:4}" worker: "${SERVICE_PROTOCOL_RPC_WORKER:64}" diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 7a25a6f..db73c09 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -50,7 +50,6 @@ service: default-inactivity-timeout-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" default-state-check-interval-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" rpc: - enabled: "${SERVICE_PROTOCOL_RPC_ENABLED:true}" port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" boss: "${SERVICE_PROTOCOL_RPC_BOSS:4}" worker: "${SERVICE_PROTOCOL_RPC_WORKER:64}" From 23812a5ef1f66696c4e0aff24a9dc1e45cab0184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:16:02 +0800 Subject: [PATCH 088/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/app-service.yml | 22 +++++----- .../app/service/grpc/DownlinkGrpcClient.java | 4 +- .../protocol/adapter/DownlinkGrpcService.java | 44 +++++++++---------- .../src/main/resources/protocol-service.yml | 22 +++++----- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index 0062f58..a9b737b 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -176,17 +176,17 @@ service: sessions: default-inactivity-timeout-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" default-state-check-interval-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" - rpc: - port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" - boss: "${SERVICE_PROTOCOL_RPC_BOSS:4}" - worker: "${SERVICE_PROTOCOL_RPC_WORKER:64}" - so-rcvbuf: "${SERVICE_PROTOCOL_RPC_SO_RCVBUF:65535}" - so-sndbuf: "${SERVICE_PROTOCOL_RPC_SO_SNDBUF:65535}" - no-delay: "${SERVICE_PROTOCOL_RPC_NO_DELAY:true}" - user-thread-pool-size: "${SERVICE_PROTOCOL_RPC_USER_THREAD_POOL_SIZE:1024}" - max-inbound-message-size: "${SERVICE_PROTOCOL_RPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" - max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" - client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" + grpc: + port: "${SERVICE_PROTOCOL_GRPC_PORT:9090}" + boss: "${SERVICE_PROTOCOL_GRPC_BOSS:4}" + worker: "${SERVICE_PROTOCOL_GRPC_WORKER:64}" + so-rcvbuf: "${SERVICE_PROTOCOL_GRPC_SO_RCVBUF:65535}" + so-sndbuf: "${SERVICE_PROTOCOL_GRPC_SO_SNDBUF:65535}" + no-delay: "${SERVICE_PROTOCOL_GRPC_NO_DELAY:true}" + user-thread-pool-size: "${SERVICE_PROTOCOL_GRPC_USER_THREAD_POOL_SIZE:1024}" + max-inbound-message-size: "${SERVICE_PROTOCOL_GRPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" + max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" + client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_GRPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" protocols: yunkuaichongV150: enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java index 641e789..2fe52bf 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/grpc/DownlinkGrpcClient.java @@ -45,10 +45,10 @@ import static sanbing.jcpp.infrastructure.proto.ProtoConverter.toTracerProto; @Slf4j public class DownlinkGrpcClient { - @Value("${downlink.rpc.grpc.netty.event_loop:}") + @Value("${service.downlink.rpc.grpc.netty.event_loop:}") private Integer rpcNettyEventLoop; - @Value("${downlink.rpc.grpc.netty.so_sndbuf:65535}") + @Value("${service.downlink.rpc.grpc.netty.so_sndbuf:65535}") private Integer rpcNettySoSndbuf; @Value("${service.downlink.rpc.grpc.netty.so_rcvbuf:65535}") diff --git a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java index e9a7d0e..a739eb2 100644 --- a/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java +++ b/jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkGrpcService.java @@ -41,23 +41,23 @@ import static sanbing.jcpp.infrastructure.util.config.ThreadPoolConfiguration.JC @Slf4j @ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='protocol'") public class DownlinkGrpcService extends ProtocolInterfaceImplBase { - @Value("${service.protocol.rpc.port}") - private int rpcPort; - @Value("${service.protocol.rpc.boss}") - private int rpcBoss; - @Value("${service.protocol.rpc.worker}") - private int rpcWorker; - @Value("${service.protocol.rpc.so-rcvbuf}") - private int rpcNettySoRcvbuf; - @Value("${service.protocol.rpc.so-sndbuf}") - private int rpcNettySoSndbuf; - @Value("${service.protocol.rpc.no-delay}") - private boolean rpcNettyNoDelay; - @Value("${service.protocol.rpc.max-inbound-message-size}") + @Value("${service.protocol.grpc.port}") + private int grpcPort; + @Value("${service.protocol.grpc.boss}") + private int grpcBoss; + @Value("${service.protocol.grpc.worker}") + private int grpcWorker; + @Value("${service.protocol.grpc.so-rcvbuf}") + private int grpcNettySoRcvbuf; + @Value("${service.protocol.grpc.so-sndbuf}") + private int grpcNettySoSndbuf; + @Value("${service.protocol.grpc.no-delay}") + private boolean grpcNettyNoDelay; + @Value("${service.protocol.grpc.max-inbound-message-size}") private int maxInboundMessageSize; - @Value("${service.protocol.rpc.max-concurrent-calls-per-connection}") + @Value("${service.protocol.grpc.max-concurrent-calls-per-connection}") private int maxConcurrentCallsPerConnection; - @Value("${service.protocol.rpc.client-max-keep-alive-time-sec}") + @Value("${service.protocol.grpc.client-max-keep-alive-time-sec}") private int clientMaxKeepAliveTimeSec; @Resource @@ -70,12 +70,12 @@ public class DownlinkGrpcService extends ProtocolInterfaceImplBase { public void init() throws Exception { log.info("Initializing Protocol Downlink Grpc service!"); - NettyServerBuilder builder = NettyServerBuilder.forPort(this.rpcPort) - .bossEventLoopGroup(new NioEventLoopGroup(this.rpcBoss)) - .workerEventLoopGroup(new NioEventLoopGroup(this.rpcWorker)) - .withOption(ChannelOption.SO_RCVBUF, rpcNettySoRcvbuf) - .withChildOption(ChannelOption.SO_SNDBUF, rpcNettySoSndbuf) - .withChildOption(ChannelOption.TCP_NODELAY, rpcNettyNoDelay) + NettyServerBuilder builder = NettyServerBuilder.forPort(this.grpcPort) + .bossEventLoopGroup(new NioEventLoopGroup(this.grpcBoss)) + .workerEventLoopGroup(new NioEventLoopGroup(this.grpcWorker)) + .withOption(ChannelOption.SO_RCVBUF, grpcNettySoRcvbuf) + .withChildOption(ChannelOption.SO_SNDBUF, grpcNettySoSndbuf) + .withChildOption(ChannelOption.TCP_NODELAY, grpcNettyNoDelay) .compressorRegistry(CompressorRegistry.getDefaultInstance()) .decompressorRegistry(DecompressorRegistry.getDefaultInstance()) .channelType(NioServerSocketChannel.class) @@ -89,7 +89,7 @@ public class DownlinkGrpcService extends ProtocolInterfaceImplBase { .addService(this); this.server = builder.build(); - log.info("Going to start RPC server using port: {}", this.rpcPort); + log.info("Going to start RPC server using port: {}", this.grpcPort); try { this.server.start(); diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index db73c09..da59e95 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -49,17 +49,17 @@ service: sessions: default-inactivity-timeout-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_INACTIVITY_TIMEOUT_IN_SEC:600}" default-state-check-interval-in-sec: "${SERVICE_PROTOCOLS_SESSIONS_DEFAULT_STATE_CHECK_INTERVAL_IN_SEC:60}" - rpc: - port: "${SERVICE_PROTOCOL_RPC_PORT:9090}" - boss: "${SERVICE_PROTOCOL_RPC_BOSS:4}" - worker: "${SERVICE_PROTOCOL_RPC_WORKER:64}" - so-rcvbuf: "${SERVICE_PROTOCOL_RPC_SO_RCVBUF:65535}" - so-sndbuf: "${SERVICE_PROTOCOL_RPC_SO_SNDBUF:65535}" - no-delay: "${SERVICE_PROTOCOL_RPC_NO_DELAY:true}" - user-thread-pool-size: "${SERVICE_PROTOCOL_RPC_USER_THREAD_POOL_SIZE:1024}" - max-inbound-message-size: "${SERVICE_PROTOCOL_RPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" - max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" - client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" + grpc: + port: "${SERVICE_PROTOCOL_GRPC_PORT:9090}" + boss: "${SERVICE_PROTOCOL_GRPC_BOSS:4}" + worker: "${SERVICE_PROTOCOL_GRPC_WORKER:64}" + so-rcvbuf: "${SERVICE_PROTOCOL_GRPC_SO_RCVBUF:65535}" + so-sndbuf: "${SERVICE_PROTOCOL_GRPC_SO_SNDBUF:65535}" + no-delay: "${SERVICE_PROTOCOL_GRPC_NO_DELAY:true}" + user-thread-pool-size: "${SERVICE_PROTOCOL_GRPC_USER_THREAD_POOL_SIZE:1024}" + max-inbound-message-size: "${SERVICE_PROTOCOL_GRPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" + max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" + client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_GRPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" protocols: yunkuaichongV150: enabled: "${PROTOCOLS_YUNKUAICHONGV150_ENABLED:true}" From fed2dd80d8a5f2003d168a6e8dc854ae5aa24842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:21:00 +0800 Subject: [PATCH 089/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index a9b737b..e6af895 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -183,7 +183,6 @@ service: so-rcvbuf: "${SERVICE_PROTOCOL_GRPC_SO_RCVBUF:65535}" so-sndbuf: "${SERVICE_PROTOCOL_GRPC_SO_SNDBUF:65535}" no-delay: "${SERVICE_PROTOCOL_GRPC_NO_DELAY:true}" - user-thread-pool-size: "${SERVICE_PROTOCOL_GRPC_USER_THREAD_POOL_SIZE:1024}" max-inbound-message-size: "${SERVICE_PROTOCOL_GRPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_GRPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" From 330d05a280d47c504e6cfbfe65a4407946607506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:21:35 +0800 Subject: [PATCH 090/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index da59e95..075b90f 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -56,7 +56,6 @@ service: so-rcvbuf: "${SERVICE_PROTOCOL_GRPC_SO_RCVBUF:65535}" so-sndbuf: "${SERVICE_PROTOCOL_GRPC_SO_SNDBUF:65535}" no-delay: "${SERVICE_PROTOCOL_GRPC_NO_DELAY:true}" - user-thread-pool-size: "${SERVICE_PROTOCOL_GRPC_USER_THREAD_POOL_SIZE:1024}" max-inbound-message-size: "${SERVICE_PROTOCOL_GRPC_MAX_INBOUND_MESSAGE_SIZE:33554432}" max-concurrent-calls-per-connection: "${SERVICE_PROTOCOL_GRPC_MAX_CONCURRENT_CALLS_PER_CONNECTION:4}" client-max-keep-alive-time-sec: "${SERVICE_PROTOCOL_GRPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:30}" From 55f416647635d26e0b6617501d91aaead32ac0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 17:42:30 +0800 Subject: [PATCH 091/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jcpp-app-bootstrap/src/main/resources/app-service.yml | 4 ++-- .../src/main/resources/protocol-service.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jcpp-app-bootstrap/src/main/resources/app-service.yml b/jcpp-app-bootstrap/src/main/resources/app-service.yml index e6af895..f0cdd51 100644 --- a/jcpp-app-bootstrap/src/main/resources/app-service.yml +++ b/jcpp-app-bootstrap/src/main/resources/app-service.yml @@ -221,7 +221,7 @@ service: # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_RETRIES:1}" - compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:none}" # none, gzip, snappy, lz4, zstd batch-size: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_BATCH_SIZE:16384}" linger-ms: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_LINGER_MS:0}" buffer-memory: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_BUFFER_MEMORY:33554432}" @@ -260,7 +260,7 @@ service: # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_RETRIES:1}" - compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:none}" # none, gzip, snappy, lz4, zstd batch-size: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_BATCH_SIZE:16384}" linger-ms: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_LINGER_MS:0}" buffer-memory: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_BUFFER_MEMORY:33554432}" diff --git a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml index 075b90f..b683070 100644 --- a/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml +++ b/jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml @@ -92,7 +92,7 @@ service: # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_RETRIES:1}" - compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + compression-type: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_COMPRESSION_TYPE:none}" # none, gzip, snappy, lz4, zstd batch-size: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_BATCH_SIZE:16384}" linger-ms: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_KAFKA_LINGER_MS:0}" buffer-memory: "${PROTOCOLS_YUNKUAICHONGV150_FORWARD_BUFFER_MEMORY:33554432}" @@ -129,7 +129,7 @@ service: # 可选 protobuf(推荐)、json encoder: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_ENCODER:protobuf}" retries: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_RETRIES:1}" - compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:lz4}" # none, gzip, snappy, lz4, zstd + compression-type: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_COMPRESSION_TYPE:none}" # none, gzip, snappy, lz4, zstd batch-size: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_BATCH_SIZE:16384}" linger-ms: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_KAFKA_LINGER_MS:0}" buffer-memory: "${PROTOCOLS_YUNKUAICHONGV160_FORWARD_BUFFER_MEMORY:33554432}" From 48afec91ce068aab76ec9facd88853ecdb815b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 19:05:01 +0800 Subject: [PATCH 092/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1235860..a3e575d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ #### 充电桩协议文档 百度网盘: https://pan.baidu.com/s/1xT8xWty1XRUHDzTZM_8aLw?pwd=jcpp +#### 项目wiki +https://www.yuque.com/chengxuyuansanbing/jcpp/xck5fvolx3l7oqhw + ------------------------------ #### 代码编写指南 ##### 教程以视频形式在自媒体平台陆续发布,敬请关注 From efe763fe5c20c9e5b11873fef3be433afeaca394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 19:07:59 +0800 Subject: [PATCH 093/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a3e575d..0d3c9d4 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ 百度网盘: https://pan.baidu.com/s/1xT8xWty1XRUHDzTZM_8aLw?pwd=jcpp #### 项目wiki +> 如果链接过期请到微信群申请协作 + https://www.yuque.com/chengxuyuansanbing/jcpp/xck5fvolx3l7oqhw ------------------------------ From 136da53a17a6bf3214bf91dc4dd841a75f752328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 19:10:58 +0800 Subject: [PATCH 094/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0d3c9d4..a2bda6c 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,17 @@ 百度网盘: https://pan.baidu.com/s/1xT8xWty1XRUHDzTZM_8aLw?pwd=jcpp #### 项目wiki -> 如果链接过期请到微信群申请协作 +> 如果链接过期请加作者微信申请协作 https://www.yuque.com/chengxuyuansanbing/jcpp/xck5fvolx3l7oqhw ------------------------------ #### 代码编写指南 ##### 教程以视频形式在自媒体平台陆续发布,敬请关注 -![抖音二维码](https://foruda.gitee.com/images/1728650678915895016/b2219d50_10604541.png) + -##### 我建了个微信技术交流群 -如过期请加个人微信 -![微信交流群](https://foruda.gitee.com/images/1729683365750232573/8b2574af_10604541.png "微信交流群") -![博主个人微信](https://foruda.gitee.com/images/1729683391329244317/bfefcca3_10604541.jpeg "博主个人微信") +##### 作者微信 + ------------------------------ #### 参与贡献 From 015e51eadd995f3c7ec8916e2d5e65821dc14f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 29 Oct 2024 19:12:33 +0800 Subject: [PATCH 095/102] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=89=8B=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a2bda6c..4d020fb 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,10 @@ https://www.yuque.com/chengxuyuansanbing/jcpp/xck5fvolx3l7oqhw ------------------------------ #### 代码编写指南 ##### 教程以视频形式在自媒体平台陆续发布,敬请关注 - + ##### 作者微信 - + ------------------------------ #### 参与贡献 From 919f51ef492db7dbb8a51b55c1b396ce1fe761e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 30 Oct 2024 09:56:47 +0800 Subject: [PATCH 096/102] =?UTF-8?q?=E9=87=87=E6=A0=B7=E7=8E=87=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java index 8126f85..a8f36d8 100644 --- a/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java +++ b/jcpp-infrastructure-stats/src/main/java/sanbing/jcpp/infrastructure/stats/DefaultStatsFactory.java @@ -33,7 +33,7 @@ public class DefaultStatsFactory implements StatsFactory { @Value("${metrics.enabled:true}") private Boolean metricsEnabled; - @Value("${metrics.timer.percentiles:1.0}") + @Value("${metrics.timer.percentiles:0.5}") private String timerPercentilesStr; private double[] timerPercentiles; From d3b6623499e2f6fd72fa80fceb682126237709a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 30 Oct 2024 10:52:39 +0800 Subject: [PATCH 097/102] =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/queue/memory/DefaultInMemoryStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java index e800140..155f512 100644 --- a/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java +++ b/jcpp-infrastructure-queue/src/main/java/sanbing/jcpp/infrastructure/queue/memory/DefaultInMemoryStorage.java @@ -26,7 +26,7 @@ public final class DefaultInMemoryStorage implements InMemoryStorage { public void printStats() { storage.forEach((topic, queue) -> { if (!queue.isEmpty()) { - log.info("[{}] Queue Size [{}]", topic, queue.size()); + log.info("[{}] Memory Queue Size [{}]", topic, queue.size()); } }); } From bcaf4b94ab564fa105a79625ff508ee835c0b316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Wed, 30 Oct 2024 15:00:36 +0800 Subject: [PATCH 098/102] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../v150/{ => cmd}/YunKuaiChongV150BmsHandshakeULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150HeartbeatULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150LoginAckDLCmd.java | 2 +- .../yunkuaichong/v150/{ => cmd}/YunKuaiChongV150LoginULCmd.java | 2 +- .../{ => cmd}/YunKuaiChongV150QueryPricingModelAckDLCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150QueryPricingModelULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150RealTimeDataULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150RemoteStartDLCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150RemoteStartResultULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150RemoteStopDLCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150RemoteStopResultULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150SetPricingModelAckULCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150SetPricingModelDLCmd.java | 2 +- .../{ => cmd}/YunKuaiChongV150TransactionRecordAckDLCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150TransactionRecordULCmd.java | 2 +- .../{ => cmd}/YunKuaiChongV150VerifyPricingModelAckDLCmd.java | 2 +- .../v150/{ => cmd}/YunKuaiChongV150VerifyPricingModelULCmd.java | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150BmsHandshakeULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150HeartbeatULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150LoginAckDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150LoginULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150QueryPricingModelAckDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150QueryPricingModelULCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150RealTimeDataULCmd.java (99%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150RemoteStartDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150RemoteStartResultULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150RemoteStopDLCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150RemoteStopResultULCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150SetPricingModelAckULCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150SetPricingModelDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150TransactionRecordAckDLCmd.java (97%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150TransactionRecordULCmd.java (99%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150VerifyPricingModelAckDLCmd.java (98%) rename jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/{ => cmd}/YunKuaiChongV150VerifyPricingModelULCmd.java (97%) diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150BmsHandshakeULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150BmsHandshakeULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java index a7058e0..36f35f8 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150BmsHandshakeULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsHandshakeULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import cn.hutool.core.util.HexUtil; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java index 183c3fb..ef41e9f 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150HeartbeatULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java index 3c9a6a1..4aa8749 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import cn.hutool.core.util.RandomUtil; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java index e311860..8073165 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150LoginULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150LoginULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java index 3824162..5f48ce2 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java index 4863012..3741366 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150QueryPricingModelULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150QueryPricingModelULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RealTimeDataULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java similarity index 99% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RealTimeDataULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java index 54e4911..8aa463c 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RealTimeDataULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RealTimeDataULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java index d5cc7d4..b82d699 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartResultULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java index 798aa85..6dca0e4 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStartResultULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStartResultULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java index 6b2685a..01e652a 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopResultULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopResultULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java index 5ad8201..dbda740 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150RemoteStopResultULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150RemoteStopResultULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelAckULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelAckULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java index 677fea9..3b76a9a 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelAckULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelAckULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java index 18f97cd..3eb57c6 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150SetPricingModelDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150SetPricingModelDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java index 5b2812f..6bea5b7 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java similarity index 99% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java index 8d6c6d9..8fbdf8b 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150TransactionRecordULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150TransactionRecordULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; import io.netty.buffer.ByteBuf; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelAckDLCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java similarity index 98% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelAckDLCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java index e7f33cb..c81e4ee 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelAckDLCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelAckDLCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java similarity index 97% rename from jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelULCmd.java rename to jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java index 5a55748..24a6d31 100644 --- a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunKuaiChongV150VerifyPricingModelULCmd.java +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150VerifyPricingModelULCmd.java @@ -2,7 +2,7 @@ * 抖音关注:程序员三丙 * 知识星球:https://t.zsxq.com/j9b21 */ -package sanbing.jcpp.protocol.yunkuaichong.v150; +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; import com.fasterxml.jackson.databind.node.ObjectNode; From e7ee6be4877b6f43d8e93a8ddc1066c6d6579013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Fri, 1 Nov 2024 10:24:36 +0800 Subject: [PATCH 099/102] =?UTF-8?q?=E8=BF=87=E6=9C=9F=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=B8=8D=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queue/consumer/ProtocolUplinkConsumerService.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java index 0316d58..4f6f129 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java @@ -141,6 +141,14 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple Callback callback = new PackCallback<>(id, ctx); + // 过期请求不处理 + if (TracerContextUtil.getCurrentTracer().getTracerTs() < (System.currentTimeMillis() - packProcessingTimeout)) { + + callback.onSuccess(); + + return; + } + try { UplinkQueueMessage uplinkQueueMsg = msg.getValue(); From 7c3dc23e8f45c42ea3cf7d5c2b54ac109aa1623b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= Date: Fri, 1 Nov 2024 10:30:04 +0800 Subject: [PATCH 100/102] =?UTF-8?q?=E4=B8=8D=E9=99=90=E5=AE=9A=E5=A0=86?= =?UTF-8?q?=E5=A4=96=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/start.sh b/docker/start.sh index d16c041..e22bc85 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -12,7 +12,7 @@ export JAVA_APP_OPTS="-XX:+UseContainerSupport -XX:InitialRAMPercentage=10 -XX:M -XX:HeapDumpPath=/var/log/sanbing/heapdump/ \ -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark \ -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10 \ - -Xss512k -XX:MaxDirectMemorySize=256M -XX:G1ReservePercent=20 \ + -Xss512k -XX:G1ReservePercent=20 \ -XX:-OmitStackTraceInFastThrow \ -Dlogging.config=/app/config/log4j2.xml" From d6caef41bfd62f38873ef64da2b93b476f7e9cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 5 Nov 2024 03:32:32 +0000 Subject: [PATCH 101/102] update README.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 三丙 <10604541+sanbing-os@user.noreply.gitee.com> --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 4d020fb..4b57f90 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,6 @@ https://www.yuque.com/chengxuyuansanbing/jcpp/xck5fvolx3l7oqhw ------------------------------ -#### 代码编写指南 -##### 教程以视频形式在自媒体平台陆续发布,敬请关注 - - ##### 作者微信 From 72146c541926066e490cf3d973e55482ede59f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E4=B8=99?= <10604541+sanbing-os@user.noreply.gitee.com> Date: Tue, 12 Nov 2024 09:51:55 +0000 Subject: [PATCH 102/102] update README.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 三丙 <10604541+sanbing-os@user.noreply.gitee.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b57f90..13bb667 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ https://www.yuque.com/chengxuyuansanbing/jcpp/xck5fvolx3l7oqhw ------------------------------ -##### 作者微信 +##### 加入社群先加作者微信 ------------------------------