Redis - 订阅发布替换 Etcd 解决方案
为了减轻项目的中间件臃肿,由于我们项目本身就应用了 Redis,正好 Redis 的也具备订阅发布监听的特性,正好应对 Etcd 的功能,所以本次给大家讲解如何使用 Redis 消息订阅发布来替代 Etcd 的解决方案。接下来,我们先看 Redis 订阅发布的常见情景……
Redis 订阅发布公共类
RedisConfig.java
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.net.UnknownHostException;@Configuration
@ComponentScan({"cn.hutool.extra.spring"})
public class RedisConfig {@BeanRedisMessageListenerContainer container (RedisConnectionFactory redisConnectionFactory){RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(redisConnectionFactory);return container;}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {RedisTemplate<String, Object> template = new RedisTemplate();// 连接工厂template.setConnectionFactory(redisConnectionFactory);// 序列化配置Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// 配置具体序列化// key采用string的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key采用string的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value序列化采用jacksontemplate.setValueSerializer(objectJackson2JsonRedisSerializer);// hash的value序列化采用jacksontemplate.setHashValueSerializer(objectJackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}
}
RedisUtil.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisUtil {@Resourceprivate RedisTemplate redisTemplate;/*** 消息发送* @param topic 主题* @param message 消息*/public void publish(String topic, String message) {redisTemplate.convertAndSend(topic, message);}
}
application.yml
server:port: 7077
spring:application:name: redis-demoredis:host: localhosttimeout: 3000jedis:pool:max-active: 300max-idle: 100max-wait: 10000port: 6379
RedisController.java
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;/*** @author Lux Sun* @date 2023/9/12*/
@RestController
@RequestMapping("/redis")
public class RedisController {@Resourceprivate RedisUtil redisUtil;@PostMappingpublic String publish(@RequestParam String topic, @RequestParam String msg) {redisUtil.publish(topic, msg);return "发送成功: " + topic + " - " + msg;}
}
一、业务情景:1 个消费者监听 1 个 Topic
教程三步走(下文业务情景类似不再描述)
- 实现接口 MessageListener
- 消息订阅,绑定业务 Topic
- 重写 onMessage 消费者业务方法
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisReceiver1 implements MessageListener {@Resourceprivate RedisMessageListenerContainer container;/*** 重点关注这方法, 进行消息订阅*/@PostConstructpublic void init() {MessageListenerAdapter adapter = new MessageListenerAdapter(this);// 绑定 Topic 语法为正则表达式container.addMessageListener(adapter, new PatternTopic("topic1.*"));}@Overridepublic void onMessage(Message message, byte[] bytes) {String key = new String(message.getChannel());String value = new String(message.getBody());log.info("Key: {}", key);log.info("Value: {}", value);}
}
测试
curl --location '127.0.0.1:7077/redis' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'topic=topic1.msg' \
--data-urlencode 'msg=我是消息1'
结果
2023-11-15 10:22:38.445 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Key: topic1.msg
2023-11-15 10:22:38.445 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Value: "我是消息1"
二、业务情景:1 个消费者监听 N 个 Topic
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisReceiver1 implements MessageListener {@Resourceprivate RedisMessageListenerContainer container;/*** 重点关注这方法, 进行消息订阅*/@PostConstructpublic void init() {MessageListenerAdapter adapter = new MessageListenerAdapter(this);// 绑定 Topic 语法为正则表达式container.addMessageListener(adapter, new PatternTopic("topic1.*"));// 只需再绑定业务 Topic 即可container.addMessageListener(adapter, new PatternTopic("topic2.*"));}@Overridepublic void onMessage(Message message, byte[] bytes) {String key = new String(message.getChannel());String value = new String(message.getBody());log.info("Key: {}", key);log.info("Value: {}", value);}
}
测试
curl --location '127.0.0.1:7077/redis' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'topic=topic2.msg' \
--data-urlencode 'msg=我是消息2'
结果
2023-11-15 10:22:38.445 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Key: topic2.msg
2023-11-15 10:22:38.445 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Value: "我是消息2"
三、业务情景:N 个消费者监听 1 个 Topic
我们看一下,现在又新增一个 RedisReceiver2,按理讲测试的时候,RedisReceiver1 和 RedisReceiver2 会同时收到消息
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisReceiver2 implements MessageListener {@Resourceprivate RedisMessageListenerContainer container;/*** 重点关注这方法, 进行消息订阅*/@PostConstructpublic void init() {MessageListenerAdapter adapter = new MessageListenerAdapter(this);// 绑定 Topic 语法为正则表达式container.addMessageListener(adapter, new PatternTopic("topic1.*"));}@Overridepublic void onMessage(Message message, byte[] bytes) {String key = new String(message.getChannel());String value = new String(message.getBody());log.info("Key: {}", key);log.info("Value: {}", value);}
}
测试
curl --location '127.0.0.1:7077/redis' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'topic=topic1.msg' \
--data-urlencode 'msg=我是消息1'
结果
2023-11-15 10:22:38.445 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Key: topic1.msg
2023-11-15 10:22:38.449 INFO 59189 --- [ container-3] com.xxx.redis.demo.RedisReceiver2 : Key: topic1.msg
2023-11-15 10:22:38.545 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Value: "我是消息1"
2023-11-15 10:22:38.645 INFO 59189 --- [ container-3] com.xxx.redis.demo.RedisReceiver2 : Value: "我是消息1"
四、业务情景:N 个消费者监听 N 个 Topic
都到这阶段了,应该不难理解了吧~
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisReceiver2 implements MessageListener {@Resourceprivate RedisMessageListenerContainer container;/*** 重点关注这方法, 进行消息订阅*/@PostConstructpublic void init() {MessageListenerAdapter adapter = new MessageListenerAdapter(this);// 绑定 Topic 语法为正则表达式container.addMessageListener(adapter, new PatternTopic("topic1.*"));// 只需再绑定业务 Topic 即可container.addMessageListener(adapter, new PatternTopic("topic2.*"));}@Overridepublic void onMessage(Message message, byte[] bytes) {String key = new String(message.getChannel());String value = new String(message.getBody());log.info("Key: {}", key);log.info("Value: {}", value);}
}
测试
curl --location '127.0.0.1:7077/redis' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'topic=topic2.msg' \
--data-urlencode 'msg=我是消息2'
结果
2023-11-15 10:22:38.445 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Key: topic2.msg
2023-11-15 10:22:38.449 INFO 59189 --- [ container-3] com.xxx.redis.demo.RedisReceiver2 : Key: topic2.msg
2023-11-15 10:22:38.545 INFO 59189 --- [ container-2] com.xxx.redis.demo.RedisReceiver1 : Value: "我是消息2"
2023-11-15 10:22:38.645 INFO 59189 --- [ container-3] com.xxx.redis.demo.RedisReceiver2 : Value: "我是消息2"
好了,Redis 订阅发布的教程到此为止。接下来,我们看下如何用它来替代 Etcd 的业务情景?
这之前,我们先大概聊下 Etcd 的 2 个要点:
- Etcd 消息事件类型
- Etcd 持久层数据
那么问题来了,Redis 虽然具备基本的消息订阅发布,但是如何契合 Etcd 的这 2 点特性,我们目前给出对应的解决方案是:
- 使用 Redis K-V 的 value 作为 Etcd 消息事件类型
- 使用 MySQL 作为 Etcd 持久层数据:字段 id 随机 UUID、字段 key 对应 Etcd key、字段 value 对应 Etcd value,这样做的一个好处是无需重构数据结构
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `t_redis_msg`;
CREATE TABLE `t_redis_msg` (
`id` varchar(32) NOT NULL,
`key` varchar(255) NOT NULL,
`value` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;SET FOREIGN_KEY_CHECKS = 1;
所以,如果想平替 Etcd 的事件类型和持久层数据的解决方案需要 MySQL & Redis 结合,接下来直接上代码……
Redis & MySQL 整合
application.yml(升级)
spring:application:name: redis-demodatasource:username: rootpassword: 123456url: jdbc:mysql://localhost:3306/db_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverhikari:connection-test-query: SELECT 1idle-timeout: 40000max-lifetime: 1880000connection-timeout: 40000minimum-idle: 1validation-timeout: 60000maximum-pool-size: 20
RedisMsg.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;/*** @author Lux Sun* @date 2021/2/19*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_redis_msg", autoResultMap = true)
public class RedisMsg {@TableId(type = IdType.ASSIGN_UUID)private String id;@TableField(value = "`key`")private String key;private String value;
}
RedisMsgEnum.java
/*** @author Lux Sun* @date 2022/11/11*/
public enum RedisMsgEnum {PUT("PUT"),DEL("DEL");private String code;RedisMsgEnum(String code) {this.code = code;}public String getCode() {return code;}}
RedisMsgService.java
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;/*** @author Lux Sun* @date 2020/6/16*/
public interface RedisMsgService extends IService<RedisMsg> {/*** 获取消息* @param key*/RedisMsg get(String key);/*** 获取消息列表* @param key*/Map<String, String> map(String key);/*** 获取消息值* @param key*/String getValue(String key);/*** 获取消息列表* @param key*/List<RedisMsg> list(String key);/*** 插入消息* @param key* @param value*/void put(String key, String value);/*** 删除消息* @param key*/void del(String key);
}
RedisMsgServiceImpl.java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** @author Lux Sun* @date 2020/6/16*/
@Slf4j
@Service
public class RedisMsgServiceImpl extends ServiceImpl<RedisMsgDao, RedisMsg> implements RedisMsgService {@Resourceprivate RedisMsgDao redisMsgDao;@Resourceprivate RedisUtil redisUtil;/*** 获取消息** @param key*/@Overridepublic RedisMsg get(String key) {LambdaQueryWrapper<RedisMsg> lqw = new LambdaQueryWrapper<>();lqw.eq(RedisMsg::getKey, key);return redisMsgDao.selectOne(lqw);}/*** 获取消息列表** @param key*/@Overridepublic Map<String, String> map(String key) {List<RedisMsg> redisMsgs = this.list(key);return redisMsgs.stream().collect(Collectors.toMap(RedisMsg::getKey, RedisMsg::getValue));}/*** 获取消息值** @param key*/@Overridepublic String getValue(String key) {RedisMsg redisMsg = this.get(key);return redisMsg.getValue();}/*** 获取消息列表** @param key*/@Overridepublic List<RedisMsg> list(String key) {LambdaQueryWrapper<RedisMsg> lqw = new LambdaQueryWrapper<>();lqw.likeRight(RedisMsg::getKey, key);return redisMsgDao.selectList(lqw);}/*** 插入消息** @param key* @param value*/@Overridepublic void put(String key, String value) {log.info("开始添加 - key: {},value: {}", key, value);LambdaQueryWrapper<RedisMsg> lqw = new LambdaQueryWrapper<>();lqw.eq(RedisMsg::getKey, key);this.saveOrUpdate(RedisMsg.builder().key(key).value(value).build(), lqw);redisUtil.putMsg(key);log.info("添加成功 - key: {},value: {}", key, value);}/*** 删除消息** @param key*/@Overridepublic void del(String key) {log.info("开始删除 - key: {}", key);LambdaQueryWrapper<RedisMsg> lqw = new LambdaQueryWrapper<>();lqw.likeRight(RedisMsg::getKey, key);redisMsgDao.delete(lqw);redisUtil.delMsg(key);log.info("删除成功 - key: {}", key);}
}
RedisUtil.java(升级)
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisUtil {@Resourceprivate RedisTemplate redisTemplate;/*** 消息发送* @param topic 主题* @param message 消息*/public void publish(String topic, String message) {redisTemplate.convertAndSend(topic, message);}/*** 消息发送 PUT* @param topic 主题*/public void putMsg(String topic) {redisTemplate.convertAndSend(topic, RedisMsgEnum.PUT);}/*** 消息发送 DELETE* @param topic 主题*/public void delMsg(String topic) {redisTemplate.convertAndSend(topic, RedisMsgEnum.DEL);}
}
演示 DEMO
RedisMsgController.java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;/*** @author Lux Sun* @date 2023/9/12*/
@RestController
@RequestMapping("/redisMsg")
public class RedisMsgController {@Resourceprivate RedisMsgService redisMsgService;@PostMappingpublic String publish(@RequestParam String topic, @RequestParam String msg) {redisMsgService.put(topic, msg);return "发送成功: " + topic + " - " + msg;}
}
RedisMsgReceiver.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Slf4j
@Component
public class RedisMsgReceiver implements MessageListener {@Resourceprivate RedisMsgService redisMsgService;@Resourceprivate RedisMessageListenerContainer container;@PostConstructpublic void init() {MessageListenerAdapter adapter = new MessageListenerAdapter(this);container.addMessageListener(adapter, new PatternTopic("topic3.*"));}@Overridepublic void onMessage(Message message, byte[] bytes) {String key = new String(message.getChannel());String event = new String(message.getBody());String value = redisMsgService.getValue(key);log.info("Key: {}", key);log.info("Event: {}", event);log.info("Value: {}", value);}
}
测试
curl --location '127.0.0.1:7077/redisMsg' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'topic=topic3.msg' \
--data-urlencode 'msg=我是消息3'
结果
2023-11-16 10:24:35.721 INFO 43794 --- [nio-7077-exec-1] c.c.redis.demo.RedisMsgServiceImpl : 开始添加 - key: topic3.msg,value: 我是消息3
2023-11-16 10:24:35.935 INFO 43794 --- [nio-7077-exec-1] c.c.redis.demo.RedisMsgServiceImpl : 添加成功 - key: topic3.msg,value: 我是消息3
2023-11-16 10:24:35.950 INFO 43794 --- [ container-2] c.xxx.redis.demo.RedisMsgReceiver : Key: topic3.msg
2023-11-16 10:24:35.950 INFO 43794 --- [ container-2] c.xxx.redis.demo.RedisMsgReceiver : Event: "PUT"
2023-11-16 10:24:35.950 INFO 43794 --- [ container-2] c.xxx.redis.demo.RedisMsgReceiver : Value: 我是消息3
相关文章:

Redis - 订阅发布替换 Etcd 解决方案
为了减轻项目的中间件臃肿,由于我们项目本身就应用了 Redis,正好 Redis 的也具备订阅发布监听的特性,正好应对 Etcd 的功能,所以本次给大家讲解如何使用 Redis 消息订阅发布来替代 Etcd 的解决方案。接下来,我们先看 R…...

Hessian协议详解
前言 Hessian协议是一种基于二进制的轻量级远程调用协议,用于在分布式系统中进行跨语言的通信。它使用简单的二进制格式来序列化和反序列化数据,并支持多种编程语言,如Java、C#、Python等。Hessian协议相对于其他协议的优势在于其简单性和高…...

【AI视野·今日Sound 声学论文速览 第三十六期】Mon, 30 Oct 2023
AI视野今日CS.Sound 声学论文速览 Mon, 30 Oct 2023 Totally 7 papers 👉上期速览✈更多精彩请移步主页 Daily Sound Papers Style Description based Text-to-Speech with Conditional Prosodic Layer Normalization based Diffusion GAN Authors Neeraj Kumar, A…...

Android Jetpack的组件介绍,常见组件解析
jetpack组件有哪些 Android Jetpack是一个集成Android应用程序组件的一站式解决方案。它使开发人员能够专注于他们的应用程序的真正创新部分,而不会受到Android平台特定的限制。Jetpack组件可分为四个类别: 架构组件(Architecture Componen…...

ImportError: cannot import name ‘url_quote‘ from...
👨🏻💻 热爱摄影的程序员 👨🏻🎨 喜欢编码的设计师 🧕🏻 擅长设计的剪辑师 🧑🏻🏫 一位高冷无情的编码爱好者 大家好,我是全栈工…...

一文看分布式锁
为什么会存在分布式锁? 经典场景-扣库存,多人去同时购买一件商品,首先会查询判断是否有剩余,如果有进行购买并扣减库存,没有提示库存不足。假如现在仅存有一件商品,3人同时购买,三个线程同时执…...

Jenkins自动化部署一个Maven项目
Jenkins自动化部署 提示:本教程基于CentOS Linux 7系统下进行 Jenkins的安装 1. 下载安装jdk11 官网下载地址:https://www.oracle.com/cn/java/technologies/javase/jdk11-archive-downloads.html 本文档教程选择的是jdk-11.0.20_linux-x64_bin.tar.g…...

K8S1.23.5部署(此前1.17版本步骤囊括)及问题记录
应版本需求,升级容器版本为1.23.5 kubernetes组件 一个kubernetes集群主要由控制节点(master)与工作节点(node)组成,每个节点上需要安装不同的组件。 master控制节点:负责整个集群的管理。 …...

基于java web的中小型人力资源管理系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...
Python学习笔记--Python关键字yield
原文:http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained 注:这是一篇 stackoverflow 上一个火爆帖子的译文 问题 Python 关键字 yield 的作用是什么?用来干什么的? 比如,我正在试图理解下面的代码: def node._get_child_candidates(self,…...
CF 850 C Arpa and a game with Mojtaba(爆搜优化SG)
CF 850 C. Arpa and a game with Mojtaba(爆搜优化SG) Problem - C - Codeforces Arpa and a game with Mojtaba - 洛谷 思路:显然对于每一种质因子来说操作都是独立的 , 因此可以考虑对于每一种质因子求当前质因子的SG &#…...

kafka分布式安装部署
1.集群规划 2.集群部署 官方下载地址:http://kafka.apache.org/downloads.html (1)上传并解压安装包 [zhangflink9wmwtivvjuibcd2e package]$ tar -zxvf kafka_2.12-3.3.1.tgz -C ../software/(2)修改解压后的文件…...

[云原生2.] Kurbernetes资源管理 ---- (陈述式资源管理方式)
文章目录 1. K8s管理资源的方法类别1.1 陈述式资源管理方式1.2 声明式资源管理方式1.3 GUI式资源管理方法 2. 陈述式资源管理方式2.1 命令行工具 ---- Kubelet2.1.1 简介2.1.2 特性2.1.3 kubelet拓展命令2.1.4 kubectl基本语法2.1.5 Kubectl工具的自动补全 2.2 k8s Service 的类…...

java:IDEA中的Scratches and Consoles
背景 IntelliJ IDEA中的Scratches and Consoles是一种临时的文件编辑环境,用于写一些文本内容或者代码片段。 其中,Scratch files拥有完整的运行和debug功能,这些文件需要指定编程语言类型并且指定后缀。 举例:调接口 可以看到…...

华为 Mate 60 Pro 拆解:陆制零件比率上升至47% | 百能云芯
近日,日经新闻联合研究公司Fomalhaut Techno Solutions对华为 Mate 60 Pro 进行了拆解,揭示了这款于8月发布的新型智能手机的成本结构。拆解结果显示,该手机的国产零部件比例达到了47%,相较于三年前的 Mate 40 Pro,提高…...

ZBrush 2024(三维数字雕刻软件)
ZBrush是一款Mac数字雕刻软件,它具有以下功能: 雕刻工具:ZBrush的雕刻工具非常强大,可以让用户在3D模型上进行雕刻,就像使用传统雕塑工具一样。高精度模型创建:ZBrush可以创建高精度的3D模型,适…...

wpf devexpress 排序、分组、过滤数据
这个教程示范在GridControl如何排序数据,分组数据给一个行创建一个过滤。这个教程基于前一个教程。 排序数据 可以使用GridControl 排序数据。这个例子如下过滤数据对于Order Date 和 Customer Id 行: 1、对于Order Date 和 Customer Id 行指定Colum…...
使用Badboy录制生成 JMeter 脚本
JMeter是一款在国外非常流行和受欢迎的开源性能测试工具,像LoadRunner 一样,它也提供了一个利用本地Proxy Server(代理服务器)来录制生成测试脚本的功能,但是这个功能并不好用。所以在本文中介绍一个更为常用的方法——…...

V10 桌面版、服务器版系统加固
V10 桌面版、服务器版系统加固 一、 文档说明 本文档中涉及的加固方法主要包括:密码策略配置、防火墙规 则配置、禁用高风险服务等。 二、 V10 桌面版系统加固 2.1 密码策略配置 密码策略包括密码老化控制策略和密码复杂度策略。密码老化 控制策略需要配置/etc…...

mtgsig1.2简单分析
{"a1": "1.2", # 加密版本"a2": new Date().valueOf() - serverTimeDiff, # 加密过程中用到的时间戳. 这次服主变坏了, 时间戳需要减去一个 serverTimeDiff(见a3) ! "a3": "这是把xxx信息加密后提交给服务器, 服主…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
手动给中文分词和 直接用神经网络RNN做有什么区别
手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动)…...
在Spring Boot中集成RabbitMQ的完整指南
前言 在现代微服务架构中,消息队列(Message Queue)是实现异步通信、解耦系统组件的重要工具。RabbitMQ 是一个流行的消息中间件,支持多种消息协议,具有高可靠性和可扩展性。 本博客将详细介绍如何在 Spring Boot 项目…...

【Linux】使用1Panel 面板让服务器定时自动执行任务
服务器就是一台24小时开机的主机,相比自己家中不定时开关机的主机更适合完成定时任务,例如下载资源、备份上传,或者登录某个网站执行一些操作,只需要编写 脚本,然后让服务器定时来执行这个脚本就可以。 有很多方法实现…...