【RocketMQ】快速入門

文章目錄

  • 消費模式
  • 同步消息
  • 異步消息
  • 單向消息
  • 延遲消息
  • 批量消息
  • 順序消息
  • 事務消息
  • Tag標簽和Key鍵
    • Tag的使用
    • Key的使用

首先引入rocketmq的依賴

<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><version>4.9.2</version>
</dependency>

然后我們編寫一個簡單的生產者和消費者

@SpringBootTest
public class RocketMQTest {/*** 對于生產者 同一組的生產者可以向不同的topic隊列發送消息*/@Testpublic void produce() throws MQClientException, MQBrokerException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");producer.setNamesrvAddr(MQConstant.NAMESRV);producer.start();Message message = new Message("testTopic","一個簡單的消息".getBytes());SendResult sendResult = producer.send(message);System.out.println(sendResult.getSendStatus());producer.shutdown();}/*** 對于消費者 同一組的消費者只能接收同一個topic的消息* 并且如果存在多個消費者組,他們都監聽同一個topic的消息* 那么就可以選擇使用  負載均衡策略 或者 廣播策略*/@Testpublic void consume() throws MQClientException, IOException {//創建一個消費者DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-producer-group");consumer.setNamesrvAddr(MQConstant.NAMESRV);// * 標識訂閱這個主題中的所有消息  后期會有消息過濾consumer.subscribe("testTopic", "*");//設置一個監聽器 (他會一直監聽,然后是一個異步回調的機制)//那么我們就不能讓他start之后這個方法就返回結束 需要掛起當前的JVM(test模式得這樣子)//正常運行項目的時候項目的JVM會正常運行的 不需要掛起consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext context) {//這個就是對應的消費方法  業務處理//消息如果消費失敗 那么就要重新放入到消費隊列System.out.println("我是消費者");System.out.println(list.get(0).toString());System.out.println("消息上下文"+context);//返回值如果為null/報錯/RECONSUMER_LATER 代表消費失敗//消息會重新回到隊列 然后過一會在投遞給當前消費者或者其他消費者return ConsumeConcurrentlyStatus.RECONSUME_LATER;}});//啟動consumer.start();//掛起當前的JVMSystem.in.read();}
}

這里需要注意的是,對于Rocketmq,如果在你的監聽器中,也就是這個MessageListenerConcurrently中,你的返回值為null,或者ConsumeConcurrentlyStatus.RECONSUME_LATER,亦或者拋出了一個異常,那么這條消息都會重新的被放回到我們的隊列中,等待其他消費者或者當前消費者再一次消費。

消費模式

MQ的消費模式可以大致分為兩種,一種是推Push,一種是拉Pull。
Push是服務端【MQ】主動推送消息給客戶端,優點是及時性較好,但如果客戶端沒有做好流控,一旦服務端推送大量消息到客戶端時,就會導致客戶端消息堆積甚至崩潰。
Pull是客戶端需要主動到服務端取數據,優點是客戶端可以依據自己的消費能力進行消費,但拉取的頻率也需要用戶自己控制,拉取頻繁容易造成服務端和客戶端的壓力,拉取間隔長又容易造成消費不及時。
Push模式也是基于pull模式的,只能客戶端內部封裝了api,一般場景下,上游消息生產量小或者均速的時候,選擇push模式。在特殊場景下,例如電商大促,搶優惠券等場景可以選擇pull模式

同步消息

上面的快速入門就是發送同步消息,發送過后會有一個返回值,也就是mq服務器接收到消息后返回的一個確認,這種方式非常安全,但是性能上并沒有這么高,而且在mq集群中,也是要等到所有的從機都復制了消息以后才會返回,所以針對重要的消息可以選擇這種方式

異步消息

異步消息通常用在對響應時間敏感的業務場景,即發送端不能容忍長時間地等待Broker的響應。發送完以后會有一個異步消息通知。

@Test
public void testAsyncProducer() throws Exception {// 創建默認的生產者DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");// 設置nameServer地址producer.setNamesrvAddr(MQConstant.NAMESRV);// 啟動實例producer.start();Message msg = new Message("testTopic", ("異步消息").getBytes());producer.send(msg, new SendCallback() {@Overridepublic void onSuccess(SendResult sendResult) {System.out.println("發送成功");}@Overridepublic void onException(Throwable e) {System.out.println("發送失敗");}});System.out.println("看看誰先執行");// 掛起jvm 因為回調是異步的不然測試不出來System.in.read();// 關閉實例producer.shutdown();
}

單向消息

這種方式主要用在不關心發送結果的場景,這種方式吞吐量很大,但是存在消息丟失的風險,例如日志信息的發送。

@Test
public void testOnewayProducer() throws Exception {// 創建默認的生產者DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");// 設置nameServer地址producer.setNamesrvAddr(MQConstant.NAMESRV);// 啟動實例producer.start();Message msg = new Message("testTopic", ("單向消息").getBytes());// 發送單向消息producer.sendOneway(msg);// 關閉實例producer.shutdown();
}

延遲消息

消息放入mq后,過一段時間,才會被監聽到,然后消費
比如下訂單業務,提交了一個訂單就可以發送一個延時消息,30min后去檢查這個訂單的狀態,如果還是未付款就取消訂單釋放庫存。
這里注意的是RocketMQ不支持任意時間的延時
只支持以下幾個固定的延時等級,等級1就對應1s,以此類推,最高支持2h延遲
private String messageDelayLevel = “1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”;

@Test
public void testDelayProducer() throws Exception {// 創建默認的生產者DefaultMQProducer producer = new DefaultMQProducer("test-group");// 設置nameServer地址producer.setNamesrvAddr("localhost:9876");// 啟動實例producer.start();Message msg = new Message("TopicTest", ("延遲消息").getBytes());// 給這個消息設定一個延遲等級// messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2hmsg.setDelayTimeLevel(3);// 發送單向消息producer.send(msg);// 打印時間System.out.println(new Date());// 關閉實例producer.shutdown();
}

批量消息

批量消息就是一次性發送一個消息集合出去。

@Test
public void testBatchProducer() throws Exception {// 創建默認的生產者DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");// 設置nameServer地址producer.setNamesrvAddr(MQConstant.NAMESRV);// 啟動實例producer.start();List<Message> messages = Arrays.asList(new Message("testTopic", "批量消息1".getBytes()),new Message("testTopic", "批量消息2".getBytes()),new Message("testTopic", "批量消息3".getBytes()));producer.send(messages);System.out.println("批量執行任務");// 掛起jvm 因為回調是異步的不然測試不出來System.in.read();// 關閉實例producer.shutdown();
}

順序消息

我們知道一個topic中可以有多個隊列,那么如果我們的消息發送到多個隊列中去,那么很明顯我們的消息消費就是并行消費的,也就是沒有了順序性。
因此如果我們需要發送順序消息,也就是希望MQ那邊的消費者順序的消費一些消息,我們就得按照如下方式發送順序消息。
消息有序指的是可以按照消息的發送順序來消費(FIFO)。RocketMQ可以嚴格的保證消息有序,可以分為:分區有序或者全局有序。
可能大家會有疑問,mq不就是FIFO嗎?
rocketMq的broker的機制,導致了rocketMq會有這個問題
因為一個broker中對應了四個queue。

不同的queue(分區隊列);而消費消息的時候從多個queue上拉取消息,這種情況發送和消費是不能保證順序。但是如果控制發送的順序消息只依次發送到同一個queue中,消費的時候只從這個queue上依次拉取,則就保證了順序。當發送和消費參與的queue只有一個,則是全局有序;如果多個queue參與,則為分區有序,即相對每個queue,消息都是有序的。
下面用訂單進行分區有序的示例。一個訂單的順序流程是:下訂單、發短信通知、物流、簽收。訂單順序號相同的消息會被先后發送到同一個隊列中,消費時,同一個順序獲取到的肯定是同一個隊列。

package zhang.blossom.seckillbyrocketmq;import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import zhang.blossom.seckillbyrocketmq.constant.MQConstant;
import zhang.blossom.seckillbyrocketmq.entity.MsgModel;import java.io.IOException;
import java.util.Arrays;
import java.util.List;/*** @author: 張錦標* @date: 2023/8/17 9:58* OrderedRocketMQTest類*/@SpringBootTest
public class OrderedRocketMQTest {private List<MsgModel> msgModels = Arrays.asList(new MsgModel("qwer", 1L, "下單"),new MsgModel("qwer", 1L, "短信"),new MsgModel("qwer", 1L, "物流"),new MsgModel("zxcv", 2L, "下單"),new MsgModel("zxcv", 2L, "短信"),new MsgModel("zxcv", 2L, "物流"));//發送順序消息@Testpublic void orderedProducer() throws MQClientException, MQBrokerException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");producer.setNamesrvAddr(MQConstant.NAMESRV);producer.start();//發送順序消息 發送時要確保有序  并且要發送到同一個隊列下面去msgModels.forEach(msgModel -> {Message message = new Message("testTopic",msgModel.toString().getBytes());try {//發送  相同的訂單號應該去相同的隊列producer.send(message, new MessageQueueSelector() {//這里的send方法的第三個參數arg 就是這個隊列選擇器的第三個參數 會傳遞過來@Overridepublic MessageQueue select(List<MessageQueue> list, Message message, Object arg) {//這個方法的返回值就是要選擇的隊列//這里可以用hash的方式就可以選擇到同樣的隊列了int hash = arg.toString().hashCode();int index = hash % list.size();return list.get(index);}}, msgModel.getOrderSn());} catch (MQClientException e) {throw new RuntimeException(e);} catch (RemotingException e) {throw new RuntimeException(e);} catch (MQBrokerException e) {throw new RuntimeException(e);} catch (InterruptedException e) {throw new RuntimeException(e);}});producer.shutdown();System.out.println("發送完畢");}@Testpublic void orderedConsumer() throws MQClientException, IOException {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-producer-group");consumer.setNamesrvAddr(MQConstant.NAMESRV);consumer.subscribe("testTopic", "*");//MessageListenerConcurrently 并發模式  多線程的  失敗后最多重試16次 然后放入死信隊列//MessageListenerOrderly 順序模式 單線程的  失敗后無限次重試 Integer.MAX_VALUEconsumer.registerMessageListener(new MessageListenerOrderly() {//順序模式只有一個線程來執行消費@Overridepublic ConsumeOrderlyStatus consumeMessage(List<MessageExt> list,ConsumeOrderlyContext consumeOrderlyContext) {//這里的一個線程是一個隊列一個線程System.out.println(new String(list.get(0).getBody()));return ConsumeOrderlyStatus.SUCCESS;}});consumer.start();System.in.read();}
}

事務消息

一般我們不使用RocketMQ的事務消息,所以有興趣的可以看看其他的實現。

Tag標簽和Key鍵

Rocketmq提供消息過濾功能,通過tag或者key進行區分。
我們往一個主題里面發送消息的時候,根據業務邏輯,可能需要區分,比如帶有tagA標簽的被A消費,帶有tagB標簽的被B消費,還有在事務監聽的類里面,只要是事務消息都要走同一個監聽,我們也需要通過過濾才區別對待。

Tag的使用

@Test
public void tagProducer() throws Exception {DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");producer.setNamesrvAddr(MQConstant.NAMESRV);producer.start();Message message1 = new Message("testTopic", "test1","test1的消息".getBytes() );Message message2 = new Message("testTopic", "test2","test2的消息".getBytes() );producer.send(message1);producer.send(message2);producer.shutdown();System.out.println("消息發送成功");
}@Test
public void test1Consumer() throws MQClientException, IOException {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-producer-group");consumer.setNamesrvAddr(MQConstant.NAMESRV);consumer.subscribe("testTopic", "test1");consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list,ConsumeConcurrentlyContext consumeConcurrentlyContext) {System.out.println("消費test1的消息"+new String(list.get(0).getBody()));return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});consumer.start();System.in.read();
}
@Test
public void test2Consumer() throws MQClientException, IOException {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-producer-group");consumer.setNamesrvAddr(MQConstant.NAMESRV);consumer.subscribe("testTopic", "test1 || test2");consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list,ConsumeConcurrentlyContext consumeConcurrentlyContext) {System.out.println("消費test1/test2的消息"+new String(list.get(0).getBody()));return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});consumer.start();System.in.read();
}

什么時候該用Topic,什么時候該用 Tag?
總結:不同的業務應該使用不同的Topic如果是相同的業務里面有不同表的表現形式,那么我們要使用tag進行區分
可以從以下幾個方面進行判斷:
1.消息類型是否一致:如普通消息、事務消息、定時(延時)消息、順序消息,不同的消息類型使用不同的 Topic,無法通過 Tag 進行區分。
2.業務是否相關聯:沒有直接關聯的消息,如淘寶交易消息,京東物流消息使用不同的 Topic 進行區分;而同樣是天貓交易消息,電器類訂單、女裝類訂單、化妝品類訂單的消息可以用 Tag 進行區分。
3.消息優先級是否一致:如同樣是物流消息,盒馬必須小時內送達,天貓超市 24 小時內送達,淘寶物流則相對會慢一些,不同優先級的消息用不同的 Topic 進行區分。
4.消息量級是否相當:有些業務消息雖然量小但是實時性要求高,如果跟某些萬億量級的消息使用同一個 Topic,則有可能會因為過長的等待時間而“餓死”,此時需要將不同量級的消息進行拆分,使用不同的 Topic。
總的來說,針對消息分類,您可以選擇創建多個Topic,或者在同一個 Topic 下創建多個 Tag。但通常情況下,不同的 Topic 之間的消息沒有必然的聯系,而 Tag 則用來區分同一個 Topic 下相互關聯的消息,例如全集和子集的關系、流程先后的關系。

Key的使用

在rocketmq中的消息,默認會有一個messageId當做消息的唯一標識,我們也可以給消息攜帶一個key,用作唯一標識或者業務標識,包括在控制面板查詢的時候也可以使用messageId或者key來進行查詢。

@Test
public void testKeyProducer() throws Exception {// 創建默認的生產者DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");// 設置nameServer地址producer.setNamesrvAddr(MQConstant.NAMESRV);// 啟動實例producer.start();Message msg = new Message("testTopic","test1","key", "我是一個帶標記和key的消息".getBytes());SendResult send = producer.send(msg);System.out.println(send);// 關閉實例producer.shutdown();
}@Test
public void testKeyConsumer() throws Exception {// 創建默認消費者組DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-producer-group");// 設置nameServer地址consumer.setNamesrvAddr(MQConstant.NAMESRV);// 訂閱一個主題來消費   表達式,默認是*,支持"tagA || tagB || tagC" 這樣或者的寫法 只要是符合任何一個標簽都可以消費consumer.subscribe("testTopic", "test1 || test2 || test3");// 注冊一個消費監聽 MessageListenerConcurrently是并發消費// 默認是20個線程一起消費,可以參看 consumer.setConsumeThreadMax()consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,ConsumeConcurrentlyContext context) {// 這里執行消費的代碼 默認是多線程消費System.out.println(Thread.currentThread().getName() + "----" + new String(msgs.get(0).getBody()));System.out.println(msgs.get(0).getTags());System.out.println(msgs.get(0).getKeys());return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});consumer.start();System.in.read();
}

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

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

相關文章

HackNos 3靶場

配置 進入控制面板配置網卡 第一步&#xff1a;啟動靶機時按下 shift 鍵&#xff0c; 進入以下界面 第二步&#xff1a;選擇第二個選項&#xff0c;然后按下 e 鍵&#xff0c;進入編輯界面 將這里的ro修改為rw single init/bin/bash&#xff0c;然后按ctrlx&#xff0c;進入…

數據結構的圖存儲結構

目錄 數據結構的圖存儲結構 圖存儲結構基本常識 弧頭和弧尾 入度和出度 (V1,V2) 和 的區別,v2> 集合 VR 的含義 路徑和回路 權和網的含義 圖存儲結構的分類 什么是連通圖&#xff0c;&#xff08;強&#xff09;連通圖詳解 強連通圖 什么是生成樹&#xff0c;生…

springboot集成ES

1.引入pom依賴2.application 配置3.JavaBean配置以及ES相關注解 3.1 Student實體類3.2 Teacher實體類3.3 Headmaster 實體類4. 啟動類配置5.elasticsearchRestTemplate 新增 5.1 createIndex && putMapping 創建索引及映射 5.1.1 Controller層5.1.2 service層5.1.3 ser…

leetcode做題筆記85最大矩形

給定一個僅包含 0 和 1 、大小為 rows x cols 的二維二進制矩陣&#xff0c;找出只包含 1 的最大矩形&#xff0c;并返回其面積。 示例 1&#xff1a; 思路一&#xff1a;單調棧 int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize){int dp[matrixSize…

使用MAT分析OOM問題

OOM和內存泄漏在我們的工作中&#xff0c;算是相對比較容易出現的問題&#xff0c;一旦出現了這個問題&#xff0c;我們就需要對堆進行分析。 一般情況下&#xff0c;我們生產應用都會設置這樣的JVM參數&#xff0c;以便在出現OOM時&#xff0c;可以dump出堆內存文件&#xff…

基于libevent的tcp服務器

libevent使用教程_evutil_make_socket_nonblocking_易方達藍籌的博客-CSDN博客 一、準備 centos7下安裝libevent庫 yum install libevent yum install -y libevent-devel 二、代碼 server.cpp /** You need libevent2 to compile this piece of code Please see: http://li…

專訪 BlockPI:共建賬戶抽象未來的新一代 RPC 基礎設施

在傳統 RPC 服務板塊上&#xff0c;開發者一直飽受故障風險、運行環境混亂等難題的折磨。實現 RPC 服務的去中心化&#xff0c;且保持成本優勢和可擴展性&#xff0c;始終是區塊鏈基礎設施建設的重要命題之一。從 2018 年觀察中心化 RPC 供應商服務現狀開始&#xff0c;BlockPI…

內存管理(1)

內存管理&#xff08;1&#xff09; 1、各類型數據在內存中的存儲空間2、C內存管理方式2.1 針對于內置類型分析2.2 針對于自定義類型分析2.3 C語言與C在申請動態內存失敗時的區別 3、operator new 和 operator delete函數&#xff08;重點&#xff09;3.1 底層知識解析3.2 實現…

linux-shell腳本收集

創建同步腳本xsync mkdir -p /home/hadoop/bin && cd /home/hadoop/bin vim xsync#!/bin/bash#1. 判斷參數個數 if [ $# -lt 1 ] thenecho Not Arguementexit; fi#2. 遍歷集群所有機器 for host in node1 node2 node3 doecho $host #3. 遍歷所有目錄&#xff0c;挨…

web3:使用Docker-compose方式部署blockscout

最近做的項目,需要blockscout來部署一個區塊鏈瀏覽器,至于blockscout是什么,咱們稍后出一篇文章專門介紹下,本次就先介紹一下如何使用Docker-compose方式部署blockscout,以及過程中遇到的種種坑 目錄 先決條件我的環境準備工作Docker-compose1.安裝方式一:下載 Docker Co…

財務數據分析之現金流量表模板分享

現金流量表是我們常說的財務數據分析三表之一。它可以呈現一個企業的現金流情況&#xff0c;揭示企業經營管理健康狀態&#xff0c;但在實際使用中卻有總給人一種用不上、用不好的矛盾感。怎么才能把現金流量表做好&#xff1f;不如借鑒下大神的現金流量表模板。 下面介紹的是…

RabbitMQ-消息中間件學習記錄(what-how-why)

什么是消息中間件 簡單的來說就是消息隊列中間件&#xff0c;生產者發送消息到中間件&#xff0c;消息中間件用于 保存消息并發送消息到消費者。 消息中間件RabbitMQ的基本組件 1&#xff09;producer -生產者 2&#xff09;customer -消費者 3&#xff09;broker (經紀人)- M…

【Java 動態數據統計圖】動態數據統計思路案例(動態,排序,數組)四(116)

需求&#xff1a;&#xff1a;前端根據后端的返回數據&#xff1a;畫統計圖&#xff1b; 1.動態獲取地域數據以及數據中的平均值&#xff0c;按照平均值降序排序&#xff1b; 說明&#xff1a; X軸是動態的&#xff0c;有對應區域數據則展示&#xff1b; X軸 區域數據降序排序…

LabVIEW調用DLL傳遞結構體參數

LabVIEW 中調用動態庫接口時&#xff0c;如果是值傳遞的結構體&#xff0c;可以根據字段拆解為多個參數&#xff1b;如果參數為結構體指針&#xff0c;可用簇&#xff08;Cluster&#xff09;來匹配&#xff0c;其內存連續相當于單字節對齊。 1.值傳遞 接口定義&#xff1a; …

【FAQ】調用視頻匯聚平臺EasyCVR的iframe地址,視頻無法播放的原因排查

有用戶反饋&#xff0c;在調用iframe地址后嵌入用戶自己的前端頁面&#xff0c;視頻無法播放并且要求登錄。 安防監控視頻匯聚平臺EasyCVR基于云邊端一體化架構&#xff0c;具有強大的數據接入、處理及分發能力&#xff0c;可提供視頻監控直播、云端錄像、視頻云存儲、視頻集中…

視頻集中存儲EasyCVR視頻匯聚平臺定制項目增加AI智能算法

安防視頻集中存儲EasyCVR視頻匯聚平臺&#xff0c;可支持海量視頻的輕量化接入與匯聚管理。平臺能提供視頻存儲磁盤陣列、視頻監控直播、視頻輪播、視頻錄像、云存儲、回放與檢索、智能告警、服務器集群、語音對講、云臺控制、電子地圖、平臺級聯、H.265自動轉碼等功能。為了便…

【Unity每日一記】Physics.Raycast 相關_Unity中的“X光射線”

&#x1f468;?&#x1f4bb;個人主頁&#xff1a;元宇宙-秩沅 &#x1f468;?&#x1f4bb; hallo 歡迎 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! &#x1f468;?&#x1f4bb; 本文由 秩沅 原創 &#x1f468;?&#x1f4bb; 收錄于專欄&#xff1a;uni…

05_bitmaphyperloglogGEO

Bitmap&hyperloglog&GEO 面試問 記錄對集合中的數據進行統計在移動應用中&#xff0c;需要統計每天的新增用戶數和第2天的留存用戶數&#xff1b;在電商網站的商品評論中&#xff0c;需要統計評論列表中的最新評論&#xff1a;在簽到打卡中&#xff0c;需要統計一個月內…

Python “貪吃蛇”游戲,在不斷改進中學習pygame編程

目錄 前言 改進過程一 增加提示信息 原版幫助摘要 pygame.draw pygame.font class Rect class Surface 改進過程二 增加顯示得分 改進過程三 增加背景景樂 增加提示音效 音樂切換 靜音切換 mixer.music.play 注意事項 原版幫助摘要 pygame.mixer pygame.mix…

kvm和vmware有什么區別?如何選擇?

一、kvm和vmware的區別 VMware vSphere 平臺 VMware 可以提供 ESXi 虛擬機監控程序和 vSphere 虛擬化平臺。VMware ESXi 是一個能夠直接安裝到物理服務器上的裸機虛擬機監控程序&#xff0c;可以幫你整合硬件。你可以用 VMware 的虛擬化技術來創建和部署虛擬機&#xff08;VM…