GoLang学习之路,对Elasticsearch的使用,一文足以(包括泛型使用思想)(二)

书写上回,上回讲到,Elasticsearch的使用前提即:语法,表结构,使用类型结构等。要学这个必须要看前面这个:GoLang学习之路,对Elasticsearch的使用,一文足以(包括泛型使用思想)(一),因为这篇是基础!!!!!!!

文章目录

  • 使用ElasticSearch
    • `使用前提`
    • 使用API实现对Elasticsearch的增删改查
      • 创建客户端
        • 创建yaml文件
        • 创建客户端
        • 将配置文件加载到客户端对象中
      • 创建索引结构
      • 定义客户端结构体
      • 定义创建索引结构的方法
      • 写一个测试方法
      • 插入一条数据的方法
      • 判断是否存在索引,不存在就创建一个
      • 批量处理
        • 方式一
          • 测试
        • 方法二
        • 方式三
      • 查询

使用ElasticSearch

使用前提

  1. 必须要有一个ElasticSearch服务器
  2. 必须要有一个可视化工具
  3. 安装API包,"github.com/elastic/go-elasticsearch/v8"
import "github.com/elastic/go-elasticsearch/v8"

但是这个包下面其实还有一些包,这些包非常的重要。当时我在使用的时候,根本不知道,走了不少的弯路的,找了官网的文档,又找了一些博客,都没有详细的说明情况和要点。要不就少些,要不就只把部分给列出来。但是现在我将这些无私的奉献给各位。
在这里插入图片描述

因为这个v8的包非常的多,所以很难将所有的放进去。这里我做一些解释:

  1. 客户端:
    • 调用NewDefaultClient()NewClient(cfg Config)方法会返回一个普通客户端
      • NewDefaultClient() 不需要去配置链接时的配置参数,默认参数链接,并返回一个普通客户端
      • NewClient(cfg Config)需要按照总共需要的配置需求去配置参数,并返回一个普通客户端
    • 调用NewTypedClient(cfg Config)会返回一个属性客户端(相比普通客户端强大,但是有局限,后面再说)
  2. 工具包:
    • 这个工具包主要是普通客户端进行调用的,使用的范围是对于批量处理数据的情况
  3. 参数类型包:
    • 我们在对ElasticSearch进行处理的时候会有很多中情况:
      • 首先是对于语法的选择,ElasticSearch有独属于他自己的一套语法。
      • 查询时会有很多选择,比如对于字段是模糊查询,还是精确查询,还是对地图进行查询。这些参数都有,也有对于AI进行处理的参数。(建议下一个翻译软件,去看看。那个参数太多了。。。也就是说功能非常齐全)

…很多内容在GoLang学习之路,对Elasticsearch的使用,一文足以(包括泛型使用思想)(一)

接下来正式开始

使用API实现对Elasticsearch的增删改查

为了实现这些CRUD,我总结了几个基本的使用步骤。(可以不按我这个创建客户端)

  1. 创建客户端
    • 生成yaml配置文件
    • 读取配置文件信息,并保存到客户端上
  2. 创建索引结构
  3. 插入数据
  4. 然后调用API

创建客户端

根据上面所说,客户端在创建的时候,分为两种,一种为普通客户端,一种是属性客户端。而后者的功能更为强大。但是前者的某些功能,属性客户端是没办法的。比如批量处理数据(bulk)

在实际的生产中我们需要创建两个客户端,以便我们在需求变化中获取主动权。

创建yaml文件

文件名: config.yaml
文件中的参数按自己配,千万别一样,你们是连不上 的。

es:adders:- http://8.210.237.26:9200username: elasticpassword:  +Svn3a*I*b2xxbCe9

yaml 中为何要实现数组结构,其本质是,Elasticsearch为了给以后分布式扩展提供渠道。到时候只要将IP地址,填充到配置文件就可以了

创建客户端

建议可以看看配置方法中的源码。

import (myElasticSearch "elasticsearch/common/esll""github.com/elastic/go-elasticsearch/v8""net""net/http""time"
)type ESConfig struct {Adders   []string `mapstructure:"adders" json:"adders" yaml:"adders"`Password string   `mapstructure:"password" json:"password" yaml:"password"`Username string   `mapstructure:"username" json:"username" yaml:"username"`
}func NewES(config *Config) *myElasticSearch.ElasticSearch {//强化版客户端clientType, err := elasticsearch.NewTypedClient(elasticsearch.Config{Addresses: config.ElasticSearch.Adders,Username:  config.ElasticSearch.Username,Password:  config.ElasticSearch.Password,Transport: &http.Transport{//每个host的idle状态的最大连接数目MaxConnsPerHost: 10,//发送完request后等待serve response的时间ResponseHeaderTimeout: 3 * time.Second,//(net.Conn, error) 创建未加密的tcp连接DialContext: (&net.Dialer{Timeout: time.Second}).DialContext,//连接保持idle状态的最大时间,超时关闭pconn// todo 看需求是否使用tls证书链接/*TLSClientConfig: &tls.Config{MaxVersion:         tls.VersionTLS11,InsecureSkipVerify: true,},*/},EnableDebugLogger: true,})if err != nil {panic("ElasticSearch clientType connect ping failed:" + err.Error())}//一般客户端client, err := elasticsearch.NewClient(elasticsearch.Config{Addresses: config.ElasticSearch.Adders,Username:  config.ElasticSearch.Username,Password:  config.ElasticSearch.Password,Transport: &http.Transport{//每个host的idle状态的最大连接数目MaxConnsPerHost: 10,//发送完request后等待serve response的时间ResponseHeaderTimeout: 3 * time.Second,//(net.Conn, error) 创建未加密的tcp连接DialContext: (&net.Dialer{Timeout: time.Second}).DialContext,//连接保持idle状态的最大时间,超时关闭pconn// todo 看需求是否使用tls证书链接/*TLSClientConfig: &tls.Config{MaxVersion:         tls.VersionTLS11,InsecureSkipVerify: true,},*/},EnableDebugLogger: true,})if err != nil {panic("ElasticSearch client connect ping failed:" + err.Error())}return &myElasticSearch.ElasticSearch{ClientTyped: clientType,Client:      client,}
}
将配置文件加载到客户端对象中

viper,这个读取配置文件的工具包:详细请看:文章

import ("fmt""github.com/fsnotify/fsnotify""github.com/go-playground/validator/v10""github.com/google/wire""github.com/spf13/viper"
)type Config struct {ElasticSearch *ESConfig `mapstructure:"es" validate:"required"`
}var Cfg *Configfunc ProvideConfig() *Config {var cfg Configv := viper.New()//索引配置文件位置v.SetConfigName("config.yaml")v.AddConfigPath("./")v.SetConfigType("yaml")err := v.ReadInConfig()if err != nil {panic(fmt.Errorf("open error of config file:%s", err))}//监视器v.WatchConfig()v.OnConfigChange(func(in fsnotify.Event) {fmt.Println("config file changed:", in.Name)err := v.Unmarshal(&cfg)if err != nil {fmt.Println(err)}})//反序列化if err := v.Unmarshal(&cfg); err != nil {panic(fmt.Errorf("fatal error config file : %s", err))}vs := validator.New()//校验结构err = vs.Struct(&cfg)if err != nil {panic(err)}Cfg = &cfgreturn &cfg
}

创建索引结构

定义索引结构

在 esll.go 文件中写入

const MappingTpl = `{"mappings":{"properties":{"categoryId":     { "type": "long" },"productName":   {"type": "keyword" },"masterPic":   {"type": "text"},"desc":   {"type": "keyword" },"price":    { "type": "long"},"startProvinceCode":  {"type": "text" },"startCityCode":         {"type": "text" },"update_time":  { "type": "long"},"create_time":  { "type": "long"}}}}`

定义客户端结构体

包:package esll

type ElasticSearch struct {ClientTyped *elasticsearchV8.TypedClientClient      *elasticsearchV8.Client
}

这里强调说明一下。这里为什么要用两个客户端?因为对于真正的实际运用中会有各种各样的问题出现,不仅会有一个一个查询,一个一个插入的情况,更会有一批一批的查询,插入的。所以这里的客户端对应的都会各有不同。

ClientTyped:功能强大,但是不支持批量处理
Client :调用复杂,但是支持批量处理

定义创建索引结构的方法

包:package esll

// CreateIndex 创建所用的索引结构
func (e *ElasticSearch) CreateIndex(ctx context.Context, indexName string, mappings string) error {mapping := types.NewTypeMapping()err := mapping.UnmarshalJSON([]byte(mappings))if err != nil {return err}_, err = e.ClientTyped.Indices.Exists(indexName).Do(ctx)if err != nil {log.Printf("索引已经存在")return err}_, err = e.ClientTyped.Indices.Create(indexName).Mappings(mapping).Do(ctx)if err != nil {log.Printf("索引创建失败")return err}return nil
}

写一个测试方法

func TestMepping(t *testing.T) {ctx, _ := context.WithTimeout(context.Background(), 50*time.Second)cfg := config.ProvideConfig()client := config.NewES(cfg)mapping := types.NewTypeMapping()err := mapping.UnmarshalJSON([]byte(esll.MappingTpl))if err != nil {return}_, err = client.ClientTyped.Indices.Create("test2").Mappings(mapping).Do(ctx)if err != nil {fmt.Println(err)}
}

这里的types文件,是参数的文件,具体可以看看源码详情,根据需求选择

插入一条数据的方法

// IndexDocument 创建一条索引进入文档
func (e *ElasticSearch) IndexDocument(ctx context.Context, indexName string, document interface{}) error {do, err := e.ClientTyped.Index(indexName).Document(document ).Do(ctx)result := do.Resultfmt.Println(result)if err != nil {log.Printf("创建索引文档失败:%s", err)return err}return nil
}

判断是否存在索引,不存在就创建一个

// IsExists 是否存在索引,不存在就创建一个
func (e *ElasticSearch) IsExists(ctx context.Context, indexName string, mappings string) error {_, err2 := e.ClientTyped.Indices.Exists(indexName).Do(ctx)if err2 != nil {//不存在就重新创建一个索引err := e.CreateIndex(ctx, indexName, mappings)if err != nil {return err}}return nil
}

批量处理

方式一
func (e *ElasticSearch) IndexDocumentList(ctx context.Context, indexName string, anyList any, mapping string) error {//验证索引是否存在if err := e.IsExists(ctx, indexName, mapping); err != nil {return err}//RW := &sync.RWMutex{}slice, err := transitionSlice(anyList)if err != nil {return err}buf := buffer(ctx, e, indexName, slice, "index")//获取当前索引下的文档个数//todo:这里诺是出现超量的索引,可以通过for循环确定索要令牌(技术上限流),或者通过协程处理//写入缓存中,并绑定索引,//转换成json格式//结果我发现这个官方已经实现了。。。。bulk, err := e.Client.Bulk(bytes.NewReader(buf.Bytes()),e.Client.Bulk.WithIndex(indexName),e.Client.Bulk.WithContext(ctx),e.Client.Bulk.WithRefresh("true"))//先关闭缓存defer bulk.Body.Close()if err != nil {log.Fatal("ElasticSearch 批量写入 失败:", err)return err}return nil
}
// 上传的缓存逻辑
func buffer(ctx context.Context, client *ElasticSearch, indexName string, slice []any, CRUD string) bytes.Buffer {c, _ := client.ClientTyped.Cat.Count().Index(indexName).Do(ctx)num, _ := strconv.Atoi(*c[0].Count)//创建缓存var buf bytes.Bufferfor i := num; i < len(slice)+num; i++ {index := []byte(fmt.Sprintf(`{ "%s" : { "_id" : "%d" } }%s`, CRUD, i, "\n"))//这里可以优化通过算法插入datas, _ := json.Marshal(slice[i-num])datas = append(datas, "\n"...)buf.Grow(len(index) + len(datas))buf.Write(index)buf.Write(datas)}return buf}
// todo 数量过多的话可以通过,三种方式,一种通过创建协程,一种通过二叉树递归的方式,另一种通过创建协程加递归的方式
func transitionSlice(anyl any) ([]any, error) {val, ok := isSlice(anyl)if !ok {return nil, errors.New("切片转换失败")}sliceLen := val.Len()list := make([]any, sliceLen)for i := 0; i < sliceLen; i++ {list[i] = val.Index(i).Interface()}return list, nil
}
// 判断是否为切片类型
func isSlice(anySlice any) (val1 reflect.Value, ok bool) {val := reflect.ValueOf(anySlice)if val.Kind() == reflect.Slice {ok = true}val1 = valreturn val1, ok
}
测试
func TestDuck2(t *testing.T) {var (buf bytes.Bufferres *esapi.Responseerr error)cfg := config.ProvideConfig()client := config.NewES(cfg).Clientfor j := 1; j <= 1000; j++ {meta := []byte(fmt.Sprintf(`{ "index" : { "_id" : "%d" } }%s`, j, "\n"))data := []byte(`{"content":"` + strings.Repeat("ABC", 100) + `"}`)data = append(data, "\n"...)buf.Grow(len(meta) + len(data))buf.Write(meta)buf.Write(data)}res, err = client.Bulk(bytes.NewReader(buf.Bytes()), client.Bulk.WithIndex("test"), client.Bulk.WithRefresh("true"))if err != nil {t.Fatalf("Failed to index data: %s", err)}res.Body.Close()if res.IsError() {t.Fatalf("Failed to index data: %s", res.Status())}
}
方法二
unc (e *ElasticSearch) UpdateDocumentList(ctx context.Context, indexName *string, anyList any, typeBulk string, mappings string) error {//验证索引是否存在if err := e.IsExists(ctx, *indexName, mappings); err != nil {return err}slice, err := transitionSlice(anyList)if err != nil {return err}start := time.Now().UTC()//设置批量配置文件indexer, err := esutil.NewBulkIndexer(esutil.BulkIndexerConfig{Index:         *indexName,Client:        e.Client,NumWorkers:    5,FlushBytes:    1024000,FlushInterval: 30 * time.Second,})if err != nil {return err}i := 1002//将数据一条一条塞入缓存中for _, data := range slice {marsha, _ := json.Marshal(data)m := fmt.Sprintf(`%s`, marsha)i++doc := esutil.BulkIndexerItem{Index:      *indexName,Action:     typeBulk,DocumentID: strconv.Itoa(i),Body:       strings.NewReader(m),OnSuccess: func(ctx context.Context, item esutil.BulkIndexerItem, item2 esutil.BulkIndexerResponseItem) {fmt.Printf("[%d] %s test/%s", item2.Status, item2.Result, item.DocumentID)},OnFailure: func(ctx context.Context, item esutil.BulkIndexerItem, item2 esutil.BulkIndexerResponseItem, err error) {if err != nil {fmt.Printf(" ERROR: %s \n", err)} else {fmt.Printf("ERROR: %s: %s \n", item2.Error.Type, item2.Error.Reason)}},}err := indexer.Add(ctx, doc)if err != nil {fmt.Println(" bulk upsert Add doc fail,", err)}}stats := indexer.Stats()fmt.Println(strings.Repeat("-", 80))dur := time.Since(start)m := int64(1000.0 / float64(dur/time.Millisecond) * float64(stats.NumFlushed))if stats.NumFailed > 0 {fmt.Printf("[%s.bulk:%s]总数据[%d]行,其中失败[%d], 耗时 %v (速度:%d docs/秒)\n",*indexName,typeBulk,stats.NumAdded,stats.NumFailed,dur.Truncate(time.Millisecond), m)} else {fmt.Printf("[%s.bulk:%s]处理数据[%d]行,耗时%v (速度:%d docs/秒)\n",*indexName,typeBulk,stats.NumAdded,dur.Truncate(time.Millisecond), m)}err = indexer.Close(ctx)//如果没有关闭就需要循环关闭直到彻底关闭if err != nil {go func(ctx2 context.Context) {for {err = indexer.Close(ctx2)if err != nil {return}}}(ctx)}return nil
}
方式三
func (e *ElasticSearch) Findne(ctx context.Context, indexName string, queryStr any, size uint, offset uint) error {if err, _ := e.ClientTyped.Indices.Exists(indexName).Do(ctx); err == false {return fmt.Errorf("该索引不存在,不能查找:%s", err)}typeList := reflect.TypeOf(queryStr)if typeList.Kind() == reflect.Ptr {typeList = typeList.Elem()}val := reflect.ValueOf(queryStr)if val.Kind() == reflect.Ptr {val = val.Elem()}var name stringvar value stringquery := make(map[string]types.MatchQuery, typeList.NumField())var chouse boolchouse = truevar dol types.HitsMetadatafor i := 0; i < typeList.NumField(); i++ {que := &types.MatchQuery{Lenient:             &chouse,FuzzyTranspositions: &chouse,}name = typeList.Field(i).Namevalue = val.FieldByName(name).String()que.Query = valuequery[name] = *quedo, _ := e.ClientTyped.Search().Index(indexName).Query(&types.Query{Match: map[string]types.MatchQuery{"price": {Query: "123456"},},},).From(int(offset)).Size(int(size)).Do(ctx)dol = do.Hits}mapp["*esll.ProductES"] = &ProductES{}m := mappfmt.Println(m)//获取类型typeLs := reflect.TypeOf(queryStr)nam := typeLs.String()fmt.Println(nam)//从map中找到对应的结构体key := mapp[typeLs.String()]list := make([]any, 0)//深拷贝/*valo := &keykey2 := *valokey3 := &key2*/var co anyfor i := 0; i < len(dol.Hits); i++ {co = deepcopy.Copy(key)//转换为json字符数组marshalJSON, _ := dol.Hits[i].Source_.MarshalJSON()//解码并绑定_ = json.Unmarshal(marshalJSON, &co)list = append(list, co)}return nil
}

查询

var mapp = make(map[string]any, 0)func (e *ElasticSearch) FindOne(ctx context.Context, searchStruct *SearchStruct) (map[string]any, error) {do, err := e.ClientTyped.Search().Index(searchStruct.IndexName).Query(&types.Query{MatchAll: &types.MatchAllQuery{Boost:      &searchStruct.Boost,QueryName_: &searchStruct.FieldName,},},).From(searchStruct.Form).Size(searchStruct.Size).Do(ctx)if err != nil {return nil, err}//name := make(map[string]any)marshalJSON, err := do.Hits.Hits[0].Source_.MarshalJSON()if err != nil {return nil, err}var p ProductES_ = json.Unmarshal(marshalJSON, &p)fmt.Println(p)//listMap := make(map[string]any)//for i := 0; i < len(do.Hits.Hits); i++ {//	structName := name["structName"]//	stringE, _ := ToStringE(structName)//	structs := mapp[stringE]//	structl := deepcopy.Copy(structs)//	_ = json.Unmarshal(marshalJSON, &structl)//	toStringE, _ := ToStringE(i)//	listMap[toStringE] = stringE//}//return nil, err}// ToStringE 字符串转换工具
func ToStringE(i any) (string, error) {i = indirectToStringerOrError(i)switch s := i.(type) {case string:return s, nilcase bool:return strconv.FormatBool(s), nilcase float64:return strconv.FormatFloat(s, 'f', -1, 64), nilcase float32:return strconv.FormatFloat(float64(s), 'f', -1, 32), nilcase int:return strconv.Itoa(s), nilcase int64:return strconv.FormatInt(s, 10), nilcase int32:return strconv.Itoa(int(s)), nilcase int16:return strconv.FormatInt(int64(s), 10), nilcase int8:return strconv.FormatInt(int64(s), 10), nilcase uint:return strconv.FormatUint(uint64(s), 10), nilcase uint64:return strconv.FormatUint(uint64(s), 10), nilcase uint32:return strconv.FormatUint(uint64(s), 10), nilcase uint16:return strconv.FormatUint(uint64(s), 10), nilcase uint8:return strconv.FormatUint(uint64(s), 10), nilcase json.Number:return s.String(), nilcase []byte:return string(s), nilcase template.HTML:return string(s), nilcase template.URL:return string(s), nilcase template.JS:return string(s), nilcase template.CSS:return string(s), nilcase template.HTMLAttr:return string(s), nilcase nil:return "", nilcase fmt.Stringer:return s.String(), nilcase error:return s.Error(), nildefault:return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i)}
}var (errorType       = reflect.TypeOf((*error)(nil)).Elem()fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
)// Copied from html/template/content.go.
// indirectToStringerOrError returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
// or error,
func indirectToStringerOrError(a any) any {if a == nil {return nil}v := reflect.ValueOf(a)for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !v.IsNil() {v = v.Elem()}return v.Interface()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/583226.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【STM32】程序在SRAM中运行

程序在RAM中运行 1、配置内存分配。 2、修改跳转文件 FUNC void Setup(void) { SP _RDWORD(0x20000000); PC _RDWORD(0x20000004); } LOAD RAM\Obj\Project.axf INCREMENTAL Setup(); 3、修改下载ROM地址和RAM地址&#xff1b; 中断向量表映射 中断向量表映射到SRA…

应用在网络摄像机领域中的国产音频ADC芯片

IPC&#xff1a;其实叫“网络摄像机”&#xff0c;是IP Camera的简称。它是在前一代模拟摄像机的基础上&#xff0c;集成了编码模块后的摄像机。它和模拟摄像机的区别&#xff0c;就是在新增的“编码模块”上。模拟摄像机&#xff0c;顾名思义&#xff0c;输出的是模拟视频信号…

Apache DolphinScheduler 3.1.9 版本发布:提升系统的稳定性和性能

&#x1f680;我们很高兴宣布&#xff0c;Apache DolphinScheduler 的最新版本 3.1.9 已正式发布&#xff01;此版本在 3.1.8 的基础上进行了关键的 bug 修复和文档更新&#xff0c;共计修复了 14 个 bug 和改进了 3 个文档。 主要更新亮点 本次更新重点解决了以下几个关键问题…

阿里云30个公共云地域、89个可用区、5个金融云和政务云地域

阿里云基础设施目前已面向全球四大洲&#xff0c;公共云地域开服运营30个公共云地域、89个可用区&#xff0c;此外还拥有5个金融云、政务云地域&#xff0c;并且致力于持续的新地域规划和建设&#xff0c;从而更好的满足用户多样化的业务和场景需求。伴随着基础设施的加速投入和…

AI大模型引领未来智慧科研暨丨ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

Unity Window安装包制作

Unity Window安装包制作 介绍一、RAR自解压方式1、找到Unity打包的可执行程序2.创建自解压文件3.配置设置4、最后点击确定等待压缩完成即可&#xff08;默认生成位置为你选中文件右键点击添加到压缩文件时的路径&#xff09; 二、Setup Factory工具安装制作Window安装包相关常用…

助力城市部件[标石/电杆/光交箱/人井]精细化管理,基于YOLOv6开发构建生活场景下城市部件检测识别系统

井盖、店杆、光交箱、通信箱、标石等为城市中常见部件&#xff0c;在方便居民生活的同时&#xff0c;因为后期维护的不及时往往会出现一些“井盖吃人”、“线杆、电杆、线缆伤人”事件。造成这类问题的原因是客观的多方面的&#xff0c;这也是城市化进程不断发展进步的过程中难…

Vue - Class和Style绑定详解

1. 模板部分 <template><div><!-- Class 绑定示例 --><div :class"{ active: isActive, text-danger: hasError }">Hello, Vue!</div><!-- Class 绑定数组示例 --><div :class"[activeClass, errorClass]">Cla…

10. Opencv检测并截取图中二维码

1. 说明 在二维码扫描功能开发中,使用相机扫描图片时,往往图片中的信息比较多样,可能会造成二维码检测失败的问题。一种提高检测精度的方式就是把二维码在图片中单独抠出来,去除其它冗余信息,然后再去识别这张提取出来的二维码。本篇博客记录采用的一种实现二维码位置检测…

编程笔记 GOLANG基础 003 Go语言开发环境搭建

编程笔记 GOLANG基础 003 Go语言开发环境搭建 一、安装VSCODE二、安装GO语言主程序 Golang的学习从开发环境搭建开始。本例记录的是WINDOWS平台下使用VSCODE做为开发工具的搭建过程。网上查到的资料都是以前版本的方法&#xff0c;新版Golang发生了一些变化。各位参数环境搭建时…

Unity Shader UVLightReveal (紫外线显示,验钞效果)

Unity Shader UVLightReveal &#xff08;紫外线显示&#xff0c;验钞效果&#xff09; UVLight Reveal 实现验钞机的效果实现方案操作实现1.Light2.将另一个图形加入3.加上图形效果4.加上灯光的颜色自定义判定 源码 UVLight Reveal 实现验钞机的效果 大家应该都有见过验钞机验…

Aseprite编译

官方网站 : https://www.aseprite.org/ Aseprite编译 步骤 : 1> App Store 下载安装 XCode 2> 安装 brew # /bin/bash -c "$(curl -fsSL https://gitee.com/ineo6/homebrew-install/raw/master/install.sh)" 或 # /bin/zsh -c "$(curl -fsSL https://g…

GoogleNetv1:Going deeper with convolutions更深的卷积神经网络

文章目录 GoogleNetv1全文翻译论文结构摘要1 引言2 相关工作3 动机和高层考虑稀疏矩阵 4 结构细节引入1x1卷积核可以减少通道数 5 GoogleNet6 训练方法7 ILSVRC 2014 分类挑战赛设置和结果8 ILSVRC 2014检测挑战赛设置和结果9 总结 论文研究背景、成果及意义论文图表 GoogleNet…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第六节 理解垃圾回收GC,提搞程序性能

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第六节 理解垃圾回收GC&#xff0c;提搞程序性能 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基…

YOLOv5改进 | 2023Neck篇 | CCFM轻量级跨尺度特征融合模块(RT-DETR结构改进v5)

一、本文介绍 本文给大家带来的改进机制是轻量级跨尺度特征融合模块CCFM&#xff08;Cross-Scale Feature Fusion Module&#xff09;其主要原理是&#xff1a;将不同尺度的特征通过融合操作整合起来&#xff0c;以增强模型对于尺度变化的适应性和对小尺度对象的检测能力。我将…

OpenHarmony南向之Audio

音频架构 Audio驱动框架基于HDF驱动框架实现&#xff0c;包含内核态&#xff08;KHDF&#xff09;&#xff0c;和用户态&#xff08;UHDF&#xff09;&#xff0c; 对北向提供音频HDI接口 音频框架图 驱动架构主要由以下几部分组成。 HDI adapter&#xff1a;实现Audio HAL层…

Spring 是如何解决循环依赖问题的方案

文章目录 Spring 是如何解决循环依赖问题的&#xff1f; Spring 是如何解决循环依赖问题的&#xff1f; 我们都知道&#xff0c;如果在代码中&#xff0c;将两个或多个 Bean 互相之间持有对方的引用就会发生循环依赖。循环的依赖将会导致注入死循环。这是 Spring 发生循环依赖…

数据库开发之图形化工具以及表操作的详细解析

2.3 图形化工具 2.3.1 介绍 前面我们讲解了DDL中关于数据库操作的SQL语句&#xff0c;在我们编写这些SQL时&#xff0c;都是在命令行当中完成的。大家在练习的时候应该也感受到了&#xff0c;在命令行当中来敲这些SQL语句很不方便&#xff0c;主要的原因有以下 3 点&#xff…

Android Studio 进行NDK开发,实现JNI,以及编写C++与Java交互(Java调用本地函数)并编译出本地so动态库

1.首先认识一下NDK。 &#xff08;1&#xff09;什么是NDK&#xff1f; NDK全称是Native Development Kit&#xff0c;NDK提供了一系列的工具&#xff0c;帮助开发者快速开发C/C的动态库&#xff0c;并能自动将so和java应用一起打包成apk。NDK集成了交叉编译器&#xff08;交叉…

Android 13 动态启用或禁用IPV6

介绍 客户想要通过APK来控制IPV6的启用和禁用&#xff0c;这里我们通过广播的方式来让客户控制IPV6。 效果展示 adb shell ifconfig 这里我们用debug软件&#xff0c;将下面节点置为1 如图ipv6已被禁用了 echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6 修改 接下来…