20230626学习记录

1. 20230626学习记录

目标

  • RabbitMQ确认和回退消息机制
  • 死信队列
  • Elasticsearch的安装配置
  • kibana的安装配置
  • IK分词器的安装配置
  • springboot整合ES的配置和使用

昨天学习了rabbitmq其中的五个模式,测试了五个模式下的生产消费。

那如何确认生产者发送的信息被交换机收到了,或者交换机成功转发到队列了

1.1. 生产端发送确认

  1. 配置开启发送确认模式和回退消息

    确认模式:能够用来确认生产者是否成功发送信息到交换机了

    回退消息:能够用来确认信息是否被交换机成功转发到队列了

publisher-confirm-type: correlated
publisher-returns: true
  1. 使用回调消息接口

RabbitMQTemplate.ConfirmCallBack接口有个回调方法confirm
confirm(CorrelationData correlationData, boolean ack, String cause)

correlationData: 消息相关数据
ack: 交换机是否收到信息
cause: 未收到原因

RabbitMQTemplate可以设置内部属性 ConfirmCallback,使用匿名内部类的方式重写回调方法confirm来获取参数信息(函数式接口,可以使用lambda)

@Override
public void sendCode(String msg) {
    rabbitTemplate.setConfirmCallback((c,ack,m) -> {
        if(ack){
            log.debug("交换机成功收到了信息 ack:{}",ack);
        }else {
            log.debug("交换机未收到消息:{}",c);
            log.debug("原因:{}",m);
        }
    });
    
    rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_TOPIC_EXCHANGE,"routingKey",msg);
}
  1. 使用回退信息接口

RabbitTemplate.ReturnsCallback接口有个回调方法returnedMessage
returnedMessage(ReturnedMessage msg)

当交换机无法正常投递时,会触发消息回退的回调方法

@Override
public void sendCode(String msg) {
    rabbitTemplate.setReturnsCallback(rMsg -> {
        log.debug("消息: {} 被退回,退回原因:{},退回交换机:{},发送的路由键:{}",
                  new String(rMsg.getMessage().getBody()),rMsg.getReplyText(),rMsg.getExchange(),rMsg.getRoutingKey());
    });
    
    rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_TOPIC_EXCHANGE,"routing",msg);
}

//消息: 1234 退回,退回原因:NO_ROUTE,退回交换机:mq_topic_exchange,发送的路由键:routing

1.2. 消费端手动确认

默认自动确认的。

1.2.1. 开启配置

listener:
  simple:
    acknowledge-mode: manual #手动确认
    default-requeue-rejected: false #是否将被拒绝的消息重新放入队列

1.2.2. 消费端处理

消费端通过参数Channel

channel.basicAck(long deliveryTag, boolean multiple); //确认消息
channel.basicNock(long deliveryTag, boolean multiple, boolean requeue); //拒收消息

long deliveryTag:当前获取的消息在队列中的索引,通过消费者方法参数Message可以获得

boolean multiple:是否 批量确认或拒绝 (批量指的是 一次性确认或拒绝 所有小于当前deliveryTag的消息)

boolean requeue:被拒绝的消息是否重新放入队列

@RabbitListener(queues = RabbitMqConstants.MQ_DELAY_QUEUE)
public void delayConsumer(String msg, Message message, Channel channel) throws IOException {
    log.debug("时间:{},延迟获取信息:{}",new Date(),msg);
    channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}

@RabbitListener(queues = RabbitMqConstants.MQ_DELAY_QUEUE)
public void delayConsumer(String msg, Message message, Channel channel) throws IOException {
    log.debug("时间:{},延迟获取信息:{}",new Date(),msg);
	channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
}

1.3. 死信队列

死信概念

  1. 被消费端拒绝的消息
  2. TTL到期的消息
  3. 队列满了无法放入的消息

当消息变为死信后,转发到另一个队列处理,这个队列就称为死信队列

1.3.1. 死信队列的创建和使用

参考:http://t.csdn.cn/2ARpx

1.3.1.1. 创建死信队列、交换机,定义绑定关系

和创建普通的队列一模一样

@Bean("delayQueue")
public Queue delayQueue(){
    return new Queue(RabbitMqConstants.MQ_DELAY_QUEUE,
                     RabbitMqConstants.DURABLE,
                     RabbitMqConstants.EXCLUSIVE,
                     RabbitMqConstants.AUTODELETE);
}
@Bean("delayExchange")
public DirectExchange delayExchange(){
    return new DirectExchange(RabbitMqConstants.MQ_DELAY_EXCHANGE,
                              RabbitMqConstants.DURABLE,
                              RabbitMqConstants.AUTODELETE);
}
@Bean
public Binding delayBinding(@Qualifier("delayQueue") Queue queue,@Qualifier("delayExchange") DirectExchange exchange){
    return BindingBuilder.bind(queue)
        .to(exchange)
        .with(RabbitMqConstants.MQ_DELAY_ROUTING_KEY);
}
1.3.1.2. 创建普通队列和交换机

创建队列时需要配置 转发给哪个死信队列交换机,以及交换机的routingKey

@Bean("normalQueue")
public Queue normalQueue(){
    Map<String, Object> args = new HashMap<String, Object>();
    //声明当前队列绑定的死信交换机
    args.put("x-dead-letter-exchange", RabbitMqConstants.MQ_DELAY_EXCHANGE);
    //声明当前队列的死信路由key
    args.put("x-dead-letter-routing-key", RabbitMqConstants.MQ_DELAY_ROUTING_KEY);
    
    return new Queue(RabbitMqConstants.MQ_NORMAL_QUEUE,
            RabbitMqConstants.DURABLE,
            RabbitMqConstants.EXCLUSIVE,
            RabbitMqConstants.AUTODELETE,
            args);//添加死信交换机信息参数
}
@Bean("normalExchange")
public DirectExchange normalExchange(){
    return new DirectExchange(RabbitMqConstants.MQ_NORMAL_EXCHANGE,
            RabbitMqConstants.DURABLE,
            RabbitMqConstants.AUTODELETE);
}
@Bean
public Binding normalBinging(@Qualifier("normalQueue") Queue queue,@Qualifier("normalExchange") DirectExchange exchange){
    return BindingBuilder.bind(queue)
            .to(exchange)
            .with(RabbitMqConstants.MQ_NORMAL_ROUTING_KEY);
}
1.3.1.3. 模拟消息过期

设置消息TTL 10s,不创建普通队列的消费者模拟消息未被消费,创建监听死信队列的消费者,10s消息过期后,死信队列消费者处理消息。

  1. 生产者

    发送方法的参数有一个消息后处理对象MessagePostProcessor,是一个函数式接口,接口中的postProcessMessage方法参数Message对象,可以设置消息相关信息

    setExpiration()设置过期时间,参数为String 单位毫秒

rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_NORMAL_EXCHANGE,
        RabbitMqConstants.MQ_NORMAL_ROUTING_KEY,
        msg,
        message -> {
            message.getMessageProperties().setExpiration("10000");//设置消息存活时间为10秒
            return message;
        }
);
  1. 消费者

    正常监听,监听死信队列

@RabbitListener(queues = RabbitMqConstants.MQ_DELAY_QUEUE)
public void delayConsumer(String msg){
    log.debug("时间:{},延迟获取信息:{}",new Date(),msg);
}

1.3.2. 利用死信队列实现延迟队列

上面的例子可以看出,死信队列10s后才收到消息,就有延迟的效果

延迟队列使用场景,例如:

1.订单在十分钟之内未支付则自动取消

2.新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。

3.预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议等

1.4. ElasticSearch

Elasticsearch 是一个分布式的、开源的搜索分析引擎,支持各种数据类型,包括文本、数字、地理、结构化、非结构化。

1.4.1. 拉取镜像

docker pull docker pull elasticsearch:7.17.7

1.4.2. 创建目录

  1. 在usr/local/software 下创建 ,并修改权限
mkdir -p elasticsearch/data
mkdir -p elasticsearch/config
mkdir -p elasticsearch/plugins

chmod 777 elasticsearch/**
  1. 在config文件夹下创建elasticsearch.yml文件,并修改其为可读写执行权限。
touch elasticsearch.yml
chmod 777 elasticsearch.yml 

文件中添加

http:
  host: 0.0.0.0
  cors:
    enabled: true
    allow-origin: "*"
xpack:
  security:
    enabled: false

1.4.3. 配置centos环境

调整max_map_count

sysctl -w vm.max_map_count=262144

1.4.4. 创建运行容器

docker run  -itd \
--name es \
--privileged=true \
-p 9200:9200 \
-p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms1g -Xmx1g" \
-v /usr/local/software/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /usr/local/software/elasticsearch/data:/usr/share/elasticsearch/data \
-v /usr/local/software/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
--net mynetwork --ip 172.18.0.9 \
--restart=always \
elasticsearch:7.17.7

1.4.5. 进入容器,修改ES内存大小

docker exec -it es bash

进入/usr/share/elasticsearch/config 目录

echo "-Xms4g"  >> jvm.options
echo "-Xmx4g"  >> jvm.options

1.4.6. 退出、重启容器

docker restart es

1.4.7. 防火墙开放9200,9300端口

firewall-cmd --zone=public --add-port=9200/tcp --permanent
firewall-cmd --zone=public --add-port=9300/tcp --permanent
firewall-cmd --reload 

1.4.8. 浏览器测试

9200端口,如图成功

image-20230626144208087

1.5. Kibana

Kibana是一个开源的分析与可视化平台,设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。

1.5.1. 拉取kibana镜像

docker pull kibana:7.17.7

1.5.2. 创建、运行容器

注意 ELASTICSEARCH_HOSTS=http://192.168.100.100:9200 填写es的端口

docker run -itd --name kibana -e "ELASTICSEARCH_HOSTS=http://192.168.100.100:9200" -p 5601:5601 --net mynetwork --ip 172.18.0.10 \
--restart=always \
kibana:7.17.7

1.5.3. 放开端口

firewall-cmd --zone=public --add-port=5601/tcp --permanent 
firewall-cmd --reload 

1.5.4. 浏览器测试

192.168.100.100:5601

image-20230626145521323

image-20230626145643726

1.6. 分词器 IK Analysis

1.6.1. 下载分词器

最好下载和elasticSearch一致的分词器,如果没有则保证大版本一致,例如:7.17.X

Releases · medcl/elasticsearch-analysis-ik · GitHub

image-20230626145823825

1.6.2. 上传分词器

上传下载好的压缩包到: /usr/local/software/elasticsearch-analysis-ik

创建elasticsearch-analysis-ik目录

mkdir elasticsearch-analysis-ik

1.6.3. 拷贝分词器到es容器的plugins/ik中

进入容器内/usr/share/elasticsearch/plugins目录下创建 ik 目录

再拷贝

docker cp elasticsearch-analysis-ik-7.17.7.zip es:/usr/share/elasticsearch/plugins/ik

1.6.4. 解压分词器

进入容器内/usr/share/elasticsearch/plugins/ik下,解压

unzip elasticsearch-analysis-ik-7.17.7.zip

删除压缩包

rm -rf elasticsearch-analysis-ik-7.17.7.zip

1.6.5. 重启容器

docker restart es

1.6.6. 使用kibana测试

成功

image-20230626151932936

1.7. springboot整合ElasticSearch

1.7.1. 引入依赖

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

1.7.2. 配置es

elasticsearch:
  rest:
    uris: 192.168.100.100:9200
data:
  elasticsearch:
    repositories:
      enabled: true

1.7.3. 创建ES客户端对象

@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
    @Value("${spring.elasticsearch.rest.uris}")
    private String url;

    //创建ES客户端对象 RestHighLevelClient
    @Bean
    @Override
    public RestHighLevelClient elasticsearchClient() {
        //客户端配置对象
        ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo(url).build();
        return RestClients.create(clientConfiguration).rest();
    }
}

1.7.4. 创建存入es的实体映射类对象

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "book_index")
public class BookDoc {
    @Id
    @Field(type = FieldType.Long)
    private Long id;

    //书名
    @Field(type = FieldType.Text)
    private String title;

    //作者
    @Field(type = FieldType.Text)
    private String author;

    //ISBN号
    @Field(type = FieldType.Text)
    private String isbn;

    //价格
    @Field(type = FieldType.Double)
    private BigDecimal price;

    //版次
    @Field(type = FieldType.Integer)
    private Integer version;

    //出版时间
    @Field(type = FieldType.Date)
    private Date pubDate;

    //库存
    @Field(type = FieldType.Integer)
    private Integer store;

    //封面图片路径
    @Field(type = FieldType.Text)
    private String imgUrl;

    //重量
    @Field(type = FieldType.Double)
    private BigDecimal weight;

    //简介描述
    @Field(type = FieldType.Text)
    private String introduction;

    //卖出数量
    @Field(type = FieldType.Integer)
    private Integer sold;

    //页数
    @Field(type = FieldType.Integer)
    private Integer pages;

    //创建时间
    @Field(type = FieldType.Date)
    private Date createTime;

    //修改时间
    @Field(type = FieldType.Date)
    private Date updateTime;

    //创建者
    @Field(type = FieldType.Text)
    private String creator;

    //书籍对应类型
    @Field(type = FieldType.Text)
    private String bookTypeName;

    //书籍对应出版社
    @Field(type = FieldType.Text)
    private String publisherName;
}

1.7.5. 创建操作es库的dao层对象

继承ElasticsearchRepository<BookDoc,Long> 两个泛型,第一个是操作的对象类型,第二个是主键的类型

在该接口中,通过定义的方法名就能自动创建各种查询

例如 findByIntroduction() 当调用该方法时,就会自动翻译为执行查询introduction的ES查询语句

@Mapper
public interface IBookDocDao extends ElasticsearchRepository<BookDoc,Long> {
    public List<BookDoc> findByIntroduction(String introduction);
}

1.7.6. 向es中添加数据,查询

public void addAllToEs() {
    //先从数据库中查询出所有的book
    List<Book> books = bookDao.selectList(null);

    //封装BookDoc对象
    List<BookDoc> bookDocs = new ArrayList<>();

    books.forEach(book -> {
        //从redis中获取bookTypeName和publisherName
        String bookTypeName = redisTemplate.opsForValue().get("bookType:" + book.getBookTypeId());
        String publisherName = redisTemplate.opsForValue().get("publisher:" + book.getPublisherId());
        BookDoc bookDoc = new BookDoc(book.getId(), book.getTitle(), book.getAuthor(),
                                      book.getIsbn(), book.getPrice(), book.getVersion(),
                                      book.getPubDate(), book.getStore(), book.getImgUrl(),
                                      book.getWeight(), book.getIntroduction(), book.getSold(),
                                      book.getPages(), book.getCreateTime(), book.getUpdateTime(),
                                      book.getCreator(), bookTypeName, publisherName);
        bookDocs.add(bookDoc);
    });
    //向ES中添加数据
    bookDocDao.saveAll(bookDocs);
}


//查询 introduction 中含有str的数据
@Override
public List<BookDoc> findFromEs(String str) {
    List<BookDoc> bookDocs = bookDocDao.findByIntroduction(str);
    return bookDocs;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值