go解决c10k problem(二)–db版下单场景

TL;DR

前言

这是一篇去年8月写的文章,本来是希望达到10k, 不过实际测试并没有成功,最终达到了1000+的事务,完成C10K的一个K,还有9个 :(

DB选择

DB 类型 业务 io
Mysql sql,支持事务,有缓存 强sql,强类型,类似java B & B+
Postgres sql,支持事务
Mongo nosql,key-value DB,读很快 弱sql,非强制,类似js B+
Redis nosql just kv,代码复杂
sqlite3 sql 强sql,支持事务 Btree

初步结论,理论上是随便用,先用sqlite3把,如果性能达不到,再来排查原因和更改DB

业务场景

我们设计一个sticker网站,可以售卖sticker,用户可以购买sticker,sticker是有库存的,如果用户的余额足够,则可以售卖,另外售卖的过程中,如果库存没了的话,需要回滚订单。

主要对象

  • 用户
  • 商品,库存字段也在商品身上
  • 订单

主要流程

  • 商品的详情页面,get v1/api/sticker/:id
  • 商品的点赞+1接口,post v1/api/sticker/likes
  • 下单接口,post v1/api/order
  • 订单查看接口,get v1/api/order/:id

表现

get 接口 读

load

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
10154 ubuntu 20 0 1757000 36888 10020 R 362.7 0.1 2:48.41 main 
9046 ubuntu 20 0 2125668 31976 9876 S 346.3 0.1 2:51.78 server

QPS表现

Read more →

go解决c10k problem(一)

零:缘起

最近两个事情

一个是easeprobe,haocl 对其http probe 做了一个10k的benchmark easeprobe benchmark

一个是读到go memory这篇文章 go-memory-ballast

谈到技术,并发是一个绕不过的话题,从第一家公司单机2万qps,到一个50w+ tps的超大型分布式交易系统。

那么自己能否来简单来测试一下呢? 跑一个web server,来个10k 并发,再来个可观测,看下瓶颈到底在哪里? 如何提升? 说干就干。

一:go http server

goroutine的存在,让go在网络编程这块很高效,也有很多优秀的http 框架,比如 gin echo fasthttp

fasthttp 比较快,我们先不读取db和其他,仅让cpu是一个瓶颈,仅写个hello world,收到请求后,返回一行文本。

示例代码

package main

import (
	"fmt"
	"github.com/valyala/fasthttp"
)

// request handler in fasthttp style, i.e. just plain function.
func fastHTTPHandler(ctx *fasthttp.RequestCtx) {
	fmt.Fprintf(ctx, "Hi there! RequestURI is %q", ctx.RequestURI())
}

func main() {
	fasthttp.ListenAndServe(":8087", fastHTTPHandler)
}

二:go http client 压测的client

2.1 压测client选择

  • 简单实现版本,使用time.Sleep 函数,来均匀分布所有的请求到一个server。
  • 复杂实现版本,来一个goroutine的pool,一个http connect的pool,分别控制最大连接数和最大goroutine数目,并且根据请求耗时,动态调整两个pool的大小,然后到一个稳定的状态。
  • 别人现有的,找到一个 go-strees-testing 的项目,可以先用起来

2.2 先选择现有的试试效果

https://github.com/link1st/go-stress-testing

Read more →

学习web3的第三天-感受ether的心跳

学习web3的第三天

前两天主要学习了solidity的语言语法,以及合约的知识,并且部署到一个测试环境。

今天呢,我们来操作线上的环境:),哈哈哈,不是真正的写,仅仅读取一些线上的交易消息,感受下ether的律动。

预备

1 申请一个infura的账号

直接官网申请即可,他是一个web2的服务,提供的api服务,把ether的信息转换成一个api和消息投递出来。你也可以本地启动一个ether的node,来接入node也可以。

infura

主要作用:提供一个proxy,把ether的信息转换成一个api和消息投递出来。

这里需要注册一个账户,并且拿到一个project-id,有足够的的免费额度,供你使用

2 打开etherscan,来进行一个信息的比对和查询,确保后续测试正常

etherscan

3 打开vscode,准备写代码

do by yourself

读ether和监听ether的交易消息

1 先来读取一个ether的最高交易区块

// 获取最高交易区块,使用infura的api
client, err := rpc.Dial("https://mainnet.infura.io/v3/<YOUR-PROJECT-ID>")
if err != nil {
	log.Fatalf("Could not connect to Infura: %v", err)
}

var lastBlock Block
err = client.Call(&lastBlock, "eth_getBlockByNumber", "latest", true)
if err != nil {
	fmt.Println("Cannot get the latest block:", err)
	return
}
// 获取最高区块的高度
fmt.Printf("Latest block: %v\n", lastBlock.Number)

2 监听NewBlock事件

// 获取最高交易区块,使用infura的websocket
ws, err := ethclient.Dial("wss://mainnet.infura.io/ws/v3/<YOUR-PROJECT-ID>")

if err != nil {
	log.Fatal(err)
}

// 设置监听事件
headers := make(chan *types.Header)
sub, err := ws.SubscribeNewHead(context.Background(), headers)
if err != nil {
	log.Fatal(err)
}

// 监听事件
for {
	select {
	case err := <-sub.Err():
		log.Fatal(err)
	case header := <-headers:
		fmt.Println(header.Hash().Hex()) 

		block, err := ws.BlockByHash(context.Background(), header.Hash())
		if err != nil {
			log.Fatal(err)
		}

		fmt.Println(block.Hash().Hex())        
		fmt.Println(block.Number().Uint64())  
		fmt.Println(block.Time())           
		fmt.Println(block.Nonce())            
		fmt.Println(len(block.Transactions()))
	}
}

3 other things

结语

现在有一个go的代码环境,可以来进行web3的交互,交易等,也是操作过线上环境了。来吧,感受下ether的律动。那一笔笔的交易消息,就是这个时代的心跳。

Read more →

学习web3的第二天

学习web3的第二天

今天就学习下写以太坊合约,并且部署到以太坊网络上,再创建一个读合约的go版本的api server。

使用到的技术如下

  • go
  • ethereum
  • solidity
  • ganache-cli

预备:软件安装

1 安装golang

brew install golang

2 安装ethereum

follow official site geth

主要作用,提供一些命令行,如 apigen ,生成合约的go代码

3 安装solidity

follow official site soliditylang

主要作用,提供solidity 的命令行,如 solc

4 安装ganache-cli

brew install ganache-cli

主要作用,提供一个本地的节点,用来测试合约的功能

写合约主要源代码-solidity 代码

1 写合约的solidity代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

contract MySmartContract {
    function Hello() public view returns (string memory) {
        return "Hello World";
    }
    function Greet(string memory str) public view returns (string memory) {
        return str;
    }
}

解释:首先是一个无参数的方法,Hello,一个是有参数的方法,Greet。

2 根据合约生成go代码

solc --optimize --abi ./contracts/MySmartContract.sol -o build
solc --optimize --bin ./contracts/MySmartContract.sol -o build
abigen --bin=./build/MySmartContract.bin --abi=./build/MySmartContract.abi --pkg=api --out=./api/MySmartContract.go

解释:solc 命令,将solidity代码转换成二进制文件,并且生成abi文件,apigen 生成go相关的代码,并且生成到api目录下。供后续deploy和client使用

Read more →

学习web3的第一天

学习web3的第一天

闲着无事,虽然自己对web3热情满满,但实际并没有去参与,仅仅是买点币作为一个对行业的贡献。

前几天看到一个twitter话题,说一周学会web3开发,来吧,说干就干,开始学习。

此篇文章仅做记录使用,记录自己的收获和学习过程。

学习过程

1 打开google docs,看了下描述

一周学会 Web 3 开发 - 建议收集

从中发觉到几个网站,开始进行遍历和递归查找。

2 先遍历solidity极简入门

  • 看了solidity的变量、函数、类等概念,在remix操作一番,结束,结果发现了一个uint256,这个感觉是从go中来的,翻看起来go-ethereum的代码,发现了一个uint256类型。

发现新的点

3 uint256 类型

一看这个就是大数,go中最大是uint64, 256的话,就是四个uint64,是一个slice。打开uint256的github,一看才100多个star,这个是一个经典的类型,可以看出实际没有人关心这个类型,只关心它的操作。没有业务上的意义,不好展开。

github首页重点讲了他和标准库的对比,就是比标准库的bigint 强悍多少多少。自己没跑过benchmark,怀着好奇,用github的codespace打开一个实例,然后跑了下benchmark,哇,比bigint快好多。

4 go-ethereum 的依赖库分析

本来是去找下uint256在go-ethereum中的依赖和使用的,意外打开了go.mod,所以顺藤摸瓜的看了看go-ethereum的依赖库,发现了很多有名的go library。

首先是依赖的云服务,可以看出是aws和azure,cloudflare 具体要看怎么用的

数据结构,用到了lru

数据传输

id生成器

数据库

yml解析

加解密

消息

btc

测试

5 再遍历下 web3-from-zero

又跑题了,首先看到一个前端学习网站

Read more →

go面试算法系列记录

面试

最近从阿里离职,开始准备面试,刷了一个月左右的leetcode,然后实践出真知,去面试了几家公司。

shopee 算法一面 螺旋矩阵,反向螺旋矩阵

描述要求

给定一个n x n的,打印出一个螺旋矩阵,比如:

输入n= 3,

输出,则打印

9 8 7 2 1 6 3 4 5

思路和解决

其实考察的是对数组的理解,还有矩阵的理解,就是找到矩阵的上下左右边界,遍历即可。反向的话,无非是从高到低遍历,正向的话,是从低到高遍历。

代码

func main() {
    matrix := generateMatrix(3)
    printMatrix(matrix)
}

func printMatrix(matrix [][]int) {
    if matrix == nil || len(matrix) == 0 {
        return
    }
    n := len(matrix)
    for i := 0; i < n; i++ {
        for j := 0; j < n; j++ {
            fmt.Printf("%d ", matrix[i][j])
        }
        fmt.Println()
    }
}

func generateMatrix(n int) [][]int {
	upper_bound, left_bound := 0, 0
	lower_bound, right_bound := n-1, n-1
    // 反向的话,number是最大值
	number := n*n
	var matrix = make([][]int, n)
	for k := range matrix {
		matrix[k] = make([]int, n)
	}
	for number > 0 {
		if upper_bound <= lower_bound {
			for j := left_bound; j <= right_bound; j++ {
				number--
				matrix[upper_bound][j] = number
			}
			upper_bound++
		}
		if left_bound <= right_bound {
			for j := upper_bound; j <= lower_bound; j++ {
				number00
				matrix[j][right_bound] = number
			}
			right_bound--
		}
		if upper_bound <= lower_bound {
			for j := right_bound; j >= left_bound; j-- {
				number--
				matrix[lower_bound][j] = number
			}
			lower_bound--
		}
		if left_bound <= right_bound {
			for j := lower_bound; j >= upper_bound; j-- {
				number--
				matrix[j][left_bound] = number
			}
			left_bound++
		}

	}
	return matrix

}

shopee 算法二面 手写LRU

描述

LRU,即Least Recently Used,最近最少使用,是一种缓存算法,它的主要目的是为了防止缓存中的数据过期,过期后就不能再使用。

Read more →

golang爬虫之无名小说网站

Read more →