├── .gitattributes ├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .mvn ├── settings.xml └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── docker ├── README.md ├── docker-compose-mysql.yml ├── docker-compose-seata.yml ├── mysql │ ├── leaf.sql │ ├── mysql-dockerfile │ ├── seata_order.sql │ ├── seata_storage.sql │ ├── spring_project_1.sql │ └── spring_project_2.sql └── seata │ └── config │ ├── file.conf │ └── registry.conf ├── monitor ├── README.md ├── jmx_prometheus_javaagent-0.16.1.jar └── prometheus-jmx-config.yml ├── mvnw ├── mvnw.cmd ├── pom.xml ├── springboot-cache ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── cache │ ├── CacheAutoConfig.java │ ├── CacheController.java │ ├── memcached │ ├── JSONTranscoder.java │ ├── MemcachedConfig.java │ └── MemcachedService.java │ ├── redis │ └── RedisService.java │ └── vo │ └── Model.java ├── springboot-common-web ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── common │ └── web │ ├── WebAutoConfig.java │ ├── advice │ └── ExceptionAdvice.java │ ├── context │ ├── RequestParamContext.java │ └── TraceIdHolder.java │ ├── filter │ ├── RequestBodyWrapper.java │ └── RequestParamFilter.java │ └── response │ ├── BaseResponse.java │ ├── MapResponse.java │ ├── PageResponse.java │ └── ResponseCode.java ├── springboot-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── common │ ├── algorithm │ └── RedEnvelopeAlgorithm.java │ ├── check │ ├── CheckUtil.java │ └── annotation │ │ ├── FieldNotEmpty.java │ │ └── FieldNotNull.java │ ├── excel │ └── ExcelVersion.java │ ├── model │ ├── Page.java │ └── RowModel.java │ ├── tuple │ └── Tuple2.java │ └── util │ ├── DateTimeUtil.java │ ├── EmailUtil.java │ ├── FileUtil.java │ ├── HttpUtil.java │ ├── IpUtil.java │ ├── LogUtil.java │ ├── MathUtil.java │ ├── NumberUtil.java │ ├── PPTUtil.java │ ├── PageUtil.java │ └── StringUtil.java ├── springboot-dao ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── dao │ │ ├── DaoAutoConfig.java │ │ ├── entity │ │ ├── User.java │ │ └── UserLoginLog.java │ │ ├── handler │ │ └── MyMetaObjectHandler.java │ │ ├── manager │ │ ├── UserLoginLogManager.java │ │ └── UserManager.java │ │ └── mapper │ │ ├── UserLoginLogMapper.java │ │ └── UserMapper.java │ └── resources │ ├── spring_project_1.sql │ └── spring_project_2.sql ├── springboot-db-controller ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── db │ └── controller │ ├── DbControllerConfig.java │ ├── DruidViewConfig.java │ ├── UserController.java │ └── UserService.java ├── springboot-dubbo-api ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── dubbo │ └── api │ ├── FirstDubboService.java │ ├── domain │ └── DubboDomain.java │ ├── request │ ├── BaseRequest.java │ └── GetDubboInfoRequest.java │ └── response │ ├── BaseResponse.java │ └── GetDubboInfoResponse.java ├── springboot-dubbo-consumer ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── dubbo │ │ └── consumer │ │ ├── DubboController.java │ │ ├── config │ │ ├── DubboConfig.java │ │ └── SentinelAspectConfiguration.java │ │ ├── filter │ │ └── DubboTraceFilter.java │ │ └── service │ │ └── DubboService.java │ └── resources │ └── META-INF │ └── dubbo │ └── org.apache.dubbo.rpc.Filter ├── springboot-dubbo-provider ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── dubbo │ │ └── provider │ │ ├── DubboApplication.java │ │ ├── config │ │ └── DubboConfig.java │ │ └── service │ │ └── FirstDubboServiceImpl.java │ └── resources │ ├── application-local.yml │ └── application.yml ├── springboot-elasticsearch ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── elasticsearch │ ├── ElasticsearchAutoConfig.java │ ├── ElasticsearchController.java │ ├── repository │ └── ArticleRepository.java │ └── vo │ └── Article.java ├── springboot-feign-consumer ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── feign │ └── consumer │ ├── FeignAutoConfig.java │ ├── FeignController.java │ └── service │ └── FeignService.java ├── springboot-feign-provider ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── feign │ │ └── provider │ │ ├── FeignApplication.java │ │ └── FeignServiceController.java │ └── resources │ └── application.yml ├── springboot-leaf ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── leaf │ │ ├── Constants.java │ │ ├── IDGen.java │ │ ├── aspect │ │ └── RtAspect.java │ │ ├── common │ │ ├── PropertyFactory.java │ │ ├── Result.java │ │ ├── Status.java │ │ └── ZeroIDGen.java │ │ ├── exception │ │ └── InitException.java │ │ └── segment │ │ ├── SegmentIDGenImpl.java │ │ ├── SegmentService.java │ │ ├── dao │ │ ├── IDAllocDao.java │ │ ├── IDAllocMapper.java │ │ └── impl │ │ │ └── IDAllocDaoImpl.java │ │ └── model │ │ ├── LeafAlloc.java │ │ ├── Segment.java │ │ └── SegmentBuffer.java │ └── resources │ └── leaf.sql ├── springboot-netty ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── netty │ │ ├── config │ │ └── ChatServerConfig.java │ │ └── server │ │ ├── ChatServer.java │ │ ├── ChatServerHandler.java │ │ ├── HeartbeatHandler.java │ │ └── WebSocketServerInitializer.java │ └── resources │ └── static │ └── chat-netty.html ├── springboot-opentelemetry ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── metrics │ ├── Counter.java │ ├── MetricsAutoConfig.java │ └── filter │ └── RequestCountFilter.java ├── springboot-rocketmq-consumer ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── rocketmq │ └── consumer │ ├── domain │ └── NewChatRecord.java │ ├── listener │ ├── ClearUserMessageListener.java │ ├── NewChatRecordListener.java │ └── OrderlyMessageListener.java │ └── service │ └── ChatService.java ├── springboot-rocketmq-producer ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── rocketmq │ │ └── producer │ │ ├── MessageController.java │ │ ├── ProducerConstants.java │ │ ├── UserController.java │ │ ├── domain │ │ ├── NewChatRecord.java │ │ └── TransactionMessageObj.java │ │ ├── listener │ │ └── ClearUserTransactionListener.java │ │ ├── service │ │ └── RocketMQService.java │ │ └── websocket │ │ ├── ChatWebSocket.java │ │ └── WebSocketConfig.java │ └── resources │ └── static │ └── chat-websocket.html ├── springboot-seata-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── doodl6 │ └── springboot │ └── seata │ └── common │ ├── Constants.java │ └── entity │ ├── Order.java │ └── Storage.java ├── springboot-seata-order ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── seata │ │ └── order │ │ ├── OrderApplication.java │ │ ├── controller │ │ └── OrderController.java │ │ ├── manager │ │ └── OrderManager.java │ │ ├── mapper │ │ └── OrderMapper.java │ │ └── service │ │ └── OrderService.java │ └── resources │ └── application.yml ├── springboot-seata-storage ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── seata │ │ └── storage │ │ ├── StorageApplication.java │ │ ├── controller │ │ └── StorageController.java │ │ ├── manager │ │ └── StorageManager.java │ │ ├── mapper │ │ └── StorageMapper.java │ │ └── service │ │ └── StorageService.java │ └── resources │ └── application.yml ├── springboot-seata ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── seata │ │ ├── SeataApplication.java │ │ ├── controller │ │ └── TradeController.java │ │ ├── feign │ │ ├── IOrderService.java │ │ └── IStorageService.java │ │ ├── response │ │ └── StorageWithOrderData.java │ │ └── service │ │ └── TradeService.java │ └── resources │ └── application.yml ├── springboot-web ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── doodl6 │ │ └── springboot │ │ └── web │ │ ├── WebApplication.java │ │ ├── config │ │ ├── DocConfig.java │ │ └── WebMvcConfig.java │ │ ├── constant │ │ └── WebConstants.java │ │ ├── controller │ │ ├── ChatController.java │ │ ├── EventStreamController.java │ │ ├── ExcelController.java │ │ ├── MonitorController.java │ │ ├── StandardController.java │ │ └── XhttpController.java │ │ ├── dto │ │ └── ExcelData.java │ │ ├── listener │ │ └── InitDataListener.java │ │ ├── request │ │ └── CheckParameterRequest.java │ │ ├── response │ │ └── CheckParameterResult.java │ │ ├── util │ │ ├── RequestUtil.java │ │ └── ResponseUtil.java │ │ └── vo │ │ ├── ExcelVo.java │ │ └── MessageVo.java │ └── resources │ ├── application-local.yml │ ├── application.properties │ ├── application.yml │ ├── leaf.properties │ ├── logback-common.xml │ ├── logback-spring.xml │ ├── logback.properties │ ├── sharding-db.yml │ └── static │ ├── chat-long-polling.html │ ├── event-stream.html │ ├── favicon.ico │ └── xhttp.html └── springboot-zookeeper ├── pom.xml └── src └── main └── java └── com └── doodl6 └── springboot └── zookeeper ├── ZookeeperController.java └── service └── ZookeeperService.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - 'feature/**' 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up JDK 17 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '17' 20 | distribution: 'temurin' 21 | - name: Build with Maven 22 | run: ./mvnw -B package --file pom.xml 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class files 2 | *.class 3 | target/ 4 | 5 | # Log files 6 | /logs/ 7 | 8 | # IntelliJ IDEA files 9 | .idea/ 10 | *.iml 11 | 12 | .flattened-pom.xml 13 | 14 | # Temp file 15 | /docker/consul/data/ 16 | /docker/elasticsearch/master/data/ 17 | /docker/elasticsearch/master/logs/ 18 | /docker/elasticsearch/master/plugins/ 19 | /docker/elasticsearch/slave/data/ 20 | /docker/elasticsearch/slave/logs/ 21 | /docker/rocketmq/broker/store/ 22 | /docker/rocketmq/broker/logs/ 23 | /docker/rocketmq/namesrv/logs/ 24 | /docker/grafana/ 25 | /docker/prometheus/data/ 26 | /docker/zk/zoo1/data 27 | /docker/zk/zoo2/data 28 | /docker/zk/zoo3/data -------------------------------------------------------------------------------- /.mvn/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | sonatype 8 | 9 | 10 | sonatype-nexus-snapshots 11 | https://oss.sonatype.org/content/repositories/snapshots 12 | 13 | true 14 | 15 | 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | sonatype 25 | 26 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinDai/SpringBoot-Project/44c26572323ad5daecedfb5c5f951a100e3761e5/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Martin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | ## Docker环境部署使用说明 2 | 3 | ### _进入当前目录,根据实际情况选择需要的容器组合命令执行_ 4 | 5 | ### MySQL服务 6 | 7 | ```bash 8 | docker-compose -f docker-compose-mysql.yml up -d 9 | ``` 10 | 11 | ### Seata-Server服务 12 | 13 | ```bash 14 | docker-compose -f docker-compose-seata.yml up -d 15 | ``` -------------------------------------------------------------------------------- /docker/docker-compose-mysql.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mysql: 3 | container_name: mysql 4 | build: 5 | context: mysql 6 | dockerfile: ./mysql-dockerfile 7 | environment: 8 | - "MYSQL_ROOT_PASSWORD=martin-local" 9 | - "MYSQL_ROOT_HOST=%" 10 | command: [ 11 | 'mysqld', 12 | '--innodb-buffer-pool-size=20M', 13 | '--character-set-server=utf8', 14 | '--collation-server=utf8_general_ci', 15 | '--default-time-zone=+8:00', 16 | '--lower-case-table-names=1' 17 | ] 18 | ports: 19 | - "3306:3306" 20 | networks: 21 | - net-mysql 22 | networks: 23 | net-mysql: 24 | driver: bridge -------------------------------------------------------------------------------- /docker/docker-compose-seata.yml: -------------------------------------------------------------------------------- 1 | services: 2 | seata-server: 3 | image: seataio/seata-server:2.0.0 4 | container_name: seata-server 5 | volumes: 6 | - ./seata/config:/root/seata-config 7 | environment: 8 | - SEATA_PORT=8091 9 | - SEATA_CONFIG_NAME=file:/root/seata-config/registry 10 | ports: 11 | - "8091:8091" 12 | networks: 13 | - net-seata 14 | networks: 15 | net-seata: 16 | driver: bridge -------------------------------------------------------------------------------- /docker/mysql/leaf.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE leaf; 2 | USE leaf; 3 | CREATE TABLE `leaf_alloc` ( 4 | `biz_tag` varchar(128) NOT NULL DEFAULT '', 5 | `max_id` bigint(20) NOT NULL DEFAULT '1', 6 | `step` int(11) NOT NULL, 7 | `description` varchar(256) DEFAULT NULL, 8 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 9 | PRIMARY KEY (`biz_tag`) 10 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 11 | 12 | insert into leaf_alloc(biz_tag, max_id, step, description) values('traceId', 1, 2000, 'trace id for every request'); 13 | -------------------------------------------------------------------------------- /docker/mysql/mysql-dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql/mysql-server:8.0 2 | 3 | ADD spring_project_1.sql /docker-entrypoint-initdb.d/ 4 | ADD spring_project_2.sql /docker-entrypoint-initdb.d/ 5 | ADD leaf.sql /docker-entrypoint-initdb.d/ 6 | ADD seata_order.sql /docker-entrypoint-initdb.d/ 7 | ADD seata_storage.sql /docker-entrypoint-initdb.d/ -------------------------------------------------------------------------------- /docker/mysql/seata_order.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE seata_order; 2 | USE seata_order; 3 | CREATE TABLE `order` 4 | ( 5 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 6 | `user_id` int(11) NOT NULL DEFAULT 0, 7 | `goods_code` varchar(32) NOT NULL, 8 | `stock_num` int(11) NOT NULL, 9 | `money` int(11) NOT NULL, 10 | PRIMARY KEY (`id`) 11 | ) ENGINE = InnoDB 12 | DEFAULT CHARSET = utf8mb4; 13 | 14 | -- for AT mode you must to init this sql for you business database. the seata server not need it. 15 | CREATE TABLE IF NOT EXISTS `undo_log` 16 | ( 17 | `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', 18 | `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id', 19 | `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization', 20 | `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', 21 | `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status', 22 | `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime', 23 | `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime', 24 | UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) 25 | ) ENGINE = InnoDB 26 | AUTO_INCREMENT = 1 27 | DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table'; -------------------------------------------------------------------------------- /docker/mysql/seata_storage.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE seata_storage; 2 | USE seata_storage; 3 | CREATE TABLE `storage` 4 | ( 5 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 6 | `goods_code` varchar(32) NOT NULL, 7 | `stock_num` int(11) NOT NULL, 8 | PRIMARY KEY (`id`), 9 | UNIQUE KEY `UK_GOODS_CODE` (`goods_code`) 10 | ) ENGINE = InnoDB 11 | DEFAULT CHARSET = utf8mb4; 12 | 13 | -- for AT mode you must to init this sql for you business database. the seata server not need it. 14 | CREATE TABLE IF NOT EXISTS `undo_log` 15 | ( 16 | `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', 17 | `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id', 18 | `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization', 19 | `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', 20 | `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status', 21 | `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime', 22 | `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime', 23 | UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) 24 | ) ENGINE = InnoDB 25 | AUTO_INCREMENT = 1 26 | DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table'; -------------------------------------------------------------------------------- /docker/mysql/spring_project_1.sql: -------------------------------------------------------------------------------- 1 | -- ---------------------------- 2 | -- create and use database `spring_project_1` 3 | -- ---------------------------- 4 | create database spring_project_1; 5 | use spring_project_1; 6 | 7 | -- ---------------------------- 8 | -- Table structure for `user` 9 | -- ---------------------------- 10 | DROP TABLE IF EXISTS `user`; 11 | CREATE TABLE `user` ( 12 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 13 | `name` varchar(48) NOT NULL, 14 | `created` datetime NOT NULL, 15 | `modified` datetime NOT NULL, 16 | PRIMARY KEY (`id`), 17 | UNIQUE KEY `UK_NAME` (`name`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 19 | 20 | -- ---------------------------- 21 | -- Table structure for `user_login_log_0` 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `user_login_log_0`; 24 | CREATE TABLE `user_login_log_0` ( 25 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 26 | `user_id` bigint(20) NOT NULL, 27 | `login_time` datetime NOT NULL, 28 | `created` datetime NOT NULL, 29 | PRIMARY KEY (`id`), 30 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 32 | 33 | -- ---------------------------- 34 | -- Table structure for `user_login_log_1` 35 | -- ---------------------------- 36 | DROP TABLE IF EXISTS `user_login_log_1`; 37 | CREATE TABLE `user_login_log_1` ( 38 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 39 | `user_id` bigint(20) NOT NULL, 40 | `login_time` datetime NOT NULL, 41 | `created` datetime NOT NULL, 42 | PRIMARY KEY (`id`), 43 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 44 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -------------------------------------------------------------------------------- /docker/mysql/spring_project_2.sql: -------------------------------------------------------------------------------- 1 | -- ---------------------------- 2 | -- create and use database `spring_project_2` 3 | -- ---------------------------- 4 | create database spring_project_2; 5 | use spring_project_2; 6 | 7 | -- ---------------------------- 8 | -- Table structure for `user_login_log_0` 9 | -- ---------------------------- 10 | DROP TABLE IF EXISTS `user_login_log_0`; 11 | CREATE TABLE `user_login_log_0` ( 12 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 13 | `user_id` bigint(20) NOT NULL, 14 | `login_time` datetime NOT NULL, 15 | `created` datetime NOT NULL, 16 | PRIMARY KEY (`id`), 17 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 19 | 20 | -- ---------------------------- 21 | -- Table structure for `user_login_log_1` 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `user_login_log_1`; 24 | CREATE TABLE `user_login_log_1` ( 25 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 26 | `user_id` bigint(20) NOT NULL, 27 | `login_time` datetime NOT NULL, 28 | `created` datetime NOT NULL, 29 | PRIMARY KEY (`id`), 30 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -------------------------------------------------------------------------------- /docker/seata/config/registry.conf: -------------------------------------------------------------------------------- 1 | # 注册中心配置 2 | registry { 3 | # 可选用的类型file 、nacos 、eureka、redis、zk、consul、etcd3、sofa 4 | type = "file" 5 | } 6 | 7 | # 配置中心配置 8 | config { 9 | # file、nacos 、apollo、zk、consul、etcd3 10 | # 配置中心类型,从配置中心获取server的配置参数 11 | # 如果type=file,则从本地file.conf中获取配置参数 12 | type = "file" 13 | 14 | file { 15 | name = "file:/root/seata-config/file.conf" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /monitor/README.md: -------------------------------------------------------------------------------- 1 | # 本目录用于存放项目启动植入监控需要的相关配置 2 | 3 | ## 基于JMX+prometheus收集监控信息 4 | 5 | 在启动的时候以下参数 6 | 7 | ``` 8 | -javaagent:/monitor/jmx_prometheus_javaagent-0.16.1.jar=8088:/monitor/prometheus-jmx-config.yml 9 | ``` 10 | 11 | 需要替换``为本地的项目目录,`8088`也可以修改为自己想要的端口,启动以后,访问 `http://localhost:8088/metrics` 即可查看当前JVM各维度统计信息(prometheus格式) 12 | 13 | -------------------------------------------------------------------------------- /monitor/jmx_prometheus_javaagent-0.16.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinDai/SpringBoot-Project/44c26572323ad5daecedfb5c5f951a100e3761e5/monitor/jmx_prometheus_javaagent-0.16.1.jar -------------------------------------------------------------------------------- /monitor/prometheus-jmx-config.yml: -------------------------------------------------------------------------------- 1 | lowercaseOutputLabelNames: true 2 | lowercaseOutputName: true 3 | whitelistObjectNames: ["java.lang:type=OperatingSystem"] 4 | blacklistObjectNames: [] 5 | rules: 6 | - pattern: 'java.lang<>(committed_virtual_memory|free_physical_memory|free_swap_space|total_physical_memory|total_swap_space)_size:' 7 | name: os_$1_bytes 8 | type: GAUGE 9 | attrNameSnakeCase: true 10 | - pattern: 'java.lang<>((?!process_cpu_time)\w+):' 11 | name: os_$1 12 | type: GAUGE 13 | attrNameSnakeCase: true -------------------------------------------------------------------------------- /springboot-cache/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-cache 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | com.googlecode.xmemcached 21 | xmemcached 22 | 23 | 24 | 25 | org.redisson 26 | redisson-spring-boot-starter 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /springboot-cache/src/main/java/com/doodl6/springboot/cache/CacheAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.cache; 2 | 3 | import org.springframework.cache.annotation.EnableCaching; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @EnableCaching 8 | public class CacheAutoConfig { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-cache/src/main/java/com/doodl6/springboot/cache/memcached/JSONTranscoder.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.cache.memcached; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import net.rubyeye.xmemcached.transcoders.CachedData; 5 | import net.rubyeye.xmemcached.transcoders.PrimitiveTypeTranscoder; 6 | 7 | import java.io.UnsupportedEncodingException; 8 | 9 | /** 10 | * Created by daixiaoming on 2018/5/12. 11 | */ 12 | public class JSONTranscoder extends PrimitiveTypeTranscoder { 13 | 14 | private static final int JSON_FLAG = 0; 15 | 16 | @Override 17 | public Object decode(CachedData d) { 18 | if (d.getFlag() == 0) { 19 | String rv = null; 20 | try { 21 | if (d.getData() != null) { 22 | rv = new String(d.getData(), this.charset); 23 | } 24 | } catch (UnsupportedEncodingException e) { 25 | throw new RuntimeException(e); 26 | } 27 | return rv; 28 | } else { 29 | throw new RuntimeException("Decode JSON error"); 30 | } 31 | } 32 | 33 | @Override 34 | public CachedData encode(Object o) { 35 | byte[] b; 36 | 37 | try { 38 | b = JSON.toJSONString(o).getBytes(this.charset); 39 | } catch (UnsupportedEncodingException e) { 40 | throw new RuntimeException(e); 41 | } 42 | return new CachedData(JSON_FLAG, b); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /springboot-cache/src/main/java/com/doodl6/springboot/cache/memcached/MemcachedConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.cache.memcached; 2 | 3 | import net.rubyeye.xmemcached.MemcachedClient; 4 | import net.rubyeye.xmemcached.MemcachedClientBuilder; 5 | import net.rubyeye.xmemcached.XMemcachedClientBuilder; 6 | import net.rubyeye.xmemcached.transcoders.Transcoder; 7 | import net.rubyeye.xmemcached.utils.AddrUtil; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import java.io.IOException; 13 | 14 | @Configuration 15 | public class MemcachedConfig { 16 | 17 | @Value("${memcached.address}") 18 | private String address; 19 | 20 | @Bean 21 | public MemcachedClient memcachedClient() throws IOException { 22 | MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(address), new int[]{1}); 23 | //存储的数据使用JSON格式 24 | Transcoder jsonTranscoder = new JSONTranscoder(); 25 | builder.setTranscoder(jsonTranscoder); 26 | return builder.build(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /springboot-cache/src/main/java/com/doodl6/springboot/cache/memcached/MemcachedService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.cache.memcached; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import jakarta.annotation.Resource; 5 | import net.rubyeye.xmemcached.GetsResponse; 6 | import net.rubyeye.xmemcached.MemcachedClient; 7 | import net.rubyeye.xmemcached.exception.MemcachedException; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.concurrent.TimeoutException; 11 | 12 | /** 13 | * 基于xmemcached实现 14 | */ 15 | @Component 16 | public class MemcachedService { 17 | 18 | @Resource 19 | private MemcachedClient memcachedClient; 20 | 21 | public void set(String key, Object value) { 22 | try { 23 | memcachedClient.set(key, 1000, value); 24 | } catch (TimeoutException | InterruptedException | MemcachedException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | public T get(String key, Class type) { 30 | try { 31 | GetsResponse getsResponse = memcachedClient.gets(key, memcachedClient.getTranscoder()); 32 | String value = getsResponse.getValue(); 33 | if (value != null) { 34 | return JSON.parseObject(value, type); 35 | } 36 | } catch (TimeoutException | InterruptedException | MemcachedException e) { 37 | e.printStackTrace(); 38 | } 39 | 40 | return null; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /springboot-cache/src/main/java/com/doodl6/springboot/cache/vo/Model.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.cache.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * Created by daixiaoming on 2018/5/12. 12 | */ 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class Model implements Serializable { 18 | 19 | private String key; 20 | 21 | private String value; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /springboot-common-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-common-web 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common 17 | 18 | 19 | 20 | org.aspectj 21 | aspectjweaver 22 | 23 | 24 | 25 | javax.servlet 26 | javax.servlet-api 27 | provided 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/WebAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web; 2 | 3 | import org.springframework.boot.web.servlet.ServletComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ServletComponentScan(basePackages = "com.doodl6.springboot.common.web.filter") 8 | public class WebAutoConfig { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/advice/ExceptionAdvice.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.advice; 2 | 3 | import com.doodl6.springboot.common.util.LogUtil; 4 | import com.doodl6.springboot.common.web.response.BaseResponse; 5 | import com.doodl6.springboot.common.web.response.ResponseCode; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.web.HttpRequestMethodNotSupportedException; 9 | import org.springframework.web.bind.annotation.ControllerAdvice; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | 13 | /** 14 | * 统一处理异常 15 | * Created by daixiaoming on 2018/5/5. 16 | */ 17 | @Slf4j 18 | @ControllerAdvice 19 | public class ExceptionAdvice { 20 | 21 | @ExceptionHandler(value = Exception.class) 22 | @ResponseBody 23 | public BaseResponse handleException(HttpServletRequest request, Exception e) { 24 | BaseResponse response = new BaseResponse<>(); 25 | if (e instanceof IllegalArgumentException) { 26 | response.setMessage(e.getMessage()); 27 | response.setResult(ResponseCode.PARAMETER_ERROR); 28 | } else if (e instanceof HttpRequestMethodNotSupportedException) { 29 | response.setMessage("不支持的请求方式"); 30 | response.setResult(ResponseCode.PARAMETER_ERROR); 31 | } else { 32 | log.error(LogUtil.buildLog("请求出现异常", request.getRequestURI(), request.getParameterMap()), e); 33 | 34 | response.setMessage("服务器未知异常"); 35 | response.setResult(ResponseCode.UNKNOWN_ERROR); 36 | } 37 | 38 | return response; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/context/RequestParamContext.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.context; 2 | 3 | public class RequestParamContext { 4 | 5 | private static final ThreadLocal HOLDER = new ThreadLocal<>(); 6 | 7 | public static void set(String content) { 8 | HOLDER.set(content); 9 | } 10 | 11 | public static String get() { 12 | return HOLDER.get(); 13 | } 14 | 15 | public static void remove() { 16 | HOLDER.remove(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/context/TraceIdHolder.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.context; 2 | 3 | public class TraceIdHolder { 4 | 5 | private static final ThreadLocal TRACE_ID = new ThreadLocal<>(); 6 | 7 | public static void setTraceId(String traceId) { 8 | TRACE_ID.set(traceId); 9 | } 10 | 11 | public static String getTraceId() { 12 | return TRACE_ID.get(); 13 | } 14 | 15 | public static void removeTraceId() { 16 | TRACE_ID.remove(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/filter/RequestBodyWrapper.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.filter; 2 | 3 | import jakarta.servlet.ReadListener; 4 | import jakarta.servlet.ServletInputStream; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletRequestWrapper; 7 | 8 | import java.io.ByteArrayInputStream; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.InputStream; 11 | 12 | 13 | public class RequestBodyWrapper extends HttpServletRequestWrapper { 14 | 15 | private final byte[] body; 16 | 17 | public RequestBodyWrapper(HttpServletRequest request) { 18 | super(request); 19 | body = getRequestBody(request); 20 | } 21 | 22 | public byte[] getBody() { 23 | return body; 24 | } 25 | 26 | @Override 27 | public ServletInputStream getInputStream() { 28 | ByteArrayInputStream inputStream = new ByteArrayInputStream(body); 29 | return new ServletInputStream() { 30 | @Override 31 | public int read() { 32 | return inputStream.read(); 33 | } 34 | 35 | @Override 36 | public boolean isFinished() { 37 | return false; 38 | } 39 | 40 | @Override 41 | public boolean isReady() { 42 | return false; 43 | } 44 | 45 | @Override 46 | public void setReadListener(ReadListener readListener) { 47 | } 48 | }; 49 | } 50 | 51 | private static byte[] getRequestBody(HttpServletRequest request) { 52 | try { 53 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 54 | InputStream in = request.getInputStream(); 55 | byte[] buf = new byte[1024]; 56 | 57 | while (true) { 58 | int len = in.read(buf); 59 | if (len == -1) { 60 | return outputStream.toByteArray(); 61 | } 62 | 63 | if (len > 0) { 64 | outputStream.write(buf, 0, len); 65 | } 66 | } 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | } 70 | 71 | return new byte[0]; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/filter/RequestParamFilter.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.filter; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.doodl6.springboot.common.web.context.RequestParamContext; 5 | import jakarta.servlet.FilterChain; 6 | import jakarta.servlet.ServletException; 7 | import jakarta.servlet.ServletRequest; 8 | import jakarta.servlet.annotation.WebFilter; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import jakarta.servlet.http.HttpServletResponse; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.springframework.core.annotation.Order; 13 | import org.springframework.web.filter.OncePerRequestFilter; 14 | 15 | import java.io.IOException; 16 | import java.util.Map; 17 | 18 | @Order(1) 19 | @WebFilter(asyncSupported = true) 20 | public class RequestParamFilter extends OncePerRequestFilter { 21 | 22 | @Override 23 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 24 | String method = request.getMethod(); 25 | String contentType = request.getContentType(); 26 | String params; 27 | ServletRequest servletRequest; 28 | if ("POST".equals(method) && contentType != null && contentType.startsWith("application/json")) { 29 | RequestBodyWrapper wrapper = new RequestBodyWrapper(request); 30 | byte[] body = wrapper.getBody(); 31 | String encoding = getRequestEncoding(request); 32 | params = new String(body, encoding); 33 | if (JSON.isValid(params)) { 34 | params = JSON.parseObject(params).toJSONString(); 35 | } 36 | servletRequest = wrapper; 37 | } else { 38 | params = getParams(request); 39 | servletRequest = request; 40 | } 41 | RequestParamContext.set(params); 42 | filterChain.doFilter(servletRequest, response); 43 | RequestParamContext.remove(); 44 | } 45 | 46 | public static String getParams(HttpServletRequest request) { 47 | Map map = request.getParameterMap(); 48 | if (map != null && !map.isEmpty()) { 49 | return JSON.toJSONString(map); 50 | } else { 51 | return StringUtils.EMPTY; 52 | } 53 | } 54 | 55 | private static String getRequestEncoding(HttpServletRequest request) { 56 | String encoding = request.getCharacterEncoding(); 57 | if (encoding == null) { 58 | encoding = "UTF-8"; 59 | } 60 | 61 | return encoding; 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/response/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.response; 2 | 3 | 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.experimental.Accessors; 8 | 9 | /** 10 | * 基础响应结果类 11 | */ 12 | @Getter 13 | @Setter 14 | @Accessors(chain = true) 15 | @NoArgsConstructor 16 | public class BaseResponse { 17 | 18 | /** 19 | * 错误信息 20 | */ 21 | private String message; 22 | /** 23 | * 结果代码 24 | */ 25 | private int result = ResponseCode.SUCCESS; 26 | /** 27 | * 响应数据 28 | */ 29 | private T data; 30 | 31 | public static BaseResponse success() { 32 | return new BaseResponse<>(); 33 | } 34 | 35 | public static BaseResponse success(T t) { 36 | return new BaseResponse<>(t); 37 | } 38 | 39 | public BaseResponse(T data) { 40 | this.data = data; 41 | } 42 | 43 | /** 44 | * 判断是否响应成功 45 | */ 46 | public boolean isSuccess() { 47 | return result == ResponseCode.SUCCESS; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/response/MapResponse.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.response; 2 | 3 | 4 | import com.google.common.collect.Maps; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * Map结果类 10 | */ 11 | public class MapResponse extends BaseResponse> { 12 | 13 | public MapResponse appendData(String key, Object value) { 14 | Map data = getData(); 15 | if (data == null) { 16 | data = Maps.newHashMap(); 17 | setData(data); 18 | } 19 | data.put(key, value); 20 | 21 | return this; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/response/PageResponse.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.response; 2 | 3 | 4 | import com.doodl6.springboot.common.model.Page; 5 | 6 | /** 7 | * 分页结果类 8 | */ 9 | public class PageResponse extends BaseResponse> { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /springboot-common-web/src/main/java/com/doodl6/springboot/common/web/response/ResponseCode.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.web.response; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * 响应代码 8 | */ 9 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 10 | public final class ResponseCode { 11 | 12 | /** 13 | * 未知异常 14 | */ 15 | public static final int UNKNOWN_ERROR = -1; 16 | 17 | /** 18 | * 成功 19 | */ 20 | public static final int SUCCESS = 200; 21 | 22 | /** 23 | * 参数错误 24 | */ 25 | public static final int PARAMETER_ERROR = 300; 26 | 27 | /** 28 | * 业务异常 29 | */ 30 | public static final int BIZ_ERROR = 400; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /springboot-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | com.doodl6 6 | SpringBoot-Project 7 | ${revision} 8 | 9 | 4.0.0 10 | springboot-common 11 | 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-logging 17 | 18 | 19 | 20 | org.apache.commons 21 | commons-lang3 22 | 23 | 24 | org.apache.commons 25 | commons-collections4 26 | 27 | 28 | commons-io 29 | commons-io 30 | 31 | 32 | org.apache.commons 33 | commons-math3 34 | 35 | 36 | 37 | org.apache.commons 38 | commons-email 39 | 40 | 41 | 42 | org.apache.httpcomponents 43 | httpclient 44 | 45 | 46 | 47 | com.google.guava 48 | guava 49 | 50 | 51 | 52 | org.apache.poi 53 | poi-ooxml 54 | 55 | 56 | org.apache.poi 57 | poi-scratchpad 58 | 59 | 60 | cn.idev.excel 61 | fastexcel 62 | 63 | 64 | 65 | com.alibaba.fastjson2 66 | fastjson2 67 | 68 | 69 | 70 | cn.hutool 71 | hutool-all 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/check/annotation/FieldNotEmpty.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.check.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target({ElementType.FIELD}) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface FieldNotEmpty { 9 | 10 | int minLength() default -1; 11 | 12 | int maxLength() default -1; 13 | 14 | String name() default "fieldName"; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/check/annotation/FieldNotNull.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.check.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Target({ElementType.FIELD}) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Documented 8 | public @interface FieldNotNull { 9 | 10 | String name() default ""; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/excel/ExcelVersion.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.excel; 2 | 3 | /** 4 | * Created by daixiaoming on 2018/9/19. 5 | */ 6 | public enum ExcelVersion { 7 | XLS(".xls"), 8 | XLSX(".xlsx"); 9 | 10 | private final String suffix; 11 | 12 | ExcelVersion(String suffix) { 13 | this.suffix = suffix; 14 | } 15 | 16 | public String getSuffix() { 17 | return suffix; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/model/Page.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | /** 10 | * 分页数据模型类 11 | */ 12 | @Getter 13 | @Setter 14 | public class Page implements Serializable { 15 | 16 | private int pageNo = 1; 17 | 18 | private int pageSize = 10; 19 | 20 | private int total; 21 | 22 | private int pageCount; 23 | 24 | private List list; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/model/RowModel.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * excel行模型 10 | */ 11 | @Getter 12 | @Setter 13 | public class RowModel { 14 | 15 | /** 16 | * 单行 17 | */ 18 | private List cellList; 19 | /** 20 | * 多行 21 | */ 22 | private List> multiCellList; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/tuple/Tuple2.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.tuple; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | /** 8 | * 元组 9 | */ 10 | @Getter 11 | @Setter 12 | @AllArgsConstructor 13 | public class Tuple2 { 14 | 15 | private P1 p1; 16 | 17 | private P2 p2; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.util; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | import org.apache.commons.lang3.StringUtils; 7 | 8 | import java.io.File; 9 | import java.io.FileWriter; 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | /** 14 | * 文件工具类 15 | */ 16 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 17 | public final class FileUtil { 18 | 19 | /** 20 | * 删除单个文件 21 | * 22 | * @param filePath 文件绝对路径 23 | */ 24 | public static void deleteFile(String filePath) { 25 | if (StringUtils.isBlank(filePath)) { 26 | throw new IllegalArgumentException("文件路径为空"); 27 | } 28 | 29 | File file = new File(filePath); 30 | if (file.isFile() && file.exists()) { 31 | file.delete(); 32 | } 33 | } 34 | 35 | /** 36 | * 删除文件夹 37 | * 38 | * @param directoryPath 文件夹路径 39 | */ 40 | public static void deleteDirectory(String directoryPath) { 41 | if (StringUtils.isBlank(directoryPath)) { 42 | throw new IllegalArgumentException("文件夹路径为空"); 43 | } 44 | 45 | if (!directoryPath.endsWith(File.separator)) { 46 | directoryPath += File.separator; 47 | } 48 | 49 | File directoryFile = new File(directoryPath); 50 | if (!directoryFile.exists() || !directoryFile.isDirectory()) { 51 | return; 52 | } 53 | 54 | File[] files = directoryFile.listFiles(); 55 | if (files != null) { 56 | for (File file : files) { 57 | if (file.isFile()) { 58 | file.delete(); 59 | } else { 60 | deleteDirectory(file.getAbsolutePath()); 61 | } 62 | } 63 | } 64 | 65 | directoryFile.delete(); 66 | } 67 | 68 | /** 69 | * 把内容写入目标文件 70 | */ 71 | public static void writeToFile(List contentList, String filePath) throws IOException { 72 | File file = new File(filePath); 73 | if (file.exists()) { 74 | file.delete(); 75 | } 76 | file.createNewFile(); 77 | try (FileWriter fileWriter = new FileWriter(file)) { 78 | for (String content : contentList) { 79 | fileWriter.write(content); 80 | fileWriter.write("\n"); 81 | fileWriter.flush(); 82 | } 83 | } 84 | } 85 | 86 | public static void writeLine(FileWriter fileWriter, Object... objArray) throws IOException { 87 | for (Object object : objArray) { 88 | fileWriter.write(JSON.toJSONString(object)); 89 | fileWriter.write("\n"); 90 | fileWriter.flush(); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/util/IpUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.util; 2 | 3 | import com.google.common.collect.Lists; 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.net.Inet4Address; 8 | import java.net.InetAddress; 9 | import java.net.NetworkInterface; 10 | import java.util.Enumeration; 11 | import java.util.List; 12 | 13 | /** 14 | * IP工具类 15 | */ 16 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 17 | public final class IpUtil { 18 | 19 | /** 20 | * 获取本地所有IP4的地址 21 | */ 22 | public static List getAllIp4Address() { 23 | Enumeration allNetInterfaces = null; 24 | List ipList = Lists.newArrayList(); 25 | try { 26 | allNetInterfaces = NetworkInterface.getNetworkInterfaces(); 27 | } catch (java.net.SocketException e) { 28 | e.printStackTrace(); 29 | } 30 | 31 | InetAddress ip; 32 | while (allNetInterfaces != null && allNetInterfaces.hasMoreElements()) { 33 | NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement(); 34 | Enumeration addresses = netInterface.getInetAddresses(); 35 | while (addresses.hasMoreElements()) { 36 | ip = (InetAddress) addresses.nextElement(); 37 | if (ip instanceof Inet4Address) { 38 | ipList.add(ip.getHostAddress()); 39 | } 40 | } 41 | } 42 | 43 | return ipList; 44 | } 45 | 46 | public static void main(String[] args) { 47 | System.out.println(getAllIp4Address()); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/util/LogUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.util; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.doodl6.springboot.common.model.Page; 5 | import lombok.AccessLevel; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * 日志工具类 10 | */ 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class LogUtil { 13 | 14 | /** 15 | * 构建日志字符串 16 | */ 17 | public static String buildLog(Object... objArray) { 18 | StringBuilder logBuilder = new StringBuilder(); 19 | 20 | for (Object obj : objArray) { 21 | if (logBuilder.length() > 0) { 22 | logBuilder.append(" | "); 23 | } 24 | 25 | if (obj instanceof String) { 26 | logBuilder.append(obj); 27 | } else { 28 | logBuilder.append(JSON.toJSONString(obj)); 29 | } 30 | } 31 | 32 | return logBuilder.toString(); 33 | } 34 | 35 | public static void main(String[] args) { 36 | Page page = new Page<>(); 37 | System.out.println(JSON.toJSONString(page)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/util/MathUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.util; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * Created by daixiaoming on 2018/9/25. 8 | */ 9 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 10 | public class MathUtil { 11 | 12 | /** 13 | * 获取数组元素的最小公倍数 14 | */ 15 | public static int lcm(int[] array) { 16 | if (array == null || array.length == 0) { 17 | return 0; 18 | } 19 | int length = array.length; 20 | int max = 0; 21 | //先找出这n个数的那个最大的数 22 | for (int num : array) { 23 | if (num > max) { 24 | max = num; 25 | } 26 | } 27 | while (true) { 28 | //设置标记 29 | boolean flag = true; 30 | for (int num : array) { 31 | if (max % num != 0) { 32 | //只要有一个数不能整除max则令标记为false 33 | flag = false; 34 | break; 35 | } 36 | } 37 | //如果标记为true说明该max可以整除这n个数 38 | if (flag) { 39 | return max; 40 | } 41 | //最大数+1 42 | max++; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/util/NumberUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.util; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * 数字工具类 8 | */ 9 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 10 | public final class NumberUtil { 11 | 12 | /** 13 | * 限制数值不超过范围 14 | */ 15 | public static long limitNumber(long num, long min, long max) { 16 | if (num < min) { 17 | num = min; 18 | } else if (num > max) { 19 | num = max; 20 | } 21 | 22 | return num; 23 | } 24 | 25 | /** 26 | * 限制数值不超过范围 27 | */ 28 | public static int limitNumber(int num, int min, int max) { 29 | num = limitMinNumber(num, min); 30 | num = limitMaxNumber(num, max); 31 | return num; 32 | } 33 | 34 | /** 35 | * 限制数值最最小 36 | */ 37 | public static int limitMinNumber(int num, int min) { 38 | if (num < min) { 39 | num = min; 40 | } 41 | 42 | return num; 43 | } 44 | 45 | /** 46 | * 限制数值最大值 47 | */ 48 | public static int limitMaxNumber(int num, int max) { 49 | if (num > max) { 50 | num = max; 51 | } 52 | 53 | return num; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /springboot-common/src/main/java/com/doodl6/springboot/common/util/PageUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.common.util; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * 分页工具类 8 | */ 9 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 10 | public final class PageUtil { 11 | 12 | public static int checkPageNo(Integer pageNo) { 13 | return (pageNo == null || pageNo < 1) ? 1 : pageNo; 14 | } 15 | 16 | public static int checkPageSize(Integer pageSize) { 17 | return checkPageSize(pageSize, 10); 18 | } 19 | 20 | public static int checkPageSize(Integer pageSize, int defaultPageSize) { 21 | return checkPageSize(pageSize, defaultPageSize, Integer.MAX_VALUE); 22 | } 23 | 24 | public static int checkPageSize(Integer pageSize, int defaultPageSize, int maxPageSize) { 25 | return (pageSize == null || pageSize < 1) ? defaultPageSize : (pageSize > maxPageSize ? maxPageSize : pageSize); 26 | } 27 | 28 | /** 29 | * 计算页数 30 | */ 31 | public static int calcPageCount(int count, int pageSize) { 32 | int pageCount = count / pageSize; 33 | if (count % pageSize != 0 || pageCount == 0) { 34 | pageCount++; 35 | } 36 | 37 | return pageCount; 38 | } 39 | 40 | /** 41 | * 计算每页数量 42 | */ 43 | public static int calcPageSize(long count, int pageCount) { 44 | int pageSize = (int) (count / pageCount); 45 | if (count % pageSize != 0) { 46 | pageSize++; 47 | } 48 | 49 | return pageSize; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /springboot-dao/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | com.doodl6 6 | SpringBoot-Project 7 | ${revision} 8 | 9 | 4.0.0 10 | springboot-dao 11 | 12 | 13 | 14 | com.doodl6 15 | springboot-common 16 | 17 | 18 | 19 | com.mysql 20 | mysql-connector-j 21 | 22 | 23 | 24 | com.alibaba 25 | druid-spring-boot-3-starter 26 | 27 | 28 | 29 | org.mybatis.spring.boot 30 | mybatis-spring-boot-starter 31 | 32 | 33 | com.baomidou 34 | mybatis-plus-boot-starter 35 | 36 | 37 | 38 | org.apache.shardingsphere 39 | shardingsphere-jdbc 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/DaoAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @MapperScan("com.doodl6.springboot.dao.mapper") 8 | public class DaoAutoConfig { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.FieldFill; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * 用户实体类 14 | */ 15 | @Getter 16 | @Setter 17 | public class User { 18 | 19 | @TableId(type = IdType.AUTO) 20 | private Long id; 21 | 22 | private String name; 23 | 24 | @TableField(fill = FieldFill.INSERT) 25 | private Date created; 26 | 27 | @TableField(fill = FieldFill.INSERT_UPDATE) 28 | private Date modified; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/entity/UserLoginLog.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.FieldFill; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | 11 | import java.util.Date; 12 | 13 | /** 14 | * 用户登录日志实体类 15 | */ 16 | @Getter 17 | @Setter 18 | @TableName("user_login_log") 19 | public class UserLoginLog { 20 | 21 | @TableId(type = IdType.AUTO) 22 | private Long id; 23 | 24 | private Long userId; 25 | 26 | private Date loginTime; 27 | 28 | @TableField(fill = FieldFill.INSERT) 29 | private Date created; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/handler/MyMetaObjectHandler.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.handler; 2 | 3 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 4 | import org.apache.ibatis.reflection.MetaObject; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.Date; 8 | 9 | @Component 10 | public class MyMetaObjectHandler implements MetaObjectHandler { 11 | 12 | @Override 13 | public void insertFill(MetaObject metaObject) { 14 | setFieldValByName("created", new Date(), metaObject); 15 | setFieldValByName("modified", new Date(), metaObject); 16 | } 17 | 18 | @Override 19 | public void updateFill(MetaObject metaObject) { 20 | setFieldValByName("modified", new Date(), metaObject); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/manager/UserLoginLogManager.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.manager; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 4 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 5 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 6 | import com.doodl6.springboot.dao.entity.UserLoginLog; 7 | import com.doodl6.springboot.dao.mapper.UserLoginLogMapper; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class UserLoginLogManager extends ServiceImpl { 12 | 13 | public int deleteByUserId(long userId) { 14 | LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); 15 | wrapper.eq(UserLoginLog::getUserId, userId); 16 | return getBaseMapper().delete(wrapper); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/manager/UserManager.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.manager; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 5 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 6 | import com.doodl6.springboot.dao.entity.User; 7 | import com.doodl6.springboot.dao.mapper.UserMapper; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class UserManager extends ServiceImpl { 12 | 13 | public User queryByName(String name) { 14 | LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); 15 | wrapper.eq(User::getName, name); 16 | return getOne(wrapper); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/mapper/UserLoginLogMapper.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.doodl6.springboot.dao.entity.UserLoginLog; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface UserLoginLogMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-dao/src/main/java/com/doodl6/springboot/dao/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dao.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.doodl6.springboot.dao.entity.User; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface UserMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-dao/src/main/resources/spring_project_1.sql: -------------------------------------------------------------------------------- 1 | -- ---------------------------- 2 | -- create and use database `spring_project_1` 3 | -- ---------------------------- 4 | create database spring_project_1; 5 | use spring_project_1; 6 | 7 | -- ---------------------------- 8 | -- Table structure for `user` 9 | -- ---------------------------- 10 | DROP TABLE IF EXISTS `user`; 11 | CREATE TABLE `user` ( 12 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 13 | `name` varchar(48) NOT NULL, 14 | `created` datetime NOT NULL, 15 | `modified` datetime NOT NULL, 16 | PRIMARY KEY (`id`), 17 | UNIQUE KEY `UK_NAME` (`name`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 19 | 20 | -- ---------------------------- 21 | -- Table structure for `user_login_log_0` 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `user_login_log_0`; 24 | CREATE TABLE `user_login_log_0` ( 25 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 26 | `user_id` bigint(20) NOT NULL, 27 | `login_time` datetime NOT NULL, 28 | `created` datetime NOT NULL, 29 | PRIMARY KEY (`id`), 30 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 32 | 33 | -- ---------------------------- 34 | -- Table structure for `user_login_log_1` 35 | -- ---------------------------- 36 | DROP TABLE IF EXISTS `user_login_log_1`; 37 | CREATE TABLE `user_login_log_1` ( 38 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 39 | `user_id` bigint(20) NOT NULL, 40 | `login_time` datetime NOT NULL, 41 | `created` datetime NOT NULL, 42 | PRIMARY KEY (`id`), 43 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 44 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /springboot-dao/src/main/resources/spring_project_2.sql: -------------------------------------------------------------------------------- 1 | -- ---------------------------- 2 | -- create and use database `spring_project_2` 3 | -- ---------------------------- 4 | create database spring_project_2; 5 | use spring_project_2; 6 | 7 | -- ---------------------------- 8 | -- Table structure for `user_login_log_0` 9 | -- ---------------------------- 10 | DROP TABLE IF EXISTS `user_login_log_0`; 11 | CREATE TABLE `user_login_log_0` ( 12 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 13 | `user_id` bigint(20) NOT NULL, 14 | `login_time` datetime NOT NULL, 15 | `created` datetime NOT NULL, 16 | PRIMARY KEY (`id`), 17 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 19 | 20 | -- ---------------------------- 21 | -- Table structure for `user_login_log_1` 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `user_login_log_1`; 24 | CREATE TABLE `user_login_log_1` ( 25 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 26 | `user_id` bigint(20) NOT NULL, 27 | `login_time` datetime NOT NULL, 28 | `created` datetime NOT NULL, 29 | PRIMARY KEY (`id`), 30 | KEY `IDX_USER_ID_LOGIN_TIME` (`user_id`,`login_time`) 31 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /springboot-db-controller/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-db-controller 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-dao 17 | 18 | 19 | com.doodl6 20 | springboot-common-web 21 | 22 | 23 | -------------------------------------------------------------------------------- /springboot-db-controller/src/main/java/com/doodl6/springboot/db/controller/DbControllerConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.db.controller; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.transaction.annotation.EnableTransactionManagement; 5 | 6 | @Configuration 7 | @EnableTransactionManagement 8 | public class DbControllerConfig { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-db-controller/src/main/java/com/doodl6/springboot/db/controller/DruidViewConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.db.controller; 2 | 3 | import com.alibaba.druid.support.jakarta.StatViewServlet; 4 | import com.alibaba.druid.support.jakarta.WebStatFilter; 5 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 6 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class DruidViewConfig { 12 | 13 | /** 14 | * 可以通过访问/druid/index.html访问Druid监控页面 15 | */ 16 | @Bean 17 | public ServletRegistrationBean druidStatViewServlet() { 18 | //org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册. 19 | ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); 20 | 21 | //添加初始化参数:initParams 22 | //白名单: 23 | servletRegistrationBean.addInitParameter("allow", "127.0.0.1"); 24 | 25 | //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page. 26 | servletRegistrationBean.addInitParameter("deny", "192.168.1.73"); 27 | 28 | //登录查看信息的账号密码. 29 | servletRegistrationBean.addInitParameter("loginUsername", "admin"); 30 | servletRegistrationBean.addInitParameter("loginPassword", "123456"); 31 | 32 | //是否能够重置数据. 33 | servletRegistrationBean.addInitParameter("resetEnable", "false"); 34 | 35 | return servletRegistrationBean; 36 | } 37 | 38 | @Bean 39 | public FilterRegistrationBean druidWebStatFilter() { 40 | FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter()); 41 | 42 | //添加过滤规则. 43 | filterRegistrationBean.addUrlPatterns("/*"); 44 | //添加不需要忽略的格式信息. 45 | filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*"); 46 | 47 | return filterRegistrationBean; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /springboot-db-controller/src/main/java/com/doodl6/springboot/db/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.db.controller; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.common.web.response.MapResponse; 5 | import com.doodl6.springboot.dao.entity.User; 6 | import jakarta.annotation.Resource; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | @RestController 16 | @RequestMapping("/user") 17 | public class UserController { 18 | 19 | @Resource 20 | private UserService userService; 21 | 22 | /** 23 | * 新增用户 24 | */ 25 | @PostMapping("/addUser") 26 | public MapResponse addUser(String name) { 27 | Assert.isTrue(StringUtils.isNotBlank(name), "用户名称不能为空"); 28 | 29 | User user = userService.insertUser(name); 30 | MapResponse response = new MapResponse(); 31 | response.appendData("user", user); 32 | 33 | return response; 34 | } 35 | 36 | /** 37 | * 批量新增用户 38 | */ 39 | @PostMapping("/batchAddUser") 40 | public MapResponse batchAddUser(String names) { 41 | Assert.isTrue(StringUtils.isNotBlank(names), "用户名称不能为空"); 42 | 43 | List nameList = Arrays.asList(names.split(",")); 44 | List users = userService.batchAddUser(nameList); 45 | MapResponse response = new MapResponse(); 46 | response.appendData("users", users); 47 | 48 | return response; 49 | } 50 | 51 | /** 52 | * 用户登录 53 | */ 54 | @PostMapping("/login") 55 | public MapResponse addUser(Long userId) { 56 | Assert.notNull(userId, "用户ID不能为空"); 57 | 58 | User user = userService.userLogin(userId); 59 | 60 | MapResponse response = new MapResponse(); 61 | response.appendData("user", user); 62 | 63 | return response; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /springboot-db-controller/src/main/java/com/doodl6/springboot/db/controller/UserService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.db.controller; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.dao.entity.User; 5 | import com.doodl6.springboot.dao.entity.UserLoginLog; 6 | import com.doodl6.springboot.dao.manager.UserLoginLogManager; 7 | import com.doodl6.springboot.dao.manager.UserManager; 8 | import com.google.common.collect.Lists; 9 | import jakarta.annotation.Resource; 10 | import org.springframework.dao.DuplicateKeyException; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.Date; 15 | import java.util.List; 16 | 17 | @Service 18 | public class UserService { 19 | 20 | @Resource 21 | private UserManager userManager; 22 | 23 | @Resource 24 | private UserLoginLogManager userLoginLogManager; 25 | 26 | public User insertUser(String name) { 27 | User user = new User(); 28 | user.setName(name); 29 | try { 30 | userManager.save(user); 31 | } catch (DuplicateKeyException e) { 32 | throw new IllegalArgumentException("该用户已存在"); 33 | } 34 | return user; 35 | } 36 | 37 | /** 38 | * 批量新增用户 39 | */ 40 | @Transactional(rollbackFor = Exception.class) 41 | public List batchAddUser(List nameList) { 42 | List userList = Lists.newArrayListWithCapacity(nameList.size()); 43 | for (String name : nameList) { 44 | User user = new User(); 45 | user.setName(name); 46 | try { 47 | userManager.save(user); 48 | } catch (DuplicateKeyException ignore) { 49 | //忽略重复的用户 50 | user = userManager.queryByName(name); 51 | } 52 | userList.add(user); 53 | } 54 | 55 | return userList; 56 | } 57 | 58 | public User userLogin(long userId) { 59 | User user = userManager.getById(userId); 60 | Assert.notNull(user, "用户不存在"); 61 | 62 | UserLoginLog userLoginLog = new UserLoginLog(); 63 | userLoginLog.setUserId(userId); 64 | userLoginLog.setLoginTime(new Date()); 65 | userLoginLogManager.save(userLoginLog); 66 | 67 | return user; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /springboot-dubbo-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-dubbo-api 12 | 13 | 14 | 8 15 | 8 16 | 17 | 18 | 19 | 20 | javax.ws.rs 21 | javax.ws.rs-api 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /springboot-dubbo-api/src/main/java/com/doodl6/springboot/dubbo/api/FirstDubboService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.api; 2 | 3 | 4 | import com.doodl6.springboot.dubbo.api.request.GetDubboInfoRequest; 5 | import com.doodl6.springboot.dubbo.api.response.GetDubboInfoResponse; 6 | 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.POST; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.Produces; 11 | import javax.ws.rs.core.MediaType; 12 | 13 | 14 | @Path("dubbo-rest") 15 | @Consumes(MediaType.APPLICATION_JSON) 16 | @Produces(MediaType.APPLICATION_JSON + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8") 17 | public interface FirstDubboService { 18 | 19 | @POST 20 | @Path("getDubboInfo") 21 | GetDubboInfoResponse getDubboInfo(GetDubboInfoRequest getDubboInfoRequest); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /springboot-dubbo-api/src/main/java/com/doodl6/springboot/dubbo/api/domain/DubboDomain.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.api.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.io.Serializable; 7 | 8 | @Getter 9 | @Setter 10 | public class DubboDomain implements Serializable { 11 | 12 | private Long id; 13 | 14 | private String name; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springboot-dubbo-api/src/main/java/com/doodl6/springboot/dubbo/api/request/BaseRequest.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.api.request; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.io.Serializable; 7 | 8 | @Getter 9 | @Setter 10 | public class BaseRequest implements Serializable { 11 | 12 | /** 13 | * 追踪ID 14 | */ 15 | private String traceId; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /springboot-dubbo-api/src/main/java/com/doodl6/springboot/dubbo/api/request/GetDubboInfoRequest.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.api.request; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class GetDubboInfoRequest extends BaseRequest { 9 | 10 | private Long id; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /springboot-dubbo-api/src/main/java/com/doodl6/springboot/dubbo/api/response/BaseResponse.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.api.response; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.io.Serializable; 7 | 8 | @Getter 9 | @Setter 10 | public class BaseResponse implements Serializable { 11 | 12 | private boolean success; 13 | 14 | private String errorMsg; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springboot-dubbo-api/src/main/java/com/doodl6/springboot/dubbo/api/response/GetDubboInfoResponse.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.api.response; 2 | 3 | 4 | import com.doodl6.springboot.dubbo.api.domain.DubboDomain; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | public class GetDubboInfoResponse extends BaseResponse { 11 | 12 | private DubboDomain dubboDomain; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /springboot-dubbo-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-dubbo-consumer 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | com.doodl6 21 | springboot-dubbo-api 22 | 23 | 24 | 25 | org.apache.dubbo 26 | dubbo 27 | 28 | 29 | org.apache.dubbo 30 | dubbo-spring-boot-starter 31 | 32 | 33 | 34 | org.apache.curator 35 | curator-x-discovery 36 | 37 | 38 | 39 | org.springframework.cloud 40 | spring-cloud-starter-netflix-hystrix 41 | 42 | 43 | 44 | com.alibaba.csp 45 | sentinel-core 46 | 47 | 48 | com.alibaba.csp 49 | sentinel-annotation-aspectj 50 | 51 | 52 | 53 | com.squareup.okhttp3 54 | okhttp 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-compiler-plugin 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /springboot-dubbo-consumer/src/main/java/com/doodl6/springboot/dubbo/consumer/DubboController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.consumer; 2 | 3 | import com.doodl6.springboot.common.web.response.MapResponse; 4 | import com.doodl6.springboot.dubbo.api.domain.DubboDomain; 5 | import com.doodl6.springboot.dubbo.consumer.service.DubboService; 6 | import jakarta.annotation.Resource; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.util.Assert; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | /** 14 | * dubbo控制类 15 | */ 16 | @Slf4j 17 | @RestController 18 | @RequestMapping("/dubbo") 19 | public class DubboController { 20 | 21 | @Resource 22 | private DubboService dubboService; 23 | 24 | /** 25 | * 获取dubbo信息 26 | */ 27 | @GetMapping("/getDubboInfoWithSentinel") 28 | public MapResponse getDubboInfoWithSentinel(Long id) { 29 | Assert.notNull(id, "id不能为空"); 30 | MapResponse mapResponse = new MapResponse(); 31 | 32 | DubboDomain dubboDomain = dubboService.getDubboInfoWithSentinel(id); 33 | mapResponse.appendData("dubboInfo", dubboDomain); 34 | 35 | return mapResponse; 36 | } 37 | 38 | /** 39 | * 获取dubbo信息 40 | */ 41 | @GetMapping("/getDubboInfoWithHystrix") 42 | public MapResponse getDubboInfoWithHystrix(Long id) { 43 | Assert.notNull(id, "id不能为空"); 44 | MapResponse mapResponse = new MapResponse(); 45 | 46 | DubboDomain dubboDomain = dubboService.getDubboInfoWithHystrix(id); 47 | mapResponse.appendData("dubboInfo", dubboDomain); 48 | 49 | return mapResponse; 50 | } 51 | 52 | /** 53 | * 通过rest协议方调用dubbo信息 54 | */ 55 | @GetMapping("/getDubboInfoByRest") 56 | public MapResponse getDubboInfoByRest(Long id) { 57 | Assert.notNull(id, "id不能为空"); 58 | MapResponse mapResponse = new MapResponse(); 59 | 60 | DubboDomain dubboDomain = dubboService.getDubboInfoByRest(id); 61 | mapResponse.appendData("dubboInfo", dubboDomain); 62 | 63 | return mapResponse; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /springboot-dubbo-consumer/src/main/java/com/doodl6/springboot/dubbo/consumer/config/DubboConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.consumer.config; 2 | 3 | import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; 4 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | @EnableHystrix 9 | @EnableDubbo(scanBasePackages = "com.doodl6.springboot.dubbo") 10 | public class DubboConfig { 11 | } 12 | -------------------------------------------------------------------------------- /springboot-dubbo-consumer/src/main/java/com/doodl6/springboot/dubbo/consumer/config/SentinelAspectConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.consumer.config; 2 | 3 | import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; 4 | import com.alibaba.csp.sentinel.slots.block.RuleConstant; 5 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; 6 | import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; 7 | import com.google.common.collect.Lists; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.util.List; 12 | 13 | @Configuration 14 | public class SentinelAspectConfiguration { 15 | 16 | static { 17 | initFlowRules(); 18 | } 19 | 20 | @Bean 21 | public SentinelResourceAspect sentinelResourceAspect() { 22 | return new SentinelResourceAspect(); 23 | } 24 | 25 | /** 26 | * 初始化限流规则,实际使用的时候可以使用动态配置,比如基于nacos 27 | */ 28 | private static void initFlowRules() { 29 | List rules = Lists.newLinkedList(); 30 | FlowRule rule = new FlowRule(); 31 | rule.setResource("getDubboInfo"); 32 | rule.setGrade(RuleConstant.FLOW_GRADE_QPS); 33 | // 设置QPS为1 34 | rule.setCount(1); 35 | rules.add(rule); 36 | FlowRuleManager.loadRules(rules); 37 | } 38 | } -------------------------------------------------------------------------------- /springboot-dubbo-consumer/src/main/java/com/doodl6/springboot/dubbo/consumer/filter/DubboTraceFilter.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.consumer.filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.dubbo.common.constants.CommonConstants; 5 | import org.apache.dubbo.common.extension.Activate; 6 | import org.apache.dubbo.rpc.Filter; 7 | import org.apache.dubbo.rpc.Invocation; 8 | import org.apache.dubbo.rpc.Invoker; 9 | import org.apache.dubbo.rpc.Result; 10 | import org.apache.dubbo.rpc.RpcException; 11 | 12 | @Slf4j 13 | @Activate(group = CommonConstants.CONSUMER) 14 | public class DubboTraceFilter implements Filter { 15 | 16 | @Override 17 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 18 | try { 19 | long start = System.currentTimeMillis(); 20 | Result result = invoker.invoke(invocation); 21 | long took = System.currentTimeMillis() - start; 22 | 23 | recordRpcLog(took, invocation); 24 | return result; 25 | } catch (RpcException e) { 26 | log.error("dubbo消费者rpc异常:", e); 27 | throw e; 28 | } 29 | } 30 | 31 | private static void recordRpcLog(long took, Invocation invocation) { 32 | String serviceAndMethod = invocation.getAttachment("interface") + "." + invocation.getMethodName(); 33 | log.info("dubbo调用 | {} | {}ms", serviceAndMethod, took); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /springboot-dubbo-consumer/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | dubboTrace=com.doodl6.springboot.dubbo.consumer.filter.DubboTraceFilter -------------------------------------------------------------------------------- /springboot-dubbo-provider/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-dubbo-provider 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-dubbo-api 17 | 18 | 19 | 20 | org.apache.dubbo 21 | dubbo-spring-boot-observability-starter 22 | 23 | 24 | 25 | com.doodl6 26 | springboot-common 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter 32 | 33 | 34 | 35 | org.apache.dubbo 36 | dubbo 37 | 38 | 39 | org.apache.dubbo 40 | dubbo-spring-boot-starter 41 | 42 | 43 | 44 | org.apache.curator 45 | curator-x-discovery 46 | 47 | 48 | 49 | javax.validation 50 | validation-api 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-maven-plugin 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /springboot-dubbo-provider/src/main/java/com/doodl6/springboot/dubbo/provider/DubboApplication.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.provider; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @EnableAutoConfiguration 8 | @ComponentScan("com.doodl6.springboot.dubbo.provider.config") 9 | public class DubboApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(DubboApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /springboot-dubbo-provider/src/main/java/com/doodl6/springboot/dubbo/provider/config/DubboConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.provider.config; 2 | 3 | import org.apache.dubbo.config.ProtocolConfig; 4 | import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @EnableDubbo(scanBasePackages = "com.doodl6.springboot.dubbo.provider.service") 11 | public class DubboConfig { 12 | 13 | @Value("${dubbo.protocol.rest.port}") 14 | private int restPort; 15 | 16 | @Value("${dubbo.protocol.dubbo.port}") 17 | private int dubboPort; 18 | 19 | @Bean 20 | public ProtocolConfig restProtocolConfig() { 21 | ProtocolConfig protocolConfig = new ProtocolConfig(); 22 | protocolConfig.setName("rest"); 23 | protocolConfig.setPort(restPort); 24 | protocolConfig.setServer("netty"); 25 | return protocolConfig; 26 | } 27 | 28 | @Bean 29 | public ProtocolConfig dubboProtocolConfig() { 30 | ProtocolConfig protocolConfig = new ProtocolConfig(); 31 | protocolConfig.setName("dubbo"); 32 | protocolConfig.setPort(dubboPort); 33 | protocolConfig.setServer("netty"); 34 | return protocolConfig; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /springboot-dubbo-provider/src/main/java/com/doodl6/springboot/dubbo/provider/service/FirstDubboServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.dubbo.provider.service; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.alibaba.fastjson2.JSON; 5 | import com.doodl6.springboot.dubbo.api.FirstDubboService; 6 | import com.doodl6.springboot.dubbo.api.domain.DubboDomain; 7 | import com.doodl6.springboot.dubbo.api.request.GetDubboInfoRequest; 8 | import com.doodl6.springboot.dubbo.api.response.GetDubboInfoResponse; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.dubbo.config.annotation.DubboService; 11 | 12 | 13 | @Slf4j 14 | @DubboService(version = "${dubbo.service.firstDubbo.version}", timeout = 10000, protocol = {"dubbo", "rest"}) 15 | public class FirstDubboServiceImpl implements FirstDubboService { 16 | 17 | @Override 18 | public GetDubboInfoResponse getDubboInfo(GetDubboInfoRequest request) { 19 | log.info("收到Dubbo请求 | {}", JSON.toJSONString(request)); 20 | GetDubboInfoResponse response = new GetDubboInfoResponse(); 21 | try { 22 | //模拟服务响应慢 23 | Thread.sleep(100); 24 | Assert.notNull(request, "参数为空"); 25 | Long id = request.getId(); 26 | Assert.notNull(id, "id为空"); 27 | 28 | DubboDomain dubboDomain = new DubboDomain(); 29 | dubboDomain.setId(id); 30 | dubboDomain.setName("dubbo" + id); 31 | response.setDubboDomain(dubboDomain); 32 | response.setSuccess(true); 33 | } catch (IllegalArgumentException e) { 34 | response.setErrorMsg(e.getMessage()); 35 | } catch (Exception e) { 36 | log.error("获取dubbo信息出现异常", e); 37 | response.setErrorMsg("未知异常"); 38 | } 39 | 40 | return response; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /springboot-dubbo-provider/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 2020 3 | dubbo: 4 | application: 5 | name: springboot-dubbo-service-local 6 | registry: 7 | address: zookeeper://127.0.0.1:2181 8 | service: 9 | firstDubbo: 10 | version: 1.0-local -------------------------------------------------------------------------------- /springboot-dubbo-provider/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: springboot-dubbo-provider 4 | profiles: 5 | active: local 6 | dubbo: 7 | application: 8 | qos-port: 20990 9 | metrics: 10 | protocol: prometheus 11 | protocol: 12 | rest: 13 | port: 8080 14 | dubbo: 15 | port: 20880 -------------------------------------------------------------------------------- /springboot-elasticsearch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-elasticsearch 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-data-elasticsearch 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /springboot-elasticsearch/src/main/java/com/doodl6/springboot/elasticsearch/ElasticsearchAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.elasticsearch; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; 5 | 6 | @Configuration 7 | @EnableElasticsearchRepositories(basePackages = "com.doodl6.springboot.elasticsearch.repository") 8 | public class ElasticsearchAutoConfig { 9 | } 10 | -------------------------------------------------------------------------------- /springboot-elasticsearch/src/main/java/com/doodl6/springboot/elasticsearch/ElasticsearchController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.elasticsearch; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.common.web.response.BaseResponse; 5 | import com.doodl6.springboot.elasticsearch.repository.ArticleRepository; 6 | import com.doodl6.springboot.elasticsearch.vo.Article; 7 | import com.google.common.collect.Lists; 8 | import jakarta.annotation.Resource; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import java.util.Date; 16 | import java.util.List; 17 | 18 | /** 19 | * elasticsearch控制类 20 | */ 21 | @RestController 22 | @RequestMapping("/elasticsearch") 23 | public class ElasticsearchController { 24 | 25 | @Resource 26 | private ArticleRepository articleRepository; 27 | 28 | /** 29 | * 添加数据 30 | */ 31 | @PostMapping("/addData") 32 | public BaseResponse addData(Long id, String title, String category, String content) { 33 | Assert.notNull(id, "ID不能为空"); 34 | Assert.isTrue(StringUtils.isNotBlank(title), "标题不能为空"); 35 | Assert.isTrue(StringUtils.isNotBlank(category), "类目不能为空"); 36 | Assert.isTrue(StringUtils.isNotBlank(content), "内容不能为空"); 37 | 38 | Article article = new Article(); 39 | article.setId(id); 40 | article.setTitle(title); 41 | article.setCategory(category); 42 | article.setContent(content); 43 | article.setPublishTime(new Date()); 44 | 45 | articleRepository.save(article); 46 | 47 | return new BaseResponse<>(); 48 | } 49 | 50 | /** 51 | * 修改数据 52 | */ 53 | @PostMapping("/updateData") 54 | public BaseResponse updateData(Long id, String title, String category, String content) { 55 | Assert.notNull(id, "ID不能为空"); 56 | Assert.isTrue(StringUtils.isNotBlank(title), "标题不能为空"); 57 | Assert.isTrue(StringUtils.isNotBlank(category), "类目不能为空"); 58 | Assert.isTrue(StringUtils.isNotBlank(content), "内容不能为空"); 59 | 60 | Article article = new Article(); 61 | article.setId(id); 62 | article.setTitle(title); 63 | article.setCategory(category); 64 | article.setContent(content); 65 | article.setPublishTime(new Date()); 66 | 67 | articleRepository.save(article); 68 | 69 | return new BaseResponse<>(); 70 | } 71 | 72 | /** 73 | * 删除数据 74 | */ 75 | @PostMapping("/deleteData") 76 | public BaseResponse deleteData(Long id) { 77 | Assert.notNull(id, "ID不能为空"); 78 | 79 | articleRepository.deleteById(id); 80 | 81 | return new BaseResponse<>(); 82 | } 83 | 84 | /** 85 | * 查询数据 86 | */ 87 | @GetMapping("/queryData") 88 | public BaseResponse> queryData(String title) { 89 | Assert.isTrue(StringUtils.isNotBlank(title), "标题不能为空"); 90 | 91 | Iterable
iterable = articleRepository.findByTitle(title); 92 | 93 | return new BaseResponse<>(Lists.newArrayList(iterable)); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /springboot-elasticsearch/src/main/java/com/doodl6/springboot/elasticsearch/repository/ArticleRepository.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.elasticsearch.repository; 2 | 3 | import com.doodl6.springboot.elasticsearch.vo.Article; 4 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 5 | 6 | public interface ArticleRepository extends ElasticsearchRepository { 7 | 8 | Iterable
findByTitle(String title); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /springboot-elasticsearch/src/main/java/com/doodl6/springboot/elasticsearch/vo/Article.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.elasticsearch.vo; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.elasticsearch.annotations.Document; 7 | 8 | import java.util.Date; 9 | 10 | @Getter 11 | @Setter 12 | @Document(indexName = "blog") 13 | public class Article { 14 | 15 | @Id 16 | private Long id; 17 | 18 | private String title; 19 | 20 | private String category; 21 | 22 | private String content; 23 | 24 | private Date publishTime; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /springboot-feign-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-feign-consumer 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | org.springframework.cloud 21 | spring-cloud-starter-openfeign 22 | 23 | 24 | org.springframework.cloud 25 | spring-cloud-starter-consul-discovery 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /springboot-feign-consumer/src/main/java/com/doodl6/springboot/feign/consumer/FeignAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.feign.consumer; 2 | 3 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 4 | import org.springframework.cloud.openfeign.EnableFeignClients; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | @EnableDiscoveryClient 9 | @EnableFeignClients(basePackages = "com.doodl6.springboot.feign.consumer.service") 10 | public class FeignAutoConfig { 11 | } 12 | -------------------------------------------------------------------------------- /springboot-feign-consumer/src/main/java/com/doodl6/springboot/feign/consumer/FeignController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.feign.consumer; 2 | 3 | import com.doodl6.springboot.feign.consumer.service.FeignService; 4 | import jakarta.annotation.Resource; 5 | import org.springframework.cloud.client.discovery.DiscoveryClient; 6 | import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | @RestController 14 | @RequestMapping("/feign") 15 | public class FeignController { 16 | 17 | @Resource 18 | private DiscoveryClient discoveryClient; 19 | 20 | @Resource 21 | private LoadBalancerClient loadBalancerClient; 22 | 23 | @Resource 24 | private FeignService feignService; 25 | 26 | @RequestMapping(value = "/testEcho/{string}", method = RequestMethod.GET) 27 | public String testEcho(@PathVariable String string) { 28 | return feignService.echo(string); 29 | } 30 | 31 | /** 32 | * 获取所有服务提供者节点信息 33 | */ 34 | @GetMapping(value = "/getServices") 35 | public Object getServices() { 36 | return discoveryClient.getInstances("springboot-feign-provider"); 37 | } 38 | 39 | /** 40 | * 轮训获取服务提供者节点 41 | */ 42 | @GetMapping(value = "/chooseService") 43 | public Object chooseService() { 44 | return loadBalancerClient.choose("springboot-feign-provider").getUri().toString(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /springboot-feign-consumer/src/main/java/com/doodl6/springboot/feign/consumer/service/FeignService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.feign.consumer.service; 2 | 3 | import org.springframework.cloud.openfeign.FeignClient; 4 | import org.springframework.web.bind.annotation.PathVariable; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | 8 | @FeignClient(name = "springboot-feign-provider") 9 | public interface FeignService { 10 | 11 | @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET) 12 | String echo(@PathVariable("str") String str); 13 | } 14 | -------------------------------------------------------------------------------- /springboot-feign-provider/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-feign-provider 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-web 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-actuator 22 | 23 | 24 | 25 | org.springframework.cloud 26 | spring-cloud-starter-consul-discovery 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-maven-plugin 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /springboot-feign-provider/src/main/java/com/doodl6/springboot/feign/provider/FeignApplication.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.feign.provider; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @SpringBootApplication(scanBasePackages = "com.doodl6") 8 | @EnableDiscoveryClient 9 | public class FeignApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(FeignApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /springboot-feign-provider/src/main/java/com/doodl6/springboot/feign/provider/FeignServiceController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.feign.provider; 2 | 3 | import org.springframework.web.bind.annotation.PathVariable; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RequestMethod; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class FeignServiceController { 10 | 11 | @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET) 12 | public String echo(@PathVariable String string) { 13 | return string; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /springboot-feign-provider/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 2021 3 | spring: 4 | application: 5 | name: springboot-feign-provider 6 | cloud: 7 | consul: 8 | host: 127.0.0.1 9 | port: 8500 10 | discovery: 11 | instanceId: ${spring.application.name}:${random.value} 12 | healthCheckInterval: 15s -------------------------------------------------------------------------------- /springboot-leaf/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-leaf 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | com.mysql 21 | mysql-connector-j 22 | 23 | 24 | 25 | org.mybatis 26 | mybatis 27 | 28 | 29 | com.baomidou 30 | mybatis-plus-boot-starter 31 | 32 | 33 | 34 | com.alibaba 35 | druid-spring-boot-3-starter 36 | 37 | 38 | 39 | org.perf4j 40 | perf4j 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/Constants.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 7 | public class Constants { 8 | 9 | public static final String LEAF_SEGMENT_ENABLE = "leaf.segment.enable"; 10 | public static final String LEAF_JDBC_URL = "leaf.jdbc.url"; 11 | public static final String LEAF_JDBC_USERNAME = "leaf.jdbc.username"; 12 | public static final String LEAF_JDBC_PASSWORD = "leaf.jdbc.password"; 13 | 14 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/IDGen.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf; 2 | 3 | 4 | import com.doodl6.springboot.leaf.common.Result; 5 | 6 | public interface IDGen { 7 | 8 | Result get(String key); 9 | 10 | boolean init(); 11 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/aspect/RtAspect.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.aspect; 2 | 3 | import cn.hutool.core.date.TimeInterval; 4 | import com.doodl6.springboot.common.web.context.RequestParamContext; 5 | import com.doodl6.springboot.common.web.context.TraceIdHolder; 6 | import com.doodl6.springboot.leaf.common.Result; 7 | import com.doodl6.springboot.leaf.common.Status; 8 | import com.doodl6.springboot.leaf.segment.SegmentService; 9 | import jakarta.annotation.Resource; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.aspectj.lang.ProceedingJoinPoint; 13 | import org.aspectj.lang.annotation.Around; 14 | import org.aspectj.lang.annotation.Aspect; 15 | import org.aspectj.lang.annotation.Pointcut; 16 | import org.springframework.stereotype.Component; 17 | 18 | /** 19 | * 接口RT切面类 20 | */ 21 | @Slf4j(topic = "rtLogger") 22 | @Aspect 23 | @Component 24 | public class RtAspect { 25 | 26 | private static final String TRACE_ID_KEY = "traceId"; 27 | 28 | @Resource 29 | private SegmentService segmentService; 30 | 31 | /** 32 | * 对添加了RequestMapping,GetMapping和PostMapping注解的方法添加切入点 33 | */ 34 | @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) ||@annotation(org.springframework.web.bind.annotation.PostMapping)") 35 | public void api() { 36 | } 37 | 38 | /** 39 | * 记录接口耗时日志 40 | */ 41 | @Around("api()") 42 | public Object recordRt(ProceedingJoinPoint pjp) throws Throwable { 43 | TimeInterval ti = new TimeInterval(); 44 | Object obj; 45 | String traceId = null; 46 | try { 47 | traceId = getTraceId(); 48 | TraceIdHolder.setTraceId(traceId); 49 | obj = pjp.proceed(); 50 | } finally { 51 | log.info("traceId:{} | {} | {} | {} | {}ms", traceId, pjp.getSignature().getDeclaringType().getSimpleName(), pjp.getSignature().getName(), RequestParamContext.get(), ti.interval()); 52 | TraceIdHolder.removeTraceId(); 53 | } 54 | 55 | return obj; 56 | } 57 | 58 | private String getTraceId() { 59 | Result result = segmentService.getId(TRACE_ID_KEY); 60 | if (result.getStatus() == Status.SUCCESS) { 61 | return String.valueOf(result.getId()); 62 | } 63 | 64 | return StringUtils.EMPTY; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/common/PropertyFactory.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.common; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.IOException; 7 | import java.util.Properties; 8 | 9 | public class PropertyFactory { 10 | 11 | private static final Logger logger = LoggerFactory.getLogger(PropertyFactory.class); 12 | 13 | private static final Properties prop = new Properties(); 14 | 15 | static { 16 | try { 17 | prop.load(PropertyFactory.class.getClassLoader().getResourceAsStream("leaf.properties")); 18 | } catch (IOException e) { 19 | logger.warn("Load Properties Ex", e); 20 | } 21 | } 22 | 23 | public static Properties getProperties() { 24 | return prop; 25 | } 26 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/common/Result.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.common; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class Result { 13 | 14 | private long id; 15 | 16 | private Status status; 17 | 18 | @Override 19 | public String toString() { 20 | return "Result{" + "id=" + id + 21 | ", status=" + status + 22 | '}'; 23 | } 24 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/common/Status.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.common; 2 | 3 | public enum Status { 4 | SUCCESS, 5 | EXCEPTION 6 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/common/ZeroIDGen.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.common; 2 | 3 | import com.doodl6.springboot.leaf.IDGen; 4 | 5 | public class ZeroIDGen implements IDGen { 6 | 7 | @Override 8 | public Result get(String key) { 9 | return new Result(0, Status.SUCCESS); 10 | } 11 | 12 | @Override 13 | public boolean init() { 14 | return true; 15 | } 16 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/exception/InitException.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.exception; 2 | 3 | public class InitException extends Exception { 4 | 5 | public InitException(String msg) { 6 | super(msg); 7 | } 8 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/SegmentService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import com.doodl6.springboot.leaf.Constants; 5 | import com.doodl6.springboot.leaf.IDGen; 6 | import com.doodl6.springboot.leaf.common.PropertyFactory; 7 | import com.doodl6.springboot.leaf.common.Result; 8 | import com.doodl6.springboot.leaf.common.ZeroIDGen; 9 | import com.doodl6.springboot.leaf.exception.InitException; 10 | import com.doodl6.springboot.leaf.segment.dao.IDAllocDao; 11 | import com.doodl6.springboot.leaf.segment.dao.impl.IDAllocDaoImpl; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.stereotype.Service; 14 | 15 | import java.sql.SQLException; 16 | import java.util.Properties; 17 | 18 | /** 19 | * 段模式获取ID 20 | */ 21 | @Slf4j 22 | @Service 23 | public class SegmentService { 24 | 25 | private final IDGen idGen; 26 | 27 | public SegmentService() throws SQLException, InitException { 28 | Properties properties = PropertyFactory.getProperties(); 29 | boolean flag = Boolean.parseBoolean(properties.getProperty(Constants.LEAF_SEGMENT_ENABLE, "true")); 30 | if (flag) { 31 | // Config dataSource 32 | DruidDataSource dataSource = new DruidDataSource(); 33 | dataSource.setUrl(properties.getProperty(Constants.LEAF_JDBC_URL)); 34 | dataSource.setUsername(properties.getProperty(Constants.LEAF_JDBC_USERNAME)); 35 | dataSource.setPassword(properties.getProperty(Constants.LEAF_JDBC_PASSWORD)); 36 | dataSource.init(); 37 | 38 | // Config Dao 39 | IDAllocDao dao = new IDAllocDaoImpl(dataSource); 40 | 41 | // Config ID Gen 42 | idGen = new SegmentIDGenImpl(); 43 | ((SegmentIDGenImpl) idGen).setDao(dao); 44 | if (idGen.init()) { 45 | log.info("Segment Service Init Successfully"); 46 | } else { 47 | throw new InitException("Segment Service Init Fail"); 48 | } 49 | } else { 50 | idGen = new ZeroIDGen(); 51 | log.info("Zero ID Gen Service Init Successfully"); 52 | } 53 | } 54 | 55 | public Result getId(String key) { 56 | return idGen.get(key); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/dao/IDAllocDao.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment.dao; 2 | 3 | 4 | import com.doodl6.springboot.leaf.segment.model.LeafAlloc; 5 | 6 | import java.util.List; 7 | 8 | public interface IDAllocDao { 9 | 10 | List getAllLeafAllocs(); 11 | 12 | LeafAlloc updateMaxIdAndGetLeafAlloc(String tag); 13 | 14 | LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc); 15 | 16 | List getAllTags(); 17 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/dao/IDAllocMapper.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment.dao; 2 | 3 | import com.doodl6.springboot.leaf.segment.model.LeafAlloc; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.util.List; 7 | 8 | public interface IDAllocMapper { 9 | 10 | @Select("SELECT biz_tag, max_id, step, update_time FROM leaf_alloc") 11 | @Results(value = { 12 | @Result(column = "biz_tag", property = "key"), 13 | @Result(column = "max_id", property = "maxId"), 14 | @Result(column = "step", property = "step"), 15 | @Result(column = "update_time", property = "updateTime") 16 | }) 17 | List getAllLeafAllocs(); 18 | 19 | @Select("SELECT biz_tag, max_id, step FROM leaf_alloc WHERE biz_tag = #{tag}") 20 | @Results(value = { 21 | @Result(column = "biz_tag", property = "key"), 22 | @Result(column = "max_id", property = "maxId"), 23 | @Result(column = "step", property = "step") 24 | }) 25 | LeafAlloc getLeafAlloc(@Param("tag") String tag); 26 | 27 | @Update("UPDATE leaf_alloc SET max_id = max_id + step WHERE biz_tag = #{tag}") 28 | void updateMaxId(@Param("tag") String tag); 29 | 30 | @Update("UPDATE leaf_alloc SET max_id = max_id + #{step} WHERE biz_tag = #{key}") 31 | void updateMaxIdByCustomStep(@Param("leafAlloc") LeafAlloc leafAlloc); 32 | 33 | @Select("SELECT biz_tag FROM leaf_alloc") 34 | List getAllTags(); 35 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/dao/impl/IDAllocDaoImpl.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment.dao.impl; 2 | 3 | import com.doodl6.springboot.leaf.segment.dao.IDAllocDao; 4 | import com.doodl6.springboot.leaf.segment.dao.IDAllocMapper; 5 | import com.doodl6.springboot.leaf.segment.model.LeafAlloc; 6 | import org.apache.ibatis.mapping.Environment; 7 | import org.apache.ibatis.session.Configuration; 8 | import org.apache.ibatis.session.SqlSession; 9 | import org.apache.ibatis.session.SqlSessionFactory; 10 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 11 | import org.apache.ibatis.transaction.TransactionFactory; 12 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 13 | 14 | import javax.sql.DataSource; 15 | import java.util.List; 16 | 17 | public class IDAllocDaoImpl implements IDAllocDao { 18 | 19 | private final SqlSessionFactory sqlSessionFactory; 20 | 21 | private final String idAllocMapperName; 22 | 23 | public IDAllocDaoImpl(DataSource dataSource) { 24 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 25 | Environment environment = new Environment("development", transactionFactory, dataSource); 26 | Configuration configuration = new Configuration(environment); 27 | configuration.addMapper(IDAllocMapper.class); 28 | idAllocMapperName = IDAllocMapper.class.getName(); 29 | sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 30 | } 31 | 32 | @Override 33 | public List getAllLeafAllocs() { 34 | try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { 35 | return sqlSession.selectList(idAllocMapperName + ".getAllLeafAllocs"); 36 | } 37 | } 38 | 39 | @Override 40 | public LeafAlloc updateMaxIdAndGetLeafAlloc(String tag) { 41 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 42 | sqlSession.update(idAllocMapperName + ".updateMaxId", tag); 43 | LeafAlloc result = sqlSession.selectOne(idAllocMapperName + ".getLeafAlloc", tag); 44 | sqlSession.commit(); 45 | return result; 46 | } 47 | } 48 | 49 | @Override 50 | public LeafAlloc updateMaxIdByCustomStepAndGetLeafAlloc(LeafAlloc leafAlloc) { 51 | try (SqlSession sqlSession = sqlSessionFactory.openSession()) { 52 | sqlSession.update(idAllocMapperName + ".updateMaxIdByCustomStep", leafAlloc); 53 | LeafAlloc result = sqlSession.selectOne(idAllocMapperName + ".getLeafAlloc", leafAlloc.getKey()); 54 | sqlSession.commit(); 55 | return result; 56 | } 57 | } 58 | 59 | @Override 60 | public List getAllTags() { 61 | try (SqlSession sqlSession = sqlSessionFactory.openSession(false)) { 62 | return sqlSession.selectList(idAllocMapperName + ".getAllTags"); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/model/LeafAlloc.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class LeafAlloc { 9 | 10 | private String key; 11 | 12 | private long maxId; 13 | 14 | private int step; 15 | 16 | private String updateTime; 17 | 18 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/model/Segment.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | @Getter 9 | @Setter 10 | public class Segment { 11 | 12 | private AtomicLong value = new AtomicLong(0); 13 | 14 | private volatile long max; 15 | 16 | private volatile int step; 17 | 18 | private final SegmentBuffer buffer; 19 | 20 | public Segment(SegmentBuffer buffer) { 21 | this.buffer = buffer; 22 | } 23 | 24 | public long getIdle() { 25 | return this.getMax() - getValue().get(); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "Segment(" + "value:" + 31 | value + 32 | ",max:" + 33 | max + 34 | ",step:" + 35 | step + 36 | ")"; 37 | } 38 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/java/com/doodl6/springboot/leaf/segment/model/SegmentBuffer.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.leaf.segment.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.Arrays; 7 | import java.util.concurrent.atomic.AtomicBoolean; 8 | import java.util.concurrent.locks.Lock; 9 | import java.util.concurrent.locks.ReadWriteLock; 10 | import java.util.concurrent.locks.ReentrantReadWriteLock; 11 | 12 | /** 13 | * 双buffer 14 | */ 15 | @Getter 16 | @Setter 17 | public class SegmentBuffer { 18 | 19 | private String key; 20 | 21 | /** 22 | * 双buffer 23 | */ 24 | private Segment[] segments; 25 | 26 | /** 27 | * 当前的使用的segment的index 28 | */ 29 | private volatile int currentPos; 30 | 31 | /** 32 | * 下一个segment是否处于可切换状态 33 | */ 34 | private volatile boolean nextReady; 35 | 36 | /** 37 | * 是否初始化完成 38 | */ 39 | private volatile boolean initOk; 40 | 41 | /** 42 | * 线程是否在运行中 43 | */ 44 | private final AtomicBoolean threadRunning; 45 | 46 | private final ReadWriteLock lock; 47 | 48 | private volatile int step; 49 | 50 | private volatile int minStep; 51 | 52 | private volatile long updateTimestamp; 53 | 54 | public SegmentBuffer() { 55 | segments = new Segment[]{new Segment(this), new Segment(this)}; 56 | currentPos = 0; 57 | nextReady = false; 58 | initOk = false; 59 | threadRunning = new AtomicBoolean(false); 60 | lock = new ReentrantReadWriteLock(); 61 | } 62 | 63 | public Segment getCurrent() { 64 | return segments[currentPos]; 65 | } 66 | 67 | public int nextPos() { 68 | return (currentPos + 1) % 2; 69 | } 70 | 71 | public void switchPos() { 72 | currentPos = nextPos(); 73 | } 74 | 75 | public Lock rLock() { 76 | return lock.readLock(); 77 | } 78 | 79 | public Lock wLock() { 80 | return lock.writeLock(); 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return "SegmentBuffer{" + "key='" + key + '\'' + 86 | ", segments=" + Arrays.toString(segments) + 87 | ", currentPos=" + currentPos + 88 | ", nextReady=" + nextReady + 89 | ", initOk=" + initOk + 90 | ", threadRunning=" + threadRunning + 91 | ", step=" + step + 92 | ", minStep=" + minStep + 93 | ", updateTimestamp=" + updateTimestamp + 94 | '}'; 95 | } 96 | } -------------------------------------------------------------------------------- /springboot-leaf/src/main/resources/leaf.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE leaf; 2 | USE leaf; 3 | CREATE TABLE `leaf_alloc` ( 4 | `biz_tag` varchar(128) NOT NULL DEFAULT '', 5 | `max_id` bigint(20) NOT NULL DEFAULT '1', 6 | `step` int(11) NOT NULL, 7 | `description` varchar(256) DEFAULT NULL, 8 | `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 9 | PRIMARY KEY (`biz_tag`) 10 | ) ENGINE=InnoDB; 11 | 12 | insert into leaf_alloc(biz_tag, max_id, step, description) values('traceId', 1, 2000, 'trace id for every request'); 13 | -------------------------------------------------------------------------------- /springboot-netty/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-netty 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | io.netty 21 | netty-all 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /springboot-netty/src/main/java/com/doodl6/springboot/netty/config/ChatServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.netty.config; 2 | 3 | import com.doodl6.springboot.netty.server.ChatServer; 4 | import jakarta.annotation.PostConstruct; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * 聊天服务配置类 9 | */ 10 | @Configuration 11 | public class ChatServerConfig { 12 | 13 | @PostConstruct 14 | public void init() { 15 | //启动聊天socket服务 16 | new Thread(() -> { 17 | try { 18 | ChatServer.start(); 19 | } catch (InterruptedException e) { 20 | e.printStackTrace(); 21 | } 22 | }).start(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /springboot-netty/src/main/java/com/doodl6/springboot/netty/server/ChatServer.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.netty.server; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioServerSocketChannel; 9 | 10 | import java.net.InetSocketAddress; 11 | 12 | /** 13 | * 聊天服务 14 | */ 15 | public class ChatServer { 16 | 17 | public static void start() throws InterruptedException { 18 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 19 | EventLoopGroup workerGroup = new NioEventLoopGroup(2); 20 | try { 21 | ServerBootstrap b = new ServerBootstrap(); 22 | b.group(bossGroup, workerGroup) 23 | .channel(NioServerSocketChannel.class) 24 | .localAddress(new InetSocketAddress(8888)) 25 | .childHandler(new WebSocketServerInitializer()) 26 | .option(ChannelOption.SO_BACKLOG, 128) 27 | .childOption(ChannelOption.SO_KEEPALIVE, true); 28 | 29 | ChannelFuture f = b.bind().sync(); 30 | 31 | System.out.println("聊天socket服务启动完成"); 32 | f.channel().closeFuture().sync(); 33 | } finally { 34 | bossGroup.shutdownGracefully().syncUninterruptibly(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /springboot-netty/src/main/java/com/doodl6/springboot/netty/server/ChatServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.netty.server; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.alibaba.fastjson2.JSONObject; 5 | import com.google.common.collect.Maps; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.SimpleChannelInboundHandler; 8 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 9 | 10 | import java.util.Map; 11 | 12 | public class ChatServerHandler extends SimpleChannelInboundHandler { 13 | 14 | private static final Map userChannelMap = Maps.newHashMap(); 15 | 16 | @Override 17 | protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) { 18 | JSONObject messageJSON = JSON.parseObject(msg.text()); 19 | String userName = messageJSON.getString("userName"); 20 | String content = messageJSON.getString("content"); 21 | ChannelHandlerContext context; 22 | for (Map.Entry entry : userChannelMap.entrySet()) { 23 | context = entry.getKey(); 24 | if (context == ctx) { 25 | context.writeAndFlush(new TextWebSocketFrame("当前用户:" + content)); 26 | } else { 27 | context.writeAndFlush(new TextWebSocketFrame("用户[" + userName + "]:" + content)); 28 | } 29 | } 30 | 31 | userChannelMap.put(ctx, userName); 32 | } 33 | 34 | @Override 35 | public void handlerAdded(ChannelHandlerContext ctx) { 36 | userChannelMap.put(ctx, "用户[" + ctx.channel().remoteAddress() + "]"); 37 | } 38 | 39 | @Override 40 | public void handlerRemoved(ChannelHandlerContext ctx) { 41 | String userName = null; 42 | for (Map.Entry entry : userChannelMap.entrySet()) { 43 | if (entry.getKey() == ctx) { 44 | userName = entry.getValue(); 45 | userChannelMap.remove(ctx); 46 | break; 47 | } 48 | } 49 | 50 | if (userName != null) { 51 | for (ChannelHandlerContext context : userChannelMap.keySet()) { 52 | context.writeAndFlush(new TextWebSocketFrame("用户[" + userName + "] 离开聊天室")); 53 | } 54 | } 55 | } 56 | 57 | @Override 58 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 59 | cause.printStackTrace(); 60 | ctx.close(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /springboot-netty/src/main/java/com/doodl6/springboot/netty/server/HeartbeatHandler.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.netty.server; 2 | 3 | import io.netty.channel.ChannelFutureListener; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelInboundHandlerAdapter; 6 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 7 | import io.netty.handler.timeout.IdleStateEvent; 8 | 9 | public class HeartbeatHandler extends ChannelInboundHandlerAdapter { 10 | 11 | @Override 12 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 13 | if (evt instanceof IdleStateEvent) { 14 | //这里也可以根据业务需要选择断开连接 15 | ctx.writeAndFlush(new TextWebSocketFrame("[系统]:哈喽,你还在嘛")).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); 16 | } else { 17 | super.userEventTriggered(ctx, evt); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /springboot-netty/src/main/java/com/doodl6/springboot/netty/server/WebSocketServerInitializer.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.netty.server; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.http.HttpContentCompressor; 7 | import io.netty.handler.codec.http.HttpObjectAggregator; 8 | import io.netty.handler.codec.http.HttpServerCodec; 9 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 10 | import io.netty.handler.timeout.IdleStateHandler; 11 | 12 | import java.util.concurrent.TimeUnit; 13 | 14 | 15 | public class WebSocketServerInitializer extends ChannelInitializer { 16 | 17 | @Override 18 | public void initChannel(SocketChannel ch) { 19 | //为channel注册handler,统一调用addLast方法,入站处理顺序为从上到下,出站则相反 20 | ChannelPipeline pipeline = ch.pipeline(); 21 | //添加闲置处理,60秒没有数据传输,触发事件 22 | pipeline.addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS)); 23 | //将字节解码为HttpMessage对象,并将HttpMessage对象编码为字节 24 | pipeline.addLast(new HttpServerCodec()); 25 | //出站数据压缩 26 | pipeline.addLast(new HttpContentCompressor()); 27 | //聚合多个HttpMessage为单个FullHttpRequest 28 | pipeline.addLast(new HttpObjectAggregator(64 * 1024)); 29 | //如果被请求的端点是/ws,则处理该升级握手 30 | pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); 31 | //聊天消息处理 32 | pipeline.addLast(new ChatServerHandler()); 33 | //心跳处理 34 | pipeline.addLast(new HeartbeatHandler()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /springboot-opentelemetry/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.doodl6 8 | SpringBoot-Project 9 | ${revision} 10 | 11 | 12 | springboot-opentelemetry 13 | 14 | 15 | 16 | com.doodl6 17 | springboot-common-web 18 | 19 | 20 | 21 | io.opentelemetry 22 | opentelemetry-sdk 23 | 24 | 25 | 26 | io.opentelemetry 27 | opentelemetry-exporter-otlp 28 | 29 | 30 | io.opentelemetry.semconv 31 | opentelemetry-semconv 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /springboot-opentelemetry/src/main/java/com/doodl6/springboot/metrics/Counter.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.metrics; 2 | 3 | import io.opentelemetry.api.GlobalOpenTelemetry; 4 | import io.opentelemetry.api.metrics.LongCounter; 5 | import io.opentelemetry.api.metrics.Meter; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | @Slf4j 9 | public class Counter { 10 | 11 | private static LongCounter REQUEST_COUNTER; 12 | 13 | public static LongCounter getRequestCounter() { 14 | if (REQUEST_COUNTER == null) { 15 | Meter meter = GlobalOpenTelemetry.getMeter("springboot-metrics"); 16 | REQUEST_COUNTER = meter.counterBuilder("http_request_count").build(); 17 | } 18 | return REQUEST_COUNTER; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /springboot-opentelemetry/src/main/java/com/doodl6/springboot/metrics/MetricsAutoConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.metrics; 2 | 3 | import cn.idev.excel.util.StringUtils; 4 | import io.opentelemetry.api.GlobalOpenTelemetry; 5 | import io.opentelemetry.api.OpenTelemetry; 6 | import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; 7 | import io.opentelemetry.sdk.OpenTelemetrySdk; 8 | import io.opentelemetry.sdk.metrics.SdkMeterProvider; 9 | import io.opentelemetry.sdk.metrics.export.MetricExporter; 10 | import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; 11 | import io.opentelemetry.sdk.resources.Resource; 12 | import io.opentelemetry.semconv.ServiceAttributes; 13 | import jakarta.annotation.PostConstruct; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.springframework.beans.factory.annotation.Value; 16 | import org.springframework.boot.web.servlet.ServletComponentScan; 17 | import org.springframework.context.annotation.Configuration; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | @Slf4j 22 | @Configuration 23 | @ServletComponentScan(basePackages = "com.doodl6.springboot.metrics.filter") 24 | public class MetricsAutoConfig { 25 | 26 | private static final String METRICS_PATH = "/opentelemetry/v1/metrics"; 27 | 28 | @Value("${spring.application.name}") 29 | private String applicationName; 30 | 31 | @Value("${victoriaMetrics.url}") 32 | private String victoriaMetricsUrl; 33 | 34 | @Value("${victoriaMetrics.interval:10}") 35 | private int interval; 36 | 37 | @PostConstruct 38 | public void init() { 39 | if (StringUtils.isBlank(victoriaMetricsUrl)) { 40 | throw new IllegalArgumentException("victoriaMetricsUrl is required"); 41 | } 42 | 43 | GlobalOpenTelemetry.set(createOpenTelemetry()); 44 | } 45 | 46 | public OpenTelemetry createOpenTelemetry() { 47 | Resource resource = Resource.builder() 48 | .put(ServiceAttributes.SERVICE_NAME, applicationName) 49 | .build(); 50 | MetricExporter metricExporter = OtlpHttpMetricExporter.builder().setEndpoint(victoriaMetricsUrl + METRICS_PATH).build(); 51 | PeriodicMetricReader metricReader = PeriodicMetricReader.builder(metricExporter).setInterval(interval, TimeUnit.SECONDS).build(); 52 | SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder() 53 | .addResource(resource) 54 | .registerMetricReader(metricReader) 55 | .build(); 56 | return OpenTelemetrySdk.builder() 57 | .setMeterProvider(sdkMeterProvider) 58 | .build(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /springboot-opentelemetry/src/main/java/com/doodl6/springboot/metrics/filter/RequestCountFilter.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.metrics.filter; 2 | 3 | import com.doodl6.springboot.metrics.Counter; 4 | import jakarta.servlet.FilterChain; 5 | import jakarta.servlet.annotation.WebFilter; 6 | import jakarta.servlet.http.HttpServletRequest; 7 | import jakarta.servlet.http.HttpServletResponse; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.core.annotation.Order; 10 | import org.springframework.web.filter.OncePerRequestFilter; 11 | 12 | @Slf4j 13 | @Order(10) 14 | @WebFilter(asyncSupported = true) 15 | public class RequestCountFilter extends OncePerRequestFilter { 16 | 17 | @Override 18 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { 19 | try { 20 | Counter.getRequestCounter().add(1); 21 | } catch (Exception e) { 22 | log.error("RequestCountFilter exception", e); 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /springboot-rocketmq-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-rocketmq-consumer 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | com.doodl6 21 | springboot-dao 22 | 23 | 24 | 25 | org.apache.rocketmq 26 | rocketmq-spring-boot-starter 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /springboot-rocketmq-consumer/src/main/java/com/doodl6/springboot/rocketmq/consumer/domain/NewChatRecord.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.consumer.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class NewChatRecord { 9 | 10 | private String userName; 11 | 12 | private String content; 13 | 14 | private long timestamp; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springboot-rocketmq-consumer/src/main/java/com/doodl6/springboot/rocketmq/consumer/listener/ClearUserMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.consumer.listener; 2 | 3 | import com.doodl6.springboot.dao.manager.UserLoginLogManager; 4 | import jakarta.annotation.Resource; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 7 | import org.apache.rocketmq.spring.core.RocketMQListener; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * 清除用户消息监听 12 | */ 13 | @Slf4j 14 | @Component 15 | @RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.clearUser.group}", topic = "${rocketmq.consumer.clearUser.topic}") 16 | public class ClearUserMessageListener implements RocketMQListener { 17 | 18 | @Resource 19 | private UserLoginLogManager userLoginLogManager; 20 | 21 | @Override 22 | public void onMessage(Long userId) { 23 | log.info("收到清除用户消息 | {}", userId); 24 | int count = userLoginLogManager.deleteByUserId(userId); 25 | log.info("删除用户登录记录完成 | userId:{} | count:{}", userId, count); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /springboot-rocketmq-consumer/src/main/java/com/doodl6/springboot/rocketmq/consumer/listener/NewChatRecordListener.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.consumer.listener; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.doodl6.springboot.rocketmq.consumer.domain.NewChatRecord; 5 | import com.doodl6.springboot.rocketmq.consumer.service.ChatService; 6 | import jakarta.annotation.Resource; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 9 | import org.apache.rocketmq.spring.core.RocketMQListener; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * 聊天记录消息监听 14 | */ 15 | @Slf4j 16 | @Component 17 | @RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.chatRecord.group}", topic = "${rocketmq.consumer.chatRecord.topic}", selectorExpression = "newChatRecord") 18 | public class NewChatRecordListener implements RocketMQListener { 19 | 20 | @Resource 21 | private ChatService chatService; 22 | 23 | @Override 24 | public void onMessage(NewChatRecord newChatRecord) { 25 | log.info("收到聊天记录MQ消息 | {}", JSON.toJSONString(newChatRecord)); 26 | chatService.saveChatRecord(newChatRecord); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springboot-rocketmq-consumer/src/main/java/com/doodl6/springboot/rocketmq/consumer/listener/OrderlyMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.consumer.listener; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.rocketmq.spring.annotation.ConsumeMode; 5 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 6 | import org.apache.rocketmq.spring.core.RocketMQListener; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * 顺序消息监听,需要配置consumeMode = ConsumeMode.ORDERLY才能保证顺序消费,默认是ConsumeMode.CONCURRENTLY会并发消费 11 | */ 12 | @Slf4j 13 | @Component 14 | @RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.orderlyMessage.group}", topic = "${rocketmq.consumer.orderlyMessage.topic}", consumeMode = ConsumeMode.ORDERLY) 15 | public class OrderlyMessageListener implements RocketMQListener { 16 | 17 | @Override 18 | public void onMessage(String content) { 19 | log.info("开始处理顺序消息 | {}", content); 20 | try { 21 | //模拟消息消费慢,如果不是顺序消费,则输出的日志会乱序 22 | Thread.sleep(1000); 23 | } catch (InterruptedException e) { 24 | e.printStackTrace(); 25 | } 26 | log.info("顺序消息处理完成 | {}", content); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springboot-rocketmq-consumer/src/main/java/com/doodl6/springboot/rocketmq/consumer/service/ChatService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.consumer.service; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.doodl6.springboot.rocketmq.consumer.domain.NewChatRecord; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * Created by daixiaoming on 2019/1/3. 10 | */ 11 | @Slf4j 12 | @Service 13 | public class ChatService { 14 | 15 | /** 16 | * 保存聊天记录 17 | */ 18 | public void saveChatRecord(NewChatRecord newChatRecord) { 19 | log.info("保存用户聊天记录 | {}", JSON.toJSONString(newChatRecord)); 20 | 21 | //真实项目可以在这里把聊天内容入库 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-rocketmq-producer 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-db-controller 17 | 18 | 19 | 20 | org.apache.rocketmq 21 | rocketmq-spring-boot-starter 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-websocket 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/MessageController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.common.web.response.BaseResponse; 5 | import com.doodl6.springboot.rocketmq.producer.service.RocketMQService; 6 | import jakarta.annotation.Resource; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | @RequestMapping("/message") 14 | public class MessageController { 15 | 16 | @Resource 17 | private RocketMQService rocketMQService; 18 | 19 | /** 20 | * 发送顺序消息 21 | */ 22 | @PostMapping(value = "/sendOrderlyMessage") 23 | public BaseResponse sendOrderlyMessage(String key, String content) { 24 | Assert.isTrue(StringUtils.isNotBlank(key), "消息key不能为空"); 25 | Assert.isTrue(StringUtils.isNotBlank(content), "消息内容不能为空"); 26 | 27 | rocketMQService.sendOrderlyMessage(key, content); 28 | 29 | return BaseResponse.success(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/ProducerConstants.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer; 2 | 3 | public class ProducerConstants { 4 | 5 | public static final String TRANSACTION_DELETE_USER = "deleteUser"; 6 | } 7 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/UserController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.common.web.response.BaseResponse; 5 | import com.doodl6.springboot.rocketmq.producer.service.RocketMQService; 6 | import jakarta.annotation.Resource; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController("mqUserController") 12 | @RequestMapping("/user") 13 | public class UserController { 14 | 15 | @Resource 16 | private RocketMQService rocketMQService; 17 | 18 | /** 19 | * 删除用户 20 | */ 21 | @PostMapping(value = "/deleteUser") 22 | public BaseResponse deleteUser(Long userId) { 23 | Assert.notNull(userId, "用户ID不能为空"); 24 | 25 | rocketMQService.sendClearUserMsg(userId); 26 | 27 | return BaseResponse.success(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/domain/NewChatRecord.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class NewChatRecord { 9 | 10 | private String userName; 11 | 12 | private String content; 13 | 14 | private long timestamp; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/domain/TransactionMessageObj.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @AllArgsConstructor 10 | public class TransactionMessageObj { 11 | 12 | private String transactionType; 13 | 14 | private Object arg; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/listener/ClearUserTransactionListener.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer.listener; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.doodl6.springboot.dao.entity.User; 5 | import com.doodl6.springboot.dao.manager.UserManager; 6 | import com.doodl6.springboot.rocketmq.producer.ProducerConstants; 7 | import com.doodl6.springboot.rocketmq.producer.domain.TransactionMessageObj; 8 | import jakarta.annotation.Resource; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; 11 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; 12 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; 13 | import org.springframework.messaging.Message; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.nio.charset.Charset; 17 | 18 | /** 19 | * 清除用户事务监听 20 | */ 21 | @Slf4j 22 | @Component 23 | @RocketMQTransactionListener 24 | public class ClearUserTransactionListener implements RocketMQLocalTransactionListener { 25 | 26 | private static final String charset = "UTF-8"; 27 | 28 | @Resource 29 | private UserManager userManager; 30 | 31 | @Override 32 | public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { 33 | if (arg instanceof TransactionMessageObj) { 34 | TransactionMessageObj obj = (TransactionMessageObj) arg; 35 | if (ProducerConstants.TRANSACTION_DELETE_USER.equals(obj.getTransactionType())) { 36 | String msgStr = new String((byte[]) msg.getPayload(), Charset.forName(charset)); 37 | log.info("收到清除用户执行本地事务消息 | msg:{} ", msgStr); 38 | Long userId = (Long) obj.getArg(); 39 | boolean success = userManager.removeById(userId); 40 | log.info("删除用户信息完成 | userId:{} | success:{}", userId, success); 41 | //如果返回RocketMQLocalTransactionState.UNKNOWN,则会定时轮训下面的checkLocalTransaction方法检查本地事务状态 42 | return RocketMQLocalTransactionState.COMMIT; 43 | } else { 44 | log.warn("收到不支持的本地事务消息 | obj:{}", JSON.toJSONString(obj)); 45 | return RocketMQLocalTransactionState.UNKNOWN; 46 | } 47 | } else { 48 | log.warn("收到未知的本地事务消息 | arg:{}", JSON.toJSONString(arg)); 49 | return RocketMQLocalTransactionState.UNKNOWN; 50 | } 51 | } 52 | 53 | @Override 54 | public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { 55 | String msgStr = new String((byte[]) msg.getPayload(), Charset.forName(charset)); 56 | long userId = Long.parseLong(msgStr); 57 | log.info("收到检查清除用户本地事务状态消息 | {}", userId); 58 | 59 | User user = userManager.getById(userId); 60 | return user == null ? RocketMQLocalTransactionState.COMMIT : RocketMQLocalTransactionState.ROLLBACK; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/service/RocketMQService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer.service; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.doodl6.springboot.rocketmq.producer.ProducerConstants; 5 | import com.doodl6.springboot.rocketmq.producer.domain.NewChatRecord; 6 | import com.doodl6.springboot.rocketmq.producer.domain.TransactionMessageObj; 7 | import jakarta.annotation.Resource; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.rocketmq.client.producer.SendCallback; 10 | import org.apache.rocketmq.client.producer.SendResult; 11 | import org.apache.rocketmq.spring.core.RocketMQTemplate; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.messaging.Message; 14 | import org.springframework.messaging.support.GenericMessage; 15 | import org.springframework.stereotype.Service; 16 | 17 | @Slf4j 18 | @Service 19 | public class RocketMQService { 20 | 21 | @Resource 22 | private RocketMQTemplate rocketMQTemplate; 23 | 24 | @Value("${rocketmq.producer.clearUser.destination}") 25 | private String clearUserDestination; 26 | 27 | @Value("${rocketmq.producer.chatRecord.destination}") 28 | private String chatRecordDestination; 29 | 30 | @Value("${rocketmq.producer.orderlyMessage.destination}") 31 | private String orderlyMessageDestination; 32 | 33 | public void sendNewChatRecord(NewChatRecord newChatRecord) { 34 | rocketMQTemplate.asyncSend(chatRecordDestination, JSON.toJSONString(newChatRecord), new SendCallback() { 35 | @Override 36 | public void onSuccess(SendResult sendResult) { 37 | log.info("发送新聊天记录MQ消息成功 | {}", JSON.toJSONString(sendResult)); 38 | } 39 | 40 | @Override 41 | public void onException(Throwable e) { 42 | log.error("发送新聊天记录消息异常", e); 43 | } 44 | }); 45 | } 46 | 47 | /** 48 | * 发送清除用户事务消息 49 | * 1.先发送一个半消息(不可消费) 50 | * 2.执行ClearUserTransactionListener这个类的executeLocalTransaction本地方法 51 | * 3.本地方法执行完成后返回RocketMQLocalTransactionState.COMMIT回执,触发半消息可被消费 52 | */ 53 | public void sendClearUserMsg(long userId) { 54 | Message message = new GenericMessage<>(userId); 55 | try { 56 | SendResult sendResult = rocketMQTemplate.sendMessageInTransaction(clearUserDestination, message, 57 | new TransactionMessageObj(ProducerConstants.TRANSACTION_DELETE_USER, userId)); 58 | log.info("发送清除用户消息完成 | {}", JSON.toJSONString(sendResult)); 59 | } catch (Exception e) { 60 | log.error("发送清除用户消息异常", e); 61 | } 62 | } 63 | 64 | /** 65 | * 发送顺序消息,使用相同的key作为hashKey,保证发送的多条内容都在同一个队列中 66 | */ 67 | public void sendOrderlyMessage(String key, String content) { 68 | for (int i = 0; i < 3; i++) { 69 | rocketMQTemplate.syncSendOrderly(orderlyMessageDestination, content + i, key); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /springboot-rocketmq-producer/src/main/java/com/doodl6/springboot/rocketmq/producer/websocket/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.rocketmq.producer.websocket; 2 | 3 | import jakarta.websocket.server.ServerEndpointConfig; 4 | import org.springframework.beans.BeansException; 5 | import org.springframework.beans.factory.BeanFactory; 6 | import org.springframework.context.ApplicationContext; 7 | import org.springframework.context.ApplicationContextAware; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.web.socket.server.standard.ServerEndpointExporter; 11 | 12 | @Configuration 13 | public class WebSocketConfig extends ServerEndpointConfig.Configurator implements ApplicationContextAware { 14 | 15 | private static volatile BeanFactory context; 16 | 17 | @Bean 18 | public ServerEndpointExporter serverEndpointExporter() { 19 | return new ServerEndpointExporter(); 20 | } 21 | 22 | @Override 23 | public T getEndpointInstance(Class clazz) { 24 | //解决WebSocket不能自动注入bean问题 25 | return context.getBean(clazz); 26 | } 27 | 28 | @Override 29 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 30 | WebSocketConfig.context = applicationContext; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /springboot-seata-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-seata-common 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | io.seata 21 | seata-spring-boot-starter 22 | 23 | 24 | com.alibaba.cloud 25 | spring-cloud-starter-alibaba-seata 26 | 27 | 28 | com.alibaba.cloud 29 | spring-cloud-alibaba-commons 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-actuator 35 | 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-consul-discovery 40 | 41 | 42 | 43 | com.baomidou 44 | mybatis-plus-annotation 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /springboot-seata-common/src/main/java/com/doodl6/springboot/seata/common/Constants.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.common; 2 | 3 | public interface Constants { 4 | 5 | String SUCCESS_RESPONSE = "ok"; 6 | 7 | String FAIL_RESPONSE = "fail"; 8 | } -------------------------------------------------------------------------------- /springboot-seata-common/src/main/java/com/doodl6/springboot/seata/common/entity/Order.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.common.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import lombok.experimental.Accessors; 9 | 10 | @Getter 11 | @Setter 12 | @Accessors(chain = true) 13 | @TableName("`order`") 14 | public class Order { 15 | 16 | @TableId(type = IdType.AUTO) 17 | private Integer id; 18 | 19 | private Integer userId; 20 | 21 | private String goodsCode; 22 | 23 | private Integer stockNum; 24 | 25 | private Integer money; 26 | 27 | } -------------------------------------------------------------------------------- /springboot-seata-common/src/main/java/com/doodl6/springboot/seata/common/entity/Storage.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.common.entity; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | import lombok.experimental.Accessors; 10 | 11 | @Getter 12 | @Setter 13 | @Accessors(chain = true) 14 | @TableName("storage") 15 | public class Storage { 16 | 17 | @TableId(type = IdType.AUTO) 18 | private Integer id; 19 | 20 | private String goodsCode; 21 | 22 | private Integer stockNum; 23 | 24 | } -------------------------------------------------------------------------------- /springboot-seata-order/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-seata-order 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-seata-common 17 | 18 | 19 | 20 | com.mysql 21 | mysql-connector-j 22 | 23 | 24 | 25 | com.alibaba 26 | druid-spring-boot-3-starter 27 | 28 | 29 | 30 | org.mybatis.spring.boot 31 | mybatis-spring-boot-starter 32 | 33 | 34 | com.baomidou 35 | mybatis-plus-boot-starter 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /springboot-seata-order/src/main/java/com/doodl6/springboot/seata/order/OrderApplication.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.order; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | 8 | @EnableDiscoveryClient 9 | @MapperScan("com.doodl6.springboot.seata.order.mapper") 10 | @SpringBootApplication(scanBasePackages = "com.doodl6.springboot") 11 | public class OrderApplication { 12 | public static void main(String[] args) { 13 | SpringApplication.run(OrderApplication.class, args); 14 | } 15 | } -------------------------------------------------------------------------------- /springboot-seata-order/src/main/java/com/doodl6/springboot/seata/order/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.order.controller; 2 | 3 | import com.doodl6.springboot.seata.common.Constants; 4 | import com.doodl6.springboot.seata.common.entity.Order; 5 | import com.doodl6.springboot.seata.order.service.OrderService; 6 | import io.seata.core.context.RootContext; 7 | import jakarta.annotation.Resource; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import java.util.List; 12 | 13 | @Slf4j 14 | @RestController 15 | @RequestMapping("/order") 16 | public class OrderController { 17 | 18 | @Resource 19 | private OrderService orderService; 20 | 21 | @PostMapping(value = "/createOrder") 22 | public String createOrder(@RequestBody Order order) { 23 | System.out.println("order-service执行createOrder 当前正在执行的事务xid:" + RootContext.getXID()); 24 | try { 25 | orderService.createOrder(order); 26 | } catch (Exception e) { 27 | log.error("createOrder error", e); 28 | return Constants.FAIL_RESPONSE; 29 | } 30 | return Constants.SUCCESS_RESPONSE; 31 | } 32 | 33 | @GetMapping(value = "/selectOrderByCode") 34 | public List selectOrderByCode(String goodsCode) { 35 | return orderService.selectOrderByCode(goodsCode); 36 | } 37 | 38 | @PostMapping(value = "/clearOrderByCode") 39 | public String clearOrderByCode(String goodsCode) { 40 | System.out.println("order-service执行clearOrderByCode 当前正在执行的事务xid:" + RootContext.getXID()); 41 | try { 42 | orderService.clearOrderByCode(goodsCode); 43 | } catch (Exception e) { 44 | log.error("clearOrderByCode error", e); 45 | return Constants.FAIL_RESPONSE; 46 | } 47 | return Constants.SUCCESS_RESPONSE; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /springboot-seata-order/src/main/java/com/doodl6/springboot/seata/order/manager/OrderManager.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.order.manager; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 5 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import com.doodl6.springboot.seata.common.entity.Order; 8 | import com.doodl6.springboot.seata.order.mapper.OrderMapper; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.List; 12 | 13 | @Component 14 | public class OrderManager extends ServiceImpl { 15 | 16 | public List queryByGoodsCode(String goodsCode) { 17 | LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); 18 | wrapper.eq(Order::getGoodsCode, goodsCode); 19 | 20 | return list(wrapper); 21 | } 22 | 23 | public void deleteByGoodsCode(String goodsCode) { 24 | LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); 25 | wrapper.eq(Order::getGoodsCode, goodsCode); 26 | getBaseMapper().delete(wrapper); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /springboot-seata-order/src/main/java/com/doodl6/springboot/seata/order/mapper/OrderMapper.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.order.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.doodl6.springboot.seata.common.entity.Order; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface OrderMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /springboot-seata-order/src/main/java/com/doodl6/springboot/seata/order/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.order.service; 2 | 3 | import com.doodl6.springboot.seata.common.entity.Order; 4 | import com.doodl6.springboot.seata.order.manager.OrderManager; 5 | import jakarta.annotation.Resource; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | @Service 11 | public class OrderService { 12 | 13 | @Resource 14 | private OrderManager orderManager; 15 | 16 | public void createOrder(Order order) { 17 | orderManager.save(order); 18 | } 19 | 20 | public List selectOrderByCode(String goodsCode) { 21 | return orderManager.queryByGoodsCode(goodsCode); 22 | } 23 | 24 | public void clearOrderByCode(String goodsCode) { 25 | orderManager.deleteByGoodsCode(goodsCode); 26 | } 27 | } -------------------------------------------------------------------------------- /springboot-seata-order/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9092 3 | spring: 4 | application: 5 | name: order-service 6 | main: 7 | allow-bean-definition-overriding: true 8 | datasource: 9 | type: com.alibaba.druid.pool.DruidDataSource 10 | url: jdbc:mysql://127.0.0.1:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT 11 | driver-class-name: com.mysql.cj.jdbc.Driver 12 | username: root 13 | password: martin-local 14 | max-wait: 60000 15 | max-active: 100 16 | cloud: 17 | consul: 18 | host: 127.0.0.1 19 | port: 8500 20 | discovery: 21 | instanceId: ${spring.application.name}:${random.value} 22 | healthCheckInterval: 15s 23 | logging: 24 | level: 25 | com.doodl6.springboot.seata.order.mapper: DEBUG 26 | 27 | #====================================Seata Config=============================================== 28 | seata: 29 | enabled: true 30 | application-id: ${spring.application.name} 31 | tx-service-group: ${spring.application.name}-tx-group # 事务群组(可以每个应用独立取名,也可以使用相同的名字),但是需要与服务端配置中的service.vgroupMapping的后缀相对应 32 | enable-auto-data-source-proxy: true 33 | data-source-proxy-mode: AT 34 | use-jdk-proxy: false 35 | service: 36 | vgroup-mapping: 37 | order-service-tx-group: default 38 | grouplist: 39 | default: 127.0.0.1:8091 40 | disable-global-transaction: false 41 | -------------------------------------------------------------------------------- /springboot-seata-storage/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-seata-storage 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-seata-common 17 | 18 | 19 | 20 | com.mysql 21 | mysql-connector-j 22 | 23 | 24 | 25 | com.alibaba 26 | druid-spring-boot-3-starter 27 | 28 | 29 | 30 | org.mybatis.spring.boot 31 | mybatis-spring-boot-starter 32 | 33 | 34 | com.baomidou 35 | mybatis-plus-boot-starter 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /springboot-seata-storage/src/main/java/com/doodl6/springboot/seata/storage/StorageApplication.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.storage; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | 8 | @EnableDiscoveryClient 9 | @MapperScan("com.doodl6.springboot.seata.storage.mapper") 10 | @SpringBootApplication(scanBasePackages = "com.doodl6.springboot") 11 | public class StorageApplication { 12 | public static void main(String[] args) { 13 | SpringApplication.run(StorageApplication.class, args); 14 | } 15 | } -------------------------------------------------------------------------------- /springboot-seata-storage/src/main/java/com/doodl6/springboot/seata/storage/controller/StorageController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.storage.controller; 2 | 3 | import com.doodl6.springboot.seata.common.Constants; 4 | import com.doodl6.springboot.seata.common.entity.Storage; 5 | import com.doodl6.springboot.seata.storage.service.StorageService; 6 | import io.seata.core.context.RootContext; 7 | import jakarta.annotation.Resource; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | @RestController 11 | @RequestMapping("/storage") 12 | public class StorageController { 13 | 14 | @Resource 15 | private StorageService storageService; 16 | 17 | @PostMapping("/reduceStock") 18 | public String reduceStock(@RequestParam(name = "goodsCode") String goodsCode, 19 | @RequestParam(name = "stockNum") Integer stockNum) { 20 | System.out.println("storage-service执行reduceStock 当前事务xid:" + RootContext.getXID()); 21 | return storageService.reduceStock(goodsCode, stockNum) ? Constants.SUCCESS_RESPONSE : Constants.FAIL_RESPONSE; 22 | } 23 | 24 | @PostMapping("/initStock") 25 | public boolean initStock(@RequestParam(name = "goodsCode") String goodsCode, 26 | @RequestParam(name = "stockNum") Integer stockNum) { 27 | return storageService.initStock(goodsCode, stockNum); 28 | } 29 | 30 | @GetMapping("/selectByCode") 31 | public Storage selectByCode(@RequestParam(name = "goodsCode") String goodsCode) { 32 | return storageService.selectByCode(goodsCode); 33 | } 34 | } -------------------------------------------------------------------------------- /springboot-seata-storage/src/main/java/com/doodl6/springboot/seata/storage/manager/StorageManager.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.storage.manager; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 5 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import com.doodl6.springboot.seata.common.entity.Storage; 8 | import com.doodl6.springboot.seata.storage.mapper.StorageMapper; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class StorageManager extends ServiceImpl { 13 | 14 | public Storage queryByGoodsCode(String goodsCode) { 15 | LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); 16 | wrapper.eq(Storage::getGoodsCode, goodsCode); 17 | return getOne(wrapper); 18 | } 19 | 20 | public int updateReduceStockNumByGoodsCode(String goodsCode, int reduceStockNum) { 21 | LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); 22 | wrapper.eq(Storage::getGoodsCode, goodsCode) 23 | .ge(Storage::getStockNum, reduceStockNum) 24 | .setSql("stock_num = stock_num - " + reduceStockNum); 25 | return getBaseMapper().update(null, wrapper); 26 | } 27 | 28 | public void deleteByGoodsCode(String goodsCode) { 29 | LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); 30 | wrapper.eq(Storage::getGoodsCode, goodsCode); 31 | getBaseMapper().delete(wrapper); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /springboot-seata-storage/src/main/java/com/doodl6/springboot/seata/storage/mapper/StorageMapper.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.storage.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.doodl6.springboot.seata.common.entity.Storage; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface StorageMapper extends BaseMapper { 9 | 10 | } -------------------------------------------------------------------------------- /springboot-seata-storage/src/main/java/com/doodl6/springboot/seata/storage/service/StorageService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.storage.service; 2 | 3 | import com.doodl6.springboot.seata.common.entity.Storage; 4 | import com.doodl6.springboot.seata.storage.manager.StorageManager; 5 | import jakarta.annotation.Resource; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | @Service 10 | public class StorageService { 11 | 12 | @Resource 13 | private StorageManager storageManager; 14 | 15 | public boolean reduceStock(String goodsCode, int stockNum) { 16 | return storageManager.updateReduceStockNumByGoodsCode(goodsCode, stockNum) == 1; 17 | } 18 | 19 | @Transactional 20 | public boolean initStock(String goodsCode, int stockNum) { 21 | storageManager.deleteByGoodsCode(goodsCode); 22 | return storageManager.save(new Storage().setGoodsCode(goodsCode).setStockNum(stockNum)); 23 | } 24 | 25 | public Storage selectByCode(String goodsCode) { 26 | return storageManager.queryByGoodsCode(goodsCode); 27 | } 28 | } -------------------------------------------------------------------------------- /springboot-seata-storage/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9093 3 | spring: 4 | application: 5 | name: storage-service 6 | main: 7 | allow-bean-definition-overriding: true 8 | datasource: 9 | type: com.alibaba.druid.pool.DruidDataSource 10 | url: jdbc:mysql://127.0.0.1:3306/seata_storage?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT 11 | driver-class-name: com.mysql.cj.jdbc.Driver 12 | username: root 13 | password: martin-local 14 | max-wait: 60000 15 | max-active: 100 16 | cloud: 17 | consul: 18 | host: 127.0.0.1 19 | port: 8500 20 | discovery: 21 | instanceId: ${spring.application.name}:${random.value} 22 | healthCheckInterval: 15s 23 | logging: 24 | level: 25 | com.doodl6.springboot.seata.storage.mapper: DEBUG 26 | 27 | #====================================Seata Config=============================================== 28 | seata: 29 | enabled: true 30 | application-id: ${spring.application.name} 31 | tx-service-group: ${spring.application.name}-tx-group # 事务群组(可以每个应用独立取名,也可以使用相同的名字),但是需要与服务端配置中的service.vgroupMapping的后缀相对应 32 | enable-auto-data-source-proxy: true 33 | data-source-proxy-mode: AT 34 | use-jdk-proxy: false 35 | service: 36 | vgroup-mapping: 37 | storage-service-tx-group: default 38 | grouplist: 39 | default: 127.0.0.1:8091 40 | disable-global-transaction: false -------------------------------------------------------------------------------- /springboot-seata/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-seata 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-seata-common 17 | 18 | 19 | 20 | org.springframework.cloud 21 | spring-cloud-starter-openfeign 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-maven-plugin 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /springboot-seata/src/main/java/com/doodl6/springboot/seata/SeataApplication.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.openfeign.EnableFeignClients; 7 | 8 | @SpringBootApplication(scanBasePackages = "com.doodl6.springboot") 9 | @EnableDiscoveryClient 10 | @EnableFeignClients(basePackages = "com.doodl6.springboot.seata.feign") 11 | public class SeataApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(SeataApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /springboot-seata/src/main/java/com/doodl6/springboot/seata/controller/TradeController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.controller; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.common.web.response.BaseResponse; 5 | import com.doodl6.springboot.seata.response.StorageWithOrderData; 6 | import com.doodl6.springboot.seata.service.TradeService; 7 | import jakarta.annotation.Resource; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @RestController 15 | @RequestMapping("/trade") 16 | public class TradeController { 17 | 18 | @Resource 19 | private TradeService tradeService; 20 | 21 | /** 22 | * 初始化商品库存和订单数据 23 | */ 24 | @PostMapping(value = "/initGoodsData") 25 | public BaseResponse initGoodsData(String goodsCode) { 26 | tradeService.initGoodsData(goodsCode); 27 | return BaseResponse.success(); 28 | } 29 | 30 | /** 31 | * 创建订单 32 | */ 33 | @PostMapping(value = "/createOrder") 34 | public BaseResponse createOrder(String goodsCode, int stockNum, boolean mockException) { 35 | Assert.isTrue(StringUtils.isNotBlank(goodsCode), "商品编码不能为空"); 36 | Assert.isTrue(stockNum > 0, "订单库存数必须大于0"); 37 | tradeService.createOrder(goodsCode, stockNum, mockException); 38 | return BaseResponse.success(); 39 | } 40 | 41 | /** 42 | * 检查商品库存和订单数据 43 | */ 44 | @GetMapping(value = "/checkData") 45 | public BaseResponse checkData(String goodsCode) { 46 | Assert.isTrue(StringUtils.isNotBlank(goodsCode), "商品编码不能为空"); 47 | StorageWithOrderData storageWithOrderData = tradeService.checkData(goodsCode); 48 | return BaseResponse.success(storageWithOrderData); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /springboot-seata/src/main/java/com/doodl6/springboot/seata/feign/IOrderService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.feign; 2 | 3 | import com.doodl6.springboot.seata.common.entity.Order; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | 10 | import java.util.List; 11 | 12 | @FeignClient(name = "order-service") 13 | public interface IOrderService { 14 | 15 | @PostMapping(value = "/order/createOrder") 16 | String createOrder(@RequestBody Order order); 17 | 18 | @GetMapping(value = "/order/selectOrderByCode") 19 | List selectOrderByCode(@RequestParam("goodsCode") String goodsCode); 20 | 21 | @PostMapping(value = "/order/clearOrderByCode") 22 | String clearOrderByCode(@RequestParam("goodsCode") String goodsCode); 23 | 24 | } -------------------------------------------------------------------------------- /springboot-seata/src/main/java/com/doodl6/springboot/seata/feign/IStorageService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.feign; 2 | 3 | import com.doodl6.springboot.seata.common.entity.Storage; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | 9 | @FeignClient(name = "storage-service") 10 | public interface IStorageService { 11 | 12 | @PostMapping("/storage/reduceStock") 13 | String reduceStock(@RequestParam(name = "goodsCode") String goodsCode, 14 | @RequestParam(name = "stockNum") Integer stockNum); 15 | 16 | @PostMapping("/storage/initStock") 17 | boolean initStock(@RequestParam(name = "goodsCode") String goodsCode, 18 | @RequestParam(name = "stockNum") Integer stockNum); 19 | 20 | @GetMapping(value = "/storage/selectByCode") 21 | Storage selectByCode(@RequestParam(name = "goodsCode") String goodsCode); 22 | } -------------------------------------------------------------------------------- /springboot-seata/src/main/java/com/doodl6/springboot/seata/response/StorageWithOrderData.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.response; 2 | 3 | import com.doodl6.springboot.seata.common.entity.Order; 4 | import com.doodl6.springboot.seata.common.entity.Storage; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import java.util.List; 9 | 10 | @Getter 11 | @Setter 12 | public class StorageWithOrderData { 13 | 14 | private Storage storage; 15 | 16 | private List orders; 17 | } 18 | -------------------------------------------------------------------------------- /springboot-seata/src/main/java/com/doodl6/springboot/seata/service/TradeService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.seata.service; 2 | 3 | import cn.hutool.core.lang.Assert; 4 | import com.doodl6.springboot.seata.common.entity.Order; 5 | import com.doodl6.springboot.seata.common.entity.Storage; 6 | import com.doodl6.springboot.seata.feign.IOrderService; 7 | import com.doodl6.springboot.seata.feign.IStorageService; 8 | import com.doodl6.springboot.seata.response.StorageWithOrderData; 9 | import io.seata.core.context.RootContext; 10 | import io.seata.spring.annotation.GlobalTransactional; 11 | import jakarta.annotation.Resource; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.List; 15 | 16 | import static com.doodl6.springboot.seata.common.Constants.SUCCESS_RESPONSE; 17 | 18 | @Service 19 | public class TradeService { 20 | 21 | @Resource 22 | private IStorageService storageService; 23 | 24 | @Resource 25 | private IOrderService orderService; 26 | 27 | /** 28 | * 初始化商品数据,清空占用记录 29 | */ 30 | @GlobalTransactional(timeoutMills = 30000) 31 | public void initGoodsData(String goodsCode) { 32 | String xid = RootContext.getXID(); 33 | String result = orderService.clearOrderByCode(goodsCode); 34 | if (!SUCCESS_RESPONSE.equals(result)) { 35 | throw new IllegalStateException("初始化商品数据失败,清理订单失败! xid:" + xid); 36 | } 37 | 38 | boolean success = storageService.initStock(goodsCode, 100); 39 | Assert.state(success, "初始化商品数据失败,更新库存失败! xid:" + xid); 40 | } 41 | 42 | /** 43 | * 创建订单 44 | */ 45 | @GlobalTransactional(timeoutMills = 30000) 46 | public void createOrder(String goodsCode, int stockNum, boolean mockException) { 47 | String xid = RootContext.getXID(); 48 | System.out.println("createOrder 当前正在执行的事务xid:" + xid); 49 | String result = storageService.reduceStock(goodsCode, stockNum); 50 | 51 | if (!SUCCESS_RESPONSE.equals(result)) { 52 | throw new IllegalStateException("库存扣减失败! xid:" + xid); 53 | } 54 | 55 | Order order = new Order().setGoodsCode(goodsCode).setStockNum(stockNum).setMoney(stockNum * 100).setUserId(1); 56 | 57 | result = orderService.createOrder(order); 58 | 59 | if (!SUCCESS_RESPONSE.equals(result)) { 60 | throw new IllegalStateException("创建订单失败! xid:" + xid); 61 | } 62 | 63 | if (mockException) { 64 | throw new IllegalStateException("模拟用户业务异常! xid:" + xid); 65 | } 66 | } 67 | 68 | public StorageWithOrderData checkData(String goodsCode) { 69 | StorageWithOrderData storageWithOrderData = new StorageWithOrderData(); 70 | Storage storage = storageService.selectByCode(goodsCode); 71 | List orders = orderService.selectOrderByCode(goodsCode); 72 | storageWithOrderData.setStorage(storage); 73 | storageWithOrderData.setOrders(orders); 74 | return storageWithOrderData; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /springboot-seata/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9091 3 | spring: 4 | application: 5 | name: springboot-seata 6 | main: 7 | allow-bean-definition-overriding: true 8 | cloud: 9 | consul: 10 | host: 127.0.0.1 11 | port: 8500 12 | discovery: 13 | instanceId: ${spring.application.name}:${random.value} 14 | healthCheckInterval: 15s 15 | 16 | #====================================Feign Config=============================================== 17 | feign: 18 | httpclient: 19 | enabled: true 20 | connection-timeout: 60000 21 | client: 22 | config: 23 | default: 24 | connectTimeout: 60000 25 | readTimeout: 60000 26 | loggerLevel: FULL 27 | ribbon: 28 | ConnectTimeout: 100000 29 | ReadTimeout: 1000000 30 | OkToRetryOnAllOperations: false 31 | 32 | #====================================Seata Config=============================================== 33 | seata: 34 | enabled: true 35 | application-id: ${spring.application.name} 36 | tx-service-group: ${spring.application.name}-tx-group # 事务群组(可以每个应用独立取名,也可以使用相同的名字),但是需要与服务端配置中的service.vgroupMapping的后缀相对应 37 | service: 38 | vgroup-mapping: 39 | springboot-seata-tx-group: default 40 | grouplist: 41 | default: 127.0.0.1:8091 42 | disable-global-transaction: false -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/WebApplication.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.ServletComponentScan; 6 | 7 | @SpringBootApplication(scanBasePackages = "com.doodl6.springboot") 8 | @ServletComponentScan(basePackages = "com.doodl6.springboot.web.listener") 9 | public class WebApplication { 10 | 11 | public static void main(String[] args) { 12 | System.setProperty("es.set.netty.runtime.available.processors", "false"); 13 | SpringApplication.run(WebApplication.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/config/DocConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Info; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class DocConfig { 10 | 11 | @Bean 12 | public OpenAPI openAPI() { 13 | return new OpenAPI().info(new Info().title("SpringBoot-Project项目的接口文档")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebMvcConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 12 | registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); 13 | registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/"); 14 | registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/constant/WebConstants.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.constant; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * Web常量类 8 | * Created by daixiaoming on 2018/5/5. 9 | */ 10 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 11 | public final class WebConstants { 12 | 13 | public static String ROOT_PATH = null; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/controller/EventStreamController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.controller; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.http.MediaType; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | import reactor.core.publisher.Flux; 8 | 9 | import java.time.Duration; 10 | import java.time.LocalTime; 11 | 12 | @Slf4j 13 | @RestController 14 | public class EventStreamController { 15 | 16 | @GetMapping(value = "/eventStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 17 | public Flux eventStream() { 18 | // 创建一个 Flux,用于每秒推送一条消息 19 | return Flux.interval(Duration.ofSeconds(1)) 20 | .map(sequence -> "服务器时间 " + LocalTime.now()) 21 | .take(10)// 限制发送 10 条消息 22 | .concatWith(Flux.just("END")) // 添加一个结束信号 23 | .doOnCancel(() -> System.out.println("客户端连接已断开")); // 客户端断开时触发 24 | } 25 | } -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/controller/MonitorController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.controller; 2 | 3 | import com.alibaba.fastjson2.JSONArray; 4 | import com.alibaba.fastjson2.JSONObject; 5 | import com.doodl6.springboot.common.web.response.BaseResponse; 6 | import io.swagger.v3.oas.annotations.Operation; 7 | import io.swagger.v3.oas.annotations.tags.Tag; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.lang.management.ManagementFactory; 13 | import java.lang.management.MemoryPoolMXBean; 14 | import java.lang.management.MemoryUsage; 15 | import java.util.List; 16 | 17 | /** 18 | * 监控控制类 19 | */ 20 | @Tag(name = "监控相关") 21 | @RestController 22 | @RequestMapping("/monitor") 23 | public class MonitorController { 24 | 25 | /** 26 | * 获取堆内存使用情况 27 | */ 28 | @Operation(summary = "获取堆内存使用情况") 29 | @GetMapping(value = "/getHeapMemoryUsage") 30 | public BaseResponse getHeapMemoryUsage() { 31 | List memoryMXBeanList = ManagementFactory.getMemoryPoolMXBeans(); 32 | JSONArray memoryUsageArray = new JSONArray(); 33 | for (MemoryPoolMXBean memoryPoolMXBean : memoryMXBeanList) { 34 | JSONObject memoryUsageJSON = new JSONObject(); 35 | 36 | //内存类型 37 | memoryUsageJSON.put("type", memoryPoolMXBean.getType().name()); 38 | memoryUsageJSON.put("name", memoryPoolMXBean.getName()); 39 | MemoryUsage memoryUsage = memoryPoolMXBean.getUsage(); 40 | //最大可用内存 41 | memoryUsageJSON.put("maxMemorySize", memoryUsage.getMax() / 1024 / 1024 + "MB"); 42 | //已提交的内存 43 | memoryUsageJSON.put("committedMemorySize", memoryUsage.getCommitted() / 1024 / 1024 + "MB"); 44 | //已使用的内存 45 | memoryUsageJSON.put("usedMemorySize", memoryUsage.getUsed() / 1024 / 1024 + "MB"); 46 | memoryUsageArray.add(memoryUsageJSON); 47 | } 48 | 49 | return BaseResponse.success(memoryUsageArray); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/controller/StandardController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.controller; 2 | 3 | import com.doodl6.springboot.common.check.CheckUtil; 4 | import com.doodl6.springboot.common.web.response.BaseResponse; 5 | import com.doodl6.springboot.common.web.response.MapResponse; 6 | import com.doodl6.springboot.web.request.CheckParameterRequest; 7 | import com.doodl6.springboot.web.response.CheckParameterResult; 8 | import io.swagger.v3.oas.annotations.Operation; 9 | import io.swagger.v3.oas.annotations.tags.Tag; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.util.Assert; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | /** 18 | * 常规控制类 19 | */ 20 | @Tag(name = "常规接口") 21 | @Slf4j 22 | @RestController 23 | public class StandardController { 24 | 25 | /** 26 | * 普通接口 27 | */ 28 | @Operation(summary = "普通接口") 29 | @GetMapping("/hello") 30 | public MapResponse hello(String name) { 31 | MapResponse mapResponse = new MapResponse(); 32 | Assert.notNull(name, "name不能为空"); 33 | 34 | mapResponse.appendData("name", name); 35 | 36 | return mapResponse; 37 | } 38 | 39 | /** 40 | * 参数校验 41 | */ 42 | @Operation(summary = "参数校验") 43 | @PostMapping("/parameterCheck") 44 | public BaseResponse parameterCheck(@RequestBody CheckParameterRequest request) { 45 | 46 | CheckUtil.check(request); 47 | 48 | CheckParameterResult result = new CheckParameterResult(); 49 | result.setName(request.getName()); 50 | result.setAge(request.getAge()); 51 | result.setFavorites(request.getFavorites()); 52 | BaseResponse response = new BaseResponse<>(); 53 | response.setData(result); 54 | 55 | return response; 56 | } 57 | 58 | /** 59 | * 测试打印日志 60 | */ 61 | @Operation(summary = "测试打印日志") 62 | @GetMapping("/testLog") 63 | public BaseResponse testLog() { 64 | BaseResponse response = new BaseResponse<>(); 65 | 66 | log.trace("test trace log"); 67 | log.debug("test debug log"); 68 | log.info("test info log"); 69 | log.warn("test warn log"); 70 | log.error("test error log"); 71 | 72 | return response; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/controller/XhttpController.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.controller; 2 | 3 | import io.swagger.v3.oas.annotations.Operation; 4 | import io.swagger.v3.oas.annotations.tags.Tag; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.io.IOException; 12 | import java.io.PrintWriter; 13 | 14 | /** 15 | * 可以支持实时输出内容,供xhttp.html页面使用 16 | * Created by daixiaoming on 2018/12/20. 17 | */ 18 | @Tag(name = "Xhttp") 19 | @RestController 20 | @RequestMapping("/xhttp") 21 | public class XhttpController { 22 | 23 | /** 24 | * 获取响应内容 25 | */ 26 | @Operation(summary = "获取响应内容") 27 | @GetMapping("/getResponseContent") 28 | public void getResponseContent(HttpServletResponse response, String content) throws IOException, InterruptedException { 29 | if (StringUtils.isEmpty(content)) { 30 | return; 31 | } 32 | 33 | response.setHeader("Content-Type", "text/html;charset=utf-8"); 34 | PrintWriter writer = response.getWriter(); 35 | for (int i = 0; i < 10; i++) { 36 | writer.write(content + i); 37 | writer.flush(); 38 | Thread.sleep(1000); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/dto/ExcelData.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.dto; 2 | 3 | import cn.idev.excel.annotation.ExcelProperty; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | public class ExcelData { 10 | 11 | @ExcelProperty("姓名") 12 | private String name; 13 | 14 | @ExcelProperty("年龄") 15 | private String age; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/listener/InitDataListener.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.listener; 2 | 3 | import com.doodl6.springboot.web.constant.WebConstants; 4 | import jakarta.servlet.ServletContext; 5 | import jakarta.servlet.ServletContextEvent; 6 | import jakarta.servlet.ServletContextListener; 7 | import jakarta.servlet.annotation.WebListener; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * 初始化数据监听 13 | * Created by daixiaoming on 2018/5/5. 14 | */ 15 | @WebListener 16 | public class InitDataListener implements ServletContextListener { 17 | 18 | @Override 19 | public void contextInitialized(ServletContextEvent sce) { 20 | ServletContext servletContext = sce.getServletContext(); 21 | WebConstants.ROOT_PATH = servletContext.getRealPath(File.separator); 22 | } 23 | 24 | @Override 25 | public void contextDestroyed(ServletContextEvent sce) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/request/CheckParameterRequest.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.request; 2 | 3 | import com.doodl6.springboot.common.check.annotation.FieldNotEmpty; 4 | import com.doodl6.springboot.common.check.annotation.FieldNotNull; 5 | import io.swagger.v3.oas.annotations.media.Schema; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | 9 | import java.util.List; 10 | 11 | @Getter 12 | @Setter 13 | @Schema(description = "校验参数请求类") 14 | public class CheckParameterRequest { 15 | 16 | @FieldNotEmpty(name = "姓名") 17 | @Schema(description = "姓名") 18 | private String name; 19 | 20 | @FieldNotNull(name = "年龄") 21 | @Schema(description = "年龄") 22 | private Integer age; 23 | 24 | @FieldNotEmpty(name = "爱好") 25 | @Schema(description = "爱好") 26 | private List favorites; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/response/CheckParameterResult.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.response; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.util.List; 8 | 9 | @Getter 10 | @Setter 11 | @Schema(description = "校验参数响应类") 12 | public class CheckParameterResult { 13 | 14 | @Schema(description = "姓名") 15 | private String name; 16 | 17 | @Schema(description = "年龄") 18 | private int age; 19 | 20 | @Schema(description = "爱好") 21 | private List favorites; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/util/RequestUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.util; 2 | 3 | 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import lombok.AccessLevel; 6 | import lombok.NoArgsConstructor; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | /** 10 | * 请求工具类 11 | */ 12 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 13 | public final class RequestUtil { 14 | 15 | /** 16 | * 获取请求来源的ip地址 17 | */ 18 | public static String getIpAddress(HttpServletRequest request) { 19 | String ip = null; 20 | try { 21 | ip = request.getHeader("X-Forwarded-For"); 22 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 23 | ip = request.getHeader("Proxy-Client-IP"); 24 | } 25 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 26 | ip = request.getHeader("WL-Proxy-Client-IP"); 27 | } 28 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 29 | ip = request.getHeader("HTTP_CLIENT_IP"); 30 | } 31 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 32 | ip = request.getHeader("HTTP_X_FORWARDED_FOR"); 33 | } 34 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 35 | ip = request.getRemoteAddr(); 36 | } 37 | } catch (Exception ignored) { 38 | } 39 | 40 | if (StringUtils.isNotBlank(ip) && ip.contains(",")) { 41 | ip = ip.split(",")[0]; 42 | } 43 | 44 | return ip; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/util/ResponseUtil.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.util; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import jakarta.servlet.http.HttpServletResponse; 6 | import lombok.AccessLevel; 7 | import lombok.NoArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.apache.poi.ss.usermodel.Workbook; 11 | 12 | import java.io.IOException; 13 | import java.io.OutputStream; 14 | import java.io.Writer; 15 | import java.net.URLEncoder; 16 | 17 | /** 18 | * 响应工具类 19 | */ 20 | @Slf4j 21 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 22 | public final class ResponseUtil { 23 | 24 | /** 25 | * 输出客户端JSON内容 26 | */ 27 | public static void responseJSON(HttpServletResponse response, Object object) { 28 | writeResponse(response, JSON.toJSONString(object), "application/json"); 29 | } 30 | 31 | /** 32 | * 输出客户端html内容 33 | */ 34 | public static void responseHtml(HttpServletResponse response, String text) { 35 | writeResponse(response, text, "text/html"); 36 | } 37 | 38 | /** 39 | * 输出客户端xml内容 40 | */ 41 | public static void responseXml(HttpServletResponse response, String text) { 42 | writeResponse(response, text, "text/xml"); 43 | } 44 | 45 | /** 46 | * 输出客户端excel 47 | */ 48 | public static void responseExcel(HttpServletResponse response, Workbook workbook, String fileName) { 49 | try (OutputStream os = response.getOutputStream()) { 50 | // 设定字符集 51 | response.setCharacterEncoding("UTF-8"); 52 | // 设定Content类型 53 | response.setContentType("multipart/form-data"); 54 | // 设定Http头部 55 | response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8")); 56 | workbook.write(os); 57 | } catch (IOException e) { 58 | log.error("输出excel异常", e); 59 | } 60 | } 61 | 62 | public static void writeSupportCrossDomain(HttpServletRequest request, HttpServletResponse response) { 63 | String origin = request.getHeader("Origin"); 64 | if (StringUtils.isNotBlank(origin) && origin.endsWith("doodl6.com")) { 65 | response.setHeader("Access-Control-Allow-Origin", origin); 66 | } 67 | response.setHeader("Access-Control-Allow-Credentials", "true"); 68 | response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers")); 69 | } 70 | 71 | private static void writeResponse(HttpServletResponse response, String content, String contentType) { 72 | response.setContentType(contentType); 73 | response.setCharacterEncoding("utf-8"); 74 | try (Writer writer = response.getWriter()) { 75 | writer.write(content); 76 | writer.flush(); 77 | } catch (IOException e) { 78 | log.error("输出客户端内容出现异常", e); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/vo/ExcelVo.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.vo; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by daixiaoming on 2018-10-31. 10 | */ 11 | @Getter 12 | @Setter 13 | public class ExcelVo { 14 | 15 | private List column1; 16 | 17 | private List column2; 18 | 19 | private String mergeColumn; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /springboot-web/src/main/java/com/doodl6/springboot/web/vo/MessageVo.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.web.vo; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * Created by daixiaoming on 2018-12-10. 10 | */ 11 | @Getter 12 | @Setter 13 | public class MessageVo { 14 | 15 | private int userId; 16 | 17 | private String userName; 18 | 19 | private String content; 20 | 21 | private Date sendTime; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /springboot-web/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 2019 3 | spring: 4 | datasource: 5 | url: jdbc:shardingsphere:classpath:sharding-db.yml 6 | data: 7 | elasticsearch: 8 | cluster-nodes: 127.0.0.1:9300 9 | redis: 10 | host: 127.0.0.1 11 | port: 6379 12 | cloud: 13 | consul: 14 | host: 127.0.0.1 15 | port: 8500 16 | stream: 17 | rocketmq: 18 | binder: 19 | name-server: 127.0.0.1:9876 20 | dubbo: 21 | application: 22 | name: springboot-web-local 23 | registry: 24 | address: zookeeper://127.0.0.1:2181 25 | reference: 26 | firstDubbo: 27 | version: 1.0-local 28 | rocketmq: 29 | name-server: 127.0.0.1:9876 30 | memcached: 31 | address: 127.0.0.1:11211 32 | zookeeper: 33 | address: 127.0.0.1:2181 34 | nacos: 35 | config: 36 | server-addr: 127.0.0.1:8848 37 | logging: 38 | level: 39 | com.doodl6.springboot.dao.mapper: DEBUG 40 | -------------------------------------------------------------------------------- /springboot-web/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=local -------------------------------------------------------------------------------- /springboot-web/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: springboot-web 4 | main: 5 | allow-bean-definition-overriding: true 6 | cloud: 7 | consul: 8 | discovery: 9 | instanceId: ${spring.application.name}:${random.value} 10 | register: false 11 | datasource: 12 | driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver 13 | dubbo: 14 | application: 15 | qos-port: 20991 16 | protocol: 17 | name: dubbo 18 | port: 20881 19 | consumer: 20 | filter: dubboTrace 21 | rocketmq: 22 | producer: 23 | group: chatRecordGroup 24 | clearUser: 25 | destination: clearUserData:* 26 | chatRecord: 27 | destination: ChatRecord:newChatRecord 28 | orderlyMessage: 29 | destination: OrderlyMessage:* 30 | consumer: 31 | clearUser: 32 | group: clearUserGroup 33 | topic: clearUserData 34 | chatRecord: 35 | group: chatRecordGroup 36 | topic: ChatRecord 37 | orderlyMessage: 38 | group: orderlyMessageGroup 39 | topic: OrderlyMessage 40 | springdoc: 41 | packagesToScan: com.doodl6 42 | victoriaMetrics: 43 | url: http://localhost:8428 44 | interval: 10 45 | -------------------------------------------------------------------------------- /springboot-web/src/main/resources/leaf.properties: -------------------------------------------------------------------------------- 1 | leaf.segment.enable = true 2 | leaf.jdbc.url = jdbc:mysql://localhost:3306/leaf 3 | leaf.jdbc.username = root 4 | leaf.jdbc.password = martin-local -------------------------------------------------------------------------------- /springboot-web/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %date %-5level [%file:%line] - %msg%n 9 | 10 | 11 | 12 | 13 | ${LOG_PATH}/rt/rt.log 14 | 15 | ${LOG_PATH}/rt/rt.%d{yyyy-MM-dd-HH}.log 16 | 48 17 | 3GB 18 | 19 | 20 | %date - %msg%n 21 | 22 | 23 | INFO 24 | ACCEPT 25 | DENY 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /springboot-web/src/main/resources/logback.properties: -------------------------------------------------------------------------------- 1 | LOG_PATH = ${user.dir}/logs -------------------------------------------------------------------------------- /springboot-web/src/main/resources/sharding-db.yml: -------------------------------------------------------------------------------- 1 | mode: 2 | type: Standalone 3 | repository: 4 | type: JDBC 5 | dataSources: 6 | ds_0: 7 | dataSourceClassName: com.alibaba.druid.pool.DruidDataSource 8 | driverClassName: com.mysql.cj.jdbc.Driver 9 | url: jdbc:mysql://127.0.0.1:3306/spring_project_1 10 | username: root 11 | password: martin-local 12 | ds_1: 13 | dataSourceClassName: com.alibaba.druid.pool.DruidDataSource 14 | driverClassName: com.mysql.cj.jdbc.Driver 15 | url: jdbc:mysql://127.0.0.1:3306/spring_project_2 16 | username: root 17 | password: martin-local 18 | rules: 19 | - !SINGLE 20 | tables: 21 | - ds_0.user 22 | - !SHARDING 23 | tables: 24 | user_login_log: 25 | actualDataNodes: ds_${0..1}.user_login_log_${0..1} 26 | tableStrategy: 27 | standard: 28 | shardingColumn: user_id 29 | shardingAlgorithmName: user_login_log_inline 30 | defaultShardingColumn: user_id 31 | defaultDatabaseStrategy: 32 | standard: 33 | shardingColumn: user_id 34 | shardingAlgorithmName: database_inline 35 | shardingAlgorithms: 36 | database_inline: 37 | type: INLINE 38 | props: 39 | algorithm-expression: ds_${user_id % 2} 40 | user_login_log_inline: 41 | type: INLINE 42 | props: 43 | algorithm-expression: user_login_log_${user_id % 2} -------------------------------------------------------------------------------- /springboot-web/src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MartinDai/SpringBoot-Project/44c26572323ad5daecedfb5c5f951a100e3761e5/springboot-web/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /springboot-zookeeper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | SpringBoot-Project 7 | com.doodl6 8 | ${revision} 9 | 10 | 4.0.0 11 | springboot-zookeeper 12 | 13 | 14 | 15 | com.doodl6 16 | springboot-common-web 17 | 18 | 19 | 20 | org.apache.curator 21 | curator-recipes 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /springboot-zookeeper/src/main/java/com/doodl6/springboot/zookeeper/service/ZookeeperService.java: -------------------------------------------------------------------------------- 1 | package com.doodl6.springboot.zookeeper.service; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.curator.framework.CuratorFramework; 5 | import org.apache.curator.framework.CuratorFrameworkFactory; 6 | import org.apache.curator.framework.recipes.locks.InterProcessMutex; 7 | import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock; 8 | import org.apache.curator.retry.RetryNTimes; 9 | import org.springframework.beans.factory.InitializingBean; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.concurrent.TimeUnit; 14 | 15 | /** 16 | * Created by daixiaoming on 2019/5/22. 17 | */ 18 | @Slf4j 19 | @Service 20 | public class ZookeeperService implements InitializingBean { 21 | 22 | private static final String ZK_LOCK_PATH = "/zkLock"; 23 | 24 | private static final int LOCK_WAIT = 1; 25 | 26 | @Value("${zookeeper.address}") 27 | private String zkAddress; 28 | 29 | private InterProcessMutex xLock; 30 | 31 | private InterProcessReadWriteLock readWriteLock; 32 | 33 | public boolean tryGetXLock() throws Exception { 34 | return xLock.acquire(LOCK_WAIT, TimeUnit.SECONDS); 35 | } 36 | 37 | public boolean tryGetWriteLock() throws Exception { 38 | return readWriteLock.writeLock().acquire(LOCK_WAIT, TimeUnit.SECONDS); 39 | } 40 | 41 | public boolean tryGetReadLock() throws Exception { 42 | return readWriteLock.readLock().acquire(LOCK_WAIT, TimeUnit.SECONDS); 43 | } 44 | 45 | public void releaseXLock() { 46 | try { 47 | xLock.release(); 48 | } catch (Exception e) { 49 | log.error("释放独占锁异常", e); 50 | } 51 | } 52 | 53 | public void releaseWriteLock() { 54 | try { 55 | readWriteLock.writeLock().release(); 56 | } catch (Exception e) { 57 | log.error("释放写锁异常", e); 58 | } 59 | } 60 | 61 | public void releaseReadLock() { 62 | try { 63 | readWriteLock.readLock().release(); 64 | } catch (Exception e) { 65 | log.error("释放读锁异常", e); 66 | } 67 | } 68 | 69 | @Override 70 | public void afterPropertiesSet() { 71 | CuratorFramework client = CuratorFrameworkFactory.newClient( 72 | zkAddress, 73 | new RetryNTimes(10, 5000) 74 | ); 75 | client.start(); 76 | 77 | xLock = new InterProcessMutex(client, ZK_LOCK_PATH); 78 | readWriteLock = new InterProcessReadWriteLock(client, ZK_LOCK_PATH); 79 | } 80 | } 81 | --------------------------------------------------------------------------------