【问题标题】:Items set with spymemcached cannot be fetched with php memcached使用 spymemcached 设置的项目无法使用 php memcached 获取
【发布时间】:2013-09-25 15:25:28
【问题描述】:

我正在使用 spymemcached。我设置了几个项目。然后我运行一个 php 脚本,但是我无法使用 php memcached 获取所有这些项目。 PHP-Memcached 只能部分检索这些项目。

我无法更改 php 的散列算法或分发策略。在我们的系统中,我们使用默认散列(根据 php.net 文档,这是 jenkin 的一次一个)。分配策略是 php-memcached 的模数。我读过 spymemcached 使用一致的哈希。有什么方法可以在 spymemcached 中使用模散列。

换句话说,我怎样才能使 spymemcached 的 set 操作或任何其他与 php-memcached 的 get 操作兼容的存储操作?

如果 spymemcached 无法做到这一点,java 中是否还有其他 memcached 客户端允许我这样做?

帮助不仅会得到赞赏,还会得到赏金。

Java 代码:

public static void main(String [] args) {
    List<InetSocketAddress> addrs = new ArrayList<>();
    addrs.add(new InetSocketAddress("10.90.12.87", 11211));
    addrs.add(new InetSocketAddress("10.90.12.87", 11311));
    try {
        MemcachedClient memcache = new MemcachedClient(addrs);
        memcache.add("foo", 0, "bar");
        memcache.add("sample", 0, "key");
        memcache.add("try", 0, "another");
        memcache.add("ax-spadg-list", 0, "3045,6645");
    } catch (IOException ex) {
        Logger.getLogger(CategoryDataOperator.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("Done");
}

PHP 代码:

<?php
$mem = new Memcached();
$mem->addServer('10.90.12.87', 11211);
$mem->addServer('10.90.12.87', 11311);
var_dump $mem->get('foo');
var_dump($mem->get('try'));
var_dump($mem->get('sample'));
var_dump($mem->get('ax-spadg-list'));

【问题讨论】:

  • 您可能有兴趣在这里阅读一下:Java Memcached Client
  • 我已经在使用它了。然而,使用 spymemcached 设置的项目在使用 php memcached 检索它们时给我带来了麻烦。我正在寻找的答案
  • 我已经完全理解了,但是看到在那里回答的用户,他写道他是 Spymemcachedn 的作者,所以如果你 ping 他,你可能会得到更多信息。他或许知道这个问题。
  • 是的,但我不知道如何联系他。他的个人资料没有联系信息。另外,我已经评论了两次,但他还没有回复。
  • 抱歉多版,但我没有正确阅读您的问题和要求 php 客户端无法更改。我为您提供 3 个解决方案。

标签: java php memcached spymemcached


【解决方案1】:

问题是关于Hash的,php-memcached默认的hash是

(Jenkins 一次一个)项目密钥散列算法

而 spymemcached 哈希列表是:

  • NATIVE_HASH: 只是 Native hash (String.hashCode()). 与默认的 php-memcached 不匹配 Memcached::HASH_DEFAULT
  • CRC_HASH => Memcached::HASH_CRC
  • FNV1_64_HASH => Memcached::HASH_FNV1_64
  • FNV1A_64_HASH => Memcached::HASH_FNV1A_64
  • FNV1_32_HASH => Memcached::HASH_FNV1_32
  • FNV1A_32_HASH => Memcached::HASH_FNV1A_32
  • KETAMA_HASH => "ketama 使用的基于 MD5 的哈希算法。"所以也许Memcached::HASH_MD5 但无论如何不是Memcached::HASH_DEFAULT

所以如果不能更改 PHP 客户端配置或扩展 spymemcached 库,这两个库之间不存在直接匹配。

解决方案 1: 如果您查看 history(您可以查看 php 客户端哈希修改的示例)。

解决方案 2: 否则,您可以创建一个 JenkinHash 类(我复制了 Xmemcached 代码:https://code.google.com/p/xmemcached/source/browse/trunk/src/main/java/net/rubyeye/xmemcached/HashAlgorithm.java?r=801#176 [但请考虑 Xmemcached 许可证并将作者/许可证保留在源代码中] )

import net.spy.memcached.HashAlgorithm;

import java.io.UnsupportedEncodingException;

public class JenkinsHash implements HashAlgorithm {
    @Override
    public long hash(String k) {
        try {
            int hash = 0;
            for (byte bt : k.getBytes("utf-8")) {
                hash += (bt & 0xFF);
                hash += (hash << 10);
                hash ^= (hash >>> 6);
            }
            hash += (hash << 3);
            hash ^= (hash >>> 11);
            hash += (hash << 15);
            return hash;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("Hash function error", e);
        }
    }
}

然后:

import net.spy.memcached.*;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

    public static void main(String[] args) throws IOException {
        List<InetSocketAddress> addrs = new ArrayList<InetSocketAddress>();
        addrs.add(new InetSocketAddress("127.0.0.1", 11211));
        addrs.add(new InetSocketAddress("172.28.29.22", 11211));
        try {
            ConnectionFactory connectionFactory = new ConnectionFactoryBuilder()
                .setProtocol(ConnectionFactoryBuilder.Protocol.TEXT)
                .setHashAlg(new JenkinsHash())
                .setLocatorType(ConnectionFactoryBuilder.Locator.ARRAY_MOD).build();
            MemcachedClient memcache = new MemcachedClient(connectionFactory, addrs);
            memcache.add("foo", 0, "bar2");
            memcache.add("sample", 0, "key");
            memcache.add("try", 0, "another");
            memcache.add("ax-spadg-list", 0, "3045,6645");
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("Done");
    }
}

用php脚本:

<?php

$memcached = new Memcached();
$memcached->addserver('127.0.0.1', 11211);
$memcached->addserver('172.28.29.22', 11211);
var_dump($memcached->get('foo'));
var_dump($memcached->get('try'));
var_dump($memcached->get('sample'));
var_dump($memcached->get('ax-spadg-list'));

测试:

$ echo "flush_all" | nc 172.28.29.22 11211 && echo "flush_all" | nc 127.0.0.1 11211
OK
OK
$ php mem.php 
bool(false)
bool(false)
bool(false)
bool(false)

RUN JAVA

$ php mem.php 
string(4) "bar2"
string(7) "another"
string(3) "key"
string(9) "3045,6645"

解决方案 3: 使用 https://code.google.com/p/xmemcached/ONE_AT_A_TIME 哈希算法

import net.rubyeye.xmemcached.HashAlgorithm;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.impl.ArrayMemcachedSessionLocator;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

public class Main {

    public static void main(String[] args) throws IOException, InterruptedException, MemcachedException, TimeoutException {
        List<InetSocketAddress> addrs = new ArrayList<InetSocketAddress>();
        addrs.add(new InetSocketAddress("127.0.0.1", 11211));
        addrs.add(new InetSocketAddress("172.28.29.22", 11211));
        MemcachedClientBuilder builder = new XMemcachedClientBuilder(addrs);
        builder.setSessionLocator(new ArrayMemcachedSessionLocator(HashAlgorithm.ONE_AT_A_TIME));
        MemcachedClient memcachedClient = builder.build();
        memcachedClient.set("foo", 0, "bar2");
        memcachedClient.set("sample", 0, "key");
        memcachedClient.set("try", 0, "another");
        memcachedClient.set("ax-spadg-list", 0, "3045,6645");
        memcachedClient.shutdown();
        System.out.println("Done");
    }
}

【讨论】:

  • 我花了很长时间才回复你。我尝试了 xmemcached 解决方案。但是,它仍然不起作用。它尝试仅在一台服务器上定位项目。我从 php 设置项目,并尝试在 java 中获取它。抛出 ArrayIndexOutOfBounds 异常!分销策略搞砸了吗?
  • 不能在这里发帖。代码看起来很乱。我们可以在某个地方聊天吗?我被困在这个项目中太久了。看来只有你能帮助我。您能否将您的电子邮件 ID 或任何其他联系方式发给我?
  • MemcachedClientBuilder builder = new xmemcachedClientBuilder(AddrUtil.getAddresses("127.0.0.1:11211 127.0.0.1:11311")); builder.setSessionLocator(new ArrayMemcachedSessionLocator(HashAlgorithm.ONE_AT_A_TIME)); MemcachedClient memcachedClient = builder.build(); System.out.println("试图获得 100 万个项目"); System.out.println(memcachedClient.get("foo")); System.out.println(memcachedClient.get("bar")); System.out.println(memcachedClient.get("yin"));
  • 明天聊天我今天不能
  • 可以使用 gist 或其他东西 :)
【解决方案2】:

spymemcached 支持的哈希算法在这里:https://github.com/couchbase/spymemcached/blob/master/src/main/java/net/spy/memcached/DefaultHashAlgorithm.java

您应该能够通过使用 ConnectionFactory 创建您的 MemcachedClient 来更改哈希算法。做这样的事情:

ConnectionFactoryBuilder builder = new ConnectionFactoryBuilder();
builder.setHashAlgorithm(HashAlgorithm.CRC_HASH);
ConnectionFactory factory = builder.build();
MemcachedClient client = new MemcachedClient(Arrays.asList(new InetSocketAddr("localhost", 11211)), factory);

【讨论】:

  • 我正在显示cannot find symbol: CRC32。是否有任何其他 jar 或我需要为 CRC32 添加的东西?
  • 我编辑了答案以包含上述文件中包含的一种哈希类型。上面的代码应该作为参考。我实际上并没有编译它。请注意文件中其他可能的哈希算法是 NATIVE_HASH、FNV1_64_HASH、KETAMA_HASH 等。
  • 谢谢。但是 HashAlgorithm 是一个接口,所以它没有你提到的字段。我用DefaultHashAlgorithm.CRC_HASH。但是我尝试了所有算法。没有任何效果:(
  • 在 php 中,memcached 散列设置为默认值,即 php.net 上指定的 jenkin's one by one。我不知道 spymemcached 中的默认散列是什么。 php memcached 也具有模数以及与默认分布策略一致的特性。模是默认值。我选择了ConnectionFactoryBuilder.Locator.ARRAY_MOD。但仍然没有运气
【解决方案3】:

回复:Kakawait(以及 Shades88)

解决方案 #2 不正确,因为 xmemcached 没有正确移植使用未签名的 Jenkins 哈希的原始 C 代码。解决这个问题也将解决 Shades88 看到的 ArrayIndexOutOfBoundsException。

public class JenkinsHash implements HashAlgorithm {
    @Override
    public long hash(String k) {
        try {
            int hash = 0;
            for (byte bt : k.getBytes("utf-8")) {
                hash += (bt & 0xFF);
                hash += (hash << 10);
                hash ^= (hash >>> 6);
            }
            hash += (hash << 3);
            hash ^= (hash >>> 11);
            hash += (hash << 15);

            // the hash variable in the original C code is a uint32.
            // convert the java signed int to an "unsigned",
            // represented via a long:
            return hash & 0xFFFFFFFFl;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("Hash function error", e);
        }
    }
}

// Unit test
public class JenkinsHashTest {
    @Test
    public void testHash() throws Exception {
        JenkinsHash j = new JenkinsHash();
        Properties p = new Properties();

        // This file contains k/v mappings,
        // with values generated by the reference C code
        p.load(new FileReader("src/test/resources/jenkinsHashTest.dat"));

        for (Entry<Object, Object> entry : p.entrySet()) {
            long result = j.hash((String)entry.getKey());
            // Print out hash mismatches
            if (result != Long.parseLong((String)entry.getValue())) {
                System.out.println("Key: " + (String)entry.getKey());
                System.out.println("Expected Hash Value: " + Long.parseLong((String)entry.getValue()));
                System.out.println("Actual Hash Value: " + result);
            }
            assertEquals(result, Long.parseLong((String)entry.getValue()));
        }
    }
}

测试数据文件用于将 Java 代码与 C 代码进行比较。构建 C 代码,然后散列一堆随机单词并将它们映射到一个文件中,如下所示:

jenkinsHashTest.dat:

sausage=2834523395
blubber=1103975961
pencil=3318404908
cloud=670342857
moon=2385442906
water=3403519606
computer=2375101981
school=1513618861
network=2981967937
hammer=1218821080

...根据需要添加任意数量

【讨论】:

  • 是的,我不久前想通了,只是在 hash 变量上使用了 abs 函数。将其类型转换为long 会使其更快一点吗?我也会试试这个。
  • 我添加的代码将哈希变量转换回无符号,这是与 C 代码等价所需要的。使用 abs() 将防止 ArrayIndexOutOfBoundsException 但会给出不同的哈希结果,这可能会导致 Java 和 PHP 之间的兼容性问题。我看的原始C代码在这里:en.wikipedia.org/wiki/Jenkins_hash_function
  • 最后一点:abs() 不会导致内存缓存问题,直到您的 Java 代码实际命中哈希为负数的键。这就是PHP不兼容的时候。
猜你喜欢
  • 1970-01-01
  • 2011-12-09
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 2016-07-07
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
相关资源
最近更新 更多