【RabbitMQ】高级篇,学习纪录+笔记

news2024/11/29 0:41:24

目录

一.高级特性

1.1消息的可靠投递

2.1Consumer Ack

3.1消费端限流

4.1TTL

5.1死信队列

6.1延迟队列

7.1日志与监控

7.1.1日志

7.1.2监控

8.1消息追踪

8.1.1Firehose

8.1.2rabbitmq_tracing

9.1消息可靠性保障(思路)

9.2消息幂等性保障(思路)


一.高级特性

1.1消息的可靠投递

在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提 供了两种方式用来控制消息的投递可靠性模式。

①confirm确认模式

②return退回模式

rabbitmq 整个消息投递的路径为: producer--->rabbitmqbroker--->exchange--->queue--->consumer 

消息从 producer 到 exchange 则会返回一个 confirmCallback 。 

消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。

我们将利用这两个 callback 控制消息的可靠性投递

接基础篇,在生产者中RabttitMQConfig中配置

    public static final String CONFIRM_EXCHANGE_NAME = "test_exchange_confirm";
    public static final String CONFIRM_QUEUE_NAME = "test_queue_confirm";
    @Bean("confirmExchange")
    public Exchange confirmExchange(){
        return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true).build();
    }

    @Bean("confirmQueue")
    public Queue confirmQueue(){
        return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
    }

    @Bean
    public Binding bindConfirmExchangeQueue(@Qualifier("confirmExchange") Exchange exchange,
                                            @Qualifier("confirmQueue") Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("confirm").noargs();
    }

在yml中开启

#配置RabbitMQ的基本信息
spring:
  rabbitmq:
    host: ip
    username: admin
    password: admin
    port: 5672
    virtual-host: /
    publisher-confirms: true //开启确认模式,这个是老版本的写法,已经废弃了,新版本在后面加type
    publisher-returns: true //开启退回模式

测试类

/*
    *
    * 确认模式步骤
    * 1.确认模式开启,在yml中配置
    * 2.在rabbitTemplate定义ConfirmCallback回调函数
    *
    * */
@Test
    public void testConfirm(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 相关的配置信息
             * @param ack exchange交换机 是否成功收到了消息,true成功,false失败
             * @param cause 如果ack为false,这是失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if(ack){
                    //接收成功
                    System.out.println("接收成功消息:"+cause);
                }else {
                    //接收失败
                    System.out.println("接收失败消息:"+cause);
                    //做一些处理,再次发送

                }
                System.out.println("Confirm方法被执行……");
            }
        });
        rabbitTemplate.convertAndSend(RabbitMQConfig.CONFIRM_EXCHANGE_NAME,"confirm","message..confirm...");
    }

以上配置方法也可以写在配置类中

回退方式测试类

/**
     * 回退模式:当消息发送给Exchange后,Exchange路由到Queue失败时才会执行ReturnCallback
     * 1.开启回退模式
     * 2.设置ReturnCallback
     * 3.设置Exchange处理消息的模式
     *   消息没有路由到Queue,丢弃消息
     *   如果消息没有路由到Queue,返回给消息发送方
     */
    @Test
    public void testCallback() throws InterruptedException {
        //设置交换机处理失败消息的模式
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replycode, String replyText, String exchange, String routingKey) {
                System.out.println("return执行了……");
                System.out.println(message);
                System.out.println(replycode);
                System.out.println(replyText);
                System.out.println(exchange);
                System.out.println(routingKey);
            }
        });
        rabbitTemplate.convertAndSend(RabbitMQConfig.CONFIRM_EXCHANGE_NAME,"confirm","message..confirm...");
        Thread.sleep(3000);
    }

消费者确认再继续看下面

2.1Consumer Ack

ack指Acknowledge,确认。 表示消费端收到消息后的确认方式。

有三种确认方式: 自动确认:acknowledge="none" •

手动确认:acknowledge="manual" •

根据异常情况确认:acknowledge="auto",(这种方式使用麻烦,不作讲解)

其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的 消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。

如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则 调用channel.basicNack()方法,让其自动重新发送消息

在消费者工程中,新建监听者

/**
 * Counsumer ACK
 * 1.设置手动签收 在yml
 * 2.让监听器类实现ChannelAwareMessageListener
 * 3.如果消息成功处理,调用channel的basicAck进行签收
 * 4.basicNack拒收 broker重新发送给counsumer
 */
@Component
public class ACKListener  {

    @RabbitListener(queues = "test_queue_confirm")
    public void ListenerQueue2(Message message,Channel channel) throws IOException {
        System.out.println("test_queue_confirm----->"+message);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println("处理业务逻辑");
            Thread.sleep(3000);
            int i = 10/0;
            /**
             * long deliveryTag,收到消息的标识
             * boolean multiple,是否签收多条消息
             */
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            //拒绝签收 第三个参数设置为true,消息重新回到queue,broker重新发送该消息给消费端
            // channel.basicNack(deliveryTag,true,true);

            // 如果真得出现了异常,我们采用消息重投,获取redelivered,判断是否为重投: false没有重投,true重投
            Boolean redelivered = message.getMessageProperties().getRedelivered();
            System.out.println("redelivered = " + redelivered);
            try {
                // (已重投)拒绝确认
                if (redelivered) {
                    /**
                     * 拒绝确认,从队列中删除该消息,防止队列阻塞(消息堆积)
                     * boolean requeue: false不重新入队列(丢弃消息)
                     */
                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
                    System.out.println();
                } else { // (没有重投) 消息重投
                    /**
                     * 消息重投,重新把消息放回队列中
                     * boolean multiple: 单条或批量
                     * boolean requeue: true重回队列
                     */
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                    System.out.println("=========消息重投了=======");
                }
            } catch (Exception ee) {
                ee.printStackTrace();
            }

        }
    }


}

配置yml

#配置RabbitMQ的基本信息
spring:
  rabbitmq:
    host: ip
    username: admin
    password: admin
    port: 5672
    virtual-host: /
    listener:
      simple:
        # 并发消费:每个侦听器线程的最小数量,具体数值根据系统性能配置(一般为系统cpu核数)
        concurrency: 2
        # 并发消费:每个侦听器线程的最大数量,具体数值根据系统性能配置(一般为系统cpu核数*2)
        max-concurrency: 4
        # 每次只能获取一条消息,处理完成才能获取下一个消息,避免照成消息堆积在一个消费线程上
        prefetch: 1
        #acknowledge-mode: manual         # 消费者开启手动ack消息确认,需要测试请看示例请AckConsumer,所有队列都会生效
        #default-requeue-rejected: false  # 设置为false,会重发消息到死信队列(防止手动ack确认失败的消息堆积),需要测试请示例AckConsumer,所有队列都会生效
        retry:
          enabled: true                   # 解决消息死循环问题-启用重试
          max-attempts: 3                 # 最大重试3次(默认),超过就丢失(或放到死信队列中,防止消息堆积)
          multiplier: 2                   # 乘子
          initial-interval: 3000          # 第一次和第二次之间的重试间隔,后面的用乘子计算 3s 6s 12s
          max-interval: 16000             # 最大重试时间间隔16s
      direct:
        acknowledge-mode: manual
        default-requeue-rejected: false

小结

在rabbit:listener-container标签中设置acknowledge属性,设置ack方式 none:自动确认,manual:手动确认 

如果在消费端没有出现异常,则调用channel.basicAck(deliveryTag,false);方法确认签收消息 

如果出现异常,则在catch中调用 basicNack或 basicReject,拒绝消息,让MQ重新发送消息。

持久化 exchange要持久化

queue要持久化

message要持久化

生产方确认Confirm

消费方确认Ack

Broker高可用

3.1消费端限流

在yml中配置 prefetch属性设置消费端一次拉取多少消息 

prefetch: 1

消费端的确认模式一定为手动确认。

acknowledge-mode: manual

详细配置见上面消费者的yml

4.1TTL

TTL 全称 Time To Live(存活时间/过期时间)。 

当消息到达存活时间后,还没有被消费,会被自动清除。 

RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。

可以直接在RabbitMQ的web端直接对队列进行设置

也可以通过java代码对其进行设置,具体代码见后面死信队列与延迟队列

小结

设置队列过期时间使用参数:x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期。 

设置消息过期时间使用参数:expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断 这一消息是否过期。 

如果两者都进行了设置,以时间短的为准。

5.1死信队列

死信队列,英文缩写:DLX 。Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以 被重新发送到另一个交换机,这个交换机就是DLX

 

消息成为死信的三种情况:

1. 队列消息长度到达限制;

2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标列,requeue=false;

3. 原队列存在消息过期设置,消息到达超时时间未被消费 

 

正常队列绑定死信交换机: 给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key

 

生产者

RabbitMQConfig中配置一对正常交换机队列、一堆死信交换机队列、正常队列与死信交换机绑定

 //死信
    //先声明正常的交换机(test_exchange_dlx)和队列(test_queue_dlx)
    //声明死信交换机(exchange_dlx)和队列(queue_dlx)
    //正常队列绑定死信交换机
    //设置两个参数
    //x-dead-letter-exchange 死信交换机名称
    //x-dead-letter-routing-key 发送给死信交换机的routing key
    @Bean("test_exchange_dlx")
    public Exchange test_exchange_dlx(){
        return ExchangeBuilder.topicExchange("test_exchange_dlx").durable(true).build();
    }

    @Bean("test_queue_dlx")
    public Queue test_queue_dlx(){
        Map<String, Object> map = new HashMap<>();
        map.put("x-dead-letter-exchange","exchange_dlx");
        map.put("x-dead-letter-routing-key","dlx.hehe");
        map.put("x-message-ttl",10000);
        map.put("x-max-length",10);
        return QueueBuilder.durable("test_queue_dlx").withArguments(map).build();
    }

    @Bean
    public Binding dlxCommonBinding(@Qualifier("test_exchange_dlx") Exchange exchange,
                              @Qualifier("test_queue_dlx") Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("test.dlx.#").noargs();
    }

    @Bean("exchange_dlx")
    public Exchange exchange_dlx(){
        return ExchangeBuilder.topicExchange("exchange_dlx").durable(true).build();
    }

    @Bean("queue_dlx")
    public Queue queue_dlx(){
        return QueueBuilder.durable("queue_dlx").build();
    }

    @Bean
    public Binding dlxBinding(@Qualifier("exchange_dlx") Exchange exchange,
                              @Qualifier("queue_dlx") Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("dlx.#").noargs();
    }

测试类

    /**
     * 发送测试死信消息
     * 1.过期时间
     * 2.长度限制
     * 3.消息拒收
     */
    @Test
    public void testDlx(){
        //1.过期时间
//        rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.haha","我是一条消息,我会死亡");

        //2.长度限制
//        for (int i = 1; i <= 20 ; i++) {
//            rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.haha","我是一条消息,我会死亡");
//        }

        //3.消息拒收
        rabbitTemplate.convertAndSend("test_exchange_dlx","test.dlx.haha","我是一条消息,我会死亡");

    }

消费者监听,监听正常队列

@Component
public class DLXListener {

    @RabbitListener(queues = "test_queue_dlx")
    public void ListenerQueue3(Message message,Channel channel) throws IOException {
        System.out.println("test_queue_dlx----->"+message);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println("处理业务逻辑");
            Thread.sleep(3000);
            int i = 10/0;
            /**
             * long deliveryTag,收到消息的标识
             * boolean multiple,是否签收多条消息
             */
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            //拒绝签收 第三个参数设置为true,消息重新回到queue,broker重新发送该消息给消费端
            // channel.basicNack(deliveryTag,true,true);
            // 如果真得出现了异常,我们采用消息重投,获取redelivered,判断是否为重投: false没有重投,true重投
            Boolean redelivered = message.getMessageProperties().getRedelivered();
            System.out.println("redelivered = " + redelivered);
            try {
                // (已重投)拒绝确认
                if (redelivered) {
                    System.out.println("拒绝了,不重投");
                    /**
                     * 拒绝确认,从队列中删除该消息,防止队列阻塞(消息堆积)
                     * boolean requeue: false不重新入队列(丢弃消息)
                     */
                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
                    System.out.println();
                } else { // (没有重投) 消息重投
                    /**
                     * 消息重投,重新把消息放回队列中
                     * boolean multiple: 单条或批量
                     * boolean requeue: true重回队列
                     */
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                    System.out.println("=========消息拒绝接收,重投了=======");
                }
            } catch (Exception ee) {
                ee.printStackTrace();
            }

        }
    }


}

观察死信队列中消息数量变化

6.1延迟队列

延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。

需求:

1. 下单后,30分钟未支付,取消订单,回滚库存。

2. 新用户注册成功7天后,发送短信问候。

实现方式: 1. 定时器 2. 延迟队列

定时器对于性能消耗很大,所以采取延迟队列

由于RabbitMQ没有直接的延迟队列,故而采取ttl+死信队列来实现

生产者

RabbitMQConfig中配置

//延迟
    //先声明正常的交换机(order_exchange)和队列(order_queue)
    //声明死信交换机(order_exchange_dlx)和队列(order_queue_dlx)
    //正常队列绑定死信交换机
    //设置两个参数
    //x-dead-letter-exchange 死信交换机名称
    //x-dead-letter-routing-key 发送给死信交换机的routing key
    @Bean("order_exchange")
    public Exchange order_exchange(){
        return ExchangeBuilder.topicExchange("order_exchange").durable(true).build();
    }

    @Bean("order_queue")
    public Queue order_queue(){
        Map<String, Object> map = new HashMap<>();
        map.put("x-dead-letter-exchange","order_exchange_dlx");
        map.put("x-dead-letter-routing-key","dlx.order.cancel");
        map.put("x-message-ttl",10000);
        map.put("x-max-length",10);
        return QueueBuilder.durable("order_queue").withArguments(map).build();
    }

    @Bean
    public Binding yanchiCommonBinding(@Qualifier("order_exchange") Exchange exchange,
                                    @Qualifier("order_queue") Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("order.#").noargs();
    }

    @Bean("order_exchange_dlx")
    public Exchange order_exchange_dlx(){
        return ExchangeBuilder.topicExchange("order_exchange_dlx").durable(true).build();
    }

    @Bean("order_queue_dlx")
    public Queue order_queue_dlx(){
        return QueueBuilder.durable("order_queue_dlx").build();
    }

    @Bean
    public Binding yanchiBinding(@Qualifier("order_exchange_dlx") Exchange exchange,
                              @Qualifier("order_queue_dlx") Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("dlx.order.#").noargs();
    }

测试类

@Test
    public void testDelay() throws InterruptedException {
        //发送订单消息
        rabbitTemplate.convertAndSend("order_exchange","order.msg","订单信息 id:1");
        //打印倒计时10s
        for (int i = 0; i < 10; i++) {
            System.out.println(i+"...");
            Thread.sleep(1000);
        }

    }

消费者监听,监听的是死信队列

@Component
public class OrderListener {

    @RabbitListener(queues = "order_queue_dlx")
    public void ListenerQueue4(Message message,Channel channel) throws IOException {
        System.out.println("order_queue_dlx----->"+message);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println("处理业务逻辑");
            Thread.sleep(3000);
            System.out.println("根据订单id查询状态");
            System.out.println("判断状态是否为支付成功");
            System.out.println("取消订单,回滚……");
            /**
             * long deliveryTag,收到消息的标识
             * boolean multiple,是否签收多条消息
             */
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            //拒绝签收 第三个参数设置为true,消息重新回到queue,broker重新发送该消息给消费端
            // channel.basicNack(deliveryTag,true,true);
            // 如果真得出现了异常,我们采用消息重投,获取redelivered,判断是否为重投: false没有重投,true重投
            Boolean redelivered = message.getMessageProperties().getRedelivered();
            System.out.println("redelivered = " + redelivered);
            try {
                // (已重投)拒绝确认
                if (redelivered) {
                    System.out.println("拒绝了,不重投");
                    /**
                     * 拒绝确认,从队列中删除该消息,防止队列阻塞(消息堆积)
                     * boolean requeue: false不重新入队列(丢弃消息)
                     */
                    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
                    System.out.println();
                } else { // (没有重投) 消息重投
                    /**
                     * 消息重投,重新把消息放回队列中
                     * boolean multiple: 单条或批量
                     * boolean requeue: true重回队列
                     */
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                    System.out.println("=========消息拒绝接收,重投了=======");
                }
            } catch (Exception ee) {
                ee.printStackTrace();
            }

        }
    }


}

小结

1.延迟队列 指消息进入队列后,可以被延迟一定时间,再进行消费。

2. RabbitMQ没有提供延迟队列功能,但是可以使用 : TTL + DLX 来实现延迟队列效果。

7.1日志与监控

7.1.1日志

RabbitMQ默认日志存放路径: /var/log/rabbitmq/rabbit@xxx.log

日志包含了RabbitMQ的版本号、Erlang的版本号、RabbitMQ服务节点名称、cookie的hash值、 RabbitMQ配置文件地址、内存限制、磁盘限制、默认账户guest的创建以及权限配置等等

7.1.2监控

web管控台监控

rabbitmqctl管理和监控

8.1消息追踪

在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况。对于RabbitMQ而言,可能 是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;也 有可能是因为交换器与队列之间不同的转发策略;甚至是交换器并没有与任何队列进行绑定,生产者又不感知或者没有采取相应的措施;另外RabbitMQ本身的集群策略也可能导致消息的丢失。这个时候就需要有一个较好的机制跟踪记录消息的投递过程,以此协助开发和运维人员进行问题的定位。

在RabbitMQ中可以使用Firehoserabbitmq_tracing插件功能来实现消息追踪

8.1.1Firehose

firehose的机制是将生产者投递给rabbitmq的消息,rabbitmq投递给消费者的消息按照指定的格式 发送到默认的exchange上。这个默认的exchange的名称为amq.rabbitmq.trace,它是一个topic类 型的exchange。发送到这个exchange上的消息的routing key为 publish.exchangename 和 deliver.queuename。其中exchangename和queuename为实际exchange和queue的名称,分别对应生产者投递到exchange的消息,和消费者从queue上获取的消息。

注意:打开 trace 会影响消息写入功能,适当打开后请关闭。

rabbitmqctl trace_on:开启Firehose命令

rabbitmqctl trace_off:关闭Firehose命令

步骤:

1.创建一个新的队列,然后将这个队列绑定到默认交换机上,Routing Key写#,表示所有消息都监听

2.然后在这个新的队列发一条消息,然后再查,发现只有一条

3.然后开启rabbitmqctl trace_on

4.再发一条消息,再查,发现有额外的消息

8.1.2rabbitmq_tracing

rabbitmq_tracing和Firehose在实现上如出一辙,只不过rabbitmq_tracing的方式比Firehose多了一 层GUI的包装,更容易使用和管理。

启用插件:rabbitmq-plugins enable rabbitmq_tracing

然后即可在web端右侧看到Tracing,点进去即可查看与操作,可以创建一个新的,Pattern写#

9.1消息可靠性保障(思路)

由于比较复杂,只提供一个思路,思路多种多样,这个不一定好,看看就好

 

9.2消息幂等性保障(思路)

幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。

也就是说,其任 意多次执行对资源本身所产生的影响均与一次执行的影响相同。

在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果。

比如发送两条相同的扣款信息,不能扣两次款。

采取乐观锁机制。

 

本文只是本人在学习复习过程中的笔记,有什么不足之处请指教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hash算法,插入排序,希尔排序,选择排序,冒泡排序,归并排序,快速排序,堆排序,基数排序

Hash算法hash就是散列表&#xff0c;就是把任意长度的输入通过散列算法变换成固定长度的输出&#xff0c;该输出就是散列值。实质就是压缩映射&#xff0c;散列值的空间通常远小于输入的空间。常利用hash算法&#xff0c;将输入的一千万个检索串转化为题目中所说大约三百万个不…

《Linux Shell脚本攻略》学习笔记-第七章

7.1 简介 随着磁盘存储技术的发展&#xff0c;最简单地备份方法是添加新的磁盘设备或是使用云存储&#xff0c;而不再是依赖磁盘。 但是也需要压缩备份数据&#xff0c;降低存储空间需求以及传输时间。把数据存放在云端之前应该对其加密。数据在加密之前通常都要先进行归档和压…

2.2总线的性能指标

文章目录一、总线传输周期二、总线时钟周期三、总线工作频率四、总线时钟频率五、总线宽度六、总线带宽1.概念2.注意点3.案例分析4.总结补充七、总线复用八、信号线数九、总结这一节我们一起学习评价 总线性能的指标&#xff0c;这些指标很可能在选择题或者大题第一小题出现。一…

Git团队协作及分支策略

目录 分布式版本控制系统 访问模型 分支策略-Git flow feature 分支策略-Github flow 分支策略-Gitlab flow 主干开发模式 总结 分布式版本控制系统 分布式相比于集中式的最大区别在于开发者可以提交到本地&#xff0c;每个开发者通过克隆&#xff08;git clone&#…

Pytest参数化-下篇

&#x1f60e;&#x1f60e;原文出自&#xff1a;测个der&#xff0c;博主的公众号&#xff0c;格式美观一些。 关于参数化的其他案例 数据嵌套及多参数传参 import pytestpwd_datas [["QINGAN",{"user":"SHIER","pwd":"1234…

5个关键词回顾2022年个推技术实践

作为一家数据智能服务商&#xff0c;2022年每日互动&#xff08;个推&#xff09;在为开发者和行业客户提供优质服务的同时&#xff0c;不断砥砺创新&#xff0c;追逐技术前沿。个推还持续参与开发者生态建设&#xff0c;积极总结、分享自身技术实战经验&#xff0c;面向行业输…

该如何测客户端专项测试?

整个行业现在越来越重视客户端的专项测试了。像接口的性能测试、webview 测试、H5性能分析等&#xff0c;每一项都需要测试。而对于卡顿的分析、系统资源分析、耗电量测试及弱网测试这块&#xff0c;也变得越来越重要了&#xff01;后面也会有相关的文章更新。大家可以戳此关注…

快解析远程访问解决方案——安全稳定,部署简单

我们说的远程办公通常指狭义上的远程办公&#xff0c;是指通过远程技术&#xff0c;或远程控制软件&#xff0c;对远程电脑进行操作办公&#xff0c;实现非本地办公&#xff0c;如在家办公、异地办公、移动办公等远程办公模式。这种技术的关键在于:穿透内网和远程控制的安全性。…

Wisej.NET 3.1.6 Crack

新功能 Wisej.NET 3.1 通过添加几个新的独特功能和简化的安装过程增强了里程碑 3.0 版本。 除了大量错误修复和对我们库的显着性能增强之外&#xff0c;3.1 还包括以下值得注意的新功能&#xff1a; 视觉工作室市场 Wisej.NET 现在比以往任何时候都更容易使用。或 Visual Studi…

联合证券|五大国际巨鳄集体爆雷,美股期指大跳水!

商场现已进入到雷雨季&#xff01; 周五晚间&#xff0c;花旗、摩根大通、高盛、富国和贝莱德团体爆雷。 花旗集团上一年第四季度每股收益低于预期&#xff0c;尽管营收好于预期。花旗集团Q4每股收益1.16美元&#xff0c;预期为1.17美元。财报发布之后一度暴降超3%&#xff1b;…

【机器学习之模型融合】Stacking堆叠法

目录 1、Stacking的基本思想&#x1f48d; 2、思考&#x1f48e; 3、在sklearn中实现Stacking&#x1f3af; 3.1、导入工具库和数据&#x1f455; 3.2、定义交叉验证评估函数&#x1f457; 3.3、个体学习器与元学习器的定义&#x1f357; 3.4、模型构建&#x1f36a; 4…

【Linux】创建新用户 sudo配置,添加信任

目录 一、创建新用户 二、sudo不被允许 三、添加信任用户 一、创建新用户 相关指令&#xff1a; adduser [用户名]&#xff1a;创建新用户 passwd [用户名]&#xff1a;修改用户密码 su [用户名]&#xff1a;切换到该用户 设置密码&#xff0c;重复输入两遍之后&#xff0…

大数据技术架构(组件)——Hive:环境准备1

1.0.1、环境准备1.0.1.0、maven安装1.0.1.0.1、下载软件包1.0.1.0.2、配置环境变量1.0.1.0.3、调整maven仓库打开$MAVEN_HOME/conf/settings.xml文件&#xff0c;调整maven仓库地址以及镜像地址<settings xmIns"http://maven.apache.org/SETTINGS/1.0.0"xmIns:xsi…

电脑磁盘占用率高怎么办?

Windows磁盘占用率高是一种普遍存在的问题&#xff0c;相信很多用户遇到过不止一次&#xff0c;它可能是在刚开机时、可能是在下载文件时、也可能是在开启关闭应用程序时……当磁盘占用高之后&#xff0c;您的计算机运行速度会变得像蜗牛一样缓慢&#xff0c;更糟糕的是有些电脑…

python 代码注释

文章目录写在前面使用方法plainEpytextGoogleNumpyreStructuredText相关程序包其他写在前面 如果说高效率的算法是一个项目的内核&#xff0c;那么完备的文档注释、API 接口则是项目的外壳&#xff0c;直接与客户交互。 pycharm 提供了 5 种 代码注释格式。 分别是 plain, epy…

Tslib配置文件ts.conf

默认&#xff1a; # Access plugins ################# Uncomment if you wish to use the linux input layer event interface module_raw input# For other driver modules, see the ts.conf man page# Filter plugins ################# Uncomment if first or last sample…

【运维心得】正确的校正mysql-slave及mysqldump

实践出真知&#xff0c;有些细节&#xff0c;记录下。本文不涉及主备知识&#xff0c;有需右转: https://blog.csdn.net/qq_26834611/article/details/121385550mysql 正确的dump 命令:0. 检查当前主库所在位置ip addr 查看虚拟ip所在位置 1. 备机数据库dump备份:mysqldump -ur…

【ChatGPT】注册OpenAI账号试用ChatGPT

主要参考&#xff1a; 手把手教你注册 AI 聊天机器人模型 ChatGPT&#xff1a;大体流程注册OpenAI账号试用ChatGPT指南&#xff1a; 相关网站 openai: https://beta.openai.com/signup直接登录charGPT&#xff1a;https://chat.openai.com/接入微信&#xff0c;1.2K&#xf…

OneKeyGhost工具进行备份还原windows操作系统

OneKeyGhost OneKey Ghost是一款设计专业、操作简便的绿色程序&#xff0c;能够在 Windows 下对任意分区进行一键备份、恢复&#xff0c;支持ISO文件、光盘、U盘里的GHO文件硬盘安装。 OneKeyGhost备份wind系统 系统需要包含两个磁盘&#xff0c;一个系统盘另外一个数据盘。 …

Linux-文件权限命令

用户及权限操作 d:表示文件夹&#xff0c;-&#xff1a;表示文件&#xff0c;后面每隔3个分别代表 u、g、o。 使用ls -l &#xff08;ll&#xff09;可查看文件&#xff08;目录&#xff09;的权限 使用 chmod 可修改文件权限&#xff0c;有两种使用格式&#xff1a;字母法…