All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.distributedlog.service.DistributedLogClientBuilder Maven / Gradle / Ivy

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.distributedlog.service;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.twitter.common.zookeeper.ServerSet;
import org.apache.distributedlog.client.ClientConfig;
import org.apache.distributedlog.client.DistributedLogClientImpl;
import org.apache.distributedlog.client.monitor.MonitorServiceClient;
import org.apache.distributedlog.client.proxy.ClusterClient;
import org.apache.distributedlog.client.resolver.DefaultRegionResolver;
import org.apache.distributedlog.client.resolver.RegionResolver;
import org.apache.distributedlog.client.routing.RegionsRoutingService;
import org.apache.distributedlog.client.routing.RoutingService;
import org.apache.distributedlog.client.routing.RoutingUtils;
import org.apache.distributedlog.thrift.service.DistributedLogService;
import com.twitter.finagle.Name;
import com.twitter.finagle.Resolver$;
import com.twitter.finagle.Service;
import com.twitter.finagle.ThriftMux;
import com.twitter.finagle.builder.ClientBuilder;
import com.twitter.finagle.stats.NullStatsReceiver;
import com.twitter.finagle.stats.StatsReceiver;
import com.twitter.finagle.thrift.ClientId;
import com.twitter.finagle.thrift.ThriftClientFramedCodec;
import com.twitter.finagle.thrift.ThriftClientRequest;
import com.twitter.util.Duration;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.Option;

/**
 * Builder to build {@link DistributedLogClient}.
 */
public final class DistributedLogClientBuilder {

    private static final Logger logger = LoggerFactory.getLogger(DistributedLogClientBuilder.class);

    private static final Random random = new Random(System.currentTimeMillis());

    private String name = null;
    private ClientId clientId = null;
    private RoutingService.Builder routingServiceBuilder = null;
    private ClientBuilder clientBuilder = null;
    private String serverRoutingServiceFinagleName = null;
    private StatsReceiver statsReceiver = new NullStatsReceiver();
    private StatsReceiver streamStatsReceiver = new NullStatsReceiver();
    private ClientConfig clientConfig = new ClientConfig();
    private boolean enableRegionStats = false;
    private final RegionResolver regionResolver = new DefaultRegionResolver();

    /**
     * Create a client builder.
     *
     * @return client builder
     */
    public static DistributedLogClientBuilder newBuilder() {
        return new DistributedLogClientBuilder();
    }

    /**
     * Create a new client builder from an existing {@code builder}.
     *
     * @param builder the existing builder.
     * @return a new client builder.
     */
    public static DistributedLogClientBuilder newBuilder(DistributedLogClientBuilder builder) {
        DistributedLogClientBuilder newBuilder = new DistributedLogClientBuilder();
        newBuilder.name = builder.name;
        newBuilder.clientId = builder.clientId;
        newBuilder.clientBuilder = builder.clientBuilder;
        newBuilder.routingServiceBuilder = builder.routingServiceBuilder;
        newBuilder.statsReceiver = builder.statsReceiver;
        newBuilder.streamStatsReceiver = builder.streamStatsReceiver;
        newBuilder.enableRegionStats = builder.enableRegionStats;
        newBuilder.serverRoutingServiceFinagleName = builder.serverRoutingServiceFinagleName;
        newBuilder.clientConfig = ClientConfig.newConfig(builder.clientConfig);
        return newBuilder;
    }

    // private constructor
    private DistributedLogClientBuilder() {}

    /**
     * Client Name.
     *
     * @param name
     *          client name
     * @return client builder.
     */
    public DistributedLogClientBuilder name(String name) {
        DistributedLogClientBuilder newBuilder = newBuilder(this);
        newBuilder.name = name;
        return newBuilder;
    }

    /**
     * Client ID.
     *
     * @param clientId
     *          client id
     * @return client builder.
     */
    public DistributedLogClientBuilder clientId(ClientId clientId) {
        DistributedLogClientBuilder newBuilder = newBuilder(this);
        newBuilder.clientId = clientId;
        return newBuilder;
    }

    /**
     * Serverset to access proxy services.
     *
     * @param serverSet
     *          server set.
     * @return client builder.
     */
    public DistributedLogClientBuilder serverSet(ServerSet serverSet) {
        DistributedLogClientBuilder newBuilder = newBuilder(this);
        newBuilder.routingServiceBuilder = RoutingUtils.buildRoutingService(serverSet);
        newBuilder.enableRegionStats = false;
        return newBuilder;
    }

    /**
     * Server Sets to access proxy services.
     *
     * 

The local server set will be tried first then remotes. * * @param local local server set. * @param remotes remote server sets. * @return client builder. */ public DistributedLogClientBuilder serverSets(ServerSet local, ServerSet...remotes) { DistributedLogClientBuilder newBuilder = newBuilder(this); RoutingService.Builder[] builders = new RoutingService.Builder[remotes.length + 1]; builders[0] = RoutingUtils.buildRoutingService(local); for (int i = 1; i < builders.length; i++) { builders[i] = RoutingUtils.buildRoutingService(remotes[i - 1]); } newBuilder.routingServiceBuilder = RegionsRoutingService.newBuilder() .resolver(regionResolver) .routingServiceBuilders(builders); newBuilder.enableRegionStats = remotes.length > 0; return newBuilder; } /** * Name to access proxy services. * * @param finagleNameStr * finagle name string. * @return client builder. */ public DistributedLogClientBuilder finagleNameStr(String finagleNameStr) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.routingServiceBuilder = RoutingUtils.buildRoutingService(finagleNameStr); newBuilder.enableRegionStats = false; return newBuilder; } /** * Finagle name strs to access proxy services. * *

The local finalge name str will be tried first, then remotes. * * @param local local server set. * @param remotes remote server sets. * @return client builder. */ public DistributedLogClientBuilder finagleNameStrs(String local, String...remotes) { DistributedLogClientBuilder newBuilder = newBuilder(this); RoutingService.Builder[] builders = new RoutingService.Builder[remotes.length + 1]; builders[0] = RoutingUtils.buildRoutingService(local); for (int i = 1; i < builders.length; i++) { builders[i] = RoutingUtils.buildRoutingService(remotes[i - 1]); } newBuilder.routingServiceBuilder = RegionsRoutingService.newBuilder() .routingServiceBuilders(builders) .resolver(regionResolver); newBuilder.enableRegionStats = remotes.length > 0; return newBuilder; } /** * URI to access proxy services. * *

Assuming the write proxies are announced under `.write_proxy` of the provided namespace uri. * The builder will convert the dl uri (e.g. distributedlog://{zkserver}/path/to/namespace) to * zookeeper serverset based finagle name str (`zk!{zkserver}!/path/to/namespace/.write_proxy`) * * @param uri namespace uri to access the serverset of write proxies * @return distributedlog builder */ public DistributedLogClientBuilder uri(URI uri) { DistributedLogClientBuilder newBuilder = newBuilder(this); String zkServers = uri.getAuthority().replace(";", ","); String[] zkServerList = StringUtils.split(zkServers, ','); String finagleNameStr = String.format( "zk!%s!%s/.write_proxy", zkServerList[random.nextInt(zkServerList.length)], // zk server uri.getPath()); newBuilder.routingServiceBuilder = RoutingUtils.buildRoutingService(finagleNameStr); newBuilder.enableRegionStats = false; return newBuilder; } /** * Address of write proxy to connect. * * @param address * write proxy address. * @return client builder. */ public DistributedLogClientBuilder host(SocketAddress address) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.routingServiceBuilder = RoutingUtils.buildRoutingService(address); newBuilder.enableRegionStats = false; return newBuilder; } private DistributedLogClientBuilder routingServiceBuilder(RoutingService.Builder builder) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.routingServiceBuilder = builder; newBuilder.enableRegionStats = false; return newBuilder; } /** * Routing Service to access proxy services. * * @param routingService * routing service * @return client builder. */ @VisibleForTesting public DistributedLogClientBuilder routingService(RoutingService routingService) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.routingServiceBuilder = RoutingUtils.buildRoutingService(routingService); newBuilder.enableRegionStats = false; return newBuilder; } /** * Stats receiver to expose client stats. * * @param statsReceiver * stats receiver. * @return client builder. */ public DistributedLogClientBuilder statsReceiver(StatsReceiver statsReceiver) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.statsReceiver = statsReceiver; return newBuilder; } /** * Stream Stats Receiver to expose per stream stats. * * @param streamStatsReceiver * stream stats receiver * @return client builder. */ public DistributedLogClientBuilder streamStatsReceiver(StatsReceiver streamStatsReceiver) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.streamStatsReceiver = streamStatsReceiver; return newBuilder; } /** * Set underlying finagle client builder. * * @param builder * finagle client builder. * @return client builder. */ public DistributedLogClientBuilder clientBuilder(ClientBuilder builder) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientBuilder = builder; return newBuilder; } /** * Backoff time when redirecting to an already retried host. * * @param ms * backoff time. * @return client builder. */ public DistributedLogClientBuilder redirectBackoffStartMs(int ms) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setRedirectBackoffStartMs(ms); return newBuilder; } /** * Max backoff time when redirecting to an already retried host. * * @param ms * backoff time. * @return client builder. */ public DistributedLogClientBuilder redirectBackoffMaxMs(int ms) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setRedirectBackoffMaxMs(ms); return newBuilder; } /** * Max redirects that is allowed per request. * *

If redirects are exhausted, fail the request immediately. * * @param redirects * max redirects allowed before failing a request. * @return client builder. */ public DistributedLogClientBuilder maxRedirects(int redirects) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setMaxRedirects(redirects); return newBuilder; } /** * Timeout per request in millis. * * @param timeoutMs * timeout per request in millis. * @return client builder. */ public DistributedLogClientBuilder requestTimeoutMs(int timeoutMs) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setRequestTimeoutMs(timeoutMs); return newBuilder; } /** * Set thriftmux enabled. * * @param enabled * is thriftmux enabled * @return client builder. */ public DistributedLogClientBuilder thriftmux(boolean enabled) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setThriftMux(enabled); return newBuilder; } /** * Set failfast stream exception handling enabled. * * @param enabled * is failfast exception handling enabled * @return client builder. */ public DistributedLogClientBuilder streamFailfast(boolean enabled) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setStreamFailfast(enabled); return newBuilder; } /** * Set the regex to match stream names that the client cares about. * * @param nameRegex * stream name regex * @return client builder */ public DistributedLogClientBuilder streamNameRegex(String nameRegex) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setStreamNameRegex(nameRegex); return newBuilder; } /** * Whether to use the new handshake endpoint to exchange ownership cache. * *

Enable this when the servers are updated to support handshaking with client info. * * @param enabled * new handshake endpoint is enabled. * @return client builder. */ public DistributedLogClientBuilder handshakeWithClientInfo(boolean enabled) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setHandshakeWithClientInfo(enabled); return newBuilder; } /** * Set the periodic handshake interval in milliseconds. * *

Every intervalMs, the DL client will handshake with existing proxies again. * If the interval is less than ownership sync interval, the handshake won't sync ownerships. Otherwise, it will. * * @see #periodicOwnershipSyncIntervalMs(long) * @param intervalMs * handshake interval * @return client builder. */ public DistributedLogClientBuilder periodicHandshakeIntervalMs(long intervalMs) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setPeriodicHandshakeIntervalMs(intervalMs); return newBuilder; } /** * Set the periodic ownership sync interval in milliseconds. * *

If periodic handshake is enabled, the handshake will sync ownership if the elapsed time is larger than * sync interval. * * @see #periodicHandshakeIntervalMs(long) * @param intervalMs * interval that handshake should sync ownerships. * @return client builder */ public DistributedLogClientBuilder periodicOwnershipSyncIntervalMs(long intervalMs) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setPeriodicOwnershipSyncIntervalMs(intervalMs); return newBuilder; } /** * Enable/Disable periodic dumping ownership cache. * * @param enabled * flag to enable/disable periodic dumping ownership cache * @return client builder. */ public DistributedLogClientBuilder periodicDumpOwnershipCache(boolean enabled) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setPeriodicDumpOwnershipCacheEnabled(enabled); return newBuilder; } /** * Set periodic dumping ownership cache interval. * * @param intervalMs * interval on dumping ownership cache, in millis. * @return client builder */ public DistributedLogClientBuilder periodicDumpOwnershipCacheIntervalMs(long intervalMs) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setPeriodicDumpOwnershipCacheIntervalMs(intervalMs); return newBuilder; } /** * Enable handshake tracing. * * @param enabled * flag to enable/disable handshake tracing * @return client builder */ public DistributedLogClientBuilder handshakeTracing(boolean enabled) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setHandshakeTracingEnabled(enabled); return newBuilder; } /** * Enable checksum on requests to the proxy. * * @param enabled * flag to enable/disable checksum * @return client builder */ public DistributedLogClientBuilder checksum(boolean enabled) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig.setChecksumEnabled(enabled); return newBuilder; } /** * Configure the finagle name string for the server-side routing service. * * @param nameStr name string of the server-side routing service * @return client builder */ public DistributedLogClientBuilder serverRoutingServiceFinagleNameStr(String nameStr) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.serverRoutingServiceFinagleName = nameStr; return newBuilder; } DistributedLogClientBuilder clientConfig(ClientConfig clientConfig) { DistributedLogClientBuilder newBuilder = newBuilder(this); newBuilder.clientConfig = ClientConfig.newConfig(clientConfig); return newBuilder; } /** * Build distributedlog client. * * @return distributedlog client. */ public DistributedLogClient build() { return buildClient(); } /** * Build monitor service client. * * @return monitor service client. */ public MonitorServiceClient buildMonitorClient() { return buildClient(); } @SuppressWarnings("unchecked") ClusterClient buildServerRoutingServiceClient(String serverRoutingServiceFinagleName) { ClientBuilder builder = this.clientBuilder; if (null == builder) { builder = ClientBuilder.get() .tcpConnectTimeout(Duration.fromMilliseconds(200)) .connectTimeout(Duration.fromMilliseconds(200)) .requestTimeout(Duration.fromSeconds(1)) .retries(20); if (!clientConfig.getThriftMux()) { builder = builder.hostConnectionLimit(1); } } if (clientConfig.getThriftMux()) { builder = builder.stack(ThriftMux.client().withClientId(clientId)); } else { builder = builder.codec(ThriftClientFramedCodec.apply(Option.apply(clientId))); } Name name; try { name = Resolver$.MODULE$.eval(serverRoutingServiceFinagleName); } catch (Exception exc) { logger.error("Exception in Resolver.eval for name {}", serverRoutingServiceFinagleName, exc); throw new RuntimeException(exc); } // builder the client Service client = ClientBuilder.safeBuildFactory( builder.dest(name).reportTo(statsReceiver.scope("routing")) ).toService(); DistributedLogService.ServiceIface service = new DistributedLogService.ServiceToClient(client, new TBinaryProtocol.Factory()); return new ClusterClient(client, service); } DistributedLogClientImpl buildClient() { checkNotNull(name, "No name provided."); checkNotNull(clientId, "No client id provided."); checkNotNull(routingServiceBuilder, "No routing service builder provided."); checkNotNull(statsReceiver, "No stats receiver provided."); if (null == streamStatsReceiver) { streamStatsReceiver = new NullStatsReceiver(); } Optional serverRoutingServiceClient = Optional.absent(); if (null != serverRoutingServiceFinagleName) { serverRoutingServiceClient = Optional.of( buildServerRoutingServiceClient(serverRoutingServiceFinagleName)); } RoutingService routingService = routingServiceBuilder .statsReceiver(statsReceiver.scope("routing")) .build(); DistributedLogClientImpl clientImpl = new DistributedLogClientImpl( name, clientId, routingService, clientBuilder, clientConfig, serverRoutingServiceClient, statsReceiver, streamStatsReceiver, regionResolver, enableRegionStats); routingService.startService(); clientImpl.handshake(); return clientImpl; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy