2020-05-13 13:11:55
摘要:别让性能被“锁”住
我们来看一段代码
var cache map[string]string
const NUM_OF_READER int = 40
const READ_TIMES = 100000
func init() {
cache = make(map[string]string)
cache[a] = aa
cache[b] = bb
}
func lockFreeAccess() {
var wg sync.WaitGroup
wg.Add(NUM_OF_READER)
for i := 0; i NUM_OF_READER; i++ {
go func() {
for j := 0; j READ_TIMES; j++ {
_, err := cache[a]
if !err {
fmt.Println(Nothing)
}
}
wg.Done()
}()
}
wg.Wait()
}
func lockAccess() {
var wg sync.WaitGroup
wg.Add(NUM_OF_READER)
m := new(sync.RWMutex)
for i := 0; i NUM_OF_READER; i++ {
go func() {
for j := 0; j READ_TIMES; j++ {
m.RLock()
_, err := cache[a]
if !err {
fmt.Println(Nothing)
}
m.RUnlock()
}
wg.Done()
}()
}
wg.Wait()
}
这段程序一个没有锁,一个有锁。我们看一下测试结果
func BenchmarkLockFree(b *testing.B) {
b.ResetTimer()
for i := 0; i b.N; i++ {
lockFreeAccess()
}
}
//169 6618595 ns/op 77 B/op 1 allocs/op
func BenchmarkLock(b *testing.B) {
b.R……
阅读全文
2020-05-09 17:30:49
摘要:json解析
内置json解析
利⽤反射实现,通过FeildTag来标识对应的json 值
type BasicInfo struct {
Name string `json:name`
Age int `json:age`
}
type JobInfo struct {
Skills []string `json:skills`
}
type Employee struct {
BasicInfo BasicInfo `json:basic_info`
JobInfo JobInfo `json:job_info`
}
var jsonStr = `{
basic_info:{
name:Mike,
age:30
},
job_info:{
skills:[Java,Go,C]
}
} `
func TestEmbeddedJson(t *testing.T) {
e := new(Employee)
err := json.Unmarshal([]byte(jsonStr), e)
if err != nil {
t.Error(err)
}
fmt.Println(*e)
if v, err := json.Marshal(e); err == nil {
fmt.Println(string(v))
} else {
t.Error(err)
}
}
更快的JSON解析
EasyJSON 采用代码生成而非反射
安装:go get -u github.com/mailru/easyjson/... (后面的...需要带上)
使⽤:easyjson -all 结构定义.go (会生成一些代码)
1.比如我们有结构文件 struct_def.go
type BasicInfo struct {
Name string
Age int
}
type JobInfo struct {
Skills []string
}
type Employee struct {
BasicInfo BasicInfo
JobInfo JobInfo
}
2.执行 easyjson -all struct_def.go会生成 struct_def_easyjs……
阅读全文
2020-05-01 20:44:02
摘要:什么是微内核架构
微内核架构(Microkernel Architecture),也被称为插件化架构(Plugin-in Architecture),是一种面向功能进行拆分的可扩展架构。例如 VS Code、Eclipse 这一类 IDE 软件、UNIX 操作系统等等,都是参照微内核架构设计实现的。
微内核架构的两个核心组件
微内核架构包含两类核心的组件:核心系统(Core System)和插件模块(Plug-in modules)。核心系统负责与具体功能无关的通用功能,例如应用生命周期的管理、插件模块的管理(包括:插件模块的注册、载入、卸载等等);插件模块负责实现具体的功能,例如一个 Web 框架基本上会按照功能模块拆分成如下的插件模块:路由模块、安全模块、HTTP 编解码模块等等,每个模块都通过插件实现,每一个插件都只做一件事情。
微内核基本架构示意图如下所示:
核心系统功能尽量保持稳定,不要因为插件模块的扩展而不断修改,插件模块可以根据功能需求进行不断扩展。
特点与要点
特点
易于扩展
错误隔离
保持架构⼀致性
要点
内核包含公共流程或通⽤逻辑
将可变或可扩展部分规划为扩展点
抽象扩展点⾏为,定义接⼝
利⽤插件进⾏扩展
实例
如下图,我们希望实现Agent在系统主机上,这个Agent可以手机文件信息、进程信息、应用信息,以及提供一个扩展点,可以扩展未来其他要收集的信息,因此我们需要提供一个Extension Point。
1.接口Collector定义
type Collector interface {
Init(evtReceiver EventReceiver) error
Start(agtCtx context.Context) error
Stop() error
Destory() error
}
type Event struct {
Source string
Content string
}
type EventReceiver interface {
OnEvent(evt Event)
}
2.定义Agent结构体
type Agent struct {
collectors map[string]Collector
evtBuf chan Event
cancel c……
阅读全文
2020-04-27 22:50:03
摘要:Pipe-Filter 模式,即管道过滤器模式,这是一种非常经典的架构模式,这种模式与工业制造生产流水线非常类似,就像薯片的生产过程,从土豆的清洗、去皮、切片、烘干、油炸,到最后打包完成,整个生产过程被拆分成了多个环节,每个环节处理完成之后,通过传送带传送到下一个环节的机器。整个生产过程每个环节都是独立的,但又环环相扣,只要有一个环节出问题了,生产出来的薯片就会有质量问题。
适用的场景
⾮常适合与数据处理及数据分析系统
Filter封装数据处理的功能
Pipe⽤于连接Filter传递数据或者在异步处理过程中缓冲数据流
进程内同步调⽤时,pipe演变为数据在⽅法调⽤间传递
松耦合:Filter只跟数据(格式)耦合
Filter 和组合模式
23 个经典设计模式里面有一个设计模式叫组合模式,当 Pipe-Filter 遇上组合模式时,多个 Filter 又可以再组合成一个新的 Filter,如下图所示,组合出来的 Filter 接收的数据与第一个 Filter 保持一致,返回的数据与最后一个 Filter 保持一致。通过组合,就可以将多个简单的 Filter 可以组合成一个更复杂的 Filter。应用这一套理论去实践,我们会发现,Filter 既可以做的很轻便,也可以做得很强大。
实例
接下来我们用Go语言实现上图实例:
1.定义filter接口
// Package pipefilter is to define the interfaces and the structures for pipe-filter style implementation
package pipefilter
// Request is the input of the filter
type Request interface{}
// Response is the output of the filter
type Response interface{}
// Filter interface is the definition of the data processing components
// Pipe-Filter structure
type Filter interface {
Process(data Request) (Respons……
阅读全文
2020-04-23 12:31:09
摘要:“不完全”行为的危险性,go语言中是不支持类型转换的,但我们使用“不安全“编程可以将类型的指针转换成任意其他类型,如下:
func TestUnsafe(t *testing.T) {
i := 10
f := *(*float64)(unsafe.Pointer(i))
t.Log(unsafe.Pointer(i))
t.Log(f) //5e-323, 并不能得到理想的结果
}
也有能转换成功的例子,比如我们对类型起的别名:
type MyInt int
//合理的类型转换
func TestConvert(t *testing.T) {
a := []int{1, 2, 3, 4}
b := *(*[]MyInt)(unsafe.Pointer(a))
t.Log(b) //[1 2 3 4]
}
原子类型操作
func TestAtomic(t *testing.T) {
var shareBufPtr unsafe.Pointer
writeDataFn := func() {
data := []int{}
for i := 0; i 100; i++ {
data = append(data, i)
}
//写完后再通过原子操作,将指针重新指向
atomic.StorePointer(shareBufPtr, unsafe.Pointer(data))
}
readDataFn := func() {
data := atomic.LoadPointer(shareBufPtr)
fmt.Println(data, *(*[]int)(data))
}
var wg sync.WaitGroup
writeDataFn()
for i := 0; i 10; i++ {
wg.Add(1)
go func() {
for i := 0; i 10; i++ {
writeDataFn()
time.Sleep(time.Microsecond * 100)
}
wg.Done()
}()
wg.Add(1)
go func() {
for i := 0; i 10; i++ {
readDataFn(……
阅读全文
2020-04-21 21:39:42
摘要:reflect.TypeOf vs. reflect.ValueOf
reflect.TypeOf 返回类型 (reflect.Type)
reflect.ValueOf 返回值 (reflect.Value)
可以从 reflect.Value 获得类型
通过 kind 的来判断类型
func CheckType(v interface{}) {
t := reflect.TypeOf(v)
switch t.Kind() {
case reflect.Float32, reflect.Float64:
fmt.Println(Float)
case reflect.Int, reflect.Int32, reflect.Int64:
fmt.Println(Integer)
default:
fmt.Println(Unknown, t)
}
}
func TestBasicType(t *testing.T) {
var f float64 = 12
CheckType(f) //Float
CheckType(f) //Unknown *float64
}
func TestTypeAndValue(t *testing.T) {
var f int64 = 10
t.Log(reflect.TypeOf(f), reflect.ValueOf(f)) //int64 10
t.Log(reflect.ValueOf(f).Type()) //int64
}
利用反射编写灵活的代码
按名字访问结构的成员
reflect.ValueOf(*e).FieldByName(Name)
按名字访问结构的方法
reflect.ValueOf(e).MethodByName(UpdateAge).Call([]reflect.Value{reflect.ValueOf(1)})
实例
type Employee struct {
EmployeeID string
Name string `format:normal`
Age int
}
……
阅读全文
2020-04-18 23:11:40
摘要:仅执行一次
C#中的单例模式(懒汉式,线程安全)
public class Singleton
{
private static volatile Singleton instance;
private static readonly object syncRoot = new object();
private Singleton() { }
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
Go的实现
type Singleton struct {
data string
}
var singleInstance *Singleton
var once sync.Once
func GetSingletonObj() *Singleton {
once.Do(func() {
fmt.Println(Create Obj)
singleInstance = new(Singleton)
})
return singleInstance
}
测试
func TestGetSingletonObj(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i 5; i++ {
wg.Add(1)
go func() {
obj := GetSingletonObj()
fmt.Printf(%X\n, unsafe.Pointer(obj))
……
阅读全文
2020-04-15 18:42:50
摘要:协程机制(Groutine)
Thead VS Groutine
创建时默认的 stack 的⼤⼩
JDK5 以后 Java Thread stack 默认为1M
Groutine 的 Stack 初始化⼤⼩为2K
和 KSE (Kernel Space Entity) 的对应关系
Java Thread 是 1:1
Groutine 是 M:N
MPG模型
M 代表着一个内核线程,也可以称为一个工作线程。goroutine就是跑在M之上的
P代表着(Processor)处理器 它的主要用途就是用来执行goroutine的,一个P代表执行一个Go代码片段的基础(可以理解为上下文环境),所以它也维护了一个可运行的goroutine队列,和自由的goroutine队列,里面存储了所有需要它来执行的goroutine
G 代表着goroutine 实际的数据结构(就是你封装的那个方法),并维护者goroutine 需要的栈、程序计数器以及它所在的M等信息
我们在看上面这个图,图中P正在执行的Goroutine为蓝色的,处于待执行状态的Goroutine为灰色的,灰色的Goroutine形成了一个队列runqueues 。 我们再看一下三者的宏观图:
由上图可以看出Groutine与KSE是M:N的多对多关系。在这里,当一个P关联多个G时,就会处理G的执行顺序,就是并发,当一个P在执行一个协程工作时,其他的会在等待,当正在执行的协程遇到阻塞情况,例如IO操作等,go的处理器就会去执行其他的协程,因为对于类似IO的操作,处理器不知道你需要多久才能执行结束,所以他不回去等你执行完。
正是因为是非抢占式的,所以才轻松的构造上万的协程,如果是抢占式,那么就会在切换任务时,保存当前的上下文环境,因为当前线程如果正在做一件事,做到一半,我们就强制停止,这时我们就必须多保存很多信息,避免再次切换回来时任务出错。
线程是操作系统层面的多任务,而go的协程属于编译器层面的多任务,go有自己的调度器来调度。一个协程在哪个线程上是不确定的,这个是由调度器来决定的,多个协程可能在一个或多个线程上运行。
编写一个Groutine
func TestGroutine(t *testing.T) {
for i := 0; i 10; i++ {
g……
阅读全文
2020-04-12 14:38:51
摘要:封装数据与行为
结构体定义
type Employee struct {
Id string
Name string
Age int
}
实例创建及初始化
e := Employee{0, Bob, 20}
e1 := Employee{Name: Mike, Age: 30}
e2 := new(Employee) //注意这⾥返回的引⽤/指针,相当于 e := Employee{}
e2.Id = “2 //与其他主要编程语⾔的差异:通过实例的指针访问成员不需要使⽤-
e2.Age = 22
e2.Name = “Rose
行为(方法)定义
//第⼀种定义⽅式在实例对应⽅法被调⽤时,实例的成员会进⾏值复制
func (e Employee) String() string {
return fmt.Sprintf(ID:%s-Name:%s-Age:%d, e.Id, e.Name, e.Age)
}
//通常情况下为了避免内存拷⻉我们使⽤第⼆种定义⽅式
func (e *Employee) String() string {
return fmt.Sprintf(ID:%s/Name:%s/Age:%d, e.Id, e.Name, e.Age)
}
type Employee struct {
Id string
Name string
Age int
}
func (e Employee) String() string { //这里传递的是类型
fmt.Printf(Address is %x, unsafe.Pointer(e.Name))
}
func TestStructOperations(t *testing.T) {
e := Employee{0, Bob, 20}
fmt.Printf(Address is %x, unsafe.Pointer(e.Name))
t.Log(e.String()) //Address is c000068520 Address is c000068550
}
可以看到上面测试程序调用String方法时传递的是类型,log得到的是2不同的地址,如果改成传递地址呢?
func (e Employee) String() ……
阅读全文
2020-04-09 13:17:12
摘要:数组
数组的声明
var a [3]int //声明并初始化为默认零值
a[0] = 1
b := [3]int{1,2,3}
c := [...]int{1,2,3,4,5} //不指定元素个数
d := [2][2]int{{1,2},{3,4}} //多维数组初始化
数组元素遍历
func TestTravelArray(t testing.T) {
a := [......
阅读全文