/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alipay.sofa.rpc.bootstrap; import com.alipay.sofa.rpc.client.ClientProxyInvoker; import com.alipay.sofa.rpc.client.Cluster; import com.alipay.sofa.rpc.client.ClusterFactory; import com.alipay.sofa.rpc.client.ProviderGroup; import com.alipay.sofa.rpc.client.ProviderHelper; import com.alipay.sofa.rpc.client.ProviderInfo; import com.alipay.sofa.rpc.client.ProviderInfoAttrs; import com.alipay.sofa.rpc.common.RpcConstants; import com.alipay.sofa.rpc.common.SofaConfigs; import com.alipay.sofa.rpc.common.SofaOptions; import com.alipay.sofa.rpc.common.utils.CommonUtils; import com.alipay.sofa.rpc.common.utils.StringUtils; import com.alipay.sofa.rpc.config.ConsumerConfig; import com.alipay.sofa.rpc.config.RegistryConfig; import com.alipay.sofa.rpc.context.RpcRuntimeContext; import com.alipay.sofa.rpc.core.exception.SofaRpcRuntimeException; import com.alipay.sofa.rpc.dynamic.DynamicConfigKeys; import com.alipay.sofa.rpc.dynamic.DynamicConfigManager; import com.alipay.sofa.rpc.dynamic.DynamicConfigManagerFactory; import com.alipay.sofa.rpc.ext.Extension; import com.alipay.sofa.rpc.invoke.Invoker; import com.alipay.sofa.rpc.listener.ConfigListener; import com.alipay.sofa.rpc.listener.ProviderInfoListener; import com.alipay.sofa.rpc.log.LogCodes; import com.alipay.sofa.rpc.log.Logger; import com.alipay.sofa.rpc.log.LoggerFactory; import com.alipay.sofa.rpc.proxy.ProxyFactory; import com.alipay.sofa.rpc.registry.Registry; import com.alipay.sofa.rpc.registry.RegistryFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * Default consumer bootstrap. * * @author <a href="mailto:[email protected]">GengZhang</a> */ @Extension("sofa") public class DefaultConsumerBootstrap<T> extends ConsumerBootstrap<T> { /** * slf4j Logger for this class */ private final static Logger LOGGER = LoggerFactory.getLogger(DefaultConsumerBootstrap.class); /** * 构造函数 * * @param consumerConfig 服务消费者配置 */ protected DefaultConsumerBootstrap(ConsumerConfig<T> consumerConfig) { super(consumerConfig); } /** * 代理实现类 */ protected transient volatile T proxyIns; /** * 代理的Invoker对象 */ protected transient volatile Invoker proxyInvoker; /** * 调用类 */ protected transient volatile Cluster cluster; /** * 计数器 */ protected transient volatile CountDownLatch respondRegistries; /** * 发布的调用者配置(含计数器) */ protected final static ConcurrentMap<String, AtomicInteger> REFERRED_KEYS = new ConcurrentHashMap<String, AtomicInteger>(); @Override public T refer() { if (proxyIns != null) { return proxyIns; } synchronized (this) { if (proxyIns != null) { return proxyIns; } String key = consumerConfig.buildKey(); String appName = consumerConfig.getAppName(); // 检查参数 checkParameters(); // 提前检查接口类 if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, "Refer consumer config : {} with bean id {}", key, consumerConfig.getId()); } // 注意同一interface,同一tags,同一protocol情况 AtomicInteger cnt = REFERRED_KEYS.get(key); // 计数器 if (cnt == null) { // 没有发布过 cnt = CommonUtils.putToConcurrentMap(REFERRED_KEYS, key, new AtomicInteger(0)); } int c = cnt.incrementAndGet(); int maxProxyCount = consumerConfig.getRepeatedReferLimit(); if (maxProxyCount > 0) { if (c > maxProxyCount) { cnt.decrementAndGet(); // 超过最大数量,直接抛出异常 throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_DUPLICATE_CONSUMER_CONFIG, key, maxProxyCount)); } else if (c > 1) { if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, "Duplicate consumer config with key {} has been referred!" + " Maybe it's wrong config, please check it." + " Ignore this if you did that on purpose!", key); } } } try { // build cluster cluster = ClusterFactory.getCluster(this); // build listeners consumerConfig.setConfigListener(buildConfigListener(this)); consumerConfig.setProviderInfoListener(buildProviderInfoListener(this)); // init cluster cluster.init(); // 构造Invoker对象(执行链) proxyInvoker = buildClientProxyInvoker(this); // 创建代理类 proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(), proxyInvoker); //动态配置 final String dynamicAlias = consumerConfig.getParameter(DynamicConfigKeys.DYNAMIC_ALIAS); if (StringUtils.isNotBlank(dynamicAlias)) { final DynamicConfigManager dynamicManager = DynamicConfigManagerFactory.getDynamicManager( consumerConfig.getAppName(), dynamicAlias); dynamicManager.initServiceConfiguration(consumerConfig.getInterfaceId()); } } catch (Exception e) { if (cluster != null) { cluster.destroy(); cluster = null; } consumerConfig.setConfigListener(null); consumerConfig.setProviderInfoListener(null); cnt.decrementAndGet(); // 发布失败不计数 if (e instanceof SofaRpcRuntimeException) { throw (SofaRpcRuntimeException) e; } else { throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_BUILD_CONSUMER_PROXY), e); } } if (consumerConfig.getOnAvailable() != null && cluster != null) { cluster.checkStateChange(false); // 状态变化通知监听器 } RpcRuntimeContext.cacheConsumerConfig(this); return proxyIns; } } /** * for check fields and parameters of consumer config */ protected void checkParameters() { } /** * Build ConfigListener for consumer bootstrap. * * @param bootstrap ConsumerBootstrap * @return ConfigListener */ protected ConfigListener buildConfigListener(ConsumerBootstrap bootstrap) { return new ConsumerAttributeListener(); } /** * Build ProviderInfoListener for consumer bootstrap. * * @param bootstrap ConsumerBootstrap * @return ProviderInfoListener */ protected ProviderInfoListener buildProviderInfoListener(ConsumerBootstrap bootstrap) { return new ClusterProviderInfoListener(bootstrap.getCluster()); } /** * Build ClientProxyInvoker for consumer bootstrap. * * @param bootstrap ConsumerBootstrap * @return ClientProxyInvoker */ protected ClientProxyInvoker buildClientProxyInvoker(ConsumerBootstrap bootstrap) { return new DefaultClientProxyInvoker(bootstrap); } @Override public void unRefer() { if (proxyIns == null) { return; } String key = consumerConfig.buildKey(); String appName = consumerConfig.getAppName(); if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, "UnRefer consumer config : {} with bean id {}", key, consumerConfig.getId()); } try { cluster.destroy(); } catch (Exception e) { if (LOGGER.isWarnEnabled(appName)) { LOGGER.warnWithApp(appName, "Catch exception when unrefer consumer config : " + key + ", but you can ignore if it's called by JVM shutdown hook", e); } } // 清除一些缓存 AtomicInteger cnt = REFERRED_KEYS.get(key); if (cnt != null && cnt.decrementAndGet() <= 0) { REFERRED_KEYS.remove(key); } consumerConfig.setConfigListener(null); consumerConfig.setProviderInfoListener(null); RpcRuntimeContext.invalidateConsumerConfig(this); proxyIns = null; // 取消订阅到注册中心 unSubscribe(); } @Override public List<ProviderGroup> subscribe() { List<ProviderGroup> result = null; String directUrl = consumerConfig.getDirectUrl(); if (StringUtils.isNotEmpty(directUrl)) { // 如果走直连 result = subscribeFromDirectUrl(directUrl); } else { // 没有配置url直连 List<RegistryConfig> registryConfigs = consumerConfig.getRegistry(); if (CommonUtils.isNotEmpty(registryConfigs)) { // 从多个注册中心订阅服务列表 result = subscribeFromRegistries(); } } return result; } @Override public boolean isSubscribed() { return respondRegistries == null || respondRegistries.getCount() <= 0; } /** * Subscribe provider list from direct url * * @param directUrl direct url of consume config * @return Provider group list */ protected List<ProviderGroup> subscribeFromDirectUrl(String directUrl) { List<ProviderGroup> result = new ArrayList<ProviderGroup>(); List<ProviderInfo> tmpProviderInfoList = new ArrayList<ProviderInfo>(); String[] providerStrs = StringUtils.splitWithCommaOrSemicolon(directUrl); for (String providerStr : providerStrs) { ProviderInfo providerInfo = convertToProviderInfo(providerStr); if (providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_SOURCE) == null) { providerInfo.setStaticAttr(ProviderInfoAttrs.ATTR_SOURCE, "direct"); } tmpProviderInfoList.add(providerInfo); } result.add(new ProviderGroup(RpcConstants.ADDRESS_DIRECT_GROUP, tmpProviderInfoList)); return result; } /** * Convert provider string to provider info * * @param providerStr provider url * @return ProviderInfo */ protected ProviderInfo convertToProviderInfo(String providerStr) { return ProviderHelper.toProviderInfo(providerStr); } /** * Subscribe provider list from all registries, the providers will be merged. * * @return Provider group list */ protected List<ProviderGroup> subscribeFromRegistries() { List<ProviderGroup> result = new ArrayList<ProviderGroup>(); List<RegistryConfig> registryConfigs = consumerConfig.getRegistry(); if (CommonUtils.isEmpty(registryConfigs)) { return result; } // 是否等待结果 int addressWaitTime = consumerConfig.getAddressWait(); int maxAddressWaitTime = SofaConfigs.getIntegerValue(consumerConfig.getAppName(), SofaOptions.CONFIG_MAX_ADDRESS_WAIT_TIME, SofaOptions.MAX_ADDRESS_WAIT_TIME); addressWaitTime = addressWaitTime < 0 ? maxAddressWaitTime : Math.min(addressWaitTime, maxAddressWaitTime); ProviderInfoListener listener = consumerConfig.getProviderInfoListener(); respondRegistries = addressWaitTime == 0 ? null : new CountDownLatch(registryConfigs.size()); // 从注册中心订阅 {groupName: ProviderGroup} Map<String, ProviderGroup> tmpProviderInfoList = new HashMap<String, ProviderGroup>(); for (RegistryConfig registryConfig : registryConfigs) { Registry registry = RegistryFactory.getRegistry(registryConfig); registry.init(); registry.start(); try { List<ProviderGroup> current; try { if (respondRegistries != null) { consumerConfig.setProviderInfoListener(new WrapperClusterProviderInfoListener(listener, respondRegistries)); } current = registry.subscribe(consumerConfig); } finally { if (respondRegistries != null) { consumerConfig.setProviderInfoListener(listener); } } if (current == null) { continue; // 未同步返回结果 } else { if (respondRegistries != null) { respondRegistries.countDown(); } } for (ProviderGroup group : current) { // 当前注册中心的 String groupName = group.getName(); if (!group.isEmpty()) { ProviderGroup oldGroup = tmpProviderInfoList.get(groupName); if (oldGroup != null) { oldGroup.addAll(group.getProviderInfos()); } else { tmpProviderInfoList.put(groupName, group); } } } } catch (SofaRpcRuntimeException e) { throw e; } catch (Throwable e) { String appName = consumerConfig.getAppName(); if (LOGGER.isWarnEnabled(appName)) { LOGGER.warnWithApp(appName, LogCodes.getLog(LogCodes.ERROR_SUBSCRIBE_FROM_REGISTRY, registryConfig.getId()), e); } } } if (respondRegistries != null) { try { respondRegistries.await(addressWaitTime, TimeUnit.MILLISECONDS); } catch (Exception ignore) { // NOPMD } } return new ArrayList<ProviderGroup>(tmpProviderInfoList.values()); } /** * 取消订阅服务列表 */ public void unSubscribe() { if (StringUtils.isEmpty(consumerConfig.getDirectUrl()) && consumerConfig.isSubscribe()) { List<RegistryConfig> registryConfigs = consumerConfig.getRegistry(); if (registryConfigs != null) { for (RegistryConfig registryConfig : registryConfigs) { Registry registry = RegistryFactory.getRegistry(registryConfig); try { registry.unSubscribe(consumerConfig); } catch (Exception e) { String appName = consumerConfig.getAppName(); if (LOGGER.isWarnEnabled(appName)) { LOGGER.warnWithApp(appName, "Catch exception when unSubscribe from registry: " + registryConfig.getId() + ", but you can ignore if it's called by JVM shutdown hook", e); } } } } } } /** * Wrapper provider info listener to record the respond status of registry. */ class WrapperClusterProviderInfoListener implements ProviderInfoListener { /** * Origin provider info listener */ private ProviderInfoListener providerInfoListener; /** * CountDownLatch of respond registries. */ private CountDownLatch respondRegistries; /** * Has been respond */ private AtomicBoolean hasRespond = new AtomicBoolean(false); public WrapperClusterProviderInfoListener(ProviderInfoListener providerInfoListener, CountDownLatch respondRegistries) { this.providerInfoListener = providerInfoListener; this.respondRegistries = respondRegistries; } private void doCountDown() { if (respondRegistries != null && hasRespond.compareAndSet(false, true)) { respondRegistries.countDown(); respondRegistries = null; } } @Override public void addProvider(ProviderGroup group) { providerInfoListener.addProvider(group); doCountDown(); } @Override public void removeProvider(ProviderGroup group) { providerInfoListener.removeProvider(group); } @Override public void updateProviders(ProviderGroup group) { providerInfoListener.updateProviders(group); doCountDown(); } @Override public void updateAllProviders(List<ProviderGroup> groups) { providerInfoListener.updateAllProviders(groups); doCountDown(); } } /** * Consumer配置发生变化监听器 */ private class ConsumerAttributeListener implements ConfigListener { @Override public void configChanged(Map newValue) { } @Override public synchronized void attrUpdated(Map newValueMap) { String appName = consumerConfig.getAppName(); // 重要: proxyIns不能换,只能换cluster。。。。 // 修改调用的tags cluster(loadblance) timeout, retries? Map<String, String> newValues = (Map<String, String>) newValueMap; Map<String, String> oldValues = new HashMap<String, String>(); boolean rerefer = false; try { // 检查是否有变化 // 是否过滤map? for (Map.Entry<String, String> entry : newValues.entrySet()) { String newValue = entry.getValue(); String oldValue = consumerConfig.queryAttribute(entry.getKey()); boolean changed = oldValue == null ? newValue != null : !oldValue.equals(newValue); if (changed) { // 记住旧的值 oldValues.put(entry.getKey(), oldValue); } rerefer = rerefer || changed; } } catch (Exception e) { LOGGER.errorWithApp(appName, LogCodes.getLog(LogCodes.ERROR_CONSUMER_ATTRIBUTE_COMPARING), e); return; } if (rerefer) { try { unSubscribe();// 取消订阅旧的 for (Map.Entry<String, String> entry : newValues.entrySet()) { // change attrs consumerConfig.updateAttribute(entry.getKey(), entry.getValue(), true); } // 需要重新发布 if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, "Rerefer consumer {}", consumerConfig.buildKey()); } } catch (Exception e) { // 切换属性出现异常 LOGGER.errorWithApp(appName, LogCodes.getLog(LogCodes.ERROR_CONSUMER_ATTRIBUTE_CHANGE), e); for (Map.Entry<String, String> entry : oldValues.entrySet()) { //rollback old attrs consumerConfig.updateAttribute(entry.getKey(), entry.getValue(), true); } subscribe(); // 重新订阅回滚后的旧的 return; } try { switchCluster(); } catch (Exception e) { //切换客户端出现异常 LOGGER.errorWithApp(appName, LogCodes.getLog(LogCodes.ERROR_CONSUMER_REFER_AFTER_CHANGE), e); unSubscribe(); // 取消订阅新的 for (Map.Entry<String, String> entry : oldValues.entrySet()) { //rollback old attrs consumerConfig.updateAttribute(entry.getKey(), entry.getValue(), true); } subscribe(); // 重新订阅回滚后的旧的 } } } /** * Switch cluster. * * @throws Exception the exception */ private void switchCluster() throws Exception { Cluster newCluster = null; Cluster oldCluster; try { // 构建新的 newCluster = ClusterFactory.getCluster(DefaultConsumerBootstrap.this); //生成新的 会再重新订阅 newCluster.init(); oldCluster = ((ClientProxyInvoker) proxyInvoker).setCluster(newCluster); } catch (Exception e) { if (newCluster != null) { newCluster.destroy(); } if (e instanceof SofaRpcRuntimeException) { throw e; } else { throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_SWITCH_CLUSTER_NEW), e); } } try { // 切换 cluster = newCluster; if (oldCluster != null) { oldCluster.destroy(); // 旧的关掉 } } catch (Exception e) { String appName = consumerConfig.getAppName(); if (LOGGER.isWarnEnabled(appName)) { LOGGER.warnWithApp(appName, LogCodes.getLog(LogCodes.WARN_SWITCH_CLUSTER_DESTROY), e); } } } } @Override public Cluster getCluster() { return cluster; } @Override public T getProxyIns() { return proxyIns; } /** * 得到实现代理类Invoker * * @return 实现代理类Invoker proxy invoker */ public Invoker getProxyInvoker() { return proxyInvoker; } }