-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 57.2 KB
/
content.json
1
{"meta":{"title":"lookupman的博客","subtitle":"","description":"","author":"lookupman","url":"https://lookupman.cn","root":"/"},"pages":[{"title":"404 Not Found:该页无法显示","date":"2020-11-08T09:55:18.319Z","updated":"2020-11-08T09:55:18.319Z","comments":false,"path":"/404.html","permalink":"https://lookupman.cn/404.html","excerpt":"","text":""},{"title":"C++","date":"2020-06-08T10:41:08.000Z","updated":"2020-06-08T10:41:30.410Z","comments":true,"path":"C/index-1.html","permalink":"https://lookupman.cn/C/index-1.html","excerpt":"","text":""},{"title":"C++","date":"2020-06-08T10:40:24.000Z","updated":"2020-06-08T10:41:02.802Z","comments":true,"path":"C/index.html","permalink":"https://lookupman.cn/C/index.html","excerpt":"","text":""},{"title":"Nginx","date":"2020-10-18T09:26:46.000Z","updated":"2020-10-18T09:27:02.203Z","comments":true,"path":"Nginx/index.html","permalink":"https://lookupman.cn/Nginx/index.html","excerpt":"","text":""},{"title":"Golang","date":"2020-11-08T13:09:23.000Z","updated":"2020-11-08T13:23:39.401Z","comments":true,"path":"Golang/index.html","permalink":"https://lookupman.cn/Golang/index.html","excerpt":"","text":""},{"title":"about","date":"2021-08-03T12:35:22.859Z","updated":"2021-08-03T12:35:22.859Z","comments":false,"path":"about/index.html","permalink":"https://lookupman.cn/about/index.html","excerpt":"","text":"关于我………"},{"title":"categories","date":"2021-08-03T12:19:22.855Z","updated":"2021-08-03T12:19:22.855Z","comments":false,"path":"categories/index.html","permalink":"https://lookupman.cn/categories/index.html","excerpt":"","text":""},{"title":"","date":"2019-07-29T08:28:26.000Z","updated":"2020-06-07T09:20:59.847Z","comments":true,"path":"album/index.html","permalink":"https://lookupman.cn/album/index.html","excerpt":"","text":"图片搜集于互联网,侵权请留言,马上处理😊。"},{"title":"","date":"2019-09-09T10:52:09.000Z","updated":"2020-06-08T13:56:53.518Z","comments":true,"path":"friend/index.html","permalink":"https://lookupman.cn/friend/index.html","excerpt":"","text":"申请友链须知 原则上只和技术类博客交换,但不包括含有和色情、暴力、政治敏感的网站。 不和剽窃、侵权、无诚信的网站交换,优先和具有原创作品的网站交换。 申请请提供:站点名称、站点链接、站点描述、logo或头像(不要设置防盗链)。 排名不分先后,刷新后重排,更新信息后请留言告知。 本站不存储友链图片,如果友链图片换了无法更新。图片裂了的会替换成默认图,需要更换的请留言告知。 本站友链信息如下,申请友链前请先添加本站信息: 网站图标:https://cdn.jsdelivr.net/gh/dream-kzx/blog_image/img/avatar.jpg 网站名称:lookupman的博客 网站地址:https://lookupman.cn 网站简介:后端开发、记录成长、分享生活 加载中,稍等几秒..."},{"title":"","date":"2019-07-30T01:33:48.000Z","updated":"2020-06-07T08:24:31.269Z","comments":true,"path":"media/index.html","permalink":"https://lookupman.cn/media/index.html","excerpt":"","text":" 听听音乐 音乐播放器由mePlayer提供,布局参照网友博客所作,感谢作者的辛勤付出。更多音乐分享请查看歌单。 看看视频 ->点击以下条目开始播放视频,向下滑动查看更多"},{"title":"音乐歌单收藏","date":"2019-07-30T02:43:45.000Z","updated":"2020-06-06T06:15:46.411Z","comments":true,"path":"music/index.html","permalink":"https://lookupman.cn/music/index.html","excerpt":"","text":"--- 温馨提示:选择喜欢的音乐双击播放,由于版权原因部分不能播放。如果喜欢歌单收藏一下,去网易云都能播放哟!"},{"title":"书单","date":"2020-11-08T09:55:18.322Z","updated":"2020-11-08T09:55:18.322Z","comments":false,"path":"books/index.html","permalink":"https://lookupman.cn/books/index.html","excerpt":"","text":""},{"title":"links","date":"2021-08-03T12:18:42.061Z","updated":"2021-08-03T12:18:42.061Z","comments":true,"path":"links/index.html","permalink":"https://lookupman.cn/links/index.html","excerpt":"","text":""},{"title":"","date":"2019-11-25T06:49:08.000Z","updated":"2020-06-07T14:41:45.927Z","comments":false,"path":"self-talking/index.html","permalink":"https://lookupman.cn/self-talking/index.html","excerpt":"","text":"日常动态和感想 tips:github登录后按时间正序查看、可点赞加❤️、本插件地址..「+99次查看」 碎碎念加载中,请稍等... $.getScript(\"/js/gitalk_self.min.js\", function () { var gitalk = new Gitalk({ clientID: '46f4ecc2bac37d9774b3', clientSecret: '2e3bfb7189e25c4ab26daaa672dc5fe3c76f3c37', id: '8888', repo: 'everyday_issues', owner: 'dream-kzx', admin: \"dream-kzx\", createIssueManually: true, distractionFreeMode: false }); gitalk.render('comment-container1'); });"},{"title":"","date":"2018-11-11T00:24:49.000Z","updated":"2020-06-07T10:42:51.523Z","comments":true,"path":"message/index.html","permalink":"https://lookupman.cn/message/index.html","excerpt":"","text":"畅所欲言,有留必应"},{"title":"Repositories","date":"2020-11-08T09:55:18.324Z","updated":"2020-11-08T09:55:18.324Z","comments":false,"path":"repository/index.html","permalink":"https://lookupman.cn/repository/index.html","excerpt":"","text":""},{"title":"tags","date":"2021-08-03T12:19:31.680Z","updated":"2021-08-03T12:19:31.680Z","comments":false,"path":"tags/index.html","permalink":"https://lookupman.cn/tags/index.html","excerpt":"","text":""},{"title":"包管理工具","date":"2020-06-08T10:43:10.000Z","updated":"2020-06-08T10:43:21.089Z","comments":true,"path":"包管理工具/index.html","permalink":"https://lookupman.cn/%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7/index.html","excerpt":"","text":""},{"title":"成长","date":"2020-06-07T06:59:41.000Z","updated":"2020-10-18T09:27:05.294Z","comments":true,"path":"成长/index.html","permalink":"https://lookupman.cn/%E6%88%90%E9%95%BF/index.html","excerpt":"","text":""},{"title":"生活","date":"2020-06-07T03:39:32.000Z","updated":"2020-06-07T07:05:08.288Z","comments":true,"path":"生活/index.html","permalink":"https://lookupman.cn/%E7%94%9F%E6%B4%BB/index.html","excerpt":"","text":""}],"posts":[{"title":"go-kit中使用consul服务注册与发现、健康检测","slug":"go-kit中使用consul服务注册与发现、健康检测","date":"2021-01-03T07:50:42.000Z","updated":"2021-08-03T12:13:34.182Z","comments":true,"path":"2021/01/03/go-kit中使用consul服务注册与发现、健康检测/","link":"","permalink":"https://lookupman.cn/2021/01/03/go-kit%E4%B8%AD%E4%BD%BF%E7%94%A8consul%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0%E3%80%81%E5%81%A5%E5%BA%B7%E6%A3%80%E6%B5%8B/","excerpt":"  本章介绍在go-kit中进行使用服务发现和注册,以及健康检测","text":"  本章介绍在go-kit中进行使用服务发现和注册,以及健康检测 首先使用kit提供的库,定义对consul的访问  首先定义服务发现的客户端接口,接口需要提供3个方法,服务注册、服务注销以及服务发现。 123456789101112package discoveryimport "log"type DiscoverClient interface{ Register(serviceName, instanceId, healthCheckUrl string, instanceHost string, instancePort int, meta map[string]string, logger *log.Logger) bool DeRegister(instanceId string, logger *log.Logger) bool DiscoveryServices(serviceName string, logger *log.Logger) []interface{}}   然后定义对接口的实现,在此处使用go-kit封装的库对接口进行实现,同时在服务发现部分通过map对服务进行缓存,在服务发生变化时,通过获取consul的通知进行更新map 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139package discoveryimport ( "github.com/go-kit/kit/sd/consul" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api/watch" "log" "strconv" "sync")type KitDiscoverClient struct { consulHost string //consul的地址 consulPort int //consul的端口 client consul.Client //kit封装consul的客户端 config *api.Config //kit封装的consul配置 mutex sync.Mutex instanceMap sync.Map}func NewKitDiscoverClient(consulHost string, consulPort int) (DiscoverClient, error) { consulConfig := api.DefaultConfig() consulConfig.Address = consulHost + ":" + strconv.Itoa(consulPort) apiClient, err := api.NewClient(consulConfig) if err != nil { return nil, err } client := consul.NewClient(apiClient) return &KitDiscoverClient{ consulHost: consulHost, consulPort: consulPort, client: client, config: consulConfig, }, nil}func (consulClient *KitDiscoverClient) Register(serviceName, instanceId, healthCheckUrl string, instanceHost string, instancePort int, meta map[string]string, logger *log.Logger) bool { serviceRegistration := &api.AgentServiceRegistration{ ID: instanceId, Name: serviceName, Address: instanceHost, Port: instancePort, Meta: meta, Check: &api.AgentServiceCheck{ DeregisterCriticalServiceAfter: "30s", HTTP: "http://" + instanceHost + ":" + strconv.Itoa(instancePort) + healthCheckUrl, Interval: "15s", }, } err := consulClient.client.Register(serviceRegistration) if err != nil { log.Println("Register Service Error!") return false } log.Println("Register Service Success!") return true}func (consulClient *KitDiscoverClient) DeRegister(instanceId string, logger *log.Logger) bool { serviceRegistration := &api.AgentServiceRegistration{ ID: instanceId, } err := consulClient.client.Deregister(serviceRegistration) if err != nil { logger.Println("Deregister Service Error!") return false } log.Println("Deregister Service Success!") return true}func (consulClient *KitDiscoverClient) DiscoveryServices(serviceName string, logger *log.Logger) []interface{} { //判断服务是否已缓存 instanceList, ok := consulClient.instanceMap.Load(serviceName) if ok { return instanceList.([]interface{}) } consulClient.mutex.Lock() defer consulClient.mutex.Unlock() //加锁后在判断一次,服务是否已缓存 instanceList, ok = consulClient.instanceMap.Load(serviceName) if ok { return instanceList.([]interface{}) } //响应服务变更通知,更新服务map go func() { params := make(map[string]interface{}) params["type"] = "service" params["service"] = serviceName plan, _ := watch.Parse(params) plan.Handler = func(u uint64, i interface{}) { if i == nil { return } v, ok := i.([]*api.ServiceEntry) if !ok { return } if len(v) == 0 { consulClient.instanceMap.Store(serviceName, []interface{}{}) } var healthServices []interface{} for _, service := range v { if service.Checks.AggregatedStatus() == api.HealthPassing { healthServices = append(healthServices, service) } } consulClient.instanceMap.Store(serviceName, healthServices) } defer plan.Stop() plan.Run(consulClient.config.Address) }() //调用go-kit库向consul获取服务 entries, _, err := consulClient.client.Service(serviceName, "", false, nil) if err != nil { consulClient.instanceMap.Store(serviceName, []interface{}{}) logger.Println("Discover Service Error") return nil } instances := make([]interface{}, 0, len(entries)) for _, instance := range entries { instances = append(instances, instance) } consulClient.instanceMap.Store(serviceName, instances) return instances} 定义服务  定义服务,本文实例定义两个服务:SayHello和HealthCheck。 12345678910111213141516171819202122232425262728293031package serviceimport ( "context" "errors" "github.com/lookupman/service_discovery/config" "github.com/lookupman/service_discovery/discovery")//定义服务接口type Service interface { HealthCheck() bool SayHello() string}//实现SayHello服务接口type DiscoveryServiceImpl struct { discoveryClient discovery.DiscoverClient}func NewDiscoveryServiceImpl(discoveryClient discovery.DiscoverClient) Service { return &DiscoveryServiceImpl{discoveryClient: discoveryClient}}func (service *DiscoveryServiceImpl) HealthCheck() bool { return true}func (service DiscoveryServiceImpl) SayHello() string { return "Hello World!"} 定义服务端点(Endpoint)  endpoint是go-kit定义的一个函数类型,定义返回这个类型的函数,传给NewServer作为参数实现对http handler的注册,另外需要定义request解码和response编码的函数。 1type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error) 12345678910111213141516171819202122232425262728293031323334353637383940package endpointimport ( "context" "github.com/go-kit/kit/endpoint" "github.com/lookupman/service_discovery/service")type DiscoveryEndpoints struct { SayHelloEndpoint endpoint.Endpoint HealthCheckEndpoint endpoint.Endpoint}type SayHelloRequest struct {}type SayHelloResponse struct { Message string `json:"message"`}func MakeSayHelloEndpoint(svc service.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { message := svc.SayHello() return SayHelloResponse{Message: message}, nil }}type HealthRequest struct{}type HealthResponse struct { Status bool `json:"status"`}func MakeHealthCheckEndpoint(svc service.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { status := svc.HealthCheck() return HealthResponse{Status: status}, nil }} 定义http层handler  在这里做的是,使用go-kit的库实现对endpoint、编解码函数封装,实现http的handler 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768package transportimport ( "context" "encoding/json" "errors" "net/http" "github.com/go-kit/kit/log" "github.com/go-kit/kit/transport" kithttp "github.com/go-kit/kit/transport/http" "github.com/gorilla/mux" "github.com/lookupman/service_discovery/endpoint")var ( ErrorRequest = errors.New("invalid request parameter"))func MakeHttpHandler(ctx context.Context, endpoints endpoint.DiscoveryEndpoints, logger log.Logger) http.Handler { r := mux.NewRouter() options := []kithttp.ServerOption{ kithttp.ServerErrorHandler(transport.NewLogErrorHandler(logger)), kithttp.ServerErrorEncoder(encodeError), } r.Methods("GET").Path("/SayHello").Handler(kithttp.NewServer( endpoints.SayHelloEndpoint, decodeSayHelloRequest, encodeJsonResponse, options..., )) r.Methods("GET").Path("/health").Handler(kithttp.NewServer( endpoints.HealthCheckEndpoint, decodeHealthRequest, encodeJsonResponse, options..., )) return r}func decodeSayHelloRequest(ctx context.Context, req *http.Request) (request interface{}, err error) { return endpoint.SayHelloRequest{}, nil}func decodeHealthRequest(ctx context.Context, req *http.Request) (request interface{}, err error) { return endpoint.HealthRequest{}, nil}func encodeJsonResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", "application/json;charset=utf-8") return json.NewEncoder(w).Encode(response)}func encodeError(ctx context.Context, err error, w http.ResponseWriter) { w.Header().Set("Content-Type", "application/json;charset=utf-8") switch err { default: w.WriteHeader(http.StatusInternalServerError) } json.NewEncoder(w).Encode(map[string]interface{}{ "error": err.Error(), })} 主函数  最后是主函数,在这里绑定监听端口以及将服务注册到consul 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788package mainimport ( "context" "fmt" "github.com/lookupman/service_discovery/config" "github.com/lookupman/service_discovery/discovery" "github.com/lookupman/service_discovery/endpoint" "github.com/lookupman/service_discovery/service" "github.com/lookupman/service_discovery/transport" uuid "github.com/satori/go.uuid" "log" "net/http" "os" "os/signal" "strconv" "syscall")var logger log.Loggerfunc main() { serviceHost := "127.0.0.1" servicePort := 9999 serviceName := "SayHello" consulHost := "127.0.0.1" consulPort := 8500 ctx := context.Background() errChan := make(chan error) var discoverClient discovery.DiscoverClient discoverClient, err := discovery.NewKitDiscoverClient(consulHost, consulPort) if err != nil { log.Println("Get Consul Client failed") return } svc := service.NewDiscoveryServiceImpl(discoverClient) sayHelloEndpoint := endpoint.MakeSayHelloEndpoint(svc) healthCheckEndpoint := endpoint.MakeHealthCheckEndpoint(svc) endpoints := endpoint.DiscoveryEndpoints{ SayHelloEndpoint: sayHelloEndpoint, HealthCheckEndpoint: healthCheckEndpoint, } router := transport.MakeHttpHandler(ctx, endpoints, config.KitLogger) instanceId := serviceName + "-" + uuid.NewV4().String() go func() { config.Logger.Println("Http Server start at port:" + strconv.Itoa(servicePort)) if !discoverClient.Register(serviceName, instanceId, "/health", serviceHost, servicePort, nil, config.Logger) { config.Logger.Printf("string-service for service %s failed.", serviceName) // 注册失败,服务启动失败 os.Exit(-1) } handler := router errChan <- http.ListenAndServe(":"+strconv.Itoa(servicePort), handler) }() go func() { // 监控系统信号,等待 ctrl + c 系统信号通知服务关闭 c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() err = <-errChan //服务退出取消注册 discoverClient.DeRegister(instanceId, config.Logger) config.Logger.Println(err)} 参考文章:Go语言高并发与微服务实战","categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"}],"tags":[]},{"title":"go-kit/etcdv3库学习","slug":"go-kit-etcdv3库学习","date":"2020-12-06T12:08:01.000Z","updated":"2020-12-06T14:23:42.186Z","comments":true,"path":"2020/12/06/go-kit-etcdv3库学习/","link":"","permalink":"https://lookupman.cn/2020/12/06/go-kit-etcdv3%E5%BA%93%E5%AD%A6%E4%B9%A0/","excerpt":"  本章介绍使用go-kit的工具包etcdv3进行对etcd做服务注册和发现操作","text":"  本章介绍使用go-kit的工具包etcdv3进行对etcd做服务注册和发现操作 预备知识etcd的key-value存储和查找方式  etcd是使用key-value的形式进行存储的,与consul不同的是,etcd的查找是通过前缀进行查找的。举个例子,服务名为hellworld的服务部署了3个实例在不同的服务器上:  ”http://192.168.10.111:8888"  ”http://192.168.10.112:8888"  ”http://192.168.10.113:8888"那么在etcd服务器中存储的值一般的存储方式为:key1 = “/service/helloworld/192.168.10.111:8888” value1 = “http://192.168.10.111:8888"key2 = “/service/helloworld/192.168.10.112:8888” value2 = “http://192.168.10.112:8888"key3 = “/service/helloworld/192.168.10.113:8888” value3 = “http://192.168.10.113:8888"  那么在服务发现时,就通过查找前缀为”/service/helloworld/“的所有的key-value,etcd就会返回前缀为”/service/helloworld/“所有的值。 服务注册  go-kit提供了对etcd进行服务注册的封装的函数 123456789101112131415161718192021type ClientOptions struct { Cert string //用于SSL Key string //用于SSL CACert string //用于SSL DialTimeout time.Duration //建立连接失败的超时时间 DialKeepAlive time.Duration //存活检测时间 Username string //etcd的用户名,未在etcd中设置,则不需要 Password string //etcd的密码,未在etcd中设置,则不需要}//etcd创建客户端接口,machines为etcd的地址func NewClient(ctx context.Context, machines []string, options ClientOptions) (Client, error)type Service struct { Key string //存储在etcd的key Value string //存储在etcd的value TTL *TTLOption //心跳检测时间}//NewRegistrarfunc NewRegistrar(client Client, service Service, logger log.Logger) *Registrar 使用etcdv3进行服务注册,etcdv3的接口中封装了对etcd存活检测的操作 12345678910111213141516171819202122232425262728293031323334353637383940414243444546func main(){ etcdHost := "127.0.0.1" etcdPort := 2379 serviceHost := "127.0.0.1" servicePort := 8888 serviceName := "service/hellworld" ctx := context.Background() clientOption := etcdv3.ClientOption{ DialTimeout: 3 * time.Second, DialKeepAlive: 3* time.Second, } addr := etcdHost+":"+strconv.Itoa(etcdPort) //创建etcd客户端 client, err := etcdv3.NewClient(ctx, []string{addr}, clientOption) if err != nil{ log.Println(err) return } addr := serviceHost + ":" + strconv.Itoa(servicePort) key := string.TrimRight(serviceName, "/") + "/" + addr value := "http://" + addr //创建注册器 registrar := etcdv3.NewRegistrar(client, etcdv3.Service{Key: key, Value: value}, kitlog.NewNopLogger()) //服务注册 registrar.Registrar() errChan := make(chan error) go func(){ errChan <- http.ListenAndServe(:+strconv.Itoa(servicePort, nil)) }() go func(){ c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() err := <-errChan registrar.DeRegister()} 服务发现  etcdv3中同样封装了服务发现的接口,同时接口提供了服务变更时通知的接口 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455func main(){ etcdHost := "127.0.0.1" etcdPort := 2379 prefix := "/service/helloworld/" ctx := context.Background() clientOption := etcdv3.ClientOption{ DialTimeout: 3 * time.Second, DialKeepAlive: 3* time.Second, } addr := etcdHost+":"+strconv.Itoa(etcdPort) //创建etcd客户端 client, err := etcdv3.NewClient(ctx, []string{addr}, clientOption) if err != nil{ log.Println(err) return } instancer, err := etcdv3.NewInstancer(client, prefix, kitlog.NewNopLogger()) if err != nil{ log.Println(err) return } ch := make(chan sd.Event) //将chan注册到instancer中,当服务发生变化时,会将新的数据更新到chan go func() { instancer.Register(ch) } //获取更新的值 go func() { for event := range ch{ if event.Err != nil{ log.Println(event.Err) } fmt.Println("service url is: ", event.Instances) } }() go func(){ c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() err := <-errChan instancer.DeRegister(ch)} 自己的接口封装  为了简化以后的使用,自己封装了一个结构体,不足之处请指出 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118package commonimport ( "context" "errors" "github.com/go-kit/kit/sd" "log" "strconv" "strings" "sync" "time" kitlog "github.com/go-kit/kit/log" "github.com/go-kit/kit/sd/etcdv3")type EtcdV3DiscoverClient struct { client etcdv3.Client registrar *etcdv3.Registrar instanceMutex sync.Mutex instances map[string]*etcdv3.Instancer serviceMutex sync.Mutex serviceUrls map[string][]string}func NewEctdV3DiscoverClient(ectdHost string, ectdPort int) *EtcdV3DiscoverClient { ctx := context.Background() clientOption := etcdv3.ClientOptions{ DialTimeout: 3 * time.Second, DialKeepAlive: 3 * time.Second, } addr := ectdHost + ":" + strconv.Itoa(ectdPort) client, err := etcdv3.NewClient(ctx, []string{addr}, clientOption) if err != nil { log.Println(err) return nil } return &EtcdV3DiscoverClient{ client: client, instances: make(map[string]*etcdv3.Instancer), serviceUrls: make(map[string][]string), }}func (cli *EtcdV3DiscoverClient) Register(serviceName string, serviceHost string, servicePort int) { addr := serviceHost + ":" + strconv.Itoa(servicePort) key := strings.TrimRight(serviceName, "/") + "/" + addr value := "http://" + addr cli.registrar = etcdv3.NewRegistrar(cli.client, etcdv3.Service{Key: key, Value: value}, kitlog.NewNopLogger()) cli.registrar.Register()}func (cli *EtcdV3DiscoverClient) DeRegister() { cli.registrar.Deregister()}func (cli *EtcdV3DiscoverClient) DiscoveryServices(serviceName string) ([]string, error) { var err error prefix := strings.TrimRight(serviceName, "/") + "/" cli.instanceMutex.Lock() instancer, ok := cli.instances[serviceName] cli.instanceMutex.Unlock() if !ok { instancer, err = etcdv3.NewInstancer(cli.client, prefix, kitlog.NewNopLogger()) if err != nil { return nil, err } cli.serviceMutex.Lock() cli.instances[serviceName] = instancer cli.serviceMutex.Unlock() cli.watch(instancer, serviceName) return cli.client.GetEntries(prefix) } cli.serviceMutex.Lock() serviceUrl, ok := cli.serviceUrls[serviceName] cli.serviceMutex.Unlock() if !ok { return nil, errors.New("no service url") } return serviceUrl, nil}func (cli *EtcdV3DiscoverClient) watch(instancer *etcdv3.Instancer , serviceName string) { ch := make(chan sd.Event) go func(){ instancer.Register(ch) }() go func() { for event := range ch { if event.Err != nil { } cli.serviceMutex.Lock() cli.serviceUrls[serviceName] = event.Instances log.Println(time.Now()) cli.serviceMutex.Unlock() } instancer.Deregister(ch) cli.instanceMutex.Lock() delete(cli.instances, serviceName) cli.instanceMutex.Unlock() }()} 参考文章:","categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"}],"tags":[]},{"title":"Linux安装Go语言","slug":"Linux安装Go语言","date":"2020-11-14T08:16:40.000Z","updated":"2020-11-14T09:03:19.625Z","comments":true,"path":"2020/11/14/Linux安装Go语言/","link":"","permalink":"https://lookupman.cn/2020/11/14/Linux%E5%AE%89%E8%A3%85Go%E8%AF%AD%E8%A8%80/","excerpt":"本章介绍在linux下安装go语言,以及一些环境变量的配置,本文的运行环境为ubuntu系统","text":"本章介绍在linux下安装go语言,以及一些环境变量的配置,本文的运行环境为ubuntu系统 说明在ubuntu中,可以直接使用apt-get命令来安装golang,但是由于apt源中的golang通常不是最新的,所以本章使用下载的二进制包进行安装golang。 获取golang进入Golang官网,点击Download Go,进入下载界面,选择Linux版本tar.gz文件,右键选择复制链接地址。 进入ubuntu的bash界面,输入命令,下载文件 1wget https://golang.org/dl/go1.15.5.linux-amd64.tar.gz 第二步解压缩包 1tar -zxvf go1.15.5.linux-amd64.tar.gz 设置环境变量对于解压后的压缩包,设置相应的环境变量之后即可运行Golang,环境变量设置如下. 123456789101112131415161718#进入根目录> cd ~/#编辑.bashrc文件> sudo vim .bashrc#在文件最下面,设置Golang相关环境变量export GOPROXY=https://goproxy.cn,directexport GO111MODULE=onexport GOROOT= "解压压缩包路径"export GOPATH= "自定义路径,用于保存一些第三方包"PATH=$GOROOT/bin:$GOPATH/bin:$PATH#保存.bashrc文件,执行如下命令即可source ./bashrc#测试是否配置成功> go versiongo version go1.15.5 linux/amd64 环境变量的描述 GOPROXY:由于国内的网络访问外网较慢,所以在下载第三方包,会出现超时的情况,所以国内大佬们搭建了库的镜像,该配置就是为了配置代理。 GO111MODULE:启用go module,go module是在go1.11版本加入的,一种包管理的工具 GOROOT:这个就不解释了 GOPATH:第三方包下载保存的路径,包括了go module管理包的路径 解决一些小问题在需要使用sudo命令进行编译运行golang时,已设置的环境变量会被置为默认,比如gopath路径被改变,程序所需要依赖的包在编译时就需要重新下载。可以通过如下配置进行设置,在sudo的情况下环境变量不被清空。 1234567#进入如下文件> sudo visudo /etc/sudoers#添加如下命令即可Defaults !env_reset#同时需要注释掉Defaults secure_path="****" 参考文章:","categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"}],"tags":[]},{"title":"gorm学习记录","slug":"gorm学习记录","date":"2020-11-08T08:29:08.000Z","updated":"2020-11-08T16:07:01.774Z","comments":true,"path":"2020/11/08/gorm学习记录/","link":"","permalink":"https://lookupman.cn/2020/11/08/gorm%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","excerpt":"","text":"安装相应的库  在命令行输入如下命令即可 12go get -u gorm.io/gormgo get -u gorm.io/driver/mysql 连接数据库  mysql连接连接方式如下,其他数据库的连接方式以及高级配置请看官方文档 123456dsn := "root:password@tcp(127.0.0.1:3306)/dbName?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil{ log.Fatal(err) return} CRUD接口结构体字段标签主键  在与数据库关联时,会有一些特定的约定,比如主键。在gorm中通过标签进行标识,一般情况下,默认将ID作为表的主键,不过可以使用primaryKey标签进行标识 12345678910type User struct{ ID string //默认ID字段为主键 Name string}type User struct{ ID string UUID string `gorm:"primaryKey"` //使用标签primaryKey将UUID标识为主键 Name string} 表名  通过结构体映射的数据库表明,默认为数据库的名字加上’s’,如User则在数据库中对应的表为users、UserAdmin为user_admins,可以通过定义TableName()方法来,修改映射的数据库名 123func(User) TableName() string{ return "admin_user" //如果字符串为小驼峰或大驼峰,则会转换为全小写} 关联数据库表  要对数据进行增删改查,首先要将数据库表和gorm进行关联,在此处是与go的结构体进行关联 123456789//其中gorm.Model是gorm库默认的一些字段,包括了id,CreatedAt,UpdateAt,DeleteAt这几个字段type Product struct { gorm.Model Code string Price uint}//将结构体与数据库表进行关联,如果数据库中不存在该表,则会自动创建db.AutoMigrate(&Product{}) 创建  创 参考文章:参考链接","categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"}],"tags":[]},{"title":"ioutil一些方便的操作函数集","slug":"ioutil一些方便的操作函数集","date":"2020-10-26T14:31:41.000Z","updated":"2020-11-08T13:24:03.759Z","comments":true,"path":"2020/10/26/ioutil一些方便的操作函数集/","link":"","permalink":"https://lookupman.cn/2020/10/26/ioutil%E4%B8%80%E4%BA%9B%E6%96%B9%E4%BE%BF%E7%9A%84%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0%E9%9B%86/","excerpt":"","text":"1. NopCloser函数  该函数是将一个io.Reader接口包装成一个io.ReadCloser接口,其中包装生成的Close方法什么也不操作。可以在需要将io.Reader传入io.ReadCloser参数的函数的情况下使用。 1234567type nopCloser struct { io.Reader}func(nopCloser) Close() error { return nil} 2. ReadAll函数  很多时候,我们需要一次性读取 io.Reader 中的数据,通过上一节的讲解,我们知道有很多种实现方式。考虑到读取所有数据的需求比较多,Go 提供了 ReadAll 这个函数,用来从io.Reader 中一次读取所有数据。 1func ReadAll(r io.Reader) ([]byte, error)   阅读该函数的源码发现,它是通过 bytes.Buffer 中的 ReadFrom 来实现读取所有数据的。该函数成功调用后会返回 err == nil 而不是 err == EOF。(成功读取完毕应该为 err == io.EOF,这里返回 nil 由于该函数成功期望 err == io.EOF,符合无错误不处理的理念) 参考文章:参考链接","categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"}],"tags":[]},{"title":"Golang基本输入输出(io包接口)","slug":"Golang基本输入输出(io包接口)","date":"2020-10-25T08:22:58.000Z","updated":"2020-11-28T06:39:17.042Z","comments":true,"path":"2020/10/25/Golang基本输入输出(io包接口)/","link":"","permalink":"https://lookupman.cn/2020/10/25/Golang%E5%9F%BA%E6%9C%AC%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA(io%E5%8C%85%E6%8E%A5%E5%8F%A3)/","excerpt":"  在本章的所有的io接口时,有一个特点,在读取或写入的n=len(p)时,返回的接口err=nil或者err=EOF;当n<len(p)时,需要返回err解释原因。","text":"  在本章的所有的io接口时,有一个特点,在读取或写入的n=len(p)时,返回的接口err=nil或者err=EOF;当n<len(p)时,需要返回err解释原因。 1. Reader接口  在官方io包中定义了如下接口,只要实现了Read方法,就是将其传递为io.Reader类型的参数 123type Reader interface { Read(p []byte)(n int, err error)} 2. Writer接口  在官方的io包中定义里如下接口,只要实现了Write方法,也就是实现了Writer接口 123type Writer interface{ Write(p []byte)(n int, err error)} 3. 实现Reader和Writer接口的类型 os.File同时实现了io.Reader和io.Writer接口 strings.Reader实现了io.Reader接口 bufio.Reader/Writer分别实现了io.Reader和io.Writer接口 bytes.Buffer同时实现了io.Reader和io.Writer接口 bytes.Reader实现了io.Reader接口 compress/gzip.StreamReader/StreamWriter分别实现了io.Reader和io.Writer接口 encoding/csv.Reader/Writer分别实现了io.Reader和io.Writer接口 net/conn分别实现了io.Reader和io.Writer接口(Conn接口定义了Read/Write)4. ReaderAt接口  该接口是从源输入的偏移量off处读取len(p)个字节到p中。123type ReaderAt struct { ReadAt(p []byte, off int64)(n int, err error)} 5. WriterAt接口  该接口是在源输出的偏移量off处写入len(p)个字节。123type WriterAt struct { WriteAt(p []byte, off int64)(n int, err error)} 6. ReaderFrom接口  ReadFrom从r中读取数据,直到EOF或发生错误。123type ReaderFrom struct { ReadFrom(r Reader)(n int64, err error)} 使用示例123456789file, err := os.Open("writerAt.txt")if err != nil{ painc(err)}defer file.Close()writer := bufio.NewWriter(os.Stdout)writer.ReadFrom(file)writer.Flush() 7. WriterTo接口 emsp;WriteTo 将数据写入 w 中,直到没有数据可写或发生错误。123type WriterTo struct{ WriteTo(w Writer)(n int, err error)} 8. Seeker接口  Seek 设置下一次 Read 或 Write 的偏移量为 offset,它的解释取决于 whence: 0 表示相对于文件的起始处,1 表示相对于当前的偏移,而 2 表示相对于其结尾处。 Seek 返回新的偏移量和一个错误,如果有的话。123456789const( SeekStart = 0 SeekCurrent = 1 SeekEnd = 2)type Seeker struct{ Seek(offset int64, whence int)(ret int64, err error)} 9. Closer接口接口定义如下:123type Closer interface{ Close() error} 10. ByteReader和ByteWriter接口  这个两个接口用于读/写一个字节 1234567type ByteReader interface{ ReadByte()(c byte, err error)}type ByteWriter interface{ WriteByte(c byte) error} 在标准库中,有如下类型实现了 io.ByteReader 或 io.ByteWriter:bufio.Reader/Writer 分别实现了io.ByteReader 和 io.ByteWriterbytes.Buffer 同时实现了 io.ByteReader 和 io.ByteWriterbytes.Reader 实现了 io.ByteReaderstrings.Reader 实现了 io.ByteReader 11. ByteScanner、RuneReader和RuneScanner接口  UnreadByte 方法的意思是:将上一次 ReadByte 的字节还原,使得再次调用 ReadByte 返回的结果和上一次调用相同,也就是说,UnreadByte 是重置上一次的 ReadByte。注意,UnreadByte 调用之前必须调用了 ReadByte,且不能连续调用 UnreadByte。 123456789101112131415type ByteScanner interface { ByteReader UnreadByte() error}//RuneReader和ByteReader类似type RuneReader interface { ReadRune()(r rune, size int, err error)}//RuneScanner和ByteScanner类似type RuneScanner interface { RuneReader UnreadRune() error} 12. SectionReader接口  NewSectionReader 返回一个 SectionReader,它从 r 中的偏移量 off 处读取 n 个字节后以 EOF 停止。也就是说,SectionReader 只是内部(内嵌)ReaderAt 表示的数据流的一部分:从 off 开始后的 n 个字节。这个类型的作用是:方便重复操作某一段 (section) 数据流;或者同时需要 ReadAt 和 Seek 的功能。 12345678910type SectionReader struct { r ReaderAt base int64 //NewSectionReader会将base设置为off off int64 //从r中off偏移处开始读取数据 limit int64 //limit-off = SectionReader流的长度}func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {} LimitedReader接口  从 R 读取但将返回的数据量限制为 N 字节。每调用一次 Read 都将更新 N 来反应新的剩余数量。也就是说,最多只能返回 N 字节数据。1234type LimitedReader struct { R Reader //underlying reader,最终的读取通过R.Read完成 N int64 //max bytes remaining}   使用示例1234567891011content := "This is LimitReader Example"reader := strings.NewReader(content)limitReader := &io.LimitedReader{R:reader, N:8}for limitReader.N > 0{ tmp := make([]byte, 2) limitReader.Read(tmp) fmt.Printf("%s", tmp)}//输出: This Is//从结果看一看出最多只能读取N个字符 14. PipeReader和PipeWriter接口  关于 PipeReader.Read 方法的说明:从管道中读取数据。该方法会堵塞,直到管道写入端开始写入数据或写入端被关闭。如果写入端关闭时带有 error(即调用 CloseWithError 关闭),该Read返回的 err 就是写入端传递的error;否则 err 为 EOF。  关于 PipeWriter.Write 方法的说明:写数据到管道中。该方法会堵塞,直到管道读取端读完所有数据或读取端被关闭。如果读取端关闭时带有 error(即调用 CloseWithError 关闭),该Write返回的 err 就是读取端传递的error;否则 err 为 ErrClosedPipe。 1234567type PipeReader struct { p *pipe}type PipeWriter struct{ p *pipe}   使用示例如下 123456789101112131415161718192021222324252627282930313233func main() { pipeReader, pipeWriter := io.Pipe() go PipeWrite(pipeWriter) go PipeRead(pipeReader) time.Sleep(30 * time.Second)}func PipeWrite(writer *io.PipeWriter){ data := []byte("hello world!") for i := 0; i < 3; i++{ n, err := writer.Write(data) if err != nil{ fmt.Println(err) return } fmt.Printf("写入字节%d\\n", n) } writer.CloseWithError(errors.New("写入段已经关闭!"))}func PipeRead(reader *io.PipeReader){ buf := make([]byte, 128) for{ fmt.Println("接口开始测试....") time.Sleep(5*time.Second) n, err := reader.Read(buf) if err != nil{ fmt.Println(err) return } fmt.Printf("收到%d个字节,数据为:%s\\n", n, buf) }} 15. Copy和CopyN函数  Copy 将 src 复制到 dst,直到在 src 上到达 EOF 或发生错误。它返回复制的字节数,如果有错误的话,还会返回在复制时遇到的第一个错误。成功的 Copy 返回 err == nil,而非 err == EOF。由于 Copy 被定义为从 src 读取直到 EOF 为止,因此它不会将来自 Read 的 EOF 当做错误来报告。  若 dst 实现了 ReaderFrom 接口,其复制操作可通过调用 dst.ReadFrom(src) 实现。此外,若 src 实现了 WriterTo 接口,其复制操作可通过调用 src.WriteTo(dst) 实现。 1func Copy(dst Writer, src Reader)(written int64, err error){}   CopyN 将 n 个字节(或到一个error)从 src 复制到 dst。 它返回复制的字节数以及在复制时遇到的最早的错误。当且仅当err == nil时,written == n 。  若 dst 实现了 ReaderFrom 接口,复制操作也就会使用它来实现。 1func CopyN(dst Writer, src Reader, n int64)(written int64, err error){} 16. ReadAtLeast和ReadFull函数  ReadAtLeast 将 r 读取到 buf 中,直到读了最少 min 个字节为止。它返回复制的字节数,如果读取的字节较少,还会返回一个错误。若没有读取到字节,错误就只是 EOF。如果一个EOF 发生在读取了少于min个字节之后,ReadAtLeast就会返回ErrUnexpectedEOF。若 min 大于 buf 的长度,ReadAtLeast 就会返回ErrShortBuffer。对于返回值,当且仅当 err == nil 时,才有 n >= min。 1func ReadAtLeast(r Reader, buf []byte, min int)(n int, err error){}   ReadFull 精确地从 r 中将 len(buf) 个字节读取到 buf 中。它返回复制的字节数,如果读取的字节较少,还会返回一个错误。若没有读取到字节,错误就只是 EOF。如果一个 EOF 发生在读取了一些但不是所有的字节后,ReadFull 就会返回 ErrUnexpectedEOF。对于返回值,当且仅当 err == nil 时,才有 n == len(buf)。 1func ReadFull(r Reader, buf []byte)(n int, err error) 17. WriteString函数  WriteString 将s的内容写入w中,当 w 实现了 WriteString 方法时,会直接调用该方法,否则执行 w.Write([]byte(s))。 1func WriteString(w writer, s string)(n int, err error) 18. MultiReader和MultiWriter函数12345678910type multiReader struct { readers []Reader}type multiWriter struct { writers []Writer}func MultiReader(readers ...Reader) Readerfunc MultiWriter(writers ...Writer) Writer 使用示例如下: 1234567891011121314151617readers := []io.Reader{ strings.NewReader("from strings reader"), bytes.NewBufferString("from bytes buffer"),}reader := io.MultiReader(readers...)data := make([]byte, 0, 128)buf := make([]byte, 10)for n, err := reader.Read(buf); err != io.EOF ; n, err = reader.Read(buf){ if err != nil{ panic(err) } data = append(data,buf[:n]...)}fmt.Printf("%s\\n", data)//输出//from strings readerfrom bytes buffer 12345678910111213file, err := os.Create("tmp.txt")if err != nil { panic(err)}defer file.Close()writers := []io.Writer{ file, os.Stdout,}writer := io.MultiWriter(writers...)writer.Write([]byte("Hello World!"))//输出:控制台和文件同时输出Hello World! 参考文章:《Go语言标准库》","categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"}],"tags":[]},{"title":"nginx学习记录","slug":"nginx学习记录","date":"2020-10-18T09:23:54.000Z","updated":"2020-10-18T09:23:54.000Z","comments":true,"path":"2020/10/18/nginx学习记录/","link":"","permalink":"https://lookupman.cn/2020/10/18/nginx%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","excerpt":"","text":"基础架构   1.请求流程 优点 高并发,高性能 可扩展性良好 高可靠性 热部署 BSD许可证 –32核64G,可支持千万并发 组成(1)Nginx二进制可执行文件(2)Nginx。conf配置文件(3)access.log访问日志(4)error.log错误日志 历史版本 第三方模块框架OpenResty 编译nginx 下载安装nginx 源码目录 目录说明① auto 用于编译,其中包含了四个主要目录,分别是cc(用于编译)、lib、os(对编译操作系统的判断)、types② CHANGES: nginx的版本变化③ CHANGES.ru: 俄罗斯版本④ conf: 一些示例文件,nginx安装好后为了方便运维去配置,会把conf的示例文件拷贝到安装目录⑤ configure: 该文件用于生成中间文件,执行编译前的必备动作⑥ contrib: 提供2个perl脚本和vim的工具,改变配置nginx.conf的vim语法配色 1cp -r contrib/vim/* ~/.vim/ ⑦ man: nginx帮助文件⑧ src: 源代码 编译 1./configure --help | more   如果不需要变动,编译时只需要指定–prefix参数即可,所有的文件会在prefix目录下建相应的文件夹  –with前缀的模块默认不加入编译  –without前缀的模块默认加入编译,执行该命令表示移除编译 123456789//安装依赖//解决zlib error依赖错误sudo apt install zlib1g-dev//解决openssl error依赖错误sudo apt install openssl libssl-dev//解决pcre error依赖错误sudo apt install libpcre3 libpcre3-dev   执行configure,会生成中间文件在objs文件夹下,其中最重要的是ngx_modules.c,它决定了哪些模块将参与编译 1./configure --prefix = ~/home/app/nginx   接下来是执行make编译,目标文件在objs目录“nginx”,c编译的中间文件在src目录 1make   最后执行安装命令,安装在prefix执行的目录中 1make install nginx命令行12345678910//使用指定配置文件 -c//使用配置指令 -g//指定运行目录 -p//发送信号 -s////stop:立即停止服务 (kill -TERM)////quit:优雅的停止服务 (kill -QUIT)////reload:重载配置文件 (kill -SIGHUP)////reopen:重新开始记录日志文件 (kill -USR1)//测试配置文件是否有语法错误 -t -T//打印nginx的版本信息、编译信息:-v -V   修改配置后,直接nginx -s reload即可,会自动加载新的配置,在这个过程中,会重新启动一个worker进行,并将之前的worker进程优雅的退出 优雅关闭  优雅的关闭只针对http协议层 热部署 部署最新版本的nginx,只需替换nginx二进制文件,首先用ps -ef | grep nginx,观察进程的状态。(注意在编译新版本的nginx时,不需要执行make install命令,否则会覆盖之前的配置文件) 然后将安装目录下sbin/nginx,修改为nginx.old1mv nginx nginx.old 然后将其他版本的nginx移到该目录下1cp ../openresty/nginx/sbin/nginx ./ 然后像nginx发送信号(kill -USR2 79637),其会启动新版本nginx进程1kill -USR2 79637 然后向老的nginx进程发送一个信号(kill -WINCH 79637),让其优雅的关闭work进程1kill -WINCH 79637 在这之后可以看到老版本的nginx的worker进程已经关闭,还剩下master进程,master进程是为了防止新部署的版本出现问题,可以做版本回退的作用版本回退 首先通过信号,kill -HUP 79637重新拉起旧版本的master进程1kill -HUP 79637 然后像新版本的nginx,发送kill -WINCH 112586信号使其优雅的关闭worker进程1kill -WINCH 112586 最后关闭新版本的nginx master进程即可1kill -9 112586 日志切割 首先到log目录下,将之前日志转移到另一个文件1mv access.log pre_access.log 然后在nginx目录下,执行reopen命令即可生成新的日志文件1sudo nginx -s reopen Nginx信号  其中master进程会检测worker进程,当worker异常终止时,master进程会接收到一个CHLD信号。然后master进程会重新将worker进程拉起。reload流程(注意配置worker_shutdown_timeout) 使用Goaccess分析日志 使用apt安装goaccess1sudo apt install goaccess 然后执行一下命令即可。-o 分析日志网页的生成路径,其余参数查看官网帮助文档,其中注意在nginx的日志格式变化的情况下,需要修改–log-foramt参数对应的值1goaccess pre_access.log -o ../html/report.html --real-time-html --time-format='%H:%M:%S' --date-format='%d/%b/%Y' --log-format=COMBINED Nginx模块数据结构  其中ngx_module_t中的type字段定义了nginx模块的类型 共享内存使用模块 参考文章 极客时间Nginx核心知识100讲","categories":[{"name":"Nginx","slug":"Nginx","permalink":"https://lookupman.cn/categories/Nginx/"}],"tags":[{"name":"成长","slug":"成长","permalink":"https://lookupman.cn/tags/%E6%88%90%E9%95%BF/"}]},{"title":"C++包管理工具vcpkg的使用","slug":"C++包管理工具vcpkg的使用","date":"2020-06-08T10:39:40.000Z","updated":"2020-06-08T20:07:50.000Z","comments":true,"path":"2020/06/08/C++包管理工具vcpkg的使用/","link":"","permalink":"https://lookupman.cn/2020/06/08/C++%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7vcpkg%E7%9A%84%E4%BD%BF%E7%94%A8/","excerpt":"","text":"前言  最近在使用c++时,发现编译第三方库真的烦,同时还要在vs的设置中引入包含目录、库目录,操作起来十分繁琐。之后github上找到了vcpkg这个微软开发的包管理工具,用起来挺顺手的,就是下载包的时候,由于包服务器在外网,很多时候会安装失败,这是一个苦恼的问题。最近,在一片博客看到有大佬搭建国内个人镜像,没有梯子的,可以去了解一下。 安装  安装很简单,可以参考github提供的README.md。 首先第一步从vcpkg仓库把代码clone下来:12> git clone git@github.com:microsoft/vcpkg.git> cd vcpkg 然后第二步,使用命令编译vcpkg12> .\\bootstrap-vcpkg.bat //windows下> ./bootstrap-vcpkg.sh //Linux下 第三步,为了方便操作,可以将目录下生成的vcpkg可执行文件,加入到系统的环境变量 第四步,为全局配置vcpkg1> vcpkg integrate install   在这一步之后,在windows下,VS中,可以直接在头部引入安装的库使用,不需要其他包含库目录等操作,十分方便。  对于不使用vs的情况,vcpkg可以在cmake中使用,通过编译参数-DCMAKE_TOOLCHAIN_FILE = [vcpkg root]\\scripts\\buildsystems\\vcpkg.cmake,进行配置,实例如下:12345678910111213141516171819cmake_minimum_required (VERSION 3.8)project(ManagerServer)set(CMAKE_CXX_STANDARD 14)#G:/c++ Depende/vcpkg, 是我的vcpkg的根目录,在使用的时候需要更换为自己的set(VCPKG_ROOT "G:/C++ Depende/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE PATH "")set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT})#这个是查找sqlite3库的操作, 安装sqlite3的操作,请看文章的下一部分find_package(sqlite3 CONFIG REQUIRED)aux_source_directory(. sourceList)# 将源代码添加到此项目的可执行文件。add_executable (ManagerServer ${sourceList})#进行sqlite3库的链接target_link_libraries(ManagerServer PRIVATE sqlite3) 使用  接下来是关于vcpkg的使用,本文以官方文档描述的安装和使用sqlite3的例子来介绍。 通过search参数来查找提供的第三方包 1234567> vcpkg search sqlite3dlib[sqlite3] sqlite3 support for dlibsoci[sqlite3] Build sqlite3 backendsqlite3 3.32.0-0 SQLite is a software library that implements a self-contained, serverless, zer...sqlite3[tool] sqlite3 executablesqlitecpp 3.0.0 SQLiteC++ (SQLiteCpp) is a smart and easy to use C++ SQLite3 wrapper.sqlpp11-connector... 0.29-2 A C++ wrapper for sqlite3 meant to be used in combination with sqlpp11. 通过install命令来安装包  在安装包之前,需要查看本机的VS环境是否安装的English的语言包,否则,会出现警告和报错的问题。接下来,是使用install安装第三方包,示例的环境是windows10。 123456789101112131415161718192021222324252627> vcpkg install sqlite3Computing installation plan...The following packages will be built and installed: sqlite3[core]:x86-windowsStarting package 1/1: sqlite3:x86-windowsBuilding package sqlite3[core]:x86-windows...-- Using cached G:/C++ Depende/vcpkg/downloads/sqlite-amalgamation-3320000.zip-- Using source at G:/C++ Depende/vcpkg/buildtrees/sqlite3/src/3320000-f17e2f22e8-- Configuring x86-windows-- Building x86-windows-dbg-- Building x86-windows-rel-- Performing post-build validation-- Performing post-build validation doneBuilding package sqlite3[core]:x86-windows... doneInstalling package sqlite3[core]:x86-windows...Installing package sqlite3[core]:x86-windows... doneElapsed time for package sqlite3:x86-windows: 11.25 sTotal elapsed time: 11.25 sThe package sqlite3:x86-windows provides CMake targets: find_package(sqlite3 CONFIG REQUIRED) target_link_libraries(main PRIVATE sqlite3)//在windows下命令默认编译的是x86的包,可以通过":"指定,比如:> vcpkg install sqlite3:x64-windows   在完成安装后,可以看出命令行输出了,在camke中的使用语句,将其写入CMakeLists.txt就可以了,特别要注意target_link_libraries(main PRIVATE sqlite3)add_executable()后面。 利用list参数,查看已经编译安装的包 123> vcpkg listsqlite3:x64-windows 3.32.0-0 SQLite is a software library that implements a s...sqlite3:x86-windows 3.32.0-0 SQLite is a software library that implements a s... 利用remove参数,卸载已经编译安装的包 1> vcpkg remove [package_name] 使用实例 main.cpp文件12345678910111213141516171819202122232425262728293031// main.cpp#include <errno.h>#include <sqlite3.h>#include <stdio.h>int main() { sqlite3 *pdb = nullptr; int result; result = sqlite3_open("test.db", &pdb); if (result != SQLITE_OK) { perror("sqlite_open error!"); return -1; } sqlite3_stmt *pStmt; const char *sql = "create table Student(" "t_id, integer primary key, " "t_name varchar(15), t_age integer)"; char *errMsg = NULL; result = sqlite3_exec(pdb, sql, NULL, NULL, &errMsg); if (result != SQLITE_OK) { printf("create table student failed!\\n"); printf("error conde %d \\t error message:%s\\n", result, errMsg); } printf("create table success!\\n"); return 0;} CMakeLists.txt文件1234567891011121314151617#CMakeLists.txtcmake_minimum_required (VERSION 3.8)project(ManagerServer)set(CMAKE_CXX_STANDARD 14)set(VCPKG_ROOT "G:/C++ Depende/vcpkg/scripts/buildsystems/vcpkg.cmake" CACHE PATH "")set(CMAKE_TOOLCHAIN_FILE ${VCPKG_ROOT})find_package(sqlite3 CONFIG REQUIRED)aux_source_directory(. sourceList)# 将源代码添加到此项目的可执行文件。add_executable (ManagerServer ${sourceList})target_link_libraries(ManagerServer PRIVATE sqlite3) 参考文章 vcpkg国内镜像使用方法","categories":[{"name":"C++","slug":"C","permalink":"https://lookupman.cn/categories/C/"}],"tags":[{"name":"C++","slug":"C","permalink":"https://lookupman.cn/tags/C/"},{"name":"包管理工具","slug":"包管理工具","permalink":"https://lookupman.cn/tags/%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7/"}]},{"title":"新的开始","slug":"新的开始","date":"2020-06-07T03:07:59.000Z","updated":"2020-06-07T14:07:50.000Z","comments":true,"path":"2020/06/07/新的开始/","link":"","permalink":"https://lookupman.cn/2020/06/07/%E6%96%B0%E7%9A%84%E5%BC%80%E5%A7%8B/","excerpt":"","text":"要开始写博客了,不然很多东西,好久没用了就忘了","categories":[{"name":"生活","slug":"生活","permalink":"https://lookupman.cn/categories/%E7%94%9F%E6%B4%BB/"}],"tags":[{"name":"成长","slug":"成长","permalink":"https://lookupman.cn/tags/%E6%88%90%E9%95%BF/"}]}],"categories":[{"name":"Golang","slug":"Golang","permalink":"https://lookupman.cn/categories/Golang/"},{"name":"Nginx","slug":"Nginx","permalink":"https://lookupman.cn/categories/Nginx/"},{"name":"C++","slug":"C","permalink":"https://lookupman.cn/categories/C/"},{"name":"生活","slug":"生活","permalink":"https://lookupman.cn/categories/%E7%94%9F%E6%B4%BB/"}],"tags":[{"name":"成长","slug":"成长","permalink":"https://lookupman.cn/tags/%E6%88%90%E9%95%BF/"},{"name":"C++","slug":"C","permalink":"https://lookupman.cn/tags/C/"},{"name":"包管理工具","slug":"包管理工具","permalink":"https://lookupman.cn/tags/%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7/"}]}