netty使用redis发布订阅实现消息推送

netty使用redis发布订阅实现消息推送

场景

项目中需要给用户推送消息:

在这里插入图片描述

接口

@RestController
public class PushApi {@Autowiredprivate PushService pushService;/*** 消息推送* @param query* @return*/@PostMapping("/push/message")public String push(@RequestBody MessagePushConfigDto query){pushService.push(query);return "success";}}@Component
@Slf4j
public class PushService {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate MessageService messageService;public void push(MessagePushConfigDto query) {String messageNo = UUID.randomUUID().toString();if (query.getType()== Constants.MSG_TYPE_ALL){doPushGroup(query, messageNo);}else {doPushToUser(query, messageNo);}}private void doPushGroup(MessagePushConfigDto query, String messageNo) {MessageDto dto = new MessageDto();dto.setModule(query.getModule());dto.setType(query.getType());dto.setMessageNo(messageNo);dto.setContent(query.getContent());//转发至其他节点redisTemplate.convertAndSend(Constants.TOPIC_MODULE, JSON.toJSONString(dto));}private void doPushToUser(MessagePushConfigDto query, String messageNo) {for (String identityNo : query.getIdentityList()) {MessageDto dto = new MessageDto();dto.setModule(query.getModule());dto.setType(query.getType());dto.setMessageNo(messageNo);dto.setContent(query.getContent());dto.setIdentityNo(identityNo);String key = MessageFormat.format(Constants.USER_KEY, query.getModule(),identityNo);String nodeIp = redisTemplate.opsForValue().get(key);if (StrUtil.isBlank(nodeIp)){log.info("no user found: {}-{}",identityNo, key);return;}if (NodeConfig.node.equals(nodeIp)){log.info("send from local: {}", identityNo);messageService.sendToUser(dto.getMessageNo(),dto.getModule(),dto.getIdentityNo(),dto.getContent());}else {//转发至其他节点redisTemplate.convertAndSend(Constants.TOPIC_USER, JSON.toJSONString(dto));}}}
}

实体

//发送的消息
@Data
public class MessageDto {private String module;/*** 1、指定用户* 2、全部*/private Integer type;private String messageNo;private String content;private String identityNo;}//消息配置
@Data
public class MessagePushConfigDto {private String module;/*** 1、指定用户* 2、全部*/private Integer type;private String content;private List<String> identityList;}//常量
public interface Constants {int MSG_TYPE_ALL = 1;int MSG_TYPE_SINGLE = 0;String TOPIC_MODULE = "topic:module";String TOPIC_USER = "topic:module:user";String USER_KEY = "socket:module:{0}:userId:{1}";
}
MessageService 发送消息接口
public interface MessageService {/*** 发送组* @param messageNo* @param module* @param content*/void sendToGroup(String messageNo, String module, String content);/*** 单用户发送* @param messageNo* @param module* @param identityNo* @param content*/void sendToUser(String messageNo, String module, String identityNo, String content);
}public class MessageServiceImpl implements MessageService {private SessionRegistry sessionRegistry;public MessageServiceImpl(SessionRegistry sessionRegistry) {this.sessionRegistry = sessionRegistry;}@Overridepublic void sendToGroup(String messageNo, String module, String content) {SessionGroup sessionGroup = sessionRegistry.retrieveGroup(module);if (!Objects.isNull(sessionGroup)){sessionGroup.sendGroup(content);}}@Overridepublic void sendToUser(String messageNo, String module, String identityNo, String content) {WssSession wssSession = sessionRegistry.retrieveSession(module, identityNo);if (!Objects.isNull(wssSession)){wssSession.send(content);}}
}
SessionService

操作 session 服务,并设置 用户到redis

public interface SessionService<WS extends WssSession<C>,C> {/*** 添加session* @param session*/void addSession(WS session);/*** 删除session* @param session*/void removeSession(WS session);}
public abstract class AbstractSessionService<SR extends SessionRegistry<WS, C>, WS extends WssSession<C>, C>implements SessionService<WS, C> {@Getterprivate SR sessionRegistry;public AbstractSessionService(SR sessionRegistry) {this.sessionRegistry = sessionRegistry;}
}public class SessionServiceImpl<SR extends SessionRegistry<WS, C>, WS extends WssSession<C>, C>extends AbstractSessionService<SR, WS, C> {private StringRedisTemplate redisTemplate;public SessionServiceImpl(SR sessionRegistry, StringRedisTemplate redisTemplate) {super(sessionRegistry);this.redisTemplate = redisTemplate;}@Overridepublic void addSession(WS session) {getSessionRegistry().addSession(session);String key = MessageFormat.format(Constants.USER_KEY, session.getModule(), session.getIdentityNo());redisTemplate.opsForValue().set(key, NodeConfig.node);}@Overridepublic void removeSession(WS session) {getSessionRegistry().removeSession(session);String key = MessageFormat.format(Constants.USER_KEY, session.getModule(), session.getIdentityNo());redisTemplate.delete(key);}}

websocket 实现

定义session接口相关

public interface WssSession<C> {/*** 模块* @return*/String getModule();/*** 用户唯一标识* @return*/String getIdentityNo();/*** 通信渠道* @return*/C getChannel();/*** 发送消息* @param message*/void send(String message);}public interface SessionGroup <T extends WssSession<C>, C>{/*** add session* @param session*/void addSession(T session);/*** remove session* @param session*/void removeSession(T session);/*** 发送组数据* @param message*/void sendGroup(String message);/*** 根据唯一标识查询session* @param identityNo* @return*/T getSession(String identityNo);}
public interface SessionRegistry<T extends WssSession<C>, C> {/*** 添加 session** @param session*/void addSession(T session);/*** 移除 session** @param session*/void removeSession(T session);/*** 查询 SessionGroup* @param module* @return*/SessionGroup<T, C> retrieveGroup(String module);/*** 查询 session* @param module* @param identityNo* @return*/T retrieveSession(String module, String identityNo);}public abstract class AbstractSession<C> implements WssSession<C>{private String module;private String identityNo;private C channel;public AbstractSession(String module, String identityNo, C channel) {this.module = module;this.identityNo = identityNo;this.channel = channel;}@Overridepublic String getModule() {return module;}@Overridepublic String getIdentityNo() {return identityNo;}@Overridepublic C getChannel() {return channel;}
}public abstract class AbstractSessionRegistry<T extends WssSession<C>, C> implements SessionRegistry<T, C> {private Map<String, SessionGroup<T, C>> map = new ConcurrentHashMap<>();@Overridepublic void addSession(T session) {SessionGroup<T, C> sessionGroup = map.computeIfAbsent(session.getModule(), key -> newSessionGroup());sessionGroup.addSession(session);}protected abstract SessionGroup<T, C> newSessionGroup();@Overridepublic void removeSession(T session) {SessionGroup<T, C> sessionGroup = map.get(session.getModule());sessionGroup.removeSession(session);}@Overridepublic SessionGroup<T, C> retrieveGroup(String module) {return map.get(module);}@Overridepublic T retrieveSession(String module, String identityNo) {SessionGroup<T, C> sessionGroup = map.get(module);if (sessionGroup != null) {return (T) sessionGroup.getSession(identityNo);}return null;}
}

使用 netty 容器

@Slf4j
@Component
public class NettyServer {private NioEventLoopGroup boss;private NioEventLoopGroup worker;@Value("${namespace:/ns}")private String namespace;@Autowiredprivate SessionService sessionService;@PostConstructpublic void start() {try {boss = new NioEventLoopGroup(1);worker = new NioEventLoopGroup();ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(boss, worker).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new IdleStateHandler(0, 0, 60));pipeline.addLast(new HeartBeatInboundHandler());pipeline.addLast(new HttpServerCodec());pipeline.addLast(new HttpObjectAggregator(64 * 1024));pipeline.addLast(new ChunkedWriteHandler());pipeline.addLast(new HttpRequestInboundHandler(namespace));pipeline.addLast(new WebSocketServerProtocolHandler(namespace, true));pipeline.addLast(new WebSocketHandShakeHandler(sessionService));}});int port = 9999;serverBootstrap.bind(port).addListener((ChannelFutureListener) future -> {if (future.isSuccess()) {log.info("server start at port successfully: {}", port);} else {log.info("server start at port error: {}", port);}}).sync();} catch (InterruptedException e) {log.error("start error", e);close();}}@PreDestroypublic void destroy() {close();}private void close() {log.info("websocket server close..");if (boss != null) {boss.shutdownGracefully();}if (worker != null) {worker.shutdownGracefully();}}}public class NettySessionGroup implements SessionGroup<NWssSession,Channel> {private ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);//Map<identityNo,channel>private Map<String, NWssSession> map = new ConcurrentHashMap<>();@Overridepublic void addSession(NWssSession session) {group.add(session.getChannel());map.put(session.getIdentityNo(), session);}@Overridepublic void removeSession(NWssSession session) {group.remove(session.getChannel());map.remove(session.getIdentityNo());}@Overridepublic void sendGroup(String message){group.writeAndFlush(new TextWebSocketFrame(message));}@Overridepublic NWssSession getSession(String identityNo) {return map.get(identityNo);}}public class NettySessionRegistry extends AbstractSessionRegistry<NWssSession, Channel> {@Overrideprotected SessionGroup<NWssSession, Channel> newSessionGroup() {return new NettySessionGroup();}
}public class NWssSession extends AbstractSession<Channel> {public NWssSession(String module, String identityNo, Channel channel) {super(module, identityNo, channel);}@Overridepublic void send(String message) {getChannel().writeAndFlush(new TextWebSocketFrame(message));}
}public class NettyUtil {//参数-module<->user-codepublic static AttributeKey<String> G_U = AttributeKey.valueOf("GU");//参数-uripublic static AttributeKey<String> P = AttributeKey.valueOf("P");/*** 设置上下文参数** @param channel* @param attributeKey* @param data* @param <T>*/public static <T> void setAttr(Channel channel, AttributeKey<T> attributeKey, T data) {Attribute<T> attr = channel.attr(attributeKey);if (attr != null) {attr.set(data);}}/*** 获取上下文参数** @param channel* @param attributeKey* @param <T>* @return*/public static <T> T getAttr(Channel channel, AttributeKey<T> attributeKey) {return channel.attr(attributeKey).get();}/*** 根据 渠道获取 session** @param channel* @return*/public static NWssSession getSession(Channel channel) {String attr = channel.attr(G_U).get();if (StrUtil.isNotBlank(attr)) {String[] split = attr.split(",");String groupId = split[0];String username = split[1];return new NWssSession(groupId, username, channel);}return null;}public static void writeForbiddenRepose(ChannelHandlerContext ctx) {String res = "FORBIDDEN";FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN, Unpooled.wrappedBuffer(res.getBytes(StandardCharsets.UTF_8)));response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());ctx.writeAndFlush(response);ctx.close();}}public interface WebSocketListener {void handShakeSuccessful(ChannelHandlerContext ctx, String uri);void handShakeFailed(ChannelHandlerContext ctx,String uri);
}//解析 request uri参数
@Slf4j
public class DefaultWebSocketListener implements WebSocketListener {private static final String G = "module";private static final String U = "userCode";@Overridepublic void handShakeSuccessful(ChannelHandlerContext ctx, String uri) {QueryStringDecoder decoderQuery = new QueryStringDecoder(uri);Map<String, List<String>> params = decoderQuery.parameters();String groupId = getParameter(G, params);String userCode = getParameter(U, params);if (StrUtil.isBlank(groupId) || StrUtil.isBlank(userCode)) {log.info("module or userCode is null: {}", uri);NettyUtil.writeForbiddenRepose(ctx);return;}//传递参数NettyUtil.setAttr(ctx.channel(), NettyUtil.G_U, groupId.concat(",").concat(userCode));}@Overridepublic void handShakeFailed(ChannelHandlerContext ctx, String uri) {log.info("handShakeFailed failed,close channel");ctx.close();}private String getParameter(String key, Map<String, List<String>> params) {if (CollectionUtils.isEmpty(params)) {return null;}List<String> value = params.get(key);if (CollectionUtils.isEmpty(value)) {return null;}return value.get(0);}}
netty handler
//心跳
@Slf4j
public class HeartBeatInboundHandler extends ChannelInboundHandlerAdapter {@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent ise){if (ise.state()== IdleState.ALL_IDLE){//关闭连接log.info("HeartBeatInboundHandler heart beat close");ctx.channel().close();return;}}super.userEventTriggered(ctx,evt);}}/*** @Date: 2024/7/17 13:06* 处理 http 协议 的请求参数并传递*/
@Slf4j
public class HttpRequestInboundHandler extends ChannelInboundHandlerAdapter {private String namespace;public HttpRequestInboundHandler(String namespace) {this.namespace = namespace;}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof FullHttpRequest request) {//ws://localhost:8080/n/ws?groupId=xx&username=tomString requestUri = request.uri();String decode = URLDecoder.decode(requestUri, StandardCharsets.UTF_8);log.info("raw request url: {}", decode);URI uri = new URI(requestUri);if (!uri.getPath().startsWith(namespace)) {NettyUtil.writeForbiddenRepose(ctx);return;}// TODO: 2024/7/17 校验token// 比如从 header中获取token// 构建自定义WebSocket握手处理器, 也可以使用 netty自带 WebSocketServerProtocolHandler//shakeHandsIfNecessary(ctx, request, requestUri);//去掉参数 ===>  ws://localhost:8080/n/ws//传递参数NettyUtil.setAttr(ctx.channel(), NettyUtil.P, requestUri);request.setUri(namespace);ctx.pipeline().remove(this);ctx.fireChannelRead(request);}}
/*private void shakeHandsIfNecessary(ChannelHandlerContext ctx, FullHttpRequest request, String requestUri) {WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(request), null, true);WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(request);if (handshaker == null) {// 如果不支持WebSocket版本,返回HTTP 405错误WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());} else {ChannelPipeline pipeline = ctx.channel().pipeline();handshaker.handshake(ctx.channel(), request).addListener((ChannelFutureListener) future -> {if (future.isSuccess()) {//握手成功 WebSocketListener listenerlistener.handShakeSuccessful(ctx, requestUri);} else {//握手失败listener.handShakeFailed(ctx, requestUri);}});}}private String getWebSocketLocation(FullHttpRequest req) {return "ws://" + req.headers().get(HttpHeaderNames.HOST) + prefix;}*/
}@Slf4j
public class WebSocketBizHandler extends SimpleChannelInboundHandler<WebSocketFrame> {private SessionService sessionService;public WebSocketBizHandler(SessionService sessionService){this.sessionService = sessionService;}@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {log.info("handlerAdded");NWssSession session = NettyUtil.getSession(ctx.channel());if (session == null) {log.info("session is null: {}", ctx.channel().id());NettyUtil.writeForbiddenRepose(ctx);return;}sessionService.addSession(session);}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {log.info("handlerRemoved");NWssSession session = NettyUtil.getSession(ctx.channel());if (session == null) {log.info("session is null: {}", ctx.channel().id());return;}sessionService.removeSession(session);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {if (msg instanceof TextWebSocketFrame) {} else if (msg instanceof BinaryWebSocketFrame) {} else if (msg instanceof PingWebSocketFrame) {} else if (msg instanceof PongWebSocketFrame) {} else if (msg instanceof CloseWebSocketFrame) {if (ctx.channel().isActive()) {ctx.close();}}ctx.writeAndFlush(new TextWebSocketFrame("默认回复"));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {//处理最后的业务异常log.info("WebSocketBizHandler error: ", cause);}
}//处理websocket协议握手
@Slf4j
public class WebSocketHandShakeHandler extends ChannelInboundHandlerAdapter {private SessionService sessionService;private WebSocketListener webSocketListener = new DefaultWebSocketListener();public WebSocketHandShakeHandler(SessionService sessionService) {this.sessionService = sessionService;}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {log.info("WebSocketHandShakeHandler shake-hands success");// 在此处获取URL、Headers等信息并做校验,通过throw异常来中断链接。String uri = NettyUtil.getAttr(ctx.channel(), NettyUtil.P);if (StrUtil.isBlank(uri)) {log.info("request uri is null");NettyUtil.writeForbiddenRepose(ctx);return;}webSocketListener.handShakeSuccessful(ctx, uri);ChannelPipeline pipeline = ctx.channel().pipeline();pipeline.addLast(new WebSocketBizHandler(sessionService));pipeline.remove(this);return;}super.userEventTriggered(ctx, evt);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {if (cause instanceof WebSocketHandshakeException) {//只处理 websocket 握手相关异常FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.BAD_REQUEST,Unpooled.wrappedBuffer(cause.getMessage().getBytes()));ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);return;}super.exceptionCaught(ctx,cause);}}

配置

@Component
public class NodeConfig {public static String node;@PostConstructpublic void init() {String localhostStr = NetUtil.getLocalhostStr();NodeConfig.node = localhostStr;Assert.notNull(NodeConfig.node, "local ip is null");}
}@Slf4j
@Configuration
public class RedisPublishConfig {@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListener messageListener) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(connectionFactory);List<PatternTopic> topicList = new ArrayList<>();topicList.add(new PatternTopic(Constants.TOPIC_USER));topicList.add(new PatternTopic(Constants.TOPIC_MODULE));container.addMessageListener(messageListener, topicList);log.info("RedisMessageListenerContainer listen topic: {}", Constants.TOPIC_USER);return container;}}@Slf4j
@Component
public class RedisPublisherListener implements MessageListener {@Autowiredprivate RedisPublisherConsumer messageService;@Overridepublic void onMessage(Message message, byte[] pattern) {try {String topic = new String(pattern);String msg = new String(message.getBody(), "utf-8");log.info("recv topic:{}, msg: {}", topic, msg);messageService.consume(topic, msg);} catch (UnsupportedEncodingException e) {log.error("recv msg error: {}", new String(pattern), e);}}
}@Configuration
public class WebSocketConfig {@Beanpublic NettySessionRegistry sessionRegistry() {return new NettySessionRegistry();}@Beanpublic SessionService<NWssSession, Channel> sessionService(StringRedisTemplate redisTemplate) {return new SessionServiceImpl<>(sessionRegistry(), redisTemplate);}@Beanpublic MessageService messageService() {return new MessageServiceImpl(sessionRegistry());}}

good luck!

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

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

相关文章

Linux gcc/g++ _ make/makefile

文章目录 库gcc/g程序编译过程链接动态链接静态链接 make _ makefile 库 一、 什么是库&#xff1f; 库是程序代码的集合&#xff0c;是共享程序代码的一种方式。根据源代码的公开情况&#xff0c;库可以分为两种类型&#xff1a; 开源库&#xff0c;公开源代码&#xff0c;能…

Godot入门 03世界构建1.0版

在game场景&#xff0c;删除StaticBody2D节点&#xff0c;添加TileMap节点 添加TileSet图块集 添加TileSet源 拖动图片到图块&#xff0c;自动创建图块 使用橡皮擦擦除。取消橡皮擦后按住Shift创建大型图块。 进入选择模式&#xff0c;TileMap选择绘制&#xff0c;选中图块后在…

MSQP Mysql数据库权限提升工具,UDF自动检测+快速反向SHELL

项目地址:https://github.com/MartinxMax/MSQP MSQP 这是一个关于Mysql的权限提升工具 安装依赖 $ python3 -m pip install mysql-connector-python 使用方法 $ python3 msqp.py -h 权限提升:建立反向Shell 在建立反向连接前,该工具会自动检测是否具有提权条件&#xff0…

软考:软件设计师 — 7.软件工程

七. 软件工程 1. 软件工程概述 &#xff08;1&#xff09;软件生存周期 &#xff08;2&#xff09;软件过程 软件开发中所遵循的路线图称为 "软件过程"。 针对管理软件开发的整个过程&#xff0c;提出了两个模型&#xff1a;能力成熟度模型&#xff08;CMM&#…

解析嵌入式世界里中断和异常的不同之处

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c;点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;是指CPU内部出现的中断&…

linux命令更新-文本处理awk

awk命令简介 awk是一种强大的文本处理工具&#xff0c;可以对文本文件进行格式化、统计、计算等操作。它逐行读取文本文件&#xff0c;并对每一行进行处理。awk的语法相对简单&#xff0c;但功能非常强大&#xff0c;是Linux系统中常用的文本处理工具之一。 awk命令基本语法 …

超燃!纯AI生成《泰坦尼克号》大片!浙大阿里发布MovieDreamer:超长电影生成“梦工厂“

论文链接&#xff1a;https://arxiv.org/pdf/2407.16655 项目主页&#xff1a;https://aim-uofa.github.io/MovieDreamer/ github链接&#xff1a;https://github.com/aim-uofa/MovieDreamer 亮点直击 MovieDreamer&#xff0c;一个新颖的分层框架&#xff0c;将自回归模型与扩…

idea设置类注释模板作者、日期、描述等信息

文章目录 前言一、新建类的时候自动添加类注释1.打开设置2.模版配置示例如下3.实际生成效果 前言 由于每次换电脑时都需要重新对idea进行设置&#xff0c;为了方便大家的开发配置&#xff0c;同时也为自己以后配置留一份记录&#xff08;毕竟每次换环境都需要重新配置一遍&…

代码审计 | .NET SqlSugar框架注入漏洞

01阅读须知 此文所节选自小报童《.NET 代码审计》专栏&#xff0c;主要内容有涉及的.NET目录和文件操作、SQL注入方向的敏感函数、还有不安全的配置导致的漏洞挖掘思路&#xff0c;对.NET代码审计感兴趣的朋友们可以解锁该电子报刊&#xff0c;解锁更多的报刊内容。 02基本介…

【Unity】 HTFramework框架(五十三)使用 Addressables 可寻址系统

更新日期&#xff1a;2024年7月25日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 Addressables 可寻址系统使用 Addressables 可寻址系统一、导入 Addressables二、切换到 Addressables 加载模式三、切换资源加载助手四、加载资源五、注…

C++基础知识:函数重载是什么?函数重载基础用法,函数重载的作用,函数重载满足条件(关键),注意事项,函数重载代码语法的函数

1.作用&#xff1a; 函数名可以相同&#xff0c;提高复用性 2.函数重载满足条件&#xff08;关键&#xff09;&#xff1a; 1.同一个作用域下 2.函数名称相同 3.函数参数类型不同 或者 个数不同 或者 顺序不同 注意&#xff1a;函数的返回值不可以作为函数重载的条件 1.常见…

【python】python销售数据分析可视化(源码+论文+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

CTF-pwn-虚拟化-vmmware 前置

文章目录 参考vmware逃逸简介虚拟机和主机通信机制(guest to host)共享内存&#xff08;弃用&#xff09;backdoor机制Message_Send和Message_RecvGuestRPC实例RpcOutSendOneRawWork实例 vmware-rpctool info-get guestinfo.ip各个步骤对应的backdoor操作Open RPC channelSend …

【MySQL进阶之路 | 高级篇】页锁+锁的思想(悲观锁和乐观锁)

1. 页锁 页锁就是在页的粒度上进行锁定&#xff0c;锁定的数据资源比行锁要多&#xff0c;因为一个页中可以有多个行记录.当我们使用页锁的时候&#xff0c;会出现数据浪费的现象&#xff0c;但这样的浪费最多也就是一个页上的数据行。页锁的开销介于表锁和行锁之间&#xff0…

Internet Download Manager2024功能特点优势分析及使用建议及注意事项

1. Internet Download Manager简介 2. 功能特点 3. 优势分析 4. 专家评价 5. 使用建议及注意事项 6. 常见问题解答 7. 用户反馈及案例分享 8. IDM下载器的未来发展趋势 文章&#xff1a; 在互联网快速发展的今日&#xff0c;人们对于网络资源的获取和利用越来越频繁。无论…

【Python面试题收录】Python编程基础练习题①(数据类型+函数+文件操作)

本文所有代码打包在Gitee仓库中https://gitee.com/wx114/Python-Interview-Questions 一、数据类型 第一题&#xff08;str&#xff09; 请编写一个Python程序&#xff0c;完成以下任务&#xff1a; 去除字符串开头和结尾的空格。使用逗号&#xff08;","&#…

axios请求大全

本文讲解axios封装方式以及针对各种后台接口的请求方式 axios的介绍和基础配置可以看这个文档: 起步 | Axios中文文档 | Axios中文网 axios的封装 axios封装的重点有三个&#xff0c;一是设置全局config,比如请求的基础路径&#xff0c;超时时间等&#xff0c;第二点是在每次…

kafka服务介绍

kafka 安装使用管理 Kafka Apache Kafka 是一个开源的分布式事件流平台&#xff0c;主要用于实时数据传输和流处理。它最初由 LinkedIn 开发&#xff0c;并在 2011 年成为 Apache 基金会的顶级项目。Kafka 设计的目标是处理大规模的数据流&#xff0c;同时提供高吞吐量、低延迟…

Java语言程序设计——篇八(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; Java常用核心类 主要内容Object: 终极父类toString( )方法equals( )方法getClass( )方法hashCode( )方法clone( )方法finalize( )方法实战演练 …

8. kubernetes资源——ingress

kubernetes资源——ingress 一、ingress介绍1、作用2、实现方式3、核心组件 二、部署ingress1、下载ingress_1.9.6.yaml文件2、事先导入镜像3、部署ingress 三、通过ingress发布k8s中的服务1、创建服务2、创建ingress规则发布服务3、测试访问 一、ingress介绍 1、作用 ingres…