/* * (C) Copyright 2015-2016 the original author or authors. * * 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. * * Contributors: * ohun@live.cn (夜色) */ package com.mpush.netty.client; import com.mpush.api.service.BaseService; import com.mpush.api.service.Client; import com.mpush.api.service.Listener; import com.mpush.netty.codec.PacketDecoder; import com.mpush.netty.codec.PacketEncoder; import com.mpush.tools.config.CC; import com.mpush.tools.thread.ThreadNames; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.*; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollSocketChannel; import io.netty.channel.epoll.Native; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.nio.channels.spi.SelectorProvider; public abstract class NettyTCPClient extends BaseService implements Client { private static final Logger LOGGER = LoggerFactory.getLogger(NettyTCPClient.class); private EventLoopGroup workerGroup; protected Bootstrap bootstrap; private void createClient(Listener listener, EventLoopGroup workerGroup, ChannelFactory<? extends Channel> channelFactory) { this.workerGroup = workerGroup; this.bootstrap = new Bootstrap(); bootstrap.group(workerGroup)// .option(ChannelOption.SO_REUSEADDR, true)// .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)// .channelFactory(channelFactory); bootstrap.handler(new ChannelInitializer<Channel>() { // (4) @Override public void initChannel(Channel ch) throws Exception { initPipeline(ch.pipeline()); } }); initOptions(bootstrap); listener.onSuccess(); } public ChannelFuture connect(String host, int port) { return bootstrap.connect(new InetSocketAddress(host, port)); } public ChannelFuture connect(String host, int port, Listener listener) { return bootstrap.connect(new InetSocketAddress(host, port)).addListener(f -> { if (f.isSuccess()) { if (listener != null) listener.onSuccess(port); LOGGER.info("start netty client success, host={}, port={}", host, port); } else { if (listener != null) listener.onFailure(f.cause()); LOGGER.error("start netty client failure, host={}, port={}", host, port, f.cause()); } }); } private void createNioClient(Listener listener) { NioEventLoopGroup workerGroup = new NioEventLoopGroup( getWorkThreadNum(), new DefaultThreadFactory(ThreadNames.T_TCP_CLIENT), getSelectorProvider() ); workerGroup.setIoRatio(getIoRate()); createClient(listener, workerGroup, getChannelFactory()); } private void createEpollClient(Listener listener) { EpollEventLoopGroup workerGroup = new EpollEventLoopGroup( getWorkThreadNum(), new DefaultThreadFactory(ThreadNames.T_TCP_CLIENT) ); workerGroup.setIoRatio(getIoRate()); createClient(listener, workerGroup, EpollSocketChannel::new); } protected void initPipeline(ChannelPipeline pipeline) { pipeline.addLast("decoder", getDecoder()); pipeline.addLast("encoder", getEncoder()); pipeline.addLast("handler", getChannelHandler()); } protected ChannelHandler getDecoder() { return new PacketDecoder(); } protected ChannelHandler getEncoder() { return PacketEncoder.INSTANCE; } protected int getIoRate() { return 50; } protected int getWorkThreadNum() { return 1; } public abstract ChannelHandler getChannelHandler(); @Override protected void doStart(Listener listener) throws Throwable { if (useNettyEpoll()) { createEpollClient(listener); } else { createNioClient(listener); } } private boolean useNettyEpoll() { if (CC.mp.core.useNettyEpoll()) { try { Native.offsetofEpollData(); return true; } catch (UnsatisfiedLinkError error) { LOGGER.warn("can not load netty epoll, switch nio model."); } } return false; } @Override protected void doStop(Listener listener) throws Throwable { if (workerGroup != null) { workerGroup.shutdownGracefully(); } LOGGER.error("netty client [{}] stopped.", this.getClass().getSimpleName()); listener.onSuccess(); } protected void initOptions(Bootstrap b) { b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4000); b.option(ChannelOption.TCP_NODELAY, true); } public ChannelFactory<? extends Channel> getChannelFactory() { return NioSocketChannel::new; } public SelectorProvider getSelectorProvider() { return SelectorProvider.provider(); } @Override public String toString() { return "NettyClient{" + ", name=" + this.getClass().getSimpleName() + '}'; } }