.NET 環境中使用RabbitMQ

在企業應用系統領域,會面對不同系統之間的通信、集成與整合,尤其當面臨異構系統時,這種分布式的調用與通信變得越發重要。其次,系統中一般會有很多對實時性要求不高的但是執行起來比較較耗時的地方,比如發送短信,郵件提醒,更新文章閱讀計數,記錄用戶操作日志等等,如果實時處理的話,在用戶訪問量比較大的情況下,對系統壓力比較大。

面對這些問題,我們一般會將這些請求,放在消息隊列中處理;異構系統之間使用消息進行通訊。消息傳遞相較文件傳遞與遠程過程調用(RPC)而言,似乎更勝一籌,因為它具有更好的平臺無關性,并能夠很好地支持并發與異步調用。所以如果系統中出現了如下情況:

  • 對操作的實時性要求不高,而需要執行的任務極為耗時;
  • 存在異構系統間的整合;

一般的可以考慮引入消息隊列。對于第一種情況,常常會選擇消息隊列來處理執行時間較長的任務。引入的消息隊列就成了消息處理的緩沖區。消息隊列引入的異步通信機制,使得發送方和接收方都不用等待對方返回成功消息,就可以繼續執行下面的代碼,從而提高了數據處理的能力。尤其是當訪問量和數據流量較大的情況下,就可以結合消息隊列與后臺任務,通過避開高峰期對大數據進行處理,就可以有效降低數據庫處理數據的負荷。

在前面的一篇講解CQRS模式的文章中,所有的對系統的狀態的更改都是通過事件來完成,一般的將事件存儲到消息隊列中,然后進行統一的處理。

本文簡單介紹在RabbitMQ這一消息代理工具,以及在.NET中如何使用RabbitMQ.

一 環境搭建

首先,由于RabbitMQ使用Erlang編寫的,需要運行在Erlang運行時環境上,所以在安裝RabbitMQ Server之前需要安裝Erlang 運行時環境,可以到Erlang官網下載對應平臺的安裝文件。如果沒有安裝運行時環境,安裝RabbitMQ Server的時候,會提示需要先安裝Erlang環境。 安裝完成之后,確保已經將Erlang的安裝路徑注冊到系統的環境變量中。安裝完Erlang之后,這個環境會自動設置,如果沒有,在administrator環境下在控制臺下面輸入,也可以設置:

Setx  ERLANG_HOME “D:\Program Files (x86)\erl6.3″

Erlang Enviroment Path

然后,去RabbitMQ官網下載RabbitMQ Server服務端程序,選擇合適的平臺版本下載。安裝完成之后,就可以開始使用了。

現在就可以對RabbitMQ Server進行配置了。

首先,切換到RabbitMQ Server的安裝目錄:

RabbitMQ Install folder

在sbin下面有很多batch文件,用來控制RabbitMQ Server,當然您也可以直接在安裝開始菜單中來執行相應的操作:

RabitMQ Menu

最簡單的方式是使RabbitMQ以Windows Service的方式在后臺運行,所以我們需要以管理員權限打開cmd,然后切換到sbin目錄下,執行這三條命令即可:

rabbitmq-service install
rabbitmq-service enable
rabbitmq-service start

Start RabbitMQ Service

現在RabbitMQ的服務端已經啟動起來了。

下面可以使用sbin目錄下面的rabbitmqctl.bat這個腳本來查看和控制服務端狀態的,在cmd中直接運行rabbitmqctl status。如果看到以下結果:

Unable to connect node

顯示node沒有連接上,需要到C:\Windows目錄下,將.erlang.cookie文件,拷貝到用戶目錄下 C:\Users\{用戶名},這是Erlang的Cookie文件,允許與Erlang進行交互,現在重復運行剛才的命令就會得到如下信息:

rabbit mq status

RabbitMQ Server上面也有用戶概念,安裝好之后,使用rabbitmqctl list_users命令,可以看到上面目前的用戶:

RabbitMQ users

可以看到,現在只有一個角色為administrator的名為guest的用戶,這個是RabbitMQ默認為我們創建的,他有RabbitMQ的所有權限,一般的,我們需要新建一個我們自己的用戶,設置密碼,并授予權限,并將其設置為管理員,可以使用下面的命令來執行這一操作:

rabbitmqctl  add_user  yy  hello!
rabbitmqctl  set_permissions  yy  ".*"  ".*"  ".*"
rabbitmqctl  set_user_tags yy administrator

Create RabbitMQ user

上面的一條命令添加了一個名為yy的用戶,并設置了密碼hello!,下面的命令為用戶yy分別授予對所有消息隊列的配置、讀和寫的權限。

現在我們可以將默認的guest用戶刪掉,使用下面的命令即可:

rabbitmqctl delete_user guest

如果要修改密碼,可以使用下面的命令:

rabbitmqctl change_password {username}  {newpassowrd}

二 開始使用

在.NET中使用RabbitMQ需要下載RabbitMQ的客戶端程序集,可以到官網下載,下載解壓后就可以得到RabbitMQ.Client.dll,這就是RabbitMQ的客戶端。

在使用RabitMQ之前,需要對下面的幾個基本概念說明一下:

RabbitMQ是一個消息代理。他從消息生產者(producers)那里接收消息,然后把消息送給消息消費者(consumer)在發送和接受之間,他能夠根據設置的規則進行路由,緩存和持久化。

一般提到RabbitMQ和消息,都用到一些專有名詞。

  • 生產(Producing)意思就是發送。發送消息的程序就是一個生產者(producer)。我們一般用"P"來表示:

producer

  • 隊列(queue)就是郵箱的名稱。消息通過你的應用程序和RabbitMQ進行傳輸,它們只能存儲在隊列(queue)中。 隊列(queue)容量沒有限制,你要存儲多少消息都可以——基本上是一個無限的緩沖區。多個生產者(producers)能夠把消息發送給同一個隊列,同樣,多個消費者(consumers)也能從同一個隊列(queue)中獲取數據。隊列可以畫成這樣(圖上是隊列的名稱):

queue

  • 消費(Consuming)和獲取消息是一樣的意思。一個消費者(consumer)就是一個等待獲取消息的程序。我們把它畫作"C":

consumer

通常,消息生產者,消息消費者和消息代理不在同一臺機器上。

2.1 Hello World

為了展示RabbitMQ的基本使用,我們發送一個HelloWorld消息,然后接收并處理。

rabbitmq hello world

首先創建一個控制臺程序,用來將消息發送到RabbitMQ的消息隊列中,代碼如下:

    static void Main(string[] args){var factory = new ConnectionFactory();factory.HostName = "localhost";factory.UserName = "yy";factory.Password = "hello!";using (var connection = factory.CreateConnection()){using (var channel = connection.CreateModel()){channel.QueueDeclare("hello", false, false, false, null);string message = "Hello World";var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish("", "hello", null, body);Console.WriteLine(" set {0}", message);}}}

首先,需要創建一個ConnectionFactory,設置目標,由于是在本機,所以設置為localhost,如果RabbitMQ不在本機,只需要設置目標機器的IP地址或者機器名稱即可,然后設置前面創建的用戶名yy和密碼hello!。

緊接著要創建一個Channel,如果要發送消息,需要創建一個隊列,然后將消息發布到這個隊列中。在創建隊列的時候,只有RabbitMQ上該隊列不存在,才會去創建。消息是以二進制數組的形式傳輸的,所以如果消息是實體對象的話,需要序列化和然后轉化為二進制數組。

現在客戶端發送代碼已經寫好了,運行之后,消息會發布到RabbitMQ的消息隊列中,現在需要編寫服務端的代碼連接到RabbitMQ上去獲取這些消息。

同樣,創建一個名為Receive的服務端控制臺應用程序,服務端代碼如下:

static void Main(string[] args)
{var factory = new ConnectionFactory();factory.HostName = "localhost";factory.UserName = "yy";factory.Password = "hello!";using (var connection = factory.CreateConnection()){using (var channel = connection.CreateModel()){channel.QueueDeclare("hello", false, false, false, null);var consumer = new QueueingBasicConsumer(channel);channel.BasicConsume("hello", true, consumer);Console.WriteLine(" waiting for message.");while (true){var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();var body = ea.Body;var message = Encoding.UTF8.GetString(body);Console.WriteLine("Received {0}", message);}}}
}

和發送一樣,首先需要定義連接,然后聲明消息隊列。要接收消息,需要定義一個Consume,然后從消息隊列中不斷Dequeue消息,然后處理。

現在發送端和接收端的代碼都寫好了,運行發送端,發送消息:

SendHelloword

現在,名為hello的消息隊列中,發送了一條消息。這條消息存儲到了RabbitMQ的服務器上了。使用rabbitmqctl 的list_queues可以查看所有的消息隊列,以及里面的消息個數,可以看到,目前Rabbitmq上只有一個消息隊列,里面只有一條消息:

D:\Program Files\RabbitMQ Server\rabbitmq_server-3.4.2\sbin>rabbitmqctl list_queues
Listing queues ...
hello   1

現在運行接收端程序,如下:

ReceiveHelloWorld

可以看到,已經接受到了客戶端發送的Hello World,現在再來看RabitMQ上的消息隊列信息:

D:\Program Files\RabbitMQ Server\rabbitmq_server-3.4.2\sbin>rabbitmqctl list_queues
Listing queues ...
hello   0

可以看到,hello這個隊列中的消息隊列個數為0,這表示,當接收端,接收到消息之后,RabbitMQ上就把這個消息刪掉了。

2.2 工作隊列

前面的例子展示了如何往一個指定的消息隊列中發送和收取消息。現在我們創建一個工作隊列(work queue)來將一些耗時的任務分發給多個工作者(workers):

rabbitmq-work queue

工作隊列(work queues, 又稱任務隊列Task Queues)的主要思想是為了避免立即執行并等待一些占用大量資源、時間的操作完成。而是把任務(Task)當作消息發送到隊列中,稍后處理。一個運行在后臺的工作者(worker)進程就會取出任務然后處理。當運行多個工作者(workers)時,任務會在它們之間共享。

這個在網絡應用中非常有用,它可以在短暫的HTTP請求中處理一些復雜的任務。在一些實時性要求不太高的地方,我們可以處理完主要操作之后,以消息的方式來處理其他的不緊要的操作,比如寫日志等等。

準備

在第一部分,發送了一個包含“Hello World!”的字符串消息。現在發送一些字符串,把這些字符串當作復雜的任務。這里使用time.sleep()函數來模擬耗時的任務。在字符串中加上點號(.)來表示任務的復雜程度,一個點(.)將會耗時1秒鐘。比如"Hello..."就會耗時3秒鐘。

對之前示例的send.cs做些簡單的調整,以便可以發送隨意的消息。這個程序會按照計劃發送任務到我們的工作隊列中。

static void Main(string[] args)
{var factory = new ConnectionFactory();factory.HostName = "localhost";factory.UserName = "yy";factory.Password = "hello!";using (var connection = factory.CreateConnection()){using (var channel = connection.CreateModel()){channel.QueueDeclare("hello", false, false, false, null);string message = GetMessage(args);  var properties = channel.CreateBasicProperties();properties.DeliveryMode = 2;var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish("", "hello", properties, body);Console.WriteLine(" set {0}", message);}}Console.ReadKey();
}private static string GetMessage(string[] args)
{return ((args.Length > 0) ? string.Join(" ", args) : "Hello World!");
}

加粗部分是經過修改過了的。

接著我們修改接收端,讓他根據消息中的逗點的個數來Sleep對應的秒數:

static void Main(string[] args)
{var factory = new ConnectionFactory();factory.HostName = "localhost";factory.UserName = "yy";factory.Password = "hello!";using (var connection = factory.CreateConnection()){using (var channel = connection.CreateModel()){channel.QueueDeclare("hello", false, false, false, null);var consumer = new QueueingBasicConsumer(channel);channel.BasicConsume("hello", true, consumer);while (true){var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();var body = ea.Body;var message = Encoding.UTF8.GetString(body);  int dots = message.Split('.').Length - 1;Thread.Sleep(dots * 1000);Console.WriteLine("Received {0}", message);Console.WriteLine("Done");}}}
}

輪詢分發

使用工作隊列的一個好處就是它能夠并行的處理隊列。如果堆積了很多任務,我們只需要添加更多的工作者(workers)就可以了,擴展很簡單。

現在,我們先啟動兩個接收端,等待接受消息,然后啟動一個發送端開始發送消息。

Send message queue?

在cmd條件下,發送了5條消息,每條消息后面的逗點表示該消息需要執行的時長,來模擬耗時的操作。

然后可以看到,兩個接收端依次接收到了發出的消息:

receive message queue?

默認,RabbitMQ會將每個消息按照順序依次分發給下一個消費者。所以每個消費者接收到的消息個數大致是平均的。 這種消息分發的方式稱之為輪詢(round-robin)。

2.3 消息響應

當處理一個比較耗時得任務的時候,也許想知道消費者(consumers)是否運行到一半就掛掉。在當前的代碼中,當RabbitMQ將消息發送給消費者(consumers)之后,馬上就會將該消息從隊列中移除。此時,如果把處理這個消息的工作者(worker)停掉,正在處理的這條消息就會丟失。同時,所有發送到這個工作者的還沒有處理的消息都會丟失。

我們不想丟失任何任務消息。如果一個工作者(worker)掛掉了,我們希望該消息會重新發送給其他的工作者(worker)。

為了防止消息丟失,RabbitMQ提供了消息響應(acknowledgments)機制。消費者會通過一個ack(響應),告訴RabbitMQ已經收到并處理了某條消息,然后RabbitMQ才會釋放并刪除這條消息。

如果消費者(consumer)掛掉了,沒有發送響應,RabbitMQ就會認為消息沒有被完全處理,然后重新發送給其他消費者(consumer)。這樣,即使工作者(workers)偶爾的掛掉,也不會丟失消息。

消息是沒有超時這個概念的;當工作者與它斷開連的時候,RabbitMQ會重新發送消息。這樣在處理一個耗時非常長的消息任務的時候就不會出問題了。

消息響應默認是開啟的。在之前的例子中使用了no_ack=True標識把它關閉。是時候移除這個標識了,當工作者(worker)完成了任務,就發送一個響應。

channel.BasicConsume("hello", false, consumer);while (true)
{var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();var body = ea.Body;var message = Encoding.UTF8.GetString(body);int dots = message.Split('.').Length - 1;Thread.Sleep(dots * 1000);Console.WriteLine("Received {0}", message);Console.WriteLine("Done");channel.BasicAck(ea.DeliveryTag, false);
}

現在,可以保證,即使正在處理消息的工作者被停掉,這些消息也不會丟失,所有沒有被應答的消息會被重新發送給其他工作者.

一個很常見的錯誤就是忘掉了BasicAck這個方法,這個錯誤很常見,但是后果很嚴重. 當客戶端退出時,待處理的消息就會被重新分發,但是RabitMQ會消耗越來越多的內存,因為這些沒有被應答的消息不能夠被釋放。調試這種case,可以使用rabbitmqct打印messages_unacknoledged字段。

rabbitmqctl list_queues name messages_ready messages_unacknowledged
Listing queues ...
hello    0       0
...done.

2.4 消息持久化

前面已經搞定了即使消費者down掉,任務也不會丟失,但是,如果RabbitMQ Server停掉了,那么這些消息還是會丟失。

當RabbitMQ Server 關閉或者崩潰,那么里面存儲的隊列和消息默認是不會保存下來的。如果要讓RabbitMQ保存住消息,需要在兩個地方同時設置:需要保證隊列和消息都是持久化的。

首先,要保證RabbitMQ不會丟失隊列,所以要做如下設置:

bool durable = true;
channel.QueueDeclare("hello", durable, false, false, null);

雖然在語法上是正確的,但是在目前階段是不正確的,因為我們之前已經定義了一個非持久化的hello隊列。RabbitMQ不允許我們使用不同的參數重新定義一個已經存在的同名隊列,如果這樣做就會報錯。現在,定義另外一個不同名稱的隊列:

bool durable = true;
channel.queueDeclare("task_queue", durable, false, false, null);

queueDeclare 這個改動需要在發送端和接收端同時設置。

現在保證了task_queue這個消息隊列即使在RabbitMQ Server重啟之后,隊列也不會丟失。 然后需要保證消息也是持久化的, 這可以通過設置IBasicProperties.SetPersistent 為true來實現:

var properties = channel.CreateBasicProperties();
properties.SetPersistent(true);

需要注意的是,將消息設置為持久化并不能完全保證消息不丟失。雖然他告訴RabbitMQ將消息保存到磁盤上,但是在RabbitMQ接收到消息和將其保存到磁盤上這之間仍然有一個小的時間窗口。 RabbitMQ 可能只是將消息保存到了緩存中,并沒有將其寫入到磁盤上。持久化是不能夠一定保證的,但是對于一個簡單任務隊列來說已經足夠。如果需要消息隊列持久化的強保證,可以使用publisher confirms

2.5 公平分發

你可能會注意到,消息的分發可能并沒有如我們想要的那樣公平分配。比如,對于兩個工作者。當奇數個消息的任務比較重,但是偶數個消息任務比較輕時,奇數個工作者始終處理忙碌狀態,而偶數個工作者始終處理空閑狀態。但是RabbitMQ并不知道這些,他仍然會平均依次的分發消息。

為了改變這一狀態,我們可以使用basicQos方法,設置perfetchCount=1 。這樣就告訴RabbitMQ 不要在同一時間給一個工作者發送多于1個的消息,或者換句話說。在一個工作者還在處理消息,并且沒有響應消息之前,不要給他分發新的消息。相反,將這條新的消息發送給下一個不那么忙碌的工作者。

channel.BasicQos(0, 1, false); 

2.6 完整實例

現在將所有這些放在一起:

發送端代碼如下:

static void Main(string[] args)
{var factory = new ConnectionFactory();factory.HostName = "localhost";factory.UserName = "yy";factory.Password = "hello!";using (var connection = factory.CreateConnection()){using (var channel = connection.CreateModel()){bool durable = true;channel.QueueDeclare("task_queue", durable, false, false, null);string message = GetMessage(args);var properties = channel.CreateBasicProperties();properties.SetPersistent(true);var body = Encoding.UTF8.GetBytes(message);channel.BasicPublish("", "task_queue", properties, body);Console.WriteLine(" set {0}", message);}}Console.ReadKey();
}private static string GetMessage(string[] args)
{return ((args.Length > 0) ? string.Join(" ", args) : "Hello World!");
}

接收端代碼如下:

static void Main(string[] args)
{var factory = new ConnectionFactory();factory.HostName = "localhost";factory.UserName = "yy";factory.Password = "hello!";using (var connection = factory.CreateConnection()){using (var channel = connection.CreateModel()){bool durable = true;channel.QueueDeclare("task_queue", durable, false, false, null);channel.BasicQos(0, 1, false);var consumer = new QueueingBasicConsumer(channel);channel.BasicConsume("task_queue", false, consumer);while (true){var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();var body = ea.Body;var message = Encoding.UTF8.GetString(body);int dots = message.Split('.').Length - 1;Thread.Sleep(dots * 1000);Console.WriteLine("Received {0}", message);Console.WriteLine("Done");channel.BasicAck(ea.DeliveryTag, false);}}}
}

三 管理界面

RabbitMQ還有一個管理界面,通過該界面可以查看RabbitMQ Server 當前的狀態,該界面是以插件形式提供的,并且在安裝RabbitMQ的時候已經自帶了該插件。需要做的是在RabbitMQ控制臺界面中啟用該插件,命令如下:

rabbitmq-plugins enable rabbitmq_management

rabbitmq management

現在,在瀏覽器中輸入?http://server-name:15672/?server-name換成機器地址或者域名,如果是本地的,直接用localhost(RabbitMQ 3.0之前版本端口號為55672)在輸入之后,彈出登錄界面,使用我們之前創建的用戶登錄。

RabbitMQ Web management?.

在該界面上可以看到當前RabbitMQServer的所有狀態。

四 總結

本文簡單介紹了消息隊列的相關概念,并介紹了RabbitMQ消息代理的基本原理以及在Windows 上如何安裝RabbitMQ和在.NET中如何使用RabbitMQ。消息隊列在構建分布式系統和提高系統的可擴展性和響應性方面有著很重要的作用,希望本文對您了解消息隊列以及如何使用RabbitMQ有所幫助。

五 參考文獻

  1. http://www.infoq.com/cn/articles/message-based-distributed-architecture
  2. http://www.rabbitmq.com/getstarted.html
  3. http://www.codethinked.com/using-rabbitmq-with-c-and-net
  4. http://www.infoq.com/cn/articles/AMQP-RabbitMQ
  5. http://www.infoq.com/cn/articles/ebay-scalability-best-practices

轉載于:https://www.cnblogs.com/soundcode/p/7151909.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254050.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254050.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254050.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

css3 pointer-events:none 允許點擊穿透

開發時加了蒙層,卻可以點擊到蒙層下邊的元素。查了以后才發現是 pointer-events:none屬性具有允許點擊穿透的功能,將其設為 pointer-events:auto就好使了。事件穿透讓我想起了zepto.js中的tap事件同樣會引發事件穿透的效果。如果用tap事件是為了提升響應…

成像質量、像素個數、感光元件尺寸的關系

成像質量、像素個數、感光元件尺寸的關系 感光元件 (影像傳感器) 就是拍攝的照片最終成像的位置。相當于傳統相機里面的膠卷,不同相機的感光元件尺寸是不一樣的。 1. 像素的含義 兩個 100 平方米的房子 A 和 B,A 房子里面平均分成 10 個房間&#xff…

setjmp與logjmp用法總結

setjmp/logjmp的最大用途是錯誤恢復&#xff0c;只要還沒有從主函數退出&#xff0c;一旦發現一個不可恢復的錯誤&#xff0c;可以把主控制轉移大主函數循環&#xff0c;并從那從新開始。使用時必須包含頭文件<setjmp.h> setjmp與longjmp結合使用時&#xff0c;它們必須…

JQ對象到底是什么

jQuery對象是什么&#xff0c;舉個例子&#xff0c;$(#id) 返回的就是jQuery對象&#xff0c;這個東西是整個jQuery的核心所在&#xff0c;所以我先來分析它。 var jQuery function( selector, context ) { // The jQuery object is actually just the init constructor enhan…

15、iOS開發之duplicate symbols for architecture x86_64錯誤

1. 錯誤提示 2. 分析錯誤原因 3. 解決問題辦法 一、錯誤提示 在我們寫代碼過程中可能會經常遇到這樣一個錯誤&#xff1a; [objc] view plaincopy print?<span style"font-size:32px;color:#ff0000;">ld: 4 duplicate symbols for architecture x86_64 clang…

【死磕Java并發】----- 死磕 Java 并發精品合集

【死磕 Java 并發】系列是 LZ 在 2017 年寫的第一個死磕系列&#xff0c;一直沒有做一個合集&#xff0c;這篇博客則是將整個系列做一個概覽。 先來一個總覽圖&#xff1a; 【高清圖&#xff0c;請關注“Java技術驛站”公眾號&#xff0c;回復&#xff1a;腦圖JUC】 【死磕Java…

vs官方使用教程中文版與英文版

Visual Studio IDE 中文文檔 https://docs.microsoft.com/zh-cn/visualstudio/ide/ Visual Studio IDE documentation https://docs.microsoft.com/en-us/visualstudio/ide/

linux subsys_initcall

宏定義__define_initcall(level,fn)對于內核的初始化很重要&#xff0c;他指示編譯器在編譯的時候&#xff0c;將一系列初始化函數的起始地址值按照一定的順序放在一個section中。在內核初始化段&#xff0c;do_initcalls() 將按順序從該section中以函數指針的形式取出這些函數…

vue transition

Vue.js 教程 (9) : 過渡動畫 Vue.js 提供非常簡單的過渡動畫接口。這些過渡動畫在 Vue.js 將目標元素插入或移除出 DOM 的時候會自動執行。能夠觸發動畫的指令包括 v-if , v-show 和 v-repeat。同時&#xff0c;vm 實例的 $appendTo() , $before() , $after() 和 $remove() 方法…

VS擴展工具

原文發布時間為&#xff1a;2011-03-09 —— 來源于本人的百度文章 [由搬家工具導入]http://visualstudiogallery.msdn.microsoft.com/site/search?f%5B0%5D.TypeRootCategory&f%5B0%5D.Valuetools轉載于:https://www.cnblogs.com/handboy/p/7163982.html

工業相機5A參數及其對圖像采集的影響

有些相機會提到5A功能&#xff0c;指的是: Automatic Shutter(自動快門)、Automatic Gain&#xff08;自動增益&#xff09;、Automatic IRIS&#xff08;自動光圈&#xff09;、Automatic Gamma&#xff08;自動伽馬&#xff09;、Automatic White Balance&#xff08;自動白平…

DM6446 OSD

DM6446 OSD TMS320DM6446支持背景窗顏色&#xff0c;兩個視頻窗口&#xff0c;兩個OSD窗口&#xff0c;一個指針(cursor)窗口。它們以遞增的順序排列&#xff1a;一個特有的第二個OSD窗口&#xff08;OSDWIN1&#xff09;可以用來配置成屬性窗口來控制視頻窗口和第一個OSD窗口&…

DOS的一些常用命令

原文發布時間為&#xff1a;2011-02-12 —— 來源于本人的百度文章 [由搬家工具導入]DOS遠程桌面連接命令 mstsc /v: 192.168.1.250 /consolecmd 運行 command刪除文件 rd 文件名/S創建文件 MD 文件名 1. net user admin godmour /add 新建一個…

機器視覺工業鏡頭-Computar

日本Computar鏡頭&#xff0c;全球工業鏡頭、CCTV鏡頭市場占有率第一。CBC板式會社成立于1925年&#xff0c;總部在日本東京。1960年 CBC香港公司成立&#xff0c;是computar鏡頭走向國際市場的前奏。 1979年 研制出第一只手動變焦鏡頭。 1985年 研制出第一款非球面高速鏡頭。1…

C++編程經驗總結1

面向對象的精髓&#xff1a; 主函數其實就是對于類的元素和動作的重新組合來進行一項活動。 一個思想概念&#xff1a;程設是清楚的&#xff0c;完美的。 數學是清楚的&#xff0c;是完美的。 物理是有趣的&#xff0c;尤其是量子物理 生物是清楚的&#xff0c;尤其是基因 外語…

DM365的BSP源碼分析-基于2.6.18內核

DM365的BSP主要包含mach-davinci和plat-davinci兩個目錄&#xff08;及相關頭文件&#xff09;&#xff0c;BSP復雜龐大又極其重要&#xff0c;它主要完成了板級的初始化&#xff0c;比如內存映射&#xff0c;時鐘和電源初始化&#xff0c;中斷和IO初始化&#xff0c;CPU及各模…

第四章:Django 模型 —— 設計系統表

1. Django框架提供了完善的模型&#xff08;Model &#xff09;層來創建和存儲數據&#xff0c;每一個模型對應數據庫中的唯一的一張表。 2. Django 模型基礎知識&#xff1a; 。每一本模型是一個Python類&#xff0c;繼承了django.db.models.Model類 。該模型中每一個屬性一個…

DM365 使用BT656協議驅動LCD的實現

前兩天已經調好了&#xff0c;主要是對davinci_platform.c的修改 因為輸入輸出都為pal的制式&#xff0c;所以就在pal的函數中進行了修改。 在PAL設置的函數中&#xff0c;修改如下&#xff1a; /* * setting PAL mode */ static void davinci_enc_set_pal(struct vid_enc_mode…

工業視覺鏡頭NAVITAR

品牌介紹 美國NAVITAR是優越的上等光學系統制造商和供應商&#xff0c;工業視覺鏡頭NAVITAR為機器視覺、檢測和生物醫學診斷行業提供的定制光學解決方案。 工業視覺鏡頭NAVITAR用于鑒定產品、檢查產品缺陷、測量零件尺寸、操縱機器人設備和協助進行科學分析與探索。 還用來引導…