fix: 多服务名的时候有问题,加个Master类搞定

This commit is contained in:
Wendal Chen 2020-04-21 16:24:17 +08:00
parent 7c149b3cb4
commit e7f17224c4
8 changed files with 205 additions and 181 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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();

View File

@ -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";
}
}

View File

@ -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";
}
}