springboot2 springcloud Greenwich.SR3 構建微服務--5.rabbitMQ的使用

其實在統一配置中心的時候就應該開始說rabbitmq 了, 但是沒有說, 所以這裏專門說下rabbitmq.

rabbitmq 在配置中心作爲消息的傳遞來使用了.

 

rabbitmq的原理, 具體使用, 請查閱這個

https://blog.csdn.net/u010398771/article/details/84136959

 

現在的mq開源的不要太多了, 我最先用的activemq, 後來阿里開源的rocketmq,

還有 kafka, rabbitmq. 最近看新聞,騰訊也開源了他們用java寫的MQ, 名字不記得了...又是個億萬級別的服務, 牛掰哄哄的樣子

其實rabbitMQ的安裝很簡單, 這裏就不推薦大家先下載erlang 再安裝rabbitmq 了, 更不推薦你去編譯源碼安裝, 因爲我安裝過一次, 裝的時候,電腦卡, 時間又長, 你可以直接在你的docker裏面安裝一個rabbitmq, 就可以使用了,很方便.

docker安裝rabbitmq, 先搜索目前的鏡像

docker search rabbitmq

先拉取下來, 再創建容器運行.注意拉取帶有management 的rabbitmq , 這是帶控制帶的版本.

#拉取鏡像
docker pull rabbitmq:3.7.17-management

#運行
docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq rabbitmq:3.7.17-management

登陸網頁端, http://localhost:15672,  用戶名和密碼都是guest, 就可以看到了管理頁面了, 可以看到目前沒有消息到來和消費

 

rabbitmq的使用

1.添加依賴

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2.添加配置文件

#rabbitmq的配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#解決input 和 output中重名bean問題,需要配置這個....
spring.cloud.stream.bindings.input.destination: raw-sensor-data
spring.cloud.stream.bindings.output.destination: raw-sensor-data

 

2.1. queue 的  bean的配置

//名字都變成常量
public class MessageConstant {

    public final static String INPUT = "input";

    public final static String OUTPUT = "output";

    public final static String QUEUE_NAME = "myQueue";

}
/**
     * 注入一個queue, 不要去手動創建!
     */
    @Bean
    public Queue queue(){
        return new Queue(MessageConstant.QUEUE_NAME,true);
    }

3.就開始發送和接收消息了.


import com.example.order.constans.MessageConstant;
import com.example.order.dto.OrderDTO;
import com.example.order.message.StreamClient;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

/**
 * @author zk
 * @Description:
 * @date 2019-09-20 16:24
 */
@RestController
public class SendMessageController {


    /**
     * 往rabbitmq發送消息
     */
    @Autowired
    private AmqpTemplate amqpTemplate;

    @GetMapping("send")
    public String sendMessage() {
        //需要實現創建一個myQueue的隊列
        amqpTemplate.convertAndSend(MessageConstant.QUEUE_NAME, "this is the first message");
//第二種消息發送
        amqpTemplate.convertAndSend("myOrder", "computer", "this is the first computer  message");
        amqpTemplate.convertAndSend("myOrder", "fruit", "this is the first message");
        return "success";
    }


    @Autowired(required = false)
    private StreamClient streamClient;

    /**
     * 發送字符串對象
     *
     * @return
     */
    /*@GetMapping("send2")
    public String send2() {
        String msg = "time  is today, haha ";
        System.out.println("發送的消息是:" + msg);
        streamClient.output().send(MessageBuilder.withPayload(msg).build());
        return "success";
    }*/


    /**
     * 發送java bean對象
     * 這個send3 不能和send2 使用的同一個變量.但是不能共存, 需要註釋掉, 不然報錯的...
     *
     * @return
     */
    @GetMapping("send3")
    public String send3(){
        OrderDTO dto = new OrderDTO();
        dto.setBuyerAddress("address");
        dto.setBuyerName("xiexin");
        streamClient.output().send(MessageBuilder.withPayload(dto).build());
        return "success";
    }


}

4.消息接收端


import com.example.order.constans.MessageConstant;
import com.example.order.dto.OrderDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

/**
 * @author zk
 * @Description: 接受消息
 * @date 2019-09-20 16:21
 */
@Component
@EnableBinding(StreamClient.class)
public class StreamReceiver {

    /**
     * 接受字符串
     */
    /*@StreamListener(MessageConstant.INPUT)
    public void process(Object msg) {
        System.out.println(msg);
    }*/


    /**
     * 接受orderDTO對象
     */
    @StreamListener(MessageConstant.INPUT)
    @SendTo(MessageConstant.OUTPUT)//處理完消息,再回發送個消息
    public Object process(OrderDTO msg) {
        System.out.println(msg);
        //發送消息
        return "這是消息";
    }


    /**
     * 接受上面的回傳的消息
     */
    @StreamListener(MessageConstant.OUTPUT)
    public void process2(String msg) {
        System.out.println(msg);
    }


}


import com.example.order.constans.MessageConstant;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author zk
 * @Description:mq 接收消息
 * @date 2019-09-20 15:29
 */
@Component
public class MQReceiver {

    //1.手動創建隊列
    //@RabbitListener(queues= MessageConstant.QUEUE_NAME)

    //2.自動創建隊列
    //@RabbitListener(queuesToDeclare = @Queue(MessageConstant.QUEUE_NAME))

    //3.自動創建 exchange 和 queue綁定
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(MessageConstant.QUEUE_NAME)
            ,exchange = @Exchange("myExchange"))
    )
    public void process(String message){
        System.out.println("接收到的消息是:"+message);
    }

    /**
     * 按照exchange 和 key 進行匹配.
     * 數碼消息接受商
     * @param message
     */
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange("myOrder"),
            key = "computer",
            value = @Queue("computerOrder"))
    )
    public void computerProcess(String message){
        System.out.println("computer接收到的消息是:"+message);
    }


    /**
     * 水果消息接受商
     * @param message
     */
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange("myOrder"),
            key = "fruit",
            value = @Queue("fruitOrder"))
    )
    public void fruitProcess(String message){
        System.out.println("fruit接收到的消息是:"+message);
    }



}


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.example.product.entity.ProductInfo;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Component
public class ProductInfoReceiver {

    private final static String HASH_KEY="product_stock";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 傳遞整個對象的方法
     * @param message
     */
    //@RabbitListener(queuesToDeclare = @Queue("productInfo"))
    public void process0(String message) {
        //注意反序列化的時候,兩個對象的前綴要求是一樣的(包名)
        //ProductInfo info = JSON.parseObject(message, ProductInfo.class);
        List<ProductInfo> infos = JSON.parseObject(message, new TypeReference<List<ProductInfo>>() {
        });
        System.out.println("接受到的消息是:" + infos);

        //存儲到redis中去.
        //stringRedisTemplate.opsForValue()
               // .set("product_stock_" + info.getProductId(), String.valueOf(info.getProductStock()));

        /**
         * 單值的key -value 可以儲存, 現在多個的時候, 最好使用hash 的方式進行存儲
         * 這種單值的存儲方式不好...
         * product_stock        1       5
         *      key           field   value
         */
        for (ProductInfo info : infos) {
            stringRedisTemplate.opsForHash().put(HASH_KEY,info.getProductId(),info.getProductStock());
        }
    }


    /**
     * 只傳遞id和剩餘庫存的方法
     * 這裏的參數的接收對象一定和發送的一致,不然rabbitmq會報類型轉換錯誤.
     * json轉換後,就是json的數組或者json字符串了.上面的那個方法是個反例
     * @param message
     */
    @RabbitListener(queuesToDeclare = @Queue("productInfo"))
    public void process(Map<String,Integer> message) {
        //注意反序列化的時候,兩個對象的前綴要求是一樣的(包名)
        System.out.println("接受到的消息是:" + message);

        //存儲到redis中去.
        //stringRedisTemplate.opsForValue()
        // .set("product_stock_" + info.getProductId(), String.valueOf(info.getProductStock()));

        /**
         * 單值的key -value 可以儲存, 現在多個的時候, 最好使用hash 的方式進行存儲
         * 這種單值的存儲方式不好...
         * product_stock        1       5
         *      key           field   value
         */
        for (Map.Entry<String, Integer> entry : message.entrySet()) {
            stringRedisTemplate.opsForHash()
                    .put(HASH_KEY,entry.getKey(),String.valueOf(entry.getValue()));
        }
    }

}


import com.example.order.constans.MessageConstant;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;

/**
 * @author zk
 * @Description:
 * @date 2019-09-20 16:19
 */
public interface StreamClient {

    /**
     * 這裏的input 和 output 必須是不同的字符串, 不然是同名的bean錯誤
     */
    @Input(MessageConstant.INPUT)
    SubscribableChannel input();


    @Output(MessageConstant.OUTPUT)
    MessageChannel output();


}

rabbitmq 的消息是根據 queue  exchange  key 做的分類存儲,

所以上面的myqueue中的不同的key 會得到不同的接收者那裏去.

 

 

 突然覺得我好懶啊, 不願意好好寫博客, 想各種偷懶, 這東西更像在寫自己的隨手筆記了,而不是在寫讓大部分都能夠看懂的博客. 

隨便代碼往這裏一貼, 然後加幾句解釋就完事了, 不講原理, 因爲不想打字.  不想說流程, 因爲懶的去畫很好看的圖出來, 懶的理由還很充分! .

甚至這一系列的博文中可能一些小章節中有誤,  看了代碼應該運行不起來, 但是我還是貼出來, 某些還涉及到其他地方的點滴, 沒有在博客中體現出來, 感覺我就是在發垃圾博客!!!! 

這次一口氣寫十篇關於springcloud 的博文, 但是現在看起來, 估計寫完後質量非常的差. 完全不是我想象的那樣子,既能夠自己總結, 又能幫助別人提高.

反思下自己!!!! 

不過寫文章確實很花時間, 就這種水平的博客, 想寫一個出來, 也差不多得花10分鐘 ! 所以有了對這10篇博客的重新修改

 

整個代碼地址是:

https://github.com/changhe626/micro-service

Java Framework,歡迎各位前來交流java相關
QQ羣:965125360
 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章