May 29, 2023
Element
#
abstract class Element extends DiagnosticableTree implements BuildContext
package:flutter/src/widgets/framework.dart
An instantiation of a [Widget] at a particular location in the tree.
– 在树里的特定位置上的一个Widget
的实例。
Widgets describe how to configure a subtree but the same widget can be used to configure multiple subtrees simultaneously because widgets are immutable. An [Element] represents the use of a widget to configure a specific location in the tree. Over time, the widget associated with a given element can change, for example, if the parent widget rebuilds and creates a new widget for this location.
– Widget
描述了如何配置一棵子树,但同一个Widget
可以被用来配置多棵相似的子树,因为Widget
是不可变的。一个Element
代表了一个Widget
配置在树里的特定位置的使用。随着时间变化,每个Widget
与一个可以改变的Element
关联。
...
May 16, 2023
What
#
mysqlrouter
是一个代理,可以将查询转发到配置好的数据库服务里。
Why
#
在办公室网络环境下基于win10 wsl2
开发应用时,需要连接到主机所在局域网的其它机器上的数据库服务。
也就是说,存在机器:wsl2、主机、其它机器。
wsl2
通过NAT
网络模式与主机
互通,并且wsl2
可以访问外网。
但是wsl2
不能访问到其它机器
上的数据库服务,不知道是不是办公室网络环境存在限制。
为了使得wsl2
能访问到其它机器
上的数据库服务成立,在主机
启动mysqlrouter
充当代理,然后wsl2
通过访问代理来访问其它机器
。
Install
#
可以使用mysql installer
选择安装。
简单模式
#
配置文件(mysqlrouter.conf):
[DEFAULT]
logging_folder = D:/Data/mysqlrouter/log
plugin_folder = C:/Program Files/MySQL/MySQL Router 8.0/lib # 这里是插件所在目录,必须是mysqlrouter安装路径下的目录,否则报错找不到插件
config_folder = D:/Data/mysqlrouter/etc # 启动配置默认查找目录,会在目录里寻找mysqlrouter.conf文件
runtime_folder = D:/Data/mysqlrouter/run
data_folder = D:/Data/mysqlrouter/data
[logger]
level = DEBUG
[routing:primary]
bind_address=172.20.96.1 # 主机ip地址
bind_port=6446 # 主机监听端口
destinations = 172.17.39.239:3306 # 目标机器,也就是实际执行查询的数据库服务所在机器的地址
mode = read-write
connect_timeout = 10
启动:mysqlrouter -c D:\Data\mysqlrouter\etc\mysqlrouter.conf
...April 24, 2023
是什么?
#
Home,
Github
NATS 是一个简单、安全和高性能的通信系统,适用于数字系统、服务和设备。
NATS 是一种允许以消息形式分段的数据交换的基础架构。
基于主题
#
发布者将消息发到主题;订阅者订阅主题,在有消息到来时消费该消息。
主题命名规则:
基本字符:a to z, A to Z and 0 to 9 (区分大小写,不能包含空白字符).
特殊字符: .
(分割符,分割不同部分,每部分视为一个token); * 和 > (通配符,*表示匹配一个token,>表示匹配一或多个token).
保留主题名称: 以 $ 开头的用在系统内部 (如:$SYS, $JS, $KV …)
发布-订阅
#
Core NATS: 一个主题,存在一个发布者,多个订阅者。
消息会复制
到多个订阅者。
请求-响应
#
A request is sent, and the application either waits on the response with a certain timeout, or receives a response asynchronously.
– 请求发出后,应用要不等待响应超时,要不就异步收到一个响应。
...
January 6, 2023
// NestedJoin like nested loop join
func NestedJoin[J, K, R any](
left []J,
right []K,
match func(J, K) bool,
mapper func(J, K) R,
) []R {
var r = make([]R, 0, len(left))
for _, j := range left {
for _, k := range right {
if !match(j, k) {
continue
}
r = append(r, mapper(j, k))
}
}
return r
}
// HashJoin like hash join
func HashJoin[K comparable, LE, RE, R any](
left []LE,
right []RE,
lk func(item LE) K,
rk func(item RE) K,
mapper func(LE, RE) R,
) []R {
var r = make([]R, 0, len(left))
rm := KeyBy(right, rk)
for _, le := range left {
k := lk(le)
re := rm[k]
r = append(r, mapper(le, re))
}
return r
}
Code From
...July 20, 2022
What’s Jump Table?
#
A jump table can be either an array of pointers to functions
or an array of machine code jump instructions
. If you have a relatively static set of functions (such as system calls or virtual functions for a class) then you can create this table once and call the functions using a simple index into the array. This would mean retrieving the pointer and calling a function or jumping to the machine code depending on the type of table used.
...July 20, 2022
背景:分多次把一批货全部出清。
要求:需要确保这批货多次出清跟一次出清收的钱一样。
现有三个数字(可整数,可小数):a b c,其中:a 为数量,b 为价格,c 为折扣。
则总额为: t, t = a*b*c
假设分三次,每次数量为:a1 a2 a3,则有:a = a1 + a2 + a3
- 直接计算:
第 1 次.
a1*b*c
第 2 次.
a2*b*c
第 3 次.
a3*b*c
(a1+a2+a3)bc 不就等于 abc 了吗?
但是,如果考虑到小数乘法计算时的精度,比如:1.22*2.33 相乘后再取精度(保留两位小数),不就会导致数量误差了吗?
那如果取精度导致结果误差,那我不取精度,直接用所有小数位数来计算呢。
虽说可以,但小数位数是有可能非常多的,占用的空间也是一笔不小的开销。
- 引入中间量(可称为’余额’): x y z
x = a
y = x*b
z = y*c
第 1 次.
x1 = (x-a1)
y1 = (y-y*a1/x)
z1 = (z-z*a1/x)
t1 = z*a1/x
第 2 次.
x2 = (x1-a2)
y2 = (y1-y1*a2/x1)
z2 = (z1-z1*a2/x1)
t2 = z1*a2/x1
第 3 次.
...May 30, 2022
泛型
#
是什么?
#
Type parameter, 类型参数。func Add[T Number](x, y T) (r T)
,其中的T
就是类型参数,它被接口Number
所约束。
type Number interface {
int | float32
}
调用方除了可自行决定参数值之外,还可以自行决定参数类型。Add[int](1, 2)
,在调用时指定T
的类型为int
,同时传入参数值1
,2
必须是int
类型。
这样使得代码更灵活,更有扩展性,同时更安全。
Go泛型
#
为什么?
#
静态语言,类型固定,比如这个函数:func Add(x, y int) int
就要求参数和结果都必须是整型。
那如果后来又需要一个浮点数的加法呢?
那使用interface{}不也可以吗?
试看:
// 准确的描述出了参数和返回值的类型,非常方便
func Add(x, y int) int
// 但也限制了Add函数的参数类型--只能接收`int`
// Add(0.1, 0.2) // can't do that
// 那怎么办呢?再写一个针对float64的呗
func AddFloat64(x, y float64) float64
AddFloat64(0.1, 0.2) // it's ok
// 如果还要支持其它类型呢?再加一个吗,每多一种类型,就多加一个。。。
func AddInt8(x, y int8) int8
func AddInt32(x, y int32) int32
func AddFloat32(x, y float32) float32
// more...
// emm.
// how about interface{}?
func AddAny(x, y interface{}) interface{} {
switch x.(type) {
case int:
case int8:
case int32:
case float32:
case float64:
// more...
default:
panic("not support type")
}
}
// interface{}表示可以接收任意类型的值,并且返回任意类型的值
// 换言之,参数的类型和返回值的类型没有必然联系--从签名看来,它们可以一样,也可以不一样
// 所以,使用interface{}不够安全。
func AddGeneric1[T any](x, y T) T // 看起来跟AddAny差不多,但是参数类型和返回值类型必然是相同的
// 但any并不一定支持+运算符,所以需要用更细粒度的约束
type Number interface {
~int|~int8|~int32|~float32|~float64
}
func AddGeneric2[T Number](x, y T) T // 通过Number约束,确保类型参数可加
// 泛型的存在,使得函数的类型集比any小,比int大;使得返回值和参数的类型能够动态联系。
func Map[T, E any](list []T, f func(T) E) []E {
r := make([]E, len(list))
for i := range list {
r[i] = f(list[i])
}
return r
}
...May 27, 2022
根据二进制文件找出应用构建时使用的Go
版本
#
使用
dlv
:
dlv exec ./app
> p runtime.buildVerion
或者,在代码里调用runtime.Version()
:
func main() {
fmt.Println("go version:", runtime.Version())
}
参照
May 13, 2022
k8s是怎么维持pod的运行的呢?
#
当接收了yaml配置的信息后,是怎么维持pod根据声明一直运行的呢?
让我们沿着命令执行的过程来一睹为快:kubectl apply -f pod.yaml
.
源码位置:cmd/kubectl/kubectl.go
-> staging/src/k8s.io/kubectl/pkg/cmd/cmd.go
-> staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go
最终的执行方法:
func (o *ApplyOptions) Run() error {
// 预处理
if o.PreProcessorFn != nil {
klog.V(4).Infof("Running apply pre-processor function")
if err := o.PreProcessorFn(); err != nil {
return err
}
}
// Enforce CLI specified namespace on server request.
if o.EnforceNamespace {
o.VisitedNamespaces.Insert(o.Namespace)
}
// Generates the objects using the resource builder if they have not
// already been stored by calling "SetObjects()" in the pre-processor.
errs := []error{}
infos, err := o.GetObjects()
if err != nil {
errs = append(errs, err)
}
if len(infos) == 0 && len(errs) == 0 {
return fmt.Errorf("no objects passed to apply")
}
// Iterate through all objects, applying each one.
for _, info := range infos {
if err := o.applyOneObject(info); err != nil {
errs = append(errs, err)
}
}
// If any errors occurred during apply, then return error (or
// aggregate of errors).
if len(errs) == 1 {
return errs[0]
}
if len(errs) > 1 {
return utilerrors.NewAggregate(errs)
}
if o.PostProcessorFn != nil {
klog.V(4).Infof("Running apply post-processor function")
if err := o.PostProcessorFn(); err != nil {
return err
}
}
return nil
}
// applyOneObject里会调用以下方法
func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte, options *metav1.PatchOptions) (runtime.Object, error) {
if options == nil {
options = &metav1.PatchOptions{}
}
if m.ServerDryRun {
options.DryRun = []string{metav1.DryRunAll}
}
if m.FieldManager != "" {
options.FieldManager = m.FieldManager
}
if m.FieldValidation != "" {
options.FieldValidation = m.FieldValidation
}
return m.RESTClient.Patch(pt).
NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource).
Name(name).
SubResource(m.Subresource).
VersionedParams(options, metav1.ParameterCodec).
Body(data).
Do(context.TODO()). // 调用api,把apply请求发到主节点,记录信息到etcd之后,再创建出相应的pod
Get()
}
// 那么,接收并处理这个Patch请求的代码在哪里呢?
// NewStreamWatcher creates a StreamWatcher from the given decoder.
func NewStreamWatcher(d Decoder, r Reporter) *StreamWatcher {
sw := &StreamWatcher{
source: d,
reporter: r,
// It's easy for a consumer to add buffering via an extra
// goroutine/channel, but impossible for them to remove it,
// so nonbuffered is better.
result: make(chan Event),
// If the watcher is externally stopped there is no receiver anymore
// and the send operations on the result channel, especially the
// error reporting might block forever.
// Therefore a dedicated stop channel is used to resolve this blocking.
done: make(chan struct{}),
}
go sw.receive() // 接收请求,然后通过chan发送出去,再由其它代码来处理?
return sw
}
// TODO:
apimachinery共享库
...May 12, 2022
Go enum
#
Go是没有内置枚举类型的,那么,当需要使用枚举时,该怎么办呢?
枚举说白了,就是一连串互斥的值
,每个值代表一样事物或一个类型。
比如,现在需要一个颜色枚举,可以这样定义:
const (
Red = "Red" // 红色
Blue = "Blue" // 蓝色
Green = "Green" // 绿色
)
也有这样定义的:
type Color string // 定义一个特定类型
// 枚举常量均声明为该类型
const (
Red Color = "Red" // 红色
Blue Color = "Blue" // 蓝色
Green Color = "Green" // 绿色
)
这样做的好处是可以通过这个类型来更明显的标记出枚举字段来:
type Car struct {
Name string
Color Color // 颜色字段声明为Color类型,在阅读代码的时候就能知道这个字段正常的可选值范围
}
但是,上面的做法都需要面临一个问题,就是我需要一个返回全部枚举值的集合时,需要这样做:
func All() []Color {
return []Color{
Red,
Blue,
Green,
}
}
func (color Color) Name() string {
switch color {
case Red:
return "红色"
case Blue:
return "蓝色"
case Green:
return "绿色"
}
return ""
}
当在定义处新增值时,All
和Name
也要同步添加,对于开发人员来说,非常容易遗漏。
...