软路由系列–Others

Others

还有一些其他的点,顺带记录下

Samba

安装软件后,配置目录,添加smb用户和密码

apt install samba
vim /etc/samba/smb.conf

[global]
  workgroup = SAMBA
  security = user
  passdb backend = tdbsam
  printing = cups 
  printcap name = cups
  load printers = yes
  cups options = raw

[other]
  comment = other
  path = /data/other
  browseable = yes
  writable = yes
  public = yes
  write list = +smbuser1  
smbpasswd -a smbuser1  # 添加用户,设置密码,这个密码是samba的密码

把Sonarr的docker IP添加到Transmission 访问白名单

Transmission我们是docker安装的,所以白名单的IP是docker的IP,我们使用以下命令获取到sonarr的IP

docker network inspect sonarr_default

假设得到的是172.18.0.1,那么我们在transmission的配置文件中添加以下内容即可,这样的话,sonarr就可以访问transmission了

Read more →

软路由系列–Docker

Read more →

软路由系列–续篇–代理和DNS设置

Read more →

软路由系列–路由器系统-Ubuntu

软路由的系统

软路由系统安装 篇1也是写于三年前左右了

自己的软路由系统一直在用linux发行版,第一次使用软路由是2018年了,当时使用的是centos7,还是小马的软路由n3700,当时自己加配了8G内存,也就是4C8G,固态硬盘120,而后这个固态也在今年挂掉了,4月份还重装了下系统,由于配置还在,所以当时继续沿用了centos7

不过呢,最近刚好入手了一个零刻的小主机,i3-N305, 8核8线程,1.8GHz->3.8GHz, 16G,512G。最近也是想换个系统了,因为centos7的软件源太老了,带的内核还是3.10的,官方也没人维护了,索性切换到Ubuntu 22.04.02了,内核5.15(晚些换成6)

Ubuntu 22.04.02

我选择的是server版本,安装的时候,根本不会跳出来GUI,全部命令行,超级nice,而且很贴心的自动把SSH远程访问勾选了,只要安装好,就可以使用远程登陆了,也不需要自己开启了。

开启软路由功能

apt install bridge-utils

编辑 netplan yaml文件,这台主机只有两个网口,所以搞个bridge,而后一个网口当做wan口,一个网口当做lan口,把lan口加入到bridge中即可

vim /etc/netplan/00-installer-config.yaml
network:
  ethernets:
    enp1s0:
      dhcp4: true
    enp2s0:
      dhcp4: no
  bridges:
    br0:
      dhcp4: no
      addresses: [10.0.1.1/24]
      nameservers:
        addresses: [8.8.8.8]
      interfaces: [enp2s0]
  version: 2

应用配置,并且查看信息

netplan apply

brctl show

类似以下就成功了,其中enp2s0 就是lan口,你下面挂其他wifi放大器,或者交换机等等

bridge name	bridge id		STP enabled	interfaces
br0		8000.6aa01d4c1f71	no		enp2s0

开启dhcp服务,给其他设备分配ip或者提供dns服务

 apt install dnsmasq

编辑配置文件, 设置dhcp的网段,dns,dns解析文件,端口

vim /etc/dnsmasq.conf

interface=br0
dhcp-range=10.0.1.2,10.0.1.254,72h
dhcp-option=6,10.0.1.1
resolv-file=/etc/resolv.dnsmasq.conf
port=1053 # 这里我改成了1053,我其他地方有个dns服务,端口是53,所以这里改成了1053

如果有自己的dns proxy,这里要注意,ubuntu有个默认的resolv.service,可以把他禁用掉,不然会冲突

Read more →

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 →

typeScript的一个坑

缘起

最近找了个兼职,写了一段node的typescript代码,虽然上次写node还是5年前,但因为使用的框架是midway,写的过程中,几乎没有碰到bug,很自然的感觉,就是spring的那一套,面向对象,依赖注入等,写web还是很方便的。

不过也由于这几年天天看和写java代码,写的时候,很容易就按照面向对象写起来,一切都是class,class的方法等,所以这也是bug的由来。

问题来龙去脉

业务bug简单描述

一个学校有n个老师,每个老师一门课程,期末的时候,老师会给学生打分,学生同时也是一个用户。

问题:在打分的过程中,为了方便,对学生对象list转换成了map,然后每个老师都会对学生的分数进行统计,然后再把统计结果返回给学生,这样就会导致学生的分数被覆盖。发现学生的成绩到最后都是一个老师打的。

复现代码

class User {
    Name: string;
    Age: number;
}

class Student extends User {
    Grade: number;
}

class Teacher {
    Name: string;
    Class: string;
    Students: Student[];
}

const user1 = new User();
user1.Name = 'John';
user1.Age = 30;


const user2 = new User();
user2.Name = 'Jane';
user2.Age = 25;



const users = [user1, user2];

const teacher = new Teacher();
teacher.Name = 'Bob';
teacher.Class = '1A';

const teacher2 = new Teacher();
teacher2.Name = 'Alice';
teacher2.Class = '1B';

const teachers = [teacher, teacher2];

const userMap = users.reduce((acc, cur) => {
    acc[cur.Name] = cur;
    return acc;
  }, {});

const gradeTeacher = new Array<Teacher>();

teachers.forEach(teacher => {
    const students = new Array<Student>();
    const teacherName = teacher.Name;

    Object.entries(userMap).forEach(([key, value]) => {

        let student = new Student();
        student = userMap[key];
        if (teacherName === 'Bob') {
          student.Grade = 100;
        } else {
            student.Grade = 50;
        }
        students.push(student);

    });
    teacher.Students = students;
    gradeTeacher.push(teacher);
});


for (const teacher of gradeTeacher) {
    console.log(teacher); 
}

理论期望的结果

Teacher {
  Name: 'Bob',
  Class: '1A',
  Students: [
    User { Name: 'John', Age: 30, Grade: 100 },
    User { Name: 'Jane', Age: 25, Grade: 100 }
  ]
}
Teacher {
  Name: 'Alice',
  Class: '1B',
  Students: [
    User { Name: 'John', Age: 30, Grade: 50 },
    User { Name: 'Jane', Age: 25, Grade: 50 }
  ]
}

实际结果

Teacher {
  Name: 'Bob',
  Class: '1A',
  Students: [
    User { Name: 'John', Age: 30, Grade: 50 },
    User { Name: 'Jane', Age: 25, Grade: 50 }
  ]
}
Teacher {
  Name: 'Alice',
  Class: '1B',
  Students: [
    User { Name: 'John', Age: 30, Grade: 50 },
    User { Name: 'Jane', Age: 25, Grade: 50 }
  ]
}

why?

可以看到,user 被循环了两次,而且每次都是user1,user2,这样就导致了学生的分数被覆盖。

Read more →