Decorative image frame

喵耳朵

略懂算法 | 在学架构 | 梦想成为 | Top插画师

喵耳朵

免费的云服务搜集

注意,以下的信息仅截止于2024年1月26,后续可能会有变化。 以下顺序不代表排名。

下面的内容主要是免费的一些云服务资源,具体有:

  1. 计算资源
    • Serverless函数
    • 容器
  2. 数据
    • KV
    • Redis
    • MySQL
    • MongoDB
    • Kafka
Read More...

C++和Go关于闭包递归调用的趣事

一次CodeReview引发的讨论

起因是这样的,Code Review的时候看到了下面这段Golang的代码:

package main

import "fmt"

func download() error {
	fmt.Println("download")
	return fmt.Errorf("download error")
}

func main() {
	times := 3
	var tryDownload func() error
	tryDownload = func() error {
		if times == 0 {
			return fmt.Errorf("exceed max times")
		}
		err := download()
		if err == nil {
			return nil
		}
		times--
		return tryDownload()
	}
	tryDownload()
	fmt.Println("last times", times)
}

大致意思是尝试下载三次。

由于被Golang的闭包坑过,所以当时给的Code Review的意见是改成for loop的方式,避免闭包捕获可能导致的问题。

Read More...

CGO函数调用与数据转换

CGO是Go程序调用C库的一套机制,可以使Go语言能够站在C/C++的肩膀上。比如Go调用Tensorflow,也就是使用了CGO来实现的。很多语言都支持对C库的调用,一般称为FFI(Foreign Function Interface)。这里需要注意一下,一般的Python、Lua等语言调用C函数的时候,是通过函数签名找到函数的地址,然后直接调用对应的函数。而CGO会先生成中间文件,然后再一起编译调用。这里不具体展开了,总之只需要知道CGO比一般的FFI多了一层中间的步骤。在程序出错的时候,能理解调用栈的关系即可。参考 CGO 和 CGO 性能之谜

函数调用

package main

/*
CGO的标准写法:
1. 先用注释的方式写入,单行注释和多行注释都支持
  1.1 编译器环境变量
	1.1.1 CFLAGS: C编译选项
	1.1.2 CXXFLAGS: C++编译选项
	1.1.3 CPPFLAGS: C和C++共有的编译选项
	1.1.4 FFLAGS: Fortran编译选项
	1.1.4 LDFLAGS: 链接选项(不区分C和C++)
  1.2 C代码
2. import "C",相当于将所有的C函数放入虚拟的package C。之后通过`C.xxx`的方式来调用。需要紧跟注释之后。
*/

/*
#cgo LDFLAGS: -lm

#include <math.h>
double my_sqrt(double x) {
	return sqrt(x);
}
*/
import "C" // CGO的标准写法,相当于将C函数放入包`C`中
import "fmt"

func main() {
	a := 100
	fmt.Printf("sqrt(%v) = %v\n", a, C.my_sqrt(C.double(a))) // 调用上面自定义的函数
	fmt.Printf("sqrt(%v) = %v\n", a, C.sqrt(C.double(a)))    // 调用系统库函数(上述的m)
}

// Output:
//  sqrt(100) = 10
//  sqrt(100) = 10

通过上述的例子,我们可以看出,CGO可以调用C的库函数,也可以执行注释中的C代码的函数。一般注释中的C代码都是比较简短的。

接下来要介绍C和Go之间的数据是如何传递的。

Read More...

C++对象模型

本文主要是在看《Inside the C++ Object Model》的时候,想通过案例的方式加深一下理解。

首先,由于编译器有内存对齐的优化,比如:

#include <iostream>

class WithAlign {
    int a;
    char b;
};

#pragma pack(push, 1)
class WithoutAlign {
    int a;
    char b;
};
#pragma pack(pop)

int main() {
    std::cout << "WithAlign: " << sizeof(WithAlign) << std::endl;
    std::cout << "WithoutAlign: " << sizeof(WithoutAlign) << std::endl;
}

// Output:
//  WithAlign: 8
//  WithoutAlign: 5

内存对齐的类大小为8(按int 4字节对齐),未对齐的为5(int + char)。这里为了更容易理解,全部默认使用1字节对齐。

注意,本文中的内容均仅在自己的机器的Docker容器中做测试,环境为:64位 ubuntu 16.04, gcc 5.5。

# lsb_release -a
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.7 LTS
Release:        16.04
Codename:       xenial

# uname -a
Linux 06f25c7abffd 5.15.49-linuxkit #1 SMP Tue Sep 13 07:51:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

# gcc --version
gcc (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010

何为C++对象模型?

引用《深度探索C++对象模型》这本书中的话:

有两个概念可以解释C++对象模型:

  1. 语言中直接支持面向对象程序设计的部分。包括构造函数、析构函数、多态、虚函数等。
  2. 对于各种支持的底层实现机制。这部分并没有标准化,因此不同的编译器可以有自己的实现。
Read More...

服务端基本概念和指标

这里主要介绍服务端架构工作中的一些常见的概念和指标,在我们部署、上线等运维工作时,方便排查问题,以及交流时的语义一致。

上游和下游 upstream and downstream

一般在谈论服务和调用关系的时候,我们会使用上游和下游来表示服务间的相关依赖。但是对于上下游的定义,会视情况而定的。

Stack Overflow上有个相同的问题:definition - Upstream / downstream terminology used backwards? (E.g. nginx) - Stack Overflow

Read More...

架构小试之IDL

为什么IDL的介绍也放在这里呢?一方面是我想不到放哪里,另一方面是之前说到,“架构”即“设计”,那么IDL、RPC框架也算是设计的一部分。不合理的选型在后续维护上会带来不小的麻烦。

本文主要介绍我用过的一些IDL,并结合真实案例,分析他们的优劣。

Read More...

我所理解的架构

本来是打算写一篇介绍我目前工作中有意思的设计的博客,写着写着发现设计的知识点还是有点多的,因此这里单独增加一栏,用来不断的更新相关的内容。

由于工作的变化,我从图像算法和框架,转向了服务端架构。做服务端架构,相较于算法和sdk框架,有着挺大的区别。而适应这一变化,确实需要花费挺长的时间,也学习到很多。

服务端的概念很多,很难在一本书上找到所有的答案,平时也只能自己多问多摸索。

这里总结了一下日常遇到的有意思的概念和技巧,希望耐心看完本文的你能够更快速的理解服务端的概念和开发过程。不至于在入职时每次讨论都一脸懵逼。

另外,我会在各部分的章节中,插入一些工作中遇到的真实案例。事实上,由于这些案例太过有意思,让我忍不住分享出来,所以才有了本文。(本文的目录也是根据这些案例归纳出来的。)

Read More...

Redis数据结构与对象

本文主要是《Redis设计与实现》的第一部分的总结,内容为数据结构和对象。这里类比了C++ STL中的数据结构,便于理解。

Redis是使用C语言编写的,C语言本身没有复杂的数据结构。因此Redis自己实现了一套底层的数据结构,这些数据结构作为工具被Redis的其他模块使用。

Read More...