每日新闻

每日新闻

GoCN每日新闻资讯
有问必答

有问必答

Go相关的问题,技术相关的问题
文章分享

文章分享

技术文章分享,让知识传播给更多的人
招聘应聘

招聘应聘

为Gopher服务的招聘应聘平台

【Go】高效截取字符串的一些思考

文章分享qiyin 发表了文章 • 0 个评论 • 688 次浏览 • 2019-10-31 08:33 • 来自相关话题

原文链接: ...查看全部

原文链接:https://blog.thinkeridea.com/201910/go/efficient_string_truncation.html

最近我在 Go Forum 中发现了 String size of 20 character 的问题,“hollowaykeanho” 给出了相关的答案,而我从中发现了截取字符串的方案并非最理想的方法,因此做了一系列实验并获得高效截取字符串的方法,这篇文章将逐步讲解我实践的过程。

字节切片截取

这正是 “hollowaykeanho” 给出的第一个方案,我想也是很多人想到的第一个方案,利用 go 的内置切片语法截取字符串:

s := "abcdef"
fmt.Println(s[1:4])

我们很快就了解到这是按字节截取,在处理 ASCII 单字节字符串截取,没有什么比这更完美的方案了,中文往往占多个字节,在 utf8 编码中是3个字节,如下程序我们将获得乱码数据:

s := "Go 语言"
fmt.Println(s[1:4])

杀手锏 - 类型转换 []rune

hollowaykeanho” 给出的第二个方案就是将字符串转换为 []rune,然后按切片语法截取,再把结果转成字符串。

s := "Go 语言"
rs := []rune(s)
fmt.Println(strings(rs[1:4]))

首先我们得到了正确的结果,这是最大的进步。不过我对类型转换一直比较谨慎,我担心它的性能问题,因此我尝试在搜索引擎和各大论坛查找答案,但是我得到最多的还是这个方案,似乎这已经是唯一的解。

我尝试写个性能测试评测它的性能:

package benchmark

import (
"testing"
)

var benchmarkSubString = "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
var benchmarkSubStringLength = 20

func SubStrRunes(s string, length int) string {
if utf8.RuneCountInString(s) > length {
rs := []rune(s)
return string(rs[:length])
}

return s
}

func BenchmarkSubStrRunes(b *testing.B) {
for i := 0; i < b.N; i++ {
SubStrRunes(benchmarkSubString, benchmarkSubStringLength)
}
}

我得到了让我有些吃惊的结果:

goos: darwin
goarch: amd64
pkg: github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark
BenchmarkSubStrRunes-8 872253 1363 ns/op 336 B/op 2 allocs/op
PASS
ok github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark 2.120s

对 69 个的字符串截取前 20 个字符需要大概 1.3 微秒,这极大的超出了我的心里预期,我发现因为类型转换带来了内存分配,这产生了一个新的字符串,并且类型转换需要大量的计算。

救命稻草 - utf8.DecodeRuneInString

我想改善类型转换带来的额外运算和内存分配,我仔细的梳理了一遍 strings 包,发现并没有相关的工具,这时我想到了 utf8 包,它提供了多字节计算相关的工具,实话说我对它并不熟悉,或者说没有主动(直接)使用过它,我查看了它所有的文档发现 utf8.DecodeRuneInString 函数可以转换单个字符,并给出字符占用字节的数量,我尝试了如此下的实验:

package benchmark

import (
"testing"
"unicode/utf8"
)

var benchmarkSubString = "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
var benchmarkSubStringLength = 20

func SubStrDecodeRuneInString(s string, length int) string {
var size, n int
for i := 0; i < length && n < len(s); i++ {
_, size = utf8.DecodeRuneInString(s[n:])
n += size
}

return s[:n]
}

func BenchmarkSubStrDecodeRuneInString(b *testing.B) {
for i := 0; i < b.N; i++ {
SubStrDecodeRuneInString(benchmarkSubString, benchmarkSubStringLength)
}
}

运行它之后我得到了令我惊喜的结果:

goos: darwin
goarch: amd64
pkg: github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark
BenchmarkSubStrDecodeRuneInString-8 10774401 105 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark 1.250s

较 []rune 类型转换效率提升了 13倍,消除了内存分配,它的确令人激动和兴奋,我迫不及待的回复了 “hollowaykeanho” 告诉他我发现了一个更好的方法,并提供了相关的性能测试。

我有些小激动,兴奋的浏览着论坛里各种有趣的问题,在查看一个问题的帮助时 (忘记是哪个问题了-_-||) ,我惊奇的发现了另一个思路。

良药不一定苦 - range 字符串迭代

许多人似乎遗忘了 range 是按字符迭代的,并非字节。使用 range 迭代字符串时返回字符起始索引和对应的字符,我立刻尝试利用这个特性编写了如下用例:

package benchmark

import (
"testing"
)

var benchmarkSubString = "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
var benchmarkSubStringLength = 20

func SubStrRange(s string, length int) string {
var n, i int
for i = range s {
if n == length {
break
}

n++
}

return s[:i]
}

func BenchmarkSubStrRange(b *testing.B) {
for i := 0; i < b.N; i++ {
SubStrRange(benchmarkSubString, benchmarkSubStringLength)
}
}

我尝试运行它,这似乎有着无穷的魔力,结果并没有令我失望。

goos: darwin
goarch: amd64
pkg: github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark
BenchmarkSubStrRange-8 12354991 91.3 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark 1.233s

它仅仅提升了13%,但它足够的简单和易于理解,这似乎就是我苦苦寻找的那味良药。

如果你以为这就结束了,不、这对我来只是探索的开始。

终极时刻 - 自己造轮子

喝了 range 那碗甜的腻人的良药,我似乎冷静下来了,我需要造一个轮子,它需要更易用,更高效。

于是乎我仔细观察了两个优化方案,它们似乎都是为了查找截取指定长度字符的索引位置,如果我可以提供一个这样的方法,是否就可以提供用户一个简单的截取实现 s[:strIndex(20)] ,这个想法萌芽之后我就无法再度摆脱,我苦苦思索两天来如何来提供易于使用的接口。

之后我创造了 exutf8.RuneIndexInString 和 exutf8.RuneIndex 方法,分别用来计算字符串和字节切片中指定字符数量结束的索引位置。

我用 exutf8.RuneIndexInString 实现了一个字符串截取测试:

package benchmark

import (
"testing"
"unicode/utf8"

"github.com/thinkeridea/go-extend/exunicode/exutf8"
)

var benchmarkSubString = "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
var benchmarkSubStringLength = 20

func SubStrRuneIndexInString(s string, length int) string {
n, _ := exutf8.RuneIndexInString(s, length)
return s[:n]
}

func BenchmarkSubStrRuneIndexInString(b *testing.B) {
for i := 0; i < b.N; i++ {
SubStrRuneIndexInString(benchmarkSubString, benchmarkSubStringLength)
}
}

尝试运行它,我对结果感到十分欣慰:

goos: darwin
goarch: amd64
pkg: github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark
BenchmarkSubStrRuneIndexInString-8 13546849 82.4 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark 1.213s

性能较 range 提升了 10%,让我很欣慰可以再次获得新的提升,这证明它是有效的。

它足够的高效,但是却不够易用,我截取字符串需要两行代码,如果我想截取 10~20之间的字符就需要4行代码,这并不是用户易于使用的接口,我参考了其它语言的 sub_string 方法,我想我应该也设计一个这个样的接口给用户。

exutf8.RuneSubString 和 exutf8.RuneSub 是我认真思索后编写的方法:

func RuneSubString(s string, start, length int) string

它有三个参数:

  • s : 输入的字符串
  • start : 开始截取的位置,如果 start 是非负数,返回的字符串将从 string 的 start 位置开始,从 0 开始计算。例如,在字符串 “abcdef” 中,在位置 0 的字符是 “a”,位置 2 的字符串是 “c” 等等。 如果 start 是负数,返回的字符串将从 string 结尾处向前数第 start 个字符开始。 如果 string 的长度小于 start,将返回空字符串。
  • length:截取的长度,如果提供了正数的 length,返回的字符串将从 start 处开始最多包括 length 个字符(取决于 string 的长度)。 如果提供了负数的 length,那么 string 末尾处的 length 个字符将会被省略(若 start 是负数则从字符串尾部算起)。如果 start 不在这段文本中,那么将返回空字符串。 如果提供了值为 0 的 length,返回的子字符串将从 start 位置开始直到字符串结尾。

我为他们提供了别名,根据使用习惯大家更倾向去 strings 包寻找这类问题的解决方法,我创建了exstrings.SubString 和 exbytes.Sub 作为更易检索到的别名方法。

最后我需要再做一个性能测试,确保它的性能:

package benchmark

import (
"testing"

"github.com/thinkeridea/go-extend/exunicode/exutf8"
)

var benchmarkSubString = "Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。"
var benchmarkSubStringLength = 20

func SubStrRuneSubString(s string, length int) string {
return exutf8.RuneSubString(s, 0, length)
}

func BenchmarkSubStrRuneSubString(b *testing.B) {
for i := 0; i < b.N; i++ {
SubStrRuneSubString(benchmarkSubString, benchmarkSubStringLength)
}
}

运行它,不会让我失望:

goos: darwin
goarch: amd64
pkg: github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark
BenchmarkSubStrRuneSubString-8 13309082 83.9 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/thinkeridea/go-extend/exunicode/exutf8/benchmark 1.215s

虽然相较 exutf8.RuneIndexInString 有所下降,但它提供了易于交互和使用的接口,我认为这应该是最实用的方案,如果你追求极致仍然可以使用 exutf8.RuneIndexInString,它依然是最快的方案。

总结

当看到有疑问的代码,即使它十分的简单,依然值得深究,并不停的探索它,这并不枯燥和乏味,反而会有极多收获。

从起初 []rune 类型转换到最后自己造轮子,不仅得到了16倍的性能提升,我还学习了utf8包、加深了range 遍历字符串的特性 以及为 go-extend 仓库收录了多个实用高效的解决方案,让更多 go-extend 的用户得到成果。

go-extend 是一个收录实用、高效方法的仓库,读者们如果好的函数和通用高效的解决方案,期待你们不吝啬给我发送 Pull request,你也可以使用这个仓库加快功能实现及提升性能。

转载:

本文作者: 戚银(thinkeridea

本文链接: https://blog.thinkeridea.com/201910/go/efficient_string_truncation.html

版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处!

DevOps 从理论到实践指南

文章分享CORNERSTONE 发表了文章 • 0 个评论 • 651 次浏览 • 2019-10-30 15:40 • 来自相关话题

什么是 DevOps ...查看全部
什么是 DevOps


如今 DevOps 已经成为一个流行词,很多公司都在说自己在做 DevOps,但是每个人、每家公司理解的 DevOps 又不尽相同,从 DevOps 诞生的第一天起,如何定义 DevOps 就是一个争论不休的话题。
这篇文章,CORNERSTONE认为基本诠释了 DevOps 的定义:DevOps 是什么不是什么
如果你没有耐心把这篇文章看完,维基百科还给出了一个太长不读版:
DevOps (a clipped compound of “development” and “operations”) is a software development and delivery process that emphasizes communication and collaboration between product management, software development, and operations professionals.It seeks to automate the process of software integration, testing, deployment, and infrastructure changes by establishing a culture and environment where building, testing, and releasing software can happen rapidly, frequently, and more reliably.
归纳成三点:
  • DevOps 是一种强调沟通与协作的软件交付过程。它包括产品管理,软件开发及运营等各个方面。
  • DevOps 自动化软件集成,测试,部署以及基础设施的变更。
  • 它的目标是建立一种文化和环境,使得软件的构建、测试、交付更快,更频繁,更可靠。

DevOps 的由来
为什么要实践 DevOps
  • 更短的交付周期,生产环境部署频率越来越快,简化生产部署流程,且自动化不停机部署
  • 更高的价值,形成特性提出到运营数据、用户反馈验证的实验性交付闭环,基于实际用户反馈调整计划和需求
  • 更好的质量保障,在代码检查,功能和非功能验证,以及部署各方面建立较完善的质量保障体系,尤其是自动化测试集
  • 更高绩效的团队,包含业务,开发测试,和运维职能在内的一体化团队,以产品交付为共同目标紧密协作,共同承担责任

DevOps 在技术领域的实践
DevOps运作包括文化(全功能,自运维)和技术(自动化,度量反馈)两方面,而技术能力的改进主要关注以下六个领域:
内建质量体系
通过持续代码评审,静态分析,自动化测试,自动部署验证等手段构成一套有效的质量保障体系。
主要实践包括:
  • TDD:测试驱动开发的思想,保证代码质量和不偏离业务需求的技术实现
  • 结对编程和代码审查,依靠团队的自治性让团队成员互相监督和审查代码质量
  • 自动化测试,高自动化,且高频率运行的测试,保证测试用例质量的同时保证了交付软件的质量

持续部署


CORNERSTONE通过自动化的构建,部署过程快速频繁地将软件交付给用户,提高吞吐量;同时保障过程的安全,平滑,可视。
主要实践包括:
  • 在已经做到持续集成的情况下,引入持续部署,每次提交均会出发构建并执行部署
  • 蓝绿部署,用于实现零宕机发布新版本
  • 金丝雀发布,用于使应用发布流程具备快速试错的能力

持续监控


CORNERSTONE持续对运行环境在系统,应用层面进行监控,及时发现风险或问题,保障系统运行的稳定性。
主要实践包括:
  • 监控预警,在项目开始初期就引入监控,让整个团队实时能够收到关于产品各个维度数据的反馈
  • 日志聚合,便于错误追踪和展示
  • 分析,利用搜集到的数据实时分析,利用分析结果指导开发进度

度量与反馈


CORNERSTONE通过对用户行为或业务指标的度量或反馈收集,为产品的决策提供依据。
主要实践包括:
  • 持续集成反馈,对代码构建质量,代码质量审查的反馈
  • 测试反馈,对软件质量,功能性的测试,给到业务的反馈
  • 运营数据反馈,新功能上线后对业务影响的反馈,用于指导业务人员提新的需求

环境管理


CORNERSTONE通过对服务器环境的定义,自动化建立和配置、更新等提高基础设施管理的效率,一致性,并更有效利用资源,可伸缩的架构,保证服务的健壮性。
主要实践包括:
  • 弹性架构,保证服务的吞吐量和具备灵活变更的能力
  • 自动化部署脚本,想胶水一样,用于解决一些工程实践不够完善的流程之间的衔接
  • 基础设施即代码,用代码定义基础设施,便于环境管理,追踪变更,以及保证环境一致性

松耦合架构
对传统应用架构进行领域组件化,服务化,提升可测试性和可部署性。
主要实践包括:
  • 采用弹性基础设施,比如公有云服务或是 PaaS(Platform as a Service) 平台
  • 构建为服务应用
  • 引入契约测试


典型DevOps的持续交付流水线全景图
软件开发全生命周期的持续优化

GoCN每日新闻(2019-10-30)

回复

每日新闻yulibaozi 发起了问题 • 1 人关注 • 0 个回复 • 3608 次浏览 • 2019-10-30 11:36 • 来自相关话题

GoCN每日新闻(2019-10-29)

回复

每日新闻傅小黑 发起了问题 • 2 人关注 • 0 个回复 • 3862 次浏览 • 2019-10-29 17:27 • 来自相关话题

GoCN每日新闻(2019-10-28)

回复

每日新闻kevin 发起了问题 • 1 人关注 • 0 个回复 • 5135 次浏览 • 2019-10-28 12:05 • 来自相关话题

gout 新版本发布,golang实现的http 流式客户端

开源程序guonaihong 发表了文章 • 0 个评论 • 345 次浏览 • 2019-10-28 09:09 • 来自相关话题

地址 ...查看全部

地址

    gout github地址

简介

    gout是go写的流式客户端,为提高编码效率而开发。特性丰富,使用方便。

技能树




GoCN每日新闻(2019-10-27)

回复

每日新闻罗发宣 发起了问题 • 1 人关注 • 0 个回复 • 6141 次浏览 • 2019-10-27 08:56 • 来自相关话题

golang workpool,工作池,线程池

每日新闻xie1xiao1jun 发表了文章 • 0 个评论 • 501 次浏览 • 2019-10-26 22:40 • 来自相关话题

## [gowp](https://github.com/xxjwxc/gowp)## golang worker pool ,线程池 , 工作池- 并发限制goroutine池。- 限制任务执行的并发性, ...查看全部


## [gowp](https://github.com/xxjwxc/gowp)

## golang worker pool ,线程池 , 工作池

- 并发限制goroutine池。
- 限制任务执行的并发性,而不是排队的任务数。
- 无论排队多少任务,都不会阻止提交任务。
- 通过队列支持

- golang 工作池公共库

### 支持最大任务数, 放到工作池里面 并等待全部完成
```
package main

import (
"fmt"
"time"

"github.com/xxjwxc/gowp/workerpool"
)

func main() {
wp := workerpool.New(10) //设置最大线程数
for i := 0; i < 20; i++ { //开启20个请求
ii := i
wp.Do(func() error {
for j := 0; j < 10; j++ { //每次打印0-10的值
fmt.Println(fmt.Sprintf("%v->\t%v", ii, j))
time.Sleep(1 * time.Second)
}
//time.Sleep(1 * time.Second)
return nil
})
}

wp.Wait()
fmt.Println("down")
}
```

### 支持错误返回
```
package main

import (
"fmt"
"time"

"github.com/xxjwxc/gowp/workerpool"
)

func main() {
wp := workerpool.New(10) //设置最大线程数
for i := 0; i < 20; i++ { //开启20个请求
ii := i
wp.Do(func() error {
for j := 0; j < 10; j++ { //每次打印0-10的值
fmt.Println(fmt.Sprintf("%v->\t%v", ii, j))
if ii == 1 {
return errors.Cause(errors.New("my test err")) //有err 立即返回
}
time.Sleep(1 * time.Second)
}

return nil
})
}

err := wp.Wait()
if err != nil {
fmt.Println(err)
}
fmt.Println("down")
}
```

### 支持判断是否完成 (非阻塞)

```
package main

import (
"fmt"
"time"

"github.com/xxjwxc/gowp/workerpool"
)

func main() {
wp := workerpool.New(5) //设置最大线程数
for i := 0; i < 10; i++ { //开启20个请求
// ii := i
wp.Do(func() error {
for j := 0; j < 5; j++ { //每次打印0-10的值
//fmt.Println(fmt.Sprintf("%v->\t%v", ii, j))
time.Sleep(1 * time.Second)
}
return nil
})

fmt.Println(wp.IsDone())
}
wp.Wait()
fmt.Println(wp.IsDone())
fmt.Println("down")
}
```

### 支持同步等待结果

```
package main

import (
"fmt"
"time"

"github.com/xxjwxc/gowp/workerpool"
)

func main() {
wp := workerpool.New(5) //设置最大线程数
for i := 0; i < 10; i++ { //开启20个请求
ii := i
wp.DoWait(func() error {
for j := 0; j < 5; j++ { //每次打印0-10的值
fmt.Println(fmt.Sprintf("%v->\t%v", ii, j))
// if ii == 1 {
// return errors.New("my test err")
// }
time.Sleep(1 * time.Second)
}

return nil
//time.Sleep(1 * time.Second)
//return errors.New("my test err")
})
}

err := wp.Wait()
if err != nil {
fmt.Println(err)
}
fmt.Println("down")
}
```

代码地址:[gowp](https://github.com/xxjwxc/gowp)
[喜欢请给星](https://github.com/xxjwxc/gowp)

抽空搭建了个国内版—The Go Playground

文章分享yeyuqiu 发表了文章 • 2 个评论 • 837 次浏览 • 2019-10-26 14:56 • 来自相关话题

1. 基于 go 最新版本 1.13.32. tab 缩进改为 4 个字符宽度访问地址为 https://play.yeyuqiu.com ...查看全部

1. 基于 go 最新版本 1.13.3
2. tab 缩进改为 4 个字符宽度

访问地址为 https://play.yeyuqiu.com

GoCN每日新闻(2019-10-26)

回复

每日新闻samurai 发起了问题 • 1 人关注 • 0 个回复 • 3936 次浏览 • 2019-10-26 12:23 • 来自相关话题

一秒让你读懂 DevOps 的本质及行业现状与趋势

文章分享CORNERSTONE 发表了文章 • 0 个评论 • 901 次浏览 • 2019-10-25 16:57 • 来自相关话题

手工编译,上传服务器文件,执行各种命令,启动和停止服务器,发现一个 BUG,改完再重复之前说的步骤...... 如今看起来啼笑皆非的重复劳动,在没有 DevOps 概念之前,全靠 IT 人员手工完成。不仅如此,DevOps 概念的提出,最初 ...查看全部

手工编译,上传服务器文件,执行各种命令,启动和停止服务器,发现一个 BUG,改完再重复之前说的步骤...... 如今看起来啼笑皆非的重复劳动,在没有 DevOps 概念之前,全靠 IT 人员手工完成。

不仅如此,DevOps 概念的提出,最初因传统模式运维之痛而生。众所周知,DevOps 一词的来自于 Development 和 Operations 的组合。字如其意,DevOps 就是让软件开发人员和运维人员更好的沟通合作,通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠。

image.png

《RightScale2018 年度云计算调查》报告数据显示,DevOps 的整体采用率从 74% 上升到了 78%,同时企业的 DevOps 采用率达到了 84%。30% 的企业正在全公司范围内采用 DevOps,同比增长 9%。

这些数字的提升不仅代表了 DevOps 可以带来实际效果的共识,也印证了 DevOps 这两年开始受到越来越多的企业重视。DevOps 概念的深入人心,离不开云计算、容器 /Docker、微服务、敏捷等相关概念和实施的成熟发展。其背后本质是企业 IT 的精益运营,以面对更快的业务试错与业务创新。


概念虽好,落地却难?

DevOps 的一个巨大好处就是可以高效交付。DevOps 理念指向“高度的自动化”,试图制定一条从开发到运行自动运行的流水线,最大程度地摆脱人工的束缚,达到企业生产力的升级。

比如,持续的集成与开发,实现从开发测试、上线运维的一体化自动流程;智能预警帮助用户自动监控集群运行状态,快速定位到问题具体发生的位置,及时通知用户以快速解决问题等。

DevOps 另外一个好处就是会改善公司组织文化、提高员工的参与感。员工们变得更高效,也更有满足和成就感。

由此可见,如果能采用 DevOps,公司就能够做更多的创新,缩短开发周期,将产品更加快速地推向市场;同时创造差别化的公司业务和价值,提高组织效率,而不是不停地修补旧问题。


技术分享

虽说 DevOps 优势多多,前途大好,企业对 DevOps 的评价也很高,但实际情况却是说的人多,做到的很少。

究其原因,在于 DevOps 并不是简单地将开发部门和运维部门合并,更是企业文化、组织结构的变革,是通过自动化的基础设施、合理的流程规范以及智能的自动运行系统测试来加强开发部门和运维部门之间的协作和沟通。

首先,这涉及到观念问题。DevOps 的最终目的是加强开发部门和运维部门之间的协作和沟通,如何把现在的文化朝 DevOps 思维模式扭转,并且在开发和运维之间找到共识?

对于传统企业,特别是大型分布式组织,在整体意义上的 DevOps 成功往往是不可能实现的。因为 DevOps 要求深层次的文化和组织变革,要改变的太多太多。这意味着大家要扔掉奉行了几十年的显规则和潜规则。你不得不告诉老部下们,大部分他们知道的和每天做的事物都已经过时了。

其次,想要为 DevOps 和应用灵活性而重塑团队,就要有打破 IT 分组壁垒的勇气,并且需要在团队成员筛选上做出艰难的决定。


困难重重,如何实现?

毫无疑问,DevOps 对组织是非常有价值的,但是需要注意的是,整个公司都需要参与到 DevOps 里才能成功。DevOps 需要高级领导层的支持,也需要和最终产品相关的所有人的参与,而不仅仅是开发和运维部门。

第一,在开发和运维之间找到共识,这需要强大的领导力来实现变革。当然,它也需要花费时间和金钱,并且需要在团队成员筛选上做出艰难的决定。

可以从小处开始,不要期望一开始就能让所有人都信服 DevOps。实际中,在特定项目的小型组织内赢得大家的支持,就赢得了会在公司其他地方帮助宣传 DevOps 的大使们,这会带来乘数效应。

第二,围绕业务系统而不是职责来组织工作,这就是 DevOps 打破 IT 分组壁垒的寓意。一个团队应该有开发人员创建代码,从用户界面到业务逻辑和数据结构,也应该有运维人员负责操作自动化和部署。团队待在一起,共同为他们的应用和系统负责。

当然,为了促进 DevOps 战略,调整考核和激励机制是必要的。应该奖励系统创建和运维的整体团队,并且根据团队工作的全部要素来确定奖励。

第三,团队需要选择最合适的工具。虽然 DevOps 是一个概念,但工具是实现 DevOps 的重要组成部分。近几年来如日中天的 CORNERSTONE 就是实现 DevOps 最合适的工具之一。

在不了解DevOps生命周期的情况下,对DevOps的理解也会片面化。我们以CORNERSTOENE一站式云端 DevOps平台为例让我们一起探讨DevOps的生命周期。


CORNERSTONE | DevOps全流程解决方案


一、持续开发:


这是DevOps生命周期中软件不断开发的阶段。与瀑布模型不同的是,软件可交付成果被分解为短开发周期的多个任务节点,在很短的时间内开发并交付。


image.png


CORNERSTONE任务模块里,任务分配给谁,就会是谁的责任。而且任务支持多责任人与子任务关联,所有的任务和状态都会体现在任务模块里,这个任务过程中的参与人员随时知道任务的状态和目前碰到的问题,可有效推进工作的解决。有了CORNERSTONE能够帮助我们追溯和监控,促进对于接任务的人员有效负起责任,并能够及时同步到信息。


二、持续测试:


在这个阶段,开发的软件将被持续地测试bug。CORNERSTONE平台覆盖完整的测试流程,可进行测试用例的编写,建立用例库,减少重复性操作,让研发团队的协作更高效,产品交付更快速。常用的两个功能为:


1)测试用例管理


通过编写测试⽤例,制定测试计划并执⾏,测试结果可直接关联到缺陷,方便对问题进行跟踪处理,实现对迭代质量的全程把控。


 Clipboard Image.png


2)缺陷管理


强大的缺陷管理与统计功能,通过分组、解决状态、优先级等列表对缺陷进行全方位记录与跟踪,同时明确缺陷责任人,及时跟进解决缺陷;同时支持导入导出功能,导出时支持任意格式,不受模板限制。


Clipboard Image.png


三、持续集成:


这是支持新功能的代码与现有代码集成的阶段。由于软件在不断地开发,更新后的代码需要不断地集成,并顺利地与系统集成,以反映对最终用户的需求更改。更改后的代码,还应该确保运行时环境中没有错误,允许我们测试更改并检查它如何与其他更改发生反应。


image.png


CORNERSTONE⽀持将持续集成的结果部署到对应的测试环境,所有部署版本在测试环境中可随时访 问,⽀持灰度发布到⽣产环境中。


四、持续部署:


它是将代码部署到生产环境的阶段。在这里,我们确保在所有服务器上正确部署代码。如果添加了任何功能或引入了新功能,那么应该准备好迎接更多的网站流量。因此,系统运维人员还有责任扩展服务器以容纳更多用户。


Clipboard Image.png


CORNERSTONE支持依赖脚本pipeline实现的DevOps,支持持续集成与自动化部署,可直接在可视化的服务器上进行操作,同时满足多种开发语言,彻底解决敏捷开发在运维层面的瓶颈,方便开发人员对项目开发生命周期进行全盘管理。


五、持续监控:


这是DevOps生命周期中非常关键的阶段,旨在通过监控软件的性能来提高软件的质量。这种做法涉及运营团队的参与,他们将监视用户活动中的错误/系统的任何不正当行为。这也可以通过使用专用监控工具来实现,该工具将持续监控应用程序性能并突出问题。


Clipboard Image.png


  CORNERSTONE嵌⼊一体化监控运维平台,实现IT环境的数字化、标准化,直接运维分析的基础,减少    人工干预,降低⼈工成本。

最后,好工具得有能人掌控才能发挥其威力。即使找到了好用的工具,也需要有熟悉这个工具链,拥有相应技能的 IT 人员来提供技术支持,才能完成实现自动化的使命。



写在最后

总体而言,DevOps 作为一种理念,推动开发和运维之间的合作,有效回应了当前的商业需求。它的实现是新观念、新工具、新技能的三者叠加。虽然实现起来还有种种问题,但 DevOps 是大势所趋,作为企业的高层和开发运维人员,对这样的变革不能视而不见。

GoCN每日新闻(2019-10-25)

回复

每日新闻cloudy 发起了问题 • 1 人关注 • 0 个回复 • 3994 次浏览 • 2019-10-25 15:34 • 来自相关话题

yiigo 4.x 版本发布

开源程序IIInsomnia 发表了文章 • 0 个评论 • 347 次浏览 • 2019-10-25 11:50 • 来自相关话题

# yiigo[![golang](https://img.shields.io/badge/Language-Go-green.svg?style=flat)](https://golang.org)[![GitHub rele ...查看全部

# yiigo

[![golang](https://img.shields.io/badge/Language-Go-green.svg?style=flat)](https://golang.org)
[![GitHub release](https://img.shields.io/github/release/IIInsomnia/yiigo.svg)](https://github.com/iiinsomnia/yiigo/releases/latest)
[![GoDoc](https://godoc.org/github.com/iiinsomnia/yiigo?status.svg)](https://godoc.org/github.com/iiinsomnia/yiigo)
[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)

The best and the most wanted package for junior gophers, probably.

## Features

- Support [MySQL](https://github.com/go-sql-driver/mysql)
- Support [PostgreSQL](https://github.com/lib/pq)
- Support [MongoDB](https://github.com/mongodb/mongo-go-driver)
- Support [Redis](https://github.com/gomodule/redigo)
- Support [Zipkin](https://github.com/openzipkin/zipkin-go)
- Support [Apollo](https://github.com/philchia/agollo)
- Use [gomail](https://github.com/go-gomail/gomail) for email sending
- Use [toml](https://github.com/pelletier/go-toml) for configuration
- Use [sqlx](https://github.com/jmoiron/sqlx) for SQL executing
- Use [gorm](https://gorm.io/) for ORM operating
- Use [zap](https://github.com/uber-go/zap) for logging

## Requirements

`Go1.11+`

## Installation

```sh
go get github.com/iiinsomnia/yiigo/v4
```

## Usage

#### Config

- `yiigo.toml`

```toml
[app]
env = "dev" # dev | beta | prod
debug = true

[apollo]
app_id = "test"
cluster = "default"
address = "127.0.0.1:8080"
cache_dir = "./"

[apollo.namespace]
# 自定义配置对应的namespace

[db]

[db.default]
driver = "mysql"
dsn = "username:password@tcp(localhost:3306)/dbname?timeout=10s&charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&loc=Local"
# dsn = "host=localhost port=5432 user=root password=secret dbname=test connect_timeout=10 sslmode=disable" # pgsql
max_open_conns = 20
max_idle_conns = 10
conn_max_lifetime = 60 # 秒

[mongo]

[mongo.default]
dsn = "mongodb://username:password@localhost:27017"
connect_timeout = 10 # 秒
pool_size = 10
max_conn_idle_time = 60 # 秒
mode = "primary" # primary | primary_preferred | secondary | secondary_preferred | nearest

[redis]

[redis.default]
address = "127.0.0.1:6379"
password = ""
database = 0
connect_timeout = 10 # 秒
read_timeout = 10 # 秒
write_timeout = 10 # 秒
pool_size = 10
pool_limit = 20
idle_timeout = 60 # 秒
wait_timeout = 10 # 秒
prefill_parallelism = 0

[log]

[log.default]
path = "app.log"
max_size = 500
max_age = 0
max_backups = 0
compress = true

[email]
host = "smtp.exmail.qq.com"
port = 25
username = ""
password = ""
```

- config usage

```go
yiigo.Env("app.env").String()
yiigo.Env("app.debug").Bool()
```

#### Apollo

```go
type QiniuConfig struct {
*yiigo.DefaultApolloConfig
BucketName string `toml:"bucket_name"`
}

var qiniu = &QiniuConfig{DefaultApolloConfig: yiigo.NewDefaultConfig("qiniu", "qiniu")}

if err := yiigo.StartApollo(qiniu); err != nil {
log.Fatal(err)
}
```

#### MySQL

```go
// default db
yiigo.DB().Get(&User{}, "SELECT * FROM `user` WHERE `id` = ?", 1)
yiigo.Orm().First(&User{}, 1)

// other db
yiigo.DB("foo").Get(&User{}, "SELECT * FROM `user` WHERE `id` = ?", 1)
yiigo.Orm("foo").First(&User{}, 1)
```

#### MongoDB

```go
// default mongodb
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

defer cancel()

yiigo.Mongo().Database("test").Collection("numbers").InsertOne(ctx, bson.M{"name": "pi", "value": 3.14159})

// other mongodb
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)

defer cancel()

yiigo.Mongo("foo").Database("test").Collection("numbers").InsertOne(ctx, bson.M{"name": "pi", "value": 3.14159})
```

#### Redis

```go
// default redis
conn, err := yiigo.Redis().Get()

if err != nil {
log.Fatal(err)
}

defer yiigo.Redis().Put(conn)

conn.Do("SET", "test_key", "hello world")

// other redis
conn, err := yiigo.Redis("foo").Get()

if err != nil {
log.Fatal(err)
}

defer yiigo.Redis("foo").Put(conn)

conn.Do("SET", "test_key", "hello world")
```

#### Zipkin

```go
reporter := yiigo.NewZipkinHTTPReporter("http://localhost:9411/api/v2/spans")

// sampler
sampler := zipkin.NewModuloSampler(1)
// endpoint
endpoint, _ := zipkin.NewEndpoint("yiigo-zipkin", "localhost")

tracer, err := yiigo.NewZipkinTracer(reporter,
zipkin.WithLocalEndpoint(endpoint),
zipkin.WithSharedSpans(false),
zipkin.WithSampler(sampler),
)

if err != nil {
log.Fatal(err)
}

client, err := tracer.HTTPClient(yiigo.WithZipkinClientOptions(zipkinHttp.ClientTrace(true)))

if err != nil {
log.Fatal(err)
}

b, err := client.Get(context.Background(), "url...",
yiigo.WithRequestHeader("Content-Type", "application/json; charset=utf-8"),
yiigo.WithRequestTimeout(5*time.Second),
)

if err != nil {
log.Fatal(err)
}

fmt.Println(string(b))
```

#### Logger

```go
// default logger
yiigo.Logger().Info("hello world")

// other logger
yiigo.Logger("foo").Info("hello world")
```

## Documentation

- [API Reference](https://godoc.org/github.com/iiinsomnia/yiigo)
- [TOML](https://github.com/toml-lang/toml)
- [Example](https://github.com/iiinsomnia/yiigo-example)

**Enjoy

GoCN每日新闻(2019-10-24)

回复

每日新闻data_worm 发起了问题 • 1 人关注 • 0 个回复 • 4707 次浏览 • 2019-10-24 14:30 • 来自相关话题

[开源] gev (支持 websocket 啦): Go 实现基于 Reactor 模式的非阻塞网络库

开源程序惜朝 发表了文章 • 0 个评论 • 363 次浏览 • 2019-10-24 11:04 • 来自相关话题

https://github.com/Allenxuxu/gev[gev](https://github.com/Allenxuxu/gev) ...查看全部

https://github.com/Allenxuxu/gev

[gev](https://github.com/Allenxuxu/gev) 是一个轻量、快速、高性能的基于 Reactor 模式的非阻塞网络库,底层并不使用 golang net 库,而是使用 epoll 和 kqueue。

现在它支持 WebSocket 啦!

支持定时任务,延时任务!

⬇️⬇️⬇️

## 特点

- 基于 epoll 和 kqueue 实现的高性能事件循环
- 支持多核多线程
- 动态扩容 Ring Buffer 实现的读写缓冲区
- 异步读写
- SO_REUSEPORT 端口重用支持
- 支持 WebSocket
- 支持定时任务,延时任务

## 性能测试

> 测试环境 Ubuntu18.04
- gev
- gnet
- eviop
- evio
- net (标准库)

### 吞吐量测试




仓库地址: https://github.com/Allenxuxu/gev