diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..9484591 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,56 @@ +kind: pipeline +type: docker +name: ds-cloud + +#分支触发 +trigger: + branch: + - dev +steps: + - name: build-jar # 流水线名称 + image: maven:3.8.5-openjdk-17 # 定义创建容器的Docker镜像 + volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置 + - name: maven-cache + path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载 + - name: maven-build + path: /mnt/app/build # 将应用打包好的Jar和执行脚本挂载出来 + commands: # 定义在Docker容器中执行的shell命令 + - mvn clean package -Ptest -DskipTests=true # 应用打包命 + - cd docker + - sh copy.sh + - cd ../ + - cp -r docker /mnt/app/build/ + + - name: build-docker + image: plugins/docker + volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置 + - name: maven-build + path: /mnt/app/build # 将应用打包好的Jar和执行脚本挂载出来 + - name: docker + path: /var/run/docker.sock # 挂载宿主机的docker + - name: dockerc + path: /usr/local/bin/docker-compose + #settings: + # dockerfile: /mnt/app/build/Dockerfile + commands: # 定义在Docker容器中执行的shell命令 + - cd /mnt/app/build/docker + - chmod +x deploy.sh + - sh deploy.sh stop + - sh deploy.sh rm + - sh deploy.sh build + - sh deploy.sh modules + - docker ps + +volumes: # 定义流水线挂载目录,用于共享数据 + - name: maven-build + host: + path: /www/mnt/maven/build # 从宿主机中挂载的目录 + - name: maven-cache + host: + path: /www/mnt/maven/cache + - name: docker + host: + path: /var/run/docker.sock + - name: dockerc + host: + path: /usr/local/bin/docker-compose \ No newline at end of file diff --git a/.gitignore b/.gitignore index bd58556..e291498 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,3 @@ nbdist/ !*/build/*.xml docker/*.jar -/.idea/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 8b52174..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index b29e007..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index a5c59a8..0000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 61c9764..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/docker/deploy.sh b/docker/deploy.sh new file mode 100644 index 0000000..ad0b67a --- /dev/null +++ b/docker/deploy.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +# 使用说明,用来提示输入参数 +usage() { + echo "Usage: sh 执行脚本.sh [port|base|modules|stop|rm|stopbase|rmbase]" + exit 1 +} + +# 开启所需端口 +port(){ +# firewall-cmd --add-port=80/tcp --permanent +# firewall-cmd --add-port=8080/tcp --permanent +# firewall-cmd --add-port=8848/tcp --permanent +# firewall-cmd --add-port=9848/tcp --permanent +# firewall-cmd --add-port=9849/tcp --permanent +# firewall-cmd --add-port=6379/tcp --permanent +# firewall-cmd --add-port=3306/tcp --permanent +# firewall-cmd --add-port=9100/tcp --permanent +# firewall-cmd --add-port=9200/tcp --permanent +# firewall-cmd --add-port=9201/tcp --permanent +# firewall-cmd --add-port=9202/tcp --permanent +# firewall-cmd --add-port=9203/tcp --permanent +# firewall-cmd --add-port=9300/tcp --permanent +# service firewalld restart + echo "开启所需端口(未实现)" +} + +# 启动基础环境(必须) +base(){ + docker-compose up -d ds-nacos +} +# 停止基础环境 +stopbase(){ + docker-compose stop ds-nacos +} +# 删除基础环境 +rmbase(){ + docker-compose rm ds-nacos -f +} + +# 启动程序模块(必须) +modules(){ + docker-compose up -d ds-gateway ds-miniapps +} + +# 关闭module环境/模块 +stop(){ + docker-compose stop ds-gateway ds-miniapps +} + +# 删除module环境/模块 +rm(){ + docker-compose rm ds-gateway ds-miniapps -f + docker rmi docker-ds-gateway docker-ds-miniapps +} + +build(){ + docker-compose build ds-gateway ds-miniapps +} + + +# 根据输入参数,选择执行对应方法,不输入则执行使用说明 +case "$1" in +"port") + port +;; +"base") + base +;; +"modules") + modules +;; +"stop") + stop +;; +"rm") + rm +;; +"stopbase") + stopbase +;; +"rmbase") + rmbase +;; +"build") + build +;; +*) + usage +;; +esac diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e69de29..670fee6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -0,0 +1,115 @@ +version: '3' +services: + #nacos + ds-nacos: + image: nacos/nacos-server:v2.2.3 + container_name: ds-nacos + ports: + - 8848:8848 + - 9848:9848 + networks: + - ds-network + environment: + - MODE=standalone + - MYSQL_SERVICE_HOST=172.19.0.3 + - MYSQL_SERVICE_PORT=3306 + - MYSQL_SERVICE_DB_NAME=nacos + - MYSQL_SERVICE_USER=nacos + - MYSQL_SERVICE_PASSWORD=ds4810 + - NACOS_AUTH_ENABLE=true + - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890987654321 + - NACOS_AUTH_IDENTITY_KEY=dashan + - NACOS_AUTH_IDENTITY_VALUE=dashan + - JVM_XMS=256m + - JVM_XMX=256m + - JVM_XMN=128m + - JVM_MS=64m + - JVM_MMS=128m + restart: on-failure + ds-xxl-job: + image: xuxueli/xxl-job-admin:2.4.0 + container_name: ds-xxl-job + ports: + - "9201:9201" + networks: + - ds-network + volumes: + - ./logs/xxl-job:/data/applogs + - ./data/xxl-job/:/xxl-job + environment: + - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/xxl_job?serverTimezone=UTC&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai + - SPRING_DATASOURCE_USERNAME=xxl_job + - SPRING_DATASOURCE_PASSWORD=ds4810 + - SERVER_PORT=9201 + - JVM_XMS=256m + - JVM_XMX=256m + - JVM_XMN=128m + - JVM_MS=64m + - JVM_MMS=128m + restart: on-failure + #elasticsearch + ds-elasticsearch: + image: elasticsearch:8.8.1 + container_name: ds-elasticsearch + ports: + - "9200:9200" + - "9300:9300" + networks: + - ds-network + environment: + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms128m -Xmx256m + - http.host=0.0.0.0 + privileged: true + volumes: + - ./data/elasticsearch:/usr/share/elasticsearch/data + - ./config/elasticsearch/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml + - es-plugins:/usr/share/elasticsearch/plugins + #restart: on-failure:3 + #kibana + ds-kibana: + image: kibana:8.8.1 + container_name: ds-kibana + ports: + - "5601:5601" + networks: + - ds-network + environment: + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms128m -Xmx256m + - http.host=0.0.0.0 + privileged: true + volumes: + - ./config/kibana/kibana.yml:/usr/share/kibana/config/kibana.yml + depends_on: + - ds-elasticsearch + #restart: on-failure:3 + ds-gateway: + container_name: ds-gateway + build: + context: ./ds/gateway + dockerfile: Dockerfile + ports: + - "8080:8080" + #depends_on: + # - ds-nacos + networks: + - ds-network + ds-miniapps: + container_name: ds-miniapps + build: + context: ./ds/modules/miniapps + dockerfile: Dockerfile + ports: + - "15101:15101" + #depends_on: + # - ds-nacos + networks: + - ds-network +networks: + ds-network: + external: true +volumes: + #解决ERROR: Named volume "es-plugins:/usr/share/elasticsearch/plugins:rw" is used in service "lottery-elasticsearch" but no declaration was found in the volumes section. + es-plugins: + diff --git a/docker/ds/gateway/Dockerfile b/docker/ds/gateway/Dockerfile index 20fa468..8449628 100644 --- a/docker/ds/gateway/Dockerfile +++ b/docker/ds/gateway/Dockerfile @@ -1 +1,16 @@ +#基础镜像 FROM openjdk:17 +# author +MAINTAINER dashan +# 挂载目录 +VOLUME /home/dashan +# 创建目录 +RUN mkdir -p /home/dashan +# 指定路径 +WORKDIR /home/dashan +# 复制jar文件到路径 +COPY ./jar/ds-gateway.jar /home/dashan/ds-gateway.jar +#暴漏端口 +EXPOSE 8080 +# 启动网关服务 +ENTRYPOINT ["java","-jar","ds-gateway.jar"] \ No newline at end of file diff --git a/docker/ds/modules/miniapps/Dockerfile b/docker/ds/modules/miniapps/Dockerfile new file mode 100644 index 0000000..3cfaeb0 --- /dev/null +++ b/docker/ds/modules/miniapps/Dockerfile @@ -0,0 +1,16 @@ +#基础镜像 +FROM openjdk:17 +# author +MAINTAINER dashan +# 挂载目录 +VOLUME /home/dashan +# 创建目录 +RUN mkdir -p /home/dashan +# 指定路径 +WORKDIR /home/dashan +# 复制jar文件到路径 +COPY ./jar/ds-miniapps-service.jar /home/dashan/ds-miniapps-service.jar +#暴漏端口 +EXPOSE 15101 +# 启动网关服务 +ENTRYPOINT ["java","-jar","ds-miniapps-service.jar"] \ No newline at end of file diff --git a/ds-commons/ds-utils/pom.xml b/ds-commons/ds-utils/pom.xml index e4dc142..f5110ab 100644 --- a/ds-commons/ds-utils/pom.xml +++ b/ds-commons/ds-utils/pom.xml @@ -17,4 +17,15 @@ UTF-8 + + + org.springframework.boot + spring-boot-starter-json + + + org.projectlombok + lombok + + + \ No newline at end of file diff --git a/ds-commons/ds-utils/src/main/java/com/ds/commons/utils/json/JsonUtil.java b/ds-commons/ds-utils/src/main/java/com/ds/commons/utils/json/JsonUtil.java new file mode 100644 index 0000000..a934568 --- /dev/null +++ b/ds-commons/ds-utils/src/main/java/com/ds/commons/utils/json/JsonUtil.java @@ -0,0 +1,168 @@ +package com.ds.commons.utils.json; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; + +/** + * JSON 工具类 + */ +@Slf4j +public class JsonUtil { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + // 时间日期格式 + private static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + //以静态代码块初始化 + static { + //对象的所有字段全部列入序列化 + objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); + //取消默认转换timestamps形式 + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + //忽略空Bean转json的错误 + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + //所有的日期格式都统一为以下的格式,即yyyy-MM-dd HH:mm:ss + objectMapper.setDateFormat(new SimpleDateFormat(STANDARD_FORMAT)); + //忽略 在json字符串中存在,但在java对象中不存在对应属性的情况。防止错误 + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + + /**===========================以下是从JSON中获取对象====================================*/ + public static T parseObject(String jsonString, Class object) { + T t = null; + try { + t = objectMapper.readValue(jsonString, object); + } catch (JsonProcessingException e) { + log.error("JsonString转为自定义对象失败:{}", e.getMessage()); + } + return t; + } + + public static T parseObject(File file, Class object) { + T t = null; + try { + t = objectMapper.readValue(file, object); + } catch (IOException e) { + log.error("从文件中读取json字符串转为自定义对象失败:{}", e.getMessage()); + } + return t; + } + + //将json数组字符串转为指定对象List列表或者Map集合 + public static T parseJSONArray(String jsonArray, TypeReference reference) { + T t = null; + try { + t = objectMapper.readValue(jsonArray, reference); + } catch (JsonProcessingException e) { + log.error("JSONArray转为List列表或者Map集合失败:{}", e.getMessage()); + } + return t; + } + + + /**=================================以下是将对象转为JSON=====================================*/ + public static String toJSONString(Object object) { + String jsonString = null; + try { + jsonString = objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + log.error("Object转JSONString失败:{}", e.getMessage()); + } + return jsonString; + } + + public static byte[] toByteArray(Object object) { + byte[] bytes = null; + try { + bytes = objectMapper.writeValueAsBytes(object); + } catch (JsonProcessingException e) { + log.error("Object转ByteArray失败:{}", e.getMessage()); + } + return bytes; + } + + public static void objectToFile(File file, Object object) { + try { + objectMapper.writeValue(file, object); + } catch (JsonProcessingException e) { + log.error("Object写入文件失败:{}", e.getMessage()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /**=============================以下是与JsonNode相关的=======================================*/ + //JsonNode和JSONObject一样,都是JSON树形模型,只不过在jackson中,存在的是JsonNode + public static JsonNode parseJSONObject(String jsonString) { + JsonNode jsonNode = null; + try { + jsonNode = objectMapper.readTree(jsonString); + } catch (JsonProcessingException e) { + log.error("JSONString转为JsonNode失败:{}", e.getMessage()); + } + return jsonNode; + } + + public static JsonNode parseJSONObject(Object object) { + JsonNode jsonNode = objectMapper.valueToTree(object); + return jsonNode; + } + + public static String toJSONString(JsonNode jsonNode) { + String jsonString = null; + try { + jsonString = objectMapper.writeValueAsString(jsonNode); + } catch (JsonProcessingException e) { + log.error("JsonNode转JSONString失败:{}", e.getMessage()); + } + return jsonString; + } + + //JsonNode是一个抽象类,不能实例化,创建JSON树形模型,得用JsonNode的子类ObjectNode,用法和JSONObject大同小异 + public static ObjectNode newJSONObject() { + return objectMapper.createObjectNode(); + } + + //创建JSON数组对象,就像JSONArray一样用 + public static ArrayNode newJSONArray() { + return objectMapper.createArrayNode(); + } + + + /**===========以下是从JsonNode对象中获取key值的方法,个人觉得有点多余,直接用JsonNode自带的取值方法会好点,出于纠结症,还是补充进来了*/ + public static String getString(JsonNode jsonObject, String key) { + String s = jsonObject.get(key).asText(); + return s; + } + + public static Integer getInteger(JsonNode jsonObject, String key) { + Integer i = jsonObject.get(key).asInt(); + return i; + } + + public static Boolean getBoolean(JsonNode jsonObject, String key) { + Boolean bool = jsonObject.get(key).asBoolean(); + return bool; + } + + public static JsonNode getJSONObject(JsonNode jsonObject, String key) { + JsonNode json = jsonObject.get(key); + return json; + } +} + diff --git a/ds-gateway/pom.xml b/ds-gateway/pom.xml index bd12544..5ef80a4 100644 --- a/ds-gateway/pom.xml +++ b/ds-gateway/pom.xml @@ -30,6 +30,16 @@ org.springframework.cloud spring-cloud-loadbalancer + + org.projectlombok + lombok + + + com.ds + ds-utils + 0.0.1-SNAPSHOT + compile + ${project.artifactId} diff --git a/ds-gateway/src/main/java/com/ds/gateway/filter/SwaggerGlobalFilter.java b/ds-gateway/src/main/java/com/ds/gateway/filter/SwaggerGlobalFilter.java new file mode 100644 index 0000000..e27fc8d --- /dev/null +++ b/ds-gateway/src/main/java/com/ds/gateway/filter/SwaggerGlobalFilter.java @@ -0,0 +1,106 @@ +package com.ds.gateway.filter; + +import com.ds.commons.utils.json.JsonUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.http.server.reactive.ServerHttpResponseDecorator; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * 因为swagger配置在业务服务中,默认的url会比网关少一层; + * 这个过滤器主要给url添加上此网关这一层,并且设置到swagger的basePath中,使其可以正常通过swagger调用接口 + */ +@Slf4j +@Component +public class SwaggerGlobalFilter implements GlobalFilter, Ordered { + + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String path = request.getPath().toString(); + if (!path.endsWith("/v3/api-docs/swagger-config")) { + return chain.filter(exchange); + } + String[] pathArray = path.split("/"); + String basePath = pathArray[1]; + ServerHttpResponse originalResponse = exchange.getResponse(); + // 定义新的消息头 + ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { + @Override + @NonNull + public Mono writeWith(@NonNull Publisher body) { + if (HttpStatus.OK.equals(super.getStatusCode()) && body instanceof Flux) { + Flux fluxBody = Flux.from(body); + return super.writeWith(fluxBody.buffer().map(dataBuffers -> { + List list = new ArrayList<>(); + dataBuffers.forEach(dataBuffer -> { + byte[] content = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(content); + DataBufferUtils.release(dataBuffer); + list.add(new String(content, StandardCharsets.UTF_8)); + }); + String s = this.listToString(list); + //获取到/v3/api-docs/swagger-config返回的json串 给文档url添加上网关层url + ObjectNode jsonNodes = JsonUtil.parseObject(s,ObjectNode.class); + JsonNode urls = jsonNodes.get("urls"); + urls.elements().forEachRemaining(item-> ((ObjectNode)item).put("url","/"+basePath+item.get("url").asText())); + + //设置basePath + jsonNodes.put("basePath", basePath); + s = jsonNodes.toString(); + // 设置更新后的header请求头长度 + int length = s.getBytes().length; + HttpHeaders headers = originalResponse.getHeaders(); + headers.setContentLength(length); + return bufferFactory().wrap(s.getBytes(StandardCharsets.UTF_8)); + })); + } + return super.writeWith(body); + } + + @Override + @NonNull + public HttpHeaders getHeaders() { + // 获取父类原始ServerHttpResponse的header请求头信息,这是代理Delegate类型 + HttpHeaders httpHeaders = super.getHeaders(); + httpHeaders.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8"); + return httpHeaders; + } + + private String listToString(List list) { + StringBuilder stringBuilder = new StringBuilder(); + for (String s : list) { + stringBuilder.append(s); + } + return stringBuilder.toString(); + } + }; + + // replace response with decorator + return chain.filter(exchange.mutate().response(decoratedResponse).build()); + } + + @Override + public int getOrder() { + return -2; + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a0df477..00a7caf 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ dev - http://server:8848 + http://ds-nacos:8848 @@ -154,7 +154,7 @@ ca82f95e-8b1b-4c59-b12e-035444e8c454 - http://server:8848 + http://ds-nacos:8848 @@ -164,7 +164,7 @@ prod 7c2f9c5d-88e9-449d-a78e-bcea0e7ada59 - http://server:8848 + http://ds-nacos:8848