/*
* 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.netty4.http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.weibo.api.motan.common.ChannelState;
import com.weibo.api.motan.common.MotanConstants;
import com.weibo.api.motan.common.URLParamType;
import com.weibo.api.motan.exception.MotanFrameworkException;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.rpc.Response;
import com.weibo.api.motan.rpc.URL;
import com.weibo.api.motan.transport.AbstractServer;
import com.weibo.api.motan.transport.MessageHandler;
import com.weibo.api.motan.transport.TransportException;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.StatisticCallback;
import com.weibo.api.motan.util.StatsUtil;
/**
*
* @Description netty 4 http server.
* @author zhanglei
* @date 2016-5-31
*
*/
// TODO move to transport netty4 module
public class Netty4HttpServer extends AbstractServer implements StatisticCallback {
private MessageHandler messageHandler;
private URL url;
private Channel channel;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
public Netty4HttpServer(URL url, MessageHandler messageHandler) {
this.url = url;
this.messageHandler = messageHandler;
}
@Override
public boolean open() {
if (isAvailable()) {
return true;
}
if (channel != null) {
channel.close();
}
if (bossGroup == null) {
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
}
boolean shareChannel = url.getBooleanParameter(URLParamType.shareChannel.getName(), URLParamType.shareChannel.getBooleanValue());
// TODO max connection protect
int maxServerConnection =
url.getIntParameter(URLParamType.maxServerConnection.getName(), URLParamType.maxServerConnection.getIntValue());
int workerQueueSize = url.getIntParameter(URLParamType.workerQueueSize.getName(), 500);
int minWorkerThread = 0, maxWorkerThread = 0;
if (shareChannel) {
minWorkerThread = url.getIntParameter(URLParamType.minWorkerThread.getName(), MotanConstants.NETTY_SHARECHANNEL_MIN_WORKDER);
maxWorkerThread = url.getIntParameter(URLParamType.maxWorkerThread.getName(), MotanConstants.NETTY_SHARECHANNEL_MAX_WORKDER);
} else {
minWorkerThread =
url.getIntParameter(URLParamType.minWorkerThread.getName(), MotanConstants.NETTY_NOT_SHARECHANNEL_MIN_WORKDER);
maxWorkerThread =
url.getIntParameter(URLParamType.maxWorkerThread.getName(), MotanConstants.NETTY_NOT_SHARECHANNEL_MAX_WORKDER);
}
final int maxContentLength = url.getIntParameter(URLParamType.maxContentLength.getName(), URLParamType.maxContentLength.getIntValue());
final NettyHttpRequestHandler handler =
new NettyHttpRequestHandler(this, messageHandler, new ThreadPoolExecutor(minWorkerThread, maxWorkerThread, 15,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(workerQueueSize)));
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(maxContentLength));
ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
ch.pipeline().addLast("serverHandler", handler);
}
}).option(ChannelOption.SO_BACKLOG, 1024).childOption(ChannelOption.SO_KEEPALIVE, false);
ChannelFuture f;
try {
f = b.bind(url.getPort()).sync();
channel = f.channel();
} catch (InterruptedException e) {
LoggerUtil.error("init http server fail.", e);
return false;
}
state = ChannelState.ALIVE;
StatsUtil.registryStatisticCallback(this);
LoggerUtil.info("Netty4HttpServer ServerChannel finish Open: url=" + url);
return true;
}
@Override
public void close() {
close(0);
}
@Override
public boolean isAvailable() {
return state.isAliveState();
}
@Override
public boolean isBound() {
return channel != null && channel.isActive();
}
@Override
public Response request(Request request) throws TransportException {
throw new MotanFrameworkException("Netty4HttpServer request(Request request) method unsupport: url: " + url);
}
@Override
public void close(int timeout) {
if (state.isCloseState()) {
LoggerUtil.info("NettyServer close fail: already close, url={}", url.getUri());
return;
}
if (state.isUnInitState()) {
LoggerUtil.info("NettyServer close Fail: don't need to close because node is unInit state: url={}",
url.getUri());
return;
}
if (channel != null) {
channel.close();
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
workerGroup = null;
bossGroup = null;
}
state = ChannelState.CLOSE;
StatsUtil.unRegistryStatisticCallback(this);
}
@Override
public boolean isClosed() {
return state.isCloseState();
}
@Override
public String statisticCallback() {
//TODO
return null;
}
@Override
public URL getUrl() {
return url;
}
}