提交 29d3b8e2 编写于 作者: Y YunaiV

集群容错

上级 066a8624
......@@ -26,6 +26,8 @@ import java.util.List;
/**
* Router. (SPI, Prototype, ThreadSafe)
* <p>
* 路由规则接口
*
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
*
* @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
......@@ -35,6 +37,8 @@ public interface Router extends Comparable<Router> {
/**
* get the router url.
* <p>
* 路由规则 URL
*
* @return url
*/
......@@ -43,10 +47,12 @@ public interface Router extends Comparable<Router> {
/**
* route.
*
* @param invokers
* 路由,筛选匹配的 Invoker 集合
*
* @param invokers Invoker 集合
* @param url refer url
* @param invocation
* @return routed invokers
* @return routed invokers 路由后的 Invoker 集合
* @throws RpcException
*/
<T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
......
......@@ -23,7 +23,9 @@ import com.alibaba.dubbo.rpc.Invocation;
/**
* RouterFactory. (SPI, Singleton, ThreadSafe)
* <p>
*
* Router 工厂接口
*
* <a href="http://en.wikipedia.org/wiki/Routing">Routing</a>
*
* @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
......@@ -35,6 +37,8 @@ public interface RouterFactory {
/**
* Create router.
*
* 创建 Router 对象
*
* @param url
* @return router
*/
......
......@@ -23,8 +23,9 @@ import com.alibaba.dubbo.common.extension.SPI;
import java.util.List;
@SPI
@Deprecated // add by 芋艿,实际未使用
public interface RuleConverter {
List<URL> convert(URL subscribeUrl, Object source);
}
}
\ No newline at end of file
......@@ -137,11 +137,11 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
return routers;
}
// 【TODO 8036】
protected void setRouters(List<Router> routers) {
// copy list
// copy list // 复制 routers ,因为下面要修改
routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
// append url router
// 拼接 `url` 中,配置的路由规则
String routerkey = url.getParameter(Constants.ROUTER_KEY);
if (routerkey != null && routerkey.length() > 0) {
RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
......@@ -149,7 +149,9 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
}
// append mock invoker selector
routers.add(new MockInvokersSelector());
// 排序
Collections.sort(routers);
// 赋值给属性
this.routers = routers;
}
......
......@@ -33,26 +33,33 @@ import java.util.List;
*/
public class MockInvokersSelector implements Router {
public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,
URL url, final Invocation invocation) throws RpcException {
@Override
public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers, URL url, final Invocation invocation) throws RpcException {
// 获得普通 Invoker 集合
if (invocation.getAttachments() == null) {
return getNormalInvokers(invokers);
} else {
// 获得 "invocation.need.mock" 配置项
String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
if (value == null)
// 获得普通 Invoker 集合
if (value == null) {
return getNormalInvokers(invokers);
else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
// 获得 MockInvoker 集合
} else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
return getMockedInvokers(invokers);
}
}
// 其它,不匹配,直接返回 `invokers` 集合
return invokers;
}
private <T> List<Invoker<T>> getMockedInvokers(final List<Invoker<T>> invokers) {
// 不包含 MockInvoker 的情况下,直接返回 null
if (!hasMockProviders(invokers)) {
return null;
}
List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(1);
// 过滤掉普通 kInvoker ,创建 MockInvoker 集合
List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(1); // 一般情况就一个,所以设置了默认数组大小为 1 。
for (Invoker<T> invoker : invokers) {
if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
sInvokers.add(invoker);
......@@ -62,9 +69,11 @@ public class MockInvokersSelector implements Router {
}
private <T> List<Invoker<T>> getNormalInvokers(final List<Invoker<T>> invokers) {
// 不包含 MockInvoker 的情况下,直接返回 `invokers` 集合
if (!hasMockProviders(invokers)) {
return invokers;
} else {
// 若包含 MockInvoker 的情况下,过滤掉 MockInvoker ,创建普通 Invoker 集合
List<Invoker<T>> sInvokers = new ArrayList<Invoker<T>>(invokers.size());
for (Invoker<T> invoker : invokers) {
if (!invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
......@@ -78,7 +87,7 @@ public class MockInvokersSelector implements Router {
private <T> boolean hasMockProviders(final List<Invoker<T>> invokers) {
boolean hasMockProvider = false;
for (Invoker<T> invoker : invokers) {
if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) {
if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL)) { // 协议为 "mock"
hasMockProvider = true;
break;
}
......@@ -86,10 +95,12 @@ public class MockInvokersSelector implements Router {
return hasMockProvider;
}
@Override
public URL getUrl() {
return null;
}
@Override
public int compareTo(Router o) {
return 1;
}
......
......@@ -41,15 +41,39 @@ import java.util.regex.Pattern;
/**
* ConditionRouter
*
* 条件 Router 实现类
*/
public class ConditionRouter implements Router, Comparable<Router> {
public class ConditionRouter implements Router {
private static final Logger logger = LoggerFactory.getLogger(ConditionRouter.class);
/**
* 分组正则匹配,详细见 {@link #parseRule(String)} 方法
*
* 前 [] 为匹配,分隔符
* 后 [] 为匹配,内容
*/
private static Pattern ROUTE_PATTERN = Pattern.compile("([&!=,]*)\\s*([^&!=,\\s]+)");
/**
* 路由规则 URL
*/
private final URL url;
/**
* 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0 。
*/
private final int priority;
/**
* 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 false 。
*/
private final boolean force;
/**
* 消费者匹配条件集合,通过解析【条件表达式 rule 的 `=>` 之前半部分】
*/
private final Map<String, MatchPair> whenCondition;
/**
* 提供者地址列表的过滤条件,通过解析【条件表达式 rule 的 `=>` 之后半部分】
*/
private final Map<String, MatchPair> thenCondition;
public ConditionRouter(URL url) {
......@@ -57,6 +81,7 @@ public class ConditionRouter implements Router, Comparable<Router> {
this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
this.force = url.getParameter(Constants.FORCE_KEY, false);
try {
// 拆分条件变大时为 when 和 then 两部分
String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
if (rule == null || rule.trim().length() == 0) {
throw new IllegalArgumentException("Illegal route rule!");
......@@ -65,7 +90,9 @@ public class ConditionRouter implements Router, Comparable<Router> {
int i = rule.indexOf("=>");
String whenRule = i < 0 ? null : rule.substring(0, i).trim();
String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
// 解析 `whenCondition`
Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
// 解析 `thenCondition`
Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
// NOTE: It should be determined on the business level whether the `When condition` can be empty or not.
this.whenCondition = when;
......@@ -75,8 +102,8 @@ public class ConditionRouter implements Router, Comparable<Router> {
}
}
private static Map<String, MatchPair> parseRule(String rule)
throws ParseException {
private static Map<String, MatchPair> parseRule(String rule) throws ParseException {
// System.out.println("rule: " + rule); // add by 芋艿,方便大家看
Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
if (StringUtils.isBlank(rule)) {
return condition;
......@@ -89,6 +116,7 @@ public class ConditionRouter implements Router, Comparable<Router> {
while (matcher.find()) { // Try to match one by one
String separator = matcher.group(1);
String content = matcher.group(2);
// System.out.println(separator + "\t" + content); // add by 芋艿,方便大家看
// Start part of the condition expression.
if (separator == null || separator.length() == 0) {
pair = new MatchPair();
......@@ -105,64 +133,60 @@ public class ConditionRouter implements Router, Comparable<Router> {
}
// The Value in the KV part.
else if ("=".equals(separator)) {
if (pair == null)
throw new ParseException("Illegal route rule \""
+ rule + "\", The error char '" + separator
+ "' at index " + matcher.start() + " before \""
+ content + "\".", matcher.start());
if (pair == null) {
throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start());
}
values = pair.matches;
values.add(content);
}
// The Value in the KV part.
else if ("!=".equals(separator)) {
if (pair == null)
throw new ParseException("Illegal route rule \""
+ rule + "\", The error char '" + separator
+ "' at index " + matcher.start() + " before \""
+ content + "\".", matcher.start());
if (pair == null) {
throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start());
}
values = pair.mismatches;
values.add(content);
}
// The Value in the KV part, if Value have more than one items.
else if (",".equals(separator)) { // Should be seperateed by ','
if (values == null || values.isEmpty())
throw new ParseException("Illegal route rule \""
+ rule + "\", The error char '" + separator
+ "' at index " + matcher.start() + " before \""
+ content + "\".", matcher.start());
if (values == null || values.isEmpty()) {
throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start());
}
values.add(content);
} else {
throw new ParseException("Illegal route rule \"" + rule
+ "\", The error char '" + separator + "' at index "
+ matcher.start() + " before \"" + content + "\".", matcher.start());
throw new ParseException("Illegal route rule \"" + rule + "\", The error char '" + separator + "' at index " + matcher.start() + " before \"" + content + "\".", matcher.start());
}
}
return condition;
}
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
throws RpcException {
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
// 为空,直接返回空 Invoker 集合
if (invokers == null || invokers.isEmpty()) {
return invokers;
}
try {
// 不匹配 `whenCondition` ,直接返回 `invokers` 集合,因为不需要走 `whenThen` 的匹配
if (!matchWhen(url, invocation)) {
return invokers;
}
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
// `whenThen` 为空,则返回空 Invoker 集合
if (thenCondition == null) {
logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
return result;
}
// 使用 `whenThen` ,匹配 `invokers` 集合。若符合,添加到 `result` 中
for (Invoker<T> invoker : invokers) {
if (matchThen(invoker.getUrl(), url)) {
result.add(invoker);
}
}
// 若 `result` 非空,返回它
if (!result.isEmpty()) {
return result;
// 如果 `force=true` ,代表强制执行,返回空 Invoker 集合
} else if (force) {
logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(Constants.RULE_KEY));
return result;
......@@ -170,21 +194,10 @@ public class ConditionRouter implements Router, Comparable<Router> {
} catch (Throwable t) {
logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
}
// 如果 `force=false` ,代表不强制执行,返回 `invokers` 集合,即忽略路由规则
return invokers;
}
public URL getUrl() {
return url;
}
public int compareTo(Router o) {
if (o == null || o.getClass() != ConditionRouter.class) {
return 1;
}
ConditionRouter c = (ConditionRouter) o;
return this.priority == c.priority ? url.toFullString().compareTo(c.url.toFullString()) : (this.priority > c.priority ? 1 : -1);
}
boolean matchWhen(URL url, Invocation invocation) {
return whenCondition == null || whenCondition.isEmpty() || matchCondition(whenCondition, url, null, invocation);
}
......@@ -195,11 +208,12 @@ public class ConditionRouter implements Router, Comparable<Router> {
private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
Map<String, String> sample = url.toMap();
boolean result = false;
boolean result = false; // 是否匹配
for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
// 获得条件属性
String key = matchPair.getKey();
String sampleValue;
//get real invoked method name from invocation
// get real invoked method name from invocation
if (invocation != null && (Constants.METHOD_KEY.equals(key) || Constants.METHODS_KEY.equals(key))) {
sampleValue = invocation.getMethodName();
} else {
......@@ -208,15 +222,16 @@ public class ConditionRouter implements Router, Comparable<Router> {
sampleValue = sample.get(Constants.DEFAULT_KEY_PREFIX + key);
}
}
// 匹配条件值
if (sampleValue != null) {
if (!matchPair.getValue().isMatch(sampleValue, param)) {
if (!matchPair.getValue().isMatch(sampleValue, param)) { // 返回不匹配
return false;
} else {
result = true;
}
} else {
//not pass the condition
if (!matchPair.getValue().matches.isEmpty()) {
// not pass the condition
if (!matchPair.getValue().matches.isEmpty()) { // 无条件值,但是有匹配条件 `matches` ,则返回不匹配。
return false;
} else {
result = true;
......@@ -226,30 +241,64 @@ public class ConditionRouter implements Router, Comparable<Router> {
return result;
}
@Override
public URL getUrl() {
return url;
}
@Override
public int compareTo(Router o) {
if (o == null || o.getClass() != ConditionRouter.class) {
return 1;
}
ConditionRouter c = (ConditionRouter) o;
return this.priority == c.priority ? url.toFullString().compareTo(c.url.toFullString()) : (this.priority > c.priority ? 1 : -1);
}
/**
* 用于匹配的值组
*/
private static final class MatchPair {
/**
* 匹配的值集合
*/
final Set<String> matches = new HashSet<String>();
/**
* 不匹配的值集合
*/
final Set<String> mismatches = new HashSet<String>();
/**
* 判断 value 是否匹配 matches + mismatches
*
* @param value 值
* @param param URL
* @return 是否匹配
*/
private boolean isMatch(String value, URL param) {
// 只匹配 matches
if (!matches.isEmpty() && mismatches.isEmpty()) {
for (String match : matches) {
if (UrlUtils.isMatchGlobPattern(match, value, param)) {
return true;
}
}
return false;
return false; // 如果没匹配上,认为为 false ,即不匹配
}
// 只匹配 mismatches
if (!mismatches.isEmpty() && matches.isEmpty()) {
for (String mismatch : mismatches) {
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
return false;
}
}
return true;
return true; // 注意,这里和上面不同。原因,你懂的。
}
if (!matches.isEmpty() && !mismatches.isEmpty()) {
// 匹配 mismatches + matches
if (!matches.isEmpty()) {
//when both mismatches and matches contain the same value, then using mismatches first
for (String mismatch : mismatches) {
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
......@@ -261,9 +310,10 @@ public class ConditionRouter implements Router, Comparable<Router> {
return true;
}
}
return false;
return false; // 如果没匹配上,认为为 false ,即不匹配
}
return false;
}
}
}
\ No newline at end of file
......@@ -23,11 +23,13 @@ import com.alibaba.dubbo.rpc.cluster.RouterFactory;
/**
* ConditionRouterFactory
*
* ConditionRouter 工厂实现类
*/
public class ConditionRouterFactory implements RouterFactory {
public static final String NAME = "condition";
@Override
public Router getRouter(URL url) {
return new ConditionRouter(url);
}
......
......@@ -27,21 +27,30 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
* 基于文件读取路由规则,创建对应的 Router 实现类的对象
*/
public class FileRouterFactory implements RouterFactory {
public static final String NAME = "file";
/**
* RouterFactory$Adaptive 对象
*/
private RouterFactory routerFactory;
public void setRouterFactory(RouterFactory routerFactory) {
this.routerFactory = routerFactory;
}
@Override
public Router getRouter(URL url) {
try {
// Transform File URL into Script Route URL, and Load
// file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=<file-content>
// 获得 router 配置项,默认为 script
String protocol = url.getParameter(Constants.ROUTER_KEY, ScriptRouterFactory.NAME); // Replace original protocol (maybe 'file') with 'script'
// 使用文件后缀做为类型
String type = null; // Use file suffix to config script type, e.g., js, groovy ...
String path = url.getPath();
if (path != null) {
......@@ -50,11 +59,16 @@ public class FileRouterFactory implements RouterFactory {
type = path.substring(i + 1);
}
}
// 读取规则内容
String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
// 创建路由规则 URL
boolean runtime = url.getParameter(Constants.RUNTIME_KEY, false);
URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type).addParameter(Constants.RUNTIME_KEY, runtime).addParameterAndEncoded(Constants.RULE_KEY, rule);
URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type)
.addParameter(Constants.RUNTIME_KEY, runtime)
.addParameterAndEncoded(Constants.RULE_KEY, rule);
// 通过 Dubbo SPI Adaptive 机制,获得 Router 对象
return routerFactory.getRouter(script);
} catch (IOException e) {
throw new IllegalStateException(e.getMessage(), e);
......
......@@ -41,19 +41,32 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* ScriptRouter
*
* 基于脚本的 Router 实现类
*/
public class ScriptRouter implements Router {
private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
/**
* 脚本类型 与 ScriptEngine 的映射缓存
*/
private static final Map<String, ScriptEngine> engines = new ConcurrentHashMap<String, ScriptEngine>();
/**
* 路由规则 URL
*/
private final ScriptEngine engine;
/**
* 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0 。
*/
private final int priority;
/**
* 路由规则内容
*/
private final String rule;
/**
* 路由规则 URL
*/
private final URL url;
public ScriptRouter(URL url) {
......@@ -61,6 +74,7 @@ public class ScriptRouter implements Router {
String type = url.getParameter(Constants.TYPE_KEY);
this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
// 初始化 `engine`
if (type == null || type.length() == 0) {
type = Constants.DEFAULT_SCRIPT_TYPE_KEY;
}
......@@ -68,7 +82,7 @@ public class ScriptRouter implements Router {
throw new IllegalStateException(new IllegalStateException("route rule can not be empty. rule:" + rule));
}
ScriptEngine engine = engines.get(type);
if (engine == null) {
if (engine == null) { // 在缓存中不存在,则进行创建 ScriptEngine 对象
engine = new ScriptEngineManager().getEngineByName(type);
if (engine == null) {
throw new IllegalStateException(new IllegalStateException("Unsupported route rule type: " + type + ", rule: " + rule));
......@@ -79,21 +93,25 @@ public class ScriptRouter implements Router {
this.rule = rule;
}
@Override
public URL getUrl() {
return url;
}
@Override
@SuppressWarnings("unchecked")
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
try {
// 执行脚本
List<Invoker<T>> invokersCopy = new ArrayList<Invoker<T>>(invokers);
Compilable compilable = (Compilable) engine;
Bindings bindings = engine.createBindings();
bindings.put("invokers", invokersCopy);
bindings.put("invocation", invocation);
bindings.put("context", RpcContext.getContext());
CompiledScript function = compilable.compile(rule);
Object obj = function.eval(bindings);
CompiledScript function = compilable.compile(rule); // 编译
Object obj = function.eval(bindings); // 执行
// 根据结果类型,转换成 (List<Invoker<T>> 类型返回
if (obj instanceof Invoker[]) {
invokersCopy = Arrays.asList((Invoker<T>[]) obj);
} else if (obj instanceof Object[]) {
......@@ -106,12 +124,14 @@ public class ScriptRouter implements Router {
}
return invokersCopy;
} catch (ScriptException e) {
//fail then ignore rule .invokers.
// 发生异常,忽略路由规则,返回全 `invokers` 集合
// fail then ignore rule .invokers.
logger.error("route error , rule has been ignored. rule: " + rule + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
return invokers;
}
}
@Override
public int compareTo(Router o) {
if (o == null || o.getClass() != ScriptRouter.class) {
return 1;
......
......@@ -37,6 +37,7 @@ public class ScriptRouterFactory implements RouterFactory {
public static final String NAME = "script";
@Override
public Router getRouter(URL url) {
return new ScriptRouter(url);
}
......
......@@ -21,23 +21,25 @@ import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcInvocation;
import com.alibaba.dubbo.rpc.RpcResult;
import com.alibaba.dubbo.rpc.*;
import com.alibaba.dubbo.rpc.cluster.Directory;
import com.alibaba.dubbo.rpc.support.MockInvoker;
import java.util.List;
/**
* Mock Cluster Invoker 实现类
*
* @param <T>
*/
public class MockClusterInvoker<T> implements Invoker<T> {
private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);
private final Directory<T> directory;
/**
* 真正的 Invoker 对象
*/
private final Invoker<T> invoker;
public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
......@@ -45,46 +47,59 @@ public class MockClusterInvoker<T> implements Invoker<T> {
this.invoker = invoker;
}
@Override
public URL getUrl() {
return directory.getUrl();
}
@Override
public boolean isAvailable() {
return directory.isAvailable();
}
@Override
public void destroy() {
this.invoker.destroy();
}
@Override
public Class<T> getInterface() {
return directory.getInterface();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result result;
// 获得 "mock" 配置项,有多种配置方式
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
//【第一种】无 mock
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//no mock
// no mock
// 调用原 Invoker ,发起 RPC 调用
result = this.invoker.invoke(invocation);
//【第二种】强制服务降级 https://dubbo.gitbooks.io/dubbo-user-book/demos/service-downgrade.html
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
//force:direct mock
// force:direct mock
// 直接调用 Mock Invoker ,执行本地 Mock 逻辑
result = doMockInvoke(invocation, null);
// 【第三种】失败服务降级 https://dubbo.gitbooks.io/dubbo-user-book/demos/service-downgrade.html
} else {
//fail-mock
// fail-mock
try {
// 调用原 Invoker ,发起 RPC 调用
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
// 业务性异常,直接抛出
if (e.isBiz()) {
throw e;
} else {
if (logger.isWarnEnabled()) {
logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
// 失败后,调用 Mock Invoker ,执行本地 Mock 逻辑
result = doMockInvoke(invocation, e);
}
}
......@@ -95,16 +110,20 @@ public class MockClusterInvoker<T> implements Invoker<T> {
@SuppressWarnings({"unchecked", "rawtypes"})
private Result doMockInvoke(Invocation invocation, RpcException e) {
Result result;
Invoker<T> minvoker;
// 第一步,获得 Mock Invoker 对象
Invoker<T> mInvoker;
// 路由匹配 Mock Invoker 集合
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
// 如果不存在,创建 MockInvoker 对象
if (mockInvokers == null || mockInvokers.isEmpty()) {
minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
mInvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
// 如果存在,选择第一个
} else {
minvoker = mockInvokers.get(0);
mInvoker = mockInvokers.get(0);
}
// 第二步,调用,执行本地 Mock 逻辑
try {
result = minvoker.invoke(invocation);
result = mInvoker.invoke(invocation);
} catch (RpcException me) {
if (me.isBiz()) {
result = new RpcResult(me.getCause());
......@@ -134,20 +153,29 @@ public class MockClusterInvoker<T> implements Invoker<T> {
* @param invocation
* @return
*/
/**
* 返回MockInvoker
* 契约:
* directory 根据 invocation中 是否有 Constants.INVOCATION_NEED_MOCK ,来判断获取的是一个 normal invoker 还是一个 mock invoker
* 如果 directorylist 返回多个 mock invoker ,只使用第一个 invoker .
*
* @param invocation
* @return
*/
private List<Invoker<T>> selectMockInvoker(Invocation invocation) {
List<Invoker<T>> invokers = null;
//TODO generic invoker?
// TODO generic invoker?
if (invocation instanceof RpcInvocation) {
//Note the implicit contract (although the description is added to the interface declaration, but extensibility is a problem. The practice placed in the attachement needs to be improved)
// 存在隐含契约(虽然在接口声明中增加描述,但扩展性会存在问题.同时放在 attachment 中的做法需要改进
((RpcInvocation) invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.toString());
//directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return.
// directory will return a list of normal invokers if Constants.INVOCATION_NEED_MOCK is present in invocation, otherwise, a list of mock invokers will return.
// directory 根据 invocation 中 attachment 是否有 Constants.INVOCATION_NEED_MOCK,来判断获取的是 normal invokers or mock invokers
try {
invokers = directory.list(invocation);
} catch (RpcException e) {
if (logger.isInfoEnabled()) {
logger.info("Exception when try to invoke mock. Get mock invokers error for service:"
+ directory.getUrl().getServiceInterface() + ", method:" + invocation.getMethodName()
+ ", will contruct a new mock with 'new MockInvoker()'.", e);
logger.info("Exception when try to invoke mock. Get mock invokers error for service:" + directory.getUrl().getServiceInterface() + ", method:" + invocation.getMethodName() + ", will contruct a new mock with 'new MockInvoker()'.", e);
}
}
}
......
......@@ -27,15 +27,19 @@ import com.alibaba.dubbo.rpc.cluster.Directory;
*/
public class MockClusterWrapper implements Cluster {
/**
* 真正的 Cluster 对象
*/
private Cluster cluster;
public MockClusterWrapper(Cluster cluster) {
this.cluster = cluster;
}
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
return new MockClusterInvoker<T>(directory,
this.cluster.join(directory));
}
}
}
\ No newline at end of file
......@@ -427,22 +427,29 @@ public class UrlUtils {
}
public static boolean isMatchGlobPattern(String pattern, String value, URL param) {
// 以美元符 `$` 开头,表示引用参数
if (param != null && pattern.startsWith("$")) {
pattern = param.getRawParameter(pattern.substring(1));
}
// 匹配
return isMatchGlobPattern(pattern, value);
}
public static boolean isMatchGlobPattern(String pattern, String value) {
if ("*".equals(pattern))
// 全匹配
if ("*".equals(pattern)) {
return true;
if ((pattern == null || pattern.length() == 0)
&& (value == null || value.length() == 0))
}
// 全部为空,匹配
if ((pattern == null || pattern.length() == 0) && (value == null || value.length() == 0)) {
return true;
if ((pattern == null || pattern.length() == 0)
|| (value == null || value.length() == 0))
}
// 有一个为空,不匹配
if ((pattern == null || pattern.length() == 0) || (value == null || value.length() == 0)) {
return false;
}
// 支持 * 的通配
int i = pattern.lastIndexOf('*');
// doesn't find "*"
if (i == -1) {
......
......@@ -350,7 +350,6 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
/**
* 校验 Stub 和 Mock 相关的配置
*
* TODO 芋艿,后续继续研究
* @param interfaceClass 接口类
*/
protected void checkStubAndMock(Class<?> interfaceClass) {
......@@ -382,20 +381,24 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
}
}
if (ConfigUtils.isNotEmpty(mock)) {
if (mock.startsWith(Constants.RETURN_PREFIX)) {
if (mock.startsWith(Constants.RETURN_PREFIX)) { // 处理 "return " 开头的情况
String value = mock.substring(Constants.RETURN_PREFIX.length());
// 校验 Mock 值,配置正确
try {
MockInvoker.parseMockValue(value);
} catch (Exception e) {
throw new IllegalStateException("Illegal mock json value in <dubbo:service ... mock=\"" + mock + "\" />");
}
} else {
// 获得 Mock 类
Class<?> mockClass = ConfigUtils.isDefault(mock) ? ReflectUtils.forName(interfaceClass.getName() + "Mock") : ReflectUtils.forName(mock);
// 校验是否实现接口类
if (!interfaceClass.isAssignableFrom(mockClass)) {
throw new IllegalStateException("The mock implementation class " + mockClass.getName() + " not implement interface " + interfaceClass.getName());
}
// 校验是否有默认构造方法
try {
mockClass.getConstructor(new Class<?>[0]);
mockClass.getConstructor();
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implementation class " + mockClass.getName());
}
......
......@@ -603,6 +603,7 @@ public class ServiceConfig<T> extends AbstractServiceConfig {
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
// 配置规则,参见《配置规则》https://dubbo.gitbooks.io/dubbo-user-book/demos/config-rule.html
// TODO 8038 ServiceConfig 为啥判断了 url.protocol
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
......
......@@ -29,20 +29,23 @@ limitations under the License.
<!-- use multicast registry center to discover service -->
<!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
<dubbo:registry id="zk01" address="zookeeper://127.0.0.1:2181"/>
<dubbo:registry id="zk01" address="zookeeper://127.0.0.1:2181">
</dubbo:registry>
<dubbo:registry id="zk02" address="zookeeper://127.0.0.1:2181"/>
<!--<dubbo:registry address="redis://127.0.0.1:6379"/>-->
<!-- generate proxy for the remote service, then demoService can be used in the same way as the
local regular interface -->
<dubbo:reference id="demoService" group="*" check="false" interface="com.alibaba.dubbo.demo.DemoService" client="netty4" timeout="10000" callbacks="1000" registry="zk01" cluster="failback">
<dubbo:reference id="demoService" group="g1" check="false" interface="com.alibaba.dubbo.demo.DemoService"
client="netty4" timeout="10000" callbacks="1000" registry="zk01">
<!--<dubbo:method name="sayHello" />-->
<!--url="registry://127.0.0.1:2181?registry=zookeeper;127.0.0.1:2181;127.0.0.1:27018"-->
<dubbo:parameter key="payload" value="1000" />
<dubbo:method name="sayHello" async="true" onreturn="callbackDemo.done" />
<!--<dubbo:method name="sayHello" async="true" onreturn="callbackDemo.done" />-->
</dubbo:reference>
......
......@@ -72,34 +72,34 @@ limitations under the License.
</dubbo:service>
<!-- declare the service interface to be exported -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" group="g2" ref="demoService" filter="demo" deprecated="false" callbacks="1000" timeout="200000" accesslog="true">
<!--<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" protocol="dubbo" ref="demoService"/>-->
<!--<dubbo:service id="sa" interface="com.alibaba.dubbo.demo.DemoService" protocol="dubbo" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl">-->
<!--&lt;!&ndash;<dubbo:method name="sayHello" retries="100">&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<dubbo:argument index="0" callback="true" />&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:argument type="com.demo.CallbackListener" callback="true" />&ndash;&gt;-->
<!--&lt;!&ndash;</dubbo:method>&ndash;&gt;-->
<!--<property name="demoDAO" ref="demoDAO" />-->
<!--</dubbo:service>-->
<!--<dubbo:service id="sb" interface="com.alibaba.dubbo.demo.DemoService" ref="demoService">-->
<!--&lt;!&ndash;<dubbo:method name="sayHello" retries="100">&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<dubbo:argument index="0" callback="true" />&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:argument type="com.demo.CallbackListener" callback="true" />&ndash;&gt;-->
<!--&lt;!&ndash; declare the service interface to be exported &ndash;&gt;-->
<!--<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" group="g2" ref="demoService" filter="demo" deprecated="false" callbacks="1000" timeout="200000" accesslog="true">-->
<!--&lt;!&ndash;<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" protocol="dubbo" ref="demoService"/>&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:service id="sa" interface="com.alibaba.dubbo.demo.DemoService" protocol="dubbo" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl">&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<dubbo:method name="sayHello" retries="100">&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;&lt;!&ndash;<dubbo:argument index="0" callback="true" />&ndash;&gt;&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<dubbo:argument type="com.demo.CallbackListener" callback="true" />&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;</dubbo:method>&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;<property name="demoDAO" ref="demoDAO" />&ndash;&gt;-->
<!--&lt;!&ndash;</dubbo:service>&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:service id="sb" interface="com.alibaba.dubbo.demo.DemoService" ref="demoService">&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<dubbo:method name="sayHello" retries="100">&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;&lt;!&ndash;<dubbo:argument index="0" callback="true" />&ndash;&gt;&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;<dubbo:argument type="com.demo.CallbackListener" callback="true" />&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;&lt;!&ndash;</dubbo:method>&ndash;&gt;&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:parameter key="threadname" value="shuaiqi" />&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:parameter key="threads" value="123" />&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:parameter key="queues" value="10" />&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:method name="callbackParam">&ndash;&gt;-->
<!--&lt;!&ndash;<dubbo:argument callback="true" type="com.alibaba.dubbo.demo.ParamCallback" />&ndash;&gt;-->
<!--&lt;!&ndash;</dubbo:method>&ndash;&gt;-->
<!--<dubbo:parameter key="threadname" value="shuaiqi" />-->
<!--<dubbo:parameter key="threads" value="123" />-->
<!--<dubbo:parameter key="queues" value="10" />-->
<!--<dubbo:method name="callbackParam">-->
<!--<dubbo:argument callback="true" type="com.alibaba.dubbo.demo.ParamCallback" />-->
<!--</dubbo:method>-->
<dubbo:method name="say01" deprecated="true" />
<!--<dubbo:method name="say01" deprecated="true" />-->
</dubbo:service>
<!--</dubbo:service>-->
<!--<dubbo:service interface="com.alibaba.dubbo.demo.BarService" ref="genericService" timeout="200000" />-->
......
......@@ -443,22 +443,25 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
* @return null : no routers ,do nothing
* else :routers list
*/
// TODO 芋艿,Router 相关文章
private List<Router> toRouters(List<URL> urls) {
List<Router> routers = new ArrayList<Router>();
if (urls == null || urls.isEmpty()) {
return routers;
}
for (URL url : urls) {
// 忽略,若是 "empty://" 。一般情况下,所有路由规则被删除时,有且仅有一条协议为 "empty://" 的路由规则 URL
if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
continue;
}
// 获得 "router"
String routerType = url.getParameter(Constants.ROUTER_KEY);
if (routerType != null && routerType.length() > 0) {
url = url.setProtocol(routerType);
}
try {
// 创建 Router 对象
Router router = routerFactory.getRouter(url);
// 添加到返回结果
if (!routers.contains(router)) {
routers.add(router);
}
......@@ -607,10 +610,12 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
return providerUrl;
}
// TODO 芋艿,路由规则
private List<Invoker<T>> route(List<Invoker<T>> invokers, String method) {
// 创建 Invocation 对象
Invocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
// 获得 Router 数组
List<Router> routers = getRouters();
// 根据路由规则,筛选 Invoker 集合
if (routers != null) {
for (Router router : routers) {
if (router.getUrl() != null) {
......@@ -664,11 +669,11 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
invokersList.add(invoker);
}
}
// TODO 芋艿,路由规则
// 路由全 `invokersList` ,匹配合适的 Invoker 集合
List<Invoker<T>> newInvokersList = route(invokersList, null);
// 添加 `newInvokersList` 到 `newMethodInvokerMap` 中,表示该服务提供者的全量 Invoker 集合
newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
// 循环 TODO 芋艿,路由规则
// 循环,基于每个方法路由,匹配合适的 Invoker 集合
if (serviceMethods != null && serviceMethods.length > 0) {
for (String method : serviceMethods) {
List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
......
......@@ -23,13 +23,7 @@ import com.alibaba.dubbo.common.utils.ConfigUtils;
import com.alibaba.dubbo.common.utils.PojoUtils;
import com.alibaba.dubbo.common.utils.ReflectUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcInvocation;
import com.alibaba.dubbo.rpc.RpcResult;
import com.alibaba.dubbo.rpc.*;
import com.alibaba.fastjson.JSON;
import java.lang.reflect.Constructor;
......@@ -40,90 +34,121 @@ import java.util.concurrent.ConcurrentHashMap;
final public class MockInvoker<T> implements Invoker<T> {
/**
* ProxyFactory$Adaptive 对象
*/
private final static ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
/**
* mock 与 Invoker 对象的映射缓存
*
* @see #getInvoker(String)
*/
private final static Map<String, Invoker<?>> mocks = new ConcurrentHashMap<String, Invoker<?>>();
/**
* mock 与 Throwable 对象的映射缓存
*
* @see #getThrowable(String)
*/
private final static Map<String, Throwable> throwables = new ConcurrentHashMap<String, Throwable>();
/**
* URL 对象
*/
private final URL url;
public MockInvoker(URL url) {
this.url = url;
}
public static Object parseMockValue(String mock) throws Exception {
public static Object parseMockValue(String mock) {
return parseMockValue(mock, null);
}
public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception {
public static Object parseMockValue(String mock, Type[] returnTypes) {
// 解析值(不考虑返回类型)
Object value;
if ("empty".equals(mock)) {
if ("empty".equals(mock)) { // 未赋值的对象,即 new XXX() 对象
value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>) returnTypes[0] : null);
} else if ("null".equals(mock)) {
} else if ("null".equals(mock)) { // null
value = null;
} else if ("true".equals(mock)) {
} else if ("true".equals(mock)) { // true
value = true;
} else if ("false".equals(mock)) {
} else if ("false".equals(mock)) { // false
value = false;
} else if (mock.length() >= 2 && (mock.startsWith("\"") && mock.endsWith("\"")
|| mock.startsWith("\'") && mock.endsWith("\'"))) {
|| mock.startsWith("\'") && mock.endsWith("\'"))) { // 使用 '' 或 "" 的字符串,截取掉头尾
value = mock.subSequence(1, mock.length() - 1);
} else if (returnTypes != null && returnTypes.length > 0 && returnTypes[0] == String.class) {
} else if (returnTypes != null && returnTypes.length > 0 && returnTypes[0] == String.class) { // 字符串
value = mock;
} else if (StringUtils.isNumeric(mock)) {
} else if (StringUtils.isNumeric(mock)) { // 数字
value = JSON.parse(mock);
} else if (mock.startsWith("{")) {
} else if (mock.startsWith("{")) { // Map
value = JSON.parseObject(mock, Map.class);
} else if (mock.startsWith("[")) {
} else if (mock.startsWith("[")) { // List
value = JSON.parseObject(mock, List.class);
} else {
value = mock;
}
// 转换成对应的返回类型
if (returnTypes != null && returnTypes.length > 0) {
value = PojoUtils.realize(value, (Class<?>) returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null);
value = PojoUtils.realize(value, (Class<?>) returnTypes[0], returnTypes.length > 1 ? returnTypes[1] /* 泛型 */ : null);
}
return value;
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY);
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(this);
}
// 获得 `"mock"` 配置项,方法级 > 类级
String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY);
if (StringUtils.isBlank(mock)) {
mock = getUrl().getParameter(Constants.MOCK_KEY);
}
if (StringUtils.isBlank(mock)) {
if (StringUtils.isBlank(mock)) { // 不允许为空
throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
}
mock = normallizeMock(URL.decode(mock));
// 标准化 `"mock"` 配置项
mock = normalizedMock(URL.decode(mock));
// 等于 "return " ,返回值为空的 RpcResult 对象
if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())) {
RpcResult result = new RpcResult();
result.setValue(null);
return result;
// 以 "return " 开头,返回对应值的 RpcResult 对象
} else if (mock.startsWith(Constants.RETURN_PREFIX)) {
mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
mock = mock.replace('`', '"');
try {
// 解析返回类型
Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
// 解析返回值
Object value = parseMockValue(mock, returnTypes);
// 创建对应值的 RpcResult 对象,并返回
return new RpcResult(value);
} catch (Exception ew) {
throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: " + url, ew);
}
// 以 "throw" 开头,抛出 RpcException 异常
} else if (mock.startsWith(Constants.THROW_PREFIX)) {
mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
mock = mock.replace('`', '"');
if (StringUtils.isBlank(mock)) {
if (StringUtils.isBlank(mock)) { // 禁止为空
throw new RpcException(" mocked exception for Service degradation. ");
} else { // user customized class
// 创建自定义异常
Throwable t = getThrowable(mock);
// 抛出业务类型的 RpcException 异常
throw new RpcException(RpcException.BIZ_EXCEPTION, t);
}
} else { //impl mock
// 自定义 Mock 类,执行自定义逻辑
} else {
try {
// 创建 Invoker 对象
Invoker<T> invoker = getInvoker(mock);
// 执行 Invoker 对象的调用逻辑
return invoker.invoke(invocation);
} catch (Throwable t) {
throw new RpcException("Failed to create mock implemention class " + mock, t);
......@@ -131,90 +156,105 @@ final public class MockInvoker<T> implements Invoker<T> {
}
}
private Throwable getThrowable(String throwstr) {
Throwable throwable = (Throwable) throwables.get(throwstr);
private Throwable getThrowable(String throwStr) {
// 从缓存中,获得 Throwable 对象
Throwable throwable = throwables.get(throwStr);
if (throwable != null) {
return throwable;
} else {
Throwable t = null;
try {
Class<?> bizException = ReflectUtils.forName(throwstr);
Constructor<?> constructor;
constructor = ReflectUtils.findConstructor(bizException, String.class);
t = (Throwable) constructor.newInstance(new Object[]{" mocked exception for Service degradation. "});
if (throwables.size() < 1000) {
throwables.put(throwstr, t);
}
} catch (Exception e) {
throw new RpcException("mock throw error :" + throwstr + " argument error.", e);
}
// 不存在,创建 Throwable 对象
Throwable t;
try {
// 获得异常类
Class<?> bizException = ReflectUtils.forName(throwStr);
// 获得构造方法
Constructor<?> constructor = ReflectUtils.findConstructor(bizException, String.class);
// 创建 Throwable 对象
t = (Throwable) constructor.newInstance(new Object[]{" mocked exception for Service degradation. "});
// 添加到缓存中
if (throwables.size() < 1000) {
throwables.put(throwStr, t);
}
return t;
} catch (Exception e) {
throw new RpcException("mock throw error :" + throwStr + " argument error.", e);
}
return t;
}
@SuppressWarnings("unchecked")
private Invoker<T> getInvoker(String mockService) {
// 从缓存中,获得 Invoker 对象
Invoker<T> invoker = (Invoker<T>) mocks.get(mockService);
if (invoker != null) {
return invoker;
} else {
Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());
if (ConfigUtils.isDefault(mockService)) {
mockService = serviceType.getName() + "Mock";
}
Class<?> mockClass = ReflectUtils.forName(mockService);
if (!serviceType.isAssignableFrom(mockClass)) {
throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
}
if (!serviceType.isAssignableFrom(mockClass)) {
throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
}
try {
T mockObject = (T) mockClass.newInstance();
invoker = proxyFactory.getInvoker(mockObject, (Class<T>) serviceType, url);
if (mocks.size() < 10000) {
mocks.put(mockService, invoker);
}
return invoker;
} catch (InstantiationException e) {
throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
// 不存在,创建 Invoker 对象
// 1. 获得接口类
Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());
// 2. 若为 `true` `default` ,修改修改为对应接口 + "Mock" 类。这种情况出现在原始 `mock = fail:true` 或 `mock = force:true` 等情况
if (ConfigUtils.isDefault(mockService)) {
mockService = serviceType.getName() + "Mock";
}
// 3. 获得 Mock 类
Class<?> mockClass = ReflectUtils.forName(mockService);
// 4. 校验 Mock 类,实现了接口类
if (!serviceType.isAssignableFrom(mockClass)) {
throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
}
try {
// 5. 创建 Mock 对象
T mockObject = (T) mockClass.newInstance();
// 6. 创建 Mock 对应,对应的 Invoker 对象
invoker = proxyFactory.getInvoker(mockObject, serviceType, url);
// 7. 添加到缓存
if (mocks.size() < 10000) {
mocks.put(mockService, invoker);
}
return invoker;
} catch (InstantiationException e) {
throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
//mock=fail:throw
//mock=fail:return
//mock=xx.Service
private String normallizeMock(String mock) {
private String normalizedMock(String mock) {
// 若为空,直接返回
if (mock == null || mock.trim().length() == 0) {
return mock;
// 若果为 "true" "default" "fail" "force" 四种字符串,修改为对应接口 + "Mock" 类
} else if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock.trim()) || "force".equalsIgnoreCase(mock.trim())) {
mock = url.getServiceInterface() + "Mock";
}
// 若以 "fail:" 开头,去掉该开头
if (mock.startsWith(Constants.FAIL_PREFIX)) {
mock = mock.substring(Constants.FAIL_PREFIX.length()).trim();
// 若以 "force:" 开头,去掉该开头
} else if (mock.startsWith(Constants.FORCE_PREFIX)) {
mock = mock.substring(Constants.FORCE_PREFIX.length()).trim();
}
return mock;
}
@Override
public URL getUrl() {
return this.url;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void destroy() {
//do nothing
}
@Override
public Class<T> getInterface() {
//FIXME
return null;
......
......@@ -24,12 +24,10 @@ import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
/**
* MockProtocol is used for generating a mock invoker by URL and type on consumer side
*
* MockProtocol 用于在 consumer side 通过 url 及类型生成一个 MockInvoker
*/
final public class MockProtocol extends AbstractProtocol {
public int getDefaultPort() {
return 0;
}
public final class MockProtocol extends AbstractProtocol {
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
......@@ -41,4 +39,9 @@ final public class MockProtocol extends AbstractProtocol {
return new MockInvoker<T>(url);
}
}
@Override
public int getDefaultPort() {
return 0;
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册