Elasticsearch基本操作

来源:https://www.cnblogs.com/leohahah/archive/2019/01/23/10310214.html
-Advertisement-
Play Games

在學習Elasticsearch的過程中想找一些可以系統的描述es操作的文章,但是官網沒有中文頁面,ES中文指南的排版和翻譯又很突兀和不協調,因此決定自己看一遍官方的maunal總結一下,由於沒時間把所有章節全部翻一遍,所以寫一篇學習筆記以便完成初步的學習。 概念總覽: 在描述ES的基本操作之前,首 ...


在學習Elasticsearch的過程中想找一些可以系統的描述es操作的文章,但是官網沒有中文頁面,ES中文指南的排版和翻譯又很突兀和不協調,因此決定自己看一遍官方的maunal總結一下,由於沒時間把所有章節全部翻一遍,所以寫一篇學習筆記以便完成初步的學習。

概念總覽:

在描述ES的基本操作之前,首先來介紹幾個概念:

Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices   -> Types  -> Documents -> Fields

以上是早期的官方文檔貼出的一個概念介紹圖,其含義不用多說,其實ES更適合與MongoDB類比:

MongoDB       -> DBs       -> Collections -> Documents -> Fields
Elasticsearch -> Indices   -> Types       -> Documents -> Fields

ES里的Index可以看做一個庫,Documents相當於表的行,而Types相當於表。

但是Types的概念將會被逐漸弱化並可能在未來版本中刪除,而在Elasticsearch 6中,一個index下已經只能包含一個type了,因此可以將index理解為一個表,types意如其名僅用於展示一個document所屬的分類,實際上在本文對ES進行操作時由於index和type的一對一關係,許多時候查詢document已經只需要指定index而無需再指定type了。

本文使用Elasticsearch 6.5.4和Kibana 6.5.4下的環境進行演示。

一、Kibana命令行操作

使用Kibana操作ES是當前最簡單的一種方式,且提供命令補全、index名稱補全等便捷的功能。同時console界面的小扳手點進去還有和官方手冊里一樣的“copy as CURL”選擇,將選中的命令copy之後粘貼到linux中就會轉換為curl命令的格式,對於想要瞭解curl直接操作ES的同學是很有幫助的。

我個人並不建議直接使用curl操作ES,因為很多時候需要自己設置header,麻煩且低效。

Elasticsearch官方操作手冊地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

這裡參考官網的reference手冊對內置API進行詳細梳理,由於官方手冊的介紹方式不適用於我這種新手,我只能打亂順序學習,本部分的介紹基本遵循學習傳統資料庫的流程,主要分為以下7個部分:

Note:本文所有命令都是在Kibana console操作的,關於Kibana的安裝配置和使用,參考《Kibana安裝配置》一文。

1.數據結構搭建

結構的搭建主要包含index的創建和刪除、查詢等等,types無需創建。

#創建名為test的index,兩種寫法等同,名字不能包含特殊字元,只能小寫,不能以-, _, +開頭,不能超過255位元組。
PUT test
PUT /test --PUT /test的本質是PUT http://ip:9200/test,kibana做了優化因此寫不寫之前的/無所謂
#當然你還可以直接插入一條數據,index會自動被創建
PUT leo/dramas/1
{
  "name":"權力的游戲"
}
#查看創建好的index的詳細信息
GET leo
#刪除index
DELETE leo
#查詢當前所有的index,這裡調用了_cat的API
GET _cat/indices

上圖為我測試創建的多個indices,每列的列名分別是:

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
--其中pri表示number of shards,rep表示number of replicas,新建的index health為yellow的原因是未搭建集群。

在使用GET <index_name>查看index詳細信息時可以看到,每個index下都有一個名為mapping的屬性,這個屬性用於描述當前type下的大致field有哪些,當然也別忘了在6.5.4版本里一個index下只有一種type了。

2.增

即向ES插入數據:

#插入單條數據,用PUT或POST都可以
PUT test/books/1 
{"name":"《阿Q正傳》","price":100}
PUT test/books/2
{"name":"《鋼鐵是怎樣煉成的》","price":200}
PUT test/books/3
{"name":"《西游記》","price":300}

插入多條數據,目前只能用_bulk API來實現,index表示新插入數據,create同理,在python的index()方法中op_type=create表示如果index不存在那麼直接創建index並插入數據,而op_type=index表示向已存在的index中插數據,此外還可以一起bulk delete、update等操作。

PUT test/books/_bulk
{ "index":{"_id":4}}
{"name":"《圍城》","price":101}
{ "index":{"_id":5}}
{"name":"《格林童話》","price":108}
}
#如果你不想設置主鍵_id,那麼可以直接置空,系統會創建預設主鍵,寫法如下:
PUT test/books/_bulk
{ "index":{}}
{"name":"《圍城》","price":101}
{ "index":{}}
{"name":"《格林童話》","price":108}

查看下插入的數據:

GET test/books/_search
{"query":{"match_all":{}}}
GET test/books/_search
{"query":{"match":{"_id":1}}}
GET test/_search
{"query":
  {"range":
    {"price":
      {"lte":1000}
    }
  }
}

index下也有_search API因此這裡你也可以省略books直接查詢整個index所有types下的記錄,實際上在6版本中由於types概念的弱化(一個index只能有一種type)許多查詢都可以直接不寫type名了。
這裡的query和range以及lte都是DSL關鍵字,其實query只相當於模糊查詢或全文搜索。關於查詢,更系統的DSL(domain specific language)關鍵字及示例會在第5部分“查”補充。

3.刪

記錄的刪除通常由2個API,直接DELETE和POST _delete_by_query完成,示例如下:

#DELETE只能根據ID進行刪除,本例中刪除的是系統自定義的ID因此比較奇怪。
DELETE test/books/_mbEdGgBH8b_BYBmOW-C
#_delete_by_query API允許你刪除符合query條件的記錄,其query body與上邊的查詢過濾的query body規則一樣。
POST test/_delete_by_query
{"query":
  {"range":
    {"price":
      {"lte":1000}
    }
  }
}
#其實刪除、修改和查詢還涉及到多版本控制的概念,這個概念在傳統資料庫中已經很熟悉了,就是為了保證數據一致性的。
#關於版本控制的內容會在第6部分“版本控制”補充。

4.改

記錄更新也是2個API,_update和_update_by_query,前者根據ID進行更新,後者可以更新指定的query結果。此外你還可以不使用這兩個API直接像新插入數據那樣更新數據,只是此時你的body部分必須包含所有的fields了,否則操作完畢後你會發現document只剩下你所更新的那幾個fields,其他的全沒了。

至於為什麼刪除使用DELETE命令,而更新只能用_update的API,只是因為ES是RESTFUL風格的,http的指令有DELETE但並沒有UPDATE關鍵字。

更新涉及到版本控制以便維護數據一致性,其實分為兩個操作:get和reindex,大致步驟是:首先取到相應的document,然後執行更新script,最後返回執行的結果。至於具體的多版本控制機制將在第6部分解釋。

更新涉及的DSL語言也與其他操作很不一樣:

#_update API,表示將id為5的document的price改為100
POST test/books/5/_update
{"script":
  {"source":"ctx._source.price=params.price",
   "lang":"painless",
   "params":{
     "price":100
    }
  }

這裡的script,source,lang,params都是DSL關鍵字,lang=painless表示使用painless腳本語言來編寫script來完成。
ctx我暫理解為當前事務,ctx._source表示當前定位的document,params表示本次更新用到的數據,source則表示更新操作,通俗來講就是用params的數據+source的操作一起完成更新。

#如果只是簡單的增加新field和刪除field那麼格式就比較簡單:
POST test/books/5/_update
{
  "script":"ctx._source.booktype='少兒童話'"
}
POST test/books/5/_update
{
  "script":"ctx._source.remove('少兒童話')"
}
#此外ctx._source或ctx._source.<field_name>還有很多其他的方法和屬性,這裡貼一個官網的示例來作出引申,更多的示例慢慢實踐吧。
POST test/_doc/1/_update
{
    "script" : {
        "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
        "lang": "painless",
        "params" : {
            "tag" : "green"
        }
    }
}

這個示例的含義就是:對於id=1的document,如果tags包含green字元,那麼刪掉這個document,否則不操作。至於contains是模糊匹配還是精確匹配,有興趣的可以花幾十秒做個測試。

5.查

4個部分的示例中已經有許多查詢的示例了,這裡在之前的基礎上介紹一些比較複雜的查詢,首先來瞭解一個DSL的概念:

DSL:Domain Specific Language,ES提供一種基於JSON的查詢語言,這種查詢語言包含兩種子句模式:

    1.Leaf query clauses

    2.Compound query clauses

好吧,其實這裡介紹這兩個概念對理解複雜查詢毫無作用,我只是照搬下官方手冊,防止某天頓悟時找不到概念,接下來再看兩個DSL的概念:

Query一般來說包含兩各部分:query context 或 filter context:

舉例來說:

GET /_search
{
  "query": { 
    "bool": { 
      "must": [
        { "match": { "title":   "Search"        }}, 
        { "match": { "content": "Elasticsearch" }}  
      ],
      "filter": [ 
        { "term":  { "status": "published" }}, 
        { "range": { "publish_date": { "gte": "2015-01-01" }}} 
      ]
    }
  }
}

這個例子的query就包含了所有2種context,可以見到他們的關係並不是並行的,filter包含在query.bool中,那麼filter和前邊的must匹配有什麼區別呢?
1.must--match進行的匹配是模糊匹配
    比如上邊的title": "Search"不僅會把‘Search’匹配到,‘Search 123’也會匹配到,並且ES會為匹配到的每個記錄打分,稱作scoring,表示匹配程度。
2.而filter是精確匹配
    表示在以上模糊匹配的結果中尋找精確匹配到的值,且filter到的記錄不會進行打分,term和range都是filter context的關鍵字,前者表示等值匹配,後者表示範圍匹配。

到這裡DSL的4個概念就介紹完了,是的全部介紹完了。官網總共也就這幾行,更多關於關鍵字的具體應用需要到特定的頁面且也通常都是一個簡單的示例完事,因此只能靠日常實踐了。

介紹完DSL那麼回到實際應用中來,用於查詢的API一般也是2種:直接通過GET index/doc_type/doc_id獲取,以及_search API

#GET獲取比較簡單,只要有id就可以了,沒id請使用_search API
GET test/books/1
#_search API是查詢使用的核心API,包含諸如聚合、排序、集群查詢、explain API等等等等,這裡只貼個官方鏈接和一個示例算啦,重在實踐掌握。
https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html
POST /twitter/_search?routing=kimchy
{
    "query": {
        "bool" : {
            "must" : {
                "query_string" : {
                    "query" : "some query string here"
                }
            },
            "filter" : {
                "term" : { "user" : "kimchy" }
            }
        }
    }
}
#這裡的?routing=kimchy是指在集群中查詢時可以指定名為kimchy的shard。

6.版本控制

Versionning,在官網中暫未找到獨立的說明頁面,只找到2篇古老的博客,分別是2011年和2013年的,地址如下:

https://www.elastic.co/blog/versioning

https://www.elastic.co/blog/elasticsearch-versioning-support

第一篇:

內容顯示versioning是由elasticsearch在0.15版本引入的新特性”樂觀併發控制“引申出來的,只介紹了每個document都會有個由系統控制自增的_version屬性,並未對版本控制機製作出細節解釋。

不過既然是樂觀併發控制我們可以參考傳統RDBMS資料庫中的樂觀鎖來理解,即資料庫伺服器會自動進行document快照存儲以便實現事務一致性,接下來看下第二篇博客(實際上看完第二篇博客,裡邊也確實介紹了樂觀鎖定)。

第二篇:

以一個經典的丟失更新示例來描述下樂觀併發控制的必要性:

#首先造一條數據
PUT bank_account/shanghai/1
{
  "name":"leo",
  "deposit":100
}
GET bank_account/shanghai/1
#如下為插入的數據,可以看到_version屬性值為1
{
  "_index" : "bank_account",
  "_type" : "shanghai",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "name" : "leo",
    "deposit" : 100
  }
}
#如果這時候兩個商戶同時要從我賬戶里扣1塊錢,結果就是兩家同時取到我賬戶餘額為100,各扣了一元並把99的餘額寫入ES,這顯然是錯的。因此ES推出了versioning特性。

對於index中的每條記錄都會有一個_version的屬性,其取值範圍為:[1,2^63),插入數據時預設的_version都是1,每次對這個document進行修改或刪除操作都會使其+1,這個過程是由ES自己控制的。

總結一下Versioning的工作機制其實是這樣的,我們以一個投票計數案例為例,1表示球員的ID,每次有人為id=1的球員投票都將投票計數votes+1:

POST NBA/all_star_votes/1/_update?retry_on_conflict=5
{"script":"ctx._source.votes += 1"}

1.首先查詢到你要更新的documents。

2.然後進行version check,記下你查詢到的documents的_version。

3.更新時指定_version=<第二步中查到的version>

4.ES server端收到更新請求後開始進行衝突檢測,如果發現有人在這期間成功投了票(那麼_version就會變化),那麼直接返回一個http的409 conflict錯誤碼,如果可以更新那麼自然返回200 ok就好。

5.如果你顯式的設置了retry_on_conflict參數,那麼步驟四的表現還會有所變化:在發現記錄被更改後,server端會嘗試根據scripts將votes+1,然後將_version也+1,然後使用新的_version值和votes值進行更新,如果再次衝突那麼重覆之前的操作直到成功更新或達到retry_on_conflict的重覆次數。

以上操作據官方手冊說是節省了頻繁獲取/釋放鎖的開銷,versioning特性並非強制開啟的,只有你指定了version參數或者retry_on_conflict參數時,ES才會啟用versioning特性為你進行version check和衝突檢測。因此對於類似投票計數這種field的更新你可以開啟versionging特性,對於不規則的併發更新你可以棄用此特性直接使用程式隊列或者乾脆用關係型資料庫存儲數據,對於存款更新這種不規則併發更新的金融場景,併發請求之間不可能每次都增減相同的金額,使用retry_on_conflict顯然是無效的,這種場景用關係型資料庫顯然更安全。

當然對於delete操作來說versioning的表現又有所不同,因為如果一個系統頻繁的進行數據的刪除,那麼保存大量的舊version會導致資源迅速被耗盡,因此對於delete的記錄ES的預設保存version的時間是1min,這被稱作GC(垃圾回收),你可以通過修改index.gc_deletes參數來擴大此超時時間。

PS:官網沒說update操作留下的舊version是否也會被定期清除,這個可以試驗來驗證,插入一條數據多次更新後進行指定_version的查詢即可驗證,這裡節省時間懶的測了。

7.集群操作

集群操作這裡省略,會寫在單獨的集群搭建筆記中。

二、Python介面操作

你可以使用Python內置的REST API:requests module來進行es的操作,但是es提供了一種更加貼近elasticsearch概念體系的API:elasticsearch-py,因此這裡使用elasticsearch-py來進行演示。

elasticsearch API詳述:https://elasticsearch-py.readthedocs.io/en/master/api.html

Note:為與Python語言相容,避免出現關鍵字衝突,使用from_代替from,doc_type代替type參數。且為保持一致性和安全性,本介面推薦使用關鍵字傳參,不建議使用位置傳參。

先來一個簡單的演示示例:

# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch
es = Elasticsearch(hosts='http://10.0.1.49:9200/')
es.delete_by_query(index="test",doc_type="books",body={"query": { "match_all":{}}})
#這裡的id=1/2在進入ES後就變為了預設主鍵,查詢時不能用id來查,而是要用_id。當然這裡的主鍵概念其實是借用了mongo或其他傳統關係型資料庫的概念,方便理解而已。
es.index(index="test", doc_type="books",id=1,body={"name": "《鋼鐵是怎樣煉成的》","price":100})
es.index(index="test", doc_type="books",id=2,body={"name": "《狂人日記》","price":200})
# res=es.search(index="test",doc_type="books",body={"query": {"match_all": {}}})
# print(res)
res=es.search(index="test",
              doc_type="books",
              body={"query":
                        {"range":
                             {"price":
                                  { "lt":400}
                              }
                         },
                    "sort":{
                        "_id":
                            {}
                            # {"order":"desc"}
                            }
                    }
              )
print("%d documents found" % res['hits']['total'])
for doc in res['hits']['hits']:
    print("%s) %s" % (doc['_id'], doc['_source']['name']))

這裡邊涉及到一些基礎的method,這些method的詳細參數和用法都可以在上邊貼出的elasticsearch API詳述網址中找到。

elasticsearch module包含CatClient, ClusterClient, IndicesClient, IngestClient, NodesClient, SnapshotClient and TasksClient等7個client子類以及一些其他暫無需介紹的類,此外還有一個底層訪問介面Elasticsearch類,你能且也只能通過Elasticsearch來訪問前述的7種介面。

定義Elasticsearch class的部分相關代碼為:

......
from ..transport import Transport
from .indices import IndicesClient
from .ingest import IngestClient
from .cluster import ClusterClient
from .cat import CatClient
from .nodes import NodesClient
from .remote import RemoteClient
from .snapshot import SnapshotClient
from .tasks import TasksClient
class Elasticsearch(object):
    def __init__(self, hosts=None, transport_class=Transport, **kwargs):
        """
        :arg transport_class: :class:`~elasticsearch.Transport` subclass to use.
        """
        self.transport = transport_class(_normalize_hosts(hosts), **kwargs)
        # namespaced clients for compatibility with API names
        self.indices = IndicesClient(self)
        self.ingest = IngestClient(self)
        self.cluster = ClusterClient(self)
        self.cat = CatClient(self)
        self.nodes = NodesClient(self)
        self.remote = RemoteClient(self)
        self.snapshot = SnapshotClient(self)
        self.tasks = TasksClient(self)
......

另一種通俗的解釋方式就是:

    當你定義了一個Elasticsearch實例後,會衍生N種諸如IndicesClient、IngestClient等實例,你可以根據自己的需求通過調用Elasticsearch的屬性來獲取這些實例,進而調用他們的各種method,這些屬性值可以是__init__方法中任意屬性,調用這些屬性後你就可以使用這些屬性實例的特有method了,這些client子類實例的屬性可以在上邊貼出的網址里學習,這裡只簡略貼一下核心介面類Elasticsearch相關解釋:

class elasticsearch.Elasticsearch(hosts=None, transport_class=<class 'elasticsearch.transport.Transport'>, **kwargs)

    hosts參數使用RESTFUL風格定義,即URL格式,類似上邊的'http://10.0.1.49:9200/'

   除此之外你還可以使用SSL協議創建連接,其參數官網並未單獨列出,但可以通過其SSL連接示例獲知使用方式。

   此class全部的method包含:

bulk(**kwargs)
clear_scroll(**kwargs)
count(**kwargs)
create(**kwargs)
delete(**kwargs)
delete_by_query(**kwargs)
delete_script(**kwargs)
exists(**kwargs)
exists_source(**kwargs)
explain(**kwargs)
field_caps(**kwargs)
get(**kwargs)
get_script(**kwargs)
get_source(**kwargs)
index(**kwargs)
info(**kwargs)
mget(**kwargs)
msearch(**kwargs)
msearch_template(**kwargs)
mtermvectors(**kwargs)
ping(**kwargs)
put_script(**kwargs)
reindex(**kwargs)
reindex_rethrottle(**kwargs)
render_search_template(**kwargs)
scroll(**kwargs)
search(**kwargs)
search_shards(**kwargs)
search_template(**kwargs)
termvectors(**kwargs)
update(**kwargs)
update_by_query(**kwargs)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 命令: | 作用: Linux允許將一個命令的輸出通過管道作為另一個命令的輸入,管道左側為寫入,右側為讀取 使用: 命令 | 命令 例子: 分頁顯示當前頁的目錄及文件詳情 ls -lha | more 查詢當前目錄下有python文字的目錄及文件 ls -lha | grep python ...
  • 命令: echo 作用: echo有重覆的意思,會在終端中顯示參數指定的文字,通常會和重定向聯合使用 使用: echo 文字內容 例子: 在終端中顯示hello echo hello 命令: > 和 >> 作用: Linux允許將命令執行結果重定向到一個文件,將本應顯示在終端上的內容 輸出 / 追加 ...
  • 本文收錄在容器技術學習系列文章總目錄 1、使用kubectl 1.1 介紹 kubectl用於運行Kubernetes集群命令的管理工具。 1.2 語法 command:指定要在一個或多個資源執行的操作,例如操作create,get,describe,delete。TYPE:指定資源類型Resour ...
  • Oracle創建函數的方法如下: 舉個例子: 調用一下 並輸出結果: ...
  • 資料庫類型 一、版本介紹和選擇 PerconaDB 主流版本 企業版本 在Linux中啟動資料庫 二、MYSQL的體繫結構 MYSQL C/S結構 實例: 1、mysqld 的三層結構 2、MySQL的邏輯結構 三、MySQL安裝 1、 https://www.mysql.com/ MySQL官網 ...
  • ### 1. MYBATIS簡介 MYBATIS是持久層框架,大大的簡化了持久層開發。 當使用MYBATIS框架時,開發人員不必再編寫繁瑣的JDBC代碼,只需要定義好每個功能對應的抽象方法與需要執行的SQL語句即可! ### 2. 基本使用 #### 2.1. 添加依賴 需要在`pom.xml`中添 ...
  • 行列之間的互相轉換是ETL中的常見需求,在Spark SQL中,行轉列有內建的PIVOT函數可用,沒什麼特別之處。而列轉行要稍微麻煩點。本文整理了2種可行的列轉行方法,供參考。 本文鏈接:https://www.cnblogs.com/hhelibeb/p/10310369.html 測試數據準備 ...
  • 1.今天系統出現BUG,經過兩個小時的排查,發現是存儲過程中的SELECT @@IDENTITY的值發生錯亂,導致的系統BUG,經過百度,發現這個函數貌似和觸發器有衝突,一旦插入的表有觸發器的話,@@IDENTITY的值就不對了,建議用函數SCOPE_IDENTITY()來替換;可參考SqlServ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...