1. 程式人生 > >併發解決方案-基於redis的訊息佇列

併發解決方案-基於redis的訊息佇列

上一篇教大家用spring-data-redis來實現redis的訊息佇列:

https://blog.csdn.net/u011870280/article/details/80012732

現在接著來做一個測試,試試redis佇列在併發場景下的效能。

首先來一個沒佇列的場景,比如團購秒殺大家來搶一雙鞋子

在上篇專案的基礎上引入資料庫相關依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId>
</dependency>

然後配置mysql

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://192.168.1.22:3306/test?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456

建立一個實體-庫存類

package com.xzg.redis.entity;
import javax.persistence.Entity;
import 
javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Stock { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String name; private Long count; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getCount() { return count; } public void setCount(Long count) { this.count = count; } }

然後來個操作介面,實現買商品減庫存操作

package com.xzg.redis.repository;
import com.xzg.redis.entity.Stock;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
public interface StockRepository extends CrudRepository<Stock, Long>{
    @Transactional
    @Modifying(clearAutomatically = true)
    @Query("update Stock stock set stock.count =stock.count-1 where stock.id =:stockId")
    void buy(@Param("stockId") Long stockId);
}

最後編寫Controller,暴露入口,完成,執行專案。

package com.xzg.redis.controller;
import com.xzg.redis.repository.StockRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class ShopController {
    @Autowired
private StockRepository stockRepository;
@GetMapping(path="/buy/{stockId}")
    public @ResponseBody String buy (@PathVariable("stockId") Long stockId) {
        stockRepository.buy(stockId);
        return "OK";
}
}

可以測試了,這時候我們先來插入一條庫存資訊

insert into stock(id,name,count) values(1,'shoes',100000);

開啟Jmeter,模擬100000次請求


配置http請求

測試結果

TPS

響應時間


沒有發生錯誤,平均響應時間是0.3秒,因為是同步處理,所以TPS=throughput= 1400,還不錯。

資料庫100000的庫存也清空了

可是這十萬的請求花了一分鐘才處理完,也就是說如果有十萬個連線同時過來搶購,有些人可能要1分鐘後才收到結果,很可能超過30秒連線就超時斷開了,更有可能的是伺服器處理不過來,響應更慢,照成服務不可用甚至宕機。

接下來試試常規的處理方法,把請求塞入佇列,然後馬上返回客戶端已提交or排隊中。

就用上一篇的redis佇列,修改下Controller程式碼:

package com.xzg.redis.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class ShopController {
    @Autowired
StringRedisTemplate template;
@GetMapping(path="/buy/{stockId}")
    public @ResponseBody String buy (@PathVariable("stockId") Long stockId) {
        template.convertAndSend("shop", stockId.toString());
        return "OK";
}
}

Reciver程式碼

package com.xzg.redis.message;
import com.xzg.redis.repository.StockRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Receiver {
    private static final Logger LOGGER = LoggerFactory.getLogger(Receiver.class);
@Autowired
StockRepository stockRepository;
    public void receiveMessage(String message) {
        stockRepository.buy(Long.valueOf(message));
}
}

Application程式碼:

package com.xzg.redis;
import com.xzg.redis.message.Receiver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@SpringBootApplication
public class Application {
    private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("shop"));
        return container;
}
    @Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
        return new MessageListenerAdapter(receiver, "receiveMessage");
}
    @Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
}
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
LOGGER.info("app started");
}
}

好,再來測試一遍!

響應時間

TPS,這一塊可能有點問題。因為我實際測試的時候比上個快多了。

總覽

用了佇列的好處很明顯,平均響應時間0.09秒,快了3倍多,而且吞吐量也上去了,接近5000!,也就是10W請求4秒就響應完了。這是很恐怖的,實際應用因為業務比較多,一般達到1000也就不錯了。

當然這是並不代表處理完成了,只能說伺服器已經接受你的請求正在處理,受限於資料庫的瓶頸,TPS還是1400,我測試報告生成了資料庫還在慢慢減庫存。

但是起碼使用者體驗好了,伺服器也可用,沒有宕機的風險。

相關推薦

併發解決方案-基於redis訊息佇列

上一篇教大家用spring-data-redis來實現redis的訊息佇列:https://blog.csdn.net/u011870280/article/details/80012732現在接著來做一個測試,試試redis佇列在併發場景下的效能。首先來一個沒佇列的場景,比

【高併發簡單解決方案redis快取佇列+mysql 批量入庫+php離線整合

需求背景:有個呼叫統計日誌儲存和統計需求,要求儲存到mysql中;儲存資料高峰能達到日均千萬,瓶頸在於直接入庫併發太高,可能會把mysql幹垮。 問題分析 思考:應用網站架構的衍化過程中,應用最新的框架和工具技術固然是最優選擇;但是,如果能在現有的框架的基礎上提

SpringBoot(9) 基於Redis訊息佇列實現非同步操作

什麼是訊息佇列?所謂訊息佇列,就是一個以佇列資料結構為基礎的一個真實存在的實體,如陣列,redis中的佇列集合等等,都可以。為什麼要使用佇列?主要原因是由於在高併發環境下,由於來不及同步處理,請求往往會發生堵塞,比如說,大量的insert,update之類的請求同時到達MyS

【高併發簡單解決方案redis佇列快取 + mysql 批量入庫 + php離線整合--轉載

轉載自::轉載需求背景:有個呼叫統計日誌儲存和統計需求,要求儲存到mysql中;儲存資料高峰能達到日均千萬,瓶頸在於直接入庫併發太高,可能會把mysql幹垮。問題分析思考:應用網站架構的衍化過程中,應用最新的框架和工具技術固然是最優選擇;但是,如果能在現有的框架的基礎上提出簡

RabbitMQ訊息可靠性投遞解決方案 - 基於SpringBoot實現

https://www.imooc.com/article/49814 參考地址: https://www.imooc.com/t/2726237 談到訊息的可靠性投遞,無法避免的,在實際的工作中會經常碰到,比如一些核心業務需要保障訊息不丟失,接下來我們看一個可靠性投遞的流程圖,說明可靠

2.基於redis非同步佇列模組(Reactor模式)-執行緒池還是Redis還是Rabbitmq訊息佇列作為非同步處理的選擇

1.訊息佇列和多執行緒兩者並不衝突,多執行緒可以作為佇列的生產者和消費者。使用外部的訊息佇列時,第一是可以提高應用的穩定性,當程式fail後,寫入外部訊息佇列的資料依舊是儲存的,如果使用兩步commit的佇列的話,可以更加提高這個專案。2. 用執行緒的話,會佔用主伺服器資源,

nodejs + nginx + redis cluster 高併發解決方案

針對於使用者使用資料而言,我們應該會對資料進行分級。例如簡單的兩級,使用者會經常訪問的(例如自己的id,地理資訊);使用者不會經常訪問的(例如歷史訂單)。因此我們設計系統時就應該去考慮消除冗餘,讓經常使用的資料有更多的訪問速度資源,不會經常使用的資料儘可能少的速度資源。最通俗的做法就是,經常訪問的放在記憶體中

大型網際網路高併發解決方案訊息中介軟體技術-activeMQ詳解

點選上方藍字關注的都是靚仔和仙女 概述 ActiveMQ是Apache所提供的一個開源的訊息系統,完全採用Java來實現,因此,它能很好地支援J2EE提出的JMS(Java Message Service,即Java訊息服務)規範。JMS是一組Java應用程式介面,它提供訊息的建立、傳送、讀取等一系列服

分析redis訊息佇列和kafka來解決分散式事務場景

1、系統A(扣減托盤)【訊息生產者】 2、系統B(扣減押金)【訊息消費者】 業務描述: 兩套系統,A中扣減托盤,B中對應的要扣減押金;A中托盤歸還,B中押金返還 利用訊息佇列來解決分散式事務過程: 傳送方【生產者】:(不關心接收方狀態,只需要確定本地OK,訊息推送即可)

新手搭建 WordPress 網站終極解決方案 基於 Bitnami 堆棧快速搭建完美個人博客(Blog)

分享 成了 rdp 網上 特色 雲服務 nco BE tro 為了搭建一個自己的博客,對於沒有相關知識的我來說,真的時相當不容易,經過很長時間的摸索研究,總算是搭起了這個博客(Blog),那麽第一篇文章就是來記錄一下,經過反復的折騰後感覺最適合我自己的一個方法,文章包含很多

Jmeter+jenkins如何快速搭建介面和效能測試持續整合解決方案-[基於windows篇]

  Jenkins + Jmeter 構建介面、效能測試持續整合解決方案   Jenkins + Jmeter 可以很輕鬆的進行打包釋出程式後自動進行介面冒煙測試,或者定時效能測試。以下是詳盡的Jenkins + Jmeter

Jmeter+jenkins如何快速搭建接口和性能測試持續集成解決方案-[基於windows篇]

pau 解決 fill containe lec 配置環境變量 java_home 保存 man 最近在用Jmeter本來想寫一個詳細的使用教程,突然看到有前輩已經寫好了不錯的教程,特此"借花獻佛"整理出來分享給大家! Jenkins + Jmete

大規模分散式應用之海量資料和高併發解決方案總結視訊教程網盤

大規模分散式應用之海量資料和高併發解決方案總結視訊教程網盤 39套Java架構師,高併發,高效能,高可用,分散式,叢集,電商,快取,微服務,微信支付寶支付,公眾號開發,java8新特性,P2P金融專案,程式設計,功能設計,資料庫設計,第三方支付,web安全,效能調優,設計模式,資料結構,併發程式

brpop阻塞redis訊息佇列

不使用brpop的時候其實也可以實現redis的訊息佇列,只是不是阻塞的,目前已知的問題長時間沒有任務的話,consumer會出現假死的狀態,使用redis3.0版本,聽說使用3.2以上的版本不會出現這種假死的問題,目前沒有測試: def parse_url(self):

併發解決方案(負載均衡)

1,什麼是負載均衡? 當一臺伺服器的效能達到極限時,我們可以使用伺服器叢集來提高網站的整體效能。那麼,在伺服器叢集中,需要有一臺伺服器充當排程者的角色,使用者的所有請求都會首先由它接收,排程者再根據每臺伺服器的負載情況將請求分配給某一臺後端伺服器去處理。 那麼在這個過程中,排程者如何合理分配

Python併發解決方案

  一、subprocess模組 call():執行命令,返回程式返回碼(int) import subprocess   print(subprocess.call("mspaint"))   check_output():執行命令,返回輸出(bytes) im

併發解決方案 -負載均衡

上一篇文章說過會轉載一篇負載均衡的介紹方面的文章,就是下面這個了~~~ 什麼是負載均衡? 當一臺伺服器的效能達到極限時,我們可以使用伺服器叢集來提高網站的整體效能。那麼,在伺服器叢集中,需要有一臺伺服器充當排程者的角色,使用者的所有請求都會首先由它接收,排程者再根據每臺伺服器的負載情

golang基於redis搭建佇列

首先你需要安裝redigo go get github.com/garyburd/redigo/redis 使用go的(interface)介面實現, 話不多說,直接上程式碼。 package main import ( "github.com/garyburd/

Java高併發解決方案之非同步處理

(() -> { // 請求1 CompletableFuture<List<Integer>> completionStage1 = CompletableFuture.supplyAsync(() -> { //

Java高併發解決方案

Java高併發,如何解決,什麼方式解決 對於我們開發的網站,如果網站的訪問量非常大的話,那麼我們就需要考慮相關的併發訪問問題了。而併發問題是絕大部分的程式設計師頭疼的問題, 但話又說回來了,既然逃避不掉,那我們就坦然面對吧~今天就讓我們一起來研究一下常見的併