3.rabbitMQ之发布确认高级和整合springboot(重要)找了很多博客整理出来的

1.极端情况下 rabbitMQ需要重启,导致消息投递失败(生产者发消息全部丢失)(交换机或者队列出问题)
生产者需要把数据放到缓存,用定时任务重新发送
解决方法:
0.必须配置文件写

   spring.rabbitmq.publisher-confirm-type=correlated
   spring.rabbitmq.publisher-returns=true
  1. correlationDate数据 ack是否确定消息发送成功,cause是失败的原因
  @Component
                class    xxx  实现  RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback//由于是只是他rabbit的内部类,需要手动设置
                  @Autowired 
                       private RabbitTemplate  rabbitTemplate;
                  @PostConstruct  //@Autowired>@PostConstruct在构造器之后执行这个方法
                  public void init(){ 
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnCallback(this);
                 }

-----2.生产者 加个发送的id和信息,加上发送信息的最后一个参数

 CorrelationData correlationData1=new CorrelationData("1");//代表传入的id

------3.故意改错交换机名和队列名导致失败
------队列挂了(消息也丢失,需要交换机退回消息)-------

-------4.类 继续实现 ReturnCallback接口(只有路由不成功才调用)(还是要注入rabbit)
--------完整代码--------
//配置类 通知被退回的消息和id,可以自己存到数据库保存,等MQ正常后在生产

  @Slf4j
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{
    @Autowired
    RabbitTemplate rabbitTemplate;
    @PostConstruct
    void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String id=correlationData!=null?correlationData.getId():"";
        if (ack){
            log.info("收到消息id为{}",id);
        }else {
            log.info("没有收到消息,id为{},原因为{}",id,cause);
        }
    }

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            log.info("路由失败,消息为{},退回原因{},交换机名{},routingKey:{}",new String(message.getBody()),replyText,exchange,routingKey);
    }


}

//生产者


public class ProducerController {
 public static final String CONFIRM_EXCHANGE_NAME = "business.exchange";
 @Autowired
 private RabbitTemplate rabbitTemplate;



 @GetMapping("sendMessage/{message}")
 public void sendMessage(@PathVariable String message){

 //指定消息 id 为 1
CorrelationData correlationData1=new CorrelationData("1");
 String routingKey="rk.001";

rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME,routingKey,message+routingKey,correlationData1);
 CorrelationData correlationData2=new CorrelationData("2");
 routingKey="key2";
 //这样只知道交换机有问题,而不知道路由有问题
 rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME,routingKey,message+routingKey,correlationData2);//设置好corelation的id数据
  log.info("发送消息内容:{}",message);
  
  }
}

// !!!也可以用下面的解决方案,如果不喜欢用整合其他框架

2.备份交换机(也是队列不成功,上面的解决方案) 可以不用重新发送,走备份交换机到消费者消费和报警消费者失败
工作流程如下图mq6
请添加图片描述

     1.队列转发到另外的交换机
     2.交换机比路由先出错会先报交换机的错误,路由的错误不报
注意!!!需要在业务交换机声明 备份交换机
        //核心步骤 声明业务 Exchange
 @Bean("confirmExchange")
 public DirectExchange confirmExchange(){
    ExchangeBuilder exchangeBuilder=ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true).withArgument("alternate-exchange", BACKUP_EXCHANGE_NAME); //翻译为可变交换机,出错了改发送的交换机

 return (DirectExchange) exchangeBuilder.build();
 }
   ------完整代码------
    -------声明交换机和队列-----

```java
            @Configuration
public class ConfirmConfig {
 public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange";
 public static final String CONFIRM_QUEUE_NAME = "confirm.queue";

 public static final String BACKUP_EXCHANGE_NAME = "backup.exchange";
 public static final String BACKUP_QUEUE_NAME = "confirm.queue";
 public static final String WARNING_QUEUE_NAME = "warning.queue";

 //声明业务 Exchange
 @Bean("confirmExchange")
 public DirectExchange confirmExchange(){
    ExchangeBuilder exchangeBuilder=ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true).withArgument("alternate-exchange", BACKUP_EXCHANGE_NAME);

 return (DirectExchange) exchangeBuilder.build();
 }
 // 声明确认队列
 @Bean("confirmQueue")
 public Queue confirmQueue(){
 return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
 }
 // 声明确认队列绑定关系
 @Bean
 public Binding queueBinding(@Qualifier("confirmQueue") Queue queue,
                             @Qualifier("confirmExchange") DirectExchange exchange){
 return BindingBuilder.bind(queue).to(exchange).with("key1");
 }
 //声明备份 Exchange
 @Bean("backupExchange")
 public FanoutExchange backupExchange(){
  return new FanoutExchange(BACKUP_EXCHANGE_NAME);
 }
 // 声明备份队列
 @Bean("backupQueue")
 public Queue backupQueue(){
  return QueueBuilder.durable(BACKUP_QUEUE_NAME).build();
 }
 // 声明警告队列
 @Bean("warningQueue")
 public Queue warningQueue(){ return QueueBuilder.durable(WARNING_QUEUE_NAME).build();}
 // 声明backup队列绑定关系
 @Bean
 public Binding backupQueueBindingBackup(@Qualifier("backupQueue") Queue queue,
                             @Qualifier("backupExchange") FanoutExchange exchange){
  return BindingBuilder.bind(queue).to(exchange);
 }
 // 声明warning队列绑定关系
 @Bean
 public Binding warningQueueBindingBackup(@Qualifier("warningQueue") Queue queue,
                                         @Qualifier("backupExchange") FanoutExchange exchange){
  return BindingBuilder.bind(queue).to(exchange);
 }
}

-------交换机和队列路由的错误回调-----

@Slf4j
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnCallback{
    @Autowired
    RabbitTemplate rabbitTemplate;
    @PostConstruct
    void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);
    }
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String id=correlationData!=null?correlationData.getId():"";
        if (ack){
            log.info("收到消息id为{}",id);
        }else {
            log.info("没有收到消息,id为{},原因为{}",id,cause);
        }
    }

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            log.info("路由失败,消息为{},退回原因{},交换机名{},routingKey:{}",new String(message.getBody()),replyText,exchange,routingKey);
    }


}

-------生产者-----

@RestController
@RequestMapping("/confirm")
@Slf4j
public class ProducerController {
 public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange";
 @Autowired
 private RabbitTemplate rabbitTemplate;
 @Autowired
 private MyCallBack myCallBack;

 @GetMapping("sendMessage/{message}")
 public void sendMessage(@PathVariable String message){
 //指定消息 id 为 1
 CorrelationData correlationData1=new CorrelationData("1");
 String routingKey="key1";
 
rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME,routingKey,message+routingKey,correlationData1);
 CorrelationData correlationData2=new CorrelationData("2");
 routingKey="key2"; //这里是错误的routingKey,会报错
 //这样只知道交换机有问题,而不知道路由有问题
 rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME,routingKey,message+routingKey,correlationData2);
  log.info("发送消息内容:{}",message);
 }
}

-------消息消费者(消息正常消费)-------

@Component
@Slf4j
public class ConfirmConsumer {
  public static final String CONFIRM_QUEUE_NAME = "confirm.queue";
  @RabbitListener(queues =CONFIRM_QUEUE_NAME)
  public void receiveMsg(Message message){
   String msg=new String(message.getBody());
   log.info("接受到队列 confirm.queue 消息:{}",msg);
  }
}

-------警告消费者(消费失败转发到备份交换机,和队列进行消费)-------

@Component
@Slf4j
public class WarningConsumer {
 public static final String WARNING_QUEUE_NAME = "warning.queue";
 @RabbitListener(queues = WARNING_QUEUE_NAME)
  public void receiveWarningMsg(Message message) {
  String msg = new String(message.getBody());
  log.error("报警发现不可路由消息:{}", msg);
  }
}

3.其他知识点

1.幂等性问题,消息被重复消费,需要先判断全局唯一id是否消费后,再消费
         1.使用mysql,唯一id,有性能瓶颈,不推荐
         2.使用redis原子性 执行setnx命令天然有幂等性

4.优先队列(在队列加优先级) 0-255 越大越优先(如天猫的大客户是小米,消息优先被消费发短信通知)

0.最好用最大优先级为0-10,因为消费者让mq排序需要时间消耗cpu和内存
1.排队后,在生产者设置最大入队后出队参数,不然全部一个一个消费完,不能看到效果

   //生产者设置消息优先级 mq界面会出现Pri
   //设置优先队列的方法
          map(发消息channel): 
                HashMap<String, Object> arg = new HashMap<>();
    arg.put("x-max-priority",10);
    channel.queueDeclare(QUEUE_NAME,Duration,false,false,arg);
    //或者
    AMQP.BasicProperties properties = new
            AMQP.BasicProperties().builder().priority(10).build();

完整代码
------生产者----------

          public class AckPriorMsg {
    public static final String QUEUE_NAME="hello1";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Channel channel = MQRabbitUtil.getChannel();
        channel.confirmSelect();//代表要确认磁盘中mq已经存储了数据,先写



        boolean Duration=true;
        //队列名
        //队列消息是否持久化到磁盘,默认在内存中
        //队列是否只供一个消费者消费(不共享)
        //是否自动删除,开新队列
        //其他参数
        HashMap<String, Object> arg = new HashMap<>();
        arg.put("x-max-priority",10);
        channel.queueDeclare(QUEUE_NAME,Duration,false,false,arg);
        AMQP.BasicProperties properties = new
                AMQP.BasicProperties().builder().priority(10).build();
        for (int i = 1; i <11; i++) {


            String message = "info" + i;
            if (i == 5) { //默认优先级是5,这里相当于是发送了不同优先级的消息
                channel.basicPublish("", QUEUE_NAME, properties, message.getBytes());
            } else{
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            }
            //交换机
            //队列名
            //设置消息持久化
            //二进制

                boolean flag = channel.waitForConfirms();
                if (flag) {
                System.out.println("消息已经写入磁盘的确认");

            }
        }

    }
}

------消费者----------

public class ConsumerPrior1 {
    public static final String QUEUE_NAME="hello1";
    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = MQRabbitUtil.getChannel();
        Map<String, Object> params = new HashMap();
        //设置了才是优先队列
        params.put("x-max-priority", 10);
        channel.basicQos(1);
        channel.queueDeclare(QUEUE_NAME, true, false, false, params);
        DeliverCallback deliverCallback=(consumerTag, delivery)->{
            SleepUtils.sleep(1);
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
            System.out.println("消息处理快接收:"+new String(delivery.getBody(),"UTF-8"));
            System.out.println(consumerTag);
            //确认的标志
            //是否批量应答



        };
        CancelCallback cancelCallback=(var1)->{

            System.out.println("应答失败");
        };
        //消费, 项目队列名,是否自动应答!!!(就是要手动处理还是被动处理)
        //失败的回调,消费者取消消费的回调(都要lambda表达式)
        boolean IsAck=false;
        channel.basicConsume(QUEUE_NAME, false, deliverCallback, cancelCallback);
        System.out.println("worker1正在等待接收消息");
    }

}

–重要------------springboot版本--------
—优先队列springboot配置文件要设置为手动确认和交换机是direct----
//写个方便设置优先级的工具类

@Component
@AllArgsConstructor
public class FileMessageSender {
 
    private static final String EXCHANGE = "priority-exchange";
 
    private static final String ROUTING_KEY_PREFIX = "priority.queue.test";
    @Autowired
    private final RabbitTemplate rabbitTemplate;
 
    /**
     * 发送设置有优先级的消息
     *
     * @param priority 优先级
     */
    public void sendPriorityMessage(String content, Integer priority) {
        rabbitTemplate.convertAndSend(EXCHANGE, ROUTING_KEY_PREFIX , content,
                message -> {
                    message.getMessageProperties().setPriority(priority);
                    return message;
                });
    }
 
}

-------------下面是不用工具类的代码,由于我看了许多博客才写成的下面的代码------------
----配置类定义配置—

@Configuration
public class ConfirmConfig {
 //----------优先队列-----------
 public static final String BUSINESS_ROUTING_KEY = "rk.001";
 public static final String BUSINESS_QUEUE = "business.queue";
 public static final String BUSINESS_EXCHANGE = "business.exchange";

 /**
  * 声明业务队列的交换机
  */
 @Bean
 public DirectExchange directExchange() {
  return new DirectExchange(BUSINESS_EXCHANGE);
 }


 /**
  * 声明业务队列
  */
 @Bean
 public Queue priorityQueue() {
  Map<String, Object> args = new HashMap<>();
  // 设置队列最大优先级
  args.put("x-max-priority", 10);
  args.put("x-queue-mode", "lazy"); //随便定义惰性队列(在磁盘取数据,而不是内存) 可以和优先队列一起使用
  return QueueBuilder.durable(BUSINESS_QUEUE).withArguments(args).build();
 }

 /**
  * 声明业务队列和业务交换机的绑定关系
  */
 @Bean
 public Binding businessBinding(Queue priorityQueue, DirectExchange directExchange) {
  return BindingBuilder.bind(priorityQueue).to(directExchange).with(BUSINESS_ROUTING_KEY);
 }

-------生产者设置队列优先级--------

@RestController
@RequestMapping("/confirm")
@Slf4j
public class ProducerController {
 public static final String CONFIRM_EXCHANGE_NAME = "business.exchange";
 @Autowired
 private RabbitTemplate rabbitTemplate;



 @GetMapping("sendMessage/{message}")
 public void sendMessage(@PathVariable String message){

 //指定消息 id 为 1
// CorrelationData correlationData1=new CorrelationData("1");
 String routingKey="rk.001";
//
//rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME,routingKey,message+routingKey,correlationData1);
// CorrelationData correlationData2=new CorrelationData("2");
// routingKey="key2";
// //这样只知道交换机有问题,而不知道路由有问题
// rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME,routingKey,message+routingKey,correlationData2);
//  log.info("发送消息内容:{}",message);

  for (int i = 0; i < 10000; i++) {
    if(i==5){
     rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME, routingKey, "消息来自 ttl 为 xS 的队列: "+message+i, correlationData ->{
      correlationData.getMessageProperties().setPriority(100);
      return correlationData;
     });
    }else if(i==1){
    rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME, routingKey, "消息来自 ttl 为 xS 的队列: "+message+i, correlationData ->{
     correlationData.getMessageProperties().setPriority(6);
     return correlationData;
    });
   }else {
     rabbitTemplate.convertAndSend(CONFIRM_EXCHANGE_NAME, routingKey, "消息来自 ttl 为 xS 的队列: "+message+i, correlationData ->{
         correlationData.getMessageProperties().setPriority(1);
         return correlationData;
     });
    }

  }


 }
}

-------消费者消费------

@Component
@Slf4j
public class ConfirmConsumer {
  public static final String CONFIRM_QUEUE_NAME = "business.queue";

  @RabbitListener(queues =CONFIRM_QUEUE_NAME)
  public void receiveMsg(String msg,Channel channel,Message message) throws IOException {
    HashMap<String, Object> arg = new HashMap<>();
    arg.put("x-max-priority",10); //配置类消费者都设置最大优先级
    arg.put("x-queue-mode", "lazy"); //配置类消费者都设置惰性队列,可以和优先队列一起使用
    channel.queueDeclare(CONFIRM_QUEUE_NAME,true,false,false,arg);
   log.info("接受到队列 confirm.queue 消息:{}",msg);
  }
}


  



}

5.惰性队列(消息保存在磁盘中,正常放在内存) 应用场景怕内存不够宕机,存在磁盘可以大量存储,缺点是性能不好(springboot完整代码如上)
内存只有索引,管理界面查看只是使用几kb内存,差不多完全存在磁盘了

           //生产者和消费者都加上参数
             HashMap<String, Object> arg = new HashMap<>();
        arg.put("x-max-priority",10);
        //惰性队列,存在磁盘中
         arg.put("x-queue-mode", "lazy");
        channel.queueDeclare(QUEUE_NAME,Duration,false,false,arg);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Word Embedding

One-hot-encoding 缺点 1.向量维度和向量个数很大&#xff0c;假设有1w个token的话&#xff0c;向量个数和维度就都是1w 2. 语义相近的词的向量并不相似 Word Embedding 核心思想&#xff1a;可以通过上下文理解单词的语义 predection-based方法 使用前一个单词预测下一个…

【机器学习】信息量、香农熵、信息增益

这节可以搭配 【机器学习】Logistic回归&#xff08;重新整理&#xff09;信息量&#xff08;信息&#xff09;信息量公式的推理过程 香农熵信息增益 【机器学习】Logistic回归&#xff08;重新整理&#xff09; B站视频&#xff1a;“交叉熵”如何做损失函数&#xff1f;打包…

Linux一学就会——编写自己的shell

编写自己的shell 进程程序替换 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行…

视觉震撼的数据可视化示例

众所周知&#xff0c;数据可以非常强大——当你真正理解它告诉你什么时。 数据和信息可视化(数据可视化或信息可视化)是对大量复杂的定量、定性数据、信息进行设计和创建易于沟通、易于理解的图形或视觉表示的实践&#xff0c;在静态、动态或交互式视觉项目的帮助下&#xff0…

存储网络架构——DAS、NAS、SAN、分布式组网架构

目录 DAS直连式存储 NAS网络附加存储 SAN存储 存储区域网络 分布式存储组网 DAS直连式存储 DAS遇到的挑战 NAS网络附加存储 向主机提供文件服务&#xff1b;文件系统由存储设备维护&#xff0c;用户访问文件系统&#xff0c;不直接访问底层存储 拥有所有主机上文件与底层存储空…

JS案例分析-某国际音x-tt-params参数分析

今天我们要分析的网站是&#xff1a;https://www.tiktok.com/selenagomez?langen&#xff0c;参数名字叫x-tt-params。 先来抓个包 这个接口是用户视频列表url&#xff0c;参数叫x-tt-params&#xff0c;该接口中还有其他参数像msToken&#xff0c;X-Bogus&#xff0c; _sig…

【51单片机】点亮一个LED灯(看开发板原理图十分重要)

&#x1f38a;专栏【51单片机】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【The Right Path】 &#x1f970;大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 &#x1f354;基础内容 &#x1f3f3…

项目集的定义及管理

一、什么是项目集 项目集是相互关联且被协调管理的项目、子项目集和项目集活动&#xff0c;以便获得分别管理所无法获 得的效益。 以项目集的形式管理项目、子项目集及项目集活动能确保项目集组件的战略和工作计划根据各组 件的成果做出相应调整&#xff0c;或者按照发起组织的…

洞车系统常见问题解决指南

洞车常见问题解决指南 1.研发脚本处理问题1.1 WMS出库单无法审核1.2 OMS入库单无法提交&#xff0c;提示更新中心库存失败1.3 当出现OMS下发成功WMS/TMS/DMS还没有任务的情况时处理方案1.4 调度波次生成或者添加任务系统异常1.5 东鹏出库单部分出库回传之后要求重传1.6 更新订单…

不会前端,怎么快速打造属于自己的个人博客?

个人博客 简介提前准备 一、初始化vuepress项目二、页面配置首页配置顶部配置顶部导航栏路由配置侧边导航栏配置 三、打包部署四、数据统计插槽自定义插槽配置整体结构页面效果 项目地址 简介 主要教大家如何快速搞一个属于自己的博客网站&#xff0c;特别是一些不怎么会前端的…

信息安全保障

文章目录 信息安全保障基础基本概念信息安全定义广义和狭义的信息安全问题信息安全问题的根源和特征情报威胁和态势感知信息安全保障基础信息安全属性信息安全视角 信息安全发展阶段通信安全计算机安全信息系统安全信息安全保障网络安全空间 信息安全保障新领域工业控制系统(IS…

【虹科案例】使用 TCP 分析测量握手时间

如何使用 Allegro Network 万用表的 TCP 分析确定握手时间 握手需要多少时间&#xff1f; 在图 1 中&#xff0c;您可以在虹科Allegro 网络万用表的 TCP 统计数据中看到过去 10 分钟的客户端握手次数。在这里&#xff0c;您可以清楚地看到在指定时间段内有延长的响应时间。但…

ChatGPT探索系列之五:讨论人工智能伦理问题及ChatGPT的责任

文章目录 前言一、安全二、隐私和道德三、我们应该做什么总结 前言 ChatGPT发展到目前&#xff0c;其实网上已经有大量资料了&#xff0c;博主做个收口&#xff0c;会出一个ChatGPT探索系列的文章&#xff0c;帮助大家深入了解ChatGPT的。整个系列文章会按照一下目标来完成&am…

给定一个文本文件,每行是一条股票信息,写程序提取出所有的股票代码

问题&#xff1a;给定一个文本文件&#xff0c;每行是一条股票信息&#xff0c;写程序提取出所有的股票代码。其中&#xff0c;股票代码规则是&#xff1a;6 位数字&#xff0c; 而且以.SH 或者.SZ 结尾。 文件内容示例&#xff1a; 2020-08-08;平安银行(000001.SZ);15.55;2940…

如何用ChatGPT做品牌联名方案策划?

该场景对应的关键词库&#xff08;15个&#xff09;&#xff1a; 品牌、个人IP、社交话题、联名策划方案、调研分析、市场影响力、资源互补性、产品体验、传播话题、视觉形象设计、合作职权分配、销售转化、曝光目标、宣发渠道、品牌形象 提问模板&#xff08;1个&#xff09;…

kubernetes项目部署

目录 ​一、容器交付流程 二、k8s平台部署项目流程 三、在K8s平台部署项目 一、容器交付流程 容器交付流程通常分为四个阶&#xff1a;开发阶段、持续集成阶段、应用部署阶段和运维阶段。 开发阶段&#xff1a;开发应用程序&#xff0c;编写Dockerfile; 持续集成阶段&#…

很佩服的一个Google大佬,离职了。。

这两天&#xff0c;科技圈又有一个突发的爆款新闻相信不少同学都已经看到了。 那就是75岁的计算机科学家Geoffrey Hinton从谷歌离职了&#xff0c;从而引起了科技界的广泛关注和讨论。 而Hinton自己也证实了这一消息。 提到Geoffrey Hinton这个名字&#xff0c;对于一些了解过…

Spring Cloud学习笔记【分布式配置中心-Config】

文章目录 SpringCloud Config概述概述传统方式弊端主要功能与GitHub整合配置 Config服务端配置与测试服务端配置(即Gitee上的配置文件)Config Demo配置Spring Cloud Config访问规则 Config客户端配置与测试bootstrap.yml说明Config客户端 Demo配置 SpringCloud Config概述 概述…

无需公网IP 使用SSH远程连接Linux CentOS服务器【内网穿透】

文章目录 视频教程1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程 本次教程我们来实现如何在外公网环境下&#xff0c;SSH远程连接家里/公司的Linux CentOS服务器&#xff0c;无需公网IP&#xff0c;也不需要设置…

深入理解Java虚拟机——垃圾收集器

1.前言 在前面我们已经说过了垃圾收集算法&#xff0c;那么现在我们要讲的垃圾收集器&#xff0c;实际上就是对垃圾收集算法的实践。 首先我们先看一张图&#xff0c;这张图可以帮助我们了解各款经典垃圾收集器之间的关系&#xff1a; 图中的垃圾收集器所在的区域代表了它是属…