使用遗传算法来解决旅行商问题

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 一直对遗传算法非常好奇,它是对自然界生物进化机制的计算机模拟。可以用于对NP问题的最优化求解。 在生活中,可以被用于排班、排课、路径优化,甚至可以被用于歌曲自动编写。 2. 问题分析 遗传算法的一个典型案例是针对旅行商问题。 旅行商问题,一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。 我们从一个最简单的情况来说明这个问题。 例1:假定有A、B、C、D四个地点,坐标如上图 假定出发点是A点,那么所有的可能的路径是 ABCDA ABDCA ACBDA ACDBA ADBCA ADCBA 由于起点和终点都是A,是完全确定的;所以所有可能的路径就是B、C、D的全排列 共有n!中情况,此处n为3,共有6种情况 我们实际是在解空间中寻找最优解。 当n=20时 20!= 2,432,902,008,176,640,000 当n=21时,解空间的大小已经溢出了int64所能存储的空间 解空间相当大,以至于遍历一遍几乎是不大可能的 3. 遗传算法如何寻找最优解 事实上,遗传算法得到有可能得到最优解,也有可能得到次优解。 下面是遗传算法的完整过程 3.1 编码 让我们再回到例1,由于只有6种情况,那么解空间的取值范围就是 [0, 5] 假定如下的对应关系 BCD – 0 – 000 BDC – 1 – 001 CBD – 2 – 010 CDB – 3 – 011 DBC – 4 – 100 DCB – 5 – 101 解被转换为数值以后,就可以使用2进制进行表示 ...

February 19, 2019 · 1 min

玩转consul(1)--watch机制探究

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 consul 经常被用于服务的注册和发现,本文将带你对watch做更深入的探究 2. consul对外暴露了4种通讯接口 2.1 RPC 主要用于内部通讯Gossip/日志分发/选主等 2.2 HTTP API 服务发现/健康检查/KV存储等几乎所有功能 默认端口为8500 2.3 Consul Commands (CLI) consul命令行工具可以与consul agent进行连接,提供一部分consul的功能。 实时上Consul CLI 默认就是调用的HTTP API来与consul集群进行通讯。 可以通过配置CONSUL_HTTP_ADDR 修改Consul CLI连接的目标地址 export CONSUL_HTTP_ADDR=http://127.0.0.1:8500 详见参考资料3 2.4 DNS 仅用于服务查询 3. 服务注册&发现 3.1 服务注册 服务注册可以通过 服务注册接口 /agent/service/register 很容易做到 3.2 服务发现 3.2.1 DNS方式 $ dig @127.0.0.1 -p 8600 web.service.consul ;; QUESTION SECTION: ;web.service.consul. IN A ;; ANSWER SECTION: web.service.consul. 0 IN A 127.0.0.1 我们可以通过cosul提供的DNS接口来获取当前的service “web” 对应的可用节点(详细用法见参考资料4) DNS方式要求使用方主动进行DNS解析,是主动请求的过程。它对线上服务节点的变化,反应是延迟的。 ...

January 10, 2019 · 2 min

玩转Prometheus(2)--计算Top Percentile

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在笔者的上一篇文章 玩转Prometheus(1)–第1个例子,我提到可以用Prometheus,来统计服务的TP90的请求耗时。 那么TP90到底是什么意思?在Prometheus中,它又是如何计算的? 2. 概念–中位数/Top Percentile 2.1 中位数 中位数(Medians)统计学名词,是指将数据按大小顺序排列起来,形成一个数列,居于数列中间位置的那个数据。中位数用Me表示。当变量值的项数N为奇数时,处于中间位置的变量值即为中位数;当N为偶数时,中位数则为处于中间位置的2个变量值的平均数。 我们来看一个示例,假定有1组请求,耗时(单位毫秒)如下: [5, 8, 6, 50, 7, 10, 9, 11] 将它们按耗时,从小到大排列 [5, 6, 7, 8, 9, 10, 11, 50] 上面的示例N = 8, 中位数取(Array[3] + Array[4])/2 = (8 + 9)/2 为8.5ms 2.2 Top Percentile Top Percentile表示百分比分布统计。TP50表示50%的请求都小于等于某个值,TP90表示90%的请求小于等于某个值。 [ 5, 6, 7, 8, 9, 10, 11, 50] [12.5%, 25%, 37.5%, 50%, 62.5%, 75%, 87.5%, 100%] 上例中TP50=8ms,TP100=50, 50%的请求在8ms以内完成,100%的请求在50ms内完成 ...

January 4, 2019 · 3 min

玩转Prometheus(1)--第1个例子

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 在工作的这几年里,接触不少监控系统, Nagios、Cacti、Zabbix、Open-falcon, 今年开始在新公司使用Prometheus, 网上有文章把Prometheus 称为新一代的监控系统,我一直很好奇,它的新体现在哪儿,相比与传统的监控系统,它有什么优势。 在经过一段时间的使用以后,我觉得我有了一些体会,下面我们通过1个例子来感受一下。 Prometheus的体系架构图 应用场景 从目前各个公司的实践情况来看,Prometheus主要用于应用服务的监控,尤其是基于docker的应用服务;而像主机的运行情况(cpu使用率、内存使用率),网络设备的监控等,依然由传统的监控系统来做。 1. 模拟的应用服务 假定我们有一个web服务叫fake_service fake_server.go package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "gopkg.in/gin-gonic/gin.v1" "strconv" "strings" "time" ) var ( //HTTPReqDuration metric:http_request_duration_seconds HTTPReqDuration *prometheus.HistogramVec //HTTPReqTotal metric:http_request_total HTTPReqTotal *prometheus.CounterVec ) func init() { // 监控接口请求耗时 // HistogramVec 是一组Histogram HTTPReqDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "The HTTP request latencies in seconds.", Buckets: nil, }, []string{"method", "path"}) // 这里的"method"、"path" 都是label // 监控接口请求次数 // HistogramVec 是一组Histogram HTTPReqTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests made.", }, []string{"method", "path", "status"}) // 这里的"method"、"path"、"status" 都是label prometheus.MustRegister( HTTPReqDuration, HTTPReqTotal, ) } // /api/epgInfo/1371648200 -> /api/epgInfo func parsePath(path string) string { itemList := strings.Split(path, "/") if len(path) >= 4 { return strings.Join(itemList[0:3], "/") } return path } //Metric metric middleware func Metric() gin.HandlerFunc { return func(c *gin.Context) { tBegin := time.Now() c.Next() duration := float64(time.Since(tBegin)) / float64(time.Second) path := parsePath(c.Request.URL.Path) // 请求数加1 HTTPReqTotal.With(prometheus.Labels{ "method": c.Request.Method, "path": path, "status": strconv.Itoa(c.Writer.Status()), }).Inc() // 记录本次请求处理时间 HTTPReqDuration.With(prometheus.Labels{ "method": c.Request.Method, "path": path, }).Observe(duration) } } func DealAPI1(c *gin.Context) { time.Sleep(time.Microsecond * 10) c.Writer.Write([]byte("/api/api1")) } func DealAPI2(c *gin.Context) { time.Sleep(time.Microsecond * 20) c.Writer.Write([]byte("/api/api2")) } func main() { router := gin.Default() g := router.Group("/api") g.Use(Metric()) g.GET("api1", DealAPI1) g.GET("api2", DealAPI2) // 暴露给Prometheus router.GET("/metrics", gin.WrapH(promhttp.Handler())) router.Run(":28181") } 使用fake_client.go模拟真实用户的请求 完整代码 用法: ...

January 3, 2019 · 2 min

ES内部分享

ES内部分享 1. ES简介 ElasticSearch是Elastic公司开发的开源分布式搜索引擎 开源 分布式 全文检索 OLAP(结合kibana使用) Resful API NoSQL database 1.1 和Lucene的关系 { "name": "uf_-1wJ", "cluster_name": "UT", "cluster_uuid": "xvGp84DyQVOSxLXP43oDpA", "version": { "number": "6.2.4", "build_hash": "ccec39f", "build_date": "2018-04-12T20:37:28.497551Z", "build_snapshot": false, "lucene_version": "7.2.1", "minimum_wire_compatibility_version": "5.6.0", "minimum_index_compatibility_version": "5.0.0" }, "tagline": "You Know, for Search" } 简单而言,ES是Lucene的分布式版本,当然扩充了接口和在线分析功能 1.2 基本概念 1.2.1 索引层面 index type doc term 1.2.2 倒排索引 正常顺序 doc -> term 倒排索引 term -> doc 1.2.2 集群层面 Cluster Node Shard Segment 多种角色 Master-eligible node 2)Data node Ingest node Tribe node coordinating node ...

December 24, 2018 · 1 min

packetbeat 初探

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 packetbeat是elastic公司开发的网络抓包、嗅探以及分析工具。 和tcpdump一样,它的底层依赖libpcap。但它比tcpdump、tcpcopy功能强大的多。 它能够直接解析以下的网络协议 ICMP (v4 and v6) DHCP (v4) DNS HTTP AMQP 0.9.1 Cassandra Mysql PostgreSQL Redis Thrift-RPC MongoDB Memcache TLS 将网络包转换成JSON字符串,然后导出到以下output File Console Elasticsearch Logstash Kafka Redis 简单描述过程 event -> filter1 -> filter2 … -> output 让我非常吃惊的是它能够捕获MySQL、Redis等的二进制通讯协议,能够从捕获的记录中,清晰的看到每一条SQL查询语句,以及每一条Redis命令 2. 我们能拿它做什么? 据笔者的了解。 以前做线上的流量复制和重放,大致有这么几种方法 (1) 使用tcpcopy (2) 在服务中引起流量复制模块 (3) 服务打印特殊格式的日志 供后期解析,并做重放 (1)是二进制数据流,人无法阅读,(2)、(3)对服务有入侵,不够友好。 JSON格式的数据,对程序优化,对人来说阅读的障碍也不大。个人认为是个不错的选择 有了这些捕获的数据,我们可以用来做 线上排障 服务功能测试/压力测试 对请求(HTTP请求,MySQL、Redis请求等)进行统计分析,为服务优化提供必要的数据支持。 3. 安装&配置&使用 3.1 安装 见参考资料4 ...

December 17, 2018 · 2 min

elasticsearch中自定义doc的路由(routing)规则

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 前几天有人在群里问,es是否可以指定某个字段为路由值。ES自定义的doc的路由,不过es的操作是,在写入一个doc时,指定doc的routing key。 2. 例子 2.1 创建一个新的index PUT /my_index2 { "settings": { "index": { "number_of_shards": 2, "number_of_replicas": 1 } } } index只有2个shard,shard 0和 shard 1 2.2 设置mapping PUT /my_index2/_mapping/student { "_routing": { "required": true }, "properties": { "name": { "type": "keyword" }, "age": { "type": "integer" } } } 2.3 指定doc路由 PUT /my_index2/student/1?routing=key1 { "name":"n1", "age":10 } PUT /my_index2/student/2?routing=key1 { "name":"n2", "age":10 } PUT /my_index2/student/3?routing=key1 { "name":"n3", "age":10 } 上面的3条命令会使得doc1、2、3放置在同一个shard上 shard 0 ...

December 12, 2018 · 1 min

聊聊RAFT的一个实现(4)–NOPCommand

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在我的文章聊聊RAFT的一个实现(3) 我曾经提到NOPCommand也是一种LogEntry,它会在集群中被分发。那么它有什么作用呢? 2. NOPCommand 我们依然使用raft paper 5.4 Safety 图8来说明这个问题 2.1 场景1 如图所示,集群中有S1 ~ S5,5个节点,在时间点(A),S1是leader, 它接收到外部的指令,生成logIndex 2的日志,并复制到S2。在时间点(B)S1奔溃了,S5在term 3通过S3、S4和自己的选票赢得选举,称为leader。它从客户端接收到一条不一样的日志条目放在logIndex 2。然后到时间点(C1), S5又崩溃了;S1重新启动,被选举为leader, 开始复制日志LogEntry{Term:2, Index:2} 到S3, 此时已经达到了提交点。LogEntry{Term:2, Index:2} 已经被复制到majority,可以向状态机中写入数据了。但是接下来时间点(D1) S1可能再次崩溃,S5重新启动,S5可以重新被选举成功(通过S3、S4以及它自己的选票)。然后复制日志LogEntry{Term:3, Index:2}到S2、S3、S4。 如果事情进展按上面的情况发生, 那么显然违反了raft论文所要求 Leader Completeness和State Machine Safety 领导人完全特性–如果某个日志条目在某个任期号中已经被提交,那么这个条目必然出现在更大任期号的所有领导人中(5.4 节) 状态机安全特性–如果一个领导人已经在给定的索引值位置的日志条目应用到状态机中,那么其他任何的服务器在这个索引位置不会提交一个不同的日志(5.4.3 节) 2.2 场景2 为了解决这个问题, raft引入了空命令NOPCommand Upon election: send initial empty AppendEntries RPCs (heartbeat) to each server; 一旦选举为leader, leader应该立马发送一个空命令给其它所有节点。 ...

December 4, 2018 · 2 min

聊聊raft的一个实现(3)--commit

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 我在 聊聊raft的一个实现(2) 中的文末曾提到 只要client等到leader完成Commit动作。即使后续leader发生变更或部分节点崩溃,raft协议可以保证,client所提交的改动依然有效。 本文将举个例子,来说明一下。 2. 示例 如下图,假定cluster中有S1、S2、S3、S4、S5共5个节点,每个方格表示一条日志,方格中的数字是日志对应的term, 当前的leader是S1 commitIndex表示当前已经提交的日志,也就是成功同步到majority的日志位置(LogIndex)的最大值。 至少有3个node包含了LogIndex1、2、3,因此commitIndex的值是3 参看raft/server.go的processAppendEntriesResponse 函数了解commitIndex的计算方法 // Determine the committed index that a majority has. var indices []uint64 indices = append(indices, s.log.currentIndex()) for _, peer := range s.peers { indices = append(indices, peer.getPrevLogIndex()) } sort.Sort(sort.Reverse(uint64Slice(indices))) // We can commit up to the index which the majority of the members have appended. commitIndex := indices[s.QuorumSize()-1] committedIndex := s.log.commitIndex if commitIndex > committedIndex { // leader needs to do a fsync before committing log entries s.log.sync() s.log.setCommitIndex(commitIndex) s.debugln("commit index ", commitIndex) } 想象一个最糟糕的场景S1、S2,因为某种原因同时崩溃了。在goraft中,节点即使崩溃,也不会从peers中删除,也就是说cluster中节点的数目没有发生变化,要想保证剩下的节点能够正常选出leader,节点的数量不能少于3(集群节点总数/2 + 1)。 ...

December 1, 2018 · 1 min

如何在Golang中制造stack overflow 故障

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 大家都知道golang的栈的动态增长的,并且是放在堆上的,理论上可以相当的大,那么怎么才能制造一个stack overflow 故障呢? 其实只要人为的加入循环引用就能做到 package main import ( //"github.com/json-iterator/go" "encoding/json" ) type A struct { ElementB *B } type B struct { ElementA *A } func main(){ a := A{} b := B{} a.ElementB = &b b.ElementA = &a //var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Marshal(a) } 栈溢出,程序奔溃,并输出 ╰─$ go run test_stackoverflow2.go runtime: goroutine stack exceeds 1000000000-byte limit fatal error: stack overflow runtime stack: runtime.throw(0x10e2e65, 0xe) /usr/local/Cellar/go/1.11.2/libexec/src/runtime/panic.go:608 +0x72 runtime.newstack() /usr/local/Cellar/go/1.11.2/libexec/src/runtime/stack.go:1008 +0x729 runtime.morestack() /usr/local/Cellar/go/1.11.2/libexec/src/runtime/asm_amd64.s:429 +0x8f goroutine 1 [running]: encoding/json.(*structEncoder).encode(0xc0000642d0, 0xc00007e000, 0x10c8d40, 0xc00000c030, 0x199, 0x100) 看来默认栈的最大大小也就1GB。 这个例子其实源于真实的线上故障,经过测试无论是标准的Json库,或是jsoniter都无法避免此问题。 另外如果Golang编译器,或者Marshal函数能够对递归的深度做出判断,超过一定深度就报错,在栈溢出前,抛出err,避免栈溢出,程序崩溃 后记 已经给json-iterator/go 提了issue,并且提了pull request。 有需要也可以直接使用我个改进库vearne/json-iterator-go 默认递归的最大层级限制是1000 ...

November 30, 2018 · 1 min