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信息加密后提交给服务器, 服主…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
uni-app学习笔记二十七--设置底部菜单TabBar的样式
官方文档地址:uni.setTabBarItem(OBJECT) | uni-app官网 uni.setTabBarItem(OBJECT) 动态设置 tabBar 某一项的内容,通常写在项目的App.vue的onLaunch方法中,用于项目启动时立即执行 重要参数: indexnumber是tabBar 的哪一项&…...
