/** @author Robert HG ([email protected]) on 8/19/14. */ public class TestJobRunner implements JobRunner { private static final Logger LOGGER = LoggerFactory.getLogger(TestJobRunner.class); private static final BizLogger bizLogger = LtsLoggerFactory.getBizLogger(); @Override public Result run(Job job) throws Throwable { try { // Thread.sleep(1000L); // // if (job.getRetryTimes() > 5) { // return new Result(Action.EXECUTE_FAILED, "重试次数超过5次了,放过你吧!"); // } // // if (SystemClock.now() % 2 == 1) { // return new Result(Action.EXECUTE_LATER, "稍后执行"); // } // TODO 业务逻辑 LOGGER.info("我要执行:" + job); // 会发送到 LTS (JobTracker上) bizLogger.info("测试,业务日志啊啊啊啊啊"); } catch (Exception e) { LOGGER.info("Run job failed!", e); return new Result(Action.EXECUTE_FAILED, e.getMessage()); } return new Result(Action.EXECUTE_SUCCESS, "执行成功了,哈哈"); } }
/** * Create a new NoopFilter using a name * * @param name the name used to create the logger. If null, will default to "NoopFilter" */ public MinaLogger(String name) { if (name == null) { this.name = MinaLogger.class.getName(); } else { this.name = name; } logger = LoggerFactory.getLogger(this.name); }
/** @author Robert HG ([email protected]) on 11/19/15. */ public class JSONFactory { private static final Logger LOGGER = LoggerFactory.getLogger(JSONFactory.class); private static volatile JSONAdapter JSON_ADAPTER; static { String json = System.getProperty("lts.json"); if ("fastjson".equals(json)) { setJSONAdapter(new FastJSONAdapter()); } else if ("jackson".equals(json)) { setJSONAdapter(new JacksonJSONAdapter()); } else { try { setJSONAdapter(new FastJSONAdapter()); } catch (Throwable ignored) { try { setJSONAdapter(new JacksonJSONAdapter()); } catch (Throwable ignored2) { try { setJSONAdapter(new LtsJSONAdapter()); } catch (Throwable ignored3) { throw new JSONException("Please check JSON lib"); } } } } } public static void setJSONAdapter(String jsonAdapter) { if (StringUtils.isNotEmpty(jsonAdapter)) { setJSONAdapter(ServiceLoader.load(JSONAdapter.class, jsonAdapter)); } } public static JSONAdapter getJSONAdapter() { return JSONFactory.JSON_ADAPTER; } public static void setJSONAdapter(JSONAdapter jsonAdapter) { if (jsonAdapter != null) { LOGGER.info("Using JSON lib " + jsonAdapter.getName()); JSONFactory.JSON_ADAPTER = jsonAdapter; } } }
/** @author Robert HG ([email protected]) on 11/3/15. */ public class NettyRemotingClient extends AbstractRemotingClient { private static final Logger LOGGER = LoggerFactory.getLogger(RemotingHelper.RemotingLogName); private final Bootstrap bootstrap = new Bootstrap(); private final EventLoopGroup eventLoopGroup; private DefaultEventExecutorGroup defaultEventExecutorGroup; public NettyRemotingClient(final RemotingClientConfig remotingClientConfig) { this(remotingClientConfig, null); } public NettyRemotingClient( final RemotingClientConfig remotingClientConfig, final ChannelEventListener channelEventListener) { super(remotingClientConfig, channelEventListener); this.eventLoopGroup = new NioEventLoopGroup(remotingClientConfig.getClientSelectorThreads()); } @Override protected void clientStart() throws RemotingException { NettyLogger.setNettyLoggerFactory(); this.defaultEventExecutorGroup = new DefaultEventExecutorGroup( remotingClientConfig.getClientWorkerThreads(), new NamedThreadFactory("NettyClientWorkerThread_")); final NettyCodecFactory nettyCodecFactory = new NettyCodecFactory(getCodec()); this.bootstrap .group(this.eventLoopGroup) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler( new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast( // defaultEventExecutorGroup, // nettyCodecFactory.getEncoder(), // nettyCodecFactory.getDecoder(), // new IdleStateHandler( remotingClientConfig.getReaderIdleTimeSeconds(), remotingClientConfig.getWriterIdleTimeSeconds(), remotingClientConfig.getClientChannelMaxIdleTimeSeconds()), // new NettyConnectManageHandler(), // new NettyClientHandler()); } }); } @Override protected void clientShutdown() { this.eventLoopGroup.shutdownGracefully(); if (this.defaultEventExecutorGroup != null) { this.defaultEventExecutorGroup.shutdownGracefully(); } } @Override protected com.lts.remoting.ChannelFuture connect(SocketAddress socketAddress) { ChannelFuture channelFuture = this.bootstrap.connect(socketAddress); return new com.lts.remoting.netty.NettyChannelFuture(channelFuture); } class NettyClientHandler extends SimpleChannelInboundHandler<RemotingCommand> { @Override protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception { processMessageReceived(new NettyChannel(ctx), msg); } } class NettyConnectManageHandler extends ChannelDuplexHandler { @Override public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { final String local = localAddress == null ? "UNKNOW" : localAddress.toString(); final String remote = remoteAddress == null ? "UNKNOW" : remoteAddress.toString(); LOGGER.info("CLIENT : CONNECT {} => {}", local, remote); super.connect(ctx, remoteAddress, localAddress, promise); if (channelEventListener != null) { assert remoteAddress != null; putRemotingEvent( new RemotingEvent( RemotingEventType.CONNECT, remoteAddress.toString(), new NettyChannel(ctx))); } } @Override public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { Channel channel = new NettyChannel(ctx); final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel); LOGGER.info("CLIENT : DISCONNECT {}", remoteAddress); closeChannel(channel); super.disconnect(ctx, promise); if (channelEventListener != null) { putRemotingEvent(new RemotingEvent(RemotingEventType.CLOSE, remoteAddress, channel)); } } @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { Channel channel = new NettyChannel(ctx); final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel); LOGGER.info("CLIENT : CLOSE {}", remoteAddress); closeChannel(channel); super.close(ctx, promise); if (channelEventListener != null) { putRemotingEvent(new RemotingEvent(RemotingEventType.CLOSE, remoteAddress, channel)); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { Channel channel = new NettyChannel(ctx); final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel); LOGGER.warn("CLIENT : exceptionCaught {}", remoteAddress); LOGGER.warn("CLIENT : exceptionCaught exception.", cause); closeChannel(channel); if (channelEventListener != null) { putRemotingEvent(new RemotingEvent(RemotingEventType.EXCEPTION, remoteAddress, channel)); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; Channel channel = new NettyChannel(ctx); final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel); if (event.state().equals(io.netty.handler.timeout.IdleState.ALL_IDLE)) { LOGGER.warn("CLIENT : IDLE [{}]", remoteAddress); closeChannel(channel); } if (channelEventListener != null) { RemotingEventType remotingEventType = RemotingEventType.valueOf(event.state().name()); putRemotingEvent(new RemotingEvent(remotingEventType, remoteAddress, channel)); } } ctx.fireUserEventTriggered(evt); } } }
/** @author Robert HG ([email protected]) on 8/15/14. 抽象节点 */ public abstract class AbstractJobNode<T extends Node, App extends Application> implements JobNode { protected static final Logger LOGGER = LoggerFactory.getLogger(JobNode.class); protected Registry registry; protected T node; protected Config config; protected App application; private List<NodeChangeListener> nodeChangeListeners; private List<MasterChangeListener> masterChangeListeners; private EventCenterFactory eventCenterFactory = ExtensionLoader.getExtensionLoader(EventCenterFactory.class).getAdaptiveExtension(); private AtomicBoolean started = new AtomicBoolean(false); public AbstractJobNode() { application = getApplication(); config = JobNodeConfigFactory.getDefaultConfig(); application.setConfig(config); nodeChangeListeners = new ArrayList<NodeChangeListener>(); masterChangeListeners = new ArrayList<MasterChangeListener>(); } public final void start() { try { if (started.compareAndSet(false, true)) { // 初始化配置 initConfig(); preRemotingStart(); remotingStart(); afterRemotingStart(); initRegistry(); registry.register(node); LOGGER.info("Start success!"); } } catch (Throwable e) { LOGGER.error("Start failed!", e); } } public final void stop() { try { if (started.compareAndSet(true, false)) { registry.unregister(node); preRemotingStop(); remotingStop(); afterRemotingStop(); LOGGER.info("Stop success!"); } } catch (Throwable e) { LOGGER.error("Stop failed!", e); } } @Override public void destroy() { try { registry.destroy(); LOGGER.info("Destroy success!"); } catch (Throwable e) { LOGGER.error("Destroy failed!", e); } } protected void initConfig() { application.setEventCenter(eventCenterFactory.getEventCenter(config)); application.setCommandBodyWrapper(new CommandBodyWrapper(config)); application.setMasterElector(new MasterElector(application)); application.getMasterElector().addMasterChangeListener(masterChangeListeners); application.setRegistryStatMonitor(new RegistryStatMonitor(application)); node = NodeFactory.create(getNodeClass(), config); config.setNodeType(node.getNodeType()); LOGGER.info("Current node config :{}", config); // 订阅的node管理 SubscribedNodeManager subscribedNodeManager = new SubscribedNodeManager(application); application.setSubscribedNodeManager(subscribedNodeManager); nodeChangeListeners.add(subscribedNodeManager); // 用于master选举的监听器 nodeChangeListeners.add(new MasterElectionListener(application)); // 监听自己节点变化(如,当前节点被禁用了) nodeChangeListeners.add(new SelfChangeListener(application)); } private void initRegistry() { registry = RegistryFactory.getRegistry(application); if (registry instanceof AbstractRegistry) { ((AbstractRegistry) registry).setNode(node); } registry.subscribe( node, new NotifyListener() { private final Logger NOTIFY_LOGGER = LoggerFactory.getLogger(NotifyListener.class); @Override public void notify(NotifyEvent event, List<Node> nodes) { if (CollectionUtils.isEmpty(nodes)) { return; } switch (event) { case ADD: for (NodeChangeListener listener : nodeChangeListeners) { try { listener.addNodes(nodes); } catch (Throwable t) { NOTIFY_LOGGER.error( "{} add nodes failed , cause: {}", listener.getClass().getName(), t.getMessage(), t); } } break; case REMOVE: for (NodeChangeListener listener : nodeChangeListeners) { try { listener.removeNodes(nodes); } catch (Throwable t) { NOTIFY_LOGGER.error( "{} remove nodes failed , cause: {}", listener.getClass().getName(), t.getMessage(), t); } } break; } } }); } protected abstract void remotingStart(); protected abstract void remotingStop(); protected void preRemotingStart() { // 检查identity是否重复 } protected void afterRemotingStart() {} protected void preRemotingStop() {} protected void afterRemotingStop() {} @SuppressWarnings("unchecked") private App getApplication() { try { return ((Class<App>) GenericsUtils.getSuperClassGenericType(this.getClass(), 1)) .newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private Class<T> getNodeClass() { return (Class<T>) GenericsUtils.getSuperClassGenericType(this.getClass(), 0); } /** 设置zookeeper注册中心地址 */ public void setRegistryAddress(String registryAddress) { config.setRegistryAddress(registryAddress); } /** 设置远程调用超时时间 */ public void setInvokeTimeoutMillis(int invokeTimeoutMillis) { config.setInvokeTimeoutMillis(invokeTimeoutMillis); } /** 设置集群名字 */ public void setClusterName(String clusterName) { config.setClusterName(clusterName); } /** 节点标识(必须要保证这个标识是唯一的才能设置,请谨慎设置) 这个是非必须设置的,建议使用系统默认生成 */ public void setIdentity(String identity) { config.setIdentity(identity); } /** 添加节点监听器 */ public void addNodeChangeListener(NodeChangeListener notifyListener) { if (notifyListener != null) { nodeChangeListeners.add(notifyListener); } } /** 添加 master 节点变化监听器 */ public void addMasterChangeListener(MasterChangeListener masterChangeListener) { if (masterChangeListener != null) { masterChangeListeners.add(masterChangeListener); } } /** 设置额外的配置参数 */ public void addConfig(String key, String value) { config.setParameter(key, value); } }
/** Robert HG ([email protected]) on 6/5/15. */ @Component public class RegistryService implements InitializingBean { private static final Logger LOGGER = LoggerFactory.getLogger(RegistryService.class); @Autowired NodeMemoryDatabase nodeMemoryDatabase; @Autowired AdminAppContext appContext; @Autowired NodeOnOfflineLogRepo nodeOnOfflineLogRepo; private Registry registry; private void register() { registry.subscribe( appContext.getNode(), new NotifyListener() { @Override public void notify(NotifyEvent event, List<Node> nodes) { if (CollectionUtils.isEmpty(nodes)) { return; } switch (event) { case ADD: nodeMemoryDatabase.addNode(nodes); LOGGER.info("ADD NODE " + nodes); break; case REMOVE: nodeMemoryDatabase.removeNode(nodes); LOGGER.info("REMOVE NODE " + nodes); break; } // 记录日志 addLog(event, nodes); } }); } public List<Node> getOnlineNodes(NodeRequest request) { return nodeMemoryDatabase.search(request); } /** 记录节点上下线日志 */ private void addLog(NotifyEvent event, List<Node> nodes) { List<NodeOnOfflineLog> logs = new ArrayList<NodeOnOfflineLog>(nodes.size()); for (Node node : nodes) { NodeOnOfflineLog log = new NodeOnOfflineLog(); log.setLogTime(new Date()); log.setEvent(event == NotifyEvent.ADD ? "ONLINE" : "OFFLINE"); log.setClusterName(node.getClusterName()); log.setCreateTime(node.getCreateTime()); log.setGroup(node.getGroup()); log.setHostName(node.getHostName()); log.setIdentity(node.getIdentity()); log.setIp(node.getIp()); log.setPort(node.getPort()); log.setThreads(node.getThreads()); log.setNodeType(node.getNodeType()); logs.add(log); } nodeOnOfflineLogRepo.insert(logs); } @Override public void afterPropertiesSet() throws Exception { registry = RegistryFactory.getRegistry(appContext); register(); } }
/** * 内部根据用户参数决定是否采用延迟批量刷盘的策略,来提高吞吐量 批量刷盘有两种情况: 1. 内存的日志量超过了设置的阀值 2. 每3S检查一次内存中是否有日志,如果有就那么刷盘 * * @author Robert HG ([email protected]) on 10/2/15. */ public class JobLoggerDelegate implements JobLogger { private static final Logger LOGGER = LoggerFactory.getLogger(JobLoggerDelegate.class); // 3S 检查输盘一次日志 private int flushPeriod; private JobLogger jobLogger; private boolean lazyLog = false; private ScheduledExecutorService executor; private ScheduledFuture scheduledFuture; private BlockingQueue<JobLogPo> memoryQueue; // 日志批量刷盘数量 private int batchFlushSize = 100; private int overflowSize = 10000; // 内存中最大的日志量阀值 private int maxMemoryLogSize; private AtomicBoolean flushing = new AtomicBoolean(false); public JobLoggerDelegate(Config config) { JobLoggerFactory jobLoggerFactory = ExtensionLoader.getExtensionLoader(JobLoggerFactory.class).getAdaptiveExtension(); jobLogger = jobLoggerFactory.getJobLogger(config); lazyLog = config.getParameter(Constants.LAZY_JOB_LOGGER, false); if (lazyLog) { // 无界Queue memoryQueue = new LinkedBlockingQueue<JobLogPo>(); maxMemoryLogSize = config.getParameter(Constants.LAZY_JOB_LOGGER_MEM_SIZE, 1000); flushPeriod = config.getParameter(Constants.LAZY_JOB_LOGGER_CHECK_PERIOD, 3); executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("LazyJobLogger")); scheduledFuture = executor.scheduleWithFixedDelay( new Runnable() { @Override public void run() { try { if (flushing.compareAndSet(false, true)) { checkAndFlush(); } } catch (Throwable t) { LOGGER.error("CheckAndFlush log error", t); } } }, flushPeriod, flushPeriod, TimeUnit.SECONDS); } } /** 检查内存中是否有日志,如果有就批量刷盘 */ private void checkAndFlush() { try { int nowSize = memoryQueue.size(); if (nowSize == 0) { return; } List<JobLogPo> batch = new ArrayList<JobLogPo>(); for (int i = 0; i < nowSize; i++) { JobLogPo jobLogPo = memoryQueue.poll(); batch.add(jobLogPo); if (batch.size() >= batchFlushSize) { flush(batch); } } if (batch.size() > 0) { flush(batch); } } finally { flushing.compareAndSet(true, false); } } private void checkOverflowSize() { if (memoryQueue.size() > overflowSize) { throw new JobLogException( "Memory Log size is " + memoryQueue.size() + " , please check the JobLogger is available"); } } private void flush(List<JobLogPo> batch) { boolean flushSuccess = false; try { jobLogger.log(batch); flushSuccess = true; } finally { if (!flushSuccess) { memoryQueue.addAll(batch); } batch.clear(); } } /** 检查内存中的日志量是否超过阀值,如果超过需要批量刷盘日志 */ private void checkCapacity() { if (memoryQueue.size() > maxMemoryLogSize) { // 超过阀值,需要批量刷盘 if (flushing.compareAndSet(false, true)) { // 这里可以采用new Thread, 因为这里只会同时new一个 new Thread( new Runnable() { @Override public void run() { try { checkAndFlush(); } catch (Throwable t) { LOGGER.error("Capacity full flush error", t); } } }) .start(); } } } @Override public void log(JobLogPo jobLogPo) { if (jobLogPo == null) { return; } if (lazyLog) { checkOverflowSize(); memoryQueue.offer(jobLogPo); checkCapacity(); } else { jobLogger.log(jobLogPo); } } @Override public void log(List<JobLogPo> jobLogPos) { if (CollectionUtils.isEmpty(jobLogPos)) { return; } if (lazyLog) { checkOverflowSize(); for (JobLogPo jobLogPo : jobLogPos) { memoryQueue.offer(jobLogPo); } // checkCapacity checkCapacity(); } else { jobLogger.log(jobLogPos); } } @Override public PageResponse<JobLogPo> search(JobLoggerRequest request) { return jobLogger.search(request); } }
/** @author Robert HG ([email protected]) on 8/14/14. 接受任务并执行 */ public class JobPushProcessor extends AbstractProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(LoggerName.TaskTracker); private RetryScheduler retryScheduler; private JobRunnerCallback jobRunnerCallback; private RemotingClientDelegate remotingClient; protected JobPushProcessor(TaskTrackerApplication application) { super(application); this.remotingClient = application.getRemotingClient(); retryScheduler = new RetryScheduler<TaskTrackerJobResult>(application, 3) { @Override protected boolean isRemotingEnable() { return remotingClient.isServerEnable(); } @Override protected boolean retry(List<TaskTrackerJobResult> results) { return retrySendJobResults(results); } }; retryScheduler.setName("JobPush"); retryScheduler.start(); // 线程安全的 jobRunnerCallback = new JobRunnerCallback(); } @Override public RemotingCommand processRequest(ChannelHandlerContext ctx, final RemotingCommand request) throws RemotingCommandException { JobPushRequest requestBody = request.getBody(); // JobTracker 分发来的 job final JobWrapper jobWrapper = requestBody.getJobWrapper(); try { application.getRunnerPool().execute(jobWrapper, jobRunnerCallback); } catch (NoAvailableJobRunnerException e) { // 任务推送失败 return RemotingCommand.createResponseCommand( JobProtos.ResponseCode.NO_AVAILABLE_JOB_RUNNER.code(), "job push failure , no available job runner!"); } // 任务推送成功 return RemotingCommand.createResponseCommand( JobProtos.ResponseCode.JOB_PUSH_SUCCESS.code(), "job push success!"); } /** 任务执行的回调(任务执行完之后线程回调这个函数) */ private class JobRunnerCallback implements RunnerCallback { @Override public JobWrapper runComplete(Response response) { // 发送消息给 JobTracker final TaskTrackerJobResult taskTrackerJobResult = new TaskTrackerJobResult(); taskTrackerJobResult.setTime(SystemClock.now()); taskTrackerJobResult.setJobWrapper(response.getJobWrapper()); taskTrackerJobResult.setAction(response.getAction()); taskTrackerJobResult.setMsg(response.getMsg()); TtJobFinishedRequest requestBody = application.getCommandBodyWrapper().wrapper(new TtJobFinishedRequest()); requestBody.addJobResult(taskTrackerJobResult); requestBody.setReceiveNewJob(response.isReceiveNewJob()); // 设置可以接受新任务 int requestCode = JobProtos.RequestCode.JOB_FINISHED.code(); RemotingCommand request = RemotingCommand.createRequestCommand(requestCode, requestBody); final Response returnResponse = new Response(); try { final CountDownLatch latch = new CountDownLatch(1); remotingClient.invokeAsync( request, new InvokeCallback() { @Override public void operationComplete(ResponseFuture responseFuture) { try { RemotingCommand commandResponse = responseFuture.getResponseCommand(); if (commandResponse != null && commandResponse.getCode() == RemotingProtos.ResponseCode.SUCCESS.code()) { JobPushRequest jobPushRequest = commandResponse.getBody(); if (jobPushRequest != null) { LOGGER.info("Get new job :{}", jobPushRequest.getJobWrapper()); returnResponse.setJobWrapper(jobPushRequest.getJobWrapper()); } } else { LOGGER.info("Job feedback failed, save local files。{}", taskTrackerJobResult); try { retryScheduler.inSchedule( taskTrackerJobResult.getJobWrapper().getJobId().concat("_") + SystemClock.now(), taskTrackerJobResult); } catch (Exception e) { LOGGER.error("Job feedback failed", e); } } } finally { latch.countDown(); } } }); try { latch.await(Constants.LATCH_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new RequestTimeoutException(e); } } catch (JobTrackerNotFoundException e) { try { LOGGER.warn("No job tracker available! save local files."); retryScheduler.inSchedule( taskTrackerJobResult.getJobWrapper().getJobId().concat("_") + SystemClock.now(), taskTrackerJobResult); } catch (Exception e1) { LOGGER.error("Save files failed, {}", taskTrackerJobResult.getJobWrapper(), e1); } } return returnResponse.getJobWrapper(); } } /** * 发送JobResults * * @param results * @return */ private boolean retrySendJobResults(List<TaskTrackerJobResult> results) { // 发送消息给 JobTracker TtJobFinishedRequest requestBody = application.getCommandBodyWrapper().wrapper(new TtJobFinishedRequest()); requestBody.setTaskTrackerJobResults(results); requestBody.setReSend(true); int requestCode = JobProtos.RequestCode.JOB_FINISHED.code(); RemotingCommand request = RemotingCommand.createRequestCommand(requestCode, requestBody); try { // 这里一定要用同步,不然异步会发生文件锁,死锁 RemotingCommand commandResponse = remotingClient.invokeSync(request); if (commandResponse != null && commandResponse.getCode() == RemotingProtos.ResponseCode.SUCCESS.code()) { return true; } else { LOGGER.warn("Send job failed, {}", commandResponse); return false; } } catch (JobTrackerNotFoundException e) { LOGGER.error("Retry send job result failed! jobResults={}", results, e); } return false; } }
/** * 主要用于 curl * * @author Robert HG ([email protected]) on 10/26/15. */ public class CommandCenter { private final Logger LOGGER = LoggerFactory.getLogger(CommandCenter.class); private ExecutorService commandExecutor; private AtomicBoolean start = new AtomicBoolean(false); private final Map<String, CommandProcessor> processorMap = new HashMap<String, CommandProcessor>(); private Config config; private int port; public CommandCenter(Config config) { this.config = config; } public void start() throws CommandException { try { if (start.compareAndSet(false, true)) { port = config.getParameter("lts.command.port", 8719); commandExecutor = new ThreadPoolExecutor( Constants.AVAILABLE_PROCESSOR, Constants.AVAILABLE_PROCESSOR, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100), new ThreadPoolExecutor.DiscardPolicy()); ServerSocket serverSocket = getServerSocket(); // 开启监听命令 startServerListener(serverSocket); LOGGER.info("Start CommandCenter succeed at port {}", port); } } catch (Exception t) { LOGGER.error( "Start CommandCenter error at port {} , use lts.command.port config change the port.", port, t); throw new CommandException(t); } } private ServerSocket getServerSocket() throws IOException { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(port); } catch (BindException e) { if (e.getMessage().contains("Address already in use")) { port = port + 1; serverSocket = getServerSocket(); } else { throw e; } } return serverSocket; } private void startServerListener(final ServerSocket serverSocket) { new Thread( new Runnable() { @Override public void run() { while (start.get()) { Socket socket = null; try { socket = serverSocket.accept(); if (socket == null) { continue; } commandExecutor.submit(new EventRunnable(socket)); } catch (Throwable t) { LOGGER.error("Accept error ", t); try { Thread.sleep(1000); // 1s } catch (InterruptedException ignored) { } } } } }) .start(); } public void stop() { try { if (start.compareAndSet(true, false)) { commandExecutor.shutdownNow(); LOGGER.info("Stop CommandCenter succeed "); } } catch (Throwable t) { LOGGER.error("Stop CommandCenter error ", t); } } public int getPort() { return port; } public void registerCommand(String command, CommandProcessor processor) { if (StringUtils.isEmpty(command)) { return; } processorMap.put(command, processor); } private CommandProcessor getCommandProcessor(String command) { return processorMap.get(command); } class EventRunnable implements Runnable { private Socket socket; public EventRunnable(Socket socket) { this.socket = socket; } @Override public void run() { BufferedReader in = null; PrintWriter out = null; try { in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); OutputStream outputStream = socket.getOutputStream(); out = new PrintWriter(outputStream); String line = in.readLine(); CommandRequest request = CommandRequest.parse(line); out.print("HTTP/1.1 200 OK\r\n\r\n"); out.flush(); if (StringUtils.isEmpty(request.getCommand())) { out.println("Command is blank"); out.flush(); return; } CommandProcessor commandProcessor = getCommandProcessor(request.getCommand()); if (commandProcessor != null) { commandProcessor.execute(outputStream, request); } else { out.println("Can not find the command:[" + request.getCommand() + "]"); } out.flush(); } catch (Throwable t) { LOGGER.error("EventRunnable error", t); try { if (out != null) { out.println("CommandCenter error, message is " + t.getMessage()); out.flush(); } } catch (Exception e) { LOGGER.error("EventRunnable error", t); } } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } socket.close(); } catch (Exception e) { LOGGER.error("EventRunnable close resource error", e); } } } } }
/** * 参考dubbo * * @author Robert HG ([email protected]) on 5/19/15. */ public final class Version { private Version() {} private static final Logger LOGGER = LoggerFactory.getLogger(Version.class); private static final String VERSION = getVersion(Version.class, "1.6.4-SNAPSHOT"); static { // 检查是否存在重复的jar包 Version.checkDuplicate(Version.class); } public static String getVersion() { return VERSION; } public static String getVersion(Class<?> cls, String defaultVersion) { try { // 首先查找MANIFEST.MF规范中的版本号 String version = cls.getPackage().getImplementationVersion(); if (version == null || version.length() == 0) { version = cls.getPackage().getSpecificationVersion(); } if (version == null || version.length() == 0) { // 如果规范中没有版本号,基于jar包名获取版本号 CodeSource codeSource = cls.getProtectionDomain().getCodeSource(); if (codeSource == null) { LOGGER.info( "No codeSource for class " + cls.getName() + " when getVersion, use default version " + defaultVersion); } else { String file = codeSource.getLocation().getFile(); if (file != null && file.length() > 0 && file.endsWith(".jar")) { file = file.substring(0, file.length() - 4); int i = file.lastIndexOf('/'); if (i >= 0) { file = file.substring(i + 1); } i = file.indexOf("-"); if (i >= 0) { file = file.substring(i + 1); } while (file.length() > 0 && !Character.isDigit(file.charAt(0))) { i = file.indexOf("-"); if (i >= 0) { file = file.substring(i + 1); } else { break; } } version = file; } } } // 返回版本号,如果为空返回缺省版本号 return version == null || version.length() == 0 ? defaultVersion : version; } catch (Throwable e) { // 防御性容错 // 忽略异常,返回缺省版本号 LOGGER.error("return default version, ignore exception " + e.getMessage(), e); return defaultVersion; } } public static void checkDuplicate(Class<?> cls, boolean failOnError) { checkDuplicate(cls.getName().replace('.', '/') + ".class", failOnError); } public static void checkDuplicate(Class<?> cls) { checkDuplicate(cls, false); } public static void checkDuplicate(String path, boolean failOnError) { try { // 在ClassPath搜文件 Enumeration<URL> urls = ClassHelper.getCallerClassLoader(Version.class).getResources(path); Set<String> files = new HashSet<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String file = url.getFile(); if (file != null && file.length() > 0) { files.add(file); } } } // 如果有多个,就表示重复 if (files.size() > 1) { String error = "Duplicate class " + path + " in " + files.size() + " jar " + files; if (failOnError) { throw new IllegalStateException(error); } else { LOGGER.error(error); } } } catch (Throwable e) { // 防御性容错 LOGGER.error(e.getMessage(), e); } } }