如何實現服的消費
- 生成遠程服務的代理
- 獲取目標服務的url地址
- 實現遠程網絡通信
- 實現負載均衡
- 實現集羣容錯
Invoker
服務引入
消費端的代碼解析是從下面這段代碼開始的
<dubbo:reference id="xxxService" interface="xxx.xxx.Service"/>
註解的方式的初始化入口是
ReferenceAnnotationBeanPostProcessor
->ReferenceBeanInvocationHandler.init
->ReferenceConfig.get() 獲得一個遠程代理類
ReferenceBean
服務引用的入口方法爲 ReferenceBean 的 getObject 方法,該方法定義在 Spring 的 FactoryBean 接口中
/**
* ReferenceFactoryBean
*/
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean,
ApplicationContextAware, InitializingBean, DisposableBean {
private static final long serialVersionUID = 213195494150089726L;
private transient ApplicationContext applicationContext;
public ReferenceBean() {
super();
}
public ReferenceBean(Reference reference) {
super(reference);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
}
@Override
// 這是入口方法
public Object getObject() {
return get();
}
@Override
public Class<?> getObjectType() {
return getInterfaceClass();
}
@Override
@Parameter(excluded = true)
public boolean isSingleton() {
return true;
}
/**
* Initializes there Dubbo's Config Beans before @Reference bean autowiring
*/
private void prepareDubboConfigBeans() {
beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class);
beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class);
beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ConfigCenterBean.class);
beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class);
beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class);
beansOfTypeIncludingAncestors(applicationContext, SslConfig.class);
}
@Override
@SuppressWarnings({"unchecked"})
public void afterPropertiesSet() throws Exception {
// 加載 dubbo的ConfigBean到ioc容器中
prepareDubboConfigBeans();
// 默認懶加載 可以通過@Reference(init = true)
if (init == null) {
init = false;
}
// eager init if necessary.
if (shouldInit()) {
getObject();
}
}
@Override
public void destroy() {
// do nothing
}
}
ReferenceConfig.get
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
// 如果當前接口的遠程代理引用爲空,則進行初始化
if (ref == null) {
init();
}
return ref;
}
ReferenceConfig.init
public synchronized void init() {
// 避免重複初始化
if (initialized) {
return;
}
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.init();
}
// 檢查和修改配置
checkAndUpdateSubConfigs();
//init serivceMetadata 初始化 服務元數據
serviceMetadata.setVersion(version);
serviceMetadata.setGroup(group);
serviceMetadata.setDefaultGroup(group);
serviceMetadata.setServiceType(getActualInterface());
serviceMetadata.setServiceInterfaceName(interfaceName);
// TODO, uncomment this line once service key is unified
serviceMetadata.setServiceKey(URL.buildKey(interfaceName, group, version));
// 檢查本地服務
checkStubAndLocal(interfaceClass);
ConfigValidationUtils.checkMock(interfaceClass, this);
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, CONSUMER_SIDE);
ReferenceConfigBase.appendRuntimeParameters(map);
if (!ProtocolUtils.isGeneric(generic)) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), COMMA_SEPARATOR));
}
}
map.put(INTERFACE_KEY, interfaceName);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
// remove 'default.' prefix for configs from ConsumerConfig
// appendParameters(map, consumer, Constants.DEFAULT_KEY);
AbstractConfig.appendParameters(map, consumer);
AbstractConfig.appendParameters(map, this);
Map<String, Object> attributes = null;
if (CollectionUtils.isNotEmpty(getMethods())) {
attributes = new HashMap<>();
for (MethodConfig methodConfig : getMethods()) {
AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName());
String retryKey = methodConfig.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(methodConfig.getName() + ".retries", "0");
}
}
ConsumerModel.AsyncMethodInfo asyncMethodInfo = AbstractConfig.convertMethodConfig2AsyncInfo(methodConfig);
if (asyncMethodInfo != null) {
// consumerModel.getMethodModel(methodConfig.getName()).addAttribute(ASYNC_KEY, asyncMethodInfo);
attributes.put(methodConfig.getName(), asyncMethodInfo);
}
}
}
// 獲取服務消費者 ip 地址
String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(REGISTER_IP_KEY, hostToRegistry);
serviceMetadata.getAttachments().putAll(map);
/******************上面都是參數組裝********************/
// 元數據中心註冊
ServiceRepository repository = ApplicationModel.getServiceRepository();
ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
repository.registerConsumer(
serviceMetadata.getServiceKey(),
attributes,
serviceDescriptor,
this,
null,
serviceMetadata);
// 創建代理類引用 這個纔是關鍵
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
repository.lookupReferredService(serviceMetadata.getServiceKey()).setProxyObject(ref);
initialized = true; // 設置已經初始化
// dispatch a ReferenceConfigInitializedEvent since 2.7.4
dispatch(new ReferenceConfigInitializedEvent(this, invoker));
}
ReferenceConfig.createProxy
- 判斷是否爲本地調用,如果是則使用injvm協議進行調用
- 判斷是否爲點對點調用,如果是則把url保存到urls集合中,如果url爲1,進入步驟4,如果urls>1,則執行5 3. 如果是配置了註冊中心,遍歷註冊中心,把url添加到urls集合,url爲1,進入步驟4,如果urls>1,則執行5
- 直連構建invoker
- 構建invokers集合,通過cluster合併多個invoker
- 最後調用 ProxyFactory 生成代理類
private T createProxy(Map<String, String> map) {
// 判斷是否是在同一個jvm進程中調用
if (shouldJvmRefer(map)) {
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = REF_PROTOCOL.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
urls.clear();
// @Reference(url = "http://xxx") 直連方式
// url如果不爲空,說明是點對點通信
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (StringUtils.isEmpty(url.getPath())) {
url = url.setPath(interfaceName);
}
// 檢測url協議是否爲registry,若是,表明用戶想使用的指定的註冊中心
if (UrlUtils.isRegistry(url)) {
// 將map轉換爲查詢字符串,並作爲refer參數的值添加到url中
urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
} else {
// 合併url,移除服務提供者的一些配置(這些配置來源用戶配置的url屬性)
// 比如線程池相關配置。並保留服務提供者的部分配置,比如版本,group,時間戳等
// 最後合將合併的配置設置url查詢字符串中
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // assemble URL from register center's configuration
// if protocols not injvm checkRegistry
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
// 檢驗註冊中心的配置以及是否必要從配置中心組裝url
checkRegistry();
// 這裏的代碼實現和服務端類似,也是根據註冊中心配置解析等到的url
// 這裏的url:registry://192.168.0.4:2181/org.apache.dubbo.registry.RegistryService
List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
for (URL u : us) {
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
if (monitorUrl != null) {
map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
}
}
// 如果沒有配置註冊中心,則報錯
if (urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
}
// 如果配置了一個註冊中心或者一個服務提供者,直接使用refprotocol.refer
if (urls.size() == 1) {
// Protocol$Adaptive --> RegistryProtocol.refer
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else { // 配置多個註冊中心
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) { // 遍歷urls生成多個invoker
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url; // use last registry url
}
}
if (registryURL != null) { // 如果registryURL不爲空,構建靜態directory
// 使用ZoneAwareCluster
URL u = registryURL.addParameterIfAbsent(CLUSTER_KEY, ZoneAwareCluster.NAME);
// 通過Cluster將多個invoker合併ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here)-> Invoker
invoker = CLUSTER.join(new StaticDirectory(u, invokers));
} else { // not a registry url, must be direct invoke.
invoker = CLUSTER.join(new StaticDirectory(invokers));
}
}
}
// 檢查invoker的有效性
if (shouldCheck() && !invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service "
+ interfaceName
+ ". No provider available for the service "
+ (group == null ? "" : group + "/")
+ interfaceName +
(version == null ? "" : ":" + version)
+ " from the url "
+ invoker.getUrl()
+ " to the consumer "
+ NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
/**
* @since 2.7.0
* ServiceData Store
*/
String metadata = map.get(METADATA_KEY);
WritableMetadataService metadataService = WritableMetadataService.getExtension(metadata == null ? DEFAULT_METADATA_STORAGE_TYPE : metadata);
if (metadataService != null) {
URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
metadataService.publishServiceDefinition(consumerURL);
}
// create service proxy 創建服務代理類
return (T) PROXY_FACTORY.getProxy(invoker);
}
protocol.refer
通過指定的協議來調用refer生成一個invoker對象,invoker前面講過,它是一個代理對象。那麼在當前的消費端而言,invoker主要用於執行遠程調用。這個protocol,又是一個自適應擴展點,它得到的是一個Protocol$Adaptive.
private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
根據當前的協議url,得到一個指定的擴展點,傳遞進來的參數中,協議地址爲registry://,所以,我們可以直接定位到RegistryProtocol.refer代碼
Protocol$Adaptive中的refer方法
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
根據當前的協議擴展名registry, 獲得一個被包裝過的RegistryProtocol
RegistryProtocol.refer
- (1) 組裝註冊中心協議url
- (2) 判斷是否配置legroup,如果有,則cluster=getMergeableCluster(),構建invoker
- (3) doRefer構建invoker
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// (1) 組裝註冊中心協議rul
// url: zookeeper://192.168.0.4:2181/org.apache.dubbo.registry.RegistryService?xxx
url = getRegistryUrl(url);
// registryFactory-> RegistryFactory$Aaptive -> zookeeperRegistryFactory.getRegistry
// registry 最終返回 zookeeperRegistry 使用curator建立到zookeeper的連接
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
// 解析group參數,根據group決定cluster的類型
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
String group = qs.get(GROUP_KEY);
if (group != null && group.length() > 0) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
// 通過SPI加載MergeableCluster實例,並調用doRefer繼續執行
return doRefer(getMergeableCluster(), registry, type, url);
}
}
// 構建invoker 調用doRefer繼續執行服務引用邏輯
return doRefer(cluster, registry, type, url);
}
// 根據配置的協議組裝url
protected URL getRegistryUrl(URL url) {
return URLBuilder.from(url)
.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
.removeParameter(REGISTRY_KEY)
.build();
}
RegistryProtocol.doRefer
- 構建一個RegistryDirectory
- 構建一個consumer://協議的地址註冊到註冊中心
- 訂閱zookeeper中節點的變化
- 調用cluster.join方法
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
// RegistryDirectory初始化
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
// 設置註冊中心和協議
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
// url:consumer://192.168.0.4/com.test.dubbo.DemoService?xxx
URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
// 註冊服務消費者,在consumers 目前下新節點
if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
// 註冊consumer://協議的url
registry.register(directory.getRegisteredConsumerUrl());
}
// 構建routerChain
directory.buildRouterChain(subscribeUrl);
// 訂閱 providers,configurators,routers等節點數據
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
// 一個註冊中心可能有多個服務提供者,因此這裏需要將多個服務提供者合併爲一個 後面負責均衡用到
// 這裏返回的invoker,應該是MockClusterWrapper(FailOverCluster(directory))
Invoker invoker = cluster.join(directory);
return invoker;
}
doRefer 方法創建一個 RegistryDirectory 實例,然後生成服務者消費者鏈接,並向註冊中心進行註冊。註冊完畢後,緊接着訂閱 providers、configurators、routers 等節點下的數據。完成訂閱後,RegistryDirectory 會收到這幾個節點下的子節點信息。由於一個服務可能部署在多臺服務器上,這樣就會在 providers 產生多個節點,這個時候就需要 Cluster 將多個服務節點合併爲一個,並生成一個 Invoker。關於 RegistryDirectory 和 Cluster,這裏不再分析,後面單獨分析。
proxyFactory.getProxy
創建代理
ReferenceConfig.createProxy方法中的最後一行,拿到invoker之後,會調用獲得一個動態代理類
return (T) PROXY_FACTORY.getProxy(invoker);
proxyFactory是一個SPI擴展點 可以直接看StubProxyFactoryWrapper(JavassistProxyFactory)
JavassistProxyFactory
中沒有getProxy(xx)
一個參數的方法會調用其父類的方法AbstractProxyFactory#getProxy(Invoker<T>)
可以直接從AbstractProxyFactory.getProxy
分析
AbstractProxyFactory.getProxy
public abstract class AbstractProxyFactory implements ProxyFactory {
private static final Class<?>[] INTERNAL_INTERFACES = new Class<?>[]{
EchoService.class, Destroyable.class
};
@Override
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
// 調用重載方法
return getProxy(invoker, false);
}
@Override
public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
Set<Class<?>> interfaces = new HashSet<>();
// 獲取接口配置
String config = invoker.getUrl().getParameter(INTERFACES);
if (config != null && config.length() > 0) {
// 切分接口列表
String[] types = COMMA_SPLIT_PATTERN.split(config);
if (types != null && types.length > 0) {
for (int i = 0; i < types.length; i++) {
// TODO can we load successfully for a different classloader?.
interfaces.add(ReflectUtils.forName(types[i]));
}
}
}
if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
interfaces.add(com.alibaba.dubbo.rpc.service.GenericService.class);
}
// 添加接口
interfaces.add(invoker.getInterface());
// 添加服務類接口 EchoService.class, Destroyable.class
interfaces.addAll(Arrays.asList(INTERNAL_INTERFACES));
// 調用模板重載方法
return getProxy(invoker, interfaces.toArray(new Class<?>[0]));
}
// 模板方法 由子類實現 如:JavassistProxyFactory.getProxy
public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
}
JavassistProxyFactory.getProxy
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
// 生成 Proxy 子類(Proxy 是抽象類)。並調用 Proxy 子類的 newInstance 方法創建 Proxy 實例
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
通過 Proxy 的 getProxy 方法獲取 Proxy 子類,然後創建 InvokerInvocationHandler 對象,並將該對象傳給 newInstance 生成 Proxy 實例
InvokerInvocationHandler 實現 JDK 的 InvocationHandler 接口,具體的用途是攔截接口類調用
Proxy.getProxy
public static Proxy getProxy(Class<?>... ics) {
// 調用重載方法
return getProxy(ClassUtils.getClassLoader(Proxy.class), ics);
}
public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {
if (ics.length > MAX_PROXY_COUNT) {
throw new IllegalArgumentException("interface limit exceeded");
}
StringBuilder sb = new StringBuilder();
// 遍歷接口列表
for (int i = 0; i < ics.length; i++) {
String itf = ics[i].getName();
// 檢查類型是否爲接口
if (!ics[i].isInterface()) {
throw new RuntimeException(itf + " is not a interface.");
}
Class<?> tmp = null;
try {
// 重新加載接口類
tmp = Class.forName(itf, false, cl);
} catch (ClassNotFoundException e) {
}
// 檢測接口是否相同,這裏tmp有可能爲空
if (tmp != ics[i]) {
throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
}
// 拼接接口全限定名,分割符爲
sb.append(itf).append(';');
}
// 使用拼接後的接口名作爲key
//eg: com.alibaba.dubbo.rpc.service.EchoService;com.test.dubbo.DemoService;org.apache.dubbo.rpc.service.Destroyable;
String key = sb.toString();
// get cache by class loader.
final Map<String, Object> cache;
synchronized (PROXY_CACHE_MAP) {
cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new HashMap<>());
}
Proxy proxy = null;
synchronized (cache) {
do {
// 從緩存中獲取Reference<Proxy>實例
Object value = cache.get(key);
if (value instanceof Reference<?>) {
proxy = (Proxy) ((Reference<?>) value).get();
if (proxy != null) {
return proxy;
}
}
// 併發控制,保證只有一個線程可以進行後續操作
if (value == PENDING_GENERATION_MARKER) {
try {
// 其他線程在此處進行等待
cache.wait();
} catch (InterruptedException e) {
}
} else {
// 放置標誌爲到緩存,並跳出while循環進行後續操作
cache.put(key, PENDING_GENERATION_MARKER);
break;
}
}
while (true);
}
long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try {
// 創建ClassGenerator對象
ccp = ClassGenerator.newInstance(cl);
Set<String> worked = new HashSet<>();
List<Method> methods = new ArrayList<>();
for (int i = 0; i < ics.length; i++) {
// 檢測接口訪問級別是否爲protocted或private
if (!Modifier.isPublic(ics[i].getModifiers())) {
// 獲取接口報名
String npkg = ics[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
} else {
// 非public級別的接口必須在同一個包下,否則拋出異常
if (!pkg.equals(npkg)) {
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
}
// 添加接口到ClassGenerator中
ccp.addInterface(ics[i]);
// 遍歷接口方法
for (Method method : ics[i].getMethods()) {
// 獲取方法描述,可以理解爲方法簽名
String desc = ReflectUtils.getDesc(method);
// 如果方法描述字符串已在 worked 中,則忽略。考慮這種情況,
// A 接口和 B 接口中包含一個完全相同的方法
if (worked.contains(desc) || Modifier.isStatic(method.getModifiers())) {
continue;
}
if (ics[i].isInterface() && Modifier.isStatic(method.getModifiers())) {
continue;
}
worked.add(desc);
int ix = methods.size();
// 獲取方法返回值類型
Class<?> rt = method.getReturnType();
Class<?>[] pts = method.getParameterTypes();
// 生成 Object[] args = new Object[1...N]
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for (int j = 0; j < pts.length; j++) {
// 生成 args[1...N] = ($w)$1...N;
code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
}
code.append(" Object ret = handler.invoke(this, methods[").append(ix).append("], args);");
// 返回值不爲 void
if (!Void.TYPE.equals(rt)) {
// 生成返回語句,形如 return (java.lang.String) ret;
code.append(" return ").append(asArgument(rt, "ret")).append(";");
}
methods.add(method);
// 添加方法名、訪問控制符、參數列表、方法代碼等信息到 ClassGenerator 中
ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
}
if (pkg == null) {
pkg = PACKAGE_NAME;
}
// 構建接口代理類名稱:pkg + ".proxy" + id,比如 org.apache.dubbo.proxy0
String pcn = pkg + ".proxy" + id;
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
// 生成 private java.lang.reflect.InvocationHandler handler
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;");
// 爲接口代理類添加默認構造方法
ccp.addDefaultConstructor();
// 生成接口代理類
Class<?> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
// 構建 Proxy 子類名稱,比如 Proxy1,Proxy2 等
String fcn = Proxy.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Proxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
// 生成 Proxy 實現類
Class<?> pc = ccm.toClass();
// 通過反射創建 Proxy 實例
proxy = (Proxy) pc.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
// 釋放資源
if (ccp != null) {
ccp.release();
}
if (ccm != null) {
ccm.release();
}
synchronized (cache) {
if (proxy == null) {
cache.remove(key);
} else {
// 寫緩存
cache.put(key, new WeakReference<Proxy>(proxy));
}
// 喚醒其他等待線程
cache.notifyAll();
}
}
return proxy;
}
最終會生成如proxy0類
package org.apache.dubbo.common.bytecode;
public class proxy0 implements com.test.dubbo.DemoService, EchoService, DemoService {
public static java.lang.reflect.Method[] methods;
private java.lang.reflect.InvocationHandler handler;
public proxy0() {
}
public proxy0(java.lang.reflect.InvocationHandler arg0) {
handler = $1;
}
public java.lang.String sayHello(java.lang.String arg0) {
// 將參數存儲到 Object 數組中
Object[] args = new Object[1];
args[0] = ($w) $1;
// 調用 InvocationHandler 實現類的 invoke 方法得到調用結果
Object ret = handler.invoke(this, methods[0], args);
// 返回調用結果
return (java.lang.String) ret;
}
/** 回聲測試方法 */
public Object $echo(Object object) {
Object[] arrobject = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[1], arrobject);
return object2;
}
}
從這個sayHello方法可以看出,我們通過@Reference注入的一個對象實例本質上就是一個動態代理類,通過調用這個類中的方法,會觸發
handler.invoke(), 而這個handler就是InvokerInvocationHandler
服務調用
proxy0#sayHello(String)
—> InvokerInvocationHandler#invoke(Object, Method, Object[])
—> MockClusterInvoker#invoke(Invocation)
—> AbstractClusterInvoker#invoke(Invocation)
—> FailoverClusterInvoker#doInvoke(Invocation, List<Invoker<T>>, LoadBalance)
—> Filter#invoke(Invoker, Invocation) // 包含多個 Filter 調用
—> ListenerInvokerWrapper#invoke(Invocation)
—> AbstractInvoker#invoke(Invocation)
—> DubboInvoker#doInvoke(Invocation)
—> ReferenceCountExchangeClient#request(Object, int)
—> HeaderExchangeClient#request(Object, int)
—> HeaderExchangeChannel#request(Object, int)
—> AbstractPeer#send(Object)
—> AbstractClient#send(Object, boolean)
—> NettyChannel#send(Object, boolean)
—> NioClientSocketChannel#write(Object)
消費者服務調用 invoker的鏈條調用 回顧上面的Proxy0$sayHello()調用 handler.invoke
public java.lang.String sayHello(java.lang.String arg0) {
// 將參數存儲到 Object 數組中
Object[] args = new Object[1];
args[0] = ($w) $1;
// 調用 InvocationHandler 實現類的 invoke 方法得到調用結果
Object ret = handler.invoke(this, methods[0], args);
// 返回調用結果
return (java.lang.String) ret;
}
proxy0.handler是在JavassistProxyFactory.getProxy獲取代理類時構造的,入口InvokerInvocationHandler
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
InvokerInvocationHandler
public class InvokerInvocationHandler implements InvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
// 獲取服務方法名
String methodName = method.getName();
// 獲取方法的參數
Class<?>[] parameterTypes = method.getParameterTypes();
// 非服務類的方法 直接調用
if (parameterTypes.length == 0) {
if ("toString".equals(methodName)) {
return invoker.toString();
} else if ("$destroy".equals(methodName)) {
invoker.destroy();
return null;
} else if ("hashCode".equals(methodName)) {
return invoker.hashCode();
}
} else if (parameterTypes.length == 1 && "equals".equals(methodName)) {
return invoker.equals(args[0]);
}
// 將method和args封裝到RpcInvocation中,傳輸對象,並執行後續的調用
RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args);
rpcInvocation.setTargetServiceUniqueName(invoker.getUrl().getServiceKey());
// 這個invoker是MockClusterInvoker 是通過Cluster的擴展點的包裝類MockClusterWrapper構建的
return invoker.invoke(rpcInvocation).recreate();
}
}
MockClusterInvoker.invoke
Mock,在這裏面有兩個邏輯
- 是否客戶端強制配置了mock調用,那麼在這種場景中主要可以用來解決服務端還沒開發好的時候
直接使用本地數據進行測試 - 是否出現了異常,如果出現異常則使用配置好的Mock類來實現服務的降級
public class MockClusterInvoker<T> implements Invoker<T> {
private static final Logger logger = LoggerFactory.getLogger(MockClusterInvoker.class);
private final Directory<T> directory;
private final Invoker<T> invoker;
public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
this.directory = directory;
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 = null;
// 獲取mock配置
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
//no mock
// 無mock邏輯,直接調用其他Invoker對象的inoker方法
// 比如: FailOverCluster 可以通過:@Reference(cluster="failover")配置
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
//force:direct mock
// force:xx 直接執行mock邏輯,不乏其遠程調用
result = doMockInvoke(invocation, null);
} else {
//fail-mock
// fail:xxx 表示消費方調用服務失敗後,再執行mock邏輯,不拋出異常
try {
// 調用其他的Invoker對象的invoke方法
result = this.invoker.invoke(invocation);
//fix:#4585 返回有異常情況
if(result.getException() != null && result.getException() instanceof RpcException){
RpcException rpcException= (RpcException)result.getException();
if(rpcException.isBiz()){ // 業務異常直接返回
throw rpcException;
}else {
// 非業務異常 服務降級mock邏輯
result = doMockInvoke(invocation, rpcException);
}
}
} catch (RpcException e) {
if (e.isBiz()) { // 業務異常直接返回
throw e;
}
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
// 非業務異常 調用降級mock邏輯
result = doMockInvoke(invocation, e);
}
}
return result;
}
// 省略其他方法
}
AbstractClusterInvoker.invoke
下一個invoke,應該是FailoverClusterInvoker
,它本身沒有invoke方法,直接進入其父類AbstractClusterInvoker
的invoker方法,有回調它的模板方法doInvoke
public abstract class AbstractClusterInvoker<T> implements Invoker<T> {
// 保存從註冊中心的拉取的所有invoker 目錄
protected Directory<T> directory;
protected boolean availablecheck; // 可用性檢查
private AtomicBoolean destroyed = new AtomicBoolean(false);
// 粘性invoker 是否支持粘性調用
private volatile Invoker<T> stickyInvoker = null;
@Override
public Result invoke(final Invocation invocation) throws RpcException {
checkWhetherDestroyed();
// binding attachments into invocation.
// 隱式參數
// 綁定attachments,Dubbo中,可以通過 RpcContext 上的 setAttachment 和 getAttachment
// 在服務消費方和提供方之間進行參數的隱式傳遞,所以這段代碼中會去綁定attachments
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
// 通過list獲取invoker列表,這個列表是從directory裏面獲得的
List<Invoker<T>> invokers = list(invocation);
// 加載loadBalance
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
// 調用Directory的list方法列舉Invoker
return directory.list(invocation);
}
// 初始化負載均衡策略
protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {
// 通過SPI獲取LoadBalance 默認 random
if (CollectionUtils.isNotEmpty(invokers)) {
return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));
} else {
return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);
}
}
}
FailoverClusterInvoker.doInvoke
容錯機制
容錯的邏輯:failover失敗重試
- 獲得重試的次數,並且進行循環
- 獲得目標服務,並且記錄當前已經調用過的目標服務防止下次繼續將請求發送過去
- 如果執行成功,則返回結果
- 如果出現異常,判斷是否爲業務異常,如果是則拋出,否則,進行下一次重試
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyInvokers = invokers;
checkInvokers(copyInvokers, invocation);
String methodName = RpcUtils.getMethodName(invocation);
// 獲取重試次數 默認2次
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
// 循環調用,失敗重試
for (int i = 0; i < len; i++) {
if (i > 0) {
checkWhetherDestroyed();
// 在進行重試前重新列舉 Invoker,這樣做的好處是,如果某個服務掛了,
// 通過調用 list 可得到最新可用的 Invoker 列表
copyInvokers = list(invocation);
// 對 copyinvokers 進行判空檢查
checkInvokers(copyInvokers, invocation);
}
// 通過負載均衡選擇invoker
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
// 記錄已經調用過的服務,下次調用會進行過濾
invoked.add(invoker);
// 設置 invoked 到 RPC 上下文中
RpcContext.getContext().setInvokers((List) invoked);
try {
// 服務調用成功,直接返回
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + methodName
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
if (e.isBiz()) { // 如果是業務異常,直接拋出不進行重試
throw e;
}
le = e; // 記錄異常信息,進行下一次循環
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
// 若重試失敗,則拋出異常
throw new RpcException(le.getCode(), "Failed to invoke the method "
+ methodName + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
toInvokers
RegistryDirectory
在RegistryDirectory中有一個成員屬性,保存了服務地方地址對應的invoke信息
private volatile Map<String, Invoker<T>> urlInvokerMap;
toInvokers
這個invoker是動態的,基於註冊中心的變化而變化的。它的初始化過程的鏈路是
RegistryDirectory.notify->refreshInvoker->toInvokers
if (enabled) {
invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
}
基於protocol.refer來構建的invoker,並且使用InvokerDelegate進行了委託,在DubboProtocol
中,
是這樣構建invoker的。返回的是一個DubboInvoker對象
// AbstractProtocol#refer
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
}
// DubboProtocol#protocolBindingRefer
public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
綜合上述通過url轉成的invoker是
InvokerDelegate
->QosProtocolWrapper
->ProtocolListenerWrapper
->ProtocolFilterWrapper
->AbstractProtocol
->DubboProtocol
->AsyncToSyncInvoker
// 異步到同步invoke
->DubboInvoker
DubboInvoker
AbstractInvoker#invoke
// 對Invocation的attachments進行處理,把attachment加入到Invocation中
public Result invoke(Invocation inv) throws RpcException {
// if invoker is destroyed due to address refresh from registry, let's allow the current invoke to proceed
if (destroyed.get()) {
logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
+ ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
}
RpcInvocation invocation = (RpcInvocation) inv;
// 設置invoker
invocation.setInvoker(this);
if (CollectionUtils.isNotEmptyMap(attachment)) {
// 設置attachment
invocation.addAttachmentsIfAbsent(attachment);
}
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (CollectionUtils.isNotEmptyMap(contextAttachments)) {
// 添加 contextAttachments 到 RpcInvocation#attachment 變量中
invocation.addAttachments(contextAttachments);
}
// 設置調用模式 同步/異步調用
invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
// 設置異步調用Id
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
AsyncRpcResult asyncResult;
try {
// 抽象方法,有子類實現
asyncResult = (AsyncRpcResult) doInvoke(invocation);
} catch (InvocationTargetException e) { // biz exception
Throwable te = e.getTargetException();
if (te == null) {
asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, te, invocation);
}
} catch (RpcException e) {
if (e.isBiz()) {
asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
} else {
throw e;
}
} catch (Throwable e) {
asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
}
// 封裝CompletableFuture 異步調用返回
RpcContext.getContext().setFuture(new FutureAdapter(asyncResult.getResponseFuture()));
return asyncResult;
}
protected abstract Result doInvoke(Invocation invocation) throws Throwable;
DubboInvoker.doInvoke
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
// 獲取調用方法名
final String methodName = RpcUtils.getMethodName(invocation);
// 設置path和version到attachment中
inv.setAttachment(PATH_KEY, getUrl().getPath());
inv.setAttachment(VERSION_KEY, version);
ExchangeClient currentClient;
// 從clients數組中獲取ExchangeClient
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
// 獲取異步配置
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
// 獲取超時數據 默認是 1s
int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
// oneWay 爲true 表示“單向"通信 沒有返回值 kafka的消費發送方式 也有 oneWay
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent); // 發送請求
return AsyncRpcResult.newDefaultAsyncResult(invocation);
} else { // 有返回值處理
ExecutorService executor = getCallbackExecutor(getUrl(), inv);
CompletableFuture<AppResponse> appResponseFuture =
currentClient.request(inv, timeout, executor).thenApply(obj -> (AppResponse) obj);
// save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
FutureContext.getContext().setCompatibleFuture(appResponseFuture);
AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, inv);
result.setExecutor(executor);
return result;
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
protected ExecutorService getCallbackExecutor(URL url, Invocation inv) {
ExecutorService sharedExecutor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension().getExecutor(url);
if (InvokeMode.SYNC == RpcUtils.getInvokeMode(getUrl(), inv)) {
return new ThreadlessExecutor(sharedExecutor);
} else {
return sharedExecutor;
}
}
ExchangeClient
ReferenceCountExchangeClient(HeaderExchangeClient())
調用鏈路
ReferenceCountExchangeClient->HeaderExchangeClient->HeaderExchangeChannel->(request())
把構建好的RpcInvocation,組裝到一個Request對象中進行傳遞
HeaderExchangeChannel#request()
public CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException {
if (closed) {
throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
}
// 創建請求對象
Request req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);
DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor);
try {
channel.send(req); // NettyClient
} catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}
channel.send(req)
調用鏈路
AbstractPeer.send ->AbstractClient.send->NettyChannel.send
最終 通過NioSocketChannel把消息發送出去
ChannelFuture future = channel.writeAndFlush(msg);