Go语言中通过goroutines和channel进行并发的控制。

  • goroutines 提供了独立任务的并发/并行可能
  • channel 提供goroutines间交流、同步的桥梁

理解Channel用法,参考这篇文章 《Go语言Channel的Buffered与Unbuffered》 ,总结channel有一下的特点:

  • 保证goroutines并发安全
  • 提供FIFO(先进先出)的特性
  • 可以在goroutines传送和存储值
  • 可以控制goroutines阻塞和非阻塞(控制执行和停止等待)

所以,我们需要从底层去了解channel的结构,channel 底层是一个hchan的数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type hchan struct {
qcount uint // 队列数据总数
dataqsiz uint // 循环队列的大小
buf unsafe.Pointer // 指向dataqsiz大小的数组元素地址
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // 发送索引
recvx uint // 接收索引
recvq waitq // 接收等待的队列
sendq waitq // 发送等待的队列

// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
// 锁
lock mutex
}

先不去关注具体的字段意义,我们从最基本的出发

阅读全文 »

Go的变量传参是值传递

这边文章有具体分析:《Go语言参数传递是传值还是传引用》,看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {

var i = make([]int, 3)
foo(i)
fmt.Println(i)
boo(i)
// 没有输出 1 0 0 3 4 5
fmt.Println(i)
}

func foo(s []int) {
s[0] = 1
}

func boo(s []int) {
s = append(s, 3, 4, 5)
}

输出:

1
2
[1 0 0]
[1 0 0]
阅读全文 »

简介

Lumen的Model操作使用的是 Laravel 内置的 Eloquent ORM,Eloquent提供了一个美观、简单的与数据库打交道的 ActiveRecord 实现,每张数据表都对应一个与该表进行交互的模型(Model),通过模型类,你可以对数据表进行查询、插入、更新、删除等操作。

配置

应用的数据库配置位于 config/database.php(但是数据库用户及密码等敏感信息位于 .env 文件)。在该文件中你可以定义所有的数据库连接,并指定哪个连接是默认连接。该文件中提供了所有支持数据库系统的配置示例。

1
env('DB_CONNECTION', 'mysql') // 配置文件中通过env()获取.env文件中配置的键值
阅读全文 »

传统的网络是一个C\S架构,相当于直连接网络

image-20200618170723322

有一天,网络服务提供商为了限制使用者访问某些互联网服务,于是设置了一堵墙,只有符合规则的网络传输才允许通过。目前全球最大,最厉害的是GFW,但是也做不到完全限制墙外访问,于是有人就找了台墙外主机,作为代理,所有墙内请求都需经过该主机经转发后访问具体目标,这样就可以在墙外轻松享受服务。

阅读全文 »

消息系统

所谓消息系统就是消息在发布者(Publishers)和订阅者(Subscribers)之间的传递规则,发布者不关心消息到底需要被谁接收使用,订阅者也不需要关心消息来自哪个发布者。发布者和订阅者之间通过频道(channels)来进行消息传递,发布者可以发布消息到多个频道中,而订阅者只会订阅感兴趣的频道。这样的解耦可以使得我们的网络拓扑结构更具扩展性。

Redis中提供了以下的命令,可实现消息系统

举个例子:

连接A

1
2
3
4
127.0.0.1:6379> SUBSCRIBE speaker
1) "subscribe"
2) "speaker"
3) (integer) 1

订阅了一个speaker的channel,我们在另外一个端口中发布消息看看:

连接B

1
2
127.0.0.1:6379> PUBLISH speaker "hello world!"
(integer) 1

连接A立即收到消息

1
2
3
1) "message"
2) "speaker"
3) "hello world!"

推送消息的格式化

Redis中的一条消息是一个数组类型

  • 订阅时
1
SUBSCRIBE speaker

实际发送了指令

1
*3\r\n$9\r\nsubscribe\r\n$7\r\nspeaker\r\n:1

*3数组的长度为3,$9,$7表示接下来的数组元素的长度,:1表示此次订阅了1个channel。

请求/响应模型和RTT

Redis的TCP的服务采用Client/Server模型。意味着一次查询将遵循以下步骤:

  • 客户端发起请求阻塞直到服务器响应。
  • 服务器会处理请求操作的命令,并返回结果。

例如下面的四次的命令执行

1
2
3
4
5
6
7
8
127.0.0.1:6379> incr x
(integer) 1
127.0.0.1:6379> incr x
(integer) 2
127.0.0.1:6379> incr x
(integer) 3
127.0.0.1:6379> incr x
(integer) 4

客户端和服务端的通讯是建立在网络之上,如果是回环链路(本地网络)将是非常快的,相反互联网之上将相对较慢。但是无论在哪种网络下,数据包从客服端到服务端,再由服务端返回到客户端并接收的一个周期我们叫做RTT(Round Trip Time),这样我们可以参考RTT时间来优化性能(比如插入大量的元素,或者添加大量的数据库键值)在回环链路的RTT是非常短的(ping 127.0.0.1 可以看到结果),但是我们写入大量数据时,性能依旧是个问题。

阅读全文 »

问题描述

Go在拉取包的报了这一个错误:

x509: certificate is valid for gomirrors.org, www.gomirrors.org, not goproxy.io

goproxy.io是Go的模块代理服务。

解决办法

出现这种情况基本是受到了中间人攻击,所谓中间人攻击就是你的电脑网络请求被监控。

一般出现在公司电脑上,或者你开启了网络请求的拦截,比如抓包工具Charles。我们把它关闭就正常了。

Mysql 有四种事务隔离级别

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
读未提交(Read uncommitted) 可能 可能 可能
读已提交(Read committed 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
  • 脏读 一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,读取到了不想得到的数据了。

  • 幻读 第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

  • 不可重复读 数据库访问中,在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

阅读全文 »

Go的变量到底是分配在堆还是栈,我们先聊下下概念。

  • 堆(heap) 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
  • 栈 (stack) 由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。Go在分配栈的时候,预先分配2k的空间。

理论上分配在栈上的数据操作速度快与堆上。堆上使用的内存需要垃圾回收(GC),栈上的内存是自动释放的且是明确不共享的。

阅读全文 »