Bläddra i källkod

:tada: 安排基于nacos的动态网关功能

smallchill 7 år sedan
förälder
incheckning
72904de35c

+ 41 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/model/GatewayFilter.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic.model;
+
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 过滤器定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayFilter {
+
+	/**
+	 * 过滤器对应的Name
+	 */
+	private String name;
+
+	/**
+	 * 对应的路由规则
+	 */
+	private Map<String, String> args = new LinkedHashMap<>();
+}

+ 41 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/model/GatewayPredicate.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic.model;
+
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 路由断言定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayPredicate {
+
+	/**
+	 * 断言对应的Name
+	 */
+	private String name;
+
+	/**
+	 * 配置的断言规则
+	 */
+	private Map<String, String> args = new LinkedHashMap<>();
+}

+ 57 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/model/GatewayRoute.java

@@ -0,0 +1,57 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic.model;
+
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Gateway的路由定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayRoute {
+
+	/**
+	 * 路由的id
+	 */
+	private String id;
+
+	/**
+	 * 路由断言集合配置
+	 */
+	private List<GatewayPredicate> predicates = new ArrayList<>();
+
+	/**
+	 * 路由过滤器集合配置
+	 */
+	private List<GatewayFilter> filters = new ArrayList<>();
+
+	/**
+	 * 路由规则转发的目标uri
+	 */
+	private String uri;
+
+	/**
+	 * 路由执行的顺序
+	 */
+	private int order = 0;
+}

+ 101 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/service/DynamicRouteService.java

@@ -0,0 +1,101 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic.service;
+
+import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+/**
+ * 动态路由业务类
+ *
+ * @author Chill
+ */
+@Service
+public class DynamicRouteService implements ApplicationEventPublisherAware {
+
+	private final RouteDefinitionWriter routeDefinitionWriter;
+
+	private ApplicationEventPublisher publisher;
+
+	public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter) {
+		this.routeDefinitionWriter = routeDefinitionWriter;
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+		this.publisher = applicationEventPublisher;
+	}
+
+	/**
+	 * 增加路由
+	 */
+	public String save(RouteDefinition definition) {
+		try {
+			routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+			this.publisher.publishEvent(new RefreshRoutesEvent(this));
+			return "save success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "save failure";
+		}
+	}
+
+	/**
+	 * 更新路由
+	 */
+	public String update(RouteDefinition definition) {
+		try {
+			this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
+			this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+			this.publisher.publishEvent(new RefreshRoutesEvent(this));
+			return "update success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "update failure";
+		}
+	}
+
+	/**
+	 * 更新路由
+	 */
+	public String updateList(List<RouteDefinition> routeDefinitions) {
+		routeDefinitions.forEach(this::update);
+		return "update done";
+	}
+
+	/**
+	 * 删除路由
+	 */
+	public String delete(String id) {
+		try {
+			this.routeDefinitionWriter.delete(Mono.just(id));
+			return "delete success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "delete failure";
+		}
+	}
+
+
+}

+ 85 - 0
blade-gateway/src/main/java/org/springblade/gateway/dynamic/service/DynamicRouteServiceListener.java

@@ -0,0 +1,85 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.config.listener.Listener;
+import com.alibaba.nacos.api.exception.NacosException;
+import org.springblade.core.launch.constant.NacosConstant;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
+import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * 动态路由监听器
+ *
+ * @author Chill
+ */
+@Order
+@Component
+public class DynamicRouteServiceListener {
+
+	private final DynamicRouteService dynamicRouteService;
+	private final NacosDiscoveryProperties nacosDiscoveryProperties;
+	private final NacosConfigProperties nacosConfigProperties;
+	private final BladeProperties bladeProperties;
+
+	public DynamicRouteServiceListener(DynamicRouteService dynamicRouteService, NacosDiscoveryProperties nacosDiscoveryProperties, NacosConfigProperties nacosConfigProperties, BladeProperties bladeProperties) {
+		this.dynamicRouteService = dynamicRouteService;
+		this.nacosDiscoveryProperties = nacosDiscoveryProperties;
+		this.nacosConfigProperties = nacosConfigProperties;
+		this.bladeProperties = bladeProperties;
+		dynamicRouteServiceListener();
+	}
+
+	/**
+	 * 监听Nacos下发的动态路由配置
+	 */
+	private void dynamicRouteServiceListener() {
+		try {
+			String dataId = NacosConstant.dataId(bladeProperties.getName(), bladeProperties.getEnv(), NacosConstant.NACOS_CONFIG_JSON_FORMAT);
+			String group = nacosConfigProperties.getGroup();
+			String serverAddr = nacosDiscoveryProperties.getServerAddr();
+			ConfigService configService = NacosFactory.createConfigService(serverAddr);
+			String content = configService.getConfig(dataId, group, 5000);
+			System.out.println(content);
+			configService.addListener(dataId, group, new Listener() {
+				@Override
+				public void receiveConfigInfo(String configInfo) {
+					List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
+					dynamicRouteService.updateList(routeDefinitions);
+				}
+
+				@Override
+				public Executor getExecutor() {
+					return null;
+				}
+			});
+		} catch (NacosException ignored) {
+
+		}
+	}
+
+}

+ 2 - 2
blade-gateway/src/main/java/org/springblade/gateway/controller/DiscoveryClientController.java → blade-gateway/src/main/java/org/springblade/gateway/endpoint/DiscoveryEndpoint.java

@@ -14,7 +14,7 @@
  *  this software without specific prior written permission.
  *  Author: Chill 庄骞 (smallchill@163.com)
  */
-package org.springblade.gateway.controller;
+package org.springblade.gateway.endpoint;
 
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -37,7 +37,7 @@ import java.util.Map;
 @RestController
 @AllArgsConstructor
 @RequestMapping("/discovery")
-public class DiscoveryClientController {
+public class DiscoveryEndpoint {
 
 	private final DiscoveryClient discoveryClient;
 

+ 86 - 0
blade-gateway/src/main/java/org/springblade/gateway/endpoint/RouteEndpoint.java

@@ -0,0 +1,86 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.endpoint;
+
+import lombok.AllArgsConstructor;
+import org.springblade.gateway.dynamic.model.GatewayPredicate;
+import org.springblade.gateway.dynamic.model.GatewayRoute;
+import org.springblade.gateway.dynamic.service.DynamicRouteService;
+import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Flux;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 动态路由端点
+ *
+ * @author Chill
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/route")
+public class RouteEndpoint {
+
+	private DynamicRouteService dynamicRouteService;
+	private RouteDefinitionLocator routeDefinitionLocator;
+
+	@GetMapping("/list")
+	public Flux<RouteDefinition> list() {
+		return routeDefinitionLocator.getRouteDefinitions();
+	}
+
+	@PostMapping("/save")
+	public String save(@RequestBody GatewayRoute gatewayRoute) {
+		RouteDefinition definition = assembleRouteDefinition(gatewayRoute);
+		return this.dynamicRouteService.save(definition);
+	}
+
+	@PostMapping("/update")
+	public String update(@RequestBody GatewayRoute gatewayRoute) {
+		RouteDefinition definition = assembleRouteDefinition(gatewayRoute);
+		return this.dynamicRouteService.update(definition);
+	}
+
+	@GetMapping("/delete/{id}")
+	public String delete(@PathVariable String id) {
+		return this.dynamicRouteService.delete(id);
+	}
+
+	private RouteDefinition assembleRouteDefinition(GatewayRoute gatewayRoute) {
+		RouteDefinition definition = new RouteDefinition();
+		List<PredicateDefinition> pdList = new ArrayList<>();
+		definition.setId(gatewayRoute.getId());
+		List<GatewayPredicate> gatewayPredicateDefinitionList = gatewayRoute.getPredicates();
+		for (GatewayPredicate gpDefinition : gatewayPredicateDefinitionList) {
+			PredicateDefinition predicate = new PredicateDefinition();
+			predicate.setArgs(gpDefinition.getArgs());
+			predicate.setName(gpDefinition.getName());
+			pdList.add(predicate);
+		}
+		definition.setPredicates(pdList);
+		URI uri = UriComponentsBuilder.fromHttpUrl(gatewayRoute.getUri()).build().toUri();
+		definition.setUri(uri);
+		return definition;
+	}
+
+}

+ 1 - 0
doc/nacos/routes/README.md

@@ -0,0 +1 @@
+动态网关配置

+ 28 - 0
doc/nacos/routes/blade-gateway-dev.json

@@ -0,0 +1,28 @@
+[
+  {
+    "id": "desk-route",
+    "order": 0,
+    "predicates": [
+      {
+        "name": "Path",
+        "args": {
+          "pattern": "/blade-desk/**"
+        }
+      }
+    ],
+    "filters": [],
+    "uri": "lb://blade-desk-me"
+  },
+  {
+    "id": "example-route",
+    "order": 0,
+    "predicates": [{
+      "name": "Path",
+      "args": {
+        "pattern": "/example"
+      }
+    }],
+    "filters": [],
+    "uri": "http://www.example.com"
+  }
+]