public abstract class AbstractServer implements Server { protected final Logger logger = LoggerLoader.getLogger(this.getClass()); RequestProcessor requestProcessor = null; ServerConfig serverConfig = null; boolean useLastPort = ConfigManagerLoader.getConfigManager().getBooleanValue("pigeon.port.uselast", true); public abstract void doStart(ServerConfig serverConfig); public abstract void doStop(); public abstract <T> void doAddService(ProviderConfig<T> providerConfig); public abstract <T> void doRemoveService(ProviderConfig<T> providerConfig); private class InnerConfigChangeListener implements ConfigChangeListener { @Override public void onKeyUpdated(String key, String value) { if (key.endsWith("pigeon.provider.processtype")) {} } @Override public void onKeyAdded(String key, String value) {} @Override public void onKeyRemoved(String key) {} } public void start(ServerConfig serverConfig) { if (logger.isInfoEnabled()) { logger.info("server config:" + serverConfig); } ConfigManagerLoader.getConfigManager() .registerConfigChangeListener(new InnerConfigChangeListener()); requestProcessor = RequestProcessorFactory.selectProcessor(serverConfig); doStart(serverConfig); if (requestProcessor != null) { requestProcessor.start(); } this.serverConfig = serverConfig; } public void stop() { doStop(); if (requestProcessor != null) { requestProcessor.stop(); } } @Override public <T> void addService(ProviderConfig<T> providerConfig) { requestProcessor.addService(providerConfig); doAddService(providerConfig); } @Override public <T> void removeService(ProviderConfig<T> providerConfig) { requestProcessor.removeService(providerConfig); doRemoveService(providerConfig); } public RequestProcessor getRequestProcessor() { return requestProcessor; } @Override public ServerConfig getServerConfig() { return serverConfig; } @Override public Future<InvocationResponse> processRequest( InvocationRequest request, ProviderContext providerContext) { return requestProcessor.processRequest(request, providerContext); } public int getAvailablePort(int port) { int lastPort = port; if (!useLastPort) { lastPort = NetUtils.getAvailablePort(lastPort); } else { String filePath = LoggerLoader.LOG_ROOT + "/pigeon-port.conf"; File file = new File(filePath); Properties properties = null; String key = null; try { key = this.getClass().getResource("/").getPath() + port; if (file.exists()) { try { properties = FileUtils.readFile(new FileInputStream(file)); String strLastPort = properties.getProperty(key); if (StringUtils.isNotBlank(strLastPort)) { lastPort = Integer.parseInt(strLastPort); } } catch (Throwable e) { } } } catch (RuntimeException e) { } lastPort = NetUtils.getAvailablePort(lastPort); if (properties == null) { properties = new Properties(); } if (key != null) { properties.put(key, lastPort); } try { FileUtils.writeFile(file, properties); } catch (IOException e) { } } return lastPort; } }
/** Created by chenchongze on 16/5/20. */ public enum RequestQualityManager { INSTANCE; private RequestQualityManager() {} private static final Logger logger = LoggerLoader.getLogger(RequestQualityManager.class); private static final ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static final String KEY_REQUEST_QUALITY_AUTO = "pigeon.invoker.request.quality.auto"; private static final String KEY_REQUEST_QUALITY_FAILED_PERCENT_GOOD = "pigeon.invoker.request.quality.failed.percent.good"; private static final String KEY_REQUEST_QUALITY_FAILED_PERCENT_NORMAL = "pigeon.invoker.request.quality.failed.percent.normal"; private static final String KEY_REQUEST_QUALITY_THRESHOLD_TOTAL = "pigeon.invoker.request.quality.threshold.total"; static { configManager.getBooleanValue(KEY_REQUEST_QUALITY_AUTO, false); configManager.getIntValue(KEY_REQUEST_QUALITY_THRESHOLD_TOTAL, 20); configManager.getFloatValue(KEY_REQUEST_QUALITY_FAILED_PERCENT_GOOD, 1f); configManager.getFloatValue(KEY_REQUEST_QUALITY_FAILED_PERCENT_NORMAL, 5f); } // hosts --> ( requestUrl:serviceName#method --> second --> { total, failed } ) private ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentHashMap<Integer, Quality>>> addrReqUrlSecondQualities = new ConcurrentHashMap< String, ConcurrentHashMap<String, ConcurrentHashMap<Integer, Quality>>>(); // hosts --> ( requestUrl:serviceName#method --> { total, failed } ) private volatile ConcurrentHashMap<String, ConcurrentHashMap<String, Quality>> addrReqUrlQualities = null; public ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentHashMap<Integer, Quality>>> getAddrReqUrlSecondQualities() { return addrReqUrlSecondQualities; } public ConcurrentHashMap<String, ConcurrentHashMap<String, Quality>> getAddrReqUrlQualities() { return addrReqUrlQualities; } public void setAddrReqUrlQualities( ConcurrentHashMap<String, ConcurrentHashMap<String, Quality>> addrReqUrlQualities) { this.addrReqUrlQualities = addrReqUrlQualities; } public void addClientRequest(InvokerContext context, boolean failed) { if (configManager.getBooleanValue(KEY_REQUEST_QUALITY_AUTO, false) && context.getClient() != null) { String address = context.getClient().getAddress(); ConcurrentHashMap<String, ConcurrentHashMap<Integer, Quality>> requestSecondQuality = addrReqUrlSecondQualities.get(address); if (requestSecondQuality == null) { requestSecondQuality = new ConcurrentHashMap<String, ConcurrentHashMap<Integer, Quality>>(); ConcurrentHashMap<String, ConcurrentHashMap<Integer, Quality>> last = addrReqUrlSecondQualities.putIfAbsent(address, requestSecondQuality); if (last != null) { requestSecondQuality = last; } } String requestUrl = getRequestUrl(context); ConcurrentHashMap<Integer, Quality> secondQuality = requestSecondQuality.get(requestUrl); if (secondQuality == null) { secondQuality = new ConcurrentHashMap<Integer, Quality>(); ConcurrentHashMap<Integer, Quality> last = requestSecondQuality.putIfAbsent(requestUrl, secondQuality); if (last != null) { secondQuality = last; } } int currentSecond = Calendar.getInstance().get(Calendar.SECOND); Quality quality = secondQuality.get(currentSecond); if (quality == null) { quality = new Quality(0, 0); Quality last = secondQuality.putIfAbsent(currentSecond, quality); if (last != null) { quality = last; } } quality.total.incrementAndGet(); if (failed) { quality.failed.incrementAndGet(); } } } public void removeClientQualities(String address) { addrReqUrlSecondQualities.remove(address); } private String getRequestUrl(InvokerContext context) { return context.getInvokerConfig().getUrl() + "#" + context.getMethodName(); } private String getRequestUrl(InvocationRequest request) { return request.getServiceName() + "#" + request.getMethodName(); } /** * 根据方法的服务质量过滤,优先保留服务质量good的clients,数量低于least时加入服务质量normal的clients * * @param clientList * @param request * @param least 最少保留个数 * @return */ public List<Client> getQualityPreferClients( List<Client> clientList, InvocationRequest request, float least) { // 筛选good,normal,bad clients // 直接进行服务质量路由,先只保留服务质量good的,如果不够(比如少于1个),加入服务质量normal的 if (!CollectionUtils.isEmpty(addrReqUrlQualities)) { String requestUrl = getRequestUrl(request); Map<RequrlQuality, List<Client>> filterQualityClientsMap = new HashMap<RequrlQuality, List<Client>>(); for (RequrlQuality reqQuality : RequrlQuality.values()) { filterQualityClientsMap.put(reqQuality, new ArrayList<Client>()); } for (Client client : clientList) { if (addrReqUrlQualities.containsKey(client.getAddress())) { ConcurrentHashMap<String, Quality> reqUrlQualities = addrReqUrlQualities.get(client.getAddress()); if (reqUrlQualities.containsKey(requestUrl)) { Quality quality = reqUrlQualities.get(requestUrl); switch (quality.getQuality()) { case REQURL_QUALITY_GOOD: filterQualityClientsMap.get(RequrlQuality.REQURL_QUALITY_GOOD).add(client); break; case REQURL_QUALITY_NORNAL: filterQualityClientsMap.get(RequrlQuality.REQURL_QUALITY_NORNAL).add(client); break; case REQURL_QUALITY_BAD: filterQualityClientsMap.get(RequrlQuality.REQURL_QUALITY_BAD).add(client); break; default: // never be here break; } } } } List<Client> filterQualityClients = new ArrayList<Client>(); filterQualityClients.addAll(filterQualityClientsMap.get(RequrlQuality.REQURL_QUALITY_GOOD)); if (filterQualityClients.size() < least) { filterQualityClients.addAll( filterQualityClientsMap.get(RequrlQuality.REQURL_QUALITY_NORNAL)); } return filterQualityClients; } return clientList; } public boolean isEnableRequestQualityRoute() { return configManager.getBooleanValue(KEY_REQUEST_QUALITY_AUTO, false); } public static class Quality { private RequrlQuality quality = RequrlQuality.REQURL_QUALITY_GOOD; private AtomicInteger failed = new AtomicInteger(); private AtomicInteger total = new AtomicInteger(); public Quality() {} public Quality(int total, int failed) { this.total.set(total); this.failed.set(failed); } public AtomicInteger getFailed() { return failed; } public int getFailedValue() { return failed.get(); } public void setFailed(int failed) { this.failed.set(failed); } public AtomicInteger getTotal() { return total; } public int getTotalValue() { return total.get(); } public void setTotal(int total) { this.total.set(total); } public float getFailedPercent() { if (total.get() > 0) { return failed.get() * 100 / total.get(); } else { return 0; } } public void clear() { total.set(0); failed.set(0); quality = RequrlQuality.REQURL_QUALITY_GOOD; } public RequrlQuality getQuality() { if (getTotalValue() > configManager.getIntValue(KEY_REQUEST_QUALITY_THRESHOLD_TOTAL, 20)) { float failedRate = getFailedPercent(); if (failedRate < configManager.getFloatValue(KEY_REQUEST_QUALITY_FAILED_PERCENT_GOOD, 1f)) { quality = RequrlQuality.REQURL_QUALITY_GOOD; } else if (failedRate >= configManager.getFloatValue(KEY_REQUEST_QUALITY_FAILED_PERCENT_GOOD, 1f) && failedRate < configManager.getFloatValue(KEY_REQUEST_QUALITY_FAILED_PERCENT_NORMAL, 5f)) { quality = RequrlQuality.REQURL_QUALITY_NORNAL; } else if (failedRate >= configManager.getFloatValue(KEY_REQUEST_QUALITY_FAILED_PERCENT_NORMAL, 5f)) { quality = RequrlQuality.REQURL_QUALITY_BAD; } } return quality; } public int getQualityValue() { return getQuality().getValue(); } } private enum RequrlQuality { REQURL_QUALITY_GOOD(0), REQURL_QUALITY_NORNAL(1), REQURL_QUALITY_BAD(2); private int value; private RequrlQuality(int value) { this.value = value; } public int getValue() { return value; } } public static void main(String[] args) { Quality quality = new Quality(105, 1); System.out.println(quality.getQualityValue()); final ConcurrentHashMap<String, ConcurrentHashMap<String, Quality>> addrReqUrlQualities = new ConcurrentHashMap<String, ConcurrentHashMap<String, Quality>>(); new Thread() { @Override public void run() { RequestQualityManager.INSTANCE.setAddrReqUrlQualities(addrReqUrlQualities); } }.start(); try { Thread.sleep(3000); } catch (InterruptedException e) { logger.warn("[main] thread interupt", e); } System.out.println(RequestQualityManager.INSTANCE.getAddrReqUrlQualities()); } }
public class AnnotationBean implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { private static final Logger logger = LoggerLoader.getLogger(AnnotationBean.class); private String annotationPackage = ConfigManagerLoader.getConfigManager() .getStringValue("pigeon.provider.interface.packages", "com.dianping"); private String[] annotationPackages = new String[] {"com.dianping"}; private final ConcurrentMap<String, InvokerConfig<?>> invokerConfigs = new ConcurrentHashMap<String, InvokerConfig<?>>(); public String getPackage() { return annotationPackage; } public void setPackage(String annotationPackage) { this.annotationPackage = annotationPackage; this.annotationPackages = (annotationPackage == null || annotationPackage.length() == 0) ? null : Constants.COMMA_SPLIT_PATTERN.split(annotationPackage); } private ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (annotationPackage == null || annotationPackage.length() == 0) { return; } if (beanFactory instanceof BeanDefinitionRegistry) { try { // init scanner Class<?> scannerClass = ClassUtils.loadClass( "org.springframework.context.annotation.ClassPathBeanDefinitionScanner"); Object scanner = scannerClass .getConstructor(new Class<?>[] {BeanDefinitionRegistry.class, boolean.class}) .newInstance(new Object[] {(BeanDefinitionRegistry) beanFactory, true}); // add filter Class<?> filterClass = ClassUtils.loadClass("org.springframework.core.type.filter.AnnotationTypeFilter"); Object filter = filterClass.getConstructor(Class.class).newInstance(Service.class); Method addIncludeFilter = scannerClass.getMethod( "addIncludeFilter", ClassUtils.loadClass("org.springframework.core.type.filter.TypeFilter")); addIncludeFilter.invoke(scanner, filter); // scan packages String[] packages = Constants.COMMA_SPLIT_PATTERN.split(annotationPackage); Method scan = scannerClass.getMethod("scan", new Class<?>[] {String[].class}); scan.invoke(scanner, new Object[] {packages}); } catch (Throwable e) { // spring 2.0 } } } public int getDefaultPort(int port) { if (port == 4040) { try { String app = ConfigManagerLoader.getConfigManager().getAppName(); if (StringUtils.isNotBlank(app)) { return LangUtils.hash(app, 6000, 2000); } } catch (Throwable t) { } } return port; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (!isMatchPackage(bean)) { return bean; } Class<?> beanClass = bean.getClass(); int idxCglib = beanClass.getName().contains("$$EnhancerByCGLIB") ? beanClass.getName().indexOf("$$EnhancerByCGLIB") : beanClass.getName().indexOf("$$EnhancerBySpringCGLIB"); if (idxCglib != -1) { try { beanClass = ClassUtils.loadClass(beanClass.getName().substring(0, idxCglib)); } catch (ClassNotFoundException e) { throw new IllegalStateException( "Failed to export remote service class " + beanClass.getName(), e); } } Service service = beanClass.getAnnotation(Service.class); if (service != null) { Class serviceInterface = service.interfaceClass(); if (void.class.equals(service.interfaceClass())) { serviceInterface = ServiceConfigUtils.getServiceInterface(beanClass); } if (serviceInterface == null) { serviceInterface = beanClass; } ProviderConfig<Object> providerConfig = new ProviderConfig<Object>(serviceInterface, bean); providerConfig.setService(bean); providerConfig.setUrl(service.url()); providerConfig.setVersion(service.version()); providerConfig.setSharedPool(service.useSharedPool()); providerConfig.setActives(service.actives()); ServerConfig serverConfig = new ServerConfig(); serverConfig.setPort(getDefaultPort(service.port())); serverConfig.setGroup(service.group()); serverConfig.setAutoSelectPort(service.autoSelectPort()); providerConfig.setServerConfig(serverConfig); ServiceFactory.addService(providerConfig); } postProcessBeforeInitialization(bean, beanName); return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!isMatchPackage(bean)) { return bean; } Method[] methods = bean.getClass().getMethods(); for (Method method : methods) { String name = method.getName(); if (name.length() > 3 && name.startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) { try { Reference reference = method.getAnnotation(Reference.class); if (reference != null) { Object value = refer(reference, method.getParameterTypes()[0]); if (value != null) { method.invoke(bean, new Object[] {}); } } } catch (Throwable e) { logger.error( "Failed to init remote service reference at method " + name + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e); } } } Class<?> superClass = bean.getClass().getSuperclass(); while (superClass != null && isMatchPackage(superClass)) { referFields(bean, superClass.getDeclaredFields()); superClass = superClass.getSuperclass(); } referFields(bean, bean.getClass().getDeclaredFields()); return bean; } private void referFields(Object bean, Field[] fields) { for (Field field : fields) { try { if (!field.isAccessible()) { field.setAccessible(true); } Reference reference = field.getAnnotation(Reference.class); if (reference != null) { Object value = refer(reference, field.getType()); if (value != null) { field.set(bean, value); } } } catch (Throwable e) { logger.error( "Failed to init remote service reference at field " + field.getName() + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e); } } } private Object refer( Reference reference, Class<?> referenceClass) { // method.getParameterTypes()[0] String interfaceName; if (!void.class.equals(reference.interfaceClass())) { interfaceName = reference.interfaceClass().getName(); } else if (referenceClass.isInterface()) { interfaceName = referenceClass.getName(); } else { throw new IllegalStateException( "The @Reference undefined interfaceClass or interfaceName, and the property type " + referenceClass.getName() + " is not a interface."); } String callbackClassName = reference.callback(); InvocationCallback callback = null; if (StringUtils.isNotBlank(callbackClassName)) { Class<?> clazz; try { clazz = ClassUtils.loadClass(callbackClassName); } catch (ClassNotFoundException e) { throw new IllegalStateException( "The @Reference undefined callback " + callbackClassName + ", is not a ServiceCallback interface."); } if (!InvocationCallback.class.isAssignableFrom(clazz)) { throw new IllegalStateException( "The @Reference undefined callback " + callbackClassName + ", is not a ServiceCallback interface."); } try { callback = (InvocationCallback) clazz.newInstance(); } catch (InstantiationException e) { throw new IllegalStateException( "The @Reference undefined callback " + callbackClassName + ", is not a ServiceCallback interface."); } catch (IllegalAccessException e) { throw new IllegalStateException( "The @Reference undefined callback " + callbackClassName + ", is not a ServiceCallback interface."); } } String key = reference.group() + "/" + reference.url() + "@" + interfaceName + ":" + reference.version() + ":" + reference.serialize() + ":" + reference.protocol() + ":" + reference.timeout() + ":" + reference.callType(); InvokerConfig<?> invokerConfig = invokerConfigs.get(key); if (invokerConfig == null) { invokerConfig = new InvokerConfig( referenceClass, reference.url(), reference.timeout(), reference.callType(), reference.serialize(), callback, reference.group(), false, reference.loadbalance(), reference.cluster(), reference.retries(), reference.timeoutRetry(), reference.vip(), reference.version(), reference.protocol()); invokerConfig.setSecret(reference.secret()); invokerConfigs.putIfAbsent(key, invokerConfig); invokerConfig = invokerConfigs.get(key); } return ServiceFactory.getService(invokerConfig); } private boolean isMatchPackage(Object bean) { if (annotationPackages == null || annotationPackages.length == 0) { return true; } String beanClassName = bean.getClass().getName(); for (String pkg : annotationPackages) { if (beanClassName.startsWith(pkg)) { return true; } } return false; } private boolean isMatchPackage(Class type) { String beanClassName = type.getName(); for (String pkg : annotationPackages) { if (beanClassName.startsWith(pkg)) { return true; } } return false; } @Override public void destroy() throws Exception {} }
public class NettyClient extends AbstractClient { private static final Logger logger = LoggerLoader.getLogger(NettyClient.class); private ClientBootstrap bootstrap; private Channel channel; private String host; private int port = ServerConfig.DEFAULT_PORT; private String address; private volatile boolean connected = false; private volatile boolean closed = false; private volatile boolean active = true; private ConnectInfo connectInfo; public static final int CLIENT_CONNECTIONS = Runtime.getRuntime().availableProcessors(); private static ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static ExecutorService bossExecutor = Executors.newCachedThreadPool(new DefaultThreadFactory("Pigeon-Netty-Client-Boss")); private static ExecutorService workExecutor = Executors.newCachedThreadPool(new DefaultThreadFactory("Pigeon-Netty-Client-Worker")); private static final int bossCount = configManager.getIntValue("pigeon.invoker.netty.bosscount", 1); private static final int workerCount = configManager.getIntValue( "pigeon.invoker.netty.workercount", Runtime.getRuntime().availableProcessors() * 2); private static ChannelFactory channelFactory = new NioClientSocketChannelFactory(bossExecutor, workExecutor, bossCount, workerCount); private static final int connectTimeout = configManager.getIntValue(Constants.KEY_CONNECT_TIMEOUT, Constants.DEFAULT_CONNECT_TIMEOUT); public int getWriteBufferHighWater() { return configManager.getIntValue( Constants.KEY_WRITE_BUFFER_HIGH_WATER, Constants.DEFAULT_WRITE_BUFFER_HIGH_WATER); } public int getWriteBufferLowWater() { return configManager.getIntValue( Constants.KEY_WRITE_BUFFER_LOW_WATER, Constants.DEFAULT_WRITE_BUFFER_LOW_WATER); } public NettyClient(ConnectInfo connectInfo) { this.host = connectInfo.getHost(); this.port = connectInfo.getPort(); this.connectInfo = connectInfo; this.address = host + ":" + port; this.bootstrap = new ClientBootstrap(channelFactory); this.bootstrap.setPipelineFactory(new NettyClientPipelineFactory(this)); this.bootstrap.setOption("tcpNoDelay", true); this.bootstrap.setOption("keepAlive", true); this.bootstrap.setOption("reuseAddress", true); this.bootstrap.setOption("connectTimeoutMillis", connectTimeout); this.bootstrap.setOption("writeBufferHighWaterMark", getWriteBufferHighWater()); this.bootstrap.setOption("writeBufferLowWaterMark", getWriteBufferLowWater()); } public synchronized void connect() { if (this.connected) { return; } logger.info("client is connecting to " + this.host + ":" + this.port); ChannelFuture future = null; try { future = bootstrap.connect(new InetSocketAddress(host, port)); if (future.awaitUninterruptibly(connectTimeout, TimeUnit.MILLISECONDS)) { if (future.isSuccess()) { Channel newChannel = future.getChannel(); try { // 关闭旧的连接 Channel oldChannel = this.channel; if (oldChannel != null) { logger.info("close old netty channel " + oldChannel); try { oldChannel.close(); } catch (Throwable t) { } } } finally { this.channel = newChannel; } logger.info("client is connected to " + this.host + ":" + this.port); this.connected = true; } else { logger.info("client is not connected to " + this.host + ":" + this.port); } } else { logger.info("timeout while connecting to " + this.host + ":" + this.port); } } catch (Throwable e) { logger.info("error while connecting to " + this.host + ":" + this.port, e); } } @Override public InvocationResponse doWrite(InvocationRequest request, Callback callback) throws NetworkException { Object[] msg = new Object[] {request, callback}; ChannelFuture future = null; if (channel == null) { logger.error("channel is null ^^^^^^^^^^^^^^"); } else { try { future = channel.write(msg); } catch (Exception e) { throw new NetworkException("remote call failed:" + request, e); } if (request.getMessageType() == Constants.MESSAGE_TYPE_SERVICE || request.getMessageType() == Constants.MESSAGE_TYPE_HEART) { future.addListener(new MsgWriteListener(request)); } } return null; } public void connectionException(Object attachment, Throwable e) { this.connected = false; connectionException(this, attachment, e); } private void connectionException(Client client, Object attachment, Throwable e) { logger.info("exception while connecting to " + client, e); if (attachment == null) { return; } Object[] msg = (Object[]) attachment; if (msg[0] instanceof InvokerContext) { InvokerContext invokerContext = (InvokerContext) msg[0]; InvocationRequest request = invokerContext.getRequest(); if (request.getMessageType() == Constants.MESSAGE_TYPE_SERVICE && msg[1] != null) { try { Callback callback = (Callback) msg[1]; if (client != null) { client.write(request, callback); } else { logger.error("no client found with service:" + request.getServiceName()); } } catch (Throwable ex) { logger.error("", ex); } logger.error("", e); } } } /** @return the connected */ public boolean isConnected() { return connected; } public boolean isActive() { return active && HeartBeatListener.isActiveAddress(address); } public void setActive(boolean active) { this.active = active; } @Override public boolean isWritable() { return this.channel.isWritable(); } /** @return the host */ public String getHost() { return host; } public int getPort() { return this.port; } /** @return the address */ public String getAddress() { return address; } public boolean equals(Object obj) { if (obj instanceof NettyClient) { NettyClient nc = (NettyClient) obj; return this.address.equals(nc.getAddress()); } else { return super.equals(obj); } } @Override public int hashCode() { return address.hashCode(); } @Override public void close() { logger.info("close client:" + this.host + ":" + this.port); closed = true; channel.close(); } @Override public String toString() { return this.getAddress() + ", connected:" + this.isConnected() + ", active:" + this.isActive(); } public class MsgWriteListener implements ChannelFutureListener { private InvocationRequest request; public MsgWriteListener(InvocationRequest request) { this.request = request; } public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { return; } if (request.getMessageType() != Constants.MESSAGE_TYPE_HEART) { connected = false; } InvocationResponse response = ProviderUtils.createFailResponse(request, future.getCause()); processResponse(response); } } @Override public ConnectInfo getConnectInfo() { return connectInfo; } @Override public boolean isDisposable() { return false; } @Override public void dispose() {} @Override public String getProtocol() { return Constants.PROTOCOL_DEFAULT; } }
/** @author qi.yin 2016/09/23 上午10:31. */ public class DefaultNettyChannel implements NettyChannel { private static final Logger logger = LoggerLoader.getLogger(NettyChannel.class); private ReentrantLock connectLock = new ReentrantLock(); private int timeout; private volatile Channel channel; private ClientBootstrap bootstrap; private InetSocketAddress localAddress; private InetSocketAddress remoteAddress; private String remoteAddressString; public DefaultNettyChannel( ClientBootstrap bootstrap, String remoteHost, int remotePort, int timeout) { this.bootstrap = bootstrap; this.remoteAddress = new InetSocketAddress(remoteHost, remotePort); this.remoteAddressString = NetUtils.toAddress(remoteHost, remotePort); this.timeout = timeout; } @Override public void connect() throws NetworkException { connectLock.lock(); try { if (isAvaliable()) { logger.info("[connect] is connected to remote " + remoteAddress + "."); return; } ChannelFuture future = bootstrap.connect(remoteAddress); try { if (future.awaitUninterruptibly(timeout, TimeUnit.MILLISECONDS)) { if (future.isSuccess()) { disConnect(); this.channel = future.getChannel(); localAddress = (InetSocketAddress) this.channel.getLocalAddress(); } else { logger.info("[connect] connected to remote " + remoteAddress + " failed."); throw new NetworkException("connected to remote " + remoteAddress + " failed."); } } else { logger.info("[connect] timeout connecting to remote " + remoteAddress + "."); throw new NetworkException("timeout connecting to remote " + remoteAddress + "."); } } catch (Throwable e) { logger.info("[connect] error connecting to remote " + remoteAddress + ".", e); throw new NetworkException("error connecting to remote " + remoteAddress + ".", e); } finally { if (!isConnected()) { future.cancel(); } } } finally { connectLock.unlock(); } } @Override public void disConnect() { connectLock.lock(); try { if (this.channel != null) { this.channel.close(); } } catch (Throwable e) { logger.error("[disConnect] error disConnecting channel. ", e); } finally { connectLock.unlock(); } } @Override public ChannelFuture write0(Object message) throws NetworkException { if (!isAvaliable()) { throw new NetworkException("[write0] channel is null or channel is close."); } return channel.write(message); } @Override public void write(Object message) throws NetworkException { write0(message); } private boolean isConnected() { if (this.channel != null) { return this.channel.isConnected(); } return false; } @Override public boolean isAvaliable() { return channel != null && channel.isConnected(); } public boolean isWritable() { return channel.isWritable(); } @Override public InetSocketAddress getLocalAddress() { return localAddress; } @Override public InetSocketAddress getRemoteAddress() { return remoteAddress; } @Override public String getRemoteAddressString() { return this.remoteAddressString; } public int getTimeout() { return timeout; } public String toString() { return "NettyChannel[avaliable = " + isAvaliable() + "localAddress=" + localAddress.toString() + "remoteAddress= " + remoteAddress.toString() + "]"; } }