Server
在暴露服务,执行到DubboProtocol的export方法的时候,会调用openServer方法,从这里开始,就是Dubbo开启请求监听的地方
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client 也可以暴露一个只有server可以调用的服务。
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
if (isServer) {
ExchangeServer server = serverMap.get(key);
if (server == null) {
serverMap.put(key, createServer(url));
} else {
//server支持reset,配合override功能使用
server.reset(url);
}
}
}
private ExchangeServer createServer(URL url) {
//默认开启server关闭时发送readonly事件
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
//默认开启heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
str = url.getParameter(Constants.CLIENT_KEY);
if (str != null && str.length() > 0) {
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return server;
}
看下Exchangers.bind
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
return getExchanger(url).bind(url, handler);
}
根据Dubbo扩展机制知道getExchange获取到的是HeaderExchanger,其bind方法如下
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
从这里会发现,Server和Handler都使用了装饰者模式,为其扩展了很多功能。后面会发现源码中已经在外面包了很多层,提供了很多新的功能
Handler:
- DecodeHandler:提供了编码解码的功能
- HeaderExchangeHandler:提供数据校验,异常处理,已经将Channel转换成ExchangeChannel
Channel主要是底层的通信类,内部负责利用netty进行通信,而ExchangeChannel在通信方法前后提供了对象的转换,校验的功能。Server那边同理
接下来看下Transporters.bind方法,这之前看到类上有个注释
Transporter facade. (API, Static, ThreadSafe)
可见Transporters主要是获取Server和Client的工厂类,主要为了统一出口,屏蔽内部实现细节,只需要给外面提供Server或者Client的对象就可以了,内部使用什么实现外部可以不用管
然后看下方法
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
....
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else {
handler = new ChannelHandlerDispatcher(handlers);
}
return getTransporter().bind(url, handler);
}
Transporter是传输层的对象,目前有netty,mina和Grizzly3种实现,根据扩展机制,得到的是NettyTransporter,其bind方法返回NettyServer对象,看下NettyServer的继承结构
调用NettyServer的构造方法时会先调用父类的
public AbstractPeer(URL url, ChannelHandler handler) {
....
this.url = url;
this.handler = handler;
}
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
....
try {
doOpen();
....
} catch (Throwable t) {
....
}
if (handler instanceof WrappedChannelHandler ){
executor = ((WrappedChannelHandler)handler).getExecutor();
}
}
public AbstractEndpoint(URL url, ChannelHandler handler) {
super(url, handler);
this.codec = getChannelCodec(url);
this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);
}
- AbstractPeer:属性初始化
- AbstractEndpoint:也是属性初始化
- AbstractServer:真正初始化网络通信的地方,实现在NettyServer的doOpen方法中
@Override
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
bootstrap = new ServerBootstrap(channelFactory);
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
/*int idleTimeout = getIdleTimeout();
if (idleTimeout > 10000) {
pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
}*/
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
channel = bootstrap.bind(getBindAddress());
}
其他先不看,这里主要看nettyHandler,熟悉Netty的看到这就知道请求会经过NettyHandler,逻辑的处理就是在这里面了,当然最后会调用到在DubboProtocol里初始化的RequestHandler
到这里,网络通信部分就结束了,这里涉及的类很多,整个获取Server以及初始化的流程如下
以下内容2018-08-11补充:
Client
在DubboPrototol的refer方法引用服务的时候,会调用getClients方法获取Client的对象
private ExchangeClient[] getClients(URL url){
//是否共享连接
boolean service_share_connect = false;
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);//获取connections参数
//如果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;
}
getSharedClient和initClient的区别在于,getSharedClient会调用initClient去初始化连接,并且缓存起来,key是provider的ip地址
/**
*获取共享连接
*/
private ExchangeClient getSharedClient(URL url){
String key = url.getAddress();
ReferenceCountExchangeClient client = referenceClientMap.get(key);
if ( client != null ){
if ( !client.isClosed()){//连接未关闭
client.incrementAndGetCount();//使用的数量加1
return client;
} else {
// logger.warn(new IllegalStateException("client is closed,but stay in clientmap .client :"+ client));
referenceClientMap.remove(key);
}
}
ExchangeClient exchagneclient = initClient(url);// 初始化连接
// ReferenceCountExchangeClient包装了原始的Client,增加了一些功能
client = new ReferenceCountExchangeClient(exchagneclient, ghostClientMap);
referenceClientMap.put(key, client);
ghostClientMap.remove(key);
return client;
}
initClient
private ExchangeClient initClient(URL url) {
// ....
ExchangeClient client ;
//....
//设置连接应该是lazy的
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
client = new LazyConnectExchangeClient(url ,requestHandler);
} else {
client = Exchangers.connect(url ,requestHandler);
}
//....
return client;
}
如果配置了lazy属性,则返回LazyConnectExchangeClient,该类型的Client在真正使用到Client请求的时候才会调用Exchangers.connect去初始化,故直接看Exchangers.connect方法
public static ExchangeClient connect(String url) throws RemotingException {
return connect(URL.valueOf(url));
}
public static ExchangeClient connect(URL url) throws RemotingException {
return connect(url, new ChannelHandlerAdapter(), null);
}
public static ExchangeClient connect(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
return connect(url, new ExchangeHandlerDispatcher(replier, handler));
}
public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
return getExchanger(url).connect(url, handler);
}
代码原理和Server初始化的时候类似,最后返回的就是NettyClient,看下去构造方法:
public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException{
super(url, wrapChannelHandler(url, handler));
}
其父类是AbstractClient
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
// 发送请求的时候如果为true且未连接则调用connect方法
send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY, Constants.DEFAULT_SHUTDOWN_TIMEOUT);
//默认重连间隔2s,1800表示1小时warning一次.
reconnect_warning_period = url.getParameter("reconnect.waring.period", 1800);
//....
doOpen();// 子类实现,NettyClient中是初始化Netty
//....
connect();
//....
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().get(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().remove(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
}
connect方法
//....
if (isConnected()) {
return;
}
initConnectStatusCheckCommand();//重连机制
doConnect();//具体连接逻辑,子类实现
if (! isConnected()) {//连接失败
throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ ", cause: Connect wait timeout: " + getTimeout() + "ms.");
} else {// 连接成功
if (logger.isInfoEnabled()){
logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ ", channel is " + this.getChannel());
}
}
//....
initConnectStatusCheckCommand方法主要是开启了一个定时器,在未连接成功的情况下定时进行重连
private synchronized void initConnectStatusCheckCommand(){
//reconnect=false to close reconnect
int reconnect = getReconnectParam(getUrl());
if(reconnect > 0 && (reconnectExecutorFuture == null || reconnectExecutorFuture.isCancelled())){
Runnable connectStatusCheckCommand = new Runnable() {
public void run() {
try {
if (! isConnected()) {
connect();
} else {
lastConnectedTime = System.currentTimeMillis();
}
} catch (Throwable t) {
String errorMsg = "client reconnect to "+getUrl().getAddress()+" find error . url: "+ getUrl();
// 如果断开连接的时间大于shutdown_timeout,则打印日志,默认15分钟
if (System.currentTimeMillis() - lastConnectedTime > shutdown_timeout){
if (!reconnect_error_log_flag.get()){
reconnect_error_log_flag.set(true);
logger.error(errorMsg, t);
return ;
}
}
//每reconnect_warning_period次失败都打印warn日志
if ( reconnect_count.getAndIncrement() % reconnect_warning_period == 0){
logger.warn(errorMsg, t);
}
}
}
};
reconnectExecutorFuture = reconnectExecutorService.scheduleWithFixedDelay(connectStatusCheckCommand, reconnect, reconnect, TimeUnit.MILLISECONDS);
}
}