mirror of
https://gitee.com/nutz/nutzboot.git
synced 2024-12-02 03:38:08 +08:00
fix: 多服务名的时候有问题,加个Master类搞定
This commit is contained in:
parent
7c149b3cb4
commit
e7f17224c4
@ -1,7 +1,6 @@
|
||||
package org.nutz.cloud.perca;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
@ -27,17 +26,22 @@ public class PercaServlet extends AsyncMiddleManServlet implements WebServletFac
|
||||
RouteContext ctx = new RouteContext();
|
||||
ctx.setup(clientRequest, proxyResponse);
|
||||
clientRequest.setAttribute(NAME_ROUTE_CONCEXT, ctx);
|
||||
Iterator<RouteFilter> it = routeConfig.getRouteFilters();
|
||||
while (it.hasNext()) {
|
||||
if (!it.next().preRoute(ctx)) {
|
||||
return;
|
||||
}
|
||||
RouterMaster master = null;
|
||||
for (RouterMaster tmp : routeConfig.getRouteMasters()) {
|
||||
if (tmp.match(ctx)) {
|
||||
master = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (master != null) {
|
||||
master.preRoute(ctx);
|
||||
super.service(clientRequest, proxyResponse);
|
||||
return;
|
||||
}
|
||||
if (ctx.targetHost == null && ctx.rewritedTarget == null) {
|
||||
proxyResponse.sendError(404);
|
||||
else {
|
||||
proxyResponse.sendError(404);
|
||||
return;
|
||||
}
|
||||
super.service(clientRequest, proxyResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,14 +1,9 @@
|
||||
package org.nutz.cloud.perca;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.nutz.boot.AppContext;
|
||||
import org.nutz.cloud.perca.impl.LoachRouteFilter;
|
||||
import org.nutz.cloud.perca.impl.NacosRouteFilter;
|
||||
import org.nutz.cloud.perca.impl.OldSimpleRouterFilter;
|
||||
import org.nutz.cloud.perca.impl.SimpleRouteFilter;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
@ -30,48 +25,24 @@ public class RouteConfig {
|
||||
@Inject
|
||||
protected AppContext appContext;
|
||||
|
||||
protected List<RouteFilter> routes = new LinkedList<>();
|
||||
protected List<RouterMaster> masters = new LinkedList<>();
|
||||
|
||||
public Iterator<RouteFilter> getRouteFilters() {
|
||||
return routes.iterator();
|
||||
public List<RouterMaster> getRouteMasters() {
|
||||
return masters;
|
||||
}
|
||||
|
||||
public void init() throws Exception {
|
||||
List<RouterMaster> masters = new LinkedList<>();
|
||||
for (String key : conf.getKeys()) {
|
||||
if (key.startsWith("gw.") && key.endsWith(".filters")) {
|
||||
String name = key.substring("gw.".length(), key.length() - ".filters".length());
|
||||
log.debug("add config for name=" + name);
|
||||
RouteFilter filter = null;
|
||||
String type = conf.get("gw."+name+".type", "simple");
|
||||
switch (type) {
|
||||
case "simple":
|
||||
filter = new SimpleRouteFilter();
|
||||
break;
|
||||
case "loach":
|
||||
filter = new LoachRouteFilter();
|
||||
break;
|
||||
case "nacos":
|
||||
filter = new NacosRouteFilter();
|
||||
break;
|
||||
default:
|
||||
// 可能是类名
|
||||
if (type.indexOf('.') > 0)
|
||||
filter = (RouteFilter) appContext.getClassLoader().loadClass(type).newInstance();
|
||||
// 可能是老代码,虽然不太可能
|
||||
else if (conf.get("gw."+name+".type") == null)
|
||||
filter = new OldSimpleRouterFilter();
|
||||
// 可能是ioc对象
|
||||
else if (type.startsWith("ioc:")) {
|
||||
filter = ioc.get(RouteFilter.class, type.substring(4));
|
||||
}
|
||||
// 啥都不是, 抛异常
|
||||
else
|
||||
throw new RuntimeException("bad gateway filter type");
|
||||
break;
|
||||
}
|
||||
filter.setPropertiesProxy(ioc, conf, key.substring(0, key.length() - ".filters".length()));
|
||||
routes.add(filter);
|
||||
RouterMaster master = new RouterMaster();
|
||||
master.setAppContext(appContext);
|
||||
master.setPropertiesProxy(ioc, conf, "gw." + name);
|
||||
masters.add(master);
|
||||
}
|
||||
}
|
||||
log.debugf("master count=%d", masters.size());
|
||||
this.masters = masters;
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,22 @@
|
||||
package org.nutz.cloud.perca.impl;
|
||||
package org.nutz.cloud.perca;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.nutz.cloud.perca.RouteContext;
|
||||
import org.nutz.cloud.perca.RouteFilter;
|
||||
import org.nutz.boot.AppContext;
|
||||
import org.nutz.cloud.perca.impl.LoachRouteFilter;
|
||||
import org.nutz.cloud.perca.impl.NacosRouteFilter;
|
||||
import org.nutz.cloud.perca.impl.SimpleRouteFilter;
|
||||
import org.nutz.cloud.perca.impl.TargetServerInfo;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
public abstract class AbstractUrlRewriteRouterFilter implements RouteFilter {
|
||||
public class RouterMaster implements Comparable<RouterMaster> {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
@ -26,8 +28,6 @@ public abstract class AbstractUrlRewriteRouterFilter implements RouteFilter {
|
||||
|
||||
protected String serviceName;
|
||||
|
||||
protected String[] servers;
|
||||
|
||||
protected String[] uriPrefixs;
|
||||
|
||||
protected boolean removePrefix;
|
||||
@ -39,6 +39,40 @@ public abstract class AbstractUrlRewriteRouterFilter implements RouteFilter {
|
||||
protected boolean corsEnable;
|
||||
|
||||
protected List<TargetServerInfo> targetServers;
|
||||
|
||||
protected AppContext appContext;
|
||||
|
||||
protected List<RouteFilter> filters;
|
||||
|
||||
protected int priority;
|
||||
|
||||
public void setAppContext(AppContext appContext) {
|
||||
this.appContext = appContext;
|
||||
}
|
||||
|
||||
public boolean match(RouteContext ctx) {
|
||||
// 校验Host
|
||||
if (!checkHost(ctx))
|
||||
return false;
|
||||
// 校验uri前缀
|
||||
if (!checkUriPrefix(ctx))
|
||||
return false;
|
||||
// 校验uri正则表达式
|
||||
if (!checkUriPattern(ctx))
|
||||
return false;
|
||||
// 设置一些必要的超时设置
|
||||
ctx.connectTimeOut = connectTimeOut;
|
||||
ctx.sendTimeOut = sendTimeOut;
|
||||
ctx.readTimeOut = readTimeOut;
|
||||
// TODO 处理跨域
|
||||
return true;
|
||||
}
|
||||
|
||||
public void preRoute(RouteContext ctx) throws IOException {
|
||||
for (RouteFilter filter : filters) {
|
||||
filter.preRoute(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPropertiesProxy(Ioc ioc, PropertiesProxy conf, String prefix) throws Exception {
|
||||
this.name = prefix;
|
||||
@ -47,24 +81,9 @@ public abstract class AbstractUrlRewriteRouterFilter implements RouteFilter {
|
||||
if (!Strings.isBlank(hostnames)) {
|
||||
this.hostnames = Strings.splitIgnoreBlank(hostnames, "(;|,)");
|
||||
}
|
||||
// 固定转发的服务器vip
|
||||
String servers = conf.get(prefix + ".servers");
|
||||
if (!Strings.isBlank(servers)) {
|
||||
this.servers = Strings.splitIgnoreBlank(servers, "(;|,)");
|
||||
List<TargetServerInfo> infos = new ArrayList<TargetServerInfo>();
|
||||
for (String server : this.servers) {
|
||||
String[] tmp = server.split(":");
|
||||
TargetServerInfo info = new TargetServerInfo();
|
||||
info.host = tmp[0];
|
||||
if (tmp.length > 1)
|
||||
info.port = Integer.parseInt(tmp[1]);
|
||||
infos.add(info);
|
||||
}
|
||||
this.targetServers = infos;
|
||||
} else {
|
||||
// 服务名
|
||||
serviceName = conf.get(prefix + ".serviceName");
|
||||
}
|
||||
|
||||
// 服务名
|
||||
serviceName = conf.get(prefix + ".serviceName");
|
||||
|
||||
String uriPrefixs = conf.get(prefix + ".uri.prefixs");
|
||||
if (!Strings.isBlank(uriPrefixs)) {
|
||||
@ -79,49 +98,43 @@ public abstract class AbstractUrlRewriteRouterFilter implements RouteFilter {
|
||||
sendTimeOut = conf.getInt(prefix + ".time.send", 3000);
|
||||
readTimeOut = conf.getInt(prefix + ".time.read", 3000);
|
||||
corsEnable = conf.getBoolean(prefix + ".cors.enable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preRoute(RouteContext ctx) throws IOException {
|
||||
// 校验Host
|
||||
if (!checkHost(ctx))
|
||||
return true;
|
||||
// 校验uri前缀
|
||||
if (!checkUriPrefix(ctx))
|
||||
return true;
|
||||
// 校验uri正则表达式
|
||||
if (!checkUriPattern(ctx))
|
||||
return true;
|
||||
// 设置一些必要的超时设置
|
||||
ctx.connectTimeOut = connectTimeOut;
|
||||
ctx.sendTimeOut = sendTimeOut;
|
||||
ctx.readTimeOut = readTimeOut;
|
||||
// TODO 处理跨域
|
||||
|
||||
if (!selectTargetServer(ctx)) {
|
||||
log.debugf("emtry server list for [%s]", serviceName);
|
||||
ctx.resp.sendError(500);
|
||||
return false; // 终止匹配
|
||||
priority = conf.getInt(prefix + ".priority", 0);
|
||||
|
||||
// 处理拦截器
|
||||
List<RouteFilter> filters = new ArrayList<RouteFilter>();
|
||||
String types = conf.get(prefix+".filters", "simple");
|
||||
for (String type : Strings.splitIgnoreBlank(types)) {
|
||||
RouteFilter filter = null;
|
||||
switch (type) {
|
||||
case "simple":
|
||||
filter = new SimpleRouteFilter();
|
||||
break;
|
||||
case "loach":
|
||||
filter = new LoachRouteFilter();
|
||||
break;
|
||||
case "nacos":
|
||||
filter = new NacosRouteFilter();
|
||||
break;
|
||||
default:
|
||||
// 可能是类名
|
||||
if (type.indexOf('.') > 0)
|
||||
filter = (RouteFilter) appContext.getClassLoader().loadClass(type).newInstance();
|
||||
// 可能是老代码,虽然不太可能
|
||||
//else if (conf.get("gw."+name+".type") == null)
|
||||
// filter = new OldSimpleRouterFilter();
|
||||
// 可能是ioc对象
|
||||
else if (type.startsWith("ioc:")) {
|
||||
filter = ioc.get(RouteFilter.class, type.substring(4));
|
||||
}
|
||||
// 啥都不是, 抛异常
|
||||
else
|
||||
throw new RuntimeException("bad gateway filter type");
|
||||
break;
|
||||
}
|
||||
filter.setPropertiesProxy(ioc, conf, prefix);
|
||||
filters.add(filter);
|
||||
}
|
||||
|
||||
return RouteFilter.super.preRoute(ctx);
|
||||
}
|
||||
|
||||
protected boolean selectTargetServer(RouteContext ctx) {
|
||||
List<TargetServerInfo> infos = this.targetServers;
|
||||
if (infos == null || infos.isEmpty())
|
||||
return false;
|
||||
int index = R.random(0, infos.size() - 1); // 支持各种算法
|
||||
if (index == infos.size()) {
|
||||
index = infos.size() - 1;
|
||||
}
|
||||
TargetServerInfo info = infos.get(index);
|
||||
ctx.targetHost = info.host;
|
||||
if (info.port > 0)
|
||||
ctx.targetPort = info.port;
|
||||
if (log.isDebugEnabled())
|
||||
log.debugf("forward reqest %s to %s:%s", ctx.uri, ctx.targetHost, ctx.targetPort);
|
||||
return true;
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
public boolean checkHost(RouteContext ctx) {
|
||||
@ -165,7 +178,13 @@ public abstract class AbstractUrlRewriteRouterFilter implements RouteFilter {
|
||||
return uriPattern.matcher(ctx.uri).find();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@Override
|
||||
public int compareTo(RouterMaster o) {
|
||||
if (this.priority == o.priority) {
|
||||
return this.name.compareTo(o.name);
|
||||
}
|
||||
|
||||
return Integer.compare(priority, o.priority);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package org.nutz.cloud.perca.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.nutz.cloud.perca.RouteContext;
|
||||
import org.nutz.cloud.perca.RouteFilter;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
public abstract class AbstractServerSelectorFilter implements RouteFilter {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
protected String name;
|
||||
|
||||
protected String serviceName;
|
||||
|
||||
protected List<TargetServerInfo> targetServers;
|
||||
|
||||
public void setPropertiesProxy(Ioc ioc, PropertiesProxy conf, String prefix) throws Exception {
|
||||
this.name = prefix;
|
||||
serviceName = conf.get(prefix + ".serviceName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preRoute(RouteContext ctx) throws IOException {
|
||||
if (!selectTargetServer(ctx)) {
|
||||
log.debugf("emtry server list for [%s]", serviceName);
|
||||
ctx.resp.sendError(500);
|
||||
return false; // 终止匹配
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean selectTargetServer(RouteContext ctx) {
|
||||
List<TargetServerInfo> infos = this.targetServers;
|
||||
if (infos == null || infos.isEmpty())
|
||||
return false;
|
||||
int index = R.random(0, infos.size() - 1); // 支持各种算法
|
||||
if (index == infos.size()) {
|
||||
index = infos.size() - 1;
|
||||
}
|
||||
TargetServerInfo info = infos.get(index);
|
||||
ctx.targetHost = info.host;
|
||||
if (info.port > 0)
|
||||
ctx.targetPort = info.port;
|
||||
if (log.isDebugEnabled())
|
||||
log.debugf("forward reqest %s to %s:%s", ctx.uri, ctx.targetHost, ctx.targetPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import org.nutz.log.Logs;
|
||||
* 从Loach服务器获取目标服务的服务器列表,并转发
|
||||
*
|
||||
*/
|
||||
public class LoachRouteFilter extends AbstractUrlRewriteRouterFilter implements LoachClient.UpdateListener {
|
||||
public class LoachRouteFilter extends AbstractServerSelectorFilter implements LoachClient.UpdateListener {
|
||||
|
||||
protected static final Log log = Logs.get();
|
||||
|
||||
|
@ -19,7 +19,7 @@ import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||
* 从nacos服务器获取目标服务的服务器列表,并转发
|
||||
*
|
||||
*/
|
||||
public class NacosRouteFilter extends AbstractUrlRewriteRouterFilter {
|
||||
public class NacosRouteFilter extends AbstractServerSelectorFilter {
|
||||
|
||||
protected static final Log log = Logs.get();
|
||||
|
||||
|
@ -1,50 +0,0 @@
|
||||
package org.nutz.cloud.perca.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.nutz.boot.starter.loach.client.LoachClient;
|
||||
import org.nutz.cloud.perca.RouteContext;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
/**
|
||||
* 兼容最老的代码, 虽然不知道有没有人在用
|
||||
*/
|
||||
public class OldSimpleRouterFilter extends AbstractUrlRewriteRouterFilter {
|
||||
|
||||
protected static final Log log = Logs.get();
|
||||
|
||||
protected LoachClient loachClient;
|
||||
|
||||
public void setPropertiesProxy(Ioc ioc, PropertiesProxy conf, String prefix) throws Exception {
|
||||
super.setPropertiesProxy(ioc, conf, prefix);
|
||||
if (!Strings.isBlank(serviceName)) {
|
||||
loachClient = ioc.get(LoachClient.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean selectTargetServer(RouteContext ctx) {
|
||||
if (servers != null)
|
||||
ctx.targetHost = servers[R.random(0, servers.length)];
|
||||
else {
|
||||
List<NutMap> services = loachClient.getService(serviceName);
|
||||
if (services.isEmpty()) {
|
||||
return false; // 终止匹配
|
||||
}
|
||||
NutMap service = services.get(R.random(0, services.size() - 1));
|
||||
ctx.targetHost = service.getString("vip");
|
||||
ctx.targetPort = service.getInt("port");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return "simple";
|
||||
}
|
||||
}
|
@ -1,28 +1,48 @@
|
||||
package org.nutz.cloud.perca.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
|
||||
/**
|
||||
* 固定服务器列表
|
||||
*
|
||||
*/
|
||||
public class SimpleRouteFilter extends AbstractUrlRewriteRouterFilter {
|
||||
public class SimpleRouteFilter extends AbstractServerSelectorFilter {
|
||||
|
||||
protected static final Log log = Logs.get();
|
||||
protected static final Log log = Logs.get();
|
||||
|
||||
public void setPropertiesProxy(Ioc ioc, PropertiesProxy conf, String prefix) throws Exception {
|
||||
super.setPropertiesProxy(ioc, conf, prefix);
|
||||
if (servers == null || servers.length == 0) {
|
||||
throw new RuntimeException("simple router need services list!! prefix=" + prefix);
|
||||
}
|
||||
}
|
||||
protected String[] servers;
|
||||
|
||||
public String getType() {
|
||||
return "simple";
|
||||
}
|
||||
public void setPropertiesProxy(Ioc ioc, PropertiesProxy conf, String prefix) throws Exception {
|
||||
super.setPropertiesProxy(ioc, conf, prefix);
|
||||
// 固定转发的服务器vip
|
||||
String str_servers = conf.get(prefix + ".servers");
|
||||
if (!Strings.isBlank(str_servers)) {
|
||||
this.servers = Strings.splitIgnoreBlank(str_servers, "(;|,)");
|
||||
List<TargetServerInfo> infos = new ArrayList<TargetServerInfo>();
|
||||
for (String server : this.servers) {
|
||||
String[] tmp = server.split(":");
|
||||
TargetServerInfo info = new TargetServerInfo();
|
||||
info.host = tmp[0];
|
||||
if (tmp.length > 1)
|
||||
info.port = Integer.parseInt(tmp[1]);
|
||||
infos.add(info);
|
||||
}
|
||||
this.targetServers = infos;
|
||||
}
|
||||
if (servers == null || servers.length == 0) {
|
||||
throw new RuntimeException("simple router need services list!! prefix=" + prefix);
|
||||
}
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return "simple";
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user