华为云云耀云服务器L实例评测|RabbitMQ的Docker版本安装 + 延迟插件安装 QQ邮箱和阿里云短信验证码的主题模式发送

news2024/12/24 11:17:26

在这里插入图片描述

前言

最近华为云云耀云服务器L实例上新,也搞了一台来玩,期间遇到各种问题,在解决问题的过程中学到不少和运维相关的知识。

本篇博客介绍RabbitMQ的Docker版本安装和配置,延迟插件的安装;结合QQ邮箱和阿里云短信验证码服务,采用主题模式进行验证码的发送。

关于邮箱验证码和手机短信验证码可以参考以下博客

SpringBoot项目(验证码整合)——springboot整合email & springboot整合阿里云短信服务

在这里插入图片描述

其他相关的华为云云耀云服务器L实例评测文章列表如下:

  • 初始化配置SSH连接 & 安装MySQL的docker镜像 & 安装redis以及主从搭建 & 7.2版本redis.conf配置文件

  • 安装Java8环境 & 配置环境变量 & spring项目部署 &【!】存在问题未解决

  • 部署spring项目端口开放问题的解决 & 服务器项目环境搭建MySQL,Redis,Minio…指南

  • 由于自己原因导致MySQL数据库被攻击 & MySQL的binlog日志文件的理解

  • 认识redis未授权访问漏洞 & 漏洞的部分复现 & 设置连接密码 & redis其他命令学习

在这里插入图片描述

  • canal | 拉取创建canal镜像配置相关参数 & 搭建canal连接MySQL数据库 & spring项目应用canal初步

  • canal | 基于canal缓存自动更新流程 & SpringBoot项目应用案例和源码

  • Docker版的Minio安装 & Springboot项目中的使用 & 结合vue进行图片的存取

  • 在Redis的Docker容器中安装BloomFilter & 在Spring中使用Redis插件版的布隆过滤器

在这里插入图片描述

  • Elasticsearch的Docker版本的安装和参数设置 & 端口开放和浏览器访问

  • Elasticsearch的可视化Kibana工具安装 & IK分词器的安装和使用

  • Elasticsearch的springboot整合 & Kibana进行全查询和模糊查询

引出


1.RabbitMQ的Docker版本安装和配置,延迟插件的安装;
2.结合QQ邮箱和阿里云短信验证码服务,采用主题模式进行验证码的发送;

RabbitMQ的Docker版本安装

1.拉取镜像创建容器

docker pull rabbitmq

在这里插入图片描述

docker run -itd --name=rabbitmq_pet \
-e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123 \
-p 15672:15672 -p 5672:5672 rabbitmq

在这里插入图片描述

查看rabbitmq的版本,3.9.11

在这里插入图片描述

docker ps查看当前运行的容器

在这里插入图片描述

2.开放端口和访问

华为云控制台开放端口

在这里插入图片描述

前端访问报错

在这里插入图片描述

无法显示页面

在这里插入图片描述

打开管理页面

前端访问,需要打开管理页面

在这里插入图片描述

 docker exec -it rabbitmq_pet bash
rabbitmq-plugins enable rabbitmq_management

输入用户名密码,进入rabbitmq管理页面

在这里插入图片描述

允许查看channels

在这里插入图片描述

进入rabbitmq容器进行修改

在这里插入图片描述

 cd /etc/rabbitmq/conf.d/
echo management_agent.disable_metrics_collector=false > management_agent.disable_metrics_collector.conf

在这里插入图片描述

重启后可以进入channels页面

在这里插入图片描述

3.安装延迟插件

下载支持3.9.x的插件

https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases?after=rabbitmq_v3_6_12

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

一开始没有延迟插件,需要安装一下延迟插件

上传延迟插件到文件夹

在这里插入图片描述

docker cp ./rabbitmq_delayed_message_exchange-3.9.0.ez rabbitmq_pet:/plugins

进入容器,允许延迟插件

在这里插入图片描述

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

刷新前端页面,延迟插件安装成功

在这里插入图片描述

安装成功

在这里插入图片描述

使用rabbitmq进行验证码的发送

在这里插入图片描述

1.依赖导入

        <!--        rabbitmq queue的包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

在这里插入图片描述

spring:
  rabbitmq:
    host: 124.70.138.34
    port: 5672
    username: admin
    password: 123
    # 确认收到
    publisher-confirm-type: correlated
    publisher-returns: true

2.配置文件

在这里插入图片描述

package com.tianju.fresh.config;


import com.tianju.fresh.util.RabbitMQConstance;
import org.springframework.amqp.core.*;
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;


import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;


/**
 * rabbitmq的配置类
 */
@Configuration
public class RabbitMQConfig {

    /**
     * 创建邮箱的队列
     * @return 邮箱的rabbitmq队列
     */
    @Bean
    public Queue emailQueue(){
        return new Queue(
                RabbitMQConstance.MQ_EMAIL_QUEUE,
                RabbitMQConstance.durable,
                RabbitMQConstance.exclusive,
                RabbitMQConstance.autoDelete
        );
    }

    /**
     * 电话队列
     * @return 电话的队列
     */
    @Bean
    public Queue phoneQueue(){
        return new Queue(
                RabbitMQConstance.MQ_PHONE_QUEUE,
                RabbitMQConstance.durable,
                RabbitMQConstance.exclusive,
                RabbitMQConstance.autoDelete
        );
    }

    /**
     * 队列的交换机fanout
     * @return 队列的交换机fanout
     */
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange(
                RabbitMQConstance.MQ_FANOUT_EXCHANGE,
                RabbitMQConstance.durable,
                RabbitMQConstance.autoDelete
        );
    }

    /**
     * 主题模式topic的交换机
     * @return 主题模式的topic交换机
     */
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange(
                RabbitMQConstance.MQ_TOPIC_EXCHANGE,
                RabbitMQConstance.durable,
                RabbitMQConstance.autoDelete
        );
    }


    /**
     * ######################建立队列和交换机的绑定关系 ###################
     */

    /**
     * 建立邮箱队列和交换机的绑定关系
     * @return 绑定的关系
     */
    @Bean
    public Binding emailBlinding(){
        return BindingBuilder.bind(emailQueue())
                .to(topicExchange())
                .with("topic.email");
    }


    /**
     * 建立电话队列和交换机的绑定关系
     * @return
     */
    @Bean
    public Binding phoneBlinding(){
        return BindingBuilder.bind(phoneQueue())
                .to(topicExchange())
                .with("topic.phone");
    }

    /**
     * ###################### 对象转换成json字符串进行发送 ##############
     */

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter());// 修改转换器
        return rabbitTemplate;
    }

}

在这里插入图片描述

package com.tianju.fresh.util;

/**
 * 项目中用的常量
 */
public interface RabbitMQConstance {

    /**
     * rabbitmq相关的常量
     */
    String MQ_EMAIL_QUEUE="mq_email_queue";
    String MQ_PHONE_QUEUE="mq_phone_queue";
    String MQ_FANOUT_EXCHANGE="mq_fanout_exchange";
    String MQ_TOPIC_EXCHANGE="mq_topic_exchange";

    // 参数 String name, boolean durable, boolean exclusive, boolean autoDelete
    boolean durable = true; // 表示队列是否持久化
    boolean exclusive = false; // 是否排它式
    boolean autoDelete = false;

}

3.发送的业务service代码

在这里插入图片描述

    /**
     * ########################## 用户注册需要的东西 ###################
     */
    HttpResp getRegisterSMSCode(String input);

    /**
     * 发送消息给主题模式的交换机,
     * 交换机将消息转发给邮箱队列
     * @param smsDto
     */
    void sendToEmailQueue(SMSDto smsDto);

    /**
     * 发送消息给主题模式的交换机,
     * 交换机把消息发送给电话队列
     * @param smsDto
     */
    void sendToPhoneQueue(SMSDto smsDto);

service接口的实现

    @Override
    public HttpResp getRegisterSMSCode(String input) {
        // 1.输入邮箱或者手机号码是否合法
        // 2.redis里面是否有
        // 3.根据邮箱 或 手机 发送不同的消息给交换机
        // 4.返回给前端验证码

        if(!SMSUtil.isEmailOrPhone(input)){
            return HttpResp.failed("输入的邮箱 或 手机号码不合法");
        }

        if (redisUtil.isKeyInRedis(input)){
            return HttpResp.failed("验证码已发送,请检查,稍后重试");
        }

        String code = SMSUtil.getCode();

        if (SMSUtil.isEmail(input)){ // 发送邮箱验证码
            sendToEmailQueue(
                    new SMSDto(input,"邮箱验证码",code)
            );
            Map map = new HashMap();
            map.put(input, code);
            return HttpResp.success(map);
        }

        sendToPhoneQueue(
                new SMSDto(input, null, code)
        );
        return HttpResp.success(code);
    }

    @Override
    public void sendToEmailQueue(SMSDto smsDto) {
        // 发送消息给邮箱,邮箱验证码
        rabbitTemplate.convertAndSend(
                RabbitMQConstance.MQ_TOPIC_EXCHANGE,
                "topic.email",
                smsDto
        );
        log.debug("{} [ 邮箱验证码 生产者向 主题模式交换机: ] 发送一条消息 {}",new Date(), smsDto);
    }

    @Override
    public void sendToPhoneQueue(SMSDto smsDto) {
        // 生产者:发送消息给主题交换机,主题交换机把消息给电话队列
        // 消费者:监听电话队列,如果电话队列有消息,就进行消费,调用发送电话验证码的方法,发送手机验证码
        rabbitTemplate.convertAndSend(
                RabbitMQConstance.MQ_TOPIC_EXCHANGE,
                "topic.phone",
                smsDto
        );
        log.debug("{} [ 手机验证码 生产者向 主题模式交换机: ] 发送一条消息 {}",new Date(), smsDto);
    }

smsDto实体类,进行验证码对象传输

在这里插入图片描述

package com.tianju.fresh.entity.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SMSDto {
    private String target;// 目标邮箱或者手机号码
    private String msg; // 补充的消息
    private String code; // 验证码
}

4.监听队列消息发送验证码

在这里插入图片描述

package com.tianju.fresh.rabbitMQ;

import com.tianju.fresh.entity.dto.SMSDto;

/**
 * 监听手机验证码 邮箱 的队列
 * 如果有消息,就进行消费 发送验证码
 */
public interface ConsumerService {

    /**
     * 调用给邮箱发送验证码
     * @param smsDto 数据传输层对象,包含目标邮箱,消息,验证码
     */
    void callSendToEmail(SMSDto smsDto);

    /**
     * 调用给手机发送短信验证码
     * @param smsDto 数据传输层对象,包含目标手机号码,验证码
     */
    void callSendToPhone(SMSDto smsDto);
}

接口代码的实现类

package com.tianju.fresh.rabbitMQ.impl;

import com.tianju.fresh.entity.dto.SMSDto;
import com.tianju.fresh.rabbitMQ.ConsumerService;
import com.tianju.fresh.util.RabbitMQConstance;
import com.tianju.fresh.util.RedisUtil;
import com.tianju.fresh.util.SMSUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
@Slf4j
public class ConsumerServiceImpl implements ConsumerService {

    @Autowired
    private SMSUtil smsUtil;

    @Autowired
    private RedisUtil redisUtil;

    @RabbitListener(queues = RabbitMQConstance.MQ_EMAIL_QUEUE)
    @Override
    public void callSendToEmail(SMSDto smsDto) {
        log.debug("[ 邮箱队列 消费者模块:] 在{} 获得一条消息{},即将发送邮箱验证码",new Date(),smsDto);
        smsUtil.sendEmailCode(smsDto.getTarget(),smsDto.getMsg(),smsDto.getCode());
        // 存到redis里面,有效时间是5分钟
        redisUtil.saveStringValue(smsDto.getTarget(), smsDto.getCode(), 60*5);
        log.debug("邮箱验证码存到redis中,有效期为 5分钟");
    }

    @RabbitListener(queues = RabbitMQConstance.MQ_PHONE_QUEUE)
    @Override
    public void callSendToPhone(SMSDto smsDto) {
        log.debug("[ 电话队列 消费者模块:] 在{} 获得一条消息{},即将发送手机验证码",new Date(),smsDto);
        smsUtil.sendPhoneCode(smsDto.getTarget(), smsDto.getCode());
        // 验证码存到redis中
        redisUtil.saveStringValue(smsDto.getTarget(), smsDto.getCode(), 60*5);
        log.debug("手机验证码存到redis中,有效期为 5分钟");
    }
}

5.用到的工具类

在这里插入图片描述

package com.tianju.fresh.util;

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@PropertySource("classpath:config/ali.properties")
@Slf4j
public class SMSUtil {

    // 阿里云短信服务的配置
    @Value("${ali.msg.AccessIdKey}")
    private String AccessIdKey;
    @Value("${ali.msg.AccessKeySecret}")
    private String AccessKeySecret;

    // QQ邮箱的配置
    @Value("${spring.mail.username}")
    private String from;
    @Resource
    private JavaMailSender javaMailSender;

    /**
     * 发送手机验证码
     * @param tel 接收验证码的手机号码
     * @param code 验证码
     */
    public void sendPhoneCode(String tel,String code) {
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-hangzhou",
                AccessIdKey, //AccessIdKey
                AccessKeySecret); //AccessKey Secret
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        //下面这3个不要改动
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");
        //接收短信的手机号码
        request.putQueryParameter("PhoneNumbers",tel);//此处写电话号码
        //短信签名名称
        request.putQueryParameter("SignName","阿里云短信测试");
        //短信模板ID
        request.putQueryParameter("TemplateCode","SMS_154950909");
        //短信模板变量对应的实际值 ${code} 中的值
        Map<String,String> param = new HashMap<>(2);
        param.put("code", String.valueOf(code)); //写入的短信内容,验证码
        request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));
        try {
            CommonResponse response = client.getCommonResponse(request);
            System.out.println(response.getData());
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            Date date = new Date();
            String formattedDate = sdf.format(date);
            log.debug("在 {} 时,发送一条短信验证码[ {} ]给手机 {}",formattedDate,code,tel);
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            e.printStackTrace();
        }
    }



    /**
     * 发送qq邮箱的代码
     * @param email 接收邮件信息的邮箱地址
     * @param subject 邮件的主题:标题
     * @param content 邮件的内容:你的验证码是3927
     */
    public void sendEmailCode(String email, String subject, String content) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setSubject(subject);
        mailMessage.setTo(email);
        mailMessage.setText(content);
        mailMessage.setSentDate(new Date());
        mailMessage.setFrom(from);
        javaMailSender.send(mailMessage);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String formattedDate = sdf.format(mailMessage.getSentDate());
        log.debug("在 {} 发送一条邮件[ {} ]给 {}",formattedDate,mailMessage.getText(),mailMessage.getTo());
    }

    /**
     * 判断是不是合法的手机号码
     * @param input 待判断的手机号码
     * @return
     */
    public static boolean isPhoneNumber(String input) {
        String pattern = "^1[3456789]\\d{9}$";
        Pattern regex = Pattern.compile(pattern);
        Matcher matcher = regex.matcher(input);
        return matcher.matches();
    }

    /**
     * 判断是不是合法的邮箱
     * @param input 待判断的邮箱
     * @return
     */
    public static boolean isEmail(String input) {
        String pattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
        Pattern regex = Pattern.compile(pattern);
        Matcher matcher = regex.matcher(input);
        return matcher.matches();
    }

    /**
     * 判断是不是合法的手机号码,或者邮箱
     * @param input
     * @return
     */
    public static boolean isEmailOrPhone(String input){
        if (isPhoneNumber(input)){
            return true;
        }else return isEmail(input);
    }

    /**
     * 获取随机生成的4位密码
     * @return 随机生成的4位密码
     */
    public static String getCode(){
        Random random = new Random();
        return random.nextInt(900000) + 100000 +"";
    }

    public static void main(String[] args) {
        System.out.println(getCode());
    }
}

在这里插入图片描述

package com.tianju.fresh.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Set;
import java.util.concurrent.TimeUnit;


@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void  saveObjectToRedis(String key,Object json){
        redisTemplate.opsForValue().set(key,json);
    }

    /**
     * 从redis里面获取json对象,如果没有,返回null
     * @param key
     * @return
     */
    public Object getJsonFromRedis(String key){
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除redis里面的值,键
     * @param key 删除的键
     * @return
     */
    public Boolean deleteKey(String key){
        return redisTemplate.delete(key);
    }

    /**
     * 存到Redis里面的 set 中
     * @param key 存的键
     * @param value 存到set的值
     */
    public void saveSetToRedis(String key,String... value){
        for (String s:value){
            if (s!=null && !s.equals("")){
                stringRedisTemplate.opsForSet().add(key, s);
            }
        }
    }

    /**
     * 判断是否在redis的set中
     * @param key
     * @param val
     * @return
     */
    public Boolean isInSet(String key,String val){
        return stringRedisTemplate.opsForSet().isMember(key, val);
    }

    /**
     * 获得set中的值
     * @param key 键
     * @return
     */
    public Set<String> getSet(String key){
        return stringRedisTemplate.opsForSet().members(key);
    }

    /**
     * 从redis里面的set删除数据
     * @param key
     * @param val
     */
    public void removeFromSet(String key,String val){
        stringRedisTemplate.opsForSet().remove(key, val);
    }



    /**
     * 获得存到redis里面的键对应的string类型的值
     * @param key 键
     * @return
     */
    public String getStringValue(String key){
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 保存到redis里面string
     * @param key
     * @param timeout 过期时间,传的是多少s之后过期
     * @param val
     */
    public void saveStringValue(String key,String val,Integer... timeout){
        if (timeout==null){
            stringRedisTemplate.opsForValue().set(key,val);
        }else {
            stringRedisTemplate.opsForValue().set(key,val,timeout[0], TimeUnit.SECONDS);
        }
    }

    /**
     * 看某个键是否存在于Redis中
     * @param key 待检验的键
     * @return
     */
    public Boolean isKeyInRedis(String key){
        return stringRedisTemplate.hasKey(key);
    }
}

6.controller层代码

在这里插入图片描述

package com.tianju.fresh.controller;

import com.tianju.fresh.entity.vo.LoginVo;
import com.tianju.fresh.resp.HttpResp;
import com.tianju.fresh.service.CustomerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/user")
@Slf4j
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @PostMapping("/login")
    public HttpResp login(@RequestBody LoginVo loginVo){
        log.debug("登陆前端传的参数>>>"+loginVo);
        return customerService.login(loginVo);
    }

    @GetMapping("/login/getSMSCode/{str}")
    public  HttpResp getSMSCode(@PathVariable("str") String str){
        log.debug("即将给邮箱/手机 "+str+"发送验证码");
        return customerService.getLoginSMSCode(str);
    }

    @GetMapping("/register/getSMSCode/{str}")
    public  HttpResp getRegisterSMSCode(@PathVariable("str") String str){
        log.debug("即将给邮箱/手机 "+str+"发送验证码");
        return customerService.getRegisterSMSCode(str);
    }
}

效果展示

项目启动后,主题模式的交换机和两个队列初始化成功

在这里插入图片描述

邮箱队列和电话队列

在这里插入图片描述

交换机和队列之间的绑定关系

在这里插入图片描述

手机验证码

调用controller层代码后,后台打印日志

在这里插入图片描述

手机验证码存储到Redis里面,有效期5分钟

在这里插入图片描述

获得阿里云发送的短信

在这里插入图片描述

邮箱验证码

调用controller层接口发送邮箱验证码

在这里插入图片描述

邮箱验证码存到Redis里面

在这里插入图片描述

收到邮箱验证码

在这里插入图片描述


总结

1.RabbitMQ的Docker版本安装和配置,延迟插件的安装;
2.结合QQ邮箱和阿里云短信验证码服务,采用主题模式进行验证码的发送;

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

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

相关文章

Gurobi设置初始可行解

目录 1. 决策变量的Start属性直接设置变量的初始值 1.1 Start&#xff1a;MIP变量的起始值&#xff08;初值&#xff09;double类型&#xff0c;可更改 1.2 StartNodeLimit&#xff1a;限制了在完善一组输入部分变量的初始解时&#xff0c;MIP所探索的分支定界的节点的数量 …

SAAS软件营销——利用人工智能帮助企业了解客户

软件营销部队(Salesforce)是全球领先的客户关系管理(CRM)解决方案供应商之一&#xff0c;其产品和服务旨在帮助企业成长&#xff0c;并追踪与客户的关系。 在1999 年成立时&#xff0c;它就率先提出了通过互联网(现在通常称为“云”)实现“软件即服务”(SaaS)的概念。 SaaS提…

周期性触发的自定义触发器

背景 本文我们实现一个周期性触发的自定义触发器&#xff0c;顺便看下实现自定义触发器的一些要点 周期性触发器实现 实现一个每分钟触发一次的自定义事件时间触发器&#xff0c;实现代码和注意事项如下所示 package wikiedits.trigger;import org.apache.flink.api.common…

线程通信初始

简单认识一下线程通信 目录 简单认识一下线程通信线程通信定义线程通信模型之一释疑示例案例案例要求案例简单实现 拓展等待和唤醒API 参考视频 注&#xff1a;线程通信 前提是 线程安全 线程通信定义 当多个线程共同操作共享的资源时&#xff0c;线程间通过某种方式互相告知自…

【算法训练-二分查找 三】【特殊二分】寻找峰值

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【数组的二分查找】&#xff0c;使用【数组】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为…

Vue3+TS+ECharts5实现中国地图数据信息显示

1.引言 最近在做一个管理系统&#xff0c;主要技术栈使用的是Vue3TSViteElementPlus&#xff0c;主要参考项目是yudao-ui-admin-vue3&#xff0c;其中用到ECharts5做数字大屏&#xff0c;展示中国地图相关信息&#xff0c;以此基础做一个分享&#xff0c;写下这篇文章。 &quo…

解决ASP.NET Core的中间件无法读取Response.Body的问题

概要 本文主要介绍如何在ASP.NET Core的中间件中&#xff0c;读取Response.Body的方法&#xff0c;以便于我们实现更多的定制化开发。本文介绍的方法适用于.Net 3.1 和 .Net 6。 代码和实现 现象解释 首先我们尝试在自定义中间件中直接读取Response.Body&#xff0c;代码如…

Appleid苹果账号自动解锁改密(自动解锁二验改密码)

目前该项目能实现以下功能&#xff1a; 多用户使用&#xff0c;权限控制多账号管理账号分享页&#xff0c;支持设置密码、有效期、自定义HTML内容自动解锁与关闭二步验证自动/定时修改密码自动删除Apple ID中的设备代理池与Selenium集群&#xff0c;提高解锁成功率允许手动触发…

先输入列,再输入行

想要的表格行数和列数是 5 行 5 列&#xff0c;以下是相应的代码实现&#xff1a; # 定义表格行数和列数 rows 5 cols 5# 创建一个二维列表作为表格 table [[ for j in range(cols)] for i in range(rows)]print("请输入表格数据&#xff1a;")while True:# 获取…

Python无废话-办公自动化Excel读取操作

openpyxl模块介绍 openpyxl是一个用于处理Excel文件的Python库,用于读取/写入Excel2010 xlsx/xlsm/xltx/xltm文件(不支持xls格式)。通过使用openpyxl库&#xff0c;可 以轻松地在Python程序中实现对Excel文件的操作。 openpyxl 安装 方式1&#xff1a;使用pip 命令安装&…

操作系统学习笔记1

文章目录 1、OS的一个宏观比喻2、OS的目的和功能3、OS的发展4、OS的运行机制5、OS的特征6、OS的体系结构 参考视频&#xff1a;操作系统 1、OS的一个宏观比喻 2、OS的目的和功能 3、OS的发展 4、OS的运行机制 中断、系统调用、异常。 5、OS的特征 6、OS的体系结构

【逐步剖C++】-第二章-C++类和对象(下)

前言&#xff1a;本文是对类和对象知识点的最后一篇总结&#xff0c;前两篇的链接如下&#xff1a; 【逐步剖C】-第二章-C类和对象&#xff08;上&#xff09; 【逐步剖C】-第二章-C类和对象&#xff08;中&#xff09; 这三篇加起来就是笔者学习在类和对象中的所有总结了&…

计算机网络网络层、应用层、数据链路层协议详解

目录 一、计算机网络 二、网络层 三、应用层 四、数据链路层 一、计算机网络 计算机网络是将多台计算机和其他网络设备通过通信链路连接起来&#xff0c;以实现数据交换和资源共享的系统。它是现代信息社会的基础设施之一&#xff0c;为人们提供了快速、可靠、安全的数据传…

学校项目培训之Carla仿真平台之Carla学习内容

一、Blender Blender入门&#xff1a;https://www.bilibili.com/video/BV1fb4y1e7PD/ Blender导入骨骼&#xff1a;https://www.bilibili.com/video/BV1hc41157nL 做一个车&#xff1a;https://www.bilibili.com/video/BV1hY411q7w2 二、Roadrunner RoadRunner Scenario…

Flink状态管理与检查点机制

1.状态分类 相对于其他流计算框架,Flink 一个比较重要的特性就是其支持有状态计算。即你可以将中间的计算结果进行保存,并提供给后续的计算使用: 具体而言,Flink 又将状态 (State) 分为 Keyed State 与 Operator State: 1.1 算子状态 算子状态 (Operator State):顾名思义…

计组—— I/O系统

&#x1f4d5;&#xff1a;参考王道课件 目录 一、I/O系统的基本概念 1.什么是“I/O”&#xff1f; ​编辑2.主机如何和I/O设备进行交互&#xff1f; 3.I/O控制方式 &#xff08;1&#xff09;程序查询方式 &#xff08;2&#xff09;程序中断方式 &#xff08;3&#x…

【MATLAB源码-第41期】基于压缩感知算法的OFDM系统信道估计和LS算法对比仿真。

操作环境&#xff1a; MATLAB 2013b 1、算法描述 压缩感知&#xff08;Compressed Sensing, CS&#xff09;是一种从稀疏或可压缩信号中重构完整信号的数学理论和技术。下面详细介绍压缩感知和它在OFDM信道估计中的应用。 1. 压缩感知基本概念 在传统采样理论中&#xff0…

数字电路逻辑与设计 之循环码和 移存码

有发现错误的能力&#xff0c;不能纠正 只能检查单次的错误&#xff0c;不能完全抗干扰 可以按照上面的方法来循环构造 移存码可以通过前推后推来实现

pytorch_神经网络构建1

文章目录 pytorch简介神经网络基础分类问题分析:逻辑回归模型逻辑回归实现多层神经网络多层网络搭建保存模型 pytorch简介 为什么神经网络要自定义数据类型torch.tensor? tensor可以放在gpu上训练,支持自动求导,方便快速训练,同时支持numpy的运算,是加强版,numpy不支持这些 为…

C++项目:【高并发内存池】

文章目录 一、项目介绍 二、什么是内存池 1.池化技术 2.内存池 3.内存池主要解决的问题 4.malloc 三、定长的内存池 四、高并发内存池整体框架设计 1.高并发内存池--thread cache 1.1申请内存&#xff1a; 1.2释放内存&#xff1a; 1.3用TLS实现thread cache无锁访…