這兩天主要是在最新版本的YCSB的基礎上用mapkeeperclient打包並連接mapkeeper端的server。不得不說ycsb在創建mapkeeper client的時候就是有問題的。我踩了很多坑,我要記下來做筆記。
首先是下載最新的YCSB:
git clone https://github.com/brianfrankcooper/YCSB.git
然後執行下面命令:
cd YCSB
vim pom.xml
在pom.xml文件中把有關mapkeeper的部分取消註釋:
取消註釋部分:
<mapkeeper.version>1.0</mapkeeper.version>
<module>mapkeeper</module>
在mapkeeper目錄下的pom.xml文件中添加:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
敲黑板的重點是這裏thrift一定要是0.6.1版本的!!!我試過最新0.12.0還有0.8.0都不行。這纔是我這幾天一直mvn打包的時候報錯的原因,如果不用這個會報以下錯誤:
Exception in thread "Thread-2" java.lang.IncompatibleClassChangeError: Implementing class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.yahoo.ycsb.db.MapKeeperClient.init(MapKeeperClient.java:69)
at com.yahoo.ycsb.DBWrapper.init(DBWrapper.java:86)
at com.yahoo.ycsb.ClientThread.run(ClientThread.java:91)
at java.lang.Thread.run(Thread.java:748)
我在網上搜索了好久也不知道上面是啥錯誤,原來是mapkeeper在mvn打包的時候只支持0.6.1版本。然後就是對於MapKeeperClient的修改了,在這裏最新版的MapKeeperClient.java文件中是有一些問題的就是把一些本該是用com.yahoo.ycsb.Status類型的表示用常用的int表示了。所以會報checkstyle的錯誤。同時格式也會不對顯示類似於本該空着幾格實際空了幾格的報錯,因爲可能是tab問題。所以需要手動修改。修改如下完整的mapkeeperclient文件:
/**
* Copyright (c) 2012 YCSB contributors. All rights reserved.
* <p>
* Licensed 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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. See accompanying
* LICENSE file.
*/
package com.yahoo.ycsb.db;
//import java.nio.CharBuffer;
//import java.util.Arrays;
//import java.nio.charset.Charset;
//import java.nio.charset.CharsetDecoder;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.Map;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
//import org.apache.commons.codec.binary.Base64;
import com.yahoo.mapkeeper.BinaryResponse;
import com.yahoo.mapkeeper.MapKeeper;
import com.yahoo.mapkeeper.Record;
import com.yahoo.mapkeeper.RecordListResponse;
import com.yahoo.mapkeeper.ResponseCode;
import com.yahoo.mapkeeper.ScanOrder;
import com.yahoo.ycsb.ByteIterator;
import com.yahoo.ycsb.DB;
import com.yahoo.ycsb.StringByteIterator;
import com.yahoo.ycsb.workloads.CoreWorkload;
import com.yahoo.ycsb.Status;
/**
* YCSB binding for <a href="http://redis.io/">Redis</a>.
*
* See {@code redis/README.md} for details.
*/
public class MapKeeperClient extends DB {
private static final String HOST = "mapkeeper.host";
private static final String HOST_DEFAULT = "localhost";
private static final String PORT = "mapkeeper.port";
private static final String PORT_DEFAULT = "9090";
private MapKeeper.Client c;
private boolean writeallfields;
private static boolean initteddb = false;
private static synchronized void initDB(Properties p, MapKeeper.Client c) throws TException {
if (!initteddb) {
initteddb = true;
c.addMap(p.getProperty(CoreWorkload.TABLENAME_PROPERTY, CoreWorkload.TABLENAME_PROPERTY_DEFAULT));
}
}
public void init() {
String host = getProperties().getProperty(HOST, HOST_DEFAULT);
int port = Integer.parseInt(getProperties().getProperty(PORT, PORT_DEFAULT));
TTransport tr = new TFramedTransport(new TSocket(host, port));
TProtocol proto = new TBinaryProtocol(tr);
c = new MapKeeper.Client(proto);
try {
tr.open();
initDB(getProperties(), c);
} catch (TException e) {
throw new RuntimeException(e);
}
writeallfields = Boolean.parseBoolean(getProperties().getProperty(CoreWorkload.WRITE_ALL_FIELDS_PROPERTY,
CoreWorkload.WRITE_ALL_FIELDS_PROPERTY_DEFAULT));
}
ByteBuffer encode(HashMap<String, ByteIterator> values) {
int len = 0;
for (Map.Entry<String, ByteIterator> entry : values.entrySet()) {
len += (entry.getKey().length() + 1 + entry.getValue().bytesLeft() + 1);
}
byte[] array = new byte[len];
int i = 0;
for (Map.Entry<String, ByteIterator> entry : values.entrySet()) {
for (int j = 0; j < entry.getKey().length(); j++) {
array[i] = (byte) entry.getKey().charAt(j);
i++;
}
array[i] = '\t';
// XXX would like to use sane delimiter (null, 254, 255, ...) but java makes this nearly impossible
i++;
ByteIterator v = entry.getValue();
i = v.nextBuf(array, i);
array[i] = '\t';
i++;
}
array[array.length - 1] = 0;
ByteBuffer buf = ByteBuffer.wrap(array);
buf.rewind();
return buf;
}
void decode(Set<String> fields, String tups, HashMap<String, ByteIterator> tup) {
String[] tok = tups.split("\\t");
if (tok.length == 0) {
throw new IllegalStateException("split returned empty array!");
}
for (int i = 0; i < tok.length; i += 2) {
if (fields == null || fields.contains(tok[i])) {
if (tok.length < i + 2) {
throw new IllegalStateException("Couldn't parse tuple <" + tups + "> at index " + i);
}
if (tok[i] == null || tok[i + 1] == null) {
throw new NullPointerException("Key is " + tok[i] + " val is + " + tok[i + 1]);
}
tup.put(tok[i], new StringByteIterator(tok[i + 1]));
}
}
if (tok.length == 0) {
System.err.println("Empty tuple: " + tups);
}
}
Status ycsbThriftRet(BinaryResponse succ, ResponseCode zero, ResponseCode one) {
return ycsbThriftRet(succ.responseCode, zero, one);
}
Status ycsbThriftRet(ResponseCode rc, ResponseCode zero, ResponseCode one) {
System.out.println("rc:"+ rc + "zero" + zero + "one" + one);
return rc == zero ? Status.OK : rc == one ? Status.NOT_FOUND : Status.ERROR;
}
ByteBuffer bufStr(String str) {
ByteBuffer buf = ByteBuffer.wrap(str.getBytes());
return buf;
}
String strResponse(BinaryResponse buf) {
try{
//Convert ByteBuffer to String
//System.out.println("bb:"+ buf.value.getClass() + Arrays.toString(buf.value.array()));
// ByteBuffer bb4= ByteBuffer.wrap(buf.value.array());
//String string = Charset.forName("UTF-8").decode(bb4).toString();
//String string = new String(buf.value.array(), "GB2312");
String string = new String(buf.value.array(), "ISO-8859-1");//一定要注意編碼問題
byte[] ret = string.getBytes("ISO-8859-1");
String s = "";
int i =0;
for(i = 0; i < ret.length - 1; i++) {
s += Byte.toString(ret[i]) + '\t';
}
s += ret[i];
//System.out.println("string:" + s);
return s;
} catch(Exception e){
e.printStackTrace();
return "";
}
}
// String getString(BinaryResponse buf) {
// try {
// ByteBuffer buffer = buf.value.array();
// Charset charset = Charset.forName("UTF-8");
// CharsetDecoder decoder = charset.newDecoder();
//// charBuffer = decoder.decode(buffer);//用這個的話,只能輸出來一次結果,第二次顯示爲空
// CharBuffer charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
// return charBuffer.toString();
// } catch (Exception ex) {
// ex.printStackTrace();
// return "";
// }
// }
@Override
public Status read(String table, String key, Set<String> fields,
Map<String, ByteIterator> result) {
try {
ByteBuffer buf = bufStr(key);
BinaryResponse succ = c.get(table, buf);
Status ret = ycsbThriftRet(
succ,
ResponseCode.Success,
ResponseCode.RecordNotFound);
System.out.println("succ:" + succ);
System.out.print("read status" + ret);
if (ret == Status.OK) {
System.out.println("str response succ" + succ.value);
decode(fields, strResponse(succ), (HashMap<String, ByteIterator>)result);
}
return ret;
} catch (TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status scan(String table, String startkey, int recordcount,
Set<String> fields, Vector<HashMap<String, ByteIterator>> result) {
try {
//XXX what to pass in for nulls / zeros?
RecordListResponse res = c.scan(table, ScanOrder.Ascending, bufStr(startkey), true, null, false, recordcount, 0);
Status ret = ycsbThriftRet(res.responseCode, ResponseCode.Success, ResponseCode.ScanEnded);
if (ret == Status.OK) {
for (Record r : res.records) {
HashMap<String, ByteIterator> tuple = new HashMap<String, ByteIterator>();
// Note: r.getKey() and r.getValue() call special helper methods that trim the buffer
// to an appropriate length, and memcpy it to a byte[]. Trying to manipulate the ByteBuffer
// directly leads to trouble.
tuple.put("key", new StringByteIterator(new String(r.getKey())));
decode(fields, new String(r.getValue())/*strBuf(r.bufferForValue())*/, tuple);
result.add(tuple);
}
}
return ret;
} catch (TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status update(String table, String key,
Map<String, ByteIterator> values) {
try {
if (!writeallfields) {
HashMap<String, ByteIterator> oldval = new HashMap<String, ByteIterator>();
read(table, key, null, oldval);
for (Map.Entry<String, ByteIterator> entry : values.entrySet()) {
oldval.put(entry.getKey(), entry.getValue());
}
values = oldval;
}
ResponseCode succ = c.update(table, bufStr(key), encode((HashMap<String, ByteIterator>)values));
return ycsbThriftRet(succ, ResponseCode.Success, ResponseCode.RecordNotFound);
} catch (TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status insert(String table, String key, Map<String, ByteIterator> values) {
try {
Status ret = ycsbThriftRet(c.insert(table, bufStr(key), encode((HashMap<String, ByteIterator>)values)),
ResponseCode.Success, ResponseCode.RecordExists);
return ret;
} catch (TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status delete(String table, String key) {
try {
return ycsbThriftRet(c.remove(table, bufStr(key)), ResponseCode.Success, ResponseCode.RecordExists);
} catch (TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
}
同時在該目錄下添加一個package-info.java文件。內容是(雖然很簡單但是對於打包很重要)
package com.yahoo.ycsb.db;
如果在進行mvn clean package的時候報錯如下:(在0.18.0版本之後com.yahoo.ycsb變成site.ycsb因此上面MapKeeperClient中的import com.yahoo.ycsb變成site.ycsb,同時MapKeeperClient和package-info中package變爲site.ycsb.db)
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-assembly-plugin:2.5.5:single (default) on project ycsb: Failed to create assembly: Artifact: site.ycsb:mapkeeper-binding:jar:0.18.0-SNAPSHOT (included by module) does not have an artifact with a file. Please ensure the package phase is run before the assembly is generated.
如果還是報錯嘗試將上面關於mapkeeper的信息註釋掉,排除mapkeeper之後進行mvn clean package,編譯成功後再取消mapkeeper的相關注釋。單獨對mapkeeper進行編譯(命令如下只編譯mapkeeper部分)。
0.18.0之後MapKeeperClient文件應該如下:
/**
* Copyright (c) 2012 YCSB contributors. All rights reserved.
*
* Licensed 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. See accompanying
* LICENSE file.
*/
package site.ycsb.db;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.Map;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import site.ycsb.Status;
import com.yahoo.mapkeeper.BinaryResponse;
import com.yahoo.mapkeeper.Record;
import com.yahoo.mapkeeper.RecordListResponse;
import com.yahoo.mapkeeper.ResponseCode;
import com.yahoo.mapkeeper.ScanOrder;
import com.yahoo.mapkeeper.MapKeeper;
import site.ycsb.ByteIterator;
import site.ycsb.StringByteIterator;
import site.ycsb.DB;
import site.ycsb.workloads.CoreWorkload;
/**
* Abstract class to adapt the default ycsb DB interface to Timeseries databases.
* This class is mostly here to be extended by Timeseries dataabases
* originally developed by Andreas Bader in <a href="https://github.com/TSDBBench/YCSB-TS">YCSB-TS</a>.
* <p>
* This class is mostly parsing the workload information passed through the default ycsb interface
* according to the information outlined in {@link TimeSeriesWorkload}.
* It also contains some minor utility methods relevant to Timeseries databases.
* </p>
* implSpec It's vital to call <tt>super.init()</tt> when overwriting the init method
* to correctly initialize the workload-parsing.
*/
public class MapKeeperClient extends DB {
private static final String HOST = "mapkeeper.host";
private static final String HOST_DEFAULT = "localhost";
private static final String PORT = "mapkeeper.port";
private static final String PORT_DEFAULT = "9090";
private MapKeeper.Client c;
private boolean writeallfields;
private static boolean initteddb = false;
private static synchronized void initDB(Properties p, MapKeeper.Client c) throws TException {
if(!initteddb) {
initteddb = true;
c.addMap(p.getProperty(CoreWorkload.TABLENAME_PROPERTY, CoreWorkload.TABLENAME_PROPERTY_DEFAULT));
}
}
public void init() {
String host = getProperties().getProperty(HOST, HOST_DEFAULT);
int port = Integer.parseInt(getProperties().getProperty(PORT, PORT_DEFAULT));
TTransport tr = new TFramedTransport(new TSocket(host, port));
TProtocol proto = new TBinaryProtocol(tr);
c = new MapKeeper.Client(proto);
try {
tr.open();
initDB(getProperties(), c);
} catch(TException e) {
throw new RuntimeException(e);
}
writeallfields = Boolean.parseBoolean(getProperties().getProperty(CoreWorkload.WRITE_ALL_FIELDS_PROPERTY,
CoreWorkload.WRITE_ALL_FIELDS_PROPERTY_DEFAULT));
}
ByteBuffer encode(Map<String, ByteIterator> values) {
int len = 0;
for(Map.Entry<String, ByteIterator> entry : values.entrySet()) {
len += (entry.getKey().length() + 1 + entry.getValue().bytesLeft() + 1);
}
byte[] array = new byte[len];
int i = 0;
for(Map.Entry<String, ByteIterator> entry : values.entrySet()) {
for(int j = 0; j < entry.getKey().length(); j++) {
array[i] = (byte)entry.getKey().charAt(j);
i++;
}
array[i] = '\t'; // XXX would like to use sane delimiter (null, 254,..) but java makes this nearly impossible
i++;
ByteIterator v = entry.getValue();
i = v.nextBuf(array, i);
array[i] = '\t';
i++;
}
array[array.length-1] = 0;
ByteBuffer buf = ByteBuffer.wrap(array);
buf.rewind();
return buf;
}
void decode(Set<String> fields, String tups, Map<String, ByteIterator> tup) {
String[] tok = tups.split("\\t");
if(tok.length == 0) {
throw new IllegalStateException("split returned empty array!");
}
for(int i = 0; i < tok.length; i+=2) {
if(fields == null || fields.contains(tok[i])) {
if(tok.length < i+2) {
throw new IllegalStateException("Couldn't parse tuple <" + tups + "> at index " + i);
}
if(tok[i] == null || tok[i+1] == null) {
throw new NullPointerException("Key is " + tok[i] + " val is + " + tok[i+1]);
}
tup.put(tok[i], new StringByteIterator(tok[i+1]));
}
}
if(tok.length == 0) {
System.err.println("Empty tuple: " + tups);
}
}
int ycsbThriftRet(BinaryResponse succ, ResponseCode zero, ResponseCode one) {
return ycsbThriftRet(succ.responseCode, zero, one);
}
int ycsbThriftRet(ResponseCode rc, ResponseCode zero, ResponseCode one) {
return
rc == zero ? 0 :
rc == one ? 1 : 2;
}
ByteBuffer bufStr(String str) {
ByteBuffer buf = ByteBuffer.wrap(str.getBytes());
return buf;
}
String strResponse(BinaryResponse buf) {
return new String(buf.value.array());
}
@Override
public Status read(String table, String key, Set<String> fields,
Map<String, ByteIterator> result) {
try {
ByteBuffer buf = bufStr(key);
BinaryResponse succ = c.get(table, buf);
int ret = ycsbThriftRet(
succ,
ResponseCode.RecordExists,
ResponseCode.RecordNotFound);
if(ret == 0) {
decode(fields, strResponse(succ), result);
}
return ret == 2 ? Status.OK : (ret == 0 ? Status.ERROR: Status.NOT_FOUND);
} catch(TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status scan(String table, String startkey, int recordcount,
Set<String> fields, Vector<HashMap<String, ByteIterator>> result) {
try {
//XXX what to pass in for nulls / zeros?
RecordListResponse res = c.scan(table, ScanOrder.Ascending, bufStr(startkey), true, null, false, recordcount, 0);
int ret = ycsbThriftRet(res.responseCode, ResponseCode.Success, ResponseCode.ScanEnded);
if(ret == 0) {
for(Record r : res.records) {
HashMap<String, ByteIterator> tuple = new HashMap<String, ByteIterator>();
// Note: r.getKey() and r.getValue() call special helper methods that trim the buffer
// to an appropriate length, and memcpy it to a byte[]. Trying to manipulate the ByteBuffer
// directly leads to trouble.
tuple.put("key", new StringByteIterator(new String(r.getKey())));
decode(fields, new String(r.getValue())/*strBuf(r.bufferForValue())*/, tuple);
result.add(tuple);
}
}
return ret == 0 ? Status.OK : (ret == 2 ? Status.ERROR: Status.NOT_FOUND);
} catch(TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status update(String table, String key,
Map<String, ByteIterator> values) {
try {
if(!writeallfields) {
HashMap<String, ByteIterator> oldval = new HashMap<String, ByteIterator>();
read(table, key, null, oldval);
for(Map.Entry<String, ByteIterator> entry : values.entrySet()) {
oldval.put(entry.getKey(), entry.getValue());
}
values = oldval;
}
ResponseCode succ = c.update(table, bufStr(key), encode(values));
int ret = ycsbThriftRet(succ, ResponseCode.RecordExists, ResponseCode.RecordNotFound);
return ret == 2 ? Status.OK : (ret == 0 ? Status.ERROR: Status.NOT_FOUND);
} catch(TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status insert(String table, String key,
Map<String, ByteIterator> values) {
try {
int r = 0;
r = ycsbThriftRet(c.insert(table, bufStr(key), encode(values)), ResponseCode.Success, ResponseCode.RecordExists);
return r == 0 ? Status.OK : (r == 1 ? Status.ERROR: Status.NOT_FOUND);
} catch(TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
@Override
public Status delete(String table, String key) {
try {
int ret = ycsbThriftRet(c.remove(table, bufStr(key)), ResponseCode.Success, ResponseCode.RecordExists);
return ret == 0 ? Status.OK : (ret == 1 ? Status.ERROR: Status.NOT_FOUND);
} catch(TException e) {
e.printStackTrace();
return Status.ERROR;
}
}
}
使用mvn打包:
編譯所有數據庫的話:
mvn clean package
只編譯mapkeeper的話(0.17版本以及之前):
mvn -pl com.yahoo.ycsb:mapkeeper-binding -am clean package
只編譯mapkeeper的話(0.18版本):
mvn -pl site.ycsb:mapkeeper-binding -am clean package
然後就可以編譯成功了。
然後就是修改YCSB/bin中的bindings.properties:
#添加
mapkeeper:com.yahoo.ycsb.db.MapKeeperClient
然後就是修改YCSB/bin中的ycsb:
#添加
"mapkeeper" : "com.yahoo.ycsb.db.MapKeeperClient",
然後就大功告成了,在client端:
bin/ycsb load mapkeeper -s -P workloads/workloada -p "mapkeeper.host=127.0.0.1" -p "mapkeeper.port=9090"
bin/ycsb run mapkeeper -s -P workloads/workloada -p "mapkeeper.host=127.0.0.1" -p "mapkeeper.port=9090"
如果執行上面命令出現報錯是因爲系統使用的python版本是python 3.X,如果使用python2.X就會正常運行,可以通過在ycsb文件中修改#!/usr/bin/env python爲#!/usr/bin/env python2即可,但是要確保系統是有python2.X的纔可以做這樣的修改:
File "bin/ycsb", line 226
except subprocess.CalledProcessError, err:
在mapkeeper server端開啓:
./mapkeeper_RHDB 0 1 1 /home/clever_wr/data
不報錯就顯示成功連接