文档章节

用Java为Hyperledger Fabric(超级账本)开发区块链链代码智能合约之编写链代码程序

笔阁
 笔阁
发布于 2018/05/24 09:12
字数 4635
阅读 8.4K
收藏 12

编写第一个 Java 链代码程序

在上一节中,您已经熟悉了如何构建、运行、部署和调用链代码,但尚未编写任何 Java 代码。

在本节中,将会使用 Eclipse IDE、一个用于 Eclipse 的 Gradle 插件,以及一个名为 ChaincodeTutorial 的 Java 链代码框架项目,编写第一个 Java 链代码程序。您将从我为此教程创建的 GitHub 存储库中获取框架代码,将该代码导入 Eclipse 中,添加代码来让链代码智慧合同按要求生效,然后在 Eclipse IDE 内使用 Gradle 构建该代码。

您将执行的步骤如下:

  • 安装适用于 Eclipse 的 Gradle Buildship 插件。
  • 从 GitHub 克隆 ChaincodeTutorial 项目。
  • 将该项目导入 Eclipse 中。
  • 探索该链代码框架项目。
  • 编写 Java 链代码。
  • 构建 Java 链代码。 完成本节后,您的链代码就可以在本地区块链网络上运行了。

1.安装适用于 Eclipse 的 Gradle Buildship 插件

您使用自己喜欢的任何 IDE,但本教程中的说明是针对 Eclipse 的。备注:Buildship Gradle 插件有助于将 Gradle 与 Eclipse 集成,但仍然需要将 Gradle 安装在计算机上。

如果您一直在按照教程进行操作,那么您应该已经将 Gradle 安装在计算机上;如果尚未安装它,请立即安装。请参阅 “安装构建软件” 部分,了解如何将 Gradle 安装在计算机上。 Eclipse Marketplace:Gradle Buildship 插件

Buildship Gradle Integration 下,单击 Install 按钮并按照提示进行操作。单击 Finish 后,将安装适用于 Eclipse 的 Buildship Gradle 插件,而且会要求您重启 Eclipse。

重新打开 Eclipse 后,Gradle 应该已经与 Eclipse IDE 全面集成。您现在已准备好从 GItHub 克隆 ChaincodeTutorial 存储库。

从 GitHub 克隆 ChaincodeTutorial 项目

配置 Eclipse IDE 和 Gradle集成后,将从 GitHub 克隆 ChaincodeTutorial 代码并将其导入 Eclipse 中。打开一个命令提示符或终端窗口,导航到 $GOPATH 并执行以下命令:

git clone https://github.com/makotogo/ChaincodeTutorial.git

命令输出应类似于:

$ export GOPATH=/Users/sperry/home/mychaincode
$ cd $GOPATH
$ git clone https://github.com/makotogo/ChaincodeTutorial.git
Cloning into 'ChaincodeTutorial'...
remote: Counting objects: 133, done.
remote: Compressing objects: 100% (90/90), done.
remote: Total 133 (delta 16), reused 118 (delta 1), pack-reused 0
Receiving objects: 100% (133/133), 9.39 MiB | 1.95 MiB/s, done.
Resolving deltas: 100% (16/16), done.
$ cd ChaincodeTutorial
$ pwd
/Users/sperry/home/mychaincode/ChaincodeTutorial

此命令将 Blockchain ChaincodeTutorial 存储库从 GitHub 克隆到 $GOPATH。它包含一个 Java 链代码框架项目,您可以在本地区块链网络中构建、运行和测试它。

但在执行所有这些操作之前,需要将该代码导入 Eclipse 中。

3.将该项目导入 Eclipse 中

在 Eclipse 中,转到 File > Import...> Gradle > Existing Gradle Project。这会打开一个向导对话框(参见图 9)。

Eclipse Import Wizard:Gradle Project

单击 Next。在向导中随后出现的对话框中(参见图 10),浏览到 $GOPATH/ChaincodeTutorial,然后单击 Finish 导入该项目。 Eclipse Import Wizard:Gradle Project(项目的 root 目录)

完成项目导入后,确保选择了 Java Perspective,您刚导入的 ChaincodeTutorial 项目会显示在 Project Explorer 视图中。

将代码导入 Eclipse 工作区后,就可以编写链代码了。

4.探索该链代码框架项目

在本节中,将探索该链代码项目,以便理解在编写任何 Java 代码前它应该如何运行。

作为开发人员,我们喜欢编写代码,所以我不想让您失去编写 Java 代码的机会。但是,项目设置可能很复杂,我不想让这些设置阻碍实现本教程的主要目的。为此,我提供了您所需的大部分代码。

首先让我们快速查看一下基类 AbstractChaincode,它位于 com.makotojava.learn.blockchain.chaincode 包中,如清单 1 所示。

清单 1. AbstractChaincode 类

package com.makotojava.learn.blockchain.chaincode;
 
import java.util.Arrays;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.java.shim.ChaincodeBase;
import org.hyperledger.java.shim.ChaincodeStub;
 
public abstract class AbstractChaincode extends ChaincodeBase {
 
  private static final Log log = LogFactory.getLog(AbstractChaincode.class);
 
  public static final String FUNCTION_INIT = "init";
  public static final String FUNCTION_QUERY = "query";
 
  protected abstract String handleInit(ChaincodeStub stub, String[] args);
  protected abstract String handleQuery(ChaincodeStub stub, String[] args);
  protected abstract String handleOther(ChaincodeStub stub, String function, String[] args);
 
  @Override
  public String run(ChaincodeStub stub, String function, String[] args) {
    String ret;
    log.info("Greetings from run(): function -> " + function + " | args -> " + Arrays.toString(args));
    switch (function) {
    case FUNCTION_INIT:
      ret = handleInit(stub, args);
      break;
    case FUNCTION_QUERY:
      ret = handleQuery(stub, args);
    default:
      ret = handleOther(stub, function, args);
      break;
    }
    return ret;
  }
 
  @Override
  public String query(ChaincodeStub stub, String function, String[] args) {
    return handleQuery(stub, args);
  }
 
}

我想指出的第一点是,AbstractChaincode 是 ChaincodeBase 的子类,后者来自该结构的 shim 客户端(第 7、10 行)。

第 17-19 行显示了需要在 ChaincodeLog 类(AbstractChaincode 的子类)中实现的方法,这些方法分别用于实现初始化、账本查询和日志功能。

第 22-36 行显示了 ChaincodeBase 类(来自链代码 shim 客户端)的 run() 方法,我们可以在其中查看调用了哪个函数,以及该调用应委托给哪个处理函数。该类是可扩展的,因为 init 和 query 以外的其他任何函数(比如 log 函数)都由 handleOther() 处理,所以您还必须实现它。

现在打开 com.makotojava.learn.blockchain.chaincode 包中的 ChaincodeLog 类。

我只提供了一个框架供您填充 — 也就是说,我仅提供了编译它所需的代码。您需要编写剩余代码。您应该执行 JUnit 测试,然后会看到测试失败(因为还未编写实现)和失败的原因。换句话说,可以使用 JUnit 测试作为指导来正确地实现代码。

现在,如果感觉难以理解,不要担心;我在 com.makotojava.learn.blockchain.chaincode.solution 中提供了解决方案,以防您遇到阻碍(或者想根据参考来帮助完成实现)。

编写 Java 链代码

首先介绍一下在 ChaincodeLog 中实现链代码方法需要了解的一些背景。Java 链代码通过 ChaincodeStub 类与 Hyperledger Fabric 框架进行通信,另外需要记住,账本是区块链技术的透明性方面的核心。让智能合约(责任性)发挥其作用的是账本的状态,而链代码是通过 ChaincodeStub 来评估账本的状态。通过访问账本状态,可以实现一个智能合约(也即链代码)。

ChaincodeStub 上有许多方法可用于在账本的当前状态中存储、检索和删除数据项,但本教程仅讨论两个方法,它们用于存储和检索账本状态:

putState(String key, String value)— 将指定的状态值存储在账本中,该值被相应映射到指定的键。

getState()— 获取与指定键关联的状态值,并以字符串形式返回它。

为本教程编写代码时,只需在账本中存储或检索状态值,就会使用 putState() 或 getState() 函数。ChaincodeLog 类仅在账本中存储和检索值来实现其智能合约,所以实现这些方法只需知道该值即可。更复杂的链代码将使用 ChaincodeStub 中的其他一些方法(但这些方法不属于本教程的介绍范畴)。

我非常喜欢测试驱动开发 (TDD),所以按照 TDD 的方式,我首先编写单元测试。继续运行它们,并观察它们的失败过程。在这之后,编写符合规范的代码,直到单元测试得到通过。单元测试的工作是确保能够获得预期的行为,通过研究单元测试,您将获得实现这些方法所需的足够信息。

但是,我还在每个方法顶部编写了 javadoc 注释,这可能有所帮助(以防您不熟悉 TDD 或 JUnit)。在学完本节的内容后,在 JUnit 测试中的代码与框架 ChaincodeLog 中的 javadoc 注释之间,你应该知道有实现链代码所需的所有信息。

从 Project Explorer 视图(在 Java 透视图中),导航到 ChaincodeLogTest 类,右键单击它并选择 Run As > Gradle Test。在它运行时,您会看到如图 11 所示的结果,其中显示了运行的所有 Gradle 任务的树结构。成功完成的任务在旁边会用一个复选标记进行指示。

Eclipse:Gradle Executions 视图

Gradle Executions 选项卡中的感叹号表示与失败的单元测试对应的 Gradle 任务(跟我们期望的一样,所有 4 个单元测试都失败了)。

由于我们编写 JUnit 测试案例的方式,每个测试方法对应于 ChaincodeLog 中的一个方法,您需要在本教程中正确实现它们。

实现 getChaincodeID() 首先,需要实现 getChaincodeID()。它的合约要求返回链代码的唯一标识符。我在 ChaincodeLog 类的顶部定义了一个名为 CHAINCODE_ID 的常量,您会用到它。可以自由更改它的值,但是,如果要更改 getChaincodeID() 返回的链代码 ID,请确保它在您的网络中是唯一的,而且不要忘记更改 JSON 消息的 ChaincodeID.name 属性。

/**
 * Returns the unique chaincode ID for this chaincode program.
 */
@Override
public String getChaincodeID() {
  return null;// ADD YOUR CODE HERE
}

练习:完成 getChaincodeID() 方法。如果需要一个参考,请参见 com.makotojava.learn.blockchain.chaincode.solution 包。

实现 handleInit()

接下来将实现 handleInit() 方法。它的合约要求处理链代码程序的初始化,在本例中,这意味着它将向账本添加一条(由调用方指定的)消息,并在调用成功时将该消息返回给调用方。

/**
 * Handles initializing this chaincode program.
 *
 * Caller expects this method to:
 *
 * 1. Use args[0] as the key for logging.
 * 2. Use args[1] as the log message.
 * 3. Return the logged message.
 */
@Override
protected String handleInit(ChaincodeStub stub, String[] args) {
  return null;// ADD YOUR CODE HERE
}

练习:完成 handieInit() 方法。如果需要一个参考,请参见 com.makotojava.learn.blockchain.chaincode.solution 包。

实现 handleQuery()

接下来将实现 handleQuery() 方法。它的合约要求查询账本,为此,它会获取指定的键,在账本中查询与这个(这些)键匹配的值,然后将该(这些)值返回给调用方。如果指定了多个键,应该使用逗号分隔返回的值。

/**
 * Handles querying the ledger.
 *
 * Caller expects this method to:
 * 
 * 1. Use args[0] as the key for ledger query.
 * 2. Return the ledger value matching the specified key
 *    (which should be the message that was logged using that key).
 */
@Override
protected String handleQuery(ChaincodeStub stub, String[] args) {
  return null;// ADD YOUR CODE HERE
}

确保编写了代码来输出查询调用的结果,以便可以在控制台输出中查看结果(如果想了解我是如何做的,请参阅解决方案)。

练习:完成 handleQuery() 方法。如果需要一个参考,请参见 com.makotojava.learn.blockchain.chaincode.solution 包。

实现 handleOther()

最后需要实现 handleOther() 方法,它的合约要求处理其他消息(这是完全开放的,但正因如此它才是可扩展的)。您将在这里实现 log 函数,它的合同要求将调用方指定的一条消息添加到账本中,并在调用成功时将该消息返回给调用方。这看起来与 init 函数中发生的事非常相似,所以或许您可以在该实现中利用此函数。

/**
 * Handles other methods applied to the ledger.
 * Currently, that functionality is limited to these functions:
 * - log
 *
 * Caller expects this method to:
 * Use args[0] as the key for logging.
 * Use args[1] as the log message.
 * Return the logged message.
 */
@Override
protected String handleOther(ChaincodeStub stub, String function, String[] args) {
  // TODO Auto-generated method stub
  return null;// ADD YOUR CODE HERE
}

练习:完成 handleOther() 方法。如果需要一个参考,请参见 com.makotojava.learn.blockchain.chaincode.solution 包。

如果您为前面的每个练习编写的代码满足本节(以及代码注释中)为它们设定的要求,JUnit 测试应该都能通过,而且将链代码部署在本地区块链网络中并运行时,它们应该能够正常工作。

请记住,如果遇到阻碍,我提供了一个解决方案(但是在查看解决方案之前,您必须自行实现这些方法)。

构建 Java 链代码

现在您已编写 Java 链代码且通过了所有 JUnit 测试,是时候使用 Eclipse 和用于 Eclipse 的 Gradle Buildship 插件构建链代码了。通过转到 Window > Show View > Other... 调出 Gradle Tasks 视图,然后搜索 gradle,选择 Gradle Tasks,并单击 OK。(参见图 12。)

Eclipse:Show View:Gradle Tasks 视图

Gradle Tasks 视图打开后,展开 ChaincodeTutorial > build 节点,选择 buildclean。(参见图 13。)

Eclipse:Gradle Tasks 视图

右键单击 buildclean,然后选择 Run Gradle Tasks(Gradle 将确定运行它们的正确顺序)。您的 Gradle Executions 视图应该显示一个干净的构建版本,如图 14 所示,其中每项的旁边仅有一个复选标记。 Eclipse:Gradle Executions 视图:干净构建

完成构建后,$GOPATH/ChaincodeTutorial 目录(您之前已从 GitHub 将代码克隆到这里)下有一个子目录 build/distributions,它包含您的链代码(这应该看起来很熟悉,因为本教程前面的 hello 示例中已经这么做过)。

构建 Java 链代码后,就可以在本地区块链网络中部署和运行它,并在它之上调用交易。

部署并运行 Java 链代码

在本节中,将会启动并注册您的链代码,部署它,并通过 Hyperledger Fabric REST 接口在链代码之上调用交易,就像本教程前面对 hello 示例所做的一样。确保本地区块链正在运行(如想温习一下相关内容,请参阅 “启动区块链网络” 部分)。

您将执行以下步骤:

  • 注册 Java 链代码。
  • 部署 Java 链代码。
  • 在 Java 链代码上调用交易。

1.注册 Java 链代码

您需要提取 build/distributions/ChaincodeTutorial.zip 文件并运行链代码脚本,就像本教程前面运行 hello 示例时一样(参见 “注册示例” 部分)。

运行 ChaincodeTutorial 脚本时,输出应如下所示:

$ ./ChaincodeTutorial/bin/ChaincodeTutorial
Feb 28, 2017 4:18:16 PM org.hyperledger.java.shim.ChaincodeBase newPeerClientConnection
INFO: Inside newPeerCLientConnection
Feb 28, 2017 4:18:16 PM io.grpc.internal.TransportSet$1 call
INFO: Created transport io.grpc.netty.NettyClientTransport@10bf86d3(/127.0.0.1:7051) for /127.0.0.1:7051
Feb 28, 2017 4:18:21 PM io.grpc.internal.TransportSet$TransportListener transportReady
INFO: Transport io.grpc.netty.NettyClientTransport@10bf86d3(/127.0.0.1:7051) for /127.0.0.1:7051 is ready

现在您的 Java 链代码已向本地区块链网络注册,您已准备好部署和测试链代码了。

2.部署 Java 链代码

就像对 hello 示例链代码执行的操作一样,将会使用该结构的 REST 接口部署 Java 链代码,并在它之上调用交易。

打开 SoapUI。如果愿意的话,可以自行创建一个新 REST 项目和它的所有请求,或者可以导入我包含在之前克隆的 GitHub 项目中的 SoapUI REST 项目。该 SoapUI 项目位于 $GOPATH/ChaincodeTutorial 目录中。

要部署链代码,可以导航到 ChaincodeLog Deploy 请求(如图 15 所示)并提交该请求。

SoapUI:ChaincodeLog Deploy 请求

如果没有使用来自 GitHub 的 SoapUI 项目(或者使用不同的 HTTP 客户端),那么应该提交的 JSON 请求如下所示:

{
"jsonrpc": "2.0",
  "method": "deploy",
  "params": {
    "type": 4,
    "chaincodeID":{
        "name": "ChaincodeLogSmartContract"
    },
    "ctorMsg": {
        "args": ["init", "KEY-1", "Chaincode Initialized"]
    }
  },
  "id": 1
}

提交请求。如果请求被成功处理,您会获得以下 JSON 响应:

{
   "jsonrpc": "2.0",
   "result":    {
      "status": "OK",
      "message": "ChaincodeLogSmartContract"
   },
   "id": 1
}

现在您的链代码已部署并准备好运行。

3.在 Java 链代码上调用交易

部署并初始化 Java 链代码后,就可以在它之上调用交易了。在本节中,将会调用 log 和 query 函数作为交易。

要调用 log 函数,可以打开 ChaincodeLog Log 请求并提交它。(参见图 16。)

SoapUI:ChaincodeLog Log 请求

如果没有使用来自 GitHub 的 SoapUI 项目(或者使用不同的 HTTP 客户端),那么应该提交的 JSON 请求如下所示:

{
"jsonrpc": "2.0",
  "method": "invoke",
  "params": {
    "type": 1,
    "chaincodeID":{
        "name": "ChaincodeLogSmartContract"
    },
    "CtorMsg": {
        "args": ["log", "KEY-2", "This is a log message."]
    }
  },
  "id": 2
}

如果请求被成功处理,您会获得以下 JSON 响应:

{
   "jsonrpc": "2.0",
   "result":    {
      "status": "OK",
      "message": "a6f7a4fc-2980-4d95-9ec2-114dd9d0e4a5"
   },
   "id": 2
}

要调用 query 函数,可以打开 ChaincodeLog Query 请求并提交它。(参见图 17。) SoapUI:ChaincodeLog Query 请求

如果没有使用来自 GitHub 的 SoapUI 项目(或者使用不同的 HTTP 客户端),那么应该提交的 JSON 请求如下所示:

{
"jsonrpc": "2.0",
  "method": "invoke",
  "params": {
    "type": 1,
    "chaincodeID":{
        "name": "ChaincodeLogSmartContract"
    },
    "ctorMsg": {
        "args": ["query", "KEY-1", "KEY-2"]
    }
  },
  "id": 3
}

如果请求被成功处理,您会获得以下 JSON 响应:

{
   "jsonrpc": "2.0",
   "result":    {
      "status": "OK",
      "message": "84cbe0e2-a83e-4edf-9ce9-71ae7289d390"
   },
   "id": 3
}

解决方案代码的终端窗口输出类似于:

$ ./ChaincodeTutorial/bin/ChaincodeTutorial
Feb 28, 2017 4:18:16 PM org.hyperledger.java.shim.ChaincodeBase newPeerClientConnection
INFO: Inside newPeerCLientConnection
Feb 28, 2017 4:18:16 PM io.grpc.internal.TransportSet$1 call
INFO: Created transport io.grpc.netty.NettyClientTransport@10bf86d3(/127.0.0.1:7051) for /127.0.0.1:7051
Feb 28, 2017 4:18:21 PM io.grpc.internal.TransportSet$TransportListener transportReady
INFO: Transport io.grpc.netty.NettyClientTransport@10bf86d3(/127.0.0.1:7051) for /127.0.0.1:7051 is ready
Feb 28, 2017 4:34:52 PM com.makotojava.learn.blockchain.chaincode.AbstractChaincode run
INFO: Greetings from run(): function -> init | args -> [KEY-1, Chaincode Initialized]
Feb 28, 2017 4:34:52 PM com.makotojava.learn.blockchain.chaincode.solution.ChaincodeLog handleLog
INFO: *** Storing log message (K,V) -> (ChaincodeLogSmartContract-CLSC-KEY-1,Chaincode Initialized) ***
Feb 28, 2017 4:50:27 PM com.makotojava.learn.blockchain.chaincode.AbstractChaincode run
INFO: Greetings from run(): function -> log | args -> [KEY-2, This is a log message.]
Feb 28, 2017 4:50:27 PM com.makotojava.learn.blockchain.chaincode.solution.ChaincodeLog handleLog
INFO: *** Storing log message (K,V) -> (ChaincodeLogSmartContract-CLSC-KEY-2,This is a log message.) ***
Feb 28, 2017 5:02:13 PM com.makotojava.learn.blockchain.chaincode.AbstractChaincode run
INFO: Greetings from run(): function -> query | args -> [KEY-1, KEY-2]
Feb 28, 2017 5:02:13 PM com.makotojava.learn.blockchain.chaincode.solution.ChaincodeLog handleQuery
INFO: *** Query: For key 'ChaincodeLogSmartContract-CLSC-KEY-1, value is 'Chaincode Initialized' ***
Feb 28, 2017 5:02:13 PM com.makotojava.learn.blockchain.chaincode.solution.ChaincodeLog handleQuery
INFO: *** Query: For key 'ChaincodeLogSmartContract-CLSC-KEY-2, value is 'This is a log message.' ***

恭喜您!您已向未来迈出了第一步。

鼓励您执行以下操作:修改 ChaincodeTutorial 项目,向它添加方法,更改实现,等等。您也可以自由地编写链代码。祝您好运,编码愉快!

结束语

本教程简要概述了区块链技术和智能合约(实现为链代码程序),以及最新的区块链技术的发展形势。

我们介绍了设置 Java 链代码开发环境的步骤,包括需要安装的软件,如何定义和运行本地区块链网络,以及如何部署来自 GitHub 中的 Hyperledger Fabric 项目的一个 Java 链代码示例程序并在它之上调用交易。

您学习了如何使用 Eclipse、JUnit 和 Gradle 编写和构建第一个 Java 链代码程序,然后部署该 Java 链代码程序并在它之上调用交易。

您亲自查看了区块链技术和智能合约,随着区块链技术发展日渐成熟和市场规模逐渐扩大,您会掌握更多的技巧来编写更复杂的 Java 链代码。

那么您接下来会怎么做?

后续行动

以下建议可帮助您在目前所学知识的基础上继续进行研究:

深入研究 Hyperledger Fabric 架构

致谢

非常感谢杜婧细心评审本文,提供建设性意见并进行校正。

如果你希望高效的学习以太坊DApp开发,可以访问汇智网提供的最热门在线互动教程:

其他更多内容也可以访问这个以太坊博客

作者: J Steven Perry 设置开发环境

© 著作权归作者所有

笔阁
粉丝 225
博文 218
码字总数 492488
作品 0
海淀
程序员
私信 提问
加载中

评论(1)

会飞的鸡翅
会飞的鸡翅
有收获。安利个我写的Fabric区块链开发详解教程:http://t.cn/Eoz7J5B,看是否可以合作合作?
Hyperledger Fabric基础知识

Hyperledger Fabric基础知识 本文我们会介绍Hyperledger Fabric的基础知识,并了解如何充分利用这个多功能的区块链框架。 区块链技术为创新提供了丰富的机会。它提供了一种新的交易方式,从而...

flydean
01/06
0
0
Hyperledger Fabric的容灾备份及恢复方法简介

Hyperledger Fabric日益增强的潜力使得许多企业正在尝试使用fabric。当使用涉及更多peers和orderers的大型网络时,维护超级账本数据的备份非常重要。如果网络出现故障,这将有所帮助。它还有...

geek12345
2019/02/27
45
0
不同区块链智能合约开发语言的选择

在本文中,将介绍比特币、超级账本Fabric和以太坊这三种区块链中,分别使用什么开发语言来进行智能合约的编程,并提供你进一步学习的资源链接。 比特币 比特币是第一个真正意义上的区块链,不...

以太坊教程
2018/05/08
417
1
Fabric-sdk-java最新教程

Fabric Java SDK是Fabric区块链官方提供的用于Java应用开发的SDK,全称为Fabric-sdk-java,网上可用资料不多,本文列出了精心整理的针对Fabric Java SDK的最新精选教程。 如果希望快速掌握F...

汇智网教程
2019/04/23
444
0
Fabric Java SDK最新教程

Fabric Java SDK是Fabric区块链官方提供的用于Java应用开发的SDK,全称为Fabric-sdk-java,网上可用资料不多,本文列出了精心整理的针对Fabric Java SDK的最新精选教程。 如果希望快速掌握F...

geek12345
2019/04/25
646
1

没有更多内容

加载失败,请刷新页面

加载更多

zookeeper实现分布式锁总结,看这一篇足矣(设计模式应用实战)

zk实现分布式锁纵观网络各种各样的帖子层出不穷,笔者查阅很多资料发现一个问题,有些文章只写原理并没有具体实现,有些文章虽然写了实现但是并不全面 借这个周末给大家做一个总结,代码拿来...

osc_75pcgicm
6分钟前
11
0
163邮箱配置imap和smtp,隐藏的设置

http://config.mail.163.com/settings/imap/index.jsp?uid=XXXXX@163.com,这是一个隐藏的设置,要到这里配置才能用163的imap或者pop...

bengozhong
7分钟前
7
0
Python可变对象和不可变对象

Python中一切皆对象,每个对象都有其唯一的id,对应的类型和值,其中id指的是对象在内存中的位置。根据对象的值是否可修改分为可变对象和不可变对象。其中, 不可对象包括:数字,字符串,t...

osc_pnyuctmm
8分钟前
4
0
数据库垂直拆分 水平拆分

1 数据库拆分 当我们使用读写分离、缓存后,数据库的压力还是很大的时候,这就需要使用到数据库拆分了。 数据库拆分简单来说,就是指通过某种特定的条件,按照某个维度,将我们存放在同一个数...

努力的学渣
9分钟前
12
0
微信小程序连接低功率蓝牙控制单片机上硬件设备

1.软件部分介绍   微信小程序是一种新的应用,用户不需要下载应用只用通过扫二维码或者打开链接就能使用,使用完后不需要卸载,直接关闭就行了。微信在2017年初推出微信小程序开发环境。任...

osc_uxgfefy0
10分钟前
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部