当前位置: 首页 > news >正文

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()
}

相关文章:

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

书写上回&#xff0c;上回讲到&#xff0c;Elasticsearch的使用前提即&#xff1a;语法&#xff0c;表结构&#xff0c;使用类型结构等。要学这个必须要看前面这个&#xff1a;GoLang学习之路&#xff0c;对Elasticsearch的使用&#xff0c;一文足以&#xff08;包括泛型使用思…...

鸿蒙APP的代码规范

鸿蒙APP的代码规范是为了确保代码质量、可读性和可维护性而定义的一系列规则和标准。以下是一些建议的鸿蒙APP代码规范&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 代码风格&#xff1a; 采用…...

蓝桥杯-每日刷题-027

出租汽车计费器 一、题目要求 题目描述 有一个城市出租汽车的计费规则是3公里内&#xff08;含3公里&#xff09;基本费6元&#xff0c;超过3公里&#xff0c;每一公里1.4元。 现在对于输入具体的公里数x&#xff08;0<x<1000&#xff09;&#xff0c;编程计算x公里所需…...

安装Node修改Node镜像地址搭建Vue脚手架创建Vue项目

1、安装VSCode和Node 下载VSCode Visual Studio Code - Code Editing. Redefined 下载Node Node.js (nodejs.org) 检验是否安装成功&#xff0c;WinR,输入cmd命令&#xff0c;使用node -v可以查看到其版本号 2、修改镜像地址 安装好node之后&#xff0c;开始修改镜像地址 …...

git 学习 之一个规范的 commit 如何写

最好的话做一件完整的事情就提交一次...

2023 年人工智能研究与技术排名前 10 的国家

人工智能研究是一项全球性的工作。虽然美国和中国因其对人工智能的贡献而备受关注&#xff0c;但事实是&#xff0c;世界各国都在涉足这项技术&#xff0c;尝试新的突破&#xff0c;并吸引投资者的关注。 斯坦福大学的《2023年人工智能报告》估计&#xff0c;到 2022 年&#…...

留言板(Mybatis连接数据库版)

目录 1.添加Mybatis和SQL的依赖 2.建立数据库和需要的表 3.对应表中的字段&#xff0c;补充Java对象 4.对代码进行逻辑分层 5.后端逻辑代码 之前的项目实例【基于Spring MVC的前后端交互案例及应用分层的实现】https://blog.csdn.net/weixin_67793092/article/details/134…...

第十二章 Sleuth分布式请求链路跟踪

Sleuth分布式请求链路跟踪 gitee:springcloud_study: springcloud&#xff1a;服务集群、注册中心、配置中心&#xff08;热更新&#xff09;、服务网关&#xff08;校验、路由、负载均衡&#xff09;、分布式缓存、分布式搜索、消息队列&#xff08;异步通信&#xff09;、数…...

EasyExcel多线程批量导出数据,动态表头,静态资源访问

1.导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency>2.建立实体 Data public class ActResultLogVO implements Serializable {private static…...

树莓派界面改成中文

安装完树莓派系统(Raspberry Pi OS with Desktop)&#xff0c;第一次启动时&#xff0c;时会有如下面二个图所示&#xff0c;让你选择区域时区和语言。 树莓派默认的语言为英文&#xff0c;如果你在安装时没有选择的话&#xff0c;默认的区域为英国&#xff0c;语言为英国英文&…...

软件工程期末复习

● 用例&#xff1a;借书 ●参与者&#xff1a;管理员,借阅者 ●操作流&#xff1a; ① 管理员进入图书借阅界面&#xff0c;用例开始。 ② 系统要求输入借阅者的借书证编码。 ③系统检验借书证编码,如果正确,则显示借阅者的信息。 A1&#xff1a;借书证编码有错。 A2: 如果该借…...

【linux】select实现定时器

/*秒级定时器*/ void seconds_sleep(unsigned long seconds) {if(seconds 0) return;struct timeval tv;tv.tv_secseconds;tv.tv_usec0;int err;do{errselect(0,NULL,NULL,NULL,&tv);}while(err<0 && errnoEINTR); }/*毫秒定时器*/void milliseconds_slee…...

Android 13 - Media框架(28)- MediaCodec(三)

上一节我们了解到 ACodec 执行完 start 流程后&#xff0c;会把所有的 input buffer 都提交给 MediaCodec 层&#xff0c;MediaCodec 是如何处理传上来的 buffer 呢&#xff1f;这一节我们就来了解一下这部分内容。 1、ACodecBufferChannel::fillThisBuffer ACodec 通过调用 A…...

Azure 学习总结

文章目录 1. Azure Function1.1 Azure Function 概念1.2 Azure Function 实现原理1.3 Azure Function 本地调试1.4 Azure Function 云部署 2. Azure API Managment 概念 以及使用2.1 Azure API 概念2.2 Azure API 基本使用 3. Service Bus 应用场景及相关特性3.1 Service Bus 基…...

数据库是否可以直接作为数据仓库的数据源

在数据仓库使用数据时&#xff0c;我们是否可以直接将数据库作为数据源&#xff1f;如果使用了&#xff0c;会存在哪些问题&#xff1f; 数据库中存储的是业务数据&#xff0c;存储方式是行式存储&#xff1b;而数据仓库中数据是以列式存储的&#xff1b;如果数据仓库要想使用…...

IntelliJ IDE 插件开发 | (四)开发一个时间管理大师插件

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听IntelliJ IDE 插件开发 |&#xff08;四&#xff09;开发一…...

【ChatGPT 默认强化学习策略】PPO 近端策略优化算法

PPO 近端策略优化算法 PPO 概率比率裁剪 演员-评论家算法演员-评论家算法&#xff1a;多智能体强化学习核心框架概率比率裁剪&#xff1a;逐步进行变化的方法PPO 目标函数的设计重要性采样KL散度 PPO 概率比率裁剪 演员-评论家算法 论文链接&#xff1a;https://arxiv.org…...

【银行测试】金融银行-理财项目面试/分析总结(二)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行理财相关的项…...

张江智荟毁约offer

毕业8年后&#xff0c;找工作被国企歧视学历&#xff01;已经收到了offer&#xff0c;在入职前一周被通知要撤回offer&#xff0c;拒绝录用&#xff0c;理由居然是他们只要本科211以上的人 这是我今天&#xff08;2023-12-26&#xff09;亲身经历的事&#xff0c;听说过面试前…...

ubuntu 系统终端颜色设置

1 开启终端颜色 # 第一步&#xff1a; 在 ~/.bashrc 中设置 force_color_promptyes# 第二步&#xff1a; 执行 source ~/.bashrc2 对于精减的 .bashrc 在 ~/.bashrc 中添加以下内容&#xff0c;再执行 source ~/.bashrc &#xff1a; # uncomment for a colored prompt, if…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...

【threejs】每天一个小案例讲解:创建基本的3D场景

代码仓 GitHub - TiffanyHoo/three_practices: Learning three.js together! 可自行clone&#xff0c;无需安装依赖&#xff0c;直接liver-server运行/直接打开chapter01中的html文件 运行效果图 知识要点 核心三要素 场景&#xff08;Scene&#xff09; 使用 THREE.Scene(…...