通知设置 新通知
C++ STL的go语言版本,欢迎各位大佬完善
stirlingx 发表了文章 • 0 个评论 • 330 次浏览 • 2019-11-20 11:21
项目地址: https://github.com/liyue201/gostl
目前已实现的功能
- data structure
- algorithm
gout: 流式http client v0.0.3版本发布
guonaihong 发表了文章 • 0 个评论 • 239 次浏览 • 2019-11-18 09:31
Excelize 发布 2.0.2 版本, Go 语言 Excel 文档基础库
xuri 发表了文章 • 0 个评论 • 402 次浏览 • 2019-10-10 14:32
Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office Open XML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。入选 2018 开源中国码云 Gitee 最有价值开源项目 GVP,目前已成为 Go 语言最受欢迎的 Excel 文档基础库。
开源代码
GitHub: github.com/xuri/excelize
Gitee: gitee.com/xurime/excelize
中文文档: xuri.me/excelize/zh-hans
Excelize 知名用户
2019年10月9日,社区正式发布了 2.0.2 版本,该版本包含了多项新增功能、错误修复和兼容性提升优化。下面是有关该版本更新内容的摘要,完整的更改列表可查看 change log。
有关更改的摘要,请参阅 Release Notes。完整的更改列表可查看 change log。
Release Notes
此版本中最显著的变化包括:
兼容性提示
升级至该版本需要您的 Go 语言版本高于 1.10。
新增功能
- 支持创建数据透视表。新增
AddPivotTable()
函数 - 支持创建迷你图 (Sparkine)。 新增
AddSparkline()
函数 - 新增
GroupSheets()
和UngroupSheets()
方法,支持设置工作表分组和取消工作表分组 - 新增
AddVBAProject()
方法,支持向 Excel 文档中嵌入包含函数和宏的 VBA 工程 - 函数
SetPageLayout()
增加适应页面宽高属性支持,相关 issue #432 - 函数
SetSheetViewOptions()
现在支持 “值为零的单元格是否显示零值” 属性的设置 - 函数
SetCellFormula()
增加了对公式类型和引用属性的设置支持 - 增加带有删除线字体样式的创建支持,相关 issue #482
问题修复
- 修复部分情况下读取批注内容文本不完整的问题,解决 issue #434
- 修复由于内部合并单元格偏移量计算错误导致的部分情况下使用
RemoveRow()
删除行出现下标越界问题,解决 issue #437 - 修复部分情况下数据验证下拉菜单中的公式失效问题
- 修复在循环迭代中调用
Save()
方法保存导致的文档损坏问题,解决 issue #443 - 提升文档内部
workbook.xml.rels
中相对路径格式解析的兼容性,解决 issue #442 - 修复部分情况下,删除带有合并单元格的文档所导致的文件损坏问题
- 修复部分情况下设置保护工作表属性失效的情况,解决 issue #454
- 修复部分情况下
GetSheetName
获取工作表名称为空的问题, 解决 issue #457 - 增加单元格内多行文本解析的支持, 相关 issue #464
- 修复 32 位操作系统环境下数字溢出问题,相关 issue #386
- 修复 go module 依赖版本不匹配问题, 相关 issue #466 和 issue #480
- 修复部分情况下调用
SetSheetPrOptions()
所致的文档损坏问题,解决 issue #483
性能表现
- 性能优化,减少读取文档时的内存开销和耗时,相关 issue #439
其他
- 完善
SetSheetRow()
函数中的异常处理 - 代码精简优化, 合并了下列内部函数:
将函数 workBookRelsWriter
, drawingRelsWriter
合并为 relsWriter
;
将函数 drawingRelsReader
, workbookRelsReader
, workSheetRelsReader
合并为 relsReader
;
将函数 addDrawingRelationships
, addSheetRelationships
合并为 addRels
[gev] 一个轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
惜朝 发表了文章 • 0 个评论 • 387 次浏览 • 2019-09-25 16:45
`gev` 是一个轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库。
➡️➡️ https://github.com/Allenxuxu/gev
特点
- 基于 epoll 和 kqueue 实现的高性能事件循环
- 支持多核多线程
- 动态扩容 Ring Buffer 实现的读写缓冲区
- 异步读写
- SO_REUSEPORT 端口重用支持
网络模型
`gev` 只使用极少的 goroutine, 一个 goroutine 负责监听客户端连接,其他 goroutine (work 协程)负责处理已连接客户端的读写事件,work 协程数量可以配置,默认与运行主机 CPU 数量相同。
性能测试
> 测试环境 Ubuntu18.04 | 4 Virtual CPUs | 4.0 GiB
吞吐量测试
限制 GOMAXPROCS=1(单线程),1 个 work 协程
限制 GOMAXPROCS=4,4 个 work 协程
其他测试
速度测试
和同类库的简单性能比较, 压测方式与 evio 项目相同。
- gnet
- eviop
- evio
- net (标准库)
限制 GOMAXPROCS=1,1 个 work 协程
限制 GOMAXPROCS=1,4 个 work 协程
限制 GOMAXPROCS=4,4 个 work 协程
安装 gev
```bash
go get -u github.com/Allenxuxu/gev
```
快速入门
```go
package main
import (
"log"
"github.com/Allenxuxu/gev"
"github.com/Allenxuxu/gev/connection"
"github.com/Allenxuxu/ringbuffer"
)
type example struct{}
func (s *example) OnConnect(c *connection.Connection) {
log.Println(" OnConnect : ", c.PeerAddr())
}
func (s *example) OnMessage(c *connection.Connection, buffer *ringbuffer.RingBuffer) (out []byte) {
log.Println("OnMessage")
first, end := buffer.PeekAll()
out = first
if len(end) > 0 {
out = append(out, end...)
}
buffer.RetrieveAll()
return
}
func (s *example) OnClose(c *connection.Connection) {
log.Println("OnClose")
}
func main() {
handler := new(example)
s, err := gev.NewServer(handler,
gev.Address(":1833"),
gev.NumLoops(2),
gev.ReusePort(true))
if err != nil {
panic(err)
}
s.Start()
}
```
Handler 是一个接口,我们的程序必须实现它。
```go
type Handler interface {
OnConnect(c *connection.Connection)
OnMessage(c *connection.Connection, buffer *ringbuffer.RingBuffer) []byte
OnClose(c *connection.Connection)
}
func NewServer(handler Handler, opts ...Option) (server *Server, err error) {
```
在消息到来时,gev 会回调 OnMessage ,在这个函数中可以通过返回一个切片来发送数据给客户端。
```go
func (s *example) OnMessage(c *connection.Connection, buffer *ringbuffer.RingBuffer) (out []byte)
```
Connection 还提供 Send 方法来发送数据。Send 并不会立刻发送数据,而是先添加到 event loop 的任务队列中,然后唤醒 event loop 去发送。
更详细的使用方式可以参考示例:[服务端定时推送]
```go
func (c *Connection) Send(buffer []byte) error
```
Connection ShutdownWrite 会关闭写端,从而断开连接。
更详细的使用方式可以参考示例:[限制最大连接数]
```go
func (c *Connection) ShutdownWrite() error
```
➡️➡️ https://github.com/Allenxuxu/gev
花了半天用go写一个带ui的redis客户端
ownGolang 回复了问题 • 6 人关注 • 5 个回复 • 1468 次浏览 • 2019-09-18 10:17
go + koa = ? 一个新的web框架 goa 诞生
nicholascao 发表了文章 • 1 个评论 • 517 次浏览 • 2019-08-08 15:52
相信绝大部分使用nodejs的开发者都知道[koa](https://koa.bootcss.com/),甚至每天都在跟koa打交道。
## goa
最近因工作需要从nodejs转到go,因此开发了一个koa for golang的web框架--goa。
几乎一样的语法,一样基于中间件。
github地址:[goa](https://github.com/goa-go/goa)
demo:
``` golang
package main
import (
"fmt"
"time"
"github.com/goa-go/goa"
"github.com/goa-go/goa/router"
)
func logger(c *goa.Context, next func()) {
start := time.Now()
fmt.Printf("[%s] <-- %s %s\n", start.Format("2006-6-2 15:04:05"), c.Method, c.URL)
next()
fmt.Printf("[%s] --> %s %s %d%s\n", time.Now().Format("2006-6-2 15:04:05"), c.Method, c.URL, time.Since(start).Nanoseconds()/1e6, "ms")
}
func json(c *goa.Context) {
c.JSON(goa.M{
"string": "string",
"int": 1,
"json": goa.M{
"key": "value",
},
})
}
func main() {
app := goa.New()
router := router.New()
router.GET("/", func(c *goa.Context) {
c.String("hello world")
})
router.GET("/json", json)
app.Use(logger)
app.Use(router.Routes())
app.Listen(":3000")
}
```
如果觉得这个项目不错的话,请给个star给予作者鼓励,
另外欢迎fork和加入开发团队共建。
再次贴上地址https://github.com/goa-go/goa
golang + qt5 开发的百度网盘不限速客户端
peterq 发表了文章 • 1 个评论 • 585 次浏览 • 2019-07-01 15:55
官网: https://github.com/peterq/pan-light
github: https://github.com/peterq/pan-light
基于Golang的轻量级并发服务器框架---Zinx
Aceld 发表了文章 • 0 个评论 • 659 次浏览 • 2019-04-18 11:48
[](LICENSE) [](https://gitter.im/zinx_go/community) [](https://www.jianshu.com/p/23d07c0a28e5) [](https://legacy.gitbook.com/book/aceld/zinx/details)
Zinx 是一个基于Golang的轻量级并发服务器框架
###源码地址:
[https://github.com/aceld/zinx](https://github.com/aceld/zinx)
### 开发人员
- 刘丹冰([@aceld](https://github.com/aceld))
- 张超([@zhngcho](https://github.com/zhngcho))
## 一、写在前面
我们为什么要做Zinx,Golang目前在服务器的应用框架很多,但是应用在游戏领域或者其他长链接的领域的轻量级企业框架甚少。
设计Zinx的目的是我们可以通过Zinx框架来了解基于Golang编写一个TCP服务器的整体轮廓,让更多的Golang爱好者能深入浅出的去学习和认识这个领域。
Zinx框架的项目制作采用编码和学习教程同步进行,将开发的全部递进和迭代思维带入教程中,而不是一下子给大家一个非常完整的框架去学习,让很多人一头雾水,不知道该如何学起。
教程会一个版本一个版本迭代,每个版本的添加功能都是微小的,让一个服务框架小白,循序渐进的曲线方式了解服务器框架的领域。
当然,最后希望Zinx会有更多的人加入,给我们提出宝贵的意见,让Zinx成为真正的解决企业的服务器框架!在此感谢您的关注!
## 二、初探Zinx架构

## 三、Zinx详细教程(代码教程同步更新)
[《Zinx框架教程-基于Golang的轻量级并发服务器》](https://www.jianshu.com/p/23d07c0a28e5)
## 四、Zinx开发API文档
### 快速开始
#### server
基于Zinx框架开发的服务器应用,主函数步骤比较精简,最多主需要3步即可。
1. 创建server句柄
2. 配置自定义路由及业务
3. 启动服务
```go
func main() {
//1 创建一个server句柄
s := znet.NewServer()
//2 配置路由
s.AddRouter(0, &PingRouter{})
//3 开启服务
s.Serve()
}
```
其中自定义路由及业务配置方式如下:
```go
import (
"fmt"
"zinx/ziface"
"zinx/znet"
)
//ping test 自定义路由
type PingRouter struct {
znet.BaseRouter
}
//Ping Handle
func (this *PingRouter) Handle(request ziface.IRequest) {
//先读取客户端的数据
fmt.Println("recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
//再回写ping...ping...ping
err := request.GetConnection().SendBuffMsg(0, []byte("ping...ping...ping"))
if err != nil {
fmt.Println(err)
}
}
```
#### client
Zinx的消息处理采用,`[MsgLength]|[MsgID]|[Data]`的封包格式
```go
package main
import (
"fmt"
"io"
"net"
"time"
"zinx/znet"
)
/*
模拟客户端
*/
func main() {
fmt.Println("Client Test ... start")
//3秒之后发起测试请求,给服务端开启服务的机会
time.Sleep(3 * time.Second)
conn,err := net.Dial("tcp", "127.0.0.1:7777")
if err != nil {
fmt.Println("client start err, exit!")
return
}
for n := 3; n >= 0; n-- {
//发封包message消息
dp := znet.NewDataPack()
msg, _ := dp.Pack(znet.NewMsgPackage(0,[]byte("Zinx Client Test Message")))
_, err := conn.Write(msg)
if err !=nil {
fmt.Println("write error err ", err)
return
}
//先读出流中的head部分
headData := make([]byte, dp.GetHeadLen())
_, err = io.ReadFull(conn, headData) //ReadFull 会把msg填充满为止
if err != nil {
fmt.Println("read head error")
break
}
//将headData字节流 拆包到msg中
msgHead, err := dp.Unpack(headData)
if err != nil {
fmt.Println("server unpack err:", err)
return
}
if msgHead.GetDataLen() > 0 {
//msg 是有data数据的,需要再次读取data数据
msg := msgHead.(*znet.Message)
msg.Data = make([]byte, msg.GetDataLen())
//根据dataLen从io中读取字节流
_, err := io.ReadFull(conn, msg.Data)
if err != nil {
fmt.Println("server unpack data err:", err)
return
}
fmt.Println("==> Recv Msg: ID=", msg.Id, ", len=", msg.DataLen, ", data=", string(msg.Data))
}
time.Sleep(1*time.Second)
}
}
```
### Zinx配置文件
```json
{
"Name":"Zinx Game",
"Host":"0.0.0.0",
"TcpPort":8999,
"MaxConn":3000,
"WorkerPoolSize":10
}
```
`Name`:服务器应用名称
`Host`:服务器IP
`TcpPort`:服务器监听端口
`MaxConn`:允许的客户端链接最大数量
`WorkerPoolSize`:工作任务池最大工作Goroutine数量
### I.服务器模块Server
```go
func NewServer () ziface.IServer
```
创建一个Zinx服务器句柄,该句柄作为当前服务器应用程序的主枢纽,包括如下功能:
#### 1)开启服务
```go
func (s *Server) Start()
```
#### 2)停止服务
```go
func (s *Server) Stop()
```
#### 3)运行服务
```go
func (s *Server) Serve()
```
#### 4)注册路由
```go
func (s *Server) AddRouter (msgId uint32, router ziface.IRouter)
```
#### 5)注册链接创建Hook函数
```go
func (s *Server) SetOnConnStart(hookFunc func (ziface.IConnection))
```
#### 6)注册链接销毁Hook函数
```go
func (s *Server) SetOnConnStop(hookFunc func (ziface.IConnection))
```
### II.路由模块
```go
//实现router时,先嵌入这个基类,然后根据需要对这个基类的方法进行重写
type BaseRouter struct {}
//这里之所以BaseRouter的方法都为空,
// 是因为有的Router不希望有PreHandle或PostHandle
// 所以Router全部继承BaseRouter的好处是,不需要实现PreHandle和PostHandle也可以实例化
func (br *BaseRouter)PreHandle(req ziface.IRequest){}
func (br *BaseRouter)Handle(req ziface.IRequest){}
func (br *BaseRouter)PostHandle(req ziface.IRequest){}
```
### III.链接模块
#### 1)获取原始的socket TCPConn
```go
func (c *Connection) GetTCPConnection() *net.TCPConn
```
#### 2)获取链接ID
```go
func (c *Connection) GetConnID() uint32
```
#### 3)获取远程客户端地址信息
```go
func (c *Connection) RemoteAddr() net.Addr
```
#### 4)发送消息
```go
func (c *Connection) SendMsg(msgId uint32, data []byte) error
func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error
```
#### 5)链接属性
```go
//设置链接属性
func (c *Connection) SetProperty(key string, value interface{})
//获取链接属性
func (c *Connection) GetProperty(key string) (interface{}, error)
//移除链接属性
func (c *Connection) RemoveProperty(key string)
```
---
### 关于作者:
作者:`Aceld(刘丹冰)`
简书号:`IT无崖子`
`mail`:
[danbing.at@gmail.com](mailto:danbing.at@gmail.com)
`github`:
[https://github.com/aceld](https://github.com/aceld)
`原创书籍gitbook`:
[http://legacy.gitbook.com/@aceld](http://legacy.gitbook.com/@aceld)
go mod包管理+docker容器化必看练手项目,真香~
erik 发表了文章 • 1 个评论 • 785 次浏览 • 2019-01-11 21:10

> 本项目见:[market_monitor](https://github.com/ErikJiang/market_monitor) , 喜欢的话,请加 star 或 fork ~
这是一个初级 Gopher 练手的小项目,该项目功能简单,主要实现监测币市行情变化、达到预警效果的功能,大致的使用场景如下:
> 1. 用户登录服务;
> 2. 用户设置关注的币种及预警的走势价格;
> 3. 当行情变化触发到用户的预警设置时,服务将自动发送提醒邮件通知用户;
ok, 就是酱紫,简明的功能,通过这个小项目你能学到:
* [基于 Go Mod 的项目包管理](https://github.com/JiangInk/market_monitor/wiki/GoModTutorials)
* [Gin 框架的基本开发](https://github.com/JiangInk/market_monitor/wiki/GoGinTutorials)
* [Gin 服务跨域问题的处理](https://github.com/JiangInk/market_monitor/wiki/GinCORSTutorials)
* [基于 JWT 注册、登录等验证流程](https://github.com/JiangInk/market_monitor/wiki/JwtTutorials)
* [数据库 MySQL ORM 的基本使用](https://github.com/JiangInk/market_monitor/wiki/GORMTutorials)
* [缓存数据库 Redis 的基本使用](https://github.com/JiangInk/market_monitor/wiki/RedisTutorials)
* [为项目工程添加配置文件](https://github.com/JiangInk/market_monitor/wiki/ConfigTutorials)
* [在项目中添加日志](https://github.com/JiangInk/market_monitor/wiki/LoggerTutorials)
* [基于 Cron 的 Scheduler 定时任务](https://github.com/JiangInk/market_monitor/wiki/CronTutorials)
* [如何为编写的 API 添加 Swagger 接口文档](https://github.com/JiangInk/market_monitor/wiki/GinSwagTutorials)
* [如何使应用服务平滑重启](https://github.com/JiangInk/market_monitor/wiki/LiveReloadTutorials)
* [构建应用服务 docker 镜像](https://github.com/JiangInk/market_monitor/wiki/DockerBuildTutorials)
* [Docker Compose 容器编排部署](https://github.com/JiangInk/market_monitor/wiki/ComposeDeployTutorials)
功能点:
1. 常规的用户注册、登录、登出、身份验证等功能
2. 监测市场源选择(用于选择支持监测的交易所,开始仅支持监测 gate.io )
3. 监测策略设置管理(用于设置监测的具体要求,开始仅支持走势大小值预警)
4. 监测信息通知(在监测策略被触发后,用于通知用户,开始仅支持邮件通知)
本项目用到的依赖:
* web framework: [gin](https://github.com/gin-gonic/gin)
* redis: [redigo](https://github.com/gomodule/redigo)
* mysql: [gorm](https://github.com/jinzhu/gorm)
* logger: [zerolog](https://github.com/rs/zerolog)
* scheduler: [cron](https://github.com/robfig/cron)
* config: [viper](https://github.com/spf13/viper)
* json web token: [jwt-go](https://github.com/dgrijalva/jwt-go)
* swagger docs: [swaggo](https://github.com/swaggo/gin-swagger)
tendermint区块链五分钟入门
ezpod 发表了文章 • 0 个评论 • 355 次浏览 • 2019-01-09 14:45
> 如果希望快速掌握基于Tendermint的区块链开发,推荐汇智网的在线互动课程:[Tendermint区块链开发详解](http://xc.hubwiz.com/course/5bdec63ac02e6b6a59171df3?affid=govip7878),技术问题可咨询课程助教。
## Tendermint简介
Tendermint萌芽于比特币、以太坊这样的加密货币,它的目标是提供一个比比特币的工作量证明(PoW)更加高效和安全的共识算法。简单地说,Tendermint是一个可供二次开发的软件包,可以在多台机器上安全、一致地实现应用状态的复制。
- Tendermint可以在不超过1/3的机器失效时依然正常工作,无论失效的原因
是什么。Tendermint实现了拜占庭容错。
- 任何正常工作的机器都会收到相同的交易日志,并分别推导出相同的状态
Tendermint的特性如下图所示:

Tendermint包含两个主要的组件:
- 区块链共识引擎,即:Tendermint内核
- 应用与区块链接口,即:**A**pplication **B**lock**C**hain **I**nterface
Tendermint内核可以托管任意的应用状态,因此可以使用任何语言开发区块链软件:Haskell、GoLang、或者Rust都可以用来开发ABCI应用。
其他区块链的一个问题是,它们都是单体设计思维的软件。以比特币为例,比特币的设计就是单体的,其区块链技术栈都包含在单一程序里,需要处理从P2P链接到交易广播、达成共识乃至检查账户余额的一切事情。
单体应用通常不容易扩展、升级或再利用,而Tendermint则致力于将区块链技术栈的两个核心组件与其他部分解耦:共识引擎和P2P连接 —— 事实上这也是开发区块链的最困难的两个技术环节 —— 从而可以使用任何开发语言来开发ABCI应用。
废话不多说了,让我们撸起袖子开干!
## Tendermint开发环境搭建与测试
### STEP 1:下载Tendermint内核
tendermint内核采用Go开发,有官方预编译程序,下载地址:[Tendermint Core](https://github.com/tendermint/tendermint/releases)。
下载后直接解压,并将tendermint程序目录添加到环境变量PATH的设置里。
### STEP 2:初始化Tendermint
执行如下命令初始化Tendermint:
```
~$ tendermint init
```
应当可以在终端看到tendermint的输出信息:
```
I[10–18|20:14:08.996] Generated private validator module=main path=/Users/niharikasingh/.tendermint/config/priv_validator.json
I[10–18|20:14:08.996] Generated node key module=main path=/Users/niharikasingh/.tendermint/config/node_key.json
I[10–18|20:14:08.996] Generated genesis file module=main path=/Users/niharikasingh/.tendermint/config/genesis.json
```
### STEP 3:启动Tendermint节点
使用node子命令启动Tendermint节点:
```
~$ tendermint node -proxy_app=kvstore
```
`-proxy_app`运行标志用来指定一个内置的ABCI应用,例如kvstore是tendermint程序内置的键值对状态机。你应该可以看到如下的tendermint程序输出:
```
I[10–18|20:16:40.037] Starting multiAppConn module=proxy impl=multiAppConn
...
I[10–18|20:16:42.051] enterPropose: Our turn to propose module=consensus height=2 round=0 proposer=601302EBD1F8B4BCE9F99B219965F2796AB6BB10 privValidator=”PrivValidator{601302EBD1F8B4BCE9F99B219965F2796AB6BB10 LH:1, LR:0, LS:3}”
I[10–18|20:16:42.055] Signed proposal module=consensus height=2 round=0 proposal=”Proposal{2/0 1:48B45F4423A5 (-1,:0:000000000000) F52DF1F111D8 @ 2018–10–18T14:46:42.051967933Z}”
I[10–18|20:16:42.056] Received proposal module=consensus proposal=”Proposal{2/0 1:48B45F4423A5 (-1,
```
### STEP 4:提交交易
要提交一个交易,可以使用curl向Tendermint节点的RPC服务发出请求,例如:
```
~$ curl http://localhost:26657/broadcast_tx_commit?tx=\"niharika\"
```
响应结果如下:
```
{
"jsonrpc":"2.0",
"id": "",
"result": {
"check_tx": {
"gasWanted": "1"
},
"deliver_tx": {
"tags": [
{
"key": "YXBwLmNyZWF0b3I=",
"value": "amFl"
},
{
"key": "YXBwLmtleQ==",
"value": "bmloYXJpa2E="
}
]
},
"hash": "EAAD936D3EDCCCF5DD214E02BB4065E5511CA5AC",
"height": "3533"
}
}
```
注意结果中的value字段,例如`bmloYXJpa2E`,这其实是字符串`niharika`的base64编码。
现在让我们查询一下:
```
~$ curl -s 'localhost:26657/abci_query?data="niharika"'
```
响应结果如下:
```
{
"jsonrpc":"2.0",
"id": "",
"result": {
"response": {
"log": "exists",
"index": "-1",
"key": "bmloYXJpa2E=",
"value": "bmloYXJpa2E="
}
}
}
```
很好,看起来我们的Tendermint内核与ABCI接口的工作一切正常!
在本文中,我们成功安装并启动了tendermint内核,然后通过节点旳ABCI接口提交了一个交易来更新内置键值库应用的状态,最后通过ABCI接口查询了ABCI应用的状态。这就是基于Tendermint进行应用开发的核心模型:可以使用任何开发语言来代替curl完成这些操作,实现自己的ABCI应用!
---
原文链接:[自主可控区块链神器Tendermint五分钟入门 - 汇智网](http://blog.hubwiz.com/2019/01/08/tendermint-101/)