From 6212311f6894e6da55853b61718b493774035da0 Mon Sep 17 00:00:00 2001 From: raoxiaoyan Date: Tue, 1 Dec 2020 18:07:50 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E7=89=88=E6=9C=AC=E5=8F=B7=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA2.1.0=202=E3=80=81=E5=A2=9E=E5=8A=A0=E7=BD=91?= =?UTF-8?q?=E5=85=B3=E6=B5=81=E6=B0=B4=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++- docs/pipeline.png | Bin 0 -> 65926 bytes kongx-common/pom.xml | 2 +- .../common/jsonwrapper/JsonHeaderWrapper.java | 4 +- kongx-serve/pom.xml | 4 +- .../flow/ServicePipelineController.java | 153 +++++++++++++++++- .../controller/gateway/TargetController.java | 3 +- .../controller/system/DefaultController.java | 2 +- .../com/kongx/serve/entity/flow/NodeMeta.java | 2 + .../serve/entity/flow/ServicePipeline.java | 1 + .../feign/TruncateEntityFeignService.java | 17 ++ .../serve/mapper/ServicePipelineMapper.java | 11 +- .../service/flow/ServicePipelineService.java | 23 ++- .../gateway/TruncateEntityService.java | 37 +++++ pom.xml | 2 +- 15 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 docs/pipeline.png create mode 100644 kongx-serve/src/main/java/com/kongx/serve/feign/TruncateEntityFeignService.java create mode 100644 kongx-serve/src/main/java/com/kongx/serve/service/gateway/TruncateEntityService.java diff --git a/README.md b/README.md index 47e4e57..42d738d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # kongx -kongx(最新版本2.0.1)是网关kong的可视化界面管理平台(参考konga的部分界面布局方式),能够集中化管理应用不同环境的网关配置,提供同步各环境的网关配置功能,并且具备规范的权限管理、参数配置、环境管理及日志审计等特性。 +kongx(最新版本2.1.0)是网关kong的可视化界面管理平台(参考konga的部分界面布局方式),能够集中化管理应用不同环境的网关配置,提供同步各环境的网关配置功能,并且具备规范的权限管理、参数配置、环境管理及日志审计等特性。 基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器,支持在线Shell。 @@ -11,6 +11,8 @@ Kongx 使用指南请参考:[Wiki](https://www.kancloud.cn/raoxiaoyan/kongx/19 Docker部署(2.0.0+支持)请参见[Docker Quick Start](./docker-quick-start/readme.md) +可视化设计网关pipeline使用文档请参考[网关流水线]() + 演示地址:http://49.232.174.106/ (用户名: guest/123456) 系统环境默认用户:admin/123456(部署登录后,请前往'个人设置'页面,及时修改密码) @@ -19,12 +21,10 @@ Docker部署(2.0.0+支持)请参见[Docker Quick Start](./docker-quick-start/rea ![](./docs/kong%20shell.png) -![](./docs/services.png) - -![](./docs/service1.png) - ![](./docs/service2.png) +![可视化网关流水线](./docs/pipeline.png) + ![](./docs/consumers.png) ![](./docs/certificate.png) @@ -33,6 +33,7 @@ Docker部署(2.0.0+支持)请参见[Docker Quick Start](./docker-quick-start/rea ## Features - Kong Manage:Upstream,Service,Route,Plugin,Consumer,Certificates及Ca Certificates等 +- 新增网关流水线 - 同步Kong配置:不同环境间的kong配置进行同步,便于多环境配置管理; - 系统管理:具有完善的权限管理系统,包括:用户管理、菜单管理、角色管理及用户组管理等功能; - 参数管理:具有良好的扩展性,基于平台的参数管理可扩展多环境及服务管理;包括:环境管理、参数参数等 @@ -48,6 +49,8 @@ Docker部署(2.0.0+支持)请参见[Docker Quick Start](./docker-quick-start/rea - [Kong与consul自主发现服务](https://www.kancloud.cn/raoxiaoyan/kongx/1984357) - 如何应用灰度插件(canary)及使用场景介绍 - kong插件开发实践 +- [kong的健康检查及熔断](https://www.kancloud.cn/raoxiaoyan/kongx/2044771) +- kong网关流水线 - 整理中...尽情期待 @@ -69,8 +72,8 @@ Docker部署(2.0.0+支持)请参见[Docker Quick Start](./docker-quick-start/rea - tech-support-qq-1 - cooperate + tech-support-qq-1 + cooperate @@ -81,6 +84,7 @@ Docker部署(2.0.0+支持)请参见[Docker Quick Start](./docker-quick-start/rea | --- | --- | --- | --- | | 1 | 1.2.x | 1.2.x | 1.2.x测试| | 1 | 2.0.0 | 1.2.x、1.3.x、1.4.x、1.5.x、2.0.x | 目前仅针对kong版本1.2.x、1.3.x测试通过,对1.4.x以上版本与1.3.x对比差异,原则上基础功能全部可用,(参考差异对比)[https://www.kancloud.cn/raoxiaoyan/kongx/1991178]| +| 1 | 2.1.0 | 1.2.x、1.3.x、1.4.x、1.5.x、2.0.x | 目前仅针对kong版本1.2.x、1.3.x测试通过,对1.4.x以上版本与1.3.x对比差异,原则上基础功能全部可用,(参考差异对比)[https://www.kancloud.cn/raoxiaoyan/kongx/1991178]| ## Upgrade [更新历史](docs/upgrade.md) diff --git a/docs/pipeline.png b/docs/pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..c3b4a9a9fb901daddf69f4889a5410141ed8661b GIT binary patch literal 65926 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 kongx com.kongx - 2.0.2 + 2.1.0 4.0.0 diff --git a/kongx-common/src/main/java/com/kongx/common/jsonwrapper/JsonHeaderWrapper.java b/kongx-common/src/main/java/com/kongx/common/jsonwrapper/JsonHeaderWrapper.java index 1f86344..eecba39 100644 --- a/kongx-common/src/main/java/com/kongx/common/jsonwrapper/JsonHeaderWrapper.java +++ b/kongx-common/src/main/java/com/kongx/common/jsonwrapper/JsonHeaderWrapper.java @@ -183,9 +183,7 @@ public class JsonHeaderWrapper { Success(0, "全部成功"), // Success4M(200, "全部成功"), //无线端统一使用200作为成功代码返回 - PartialSuccess(202, "库存数不足"), // - Timeout(405, "接口超时返回"), // - OverFlowCtrl(406, "客户流量超量"), // + NOT_FOUND(404, "not found"), Failed(500, "全部失败"), // ParamError(505, "传入参数错误"), // UnknownOther(599, "未知错误,系统错误"); diff --git a/kongx-serve/pom.xml b/kongx-serve/pom.xml index e173ad1..c879b26 100644 --- a/kongx-serve/pom.xml +++ b/kongx-serve/pom.xml @@ -5,7 +5,7 @@ kongx com.kongx - 2.0.2 + 2.1.0 4.0.0 @@ -14,7 +14,7 @@ com.kongx kongx-common - 2.0.2 + 2.1.0 diff --git a/kongx-serve/src/main/java/com/kongx/serve/controller/flow/ServicePipelineController.java b/kongx-serve/src/main/java/com/kongx/serve/controller/flow/ServicePipelineController.java index ff05a83..e9008fe 100644 --- a/kongx-serve/src/main/java/com/kongx/serve/controller/flow/ServicePipelineController.java +++ b/kongx-serve/src/main/java/com/kongx/serve/controller/flow/ServicePipelineController.java @@ -1,28 +1,175 @@ package com.kongx.serve.controller.flow; +import com.fasterxml.jackson.core.type.TypeReference; +import com.kongx.common.core.entity.UserInfo; +import com.kongx.common.jsonwrapper.JsonHeaderWrapper; +import com.kongx.common.utils.Jackson2Helper; import com.kongx.serve.controller.system.DefaultController; +import com.kongx.serve.entity.flow.FlowNode; +import com.kongx.serve.entity.flow.NodeMeta; import com.kongx.serve.entity.flow.ServicePipeline; +import com.kongx.serve.entity.gateway.KongEntity; +import com.kongx.serve.entity.gateway.TargetHealth; import com.kongx.serve.entity.system.OperationLog; +import com.kongx.serve.entity.system.SystemProfile; import com.kongx.serve.service.IBaseService; import com.kongx.serve.service.flow.ServicePipelineService; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import com.kongx.serve.service.gateway.TargetService; +import com.kongx.serve.service.gateway.TruncateEntityService; +import com.kongx.serve.service.system.SystemProfileService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @RestController("/ServiceFlowController") @RequestMapping("/kong/api/pipeline") public class ServicePipelineController extends DefaultController { - @Resource(name = "servicePipelineService") + @Autowired + private TruncateEntityService truncateEntityService; + @Autowired private ServicePipelineService servicePipelineService; + @Autowired + private TargetService targetService; + + @Autowired + private SystemProfileService systemProfileService; + @Override @Resource(name = "servicePipelineService") protected void setBaseService(IBaseService iBaseService) { this.baseService = iBaseService; } + @RequestMapping(value = "/list/profile", method = RequestMethod.GET) + public List findAll(UserInfo userInfo, ServicePipeline project) { + return servicePipelineService.findAll(this.systemProfile(userInfo), project); + } + + @RequestMapping(path = "/truncate/entity", method = RequestMethod.POST) + public JsonHeaderWrapper removeEntity(UserInfo userInfo, @RequestBody FlowNode flowNode) { + JsonHeaderWrapper jsonHeaderWrapper = this.init(); + try { + if (!flowNode.getMeta().isReady()) { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.Failed.getCode()); + jsonHeaderWrapper.setErrmsg("该实体未进行配置,请检查后再试!"); + return jsonHeaderWrapper; + } + this.truncateEntityService.remove(this.systemProfile(userInfo), this.wrapUri(flowNode)); + } catch (Exception e) { + String content = e.getMessage(); + if (content.contains("Not found")) { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.NOT_FOUND.getCode()); + } else { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.Failed.getCode()); + } + jsonHeaderWrapper.setErrmsg(content); + } + return jsonHeaderWrapper; + } + + @RequestMapping(path = "/{id}", method = RequestMethod.GET) + @Override + public JsonHeaderWrapper findById(UserInfo userInfo, @PathVariable Integer id) { + JsonHeaderWrapper jsonHeaderWrapper = init(); + try { + ServicePipeline servicePipeline = this.servicePipelineService.findById(id); + SystemProfile systemProfile = this.systemProfileService.findByProfile(servicePipeline.getProfile()); + List flowNodeList = Jackson2Helper.jsonToList(Jackson2Helper.toJsonString(servicePipeline.getNodeList()), FlowNode.class); + KongEntity kongEntity = null; + for (FlowNode flowNode : flowNodeList) { + if (flowNode.getMeta().isReady()) { + this.wrapFlowNode(systemProfile, flowNode, kongEntity); + } + } + servicePipeline.setNodeList(flowNodeList); + jsonHeaderWrapper.setData(servicePipeline); + } catch (Exception e) { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.Failed.getCode()); + jsonHeaderWrapper.setErrmsg(e.getMessage()); + } + return jsonHeaderWrapper; + } + + private FlowNode wrapFlowNode(SystemProfile systemProfile, FlowNode flowNode, KongEntity kongEntity) { + try { + if (flowNode.getMeta().getProp().equals("targets")) { + if (kongEntity == null) { + kongEntity = Jackson2Helper.parsonObject( + Jackson2Helper.toJsonString(this.targetService.findAllHealth(systemProfile, flowNode.getMeta().getParent().getId())), + new TypeReference>() { + }); + } + for (TargetHealth datum : kongEntity.getData()) { + if (datum.getId().equals(flowNode.getMeta().getId())) { + flowNode.getMeta().setEntity(Jackson2Helper.parsonObject(Jackson2Helper.toJsonString(datum), new TypeReference() { + })); + return flowNode; + } + } + flowNode.getMeta().setReady(false); + + Map parent = new HashMap(); + parent.put("id", flowNode.getMeta().getParent().getId()); + flowNode.getMeta().setEntity(parent); + } else { + String uri = this.wrapUri(flowNode); + flowNode.getMeta().setEntity(this.truncateEntityService.findById(systemProfile, uri)); + } + } catch (Exception e) { + flowNode.getMeta().setReady(false); + if (flowNode.getMeta().getParent() != null) { + Map parent = new HashMap(); + parent.put("id", flowNode.getMeta().getParent().getId()); + flowNode.getMeta().setEntity(parent); + } + + } + return flowNode; + } + + private String wrapUri(FlowNode flowNode) { + String url = flowNode.getMeta().getProp(); + String id = flowNode.getMeta().getId(); + if ("targets".equals(url)) { + NodeMeta parent = flowNode.getMeta().getParent(); + url = "/" + parent.getProp() + "/%s/" + url + "/%s"; + url = String.format(url, parent.getId(), id); + } else { + url = "/" + url + "/%s"; + url = String.format(url, id); + } + return url; + } + + @RequestMapping(path = "/query/entity", method = RequestMethod.POST) + public JsonHeaderWrapper findEntityById(UserInfo userInfo, @RequestBody FlowNode flowNode) { + JsonHeaderWrapper jsonHeaderWrapper = this.init(); + try { + if (!flowNode.getMeta().isReady()) { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.Failed.getCode()); + jsonHeaderWrapper.setErrmsg("该实体未进行配置,请检查后再试!"); + return jsonHeaderWrapper; + } + jsonHeaderWrapper.setData(this.truncateEntityService.findById(this.systemProfile(userInfo), this.wrapUri(flowNode))); + } catch (Exception e) { + String content = e.getMessage(); + if (content.contains("Not found")) { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.NOT_FOUND.getCode()); + jsonHeaderWrapper.setErrmsg("该实体可能已被移除,请检查后再试!"); + } else { + jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.Failed.getCode()); + jsonHeaderWrapper.setErrmsg(content); + } + } + return jsonHeaderWrapper; + } + @Override protected OperationLog.OperationTarget operationTarget() { return OperationLog.OperationTarget.SERVICE_PIPELINE; diff --git a/kongx-serve/src/main/java/com/kongx/serve/controller/gateway/TargetController.java b/kongx-serve/src/main/java/com/kongx/serve/controller/gateway/TargetController.java index 797e0fa..745d574 100644 --- a/kongx-serve/src/main/java/com/kongx/serve/controller/gateway/TargetController.java +++ b/kongx-serve/src/main/java/com/kongx/serve/controller/gateway/TargetController.java @@ -62,7 +62,6 @@ public class TargetController extends BaseController { try { jsonHeaderWrapper.setData(this.targetFeignService.add(systemProfile(userInfo), id, target)); } catch (Exception e) { - e.printStackTrace(); jsonHeaderWrapper.setStatus(JsonHeaderWrapper.StatusEnum.Failed.getCode()); jsonHeaderWrapper.setErrmsg(e.getMessage()); } @@ -77,7 +76,7 @@ public class TargetController extends BaseController { * @throws URISyntaxException */ @RequestMapping(value = TARGET_URI_ID_PATH, method = RequestMethod.DELETE) - @KongLog(target = OperationLog.OperationTarget.TARGETS, content = "#id 从属于上游服务 #upstreamId") + @KongLog(target = OperationLog.OperationTarget.TARGETS, content = "#id") public JsonHeaderWrapper remove(UserInfo userInfo, @PathVariable String upstreamId, @PathVariable String id) { JsonHeaderWrapper jsonHeaderWrapper = init(); try { diff --git a/kongx-serve/src/main/java/com/kongx/serve/controller/system/DefaultController.java b/kongx-serve/src/main/java/com/kongx/serve/controller/system/DefaultController.java index e77dce1..baede20 100644 --- a/kongx-serve/src/main/java/com/kongx/serve/controller/system/DefaultController.java +++ b/kongx-serve/src/main/java/com/kongx/serve/controller/system/DefaultController.java @@ -67,7 +67,7 @@ public abstract class DefaultController extends BaseController { } @RequestMapping(path = "/{id}", method = RequestMethod.GET) - public JsonHeaderWrapper findById(@PathVariable Integer id) { + public JsonHeaderWrapper findById(UserInfo userInfo, @PathVariable Integer id) { JsonHeaderWrapper jsonHeaderWrapper = init(); try { jsonHeaderWrapper.setData(this.baseService.findById(id)); diff --git a/kongx-serve/src/main/java/com/kongx/serve/entity/flow/NodeMeta.java b/kongx-serve/src/main/java/com/kongx/serve/entity/flow/NodeMeta.java index 769817e..73b7eb2 100644 --- a/kongx-serve/src/main/java/com/kongx/serve/entity/flow/NodeMeta.java +++ b/kongx-serve/src/main/java/com/kongx/serve/entity/flow/NodeMeta.java @@ -8,6 +8,8 @@ import java.util.Map; public class NodeMeta { private Map entity; private String id; + private NodeMeta parent; private String name; private String prop; + private boolean ready; } diff --git a/kongx-serve/src/main/java/com/kongx/serve/entity/flow/ServicePipeline.java b/kongx-serve/src/main/java/com/kongx/serve/entity/flow/ServicePipeline.java index 67a3a05..3161d8c 100644 --- a/kongx-serve/src/main/java/com/kongx/serve/entity/flow/ServicePipeline.java +++ b/kongx-serve/src/main/java/com/kongx/serve/entity/flow/ServicePipeline.java @@ -14,4 +14,5 @@ public class ServicePipeline extends BaseEntity { private List linkList = new ArrayList(); private List nodeList = new ArrayList(); private List origin = new ArrayList(); + private String profile; } diff --git a/kongx-serve/src/main/java/com/kongx/serve/feign/TruncateEntityFeignService.java b/kongx-serve/src/main/java/com/kongx/serve/feign/TruncateEntityFeignService.java new file mode 100644 index 0000000..a0a8039 --- /dev/null +++ b/kongx-serve/src/main/java/com/kongx/serve/feign/TruncateEntityFeignService.java @@ -0,0 +1,17 @@ +package com.kongx.serve.feign; + +import feign.RequestLine; +import org.springframework.cloud.openfeign.FeignClient; + +import java.net.URI; +import java.util.Map; + +@FeignClient(name = "truncateEntityFeignService") +public interface TruncateEntityFeignService { + + @RequestLine("GET") + Map findById(URI uri); + + @RequestLine("DELETE") + void remove(URI uri); +} diff --git a/kongx-serve/src/main/java/com/kongx/serve/mapper/ServicePipelineMapper.java b/kongx-serve/src/main/java/com/kongx/serve/mapper/ServicePipelineMapper.java index 4c4e1c9..d4daa9f 100644 --- a/kongx-serve/src/main/java/com/kongx/serve/mapper/ServicePipelineMapper.java +++ b/kongx-serve/src/main/java/com/kongx/serve/mapper/ServicePipelineMapper.java @@ -2,13 +2,14 @@ package com.kongx.serve.mapper; import com.kongx.common.handler.JSONHandler; import com.kongx.serve.entity.flow.ServicePipeline; +import com.kongx.serve.entity.system.SystemProfile; import org.apache.ibatis.annotations.*; import java.util.List; @Mapper public interface ServicePipelineMapper { - @Select({"