/* * Copyright (c) 2014 Globo.com - ATeam * All rights reserved. * * This source is subject to the Apache License, Version 2.0. * Please see the LICENSE file for more information. * * Authors: See AUTHORS file * * 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.globo.galeb.verticles; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashSet; import java.util.Set; import com.globo.galeb.bus.ICallbackHealthcheck; import com.globo.galeb.bus.IEventObserver; import com.globo.galeb.bus.IQueueService; import com.globo.galeb.bus.MessageBus; import com.globo.galeb.bus.VertxQueueService; import com.globo.galeb.entity.Entity; import com.globo.galeb.entity.IJsonable; import com.globo.galeb.entity.IJsonable.StatusType; import com.globo.galeb.entity.impl.Farm; import com.globo.galeb.entity.impl.backend.BackendPool; import com.globo.galeb.entity.impl.backend.BackendWithoutSessionPool; import com.globo.galeb.logger.SafeLogger; import com.globo.galeb.rulereturn.HttpCode; import com.globo.galeb.scheduler.ISchedulerHandler; import com.globo.galeb.scheduler.impl.VertxPeriodicScheduler; import org.vertx.java.core.Handler; import org.vertx.java.core.http.HttpClient; import org.vertx.java.core.http.HttpClientRequest; import org.vertx.java.core.http.HttpClientResponse; import org.vertx.java.core.http.HttpHeaders; import org.vertx.java.core.json.JsonObject; import org.vertx.java.platform.Verticle; /** * Class HealthManagerVerticle. * * @author: See AUTHORS file. * @version: 1.0.0, Oct 23, 2014. */ public class HealthManagerVerticle extends Verticle implements IEventObserver, ICallbackHealthcheck { /** The backends map. */ private final Set<Entity> backendsMap = new HashSet<>(); /** The bad backends map. */ private final Set<Entity> badBackendsMap = new HashSet<>(); /** The http header host. */ private final String httpHeaderHost = HttpHeaders.HOST.toString(); /** The farm. */ private Farm farm; /** The queue service. */ private IQueueService queueService; /** The Json conf. */ private JsonObject conf; /** The logger. */ private SafeLogger log; /** * Class CheckBadBackendTaskHandler. * * @author: See AUTHORS file. * @version: 1.0.0, Oct 23, 2014. */ private class CheckBadBackendTaskHandler implements ISchedulerHandler { /* (non-Javadoc) * @see org.vertx.java.core.Handler#handle(java.lang.Object) */ @Override public void handle() { log.info("Checking bad backends..."); if (badBackendsMap!=null) { for (final Entity backend : badBackendsMap) { final BackendPool backendPool = farm.getBackendPoolById(backend .getParentId()); String[] hostWithPort = backend.toString().split(":"); String host = hostWithPort[0]; Integer port = Integer.parseInt(hostWithPort[1]); try { HttpClient client = vertx.createHttpClient() .setHost(host) .setPort(port) .exceptionHandler(new Handler<Throwable>() { @Override public void handle(Throwable event) {} }); HttpClientRequest cReq = client.get(backendPool.getHealthCheck(), new Handler<HttpClientResponse>() { @Override public void handle(HttpClientResponse cResp) { if (cResp!=null && cResp.statusCode()==HttpCode.OK) { queueService.publishBackendOk(backend.toJson()); } } }); cReq.headers().set(httpHeaderHost, backend.toString()); cReq.exceptionHandler(new Handler<Throwable>() { @Override public void handle(Throwable event) {} }); cReq.end(); } catch (RuntimeException e) { log.error(e.getMessage()); } } } } } /* (non-Javadoc) * @see org.vertx.java.platform.Verticle#start() */ @Override public void start() { log = new SafeLogger().setLogger(container.logger()); conf = container.config(); Long checkInterval = conf.getLong("checkInterval", 5000L); // Milliseconds Interval queueService = new VertxQueueService(vertx.eventBus(), log); queueService.registerHealthcheck(this); farm = new Farm(this); farm.setLogger(log) .setPlataform(vertx) .setQueueService(queueService) .setStaticConf(conf) .start(); new VertxPeriodicScheduler(vertx) .setPeriod(checkInterval).setHandler(new CheckBadBackendTaskHandler()).start(); log.info(String.format("Instance %s started", this.toString())); } /* (non-Javadoc) * @see com.globo.galeb.core.bus.IEventObserver#setVersion(java.lang.Long) */ @Override public void setVersion(Long version) {} /* (non-Javadoc) * @see com.globo.galeb.core.bus.IEventObserver#postAddEvent(java.lang.String) */ @Override public void postAddEvent(String message) { MessageBus messageBus = new MessageBus(message); if ("backend".equals(messageBus.getUriBase())) { boolean running = messageBus.getEntity().getString(IJsonable.STATUS_FIELDNAME, StatusType.RUNNING_STATUS.toString()) .equals(StatusType.RUNNING_STATUS.toString()); final Set<Entity> tempSet = running ? backendsMap : badBackendsMap; tempSet.add(new BackendWithoutSessionPool(messageBus.getEntity())); } }; /* (non-Javadoc) * @see com.globo.galeb.core.bus.IEventObserver#postDelEvent(java.lang.String) */ @Override public void postDelEvent(String message) { MessageBus messageBus = new MessageBus(message); if ("backend".equals(messageBus.getUriBase())) { boolean running = messageBus.getEntity().getString(IJsonable.STATUS_FIELDNAME, StatusType.RUNNING_STATUS.toString()) .equals(StatusType.RUNNING_STATUS.toString()); final Set<Entity> tempSet = running ? backendsMap : badBackendsMap; tempSet.remove(new BackendWithoutSessionPool(messageBus.getEntity())); } }; /* (non-Javadoc) * @see com.globo.galeb.core.bus.ICallbackHealthcheck#moveBackend(java.lang.String, boolean) */ @Override public void moveBackend(JsonObject backend, boolean status) throws UnsupportedEncodingException { String backendId = backend.getString(IJsonable.ID_FIELDNAME); if (backend!=null) { String uriDel = String.format("/backend/%s", URLEncoder.encode(backendId,"UTF-8")); String uriAdd = "/backend"; backend.putString(IJsonable.STATUS_FIELDNAME, status ? StatusType.FAILED_STATUS.toString() : StatusType.RUNNING_STATUS.toString()); queueService.queueToDel(backend, uriDel); backend.putString(IJsonable.STATUS_FIELDNAME, !status ? StatusType.FAILED_STATUS.toString() : StatusType.RUNNING_STATUS.toString()); queueService.queueToAdd(backend, uriAdd); } } }