文章目錄
- 前言
- 1、前置條件
- 2、搭建service-search模塊
- 3、開發(fā)功能接口
-
-
3.1 添加遠程調(diào)用方法
- 3.2、創(chuàng)建遠程調(diào)用模塊
- 3.3、開發(fā)service-search 模塊接口
-
3.1 添加遠程調(diào)用方法
- 4、RabbitMQ
- 5、完善SKU管理商品上下架
-
-
5.1、商品服務(wù)
- 5.2、es服務(wù)
-
5.1、商品服務(wù)
- 6、最終測試
- 總結(jié)
前言
最終實現(xiàn)效果:針對SKU的上下架
上架效果:
1、后臺選擇SKU
,點擊上架,該SKU
修改為上架
狀態(tài)
2、同時向MQ
發(fā)送消息
3、服務(wù)監(jiān)聽收到消息后向Es
中新增該SKU
基本信息
下架效果:
1、后臺選擇SKU
,點擊下架,該SKU
修改為下架
狀態(tài)
2、同時向MQ
發(fā)送消息
3、服務(wù)監(jiān)聽收到消息后向Es
中刪除該SKU
基本信息
那為什么一個上架的功能要使用這么多知識點呢,最簡單的方式不就是修改SKU的狀態(tài),然后用戶端只顯示已上架的SKU就可以么。
原因有兩點:
1.提高效率
2.還是效率
我們知道,在用戶端搜索SKU信息,最簡單的方式就是通過sku關(guān)鍵詞直接模糊查詢數(shù)據(jù)庫中的sku信息,但是每次查詢數(shù)據(jù)庫都會發(fā)生IO的碰撞,顯然不是最優(yōu)的解決方法,而ElasticSearch就是為了檢索而生,所以我們上架后自動將SKU數(shù)據(jù)存放到ElasticSearch中,然后用戶端直接檢索es中的數(shù)據(jù)即可,這樣檢索速度就哐哐哐的提上去了。
那為什么還要用到消息隊列MQ呢,這是因為RabbitMQ是異步發(fā)送,當我們一個人操作sku可能沒什么,那如果我們的系統(tǒng)有100萬個商戶,并且這100萬個商戶同時操作上下架呢?那么想想就是很恐怖的,當然,真到了這個量級肯定還需要再次調(diào)優(yōu)。這個時候異步的重要的不言而喻。
主要使用技術(shù)點:
- JDK(版本為:1.8)
- SpringBoot(版本為:2.3.6.RELEASE)
- SpringData(用來操作ES)
- RabbitMQ(版本為:3.8)
- ElaticSearch(版本為:7.8)
- Spring Cloud(版本為:Hoxton.SR8)
- Nacos(版本為:2.2.3)
1、前置條件
- Nacos的運行
- ElasticSearch的運行(我這里使用的是IK分詞器和Kibana工具)
- RabbitMQ的運行
- 項目使用的是SpringBoot
關(guān)于如何安裝和運行這些軟件大家可以去百度搜索下,很簡單的。
2、搭建service-search模塊
1、創(chuàng)建一個模塊專門用來操作ES,根據(jù)自己項目接口來放,我這里放在service業(yè)務(wù)模塊下
2、引入ES依賴
<dependencies>
<!-- ES依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- 引入遠程調(diào)用模塊 - 商品模塊 -->
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-product-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
3、es模塊的配置文件
server:
port: 8204
feign:
sentinel:
enabled: true
client:
config:
default: #配置全局的feign的調(diào)用超時時間 如果 有指定的服務(wù)配置 默認的配置不會生效
connectTimeout: 30000 # 指定的是 消費者 連接服務(wù)提供者的連接超時時間 是否能連接 單位是毫秒
readTimeout: 50000 # 指定的是調(diào)用服務(wù)提供者的 服務(wù) 的超時時間() 單位是毫秒
spring:
main:
allow-bean-definition-overriding: true #當遇到同樣名字的時候,是否允許覆蓋注冊
elasticsearch: # ElaticSearch
rest:
uris: http://localhost:9200
rabbitmq:
host: 192.168.64.109
port: 5672
username: guest
password: guest
publisher-confirm-type: CORRELATED
publisher-returns: true
listener:
simple:
prefetch: 1
concurrency: 3
acknowledge-mode: manual
redis:
host: localhost
port: 6379
database: 0
timeout: 1800000
password:
lettuce:
pool:
max-active: 20 #最大連接數(shù)
max-wait: -1 #最大阻塞等待時間(負數(shù)表示沒限制)
max-idle: 5 #最大空閑
min-idle: 0 #最小空閑
4、記得在ES啟動類上加上服務(wù)注冊和遠程調(diào)用注解
這里需要引入兩個依賴,一個是nacos服務(wù)注冊
,一個是服務(wù)調(diào)用openfeign
,因為這兩個依賴我是放在了es模塊的父工程中,所以es模塊我是不需要引入的,大家根據(jù)自己項目結(jié)構(gòu)來即可
@EnableDiscoveryClient //服務(wù)注冊
@EnableFeignClients //服務(wù)調(diào)用
3、開發(fā)功能接口
3.1 添加遠程調(diào)用方法
說明:因為我這里的設(shè)計是在商品上架的時候,向MQ發(fā)送的是SKU主鍵ID,并不是該sku的所有基本信息,所以我需要額外寫接口來根據(jù)sku主鍵id查詢基本信息,這里大家根據(jù)自身情況來寫即可。
找到我們的商品模塊,先創(chuàng)建一個api包,然后將提供給遠程調(diào)用的接口放在api包下
/**
* 遠程調(diào)用API(生產(chǎn)者)
* @author Eric
* @date 2023-06-29 10:00
*/
@RestController
@RequestMapping("/api/product")
public class ProductInnnerController {
@Autowired
private CategoryService categoryService;
@Autowired
private SkuInfoService skuInfoService;
@ApiOperation(value = "根據(jù)分類id獲取分類信息")
@GetMapping("/inner/getCategory/{categoryId}")
public Category getCategory(@PathVariable Long categoryId) {
return categoryService.getById(categoryId);
}
@ApiOperation(value = "根據(jù)skuId獲取sku信息")
@GetMapping("/ inner/getSkuInfo/{skuId}")
public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId) {
return skuInfoService.getById(skuId);
}
}
3.2、創(chuàng)建遠程調(diào)用模塊
1、創(chuàng)建service-client模塊(我這里的設(shè)定是所有遠程調(diào)用的服務(wù)都放在該模塊中,但該模塊并不負責調(diào)用,而是由各自對應(yīng)的子模塊服務(wù)來進行調(diào)用)
2、service-client模塊下再創(chuàng)建子模塊 service-product-client定義接口
我的結(jié)構(gòu)如下:
3、service-client模塊引入依賴:(主要還是openfeign的依賴)
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>common-util</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>model</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<!-- 服務(wù)調(diào)用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
4、ProductFeignClient添加定義方法
/**
* 遠程調(diào)用其他模塊中的api
* @author Eric
* @date 2023-06-29 10:04
*/
@FeignClient(value = "service-product") //指定調(diào)用模塊
public interface ProductFeignClient {
//根據(jù)分類id獲取分類信息
@GetMapping("/api/product/inner/getCategory/{categoryId}")
public Category getCategory(@PathVariable("categoryId") Long categoryId);
//根據(jù)skuId獲取sku信息
@GetMapping("/api/product/inner/getSkuInfo/{skuId}")
public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId);
}
3.3、開發(fā)service-search 模塊接口
1、controller
import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.search.service.SkuService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 商品搜索列表接口
*
* @author Eric
* @date 2023-06-29 10:15
*/
@RestController
@RequestMapping("api/search/sku")
public class SkuApiController {
@Autowired
private SkuService skuService;
@ApiOperation(value = "上架商品")
@GetMapping("inner/upperSku/{skuId}")
public Result upperGoods(@PathVariable("skuId") Long skuId) {
skuService.upperSku(skuId);
return Result.ok();
}
@ApiOperation(value = "下架商品")
@GetMapping("inner/lowerSku/{skuId}")
public Result lowerGoods(@PathVariable("skuId") Long skuId) {
skuService.lowerSku(skuId);
return Result.ok();
}
}
2、service接口
/**
* @author Eric
* @date 2023-06-29 10:16
*/
public interface SkuService {
/**
* 上架商品列表
* @param skuId
*/
void upperSku(Long skuId);
/**
* 下架商品列表
* @param skuId
*/
void lowerSku(Long skuId);
}
3、impl
import com.alibaba.fastjson.JSON;
import com.atguigu.ssyx.enums.SkuType;
import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.model.search.SkuEs;
import com.atguigu.ssyx.product.client.ProductFeignClient;
import com.atguigu.ssyx.search.repository.SkuRepository;
import com.atguigu.ssyx.search.service.SkuService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author Eric
* @date 2023-06-29 10:16
*/
@Slf4j
@Service
public class SkuServiceImpl implements SkuService {
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private SkuRepository skuEsRepository;
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 上架商品列表
*
* @param skuId
*/
@Override
public void upperSku(Long skuId) {
log.info("upperSku:" + skuId);
SkuEs skuEs = new SkuEs();
//查詢sku信息
SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
if (null == skuInfo) return;
// 查詢分類
Category category = productFeignClient.getCategory(skuInfo.getCategoryId());
if (category != null) {
skuEs.setCategoryId(category.getId());
skuEs.setCategoryName(category.getName());
}
skuEs.setId(skuInfo.getId());
skuEs.setKeyword(skuInfo.getSkuName() + "," + skuEs.getCategoryName());
skuEs.setWareId(skuInfo.getWareId());
skuEs.setIsNewPerson(skuInfo.getIsNewPerson());
skuEs.setImgUrl(skuInfo.getImgUrl());
skuEs.setTitle(skuInfo.getSkuName());
if (skuInfo.getSkuType() == SkuType.COMMON.getCode()) {
skuEs.setSkuType(0);
skuEs.setPrice(skuInfo.getPrice().doubleValue());
skuEs.setStock(skuInfo.getStock());
skuEs.setSale(skuInfo.getSale());
skuEs.setPerLimit(skuInfo.getPerLimit());
} else {
//TODO 待完善-秒殺商品
}
SkuEs save = skuEsRepository.save(skuEs);//往Es中新增數(shù)據(jù)
log.info("upperSku:" + JSON.toJSONString(save));
}
/**
* 下架商品列表
*
* @param skuId
*/
@Override
public void lowerSku(Long skuId) {
this.skuEsRepository.deleteById(skuId);//刪除Es中的數(shù)據(jù)
}
}
4、創(chuàng)建SkuRepository(用來操作Es,這里使用的是SpringData技術(shù))
/**
* @author Eric
* @date 2023-06-29 10:19
*/
// 參數(shù)一:泛型 參數(shù)二:類型,是由泛型中的主鍵類型而決定的
public interface SkuRepository extends ElasticsearchRepository<SkuEs, Long> {
}
4、RabbitMQ
1、創(chuàng)建mq模塊
因為mq的作用類似于工具類,并且多處需要使用,所以我這里選擇放在common模塊下
2、引入MQ依賴
<dependencies>
<!--rabbitmq消息隊列-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
</dependencies>
3、添加service方法
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author Eric
* @date 2023-06-30 22:57
*/
@Service
public class RabbitService {
// 引入操作rabbitmq 的模板
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 發(fā)送消息
*
* @param exchange 交換機
* @param routingKey 路由鍵(路由key)
* @param message 消息
* @return
*/
public boolean sendMessage(String exchange, String routingKey, Object message) {
// 調(diào)用發(fā)送數(shù)據(jù)的方法
rabbitTemplate.convertAndSend(exchange, routingKey, message);
return true;
}
/**
* 發(fā)送延遲消息的方法
*
* @param exchange 交換機
* @param routingKey 路由鍵
* @param message 消息內(nèi)容
* @param delayTime 延遲時間
* @return
*/
public boolean sendDelayMessage(String exchange, String routingKey, Object message, int delayTime) {
// 在發(fā)送消息的時候設(shè)置延遲時間
rabbitTemplate.convertAndSend(exchange, routingKey, message, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
// 設(shè)置一個延遲時間
message.getMessageProperties().setDelay(delayTime * 1000);
return message;
}
});
return true;
}
}
4、配置mq消息轉(zhuǎn)換器
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mq消息轉(zhuǎn)換器(默認是字符串轉(zhuǎn)換器)
*/
@Configuration
public class MQConfig {
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
}
5、添加消息的確認配置(我這里配置的是手動確認模式)
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class MQProducerAckConfig implements RabbitTemplate.ReturnCallback,RabbitTemplate.ConfirmCallback {
// 我們發(fā)送消息使用的是 private RabbitTemplate rabbitTemplate; 對象
// 如果不做設(shè)置的話 當前的rabbitTemplate 與當前的配置類沒有任何關(guān)系!
@Autowired
private RabbitTemplate rabbitTemplate;
// 設(shè)置 表示修飾一個非靜態(tài)的void方法,在服務(wù)器加載Servlet的時候運行。并且只執(zhí)行一次!
@PostConstruct
public void init(){
rabbitTemplate.setReturnCallback(this);
rabbitTemplate.setConfirmCallback(this);
}
/**
* 表示消息是否正確發(fā)送到了交換機上
* @param correlationData 消息的載體
* @param ack 判斷是否發(fā)送到交換機上
* @param cause 原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
System.out.println("消息發(fā)送成功!");
}else {
System.out.println("消息發(fā)送失??!"+cause);
}
}
/**
* 消息如果沒有正確發(fā)送到隊列中,則會走這個方法!如果消息被正常處理,則這個方法不會走!
* @param message
* @param replyCode
* @param replyText
* @param exchange
* @param routingKey
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("消息主體: " + new String(message.getBody()));
System.out.println("應(yīng)答碼: " + replyCode);
System.out.println("描述:" + replyText);
System.out.println("消息使用的交換器 exchange : " + exchange);
System.out.println("消息使用的路由鍵 routing : " + routingKey);
}
}
6、RabbitMQ常量類
/**
**自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過,也去過華為、OPPO等大廠,18年進入阿里一直到現(xiàn)在。**
**深知大多數(shù)大數(shù)據(jù)工程師,想要提升技能,往往是自己摸索成長或者是報班學習,但對于培訓機構(gòu)動則幾千的學費,著實壓力不小。自己不成體系的自學效果低效又漫長,而且極易碰到天花板技術(shù)停滯不前!**
**因此收集整理了一份《2024年大數(shù)據(jù)全套學習資料》,初衷也很簡單,就是希望能夠幫助到想自學提升又不知道該從何學起的朋友。**
**既有適合小白學習的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗的小伙伴深入學習提升的進階課程,基本涵蓋了95%以上大數(shù)據(jù)開發(fā)知識點,真正體系化!**
**由于文件比較大,這里只是將部分目錄大綱截圖出來,每個節(jié)點里面都包含大廠面經(jīng)、學習筆記、源碼講義、實戰(zhàn)項目、講解視頻,并且后續(xù)會持續(xù)更新**
**如果你覺得這些內(nèi)容對你有幫助,可以添加VX:vip204888 (備注大數(shù)據(jù)獲?。?*
量類
/**
自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過,也去過華為、OPPO等大廠,18年進入阿里一直到現(xiàn)在。
深知大多數(shù)大數(shù)據(jù)工程師,想要提升技能,往往是自己摸索成長或者是報班學習,但對于培訓機構(gòu)動則幾千的學費,著實壓力不小。自己不成體系的自學效果低效又漫長,而且極易碰到天花板技術(shù)停滯不前!
因此收集整理了一份《2024年大數(shù)據(jù)全套學習資料》,初衷也很簡單,就是希望能夠幫助到想自學提升又不知道該從何學起的朋友。
[外鏈圖片轉(zhuǎn)存中…(img-J2uRuOZu-1712862333773)]
[外鏈圖片轉(zhuǎn)存中…(img-H15zZfNh-1712862333774)]
[外鏈圖片轉(zhuǎn)存中…(img-UmW61keA-1712862333774)]
[外鏈圖片轉(zhuǎn)存中…(img-QJXCyMLO-1712862333774)]
[外鏈圖片轉(zhuǎn)存中…(img-iKPM94eN-1712862333774)]
既有適合小白學習的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗的小伙伴深入學習提升的進階課程,基本涵蓋了95%以上大數(shù)據(jù)開發(fā)知識點,真正體系化!
由于文件比較大,這里只是將部分目錄大綱截圖出來,每個節(jié)點里面都包含大廠面經(jīng)、學習筆記、源碼講義、實戰(zhàn)項目、講解視頻,并且后續(xù)會持續(xù)更新文章來源:http://www.zghlxwxcb.cn/news/detail-854205.html
如果你覺得這些內(nèi)容對你有幫助,可以添加VX:vip204888 (備注大數(shù)據(jù)獲?。?/strong>
[外鏈圖片轉(zhuǎn)存中…(img-eBFCxzdx-1712862333775)]文章來源地址http://www.zghlxwxcb.cn/news/detail-854205.html
到了這里,關(guān)于SpringBoot實戰(zhàn)項目整合RabbitMQ+ElaticSearch實現(xiàn)SKU上下架功能_尚上優(yōu)選整合es+mq實現(xiàn)商品上下架(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!