主要工作
-
獲取invoker對象
a)註冊消費端的臨時節點
b) 訂閱以下的節點
/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers
c) 當這些節點下的子節點發生變化: 刷新本地的RequestDirector的本地urlInvokerMap列表
第一次初始化invoker,創建DubboInvoker。啓動netty客戶端連接 -
將invoker包裝在代理對象Proxy$0中。返回代理對象
流程
主流程
消費端接口初始化或者有提供者節點變化時,通知更新本地zookeeper緩存文件
消費端接口初始化或者有提供者節點變化時,通知更新本地的invoker列表 urlInvokerMap
在更新本地的invoker列表時,沒有生成過Invoker,需要指定Invoker且netty客戶端連接服務端
入口
ReferenceBean 實現了FactoryBean, 那麼在從io容器獲取Bean時,就調用了Object getObject()
private transient volatile T ref; //保存代理對象
當有對象時,直接返回,否則初始化
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
init()組裝參數,最終調用 ref = createProxy(map);
- 封裝urls
用戶指定URL,指定的URL可能是對點對直連地址;否則通過註冊中心配置拼裝URL
//用戶指定URL,指定的URL可能是對點對直連地址,也可能是註冊中心URL
if (url != null && url.length() > 0) {
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else {
//通過註冊中心配置拼裝URL
List<URL> us = loadRegistries(false);
if (us != null && !us.isEmpty()) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.isEmpty()) {
throw new IllegalStateException();
}
}
- 獲取invoker
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url;
}
}
//有註冊中心的地址, StaticDirectory
if (registryURL != null) {
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // not a registry url
invoker = cluster.join(new StaticDirectory(invokers));
}
}
獲取invoker對象
refprotocol.refer(interfaceClass, url)
refprotocol這裏的對象是RegistryProtocol
1.獲取Registry:ZookeeperRegistry
//將registry協議改成zookeeper。
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
2.向zookeeper註冊consumer://10.118.14.24/com.test.ITestService節點
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
a)將url保存到註冊列表中
b)創建提供者的臨時節點
c)出現異常時,開啓啓動時檢測,直接拋出異常;否則記錄到failedRegistered,等待定時重新註冊
3.創建RegistryDirectory,並且訂閱consumer://10.118.14.24/com.test.ITestService
consumer://10.118.14.24/com.test.ITestService?category=providers,configurators,routers&side=consumer…
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0,
type.getName(), directory.getUrl().getParameters());
//加參數category:providers,configurators,routers
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
4.cluster.join(directory);
默認是FailoverCluster, 所以返回的invoker對象是MockClusterInvoker(FailoverClusterInvoker)
mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
failfast=com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
failsafe=com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
failback=com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
forking=com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
available=com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
mergeable=com.alibaba.dubbo.rpc.cluster.support.MergeableCluster
broadcast=com.alibaba.dubbo.rpc.cluster.support.BroadcastCluster
創建代理對象
上面創建了invoker對象
proxyFactory.getProxy(invoker)
StubProxyFactoryWrapper(JavassistProxyFactory)
stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
- JavassistProxyFactory創建代理對象
獲取代理方法
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
攔截器
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler){
this.invoker = handler;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
總結
從ioc獲取的對象是代理對象Proxy$0
交給InvokerInvocationHandler進行處理->MockClusterInvoker -> FailoverClusterInvoker-> RegistryDirectory->invoke列表
以下是分支流程=======================
zookeeper 訂閱
RegistryDirectory 實現了NotifyListener, 本身就是個監聽器
調用RegistryDirectory的subscribe,registry就是ZookeeperRegistry
而訂閱直接交給registry去處理, 而其中的this是RegistryDirectory
public void subscribe(URL url) {
setConsumerUrl(url);
registry.subscribe(url, this);
}
一:url和RegistryDirectory保存在subscribed中
consumer://10.118.14.24/com.test.ITestService?category=providers,configurators,routers&side=consumer…
二:ZookeeperRegistry 訂閱url
調用ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener) ;
consumer://10.118.14.24/10.118.14.24/com.test.ITestService?category=providers,configurators,routers&default.check=false&default.cluster=failfast&default.timeout=60000&dubbo=2.8.4&interface=com.insaic.itbase.service.BusinessNoGeneratorService&logger=slf4j&methods=test,save&pid=10132&revision=1.0-20191223.094125-383&side=consumer×tamp=1577323081022&version=1.0.0
- I) 創建接口下providers、configurators、routers的路徑,並且增加他們的子節點變化監聽器
/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers
private String[] toCategoriesPath(URL url) {
String[] categroies;
//接口是* ,那麼categroies 就是全量
if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
categroies = new String[] {Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
} else {
//從url中獲取category
categroies = url.getParameter(Constants.CATEGORY_KEY, new String[] {Constants.DEFAULT_CATEGORY});
}
String[] paths = new String[categroies.length];
for (int i = 0; i < categroies.length; i ++) {
//dubbo+接口+category
paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categroies[i];
}
return paths;
}
以下是遍歷這些路徑path
-------> 1. 獲取ChildListener子節點監聽器
查看是否創建了監聽器
沒有,對/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers
初始化監聽器
子節點變化通知當前節點子節點有變化,見下一節
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
public void childChanged(String parentPath, List<String> currentChilds) {
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
zkListener = listeners.get(listener);
}
------->2. 創建持久化節點,並且增加子節點監聽器
/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
------->3. 獲取當前com.test.ITestService的下的providers、routes、configurators所有的子節點
在第一步遍歷providers時,當前子節點是
dubbo://10.118.22.51:20710/com.test.ITestService?default.cluster=failfast&default.timeout=3000&dubbo=2.8.4&generic=false&interface=com.test.ITestService&logger=slf4j&methods=test,save&pid=3091&revision=1.0.0&side=provider×tamp=1576650287438&version=1.0.0
當configurators和routers當前沒有子節點,就封裝成
configurators:
empty://10.118.14.24/com.test.ITestService?&category=configurators&side=consumer×tamp=1577329888735&version=1.0.0
routers:
empty://10.118.14.24/com.test.ITestService?&category=routers&side=consumer×tamp=1577329888735&version=1.0.0
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
//當沒有子節點時,對consumer地址將協議改成empty,增加對應的category
private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
if (urls.isEmpty()) {
int i = path.lastIndexOf('/');
String category = i < 0 ? path : path.substring(i + 1);
URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
urls.add(empty);
}
return urls;
}
- II)通知子節點發生變化
見下一節
訂閱節點的子節點發生變化
notify(url, listener, urls);
url:消費者的url
urls: com.test.ITestService的下的providers、routes、configurators變化的子節點。
listener: RegistryDirectory
- 場景
1. 在訂閱初始化的時候 (providers、routes、configurators全量)
url: 當前節點
consumer://10.118.14.24/com.test.ITestService?category=providers,configurators,routers&default.check=false&default.cluster=failfast&default.timeout=60000&dubbo=2.8.4&side=consumer×tamp=1577329888735&version=1.0.0…
listener: RegistryDirectory
urls:當前com.test.ITestService的下的providers、routes、configurators所有的子節點
dubbo://10.118.22.51:20710/com.test.ITestService?default.cluster=failfast&default.timeout=3000&dubbo=2.8.4&generic=false&interface=com.test.ITestService&logger=slf4j&methods=test,save&pid=3091&revision=1.0.0&side=provider×tamp=1576650287438&version=1.0.0
empty://10.118.14.24/com.test.ITestService?&category=configurators&side=consumer×tamp=1577329888735&version=1.0.0
empty://10.118.14.24/com.test.ITestService?&category=routers&side=consumer×tamp=1577329888735&version=1.0.0
2. 子節點發生變化時(providers、routes、configurators哪個有變化才調用)
new ChildListener() {
public void childChanged(String parentPath, List<String> currentChilds) {
ZookeeperRegistry.this.notify(url, listener,
toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
providers、routes、configurators任意一個的子節點發生變化就調用notify。
url: 當前的消費者url
currentChilds是所有的子節點路徑
parentPath:providers、routes、configurators
- 源碼
1. 對當前的url進行分類。按照category(providers、routes、configurators)分類, 並且放入到notified中
Map<String, List<URL>> result = new HashMap<String, List<URL>>();
for (URL u : urls) {
if (UrlUtils.isMatch(url, u)) {
String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
List<URL> categoryList = result.get(category);
if (categoryList == null) {
categoryList = new ArrayList<URL>();
result.put(category, categoryList);
}
categoryList.add(u);
}
}
2. 保存urls到文件中和properties
private void saveProperties(URL url) {
if (file == null) {
return;
}
//組裝內容
StringBuilder buf = new StringBuilder();
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified != null) {
for (List<URL> us : categoryNotified.values()) {
for (URL u : us) {
if (buf.length() > 0) {
//空格分隔符
buf.append(' ');
}
buf.append(u.toFullString());
}
}
}
//放入到properties
properties.setProperty(url.getServiceKey(), buf.toString());
long version = lastCacheChanged.incrementAndGet();
//保存文件
if (syncSaveFile) {
doSaveProperties(version);
} else {
registryCacheExecutor.execute(new SaveProperties(version));
}
}
先創建dubbo-registry-10.118.22.55.cache.lock 空文件, 對空文件進行上鎖,保存文件。然後再釋放鎖。
public void doSaveProperties(long version) {
// 當前版本小於最後一次,不再保存
if(version < lastCacheChanged.get()){
return;
}
if (file == null) {
return;
}
Properties newProperties = new Properties();
// 保存之前先讀取一遍,防止多個註冊中心之間衝突
InputStream in = null;
if (file.exists()) {
in = new FileInputStream(file);
newProperties.load(in);
}
// 保存
try {
newProperties.putAll(properties);
//先創建dubbo-registry-10.118.22.55.cache.lock 空文件
File lockfile = new File(file.getAbsolutePath() + ".lock");
if (!lockfile.exists()) {
lockfile.createNewFile();
}
//先對空文件進行上鎖
RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
try {
FileChannel channel = raf.getChannel();
try {
FileLock lock = channel.tryLock();
if (lock == null) {
throw new IOException("");
}
// 保存
try {
if (! file.exists()) {
file.createNewFile();
}
FileOutputStream outputFile = new FileOutputStream(file);
try {
newProperties.store(outputFile, "Dubbo Registry Cache");
} finally {
outputFile.close();
}
} finally {
lock.release();
}
} finally {
channel.close();
}
} finally {
raf.close();
}
} catch (Throwable e) {
if (version < lastCacheChanged.get()) {
return;
} else {
registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
}
logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
}
}
3.listener.notify(categoryList); 刷新本地的invoker
見下一節
RegistryDirectory.notify 刷新本地的invoker列表
- 分類
routerUrls : category是routers或者protocol是route
configuratorUrls : category是configurators或者override是route
invokerUrls : category是providers
List<URL> invokerUrls = new ArrayList<URL>();
List<URL> routerUrls = new ArrayList<URL>();
List<URL> configuratorUrls = new ArrayList<URL>();
for (URL url : urls) {
String protocol = url.getProtocol();
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
if ("routers".equals(category) || "route".equals(protocol)) {
routerUrls.add(url);
} else if ("configurators".equals(category) || "override".equals(protocol)) {
configuratorUrls.add(url);
} else if (“”providers“”.equals(category)) {
invokerUrls.add(url);
}
}
- 獲取List<Configurator> configurators
對configuratorUrls 循環處理, configuratorFactory.getConfigurator(url)
根據協議來判斷來做處理
override=com.alibaba.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
absent=com.alibaba.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
- 獲取List<Router> routers
Router router = routerFactory.getRouter(url);
file=com.alibaba.dubbo.rpc.cluster.router.file.FileRouterFactory
script=com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouterFactory
condition=com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
- 刷新本地的invoker列表
最終將生成的invoker列表保存在本地urlInvokerMap中
private void refreshInvoker(List<URL> invokerUrls){
//....
this.forbidden = false; // 允許訪問
//舊的invoker列表
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap;
if (invokerUrls.size() ==0 ){
return;
}
// 將URL列表轉成Invoker列表
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;
// 換方法名映射Invoker列表
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
//沒有新的invoker,則不再處理
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
return ;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
//保存到本地urlInvokerMap中
this.urlInvokerMap = newUrlInvokerMap;
try{
//銷燬未使用的invoker列表(在舊的invoker列表中,不在新的invoker列表)
destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap);
}catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
重點:獲取新的invoker列表
a) 合併url參數 順序爲override >Consumer > Provider
b) 查看urlInvokerMap中是否有了,沒有重新生成消費端的invoker代理對象
new InvokerDelegete(protocol.refer(serviceType, url), url, providerUrl);見下一節
c)銷燬 未使用的invoker列表( in(老的invoker列表) and not in(新的invoker列表))
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
if(urls == null || urls.size() == 0){
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<String>();
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
//查看協議是否支持
if (! ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
continue;
}
//合併url (提供者的url和消費者的url進行合併)
URL url = mergeUrl(providerUrl);
String key = url.toFullString(); // URL參數是排序的
if (keys.contains(key)) { // 重複URL
continue;
}
keys.add(key);
// 緩存key爲沒有合併消費端參數的URL,不管消費端如何合併參數,如果服務端URL發生變化,則重新refer
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 緩存中沒有,重新refer
//...查看是否增加disabled或者enabled
boolean enabled = true;
if (enabled) {
invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
}
if (invoker != null) { // 將新的引用放入緩存
newUrlInvokerMap.put(key, invoker);
}
}else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
合併url參數 順序爲override > -D >Consumer > Provider
private URL mergeUrl(URL providerUrl){
providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // 合併消費端參數
//對提供者進行處理
List<Configurator> localConfigurators = this.configurators;
if (localConfigurators != null && localConfigurators.size() > 0) {
for (Configurator configurator : localConfigurators) {
providerUrl = configurator.configure(providerUrl);
}
}
providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不檢查連接是否成功,總是創建Invoker!
//directoryUrl 與 override 合併是在notify的最後,這裏不能夠處理
this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // 合併提供者參數
return providerUrl;
}
DubboProtocol.refer
ProtocolFilterWrapper:
過濾所有的consumer下的Filter,循環對invoker進行包裝
- 創建DubboInvoker並且保存在invokers
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
- 初始化netty客戶端
private ExchangeClient[] getClients(URL url){
//是否共享連接
boolean service_share_connect = false;
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
//如果connections不配置,則共享連接,否則每服務每連接
if (connections == 0){
service_share_connect = true;
connections = 1;
}
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
if (service_share_connect){
clients[i] = getSharedClient(url);
} else {
clients[i] = initClient(url);
}
}
return clients;
}
/**
*獲取共享連接
*/
private ExchangeClient getSharedClient(URL url){
String key = url.getAddress();
ReferenceCountExchangeClient client = referenceClientMap.get(key);
if ( client != null ){
if ( !client.isClosed()){
client.incrementAndGetCount();
return client;
} else {
referenceClientMap.remove(key);
}
}
ExchangeClient exchagneclient = initClient(url);
client = new ReferenceCountExchangeClient(exchagneclient, ghostClientMap);
referenceClientMap.put(key, client);
ghostClientMap.remove(key);
return client;
}
初始化客戶端
/**
* 創建新連接.
*/
private ExchangeClient initClient(URL url) {。
//默認開啓heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
ExchangeClient client ;
try {
//設置連接應該是lazy的
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
client = new LazyConnectExchangeClient(url ,requestHandler);
} else {
client = Exchangers.connect(url ,requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url
+ "): " + e.getMessage(), e);
}
return client;
}
netty客戶端連接
創建客戶端連接 Exchangers.connect(url ,requestHandler)
new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))))
- HeaderExchangeClient
初始化時,開啓定時心跳
this.channel = new HeaderExchangeChannel(client);
定時發送心跳數據給服務端,並且超時了,重連服務端
heatbeatTimer = scheduled.scheduleWithFixedDelay(
new HeartBeatTask( new HeartBeatTask.ChannelProvider() {
public Collection<Channel> getChannels() {
return Collections.<Channel>singletonList( HeaderExchangeClient.this );
}
}, heartbeat, heartbeatTimeout),
heartbeat, heartbeat, TimeUnit.MILLISECONDS );
//上次讀取的時間
Long lastRead = ( Long ) channel.getAttribute(
HeaderExchangeHandler.KEY_READ_TIMESTAMP );
//上次寫入的時間
Long lastWrite = ( Long ) channel.getAttribute(
HeaderExchangeHandler.KEY_WRITE_TIMESTAMP );
//在時間內, 發送心跳信息給服務端
if ( ( lastRead != null && now - lastRead > heartbeat )
|| ( lastWrite != null && now - lastWrite > heartbeat ) ) {
Request req = new Request();
req.setVersion( "2.0.0" );
req.setTwoWay( true );
req.setEvent( Request.HEARTBEAT_EVENT );
channel.send( req );
}
//超時了,重連
if ( lastRead != null && now - lastRead > heartbeatTimeout ) {
if (channel instanceof Client) {
((Client)channel).reconnect();
} else {
channel.close();
}
}
- netty客戶端初始化 new NettyClient
初始化
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
bootstrap = new ClientBootstrap(channelFactory);
// config
// @see org.jboss.netty.channel.socket.SocketChannelConfig
bootstrap.setOption("keepAlive", true);
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("connectTimeoutMillis", getTimeout());
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
}
連接
啓動定時,查看是否連接,沒有的話,重新連接
protected void connect() throws RemotingException {
connectLock.lock();
try {
if (isConnected()) {
return;
}
//啓動
initConnectStatusCheckCommand();
doConnect();
if (! isConnected()) {
throw new RemotingException();
}
reconnect_count.set(0);
reconnect_error_log_flag.set(false);
} finally {
connectLock.unlock();
}
}
等待服務器的響應
protected void doConnect() throws Throwable {
long start = System.currentTimeMillis();
ChannelFuture future = bootstrap.connect(getConnectAddress());
try{
//等待
boolean ret = future.awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
//連接成功
if (ret && future.isSuccess()) {
Channel newChannel = future.getChannel();
newChannel.setInterestOps(Channel.OP_READ_WRITE);
try {
// 關閉舊的連接
Channel oldChannel = NettyClient.this.channel; // copy reference
if (oldChannel != null) {
try {
oldChannel.close();
} finally {
NettyChannel.removeChannelIfDisconnected(oldChannel);
}
}
} finally {
//新連接也是關閉的,將新連接關閉
if (NettyClient.this.isClosed()) {
try {
newChannel.close();
} finally {
NettyClient.this.channel = null;
NettyChannel.removeChannelIfDisconnected(newChannel);
}
} else {
//賦值
NettyClient.this.channel = newChannel;
}
}
} else if (future.getCause() != null) {
throw new RemotingException();
} else {
throw new RemotingException();
}
}finally{
if (! isConnected()) {
future.cancel();
}
}
}