https://blog.csdn.net/oe1019/article/details/105982128
由于原作者未提供源码,未解析代码逻辑实现,因此整理一下代码实现逻辑
项目结构
调用流程
1. 创建fabric用户信息
/**
通过获取配置文件中用户的身份信息,创建用户对象
*/
public static User getUser() {
User appuser = null;
File sampleStoreFile = new File(System.getProperty("user.home") + "/test.properties");
if (sampleStoreFile.exists()) { //For testing start fresh
sampleStoreFile.delete();
}
final SampleStore sampleStore = new SampleStore(sampleStoreFile);
try {
appuser = sampleStore.getMember("peer1", "Org1", "Org1MSP",
new File(String.valueOf(findFileSk(Paths.get(configUserPath).toFile()))),
new File("./src/main/resources/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem"));
} catch (Exception e) {
e.printStackTrace();
}
return appuser;
}
2. 创建Fabric连接池
private static ObjectPool<FabricConnection> fabricJavaPool = FabricConnectionPoolFactory.getPool(getUser(), "mychannel");
public class FabricConnectionPoolFactory {
private FabricConnectionPoolFactory() {
}
public static GenericObjectPool<FabricConnection> getPool(User appUser, String channel) {
return new FabricJavaPool(appUser, channel);
}
}
import org.apache.commons.pool2.impl.GenericObjectPool;
import com.github.samyuan1990.FabricJavaPool.api.FabricConnection;
public class FabricJavaPool extends GenericObjectPool<FabricConnection> {
public FabricJavaPool(User appUser, String channel) {
super(new ConnectionPoolFactory(appUser, channel));
}
/**
私有的连接池工厂
*/
private static class ConnectionPoolFactory extends BasePooledObjectFactory<FabricConnection> {
private FabricJavaPoolConfig config = new FabricJavaPoolConfig();
private String config_network_path = "";
private User appUser;
private String channel = "";
ConnectionPoolFactory(User appUser, String channel) {
this.config_network_path = config.getConfigNetworkPath();
this.appUser = appUser;
this.channel = channel;
}
//创建fabric客户端连接的方法
@Override
public FabricConnection create() throws Exception {
FabricConnectionImpl myConnection;
CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite();
HFClient hfclient = HFClient.createNewInstance();
hfclient.setCryptoSuite(cryptoSuite);
NetworkConfig networkConfig = NetworkConfig.fromJsonFile(new File(config_network_path));
hfclient.setUserContext(appUser);
hfclient.loadChannelFromConfig(channel, networkConfig);
Channel myChannel = hfclient.getChannel(channel);
myChannel.initialize();
//这一步,创建连接池中连接
myConnection = new FabricConnectionImpl(hfclient, myChannel, appUser);
//这一步很关键,是否使用缓存
if (config.isUseCache()) {
FabricConnectionImplCacheProxy proxy = new FabricConnectionImplCacheProxy(myConnection, config.getCacheURL(), appUser.getName(), channel, config.getCacheTimeout());
return (FabricConnection) Proxy.newProxyInstance(FabricConnectionImpl.class.getClassLoader(), new Class[]{FabricConnection.class}, proxy);
} else {
return myConnection;
}
}
@Override
public PooledObject<FabricConnection> wrap(FabricConnection obj) {
return new DefaultPooledObject<>(obj);
}
}
}
3. 获取Fabric连接
/**
此方法如果队列中没有存在连接对象,那就调用ConnectionPoolFactory 的create方法
*/
FabricConnection myConnection = fabricJavaPool.borrowObject();
4. 查询
ExecuteResult result = myConnection.query("mycc", "query","a");
rs = result.getResult();
5. 回收Fabric连接
fabricJavaPool.returnObject(myConnection);
缓存实现分析
- 缓存实现关键代码
FabricConnectionImplCacheProxy proxy = new FabricConnectionImplCacheProxy(myConnection, config.getCacheURL(), appUser.getName(), channel, config.getCacheTimeout());
return (FabricConnection) Proxy.newProxyInstance(FabricConnectionImpl.class.getClassLoader(), new Class[]{FabricConnection.class}, proxy);
- FabricConnectionImplCacheProxy 类
public class FabricConnectionImplCacheProxy extends FabricContractConnectImplCacheProxy implements InvocationHandler {
public FabricConnectionImplCacheProxy(Object obj, String cacheURL, String userName, String channelName, int timeout) {
super(obj, cacheURL, userName, channelName, timeout);
}
//父类构造方法
public FabricContractConnectImplCacheProxy(Object obj, String cacheURL, String userName, String channelName, int timeout) {
this.timeout = timeout;
this.channelName = channelName;
this.userName = userName;
this.cacheURL = cacheURL;
//使用memcache对查询结果进行缓存
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(AddrUtil.getAddresses(this.cacheURL));
try {
memcachedClient = memcachedClientBuilder.build();
} catch (IOException e) {
e.printStackTrace();
}
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (method.getName().equals("query")) {
//查询时,根据查询方法参数用户通道拼接成唯一key
String key = genericKey(userName, channelName, args);
//先去缓存中查询,查询到结果直接返回
result = memcachedClient.get(key);
if (result != null) {
//System.out.println("hit");
return result;
}
//如果没有查询到结果,调用合约查询
result = method.invoke(obj, args);
ExecuteResult executeResult = (ExecuteResult) result;
if (executeResult.getPropResp() == null) {
return result;
}
//查询出请求参数,将请求参数作为key,将上面生成的key作为value,保存到缓存中
//此步骤的作用是,在invoke是,如果对相同条件的链码和数据key作操作,就会从缓冲中将所有相关数据删除
for (ProposalResponse p : executeResult.getPropResp()) {
TxReadWriteSetInfo txReadWriteSetInfo = p.getChaincodeActionResponseReadWriteSetInfo();
for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : txReadWriteSetInfo.getNsRwsetInfos()) {
KvRwset.KVRWSet rws = nsRwsetInfo.getRwset();
for (KvRwset.KVRead readList : rws.getReadsList()) {
String blockKey = readList.getKey();
memcachedClient.set(blockKey, timeout, key);
}
}
}
//将查询结果保存到缓冲中
memcachedClient.set(key, timeout, result);
return result;
}
if (method.getName().equals("invoke")) {
result = method.invoke(obj, args);
ExecuteResult executeResult = (ExecuteResult) result;
if (executeResult.getPropResp() == null) {
return result;
}
//获取读写集合, 以读参数为key查询缓存,如果缓冲中有值,则删除
for (ProposalResponse p : executeResult.getPropResp()) {
TxReadWriteSetInfo txReadWriteSetInfo = p.getChaincodeActionResponseReadWriteSetInfo();
for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : txReadWriteSetInfo.getNsRwsetInfos()) {
KvRwset.KVRWSet rws = nsRwsetInfo.getRwset();
for (KvRwset.KVRead readList : rws.getReadsList()) {
String blockKey = readList.getKey();
String blockCache = memcachedClient.get(blockKey);
if (!blockCache.equals(null)) {
memcachedClient.delete(blockCache);
memcachedClient.delete(blockKey);
}
}
}
}
return result;
}
result = method.invoke(obj, args);
return result;
}
}
简单总结:
- 如果使用缓存,那么连接池中的连接对象将是动态生成的代理连接对象;
- 在查询前会先查询缓存,如果存在就直接返回结果,如果不存在,再去查询;
- 将查询后的结果存到缓存中,并且将数据关键信息为key,查询结果缓冲key为value保存到缓冲中,以应对数据变动问题;
- 当有数据变动时,会查询缓存中是否有数据,如果有则删除.