一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

現(xiàn)代的服務(wù)端技術(shù)棧:Golang/Protobuf/gRPC詳解

電子設(shè)計(jì) ? 來源:電子設(shè)計(jì) ? 作者:電子設(shè)計(jì) ? 2020-12-25 17:32 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

譯注:

并發(fā)與并行:并發(fā)是虛擬的并行,比如通過時間切片技術(shù)在單核CPU上運(yùn)行多個任務(wù),讓每個使用者“以為”自己在獨(dú)占這一CPU資源;并行是實(shí)際的同一時間多任務(wù)同時運(yùn)行,多數(shù)是指在多核CPU的場景下。

隊(duì)列與雙端隊(duì)列:隊(duì)列遵循先入先出的原則,從一端存數(shù),從另一端取數(shù),雙端隊(duì)列支持從隊(duì)列的兩端存數(shù)和取數(shù)。

阻塞和非阻塞:阻塞和非阻塞描述了程序等待返回結(jié)果時的狀態(tài),阻塞代表不返回結(jié)果就掛起,不進(jìn)行任何操作;非阻塞是在沒返回結(jié)果時可以執(zhí)行其他任務(wù)。

合作和搶占:高優(yōu)先級任務(wù)可以打斷其他正在運(yùn)行的低優(yōu)先級任務(wù),則調(diào)度器是搶占式的;反之,則是合作式的。

服務(wù)端編程的陣營中有很多新面孔,一水兒的谷歌血統(tǒng)。在谷歌開始將Golang應(yīng)用于其產(chǎn)品系統(tǒng)后,Golang快速的吸引了大量的關(guān)注。隨著微服務(wù)架構(gòu)的興起,人們開始關(guān)注一些現(xiàn)代的數(shù)據(jù)通信解決方案,如gRPC和Protobuf。在本文中,我們會對以上這些概念作一些簡要的介紹。

一、Golang

Golang又稱Go語言,是一個開源的、多用途的編程語言,由Google研發(fā),并由于種種原因,正在日益流行。Golang已經(jīng)有10年的歷史,并且據(jù)Google稱已經(jīng)在生產(chǎn)環(huán)境中使用了接近7年的時間,這一點(diǎn)可能讓大多數(shù)人大跌眼鏡。

Golang的設(shè)計(jì)理念是,簡單、現(xiàn)代、易于理解和快速上手。Golang的創(chuàng)造者將Golang設(shè)計(jì)為一個普通的程序員可以在一個周末的時間就可以掌握,并達(dá)到使用Golang進(jìn)行工作的程度。這一點(diǎn)我已經(jīng)親身證實(shí)。Golang的創(chuàng)造者,都是C語言原始草案的專家組成員,可以說,Golang根紅苗正,值得信賴。

理都懂,但話說回來,為什么我們需要另一門編程語言呢

多數(shù)場景下,確實(shí)并不需要。事實(shí)上,Go語言并不能解決其他語言或工具無法解決的新問題。但在一些強(qiáng)調(diào)效率、優(yōu)雅與直觀的場景下,人們通常會面臨一系列的相關(guān)問題,這正是Go所致力于解決的領(lǐng)域。Go的主要特點(diǎn)是:

一流的并發(fā)支持

內(nèi)核十分簡單,語言優(yōu)雅、現(xiàn)代

高性能

提供現(xiàn)代軟件開發(fā)所需要的原生工具支持

我將簡要介紹Go是如何提供上述的支持的。在Go語言的官網(wǎng)可以了解更多的特性和細(xì)節(jié)。

一流的并發(fā)支持

并發(fā)是多數(shù)服務(wù)端應(yīng)用所需要考慮的主要問題之一,考慮到現(xiàn)代微處理器的特性,并發(fā)也成為編程語言的主要關(guān)切之一。Go語言引入了“goroutine”的理念??梢园选癵oroutine”理解為一個“輕量級的用戶空間線程”(現(xiàn)實(shí)中,當(dāng)然遠(yuǎn)比這要復(fù)雜得多,同一線程可能會附著多路的goroutine,但這樣的提法可以讓你有一個大致的概念)。所謂“輕量級”,可以這樣理解,由于采用了十分袖珍的堆棧,你可以同時啟動數(shù)以百萬計(jì)的goroutine,事實(shí)上這也是Go語言官方所推薦的方式。在Go語言中,任何函數(shù)或方法都可以生成一個goroutine。比如,只需要運(yùn)行“go myAsyncTask()”就可以從“myAsyncTask”函數(shù)生成一個goroutine。示例代碼如下:

// This function performs the given task concurrently by spawing a goroutine

// for each of those tasks.

func performAsyncTasks(task []Task) {

for _, task := range tasks {

// This will spawn a separate goroutine to carry out this task.

// This call is non-blocking

go task.Execute()

goroutineExample.go hosted with ? by GitHub

(左右滑動查看全部代碼)

怎么樣,是不是很簡單?Go是一門簡單的語言,因此注定是以這樣的方式來解決問題。你可以為每個獨(dú)立的異步任務(wù)生成一個goroutine而不需要顧慮太多事情。如果處理器支持多核運(yùn)行,Go語言運(yùn)行時會自動的以并行的方式運(yùn)行所有的goroutine。那么,goroutine之間是如何通信的呢,答案是channel。

“channel”也是Go語言的一個概念,用于進(jìn)行g(shù)oroutine之間的通信。通過channel,你可以向另一個goroutine傳遞各種信息(比如Go語言概念里的type或者struct甚至是channel)。一個channel大體上是一個“雙端阻塞隊(duì)列”(也可以單端的)。如果需要goroutine基于特定條件觸發(fā)下一步的行動,也可以利用channel來實(shí)現(xiàn)goroutine的協(xié)作阻塞任務(wù)模式。

在編寫異步或者并發(fā)的代碼時,goroutine和channel這兩個概念賦予了編程者大量的靈活性和簡便性。可以籍此很容易的建立其他很有用的庫,比如goroutine pool,舉個簡單的例子:

package executor

import (

"log"

"sync/atomic"

// The Executor struct is the main executor for tasks.

// 'maxWorkers' represents the maximum number of simultaneous goroutines.

// 'ActiveWorkers' tells the number of active goroutines spawned by the Executor at given time.

// 'Tasks' is the channel on which the Executor receives the tasks.

// 'Reports' is channel on which the Executor publishes the every tasks reports.

// 'signals' is channel that can be used to control the executor. Right now, only the termination

// signal is supported which is essentially is sending '1' on this channel by the client.

type Executor struct {

maxWorkers int64

ActiveWorkers int64

Tasks chan Task

Reports chan Report

signals chan int

// NewExecutor creates a new Executor.

// 'maxWorkers' tells the maximum number of simultaneous goroutines.

// 'signals' channel can be used to control the Executor.

func NewExecutor(maxWorkers int, signals chan int) *Executor {

chanSize := 1000

if maxWorkers > chanSize {

chanSize = maxWorkers

executor := Executor{

maxWorkers: int64(maxWorkers),

Tasks: make(chan Task, chanSize),

Reports: make(chan Report, chanSize),

signals: signals,

go executor.launch()

return &executor

// launch starts the main loop for polling on the all the relevant channels and handling differents

// messages.

func (executor *Executor) launch() int {

reports := make(chan Report, executor.maxWorkers)

for {

select {

case signal := <-executor.signals:

if executor.handleSignals(signal) == 0 {

return 0

case r := <-reports:

executor.addReport(r)

default:

if executor.ActiveWorkers < executor.maxWorkers && len(executor.Tasks) > 0 {

task := <-executor.Tasks

atomic.AddInt64(&executor.ActiveWorkers, 1)

go executor.launchWorker(task, reports)

// handleSignals is called whenever anything is received on the 'signals' channel.

// It performs the relevant task according to the received signal(request) and then responds either

// with 0 or 1 indicating whether the request was respected(0) or rejected(1).

func (executor *Executor) handleSignals(signal int) int {

if signal == 1 {

log.Println("Received termination request...")

if executor.Inactive() {

log.Println("No active workers, exiting...")

executor.signals <- 0

return 0

executor.signals <- 1

log.Println("Some tasks are still active...")

return 1

// launchWorker is called whenever a new Task is received and Executor can spawn more workers to spawn

// a new Worker.

// Each worker is launched on a new goroutine. It performs the given task and publishes the report on

// the Executor's internal reports channel.

func (executor *Executor) launchWorker(task Task, reports chan<- Report) {

report := task.Execute()

if len(reports) < cap(reports) {

reports <- report

} else {

log.Println("Executor's report channel is full...")

atomic.AddInt64(&executor.ActiveWorkers, -1)

// AddTask is used to submit a new task to the Executor is a non-blocking way. The Client can submit

// a new task using the Executor's tasks channel directly but that will block if the tasks channel is

// full.

// It should be considered that this method doesn't add the given task if the tasks channel is full

// and it is up to client to try again later.

func (executor *Executor) AddTask(task Task) bool {

if len(executor.Tasks) == cap(executor.Tasks) {

return false

executor.Tasks <- task

return true

// addReport is used by the Executor to publish the reports in a non-blocking way. It client is not

// reading the reports channel or is slower that the Executor publishing the reports, the Executor's

// reports channel is going to get full. In that case this method will not block and that report will

// not be added.

func (executor *Executor) addReport(report Report) bool {

if len(executor.Reports) == cap(executor.Reports) {

return false

executor.Reports <- report

return true

// Inactive checks if the Executor is idle. This happens when there are no pending tasks, active

// workers and reports to publish.

func (executor *Executor) Inactive() bool {

return executor.ActiveWorkers == 0 && len(executor.Tasks) == 0 && len(executor.Reports) == 0

executor.go hosted with ? by GitHub

(左右滑動查看全部代碼)

內(nèi)核十分簡單,語言優(yōu)雅、現(xiàn)代

與其他多數(shù)的現(xiàn)代語言不同,Golang本身并沒有提供太多的特性。事實(shí)上,嚴(yán)格限制特性集的范圍正是Go語言的顯著特征,且Go語言著意于此。Go語言的設(shè)計(jì)與Java的編程范式不同,也不支持如Python一樣的多語言的編程范式。Go只是一個編程的骨架結(jié)構(gòu)。除了必要的特性,其他一無所有。

看過Go語言之后,第一感覺是其不遵循任何特定的哲學(xué)或者設(shè)計(jì)指引,所有的特性都是以引用的方式解決某一個特定的問題,不會畫蛇添足做多余的工作。比如,Go語言提供方法和接口但沒有類;Go語言的編譯器生成動態(tài)鏈接庫,但同時保留垃圾回收器;Go語言有嚴(yán)格的類型但不支持泛型;Go語言有一個輕量級的運(yùn)行時但不支持異常。

Go的這一設(shè)計(jì)理念的主要用意在于,在表達(dá)想法、算法或者編碼的環(huán)節(jié),開發(fā)者可以盡量少想或者不去想“在某種編程語言中處理此事的最佳方案”,讓不同的開發(fā)者可以更容易理解對方的代碼。不支持泛型和異常使得Go語言并不那么完美,也因此在很多場景下束手束腳 ,因此在“Go 2”版本中,官方加入了對這些必要特性的考慮。

高性能

單線程的執(zhí)行效率并不足以評估一門語言的優(yōu)劣,當(dāng)語言本身聚焦于解決并發(fā)和并行問題的時候尤其如此。即便如此,Golang還是跑出了亮眼的成績,僅次于一些硬核的系統(tǒng)編程語言,如C/C++/Rust等等,并且Golang還在不斷的改進(jìn)??紤]到Go是有垃圾回收機(jī)制的語言,這一成績實(shí)際上相當(dāng)?shù)牧钊擞∠笊羁蹋@使得Go語言的性能可以應(yīng)付幾乎所有的使用場景。

(Image Source: Medium)

提供現(xiàn)代軟件開發(fā)所需要的原生工具支持

是否采用一種新的語言或工具,直接取決于開發(fā)者體驗(yàn)的好壞。就Go語言來說,其工具集是用戶采納的主要考量。同最小化的內(nèi)核一樣,Go的工具集也采用了同樣的設(shè)計(jì)理念,最小化,但足夠應(yīng)付需要。執(zhí)行所有Go語言工具,都采用 go 命令及其子命令,并且全部是以命令行的方式。

Go語言中并沒有類似pip或者npm這類包管理器。但只需要下面的命令,就可以得到任何的社區(qū)包:

go get github.com/farkaskid/WebCrawler/blob/master/executor/executor.go

(左右滑動查看全部代碼)

是的,這樣就行??梢灾苯訌腉ithub或其他地方拉取所需要的包。所有的包都是源代碼文件的形態(tài)。

對于package.json這類的包,我沒有看到與 goget 等價的命令。事實(shí)上也沒有。在Go語言中,無須在一個單一文件中指定所有的依賴,可以在源文件中直接使用下面的命令:

import "github.com/xlab/pocketsphinx-go/sphinx"

(左右滑動查看全部代碼)

那么,當(dāng)執(zhí)行g(shù)o build命令的時候,運(yùn)行時會自動的運(yùn)行 goget 來獲取所需要的依賴。完整的源碼如下:

package main

import (

"encoding/binary"

"bytes"

"log"

"os/exec"

"github.com/xlab/pocketsphinx-go/sphinx"

pulse "github.com/mesilliac/pulse-simple" // pulse-simple

var buffSize int

func readInt16(buf []byte) (val int16) {

binary.Read(bytes.NewBuffer(buf), binary.LittleEndian, &val)

return

func createStream() *pulse.Stream {

ss := pulse.SampleSpec{pulse.SAMPLE_S16LE, 16000, 1}

buffSize = int(ss.UsecToBytes(1 * 1000000))

stream, err := pulse.Capture("pulse-simple test", "capture test", &ss)

if err ?。?nil {

log.Panicln(err)

return stream

func listen(decoder *sphinx.Decoder) {

stream := createStream()

defer stream.Free()

defer decoder.Destroy()

buf := make([]byte, buffSize)

var bits []int16

log.Println("Listening...")

for {

_, err := stream.Read(buf)

if err ?。?nil {

log.Panicln(err)

for i := 0; i < buffSize; i += 2 {

bits = append(bits, readInt16(buf[i:i+2]))

process(decoder, bits)

bits = nil

func process(dec *sphinx.Decoder, bits []int16) {

if !dec.StartUtt() {

panic("Decoder failed to start Utt")

dec.ProcessRaw(bits, false, false)

dec.EndUtt()

hyp, score := dec.Hypothesis()

if score > -2500 {

log.Println("Predicted:", hyp, score)

handleAction(hyp)

func executeCommand(commands ...string) {

cmd := exec.Command(commands[0], commands[1:]...)

cmd.Run()

func handleAction(hyp string) {

switch hyp {

case "SLEEP":

executeCommand("loginctl", "lock-session")

case "WAKE UP":

executeCommand("loginctl", "unlock-session")

case "POWEROFF":

executeCommand("poweroff")

func main() {

cfg := sphinx.NewConfig(

sphinx.HMMDirOption("/usr/local/share/pocketsphinx/model/en-us/en-us"),

sphinx.DictFileOption("6129.dic"),

sphinx.LMFileOption("6129.lm"),

sphinx.LogFileOption("commander.log"),

dec, err := sphinx.NewDecoder(cfg)

if err != nil {

panic(err)

listen(dec)

client.go hosted with ? by GitHub

(左右滑動查看全部代碼)

上述的代碼將把所有的依賴聲明與源文件綁定在一起。

如你所見,Go語言是如此的簡單、最小化但仍足夠滿足需要并且十分優(yōu)雅。Go語言提供了諸多的直接的工具支持,既可用于單元測試,也可以用于benchmark的火焰圖。誠然,正如前面所講到的特性集方面的限制,Go語言也有其缺陷。比如, goget 并不支持版本化,一旦源文件中引用了某個URL,就將鎖定于此。但是,Go也還在逐漸的演進(jìn),一些依賴管理的工具也正在涌現(xiàn)。

Golang最初是設(shè)計(jì)用來解決Google的一些產(chǎn)品問題,比如厚重的代碼庫,以及滿足編寫高效并發(fā)類應(yīng)用的急迫需求。在需要利用現(xiàn)代處理器的多核特性的場景,Go語言使得在應(yīng)用和庫文件的編程方面變得更加容易。并且,這些都不需要開發(fā)者來考慮。Go語言是一門現(xiàn)代的編程語言,簡單是其主旨,Go語言永遠(yuǎn)不會考慮超過這一主旨的范疇。

二、Protobuf(Protocol Buffers)

Protobuf 或者說 Protocol Buffers是由Google研發(fā)的一種二進(jìn)制通信格式,用以對結(jié)構(gòu)化數(shù)據(jù)進(jìn)行序列化。格式是什么意思?類似于JSON這樣?是的。Protobuf已經(jīng)有10年的歷史,在Google內(nèi)部也已經(jīng)使用了一段時間。

既然已經(jīng)有了JSON這種通信格式,并且得到了廣泛的應(yīng)用,為什么需要Protobuf?

與Golang一樣,Protobuf實(shí)際上并有解決任何新的問題,只是在解決現(xiàn)有的問題方面更加高效,更加現(xiàn)代化。與Golang不同的是,Protobuf并不一定比現(xiàn)存的解決方案更加優(yōu)雅。下面是Protobuf的主要特性:

Protobuf是一種二進(jìn)制格式,不同于JSON和XML,后者是基于文本的也因此相對比較節(jié)省空間。

Protobuf提供了對于schema的精巧而直接的支持

Protobuf為生成解析代碼和消費(fèi)者代碼提供直接的多語言支持。

Protobuf的二進(jìn)制格式帶來的是傳輸速度方面的優(yōu)化

那么Protobuf是不是真的很快?簡單回答,是的。根據(jù)Google Developer的數(shù)據(jù),相對于XML來說,Protobuf在體積上只有前者的1/3到1/10,在速度上卻要快20到100倍。毋庸置疑的是,由于采用了二進(jìn)制格式,序列化的數(shù)據(jù)對于人類來說是不可讀的。

(Image Source: Beating JSON performance with Protobuf)

相對其他傳輸協(xié)議格式來說,Protobuf采用了更有規(guī)劃性的方式。首先需要定義 .proto 文件,這種文件與schema類似,但更強(qiáng)大。在 .proto 文件中定義消息結(jié)構(gòu),哪些字段是必選的哪些是可選的,以及字段的數(shù)據(jù)類型等。接下來,Protobuf編譯器會生成用于數(shù)據(jù)訪問的類,開發(fā)者可以在業(yè)務(wù)邏輯中使用這些類來更方便的進(jìn)行數(shù)據(jù)傳輸。

觀察某個服務(wù)的 .proto 文件,可以清晰的獲知通信的細(xì)節(jié)以及暴露的特性。一個典型的 .proto 文件類似如下:

message Person {

required string name = 1;

required int32 id = 2;

optional string email = 3;

enum PhoneType {

MOBILE = 0;

HOME = 1;

WORK = 2;

message PhoneNumber {

required string number = 1;

optional PhoneType type = 2 [default = HOME];

repeated PhoneNumber phone = 4;

protobufExample.proto hosted with ? by GitHub

(左右滑動查看全部代碼)

曝個料:Stack Overflow的大牛Jon Skeet也是Protobuf項(xiàng)目的主要貢獻(xiàn)者之一。

三、gRPC

gRPC,物如其名,是一種功能齊備的現(xiàn)代的RPC框架,提供了諸多內(nèi)置支持的機(jī)制,如負(fù)載均衡、跟蹤、健康檢查和認(rèn)證等。gRPC由Google在2015年開源,并由此日益火爆。

既然已經(jīng)有了REST,還搞個RPC做什么?

在SOA架構(gòu)的時代,有相當(dāng)長的時間,基于WSDL的SOAP協(xié)議是系統(tǒng)間通信的解決方案。彼時,通信協(xié)議的定義是十分嚴(yán)格的,龐大的單體架構(gòu)系統(tǒng)暴露大量的接口用于擴(kuò)展。

隨著B/S理念的興起,服務(wù)器和客戶端開始解耦,在這樣的架構(gòu)下,即使客戶端和服務(wù)端分別進(jìn)行獨(dú)立的編碼,也不影響對服務(wù)的調(diào)用??蛻舳讼氩樵円槐緯男畔ⅲ?wù)端會根據(jù)請求提供相關(guān)的列表供客戶端瀏覽。REST范式主要解決的就是這種場景下的問題,REST允許服務(wù)端和客戶端可以自由的通信,而不需要定義嚴(yán)格的契約以及獨(dú)有的語義。

從某種意義上講,此時的服務(wù)已經(jīng)開始變得像是單體式架構(gòu)系統(tǒng)一樣,對于某個特定的請求,會返回一坨毫無必要的數(shù)據(jù),用以滿足客戶端的“瀏覽”需求。但這并不是所有場景下都會發(fā)生的情況,不是么?

跨入微服務(wù)的時代

采用微服務(wù)架構(gòu)理由多多。最常提及的事實(shí)是,單體架構(gòu)太難擴(kuò)展了。以微服務(wù)架構(gòu)設(shè)計(jì)大型系統(tǒng),所有的業(yè)務(wù)和技術(shù)需求都傾向于實(shí)現(xiàn)成互相合作的組件,這些組件就是“微”服務(wù)。

微服務(wù)不需要以包羅萬象的信息響應(yīng)用戶請求,而僅需要根據(jù)請求完成特定的任務(wù)并給出所需要的回應(yīng)。理想情況下,微服務(wù)應(yīng)該像一堆可以無縫組裝的函數(shù)。

使用REST做為此類服務(wù)的通信范式變得不那么有效。一方面,采用REST API確實(shí)可以讓服務(wù)的表達(dá)能力更強(qiáng),但同時,如果這種表達(dá)的能力既非必要也并不出自設(shè)計(jì)者的本意,我們就需要根據(jù)不同的因素考慮其他范式了。

gRPC嘗試在如下的技術(shù)方面改進(jìn)傳統(tǒng)的HTTP請求:

默認(rèn)支持HTTP/2協(xié)議,并可以享受該協(xié)議帶來的所有好處

采用Protobuf格式用于機(jī)器間通信

得益于HTTP/2協(xié)議,提供了對流式調(diào)用的專有支持

對所有常用的功能提供了插件化的支持,如認(rèn)證、跟蹤、負(fù)載均衡和健康檢查等。

當(dāng)然,既然是RPC框架,仍舊會有服務(wù)定義和接口描述語言(DSL)的相關(guān)概念,REST世代的開發(fā)者可能會感覺這些概念有些格格不入,但是由于gRPC采用Protobuf做為通信格式,就不會顯得像以前那么笨拙。

Protobuf的設(shè)計(jì)理念使得其既是一種通信格式,又可以是一種協(xié)議規(guī)范工具,在此過程中無需做任何額外的工作。一個典型的gRPC服務(wù)定義類似如下:

service HelloService {

rpc SayHello (HelloRequest) returns (HelloResponse);

message HelloRequest {

string greeting = 1;

message HelloResponse {

string reply = 1;

serviceDefinition.proto hosted with ? by GitHub

(左右滑動查看全部代碼)

只需要為服務(wù)定義一個 .proto 文件,并在其中描述接口名稱,服務(wù)的需求,以及以Protobuf格式返回的消息即可。Protobuf編譯器會生成客戶端和服務(wù)端代碼。客戶端可以直接調(diào)用這些代碼,服務(wù)端可以用這些代碼實(shí)現(xiàn)API來填充業(yè)務(wù)邏輯。

四、結(jié)語

Golang,Protobuf和gRPC是現(xiàn)代服務(wù)端編程的后起之秀。Golang簡化了并發(fā)/并行應(yīng)用的編程,gRPC和Protobuf的結(jié)合提供了更高效的通信并同時提供了更愉悅的開發(fā)者體驗(yàn)。

審核編輯:符乾江
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4900

    瀏覽量

    70786
  • 服務(wù)端
    +關(guān)注

    關(guān)注

    0

    文章

    68

    瀏覽量

    7250
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    labview opc ua 讀取客戶數(shù)據(jù)

    請教各位大神,在電腦上,用自帶OPC讀取OPCUA服務(wù)端數(shù)據(jù)是OK的,但是自己寫程序讀取就讀不到,報錯了,請教一下,節(jié)點(diǎn)路徑怎么寫,非常感謝
    發(fā)表于 06-02 12:01

    不用聯(lián)網(wǎng)不用編程,PLC通過智能網(wǎng)關(guān)快速實(shí)現(xiàn)HTTP協(xié)議JSON格式與MES等系統(tǒng)平臺雙向數(shù)據(jù)通訊

    智能網(wǎng)關(guān)IGT-DSER支持POST/GET/PUT等多種方法,可同時作為HTTP協(xié)議的客戶服務(wù)端。作為客戶通訊時將JSON文件提交給HTTP的服務(wù)端, 如果
    的頭像 發(fā)表于 05-13 14:40 ?369次閱讀
    不用聯(lián)網(wǎng)不用編程,PLC通過智能網(wǎng)關(guān)快速實(shí)現(xiàn)HTTP協(xié)議JSON格式與MES等系統(tǒng)平臺雙向數(shù)據(jù)通訊

    Nginx緩存配置詳解

    Nginx 是一個功能強(qiáng)大的 Web 服務(wù)器和反向代理服務(wù)器,它可以用于實(shí)現(xiàn)靜態(tài)內(nèi)容的緩存,緩存可以分為客戶緩存和服務(wù)端緩存。
    的頭像 發(fā)表于 05-07 14:03 ?600次閱讀
    Nginx緩存配置<b class='flag-5'>詳解</b>

    中軟國際2024年度業(yè)績營收169.51億:持續(xù)投資云邊AI 全AI業(yè)務(wù)收入初具規(guī)模

    %。報告期內(nèi),公司實(shí)現(xiàn)經(jīng)調(diào)整溢利6.50億元,年度溢利5.11億元,擬每股派發(fā)股息5.33港仙。 公司首次披露全AI產(chǎn)品及服務(wù)業(yè)務(wù),其內(nèi)涵包括從算力基礎(chǔ)設(shè)施運(yùn)營、數(shù)據(jù)治理、模型訓(xùn)練、推理部署、側(cè)智能、云智能到AI原生ERP及
    的頭像 發(fā)表于 03-31 10:15 ?752次閱讀

    解密北斗時鐘服務(wù)器的現(xiàn)代使命

    在ICU病房的生命監(jiān)護(hù)儀上,在高鐵調(diào)度中心的運(yùn)行圖上,在智能制造車間的機(jī)械臂之間,北斗時鐘服務(wù)器正以人類難以察覺的方式編織著精密的時間網(wǎng)絡(luò)。這些看似普通的數(shù)字顯示器背后,連接著維系現(xiàn)代文明運(yùn)轉(zhuǎn)的隱形
    發(fā)表于 02-25 14:54

    TinyEngine服務(wù)端Java版本開源

    你是否曾經(jīng)在埋頭編碼的時候,別人用一行JavaScript代碼就解決了問題?這就像一個編碼魔術(shù)。
    的頭像 發(fā)表于 02-10 14:25 ?490次閱讀
    TinyEngine<b class='flag-5'>服務(wù)端</b>Java版本開源

    SSR與微服務(wù)架構(gòu)的結(jié)合應(yīng)用

    隨著互聯(lián)網(wǎng)技術(shù)的快速發(fā)展,前端技術(shù)不斷更新迭代,后端架構(gòu)也經(jīng)歷了從單體應(yīng)用到微服務(wù)的變革。在這個過程中,服務(wù)端渲染(SSR)作為一種提升頁
    的頭像 發(fā)表于 11-18 11:34 ?840次閱讀

    Golang配置代理方法

    由于一些客觀原因的存在,我們開發(fā) Golang 項(xiàng)目的過程總會碰到無法下載某些依賴包的問題。這不是一個小問題,因?yàn)槟愕墓ぷ鲿淮驍?,即便你使用各種神通解決了問題,很可能這時你的線程已經(jīng)切換到其他的事情上了(痛恨思路被打斷!)。所以最好是一開始我們就重視這個問題,并一勞永逸的解決它。
    的頭像 發(fā)表于 11-11 11:17 ?1043次閱讀
    <b class='flag-5'>Golang</b>配置代理方法

    從藍(lán)牙協(xié)議視角探索信道探測

    親愛的藍(lán)牙科技愛好者們,上期的“干貨”希望大家看得過癮!在本期節(jié)目中,主講人藍(lán)牙技術(shù)聯(lián)盟技術(shù)與市場工程師魯公羽會繼續(xù)為您詳解信道探測(Channel Sounding),從藍(lán)牙協(xié)議
    的頭像 發(fā)表于 10-12 16:49 ?886次閱讀
    從藍(lán)牙協(xié)議<b class='flag-5'>棧</b>視角探索信道探測

    【米爾NXP i.MX 93開發(fā)板試用評測】4、使用golang搭建Modbus 服務(wù)

    負(fù)責(zé)處理來自客戶(通常稱為Modbus客戶或從站)的請求,并根據(jù)請求提供相應(yīng)的數(shù)據(jù)或執(zhí)行操作。 快速開發(fā)modbus服務(wù)器 可以使用golang快速部署一個modbus
    發(fā)表于 09-21 22:51

    調(diào)試stm32的TCP服務(wù)端程序,會導(dǎo)致hard fault的原因?

    最近調(diào)試stm32的TCP服務(wù)端程序,遇到了想不明白的問題,具體過程如下: 使用的是rt-thread4.1.0的內(nèi)核; 通過串口和esp32-c3通信; 啟用at_socket; 建立tcp
    發(fā)表于 09-13 06:59

    軟通動力數(shù)據(jù)庫全服務(wù),助力企業(yè)數(shù)據(jù)庫體系全面升級

    。在企業(yè)節(jié)與"數(shù)博會"展區(qū),軟通動力受邀分享數(shù)據(jù)庫專業(yè)服務(wù)解決方案,并重點(diǎn)展示以全服務(wù)為核心的數(shù)智化能力。 軟通動力高級數(shù)據(jù)庫服務(wù)
    的頭像 發(fā)表于 09-05 15:30 ?587次閱讀
    軟通動力數(shù)據(jù)庫全<b class='flag-5'>棧</b><b class='flag-5'>服務(wù)</b>,助力企業(yè)數(shù)據(jù)庫體系全面升級

    使用NS1串口服務(wù)器HTTP模式上傳服務(wù)器數(shù)據(jù)

    HTTP協(xié)議工作于客戶-服務(wù)端架構(gòu)之上。瀏覽器作為HTTP客戶通過URL向HTTP服務(wù)端即Web服務(wù)器發(fā)送所有請求。Web
    的頭像 發(fā)表于 08-30 12:36 ?1004次閱讀
    使用NS1串口<b class='flag-5'>服務(wù)</b>器HTTP模式上傳<b class='flag-5'>服務(wù)</b>器數(shù)據(jù)

    串口服務(wù)器和TCP/IP協(xié)議是什么關(guān)系

    串口服務(wù)器與TCP/IP協(xié)議之間存在著緊密而復(fù)雜的關(guān)系。這種關(guān)系主要體現(xiàn)在串口服務(wù)器如何利用TCP/IP協(xié)議來實(shí)現(xiàn)串口設(shè)備與網(wǎng)絡(luò)之間的數(shù)據(jù)通信。以下是對這種關(guān)系的詳細(xì)解析。
    的頭像 發(fā)表于 08-25 17:35 ?2134次閱讀

    InRouter路由器與中心OpenVPN服務(wù)器構(gòu)建OpenVPN的方法

    動態(tài)獲取,則需要采用DDNS技術(shù)輔助Openvpn的搭建(具體操作步驟請查閱相關(guān)文檔)? OpenVPN服務(wù)端采用PC服務(wù)器,操作系統(tǒng)為Windows XP Professional SP3,IP地址為
    發(fā)表于 07-26 07:26