如何使用golang实现一个redis的telnet交互程序

网站管理员 Published the article • 0 comments • 5 views • 4 天前 • 来自相关话题

该代码实例只是一个引子,并未实现交互,差一个循环输入,读取用户交互输入的实现。

### 实现步骤

1. 首先我们需要创建一个redis的连接`net.Dial`, 协议是`tcp`,端口6380。
2. `defer conn.Close()` 延迟关闭连接
3. 接下来我们需要做的事情就是发送数据和读取数据`func (conn RedisConn) Command(cmd []byte) ([]byte, error)`


### 代码实例

```go
package main

import (
"bufio"
"fmt"
"net"
"sync"
"unsafe"
)

type RedisConn struct {
RedisConn net.Conn
}

func (conn RedisConn) Command(cmd []byte) ([]byte, error) {
if _, err := conn.RedisConn.Write(cmd); err != nil {
panic(err)
}
reader := bufio.NewReader(conn.RedisConn)
buffer, _, err := reader.ReadLine()
if err != nil {
return nil, err
}
return buffer, nil
}

func main() {

conn, err := net.Dial("tcp", "192.168.1.18:6380")
if err != nil {
panic(err)
}
redis := RedisConn{
RedisConn: conn,
}

defer conn.Close()

if result, err := redis.Command([]byte("Ping\n")); err == nil {
fmt.Println(string(result))
}

if result, err := redis.Command([]byte("set test 2\n")); err == nil {
fmt.Println(string(result))
}

if result, err := redis.Command([]byte("get test\n")); err == nil {
fmt.Println(string(result))
}
}
``` 查看全部

该代码实例只是一个引子,并未实现交互,差一个循环输入,读取用户交互输入的实现。

### 实现步骤

1. 首先我们需要创建一个redis的连接`net.Dial`, 协议是`tcp`,端口6380。
2. `defer conn.Close()` 延迟关闭连接
3. 接下来我们需要做的事情就是发送数据和读取数据`func (conn RedisConn) Command(cmd []byte) ([]byte, error)`


### 代码实例

```go
package main

import (
"bufio"
"fmt"
"net"
"sync"
"unsafe"
)

type RedisConn struct {
RedisConn net.Conn
}

func (conn RedisConn) Command(cmd []byte) ([]byte, error) {
if _, err := conn.RedisConn.Write(cmd); err != nil {
panic(err)
}
reader := bufio.NewReader(conn.RedisConn)
buffer, _, err := reader.ReadLine()
if err != nil {
return nil, err
}
return buffer, nil
}

func main() {

conn, err := net.Dial("tcp", "192.168.1.18:6380")
if err != nil {
panic(err)
}
redis := RedisConn{
RedisConn: conn,
}

defer conn.Close()

if result, err := redis.Command([]byte("Ping\n")); err == nil {
fmt.Println(string(result))
}

if result, err := redis.Command([]byte("set test 2\n")); err == nil {
fmt.Println(string(result))
}

if result, err := redis.Command([]byte("get test\n")); err == nil {
fmt.Println(string(result))
}
}
```

如何将packages中的main.go自动编译安装包go bin目录下?

网站管理员 Published the article • 0 comments • 13 views • 2019-08-06 16:48 • 来自相关话题

在我的项目中有个main.go的入口文件,这是一个命令行脚本,编译之后可以生成一个二进制可执行文件。如何在安装该包之后,自动完成安装。

### 命令如下

```bash
go get -u github.com/deliangyang/huge-mysqldump
```

### go get -u命令参数详解

> The -u flag instructs get to use the network to update the named packages
> and their dependencies. By default, get uses the network to check out
> missing packages but does not use it to look for updates to existing packages.

从网络上下载或者更新包,及其依赖的时候,使用参数`-u`,默认情况下,get使用网络取检查丢失的包,但是查找更新已经存在的包。
下载完毕之后,会在`go bin`目录下生成一个package名的二进制可执行文件。 查看全部

在我的项目中有个main.go的入口文件,这是一个命令行脚本,编译之后可以生成一个二进制可执行文件。如何在安装该包之后,自动完成安装。

### 命令如下

```bash
go get -u github.com/deliangyang/huge-mysqldump
```

### go get -u命令参数详解

> The -u flag instructs get to use the network to update the named packages
> and their dependencies. By default, get uses the network to check out
> missing packages but does not use it to look for updates to existing packages.

从网络上下载或者更新包,及其依赖的时候,使用参数`-u`,默认情况下,get使用网络取检查丢失的包,但是查找更新已经存在的包。
下载完毕之后,会在`go bin`目录下生成一个package名的二进制可执行文件。

golang源代码分析之异步通信WaitGroup

网站管理员 Published the article • 0 comments • 10 views • 2019-08-05 14:56 • 来自相关话题

Golang source code analysis asynchronous communication WaitGroup.

### 常用方法介绍

WaitGroup主要是阻塞主线程,等待一组goroutine执行完毕。通常我们会用到sync.WaitGroup中的三个函数
```
Add() // 添加计数器
Done() // 减掉计数器,等价于Add(-1)
Wait() // 阻塞,知道计数器减为0
```
上面所述当计数器 Add adds delta, which may be negative, to the WaitGroup counter.
> If the counter becomes zero, all goroutines blocked on Wait are released.
> If the counter goes negative, Add panics.

Add的参数是一个整数,可能是负数,如果计数器变为0了,所有的goroutines会被阻塞等待释放,如果计数器变成负值,则会报错。
从方法`statep, semap := wg.state()`中拿到`statep`,它是一个原子计数器。

```go
func (wg *WaitGroup) Add(delta int) {
statep, semap := wg.state()
if race.Enabled {
_ = *statep // trigger nil deref early
if delta < 0 {
// Synchronize decrements with Wait.
race.ReleaseMerge(unsafe.Pointer(wg))
}
race.Disable()
defer race.Enable()
}
state := atomic.AddUint64(statep, uint64(delta) 32)
w := uint32(state)
if race.Enabled && delta > 0 && v == int32(delta) {
// The first increment must be synchronized with Wait.
// Need to model this as a read, because there can be
// several concurrent wg.counter transitions from 0.
race.Read(unsafe.Pointer(semap))
}
if v < 0 {
panic("sync: negative WaitGroup counter")
}
if w != 0 && delta > 0 && v == int32(delta) {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
if v > 0 || w == 0 {
return
}
// This goroutine has set counter to 0 when waiters > 0.
// Now there can't be concurrent mutations of state:
// - Adds must not happen concurrently with Wait,
// - Wait does not increment waiters if it sees counter == 0.
// Still do a cheap sanity check to detect WaitGroup misuse.
if *statep != state {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
// Reset waiters count to 0.
*statep = 0
for ; w != 0; w-- {
runtime_Semrelease(semap, false)
}
}
```

#### Wait()

```go
// Wait blocks until the WaitGroup counter is zero.
func (wg *WaitGroup) Wait() {
statep, semap := wg.state()
if race.Enabled {
_ = *statep // trigger nil deref early
race.Disable()
}
for {
state := atomic.LoadUint64(statep)
v := int32(state >> 32)
w := uint32(state)
if v == 0 {
// Counter is 0, no need to wait.
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(wg))
}
return
}
// Increment waiters count.
if atomic.CompareAndSwapUint64(statep, state, state+1) {
if race.Enabled && w == 0 {
// Wait must be synchronized with the first Add.
// Need to model this is as a write to race with the read in Add.
// As a consequence, can do the write only for the first waiter,
// otherwise concurrent Waits will race with each other.
race.Write(unsafe.Pointer(semap))
}
runtime_Semacquire(semap)
if *statep != 0 {
panic("sync: WaitGroup is reused before previous Wait has returned")
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(wg))
}
return
}
}
}
``` 查看全部
Golang source code analysis asynchronous communication WaitGroup.

### 常用方法介绍

WaitGroup主要是阻塞主线程,等待一组goroutine执行完毕。通常我们会用到sync.WaitGroup中的三个函数
```
Add() // 添加计数器
Done() // 减掉计数器,等价于Add(-1)
Wait() // 阻塞,知道计数器减为0
```
上面所述当计数器<=0之后,Add的参数不能为为负值,会出现如下错误`panic("sync: negative WaitGroup counter")`,`sync.WaitGroup`的源码在
如下两个文件中,包含源码和单元测试的源码:
```
src/sync/waitgroup.go
src/sync/waitgroup_test.go
```

### 如何使用

`sync.WaitGroup`主要配置`go func`来使用,阻塞主线程,等到`go func`执行完毕。
如下代码其实是没有任何输出的,因为主线程执行完成先于`go func`。所以里面的代码是不会被执行的。
```go
package main

func main() {

go func() {
for i := 0; i < 10; i++ {
fmt.Println("i: ", i)
}
}()
}
```

那么,我们需要如何解决这个问题呢?这里我们就需要用到`sync.WaitGroup`这个解决方案了。
```go
package main

func main() {
var wg = sync.WaitGroup{}
wg.Add(1)

go func() {
for i := 0; i < 10; i++ {
fmt.Println("i: ", i)
}
wg.Done()
}()

wg.Wait()
}
```


### 源码分析
结构体`WaitGroup`有两个字段,noCopy和state1的一个数组
```go
type WaitGroup struct {
noCopy noCopy

state1 [3]uint32
}
```

主要的方法是`Add()`和`Wait()`。函数`Done()`的实现如下,其实就是`wg.Add(-1)`。
```go
func (wg *WaitGroup) Done() {
wg.Add(-1)
}
```

#### state()

state()是一个私有方法,state()返回指向wg.state1中存储的state和sema字段的指针

```go
// state returns pointers to the state and sema fields stored within wg.state1.
func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {
if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2]
} else {
return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0]
}
}
```

#### Add()

> Add adds delta, which may be negative, to the WaitGroup counter.
> If the counter becomes zero, all goroutines blocked on Wait are released.
> If the counter goes negative, Add panics.

Add的参数是一个整数,可能是负数,如果计数器变为0了,所有的goroutines会被阻塞等待释放,如果计数器变成负值,则会报错。
从方法`statep, semap := wg.state()`中拿到`statep`,它是一个原子计数器。

```go
func (wg *WaitGroup) Add(delta int) {
statep, semap := wg.state()
if race.Enabled {
_ = *statep // trigger nil deref early
if delta < 0 {
// Synchronize decrements with Wait.
race.ReleaseMerge(unsafe.Pointer(wg))
}
race.Disable()
defer race.Enable()
}
state := atomic.AddUint64(statep, uint64(delta)<<32)
v := int32(state >> 32)
w := uint32(state)
if race.Enabled && delta > 0 && v == int32(delta) {
// The first increment must be synchronized with Wait.
// Need to model this as a read, because there can be
// several concurrent wg.counter transitions from 0.
race.Read(unsafe.Pointer(semap))
}
if v < 0 {
panic("sync: negative WaitGroup counter")
}
if w != 0 && delta > 0 && v == int32(delta) {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
if v > 0 || w == 0 {
return
}
// This goroutine has set counter to 0 when waiters > 0.
// Now there can't be concurrent mutations of state:
// - Adds must not happen concurrently with Wait,
// - Wait does not increment waiters if it sees counter == 0.
// Still do a cheap sanity check to detect WaitGroup misuse.
if *statep != state {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
// Reset waiters count to 0.
*statep = 0
for ; w != 0; w-- {
runtime_Semrelease(semap, false)
}
}
```

#### Wait()

```go
// Wait blocks until the WaitGroup counter is zero.
func (wg *WaitGroup) Wait() {
statep, semap := wg.state()
if race.Enabled {
_ = *statep // trigger nil deref early
race.Disable()
}
for {
state := atomic.LoadUint64(statep)
v := int32(state >> 32)
w := uint32(state)
if v == 0 {
// Counter is 0, no need to wait.
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(wg))
}
return
}
// Increment waiters count.
if atomic.CompareAndSwapUint64(statep, state, state+1) {
if race.Enabled && w == 0 {
// Wait must be synchronized with the first Add.
// Need to model this is as a write to race with the read in Add.
// As a consequence, can do the write only for the first waiter,
// otherwise concurrent Waits will race with each other.
race.Write(unsafe.Pointer(semap))
}
runtime_Semacquire(semap)
if *statep != 0 {
panic("sync: WaitGroup is reused before previous Wait has returned")
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(wg))
}
return
}
}
}
```

Golang source code complie, missing _Gidle redeclared in this block

网站管理员 Published the article • 0 comments • 255 views • 2019-01-31 14:57 • 来自相关话题

_Gidle redeclared in this block

When compile the golang source code, having an error message, which is _Gidle redeclared in this block

```bash
git clean -df // clean your local change
```

if you find some warning, romove it, or ```git clone``` source code again.

and then

```bash
cd src
./all.bash
```

Maybe the problem is having the same source code in your directory 查看全部
_Gidle redeclared in this block

When compile the golang source code, having an error message, which is _Gidle redeclared in this block

```bash
git clean -df // clean your local change
```

if you find some warning, romove it, or ```git clone``` source code again.

and then

```bash
cd src
./all.bash
```

Maybe the problem is having the same source code in your directory


golang如何导出excel表格?

网站管理员 Published the article • 0 comments • 960 views • 2018-08-29 10:43 • 来自相关话题

golang如何导出excel表格?
做后台或者数据统计难免需要做数据导入和导出,导出成excel,方便在本地进行数据筛选,对比处理
这个时候我们就需要用到三方库```teatleg/xlsx```了


### 安装使用

[https://github.com/tealeg/xlsx](https://github.com/tealeg/xlsx)

```bash
dep ensure --add github.com/tealeg/xlsx
```

### 文档支持

[https://godoc.org/github.com/tealeg/xlsx](https://godoc.org/github.com/tealeg/xlsx)

### For example

```go
package main

import (
"github.com/tealeg/xlsx"
"fmt"
"log"
)

func main() {
var file *xlsx.File
var sheet *xlsx.Sheet
var row *xlsx.Row
var cell *xlsx.Cell

style := xlsx.NewStyle()

fill := *xlsx.NewFill("solid", "00FF0000", "FF000000")
font := *xlsx.NewFont(20, "Verdana")
border := *xlsx.NewBorder("thin", "thin", "thin", "thin")
style.Fill = fill
style.Font = font
style.Border = border

style.ApplyFill = true
style.ApplyFont = true
style.ApplyBorder = true

file = xlsx.NewFile()
sheet, er := file.AddSheet("SheetName")
if er != nil {
log.Fatal(er)
}
row = sheet.AddRow()

cell = row.AddCell()
cell.Value = "100000"
cell.SetStyle(style)

cell = row.AddCell()
cell.Value = "test"

err := file.Save("D:\\test.xlsx")
if err != nil {
fmt.Printf(err.Error())
}
}
```

### 读取一个文件excel文件

```golang
package main

import (
"fmt"
"github.com/tealeg/xlsx"
)

func main() {
excelFileName := "/home/tealeg/foo.xlsx"
xlFile, err := xlsx.OpenFile(excelFileName)
if err != nil {
...
}
for _, sheet := range xlFile.Sheets {
for _, row := range sheet.Rows {
for _, cell := range row.Cells {
text := cell.String()
fmt.Printf("%s\n", text)
}
}
}
}
```

### 将数据写入到excel中
```golang
package main

import (
"fmt"
"github.com/tealeg/xlsx"
)

func main() {
var file *xlsx.File
var sheet *xlsx.Sheet
var row *xlsx.Row
var cell *xlsx.Cell
var err error

file = xlsx.NewFile()
sheet, err = file.AddSheet("Sheet1")
if err != nil {
fmt.Printf(err.Error())
}
row = sheet.AddRow()
cell = row.AddCell()
cell.Value = "I am a cell!"
err = file.Save("MyXLSXFile.xlsx")
if err != nil {
fmt.Printf(err.Error())
}
}
```

### 参考资料

1. [https://github.com/tealeg/xlsx](https://github.com/tealeg/xlsx)
2. [https://godoc.org/github.com/tealeg/xlsx](https://godoc.org/github.com/tealeg/xlsx) 查看全部

golang如何导出excel表格?
做后台或者数据统计难免需要做数据导入和导出,导出成excel,方便在本地进行数据筛选,对比处理
这个时候我们就需要用到三方库```teatleg/xlsx```了


### 安装使用

[https://github.com/tealeg/xlsx](https://github.com/tealeg/xlsx)

```bash
dep ensure --add github.com/tealeg/xlsx
```

### 文档支持

[https://godoc.org/github.com/tealeg/xlsx](https://godoc.org/github.com/tealeg/xlsx)

### For example

```go
package main

import (
"github.com/tealeg/xlsx"
"fmt"
"log"
)

func main() {
var file *xlsx.File
var sheet *xlsx.Sheet
var row *xlsx.Row
var cell *xlsx.Cell

style := xlsx.NewStyle()

fill := *xlsx.NewFill("solid", "00FF0000", "FF000000")
font := *xlsx.NewFont(20, "Verdana")
border := *xlsx.NewBorder("thin", "thin", "thin", "thin")
style.Fill = fill
style.Font = font
style.Border = border

style.ApplyFill = true
style.ApplyFont = true
style.ApplyBorder = true

file = xlsx.NewFile()
sheet, er := file.AddSheet("SheetName")
if er != nil {
log.Fatal(er)
}
row = sheet.AddRow()

cell = row.AddCell()
cell.Value = "100000"
cell.SetStyle(style)

cell = row.AddCell()
cell.Value = "test"

err := file.Save("D:\\test.xlsx")
if err != nil {
fmt.Printf(err.Error())
}
}
```

### 读取一个文件excel文件

```golang
package main

import (
"fmt"
"github.com/tealeg/xlsx"
)

func main() {
excelFileName := "/home/tealeg/foo.xlsx"
xlFile, err := xlsx.OpenFile(excelFileName)
if err != nil {
...
}
for _, sheet := range xlFile.Sheets {
for _, row := range sheet.Rows {
for _, cell := range row.Cells {
text := cell.String()
fmt.Printf("%s\n", text)
}
}
}
}
```

### 将数据写入到excel中
```golang
package main

import (
"fmt"
"github.com/tealeg/xlsx"
)

func main() {
var file *xlsx.File
var sheet *xlsx.Sheet
var row *xlsx.Row
var cell *xlsx.Cell
var err error

file = xlsx.NewFile()
sheet, err = file.AddSheet("Sheet1")
if err != nil {
fmt.Printf(err.Error())
}
row = sheet.AddRow()
cell = row.AddCell()
cell.Value = "I am a cell!"
err = file.Save("MyXLSXFile.xlsx")
if err != nil {
fmt.Printf(err.Error())
}
}
```

### 参考资料

1. [https://github.com/tealeg/xlsx](https://github.com/tealeg/xlsx)
2. [https://godoc.org/github.com/tealeg/xlsx](https://godoc.org/github.com/tealeg/xlsx)

golang的路由控制器框架chi

网站管理员 Published the article • 0 comments • 740 views • 2018-08-27 15:00 • 来自相关话题

### golang的路由控制器框架chi

#### 官方介绍

> chi is a lightweight, idiomatic and composable router for building Go 1.7+ HTTP services. It's especially good at helping you write large REST API services that are kept maintainable as your project grows and changes. chi is built on the new context package introduced in Go 1.7 to handle signaling, cancelation and request-scoped values across a handler chain.

> The focus of the project has been to seek out an elegant and comfortable design for writing REST API servers, written during the development of the Pressly API service that powers our public API service, which in turn powers all of our client-side applications.

> The key considerations of chi's design are: project structure, maintainability, standard http handlers (stdlib-only), developer productivity, and deconstructing a large system into many small parts. The core router github.com/go-chi/chi is quite small (less than 1000 LOC), but we've also included some useful/optional subpackages: middleware, render and docgen. We hope you enjoy it too!


#### 翻译
- Chi是一个轻量级的、惯用的、可组合的路由器,用于构建GO 1.7 + HTTP服务。它特别擅长帮助您编写大型REST API服务,这些服务在项目增长和更改时保持可维护性。chi构建在Go 1.7中引入的新上下文包之上,用于跨处理程序链处理信令、取消和请求范围值。

- 该项目的重点是寻求一种优雅和舒适的设计来编写REST API服务器,该服务器是在Pressly API服务的开发过程中编写的,该服务为我们的公共API服务提供动力,而公共API服务又为我们的所有客户端应用程序提供动力。

- Chi设计的主要考虑因素是:项目结构、可维护性、标准http处理程序(stdlib-only)、开发人员的生产力,以及将大型系统分解为许多小部件。核心路由器github.com/go-chi/chi非常小(小于1000LOC),但是我们也包括了一些有用的/可选的子包:中间件、呈现和docgen。我们希望你也喜欢它!

### 官方地址

```https://github.com/go-chi/chi```

### 如何安装

#### dep
```bash
dep ensure --add github.com/go-chi/chi
```

#### go
```bash
go get -u github.com/go-chi/chi
```

### 示例

#### hello world
```go
package main

import (
"net/http"
"github.com/go-chi/chi"
)

func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
http.ListenAndServe(":3000", r)
}
```

#### 中间件的使用

中间件起承上启下的作用,所有它的实现需要一个context把中间件拿到的值,传递下去,
比如我们需要在request中做处理,那么我们需要调用```r.WithContext(ctx)```,把request传递到server中,
同理,在response中我们仍需要```response.WriteContext(ctx)```
```go
package main

import (
"github.com/go-chi/chi"
"net/http"
"context"
"log"
"encoding/gob"
"bytes"
)

func main() {
r := chi.NewRouter()
r.Use(MyMiddleWare)
r.Get("/test", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("hello world\n"))
writer.Write([]byte("hello world\n"))
a := request.Context().Value("user")
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(a)
if err != nil {
log.Fatal(err)
}
//writer.Write([]byte(buf.Bytes()))
log.Println(a)
})
http.ListenAndServe(":3000", r)
}


func MyMiddleWare(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "user", "123")
log.Println("test")
next.ServeHTTP(w, r.WithContext(ctx))
})
}
```

#### 核心中间件

允许访问的ContentType、是否压缩、获取请求头header、心跳、无缓存、真实ip等等,当然我们也可以扩展自己的中间件

-----------------------------------------------------------------------------------------------------------
| chi/middleware Handler | description |
|:----------------------|:---------------------------------------------------------------------------------
| AllowContentType | Explicit whitelist of accepted request Content-Types |
| Compress | Gzip compression for clients that accept compressed responses |
| GetHead | Automatically route undefined HEAD requests to GET handlers |
| Heartbeat | Monitoring endpoint to check the servers pulse |
| Logger | Logs the start and end of each request with the elapsed processing time |
| NoCache | Sets response headers to prevent clients from caching |
| Profiler | Easily attach net/http/pprof to your routers |
| RealIP | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP |
| Recoverer | Gracefully absorb panics and prints the stack trace |
| RequestID | Injects a request ID into the context of each request |
| RedirectSlashes | Redirect slashes on routing paths |
| SetHeader | Short-hand middleware to set a response header key/value |
| StripSlashes | Strip slashes on routing paths |
| Throttle | Puts a ceiling on the number of concurrent requests |
| Timeout | Signals to the request context when the timeout deadline is reached |
| URLFormat | Parse extension from url and put it on request context |
| WithValue | Short-hand middleware to set a key/value on the request context |
-----------------------------------------------------------------------------------------------------------

### 路由器的设计

> Each routing method accepts a URL pattern and chain of handlers

每个路由的设计都支持URL的正则匹配和```chain```话柄,如```user/{userId}```,```test/*```等,
实例化一个chi的router ```r := chi.NewRouter()```我们就可以实现开始我们的web后端api的开发了 查看全部
### golang的路由控制器框架chi

#### 官方介绍

> chi is a lightweight, idiomatic and composable router for building Go 1.7+ HTTP services. It's especially good at helping you write large REST API services that are kept maintainable as your project grows and changes. chi is built on the new context package introduced in Go 1.7 to handle signaling, cancelation and request-scoped values across a handler chain.

> The focus of the project has been to seek out an elegant and comfortable design for writing REST API servers, written during the development of the Pressly API service that powers our public API service, which in turn powers all of our client-side applications.

> The key considerations of chi's design are: project structure, maintainability, standard http handlers (stdlib-only), developer productivity, and deconstructing a large system into many small parts. The core router github.com/go-chi/chi is quite small (less than 1000 LOC), but we've also included some useful/optional subpackages: middleware, render and docgen. We hope you enjoy it too!


#### 翻译
- Chi是一个轻量级的、惯用的、可组合的路由器,用于构建GO 1.7 + HTTP服务。它特别擅长帮助您编写大型REST API服务,这些服务在项目增长和更改时保持可维护性。chi构建在Go 1.7中引入的新上下文包之上,用于跨处理程序链处理信令、取消和请求范围值。

- 该项目的重点是寻求一种优雅和舒适的设计来编写REST API服务器,该服务器是在Pressly API服务的开发过程中编写的,该服务为我们的公共API服务提供动力,而公共API服务又为我们的所有客户端应用程序提供动力。

- Chi设计的主要考虑因素是:项目结构、可维护性、标准http处理程序(stdlib-only)、开发人员的生产力,以及将大型系统分解为许多小部件。核心路由器github.com/go-chi/chi非常小(小于1000LOC),但是我们也包括了一些有用的/可选的子包:中间件、呈现和docgen。我们希望你也喜欢它!

### 官方地址

```https://github.com/go-chi/chi```

### 如何安装

#### dep
```bash
dep ensure --add github.com/go-chi/chi
```

#### go
```bash
go get -u github.com/go-chi/chi
```

### 示例

#### hello world
```go
package main

import (
"net/http"
"github.com/go-chi/chi"
)

func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
http.ListenAndServe(":3000", r)
}
```

#### 中间件的使用

中间件起承上启下的作用,所有它的实现需要一个context把中间件拿到的值,传递下去,
比如我们需要在request中做处理,那么我们需要调用```r.WithContext(ctx)```,把request传递到server中,
同理,在response中我们仍需要```response.WriteContext(ctx)```
```go
package main

import (
"github.com/go-chi/chi"
"net/http"
"context"
"log"
"encoding/gob"
"bytes"
)

func main() {
r := chi.NewRouter()
r.Use(MyMiddleWare)
r.Get("/test", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("hello world\n"))
writer.Write([]byte("hello world\n"))
a := request.Context().Value("user")
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(a)
if err != nil {
log.Fatal(err)
}
//writer.Write([]byte(buf.Bytes()))
log.Println(a)
})
http.ListenAndServe(":3000", r)
}


func MyMiddleWare(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "user", "123")
log.Println("test")
next.ServeHTTP(w, r.WithContext(ctx))
})
}
```

#### 核心中间件

允许访问的ContentType、是否压缩、获取请求头header、心跳、无缓存、真实ip等等,当然我们也可以扩展自己的中间件

-----------------------------------------------------------------------------------------------------------
| chi/middleware Handler | description |
|:----------------------|:---------------------------------------------------------------------------------
| AllowContentType | Explicit whitelist of accepted request Content-Types |
| Compress | Gzip compression for clients that accept compressed responses |
| GetHead | Automatically route undefined HEAD requests to GET handlers |
| Heartbeat | Monitoring endpoint to check the servers pulse |
| Logger | Logs the start and end of each request with the elapsed processing time |
| NoCache | Sets response headers to prevent clients from caching |
| Profiler | Easily attach net/http/pprof to your routers |
| RealIP | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP |
| Recoverer | Gracefully absorb panics and prints the stack trace |
| RequestID | Injects a request ID into the context of each request |
| RedirectSlashes | Redirect slashes on routing paths |
| SetHeader | Short-hand middleware to set a response header key/value |
| StripSlashes | Strip slashes on routing paths |
| Throttle | Puts a ceiling on the number of concurrent requests |
| Timeout | Signals to the request context when the timeout deadline is reached |
| URLFormat | Parse extension from url and put it on request context |
| WithValue | Short-hand middleware to set a key/value on the request context |
-----------------------------------------------------------------------------------------------------------

### 路由器的设计

> Each routing method accepts a URL pattern and chain of handlers

每个路由的设计都支持URL的正则匹配和```chain```话柄,如```user/{userId}```,```test/*```等,
实例化一个chi的router ```r := chi.NewRouter()```我们就可以实现开始我们的web后端api的开发了


golang websocket解决跨域问题

网站管理员 Published the article • 0 comments • 1590 views • 2018-08-24 16:43 • 来自相关话题

### golang websocket解决跨域问题

error: request origin not allowed by Upgrader.CheckOrigin
修改如下配置即可,CheckOrigin return true

```golang
var upgrade = websocket.Upgrader{
// cross origin domain
CheckOrigin: func(r *http.Request) bool {
return true
},
}
```

### 源码分析

在```github.com/gorilla/websocket/server.go:49```行中有这么个定义:```CheckOrigin func(r *http.Request) bool```

这是一个回调函数,当```CheckOrigin``` return true的时候,支持websocket跨域,否者就需要在同一个域名下了。

```golang
type Upgrader struct {
...
// CheckOrigin returns true if the request Origin header is acceptable. If
// CheckOrigin is nil, the host in the Origin header must not be set or
// must match the host of the request.
CheckOrigin func(r *http.Request) bool
...
}
```

在127行有如下判断,如果CheckOrigin非空,则```checkSameOrigin```, 检测是否是请求的是相同的host

```golang
checkOrigin := u.CheckOrigin
if checkOrigin == nil {
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
checkOrigin = checkSameOrigin
}
if !checkOrigin(r) {
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
}
```


### golang websocket 小案例

```golang
package main

import (
"github.com/gorilla/websocket"
"net/http"
"fmt"
"log"
)

// 这是支持跨域
var upgrade = websocket.Upgrader{
// cross origin domain
CheckOrigin: func(r *http.Request) bool {
return true
},
}

func main() {
http.HandleFunc("/echo", echo)
http.ListenAndServe("localhost:8080", nil)
log.Fatal("start")
}

// 接收数据并返回
func echo(w http.ResponseWriter, r *http.Request) {
c, err := upgrade.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
}

for {
mt, message, _ := c.ReadMessage()
fmt.Println(mt, string(message))
c.WriteMessage(mt, append([]byte("hello "), message[:]...))
}

}
```

### js测试,并与websocket进行通讯案例

```js
// 创建一个websocket实例
var ws = new WebSocket("ws://localhost:8080/echo");
// 发送数据 open connection
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
// 接收数据
ws.onmessage = function(evt) {
console.log("Received Message: " + evt.data);
ws.close();
};
// 关闭websocket连接close connection
ws.onclose = function(evt) {
console.log("Connection closed.");
```

### 参考

1. [https://blog.csdn.net/wangzhan ... 03532](https://blog.csdn.net/wangzhan ... 603532)
2. [https://github.com/gorilla/websocket](https://github.com/gorilla/websocket) 查看全部

### golang websocket解决跨域问题

error: request origin not allowed by Upgrader.CheckOrigin
修改如下配置即可,CheckOrigin return true

```golang
var upgrade = websocket.Upgrader{
// cross origin domain
CheckOrigin: func(r *http.Request) bool {
return true
},
}
```

### 源码分析

在```github.com/gorilla/websocket/server.go:49```行中有这么个定义:```CheckOrigin func(r *http.Request) bool```

这是一个回调函数,当```CheckOrigin``` return true的时候,支持websocket跨域,否者就需要在同一个域名下了。

```golang
type Upgrader struct {
...
// CheckOrigin returns true if the request Origin header is acceptable. If
// CheckOrigin is nil, the host in the Origin header must not be set or
// must match the host of the request.
CheckOrigin func(r *http.Request) bool
...
}
```

在127行有如下判断,如果CheckOrigin非空,则```checkSameOrigin```, 检测是否是请求的是相同的host

```golang
checkOrigin := u.CheckOrigin
if checkOrigin == nil {
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
checkOrigin = checkSameOrigin
}
if !checkOrigin(r) {
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
}
```


### golang websocket 小案例

```golang
package main

import (
"github.com/gorilla/websocket"
"net/http"
"fmt"
"log"
)

// 这是支持跨域
var upgrade = websocket.Upgrader{
// cross origin domain
CheckOrigin: func(r *http.Request) bool {
return true
},
}

func main() {
http.HandleFunc("/echo", echo)
http.ListenAndServe("localhost:8080", nil)
log.Fatal("start")
}

// 接收数据并返回
func echo(w http.ResponseWriter, r *http.Request) {
c, err := upgrade.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
}

for {
mt, message, _ := c.ReadMessage()
fmt.Println(mt, string(message))
c.WriteMessage(mt, append([]byte("hello "), message[:]...))
}

}
```

### js测试,并与websocket进行通讯案例

```js
// 创建一个websocket实例
var ws = new WebSocket("ws://localhost:8080/echo");
// 发送数据 open connection
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
// 接收数据
ws.onmessage = function(evt) {
console.log("Received Message: " + evt.data);
ws.close();
};
// 关闭websocket连接close connection
ws.onclose = function(evt) {
console.log("Connection closed.");
```

### 参考

1. [https://blog.csdn.net/wangzhan ... 03532](https://blog.csdn.net/wangzhan ... 603532)
2. [https://github.com/gorilla/websocket](https://github.com/gorilla/websocket)

golang byte array convert to string error

网站管理员 Published the article • 0 comments • 426 views • 2018-08-17 18:22 • 来自相关话题

#### golang byte array convert to string error

This day, I try to convert byte array to string, but got an error, as follows:

```
# command-line-arguments
.\md5.go:12:57: invalid operation md5.Sum(([]byte)(*originName))[:] (slice of unaddressable value)
```

This is my code
```go
package main

import (
"fmt"
"flag"
"crypto/md5"
)

func main() {
originName := flag.String("md5", "", "must set value")
flag.Parse()
fmt.Println("dm5:", string(md5.Sum([]byte(*originName))[:]))
}
```

### Solution

```go
package main

import (
"fmt"
"flag"
"crypto/md5"
"encoding/hex"
)

func main() {
// must set a var of the array byte string "s"
data := []byte("s")
fmt.Printf("md5:%x\n", md5.Sum(data))

md5Ctx := md5.New()
md5Ctx.Write(data)
value := md5Ctx.Sum(nil)
fmt.Printf("%x\n", value)
fmt.Print(hex.EncodeToString(value))
}
```

### how to covert the result of the ```md5.sum()``` return

by the function

```hex.EncodeToString(value)``` 查看全部

#### golang byte array convert to string error

This day, I try to convert byte array to string, but got an error, as follows:

```
# command-line-arguments
.\md5.go:12:57: invalid operation md5.Sum(([]byte)(*originName))[:] (slice of unaddressable value)
```

This is my code
```go
package main

import (
"fmt"
"flag"
"crypto/md5"
)

func main() {
originName := flag.String("md5", "", "must set value")
flag.Parse()
fmt.Println("dm5:", string(md5.Sum([]byte(*originName))[:]))
}
```

### Solution

```go
package main

import (
"fmt"
"flag"
"crypto/md5"
"encoding/hex"
)

func main() {
// must set a var of the array byte string "s"
data := []byte("s")
fmt.Printf("md5:%x\n", md5.Sum(data))

md5Ctx := md5.New()
md5Ctx.Write(data)
value := md5Ctx.Sum(nil)
fmt.Printf("%x\n", value)
fmt.Print(hex.EncodeToString(value))
}
```

### how to covert the result of the ```md5.sum()``` return

by the function

```hex.EncodeToString(value)```

php.composer/golang.dep/nodejs.npm/python.pip编程语言包管理器

网站管理员 Published the article • 0 comments • 378 views • 2018-08-13 17:22 • 来自相关话题

## 编程语言包管理器

### 背景

很多编程语言都有自己的依赖扩展包管理工具,由于项目越来越大,我们不得不按功能拆分模块,复用框架,借鉴优秀的三方依赖和框架,当然一些三方的sdk也可以封装成为依赖。

### 包管理工具

|语言|包管理工具|国内镜像|使用方法|
|:-:|:-:|:-:|:-:|
|php|[composer](https://getcomposer.org/)|https://www.phpcomposer.com/|https://pkg.phpcomposer.com/|
|python| [pip](https://pip.pypa.io/en/stable/installing/) | http://pypi.douban.com/simple/ |https://www.douban.com/note/302711300/ |
|nodejs|[npm](https://www.npmjs.com)|https://npm.taobao.org/|https://npm.taobao.org/|
|golang|[dep](https://github.com/golang/dep/cmd/dep)|无|https://studygolang.com/articles/10589|

### golang dep的安装

dep是golang的原型依赖管理工具,但是golang的版本必须是Go 1.7及其更高的版本

1. 安装方式很简单
```bash
go get -u github.com/golang/dep/cmd/dep
```

2. 初始化话项目资源依赖
```bash
dep init
```
3. 下载依赖且将依赖添加到项目中
```bash
dep ensure -add github.com/pkg/errors
```

4. 更新依赖
```bash
dep ensure -update
``` 查看全部
## 编程语言包管理器

### 背景

很多编程语言都有自己的依赖扩展包管理工具,由于项目越来越大,我们不得不按功能拆分模块,复用框架,借鉴优秀的三方依赖和框架,当然一些三方的sdk也可以封装成为依赖。

### 包管理工具

|语言|包管理工具|国内镜像|使用方法|
|:-:|:-:|:-:|:-:|
|php|[composer](https://getcomposer.org/)|https://www.phpcomposer.com/|https://pkg.phpcomposer.com/|
|python| [pip](https://pip.pypa.io/en/stable/installing/) | http://pypi.douban.com/simple/ |https://www.douban.com/note/302711300/ |
|nodejs|[npm](https://www.npmjs.com)|https://npm.taobao.org/|https://npm.taobao.org/|
|golang|[dep](https://github.com/golang/dep/cmd/dep)|无|https://studygolang.com/articles/10589|

### golang dep的安装

dep是golang的原型依赖管理工具,但是golang的版本必须是Go 1.7及其更高的版本

1. 安装方式很简单
```bash
go get -u github.com/golang/dep/cmd/dep
```

2. 初始化话项目资源依赖
```bash
dep init
```
3. 下载依赖且将依赖添加到项目中
```bash
dep ensure -add github.com/pkg/errors
```

4. 更新依赖
```bash
dep ensure -update
```

golang Chanel

网站管理员 Published the article • 0 comments • 550 views • 2018-08-03 11:48 • 来自相关话题

go中channel是一种重要的引用数据类型,可以看作是管道,可以通过并发的核心单元发送或者接收数据进行通讯。

操作符 ``` 查看全部


go中channel是一种重要的引用数据类型,可以看作是管道,可以通过并发的核心单元发送或者接收数据进行通讯。

操作符 ```<-```,箭头指向,即数据的流向,```没有指明方向,那么Channel就是双向的,既可以接收数据,也可以发送数据```

```go
ch <- v // 发送值到Channel ch中
v := <-ch // 从Channel ch中接收数据,并赋值给v

// 初始化 Channel ch, 类似于Slice,map的数据类型一样,channel必须先创建,然后再使用

ch := make(chan int)
```

## Channel的方向

- 不确定双向性、指定唯一单向

```go
chan T // 既可以接收数据,也可以发送数据
chan<- float64 // 只可以用来发送 float64 类型的数据
<-chan int // 只可以接收 float64 数据类型的数据
```

- ```<-总是优先和最左边的类型结合```

```go
chan<- chan int // 等价 chan<- (chan int)
chan<- <-chan int // 等价 chan<- (<-chan int)
<-chan <-chan int // 等价 <-chan (<-chan int)
chan (<-chan int)
```

## 设置Channel的容量,代表Channel缓存的大小

make初始化的时候可以设置
如果没有设置Channel的大小,或者设置为0,那么默认Channel的没有缓存的,当接收者和发送者都准备好了后,他们通讯才会发生阻塞(blocking)
如果我们设置了缓存的大小,那么就有可能不会发送阻塞了,只有buffer满了后,发送者才会阻塞,只有清空了缓存,接收者才会阻塞。
一个nil的Channel不会通讯。

```go
make(chan int, 100)
```

## 关闭Channel

内置的方法```close()```可以关闭Channel,如何检测Channel是否被关闭

```go
defer close(c) // 关闭 channel

v, ok := <-ch // 检测 channel是否被关闭
```

## select的使用,类似于```switch```

```case```用可能处理的接收语句,有可能是发送语句,还有可能是默认 ```default```

## timeout 超时处理

如果没有case需要处理,select语句就会一直阻塞着

```go
import "time"
import "fmt"
func main() {
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()

select {
case res := <-c1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout 1")
}
}
```

其实使用```time.After```函数,它是返回一个```<-chan Time```的单向channel,在指定时间发送一个当前时间给返回的channel中

## 同步

channel可以用在goroutine之间的同步

```go
import (
"fmt"
"time"
)
func worker(done chan bool) {
time.Sleep(time.Second)
// 通知任务已完成
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待任务完成
<-done
}
```

## 生产者和消费者

```go

package main

import (
"fmt"
"time"
)

func main() {

fmt.Println("show channel")
queue := make(chan int, 1)
go test.Producer(queue)
go test.Consumer(queue)
time.Sleep(1e9)
}

func Producer(queue chan<- int) {
for i := 0; i < 10; i++ {
fmt.Println("send:", i)
queue<- i+10000
}
}

func Consumer(queue <-chan int) {
for i := 0; i < 10; i++ {
v := <-queue
fmt.Println("receive:", v)
}
}

```