// ================================================================================================= // Copyright 2011 Twitter, Inc. // ------------------------------------------------------------------------------------------------- // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this work except in compliance with the License. // You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.memcached; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import net.spy.memcached.AddrUtil; import net.spy.memcached.BinaryConnectionFactory; import net.spy.memcached.DefaultConnectionFactory; import net.spy.memcached.MemcachedClient; import net.spy.memcached.MemcachedNode; import net.spy.memcached.NodeLocator; import net.spy.memcached.vbucket.config.Config; /** * Helper class for creating a memcached client. * * @author William Farner */ public class Memcached { private static Logger log = Logger.getLogger(Memcached.class.getName()); private static final Joiner serverListJoiner = Joiner.on(" "); /** * Creates a new memcached client for use as an interface to a kestrel queue. * * @param servers The kestrel servers to use. * @return A memcached client. */ public static MemcachedClient newKestrelClient(List<String> servers) { try { return new MemcachedClient(new KestrelConnectionFactory(), AddrUtil.getAddresses(serverListJoiner.join(servers))); } catch (IOException e) { log.log(Level.SEVERE, "Failed to build server list.", e); return null; } } /** * Creates a new generic memcached client that uses daemon threads for IO and the binary protocol. * * @param servers The memcached servers to use. * @return A memcached client. */ public static MemcachedClient newMemcachedClient(List<String> servers) { try { return new MemcachedClient(new DaemonThreadBinaryConnectionFactory(), AddrUtil.getAddresses(serverListJoiner.join(servers))); } catch (IOException e) { log.log(Level.SEVERE, "Failed to build server list.", e); return null; } } /** * Connection factory to use for interfacing with kestrel. */ private static class KestrelConnectionFactory extends DaemonThreadAsciiConnectionFactory { @Override public NodeLocator createLocator(List<MemcachedNode> nodes) { return new KestrelNodeLocator(nodes); } } /** * Connection factory that uses daemon threads, so the process completes after the application * thread(s) complete. This connection factory uses the default (ASCII) protocol. */ private static class DaemonThreadAsciiConnectionFactory extends DefaultConnectionFactory { @Override public boolean isDaemon() { return true; } } /** * Connection factory that uses daemon threads, so the process completes after the application * thread(s) complete. This connection factory uses the binary protocol. */ private static class DaemonThreadBinaryConnectionFactory extends BinaryConnectionFactory { @Override public boolean isDaemon() { return true; } } /** * Node locator for kestrel. This issues requests to a random node. */ private static class KestrelNodeLocator implements NodeLocator { private final List<MemcachedNode> nodes = Lists.newArrayList(); private final Random rand = new Random(); public KestrelNodeLocator(List<MemcachedNode> nodes) { Preconditions.checkNotNull(nodes); Preconditions.checkState(nodes.size() > 0); updateNodes(nodes); } @Override public MemcachedNode getPrimary(String s) { return nodes.get(rand.nextInt(nodes.size())); } @Override public Iterator<MemcachedNode> getSequence(String s) { List<MemcachedNode> shuffled = Lists.newLinkedList(nodes); Collections.shuffle(shuffled); return shuffled.iterator(); } @Override public Collection<MemcachedNode> getAll() { return nodes; } @Override public NodeLocator getReadonlyCopy() { return new KestrelNodeLocator(nodes); } @Override public void updateLocator(List<MemcachedNode> memcachedNodes, Config config) { updateNodes(memcachedNodes); } private void updateNodes(Collection<MemcachedNode> nodes) { this.nodes.clear(); this.nodes.addAll(nodes); } } }