/*
* Copyright 2009-2016 Weibo, Inc.
*
* Licensed 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.weibo.api.motan.transport.support;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.weibo.api.motan.common.URLParamType;
import com.weibo.api.motan.core.extension.ExtensionLoader;
import com.weibo.api.motan.exception.MotanErrorMsgConstant;
import com.weibo.api.motan.exception.MotanFrameworkException;
import com.weibo.api.motan.rpc.URL;
import com.weibo.api.motan.transport.Client;
import com.weibo.api.motan.transport.Endpoint;
import com.weibo.api.motan.transport.EndpointFactory;
import com.weibo.api.motan.transport.EndpointManager;
import com.weibo.api.motan.transport.HeartbeatFactory;
import com.weibo.api.motan.transport.MessageHandler;
import com.weibo.api.motan.transport.Server;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.MotanFrameworkUtil;
/**
*
* abstract endpoint factory
*
* <pre>
* 一些约定:
*
* 1) service :
* 1.1) not share channel : 某个service暴露服务的时候,不期望和别的service共享服务,明哲自保,比如你说:我很重要,我很重要。
*
* 1.2) share channel : 某个service 暴露服务的时候,如果有某个模块,但是拆成10个接口,可以使用这种方式,不过有一些约束条件:接口的几个serviceConfig配置需要保持一致。
*
* 不允许差异化的配置如下:
* protocol, codec , serialize, maxContentLength , maxServerConnection , maxWorkerThread, workerQueueSize, heartbeatFactory
*
* 2)心跳机制:
*
* 不同的protocol的心跳包格式可能不一样,无法进行强制,那么通过可扩展的方式,依赖heartbeatFactory进行heartbeat包的创建,
* 同时对于service的messageHandler进行wrap heartbeat包的处理。
*
* 对于service来说,把心跳包当成普通的request处理,因为这种heartbeat才能够探测到整个service处理的关键路径的可用状况
*
* </pre>
*
*
* @author maijunsheng
* @version 创建时间:2013-6-5
*
*/
public abstract class AbstractEndpointFactory implements EndpointFactory {
/** 维持share channel 的service列表 **/
protected Map<String, Server> ipPort2ServerShareChannel = new HashMap<String, Server>();
protected ConcurrentMap<Server, Set<String>> server2UrlsShareChannel = new ConcurrentHashMap<Server, Set<String>>();
private EndpointManager heartbeatClientEndpointManager = null;
public AbstractEndpointFactory() {
heartbeatClientEndpointManager = new HeartbeatClientEndpointManager();
heartbeatClientEndpointManager.init();
}
@Override
public Server createServer(URL url, MessageHandler messageHandler) {
HeartbeatFactory heartbeatFactory = getHeartbeatFactory(url);
messageHandler = heartbeatFactory.wrapMessageHandler(messageHandler);
synchronized (ipPort2ServerShareChannel) {
String ipPort = url.getServerPortStr();
String protocolKey = MotanFrameworkUtil.getProtocolKey(url);
boolean shareChannel =
url.getBooleanParameter(URLParamType.shareChannel.getName(), URLParamType.shareChannel.getBooleanValue());
if (!shareChannel) { // 独享一个端口
LoggerUtil.info(this.getClass().getSimpleName() + " create no_share_channel server: url={}", url);
// 如果端口已经被使用了,使用该server bind 会有异常
return innerCreateServer(url, messageHandler);
}
LoggerUtil.info(this.getClass().getSimpleName() + " create share_channel server: url={}", url);
Server server = ipPort2ServerShareChannel.get(ipPort);
if (server != null) {
// can't share service channel
if (!MotanFrameworkUtil.checkIfCanShallServiceChannel(server.getUrl(), url)) {
throw new MotanFrameworkException(
"Service export Error: share channel but some config param is different, protocol or codec or serialize or maxContentLength or maxServerConnection or maxWorkerThread or heartbeatFactory, source="
+ server.getUrl() + " target=" + url, MotanErrorMsgConstant.FRAMEWORK_EXPORT_ERROR);
}
saveEndpoint2Urls(server2UrlsShareChannel, server, protocolKey);
return server;
}
url = url.createCopy();
url.setPath(""); // 共享server端口,由于有多个interfaces存在,所以把path设置为空
server = innerCreateServer(url, messageHandler);
ipPort2ServerShareChannel.put(ipPort, server);
saveEndpoint2Urls(server2UrlsShareChannel, server, protocolKey);
return server;
}
}
@Override
public Client createClient(URL url) {
LoggerUtil.info(this.getClass().getSimpleName() + " create client: url={}", url);
return createClient(url, heartbeatClientEndpointManager);
}
@Override
public void safeReleaseResource(Server server, URL url) {
safeReleaseResource(server, url, ipPort2ServerShareChannel, server2UrlsShareChannel);
}
@Override
public void safeReleaseResource(Client client, URL url) {
destory(client);
}
private <T extends Endpoint> void safeReleaseResource(T endpoint, URL url, Map<String, T> ipPort2Endpoint,
ConcurrentMap<T, Set<String>> endpoint2Urls) {
boolean shareChannel = url.getBooleanParameter(URLParamType.shareChannel.getName(), URLParamType.shareChannel.getBooleanValue());
if (!shareChannel) {
destory(endpoint);
return;
}
synchronized (ipPort2Endpoint) {
String ipPort = url.getServerPortStr();
String protocolKey = MotanFrameworkUtil.getProtocolKey(url);
if (endpoint != ipPort2Endpoint.get(ipPort)) {
destory(endpoint);
return;
}
Set<String> urls = endpoint2Urls.get(endpoint);
urls.remove(protocolKey);
if (urls.isEmpty()) {
destory(endpoint);
ipPort2Endpoint.remove(ipPort);
endpoint2Urls.remove(endpoint);
}
}
}
private <T> void saveEndpoint2Urls(ConcurrentMap<T, Set<String>> map, T endpoint, String namespace) {
Set<String> sets = map.get(endpoint);
if (sets == null) {
sets = new HashSet<String>();
sets.add(namespace);
map.putIfAbsent(endpoint, sets); // 规避并发问题,因为有release逻辑存在,所以这里的sets预先add了namespace
sets = map.get(endpoint);
}
sets.add(namespace);
}
private HeartbeatFactory getHeartbeatFactory(URL url) {
String heartbeatFactoryName = url.getParameter(URLParamType.heartbeatFactory.getName(), URLParamType.heartbeatFactory.getValue());
HeartbeatFactory heartbeatFactory = ExtensionLoader.getExtensionLoader(HeartbeatFactory.class).getExtension(heartbeatFactoryName);
if (heartbeatFactory == null) {
throw new MotanFrameworkException("HeartbeatFactory not exist: " + heartbeatFactoryName);
}
return heartbeatFactory;
}
private Client createClient(URL url, EndpointManager endpointManager) {
Client client = innerCreateClient(url);
endpointManager.addEndpoint(client);
return client;
}
private <T extends Endpoint> void destory(T endpoint) {
if (endpoint instanceof Client) {
endpoint.close();
heartbeatClientEndpointManager.removeEndpoint(endpoint);
} else {
endpoint.close();
}
}
public Map<String, Server> getShallServerChannels() {
return Collections.unmodifiableMap(ipPort2ServerShareChannel);
}
public EndpointManager getEndpointManager() {
return heartbeatClientEndpointManager;
}
protected abstract Server innerCreateServer(URL url, MessageHandler messageHandler);
protected abstract Client innerCreateClient(URL url);
}