ES 对应 mysql
索引Index 对应 insert
类型Type 对应 不同表
一条条的记录称为文档Document 对应 一条条的记录称为记录
概念理解:数据库=》数据表=》数据=》属性(列名)
GET/_cat/nodes:查看所有节点
GET/_cat/health:查看es 健康状况
GET/_cat/master:查看主节点
GET/_cat/indices:查看所有索引,等价于数据库的show databases;
总结:用于查看elasticsearch的基本信息
保存一个数据,保存在哪个索引的哪个类型下,指定用哪个唯一标识
PUT customer/external/1;在 customer索引下的 external类型下保存1号数据为
PUT customer/external/1
语法
{
“name”: “John Doe”
}
PUT和 POST都可以,
POST 新增。如果不指定id,会自动生成id。指定id就会修改这个数据,并新增版本号
PUT可以新增可以修改。PUT必须指定id;由于PUT需要指定id,我们一般都用来做修改操作,不指定id会报错。
3.2.1 put操作
_get查询数据&乐观锁字段
3.3.1 查询数据信息
3.3.2 A B同时操作更新
B更新name为 “name”: “red”
修改seq_no值重新操作更新,更新成功,seq_no会发生变化
注:primary_term代表机器重启,重启会发生变化
总结:A B修改同一条记录,假设版本号1,A改了-版本号更新2,B再更改则失败(版本号发生变化了)=》此操作过程请求需要携带看到的数据记录版本号
注意:_update更新,会对比原来数据,若没有新数据,version和seq_no不会发生变化,即什么都不变。修改的数据发生变化才会更新版本号等其他值。
案列:
注:_score代表查询命中得分
全文检索按照评分进行排序,会对检索条件进行分词匹配。
1.搜索address中包含mill的所有人的年龄分布以及平均年龄及平均薪资
2.按照年龄聚合,并且请求这些年龄段的这些人的平均薪资
3.查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资以及这个年龄段的总体平均薪资
1Mb=1024kb
1G=1024Mb
100万个请求:32kb*1000000=32000mb=32G =>网络阻塞
PUT product
{
"mappings":{
"properties": {
"skuId":{
"type":"long"
},
"spuId":{
"type":"keyword"
},
"skuTitle":{
"type":"text",
"analyzer": "ik_smart"
},
"skuPrice":{
"type":"keyword"
},
"skuImg":{
"type":"keyword",
"index":false,
"doc_values":false
},
"saleCount":{
"type":"long"
},
"hasStock":{
"type":"boolean"
},
"hasScore":{
"type":"long"
},
"brandId":{
"type":"long"
},
"catalogId":{
"type":"long"
},
"brandName":{
"type":"keyword",
"index":false,
"doc_values":false
},
"brandImg":{
"type":"keyword",
"index":false,
"doc_values":false
},
"catalogName":{
"type":"keyword",
"index":false,
"doc_values":false
},
"attrs":{
"type":"nested",
"properties": {
"attrId":{
"type":"long"
},
"attrName":{
"type":"keyword",
"index":false,
"doc_values":false
},
"attrValue":{
"type":"keyword"
}
}
}
}
}
}
GET /product/_search
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
spring:
thymeleaf:
cache: false
注:SpringBoot,访问项目的时候,默认会找index
WebMvcAutoConfiguration.class
2.1.1 虚拟机地址映射到mall.com
2.1.2 mall.com:9200 可以访问到es等
注:即域名映射ip
mall.com文件名需改为mall.conf
配置完重启下nginx: docker restart nginx
2.2.3 文件名写错修改
2.2.4 访问mall.com
2.2.5 流程描述
官网下载:https://jmeter.apache.org/download_jmeter.cgi
注:循环次数,若为10,则代表每个线程发10次请求,若线程数为200,则总共发2000个请求
测试效果:注意吞吐量
吞吐量:每秒钟系统能够处理的请求数、任务数。
设置回收端口时间
更多参考windows帮助文档
退出注册表,重启计算机才生效
CPU密集型:如查询出数据,然后进行处理,排序,筛选等
IO密集型:如从redis缓存读取数据,读文件等
注:假设进行100次 MinorGc花费1s。但10次Full Gc就要花费1s,非常慢(慢将近10倍),尽量减少Full Gc的次数。
1.打开 https://visualvm.github.io/pluginscenters.html
2.查看jdk版本
3.使用https://visualvm.github.io/archive/uc/8u40/updates.xml.gz
3)、安装GC插件-查看完整GC过程
重启jvisualvm
将静态资源上传到linux下的nginx/html/static下
注:root用于配置资源路径
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-redisartifactId>
dependency>
项目其他模块使用了2.2.4.RELEASE,这里使用下面配置
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.2.4.RELEASEversion>
dependency>
RedisAutoConfiguration.class=>StringRedisTemplate
redis:
host: xxx.xxx.xxx.xxx
port: 6379
passsword:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.2.4.RELEASEversion>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.1.0version>
dependency>
注:
=>使用lettuce、jedis操作redis的底层客户端。Spring再次封装redisTemplate
=>即无论使用ettuce还是jedis,都可以使用redisTemplate操作redis
/**
* 1、空结果缓存:解决缓存穿透
* 2、设置过期时间(加随机值):解决缓存雪崩
* 3、加锁:解决缓存击穿
*/
启动多个服务:可以复制10001,10002,10003启动多个服务
进行压测
<dependency>
<groupId>org.redissongroupId>
<artifactId>redisson-spring-boot-starterartifactId>
<version>3.13.5version>
dependency>
官方文档:
可参考:https://blog.csdn.net/liu320yj/article/details/109026854
@Configuration
public class MyRedissonConfig {
/**
* 所有对Redisson的使用都是通过RedissonClient对象
* @return
* @throws IOException
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson() throws IOException {
//1、创建配置
Config config = new Config();
config.useSingleServer().setAddress("redis://xxx.xxx.xxx.xxx:6379");
//2、根据Config创建出RedissonClient示例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
https://docs.spring.io/spring-framework/docs/5.2.19.RELEASE/spring-framework-reference/integration.html#cache-jsr-107
https://docs.spring.io/spring-framework/docs/5.2.19.RELEASE/spring-framework-reference/integration.html#cache-spel-context
org.springframework.cache.CacheManager
org.springframework.cache.Cache
spring-boot-starter-cache、spring-boot-starter-data-redis
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.2.4.RELEASEversion>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.1.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
注意
CacheConfigurations.getConfigurationClass(types[i])
Class> configurationClass = MAPPINGS.get(cacheType);
static {
Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
MAPPINGS = Collections.unmodifiableMap(mappings);
}
1)、自动配置了哪些
CacheAutoConfiguration会导入RedisCacheConfiguration;
自动配好了缓存管理器RedisCacheManager
@Cacheable: Triggers cache popuLation.触发将数据保存到缓存的操作
@cacheEvict:Triggers cache eviction.触发将数据从缓存删除的操作
@CachePut: Updatcs the cache without intcrfcring with the mcthod execution.不影响方法执行更新缓存
@Caching: Regroups multiple cache operations to be applied on a method.组合以上多个操作
@CacheConfig: Shares some common cache-rclated settings at class-level.在类级别共享缓存的相同配置
3.1)、开启缓存功能:@EnableCaching
3.2)、只需要注解就能完成缓存操作
调用方法
指定缓存的数据存活时间
指定缓存使用的key
数据保存为json格式
#application.properties
spring.cache.type=redis
spring.cache.redis.time-to-live=3600000
spring.cache.redis.use-key-prefix=true
#如果指定了前缀就用我们指定的前缀,如果没有就默认使用缓存的名字作为前缀
#spring.cache.redis.key-prefix=CACHE
#是否缓存控制:解决缓存穿透
spring.cache.redis.cache-null-values=true
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class MyCacheConfig {
/*@Autowired
CacheProperties cacheProperties;*/
/**
* 配置文件中的东西没有用上:
* 1、原来和配置文件绑定的配置类是这样子的
* @ConfigurationProperties(prefix = "spring.cache")
* public class CacheProperties
* 2、要让他生效
* @EnableConfigurationProperties(CacheProperties.class)
* @return
*/
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
//config = config.entryTtl();
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
//将配置文件中的所有配置都生效
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
@CacheEvict : 触发将数据从缓存删除的操作
@Caching: 组合多种缓存操作
@CachePut:双写模式,用于更新缓存
配置nginx:把search.mall.com也转发到网关
网关根据$host负载均衡到gulimall-search
跳转到搜索页面
search.mall.com/list.html?catalog3ld=225&keyword=华为&brandld=1&brandld=2&attrs=1_5.56以上&attrs=2_白色:蓝色
GET product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "华为"
}
}
],
"filter": [
{
"term": {
"catalogId": "225"
}
},
{
"terms": {
"brandId": [
"1",
"2",
"9"
]
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "1"
}
}
},
{
"terms": {
"attrs.attrValue": [
"aaa",
"bbb"
]
}
}
]
}
}
}
},
{
"range": {
"skuPrice": {
"gte": 0,
"lte": 9000
}
}
}
]
}
},
"sort": [
{
"skuPrice": {
"order": "desc"
}
}
],
"from":0,
"size":2,
"highlight":{
"fields": {"skuTitle":{}},
"pre_tags": "<b style='color:red'>",
"post_tags": "b>"
}
}
模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析
1.修改映射
先查看原映射:GET product/_mapping
{
"product" : {
"mappings" : {
"properties" : {
"attrs" : {
"type" : "nested",
"properties" : {
"attrId" : {
"type" : "long"
},
"attrName" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
},
"attrValue" : {
"type" : "keyword"
}
}
},
"brandId" : {
"type" : "long"
},
"brandImg" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
},
"brandName" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
},
"catalogId" : {
"type" : "long"
},
"catalogName" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
},
"hasScore" : {
"type" : "long"
},
"hasStock" : {
"type" : "boolean"
},
"hotScore" : {
"type" : "long"
},
"saleCount" : {
"type" : "long"
},
"skuId" : {
"type" : "long"
},
"skuImg" : {
"type" : "keyword",
"index" : false,
"doc_values" : false
},
"skuPrice" : {
"type" : "keyword"
},
"skuTitle" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"spuId" : {
"type" : "keyword"
}
}
}
}
}
修改映射
PUT mall_product
{
"mappings" : {
"properties" : {
"attrs" : {
"type" : "nested",
"properties" : {
"attrId" : {
"type" : "long"
},
"attrName" : {
"type" : "keyword"
},
"attrValue" : {
"type" : "keyword"
}
}
},
"brandId" : {
"type" : "long"
},
"brandImg" : {
"type" : "keyword"
},
"brandName" : {
"type" : "keyword"
},
"catalogId" : {
"type" : "long"
},
"catalogName" : {
"type" : "keyword"
},
"hasScore" : {
"type" : "long"
},
"hasStock" : {
"type" : "boolean"
},
"hotScore" : {
"type" : "long"
},
"saleCount" : {
"type" : "long"
},
"skuId" : {
"type" : "long"
},
"skuImg" : {
"type" : "keyword"
},
"skuPrice" : {
"type" : "keyword"
},
"skuTitle" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"spuId" : {
"type" : "keyword"
}
}
}
}
查看映射信息:GET mall_product/_mapping
查看数据:GET mall_product/_search
进行数据迁移
#迁移数据
POST _reindex
{
"source":{
"index":"product"
},
"dest": {
"index":"mall_product"
}
}
#如果是嵌入式的属性,查询,聚合,分析都应该用嵌入式的
#检索
GET mall_product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"skuTitle": "华为"
}
}
],
"filter": [
{
"term": {
"catalogId": "225"
}
},
{
"terms": {
"brandId": [
"1",
"2",
"9"
]
}
},
{
"nested": {
"path": "attrs",
"query": {
"bool": {
"must": [
{
"term": {
"attrs.attrId": {
"value": "1"
}
}
},
{
"terms": {
"attrs.attrValue": [
"aaa",
"bbb"
]
}
}
]
}
}
}
},
{
"range": {
"skuPrice": {
"gte": 0,
"lte": 9000
}
}
}
]
}
},
"sort": [
{
"skuPrice": {
"order": "desc"
}
}
],
"from":0,
"size":2,
"highlight":{
"fields": {"skuTitle":{}},
"pre_tags": "",
"post_tags": ""
},
"aggs":{
"brand_agg":{
"terms": {
"field":"brandId",
"size":10
},
"aggs": {
"brand_name_agg": {
"terms": {
"field": "brandName",
"size": 10
}
},
"brand_img_agg":{
"terms": {
"field": "brandImg",
"size": 10
}
}
}
},
"catalog_agg":{
"terms": {
"field": "catalogId",
"size": 10
},
"aggs": {
"catalog_name_agg": {
"terms": {
"field": "catalogName",
"size": 10
}
}
}
},
"attr_agg":{
"nested": {
"path": "attrs"
},
"aggs":{
"attr_id_agg":{
"terms": {
"field":"attrs.attrId",
"size":10
},
"aggs":{
"attr_name_agg":{
"terms": {
"field": "attrs.attrName",
"size": 10
}
},
"attr_value_agg":{
"terms": {
"field": "attrs.attrValue",
"size": 10
}
}
}
}
}
}
}
}
whenComplete 不用开启新线程
whenCompleteAsyn 需要开启新线程
exceptionally 可以用来进行异常处理
thenRunAsync
thenAcceptAsync
thenApplyAsync
runAfterBothAsync:不能感知到前两个的结果
thenAcceptBothAsync:能感知到前两个的结果
thenCombineAsync:能处理到前两个的结果,有返回值
runAfterEitherAsync:不感知结果,自己没有返回值
acceptEitherAsync:感知结果,自己没有返回值
applyToEitherAsync:处理结果,有返回值
allof:所有任务必须完成
anyOf:只要有一个任务完成
参考:https://blog.csdn.net/qq_44035485/article/details/121174570
<dependency>
<groupId>com.aliyungroupId>
<artifactId>tea-openapiartifactId>
<version>0.0.19version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>dysmsapi20170525artifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<version>2.2.4.RELEASEversion>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.1.0version>
dependency>
String s = DigestUtils.md5Hex(“123456”);
不可逆(使用彩虹表可以破解),MD5不能直接进行密码的加密存储
String s = Md5Crypt.md5Crypt(“123456”.getBytes(), “abc”);
盐值使用特别方式
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode("123456");
注:
1.使用code(有效期内可换取)换取AccessToken,Code只能使用一次
2.同一个用户的accessToken一段时间是不会变化的,即使多次获取。
spring session共享不能解决以下问题
配置hosts文件
问题client1.com可以登录了但
client2.com:8081//employees还是不能登录
复制一份client服务作为client2,修改端口为8082
direct、fanout、topic
fanout:广播类型
topic:主题类型(重点)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
spring.rabbitmq.host=192.168.136.129
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/
@Autowired
AmqpAdmin amqpAdmin;
/**
* 1、如何创建Exchange[hello.java.exchange]、Queue、Binding
* 1)、使用AmqpAdmin进行创建
*/
@Test
void createExchange() {
//DirectExchange(String name, boolean durable, boolean autoDelete, Map arguments)
//durable:是否持久化
DirectExchange directExchange = new DirectExchange("hello-java-exchange",true,false);
amqpAdmin.declareExchange(directExchange);
log.info("exchange:[{}]创建成功","hello-java-exchange");
}
@Test
void createQueue() {
//exclusive:是否排他的,false:允许同时有多个连接到此queue
Queue queue = new Queue("hello-java-queue",true,false,false);
amqpAdmin.declareQueue(queue);
log.info("queue:[{}]创建成功","hello-java-queue");
}
@Test
void createBinding(){
// String destination【目的地】,
// DestinationType destinationType 【目的地类型】
// String exchange【交换机】,
// String routingKey【路由键】,
// Map arguments【自定义参数】
// 将exchange指定的交换机和destination目的地进行绑定,使用routingKey作为指定的路由键
Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE,
"hello-java-exchange",
"hello.java",null);
amqpAdmin.declareBinding(binding);
log.info("binding:[{}]创建成功","hello-java-binding");
}
@Test
void sendMessageTests() {
//1、发送消息,如果发送的消息是个对象,我们会使用序列化机制,将对象写出去。对象必须实现Serializable
String msg = "Hello World";
OrderReturnReasonEntity entity = new OrderReturnReasonEntity();
entity.setId(1L);
entity.setCreateTime(new Date());
//2、发送的对象类型的消息,可以是一个json
for (int i = 0;i<10;i++) {
if(i%2==0) {
entity.setName("Vc" + i);
rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", entity);
}else {
OrderEntity orderEntity = new OrderEntity();
orderEntity.setOrderSn(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("hello-java-exchange","hello.java",orderEntity);
}
log.info("消息发送完成{}" + entity);
}
}
@RabbitListener(queues = {"hello-java-queue"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService
/**
* queue:声明需要监听的所有队列
*
* org.springframework.amqp.core.Message
*
* 参考可以写一下类型
* 1、Message message:原生消息详细信息。头+体
* 2、T<发送的消息的类型> OrderReturnReasonEntity content
* 3、Channel channel: 当前传输数据的通道
*
* Queue: 可以很多人都来监听。只要收到消息,队列删除消息,而且只能有一个收到此消息
* 场景:
* 1)、订单服务启动多个;同一个消息,只能有一个客户端收到
* 2)、只有一个消息完全处理完,方法运行结束,我们就可以接收到下一个消息
* @param message
*/
//RabbitListener(queues = {"hello-java-queue"})
@RabbitHandler
public void recieveMessage(Message message,
OrderReturnReasonEntity content,
Channel channel) throws InterruptedException {
//{"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1652100907404}
System.out.println("接收到消息..."+content);
byte[] body = message.getBody();
//消息头属性信息
MessageProperties properties = message.getMessageProperties();
Thread.sleep(3000);
System.out.println("消息处理完成=》"+content.getName());
}
@RabbitHandler
public void recieveMessage2(OrderEntity content) {
//{"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1652100907404}
System.out.println("接收到消息..."+content);
}
}
注:springboot.rabbitmq.publisher-confirm 已被弃用.
#新版使用
#开启发送端确认
spring.rabbitmq.publisher-confirm-type=correlated
参考https://blog.csdn.net/z69183787/article/details/109371628
#开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列,以异步发送优先回调我们这个return
spring.rabbitmq.template.mandatory=true
#手动ack消息
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@RabbitHandler
public void recieveMessage(Message message,
OrderReturnReasonEntity content,
Channel channel) throws InterruptedException {
//{"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1652100907404}
System.out.println("接收到消息..."+content);
byte[] body = message.getBody();
//消息头属性信息
MessageProperties properties = message.getMessageProperties();
Thread.sleep(3000);
System.out.println("消息处理完成=》"+content.getName());
//Channel内按顺序自增
long deliveryTag = message.getMessageProperties().getDeliveryTag();
//签收货物,非批量模式
try {
if(deliveryTag%2==0){
//收货
channel.basicAck(deliveryTag,false);
System.out.println("签收了货物..."+deliveryTag);
}else {
//退货 requeue=false 丢弃 requeue=true 发回服务器,服务器重新入队
//deliveryTag, multiple, requeue(false不再入队)
channel.basicNack(deliveryTag,false,false);
System.out.println("没有签收了货物..."+deliveryTag);
}
} catch (Exception e) {
//网络中断
}
}
@Configuration
public class GulimallFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
//1、RequestContextHolder拿到刚进来的这个请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest(); //旧请求
//同步请求头数据,Cookie、
String cookie = request.getHeader("Cookie");
//给新请求同步了旧请求的cookie
template.header("Cookie",cookie);
}
};
}
}
@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
OrderConfirmVo confirmVo = new OrderConfirmVo();
MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
System.out.println("主线程..."+Thread.currentThread().getId());
//获取之前的请求
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
//1、远程查询所有的收货地址列表
System.out.println("member线程..."+Thread.currentThread().getId());
//每一个线程都来共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);
List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
confirmVo.setAddress(address);
}, executor);
CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
//2、远程查询购物车所有选中的购物项
System.out.println("cart线程..."+Thread.currentThread().getId());
//每一个线程都来共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);
List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
confirmVo.setItems(items);
//feign在远程调用之前要构造请求,调用很多的拦截器
//RequestInterceptor interceptor : requestInterceptors
}, executor);
//3、查询用户积分
Integer integration = memberRespVo.getIntegration();
confirmVo.setIntegration(integration);
//4、其他数据自动计算
//TODO 5、防重令牌
CompletableFuture.allOf(getAddressFuture,cartFuture).get();
return confirmVo;
}
一、什么是接口幂等性
二、哪些情况需要防止
三、什么情况需要幂等
四、幂等解决方案
1.token机制
2.各种锁机制
3.各种唯一约束
4.防重表
5.全局请求唯一id
1.事务的基本性质
2.事务的隔离级别
3.事务的传播行为
伪代码
同理,c发送生异常,c回滚。b,c不回滚。
注:应该在不同service,直接调用同个类里的事务方法会导致事务失效
可提供的解决方案:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
主类开启
@EnableAspectJAutoProxy(exposeProxy = true) //开启了aspect动态代理模式,对外暴露代理对象
一、CAP定理
raft动画演示:raft.github.io
采用该方案
注:根据业务场景选择合适方案
文档:https://seata.io/zh-cn/docs/overview/what-is-seata.html
wms、ums、sms、pms、oms、admin数据库
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
从 https://github.com/seata/seata/releases,下载服务器软件包,将其解压缩。
Usage: sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for windows) [options]
Options:
--host, -h
The address is expose to registration center and other service can access seata-server via this ip
Default: 0.0.0.0
--port, -p
The port to listen.
Default: 8091
--storeMode, -m
log store mode : file、db
Default: file
--help
e.g.
sh seata-server.sh -p 8091 -h 127.0.0.1 -m file
使用下面方案:
WareOrderTaskDetailEntity添加wareId,lockStatus字段
CREATE TABLE `mq_message` (
`message_id` char(32) NOT NULL,
`content` text,
`to_exchane` varchar(255) DEFAULT NULL,
`routing_key` varchar(255) DEFAULT NULL,
`class_type` varchar(255) DEFAULT NULL,
`message_status` int(1) DEFAULT '0' COMMENT '0-新建 1-已发送 2-错误抵达 3-已抵达',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1.进入“蚂蚁金服开放平台”
https://open.alipay.com/platform/home.htm
2.创建应用,进行配置测试
测试环境使用沙箱环境:https://opendocs.alipay.com/open/02np8i
具体流程根据官方文档
可以使用在线cron表达式生成器
通过空格区分
注:Spring不支持年
看官网文档
github: https://github.com/alibaba/Sentinel
https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
从 release 页面 下载最新版本的控制台 jar 包: https://github.com/alibaba/Sentinel/releases