当前位置: 首页 > news >正文

微服务实战——登录(普通登录、社交登录、SSO单点登录)

登录

1.1. 用户密码

@PostMapping("/login")public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session){R r = memberFeignService.login(vo);if(r.getCode() == 0){MemberRespVo data = r.getData("data", new TypeReference<MemberRespVo>() {});session.setAttribute("loginUser", data);return "redirect:http://gulimall.com";}else {Map<String, String> errors = new HashMap<>();errors.put("msg", r.getData("msg", new TypeReference<String>(){}));redirectAttributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}@GetMapping("/login.html")public String loginPage(HttpSession session){Object loginUser = session.getAttribute("loginUser");if(loginUser != null){return "redirect:http://gulimall.com";}return "login";}
@Overridepublic MemberEntity login(MemberLoginVo vo) {String loginacct = vo.getLoginacct();String password = vo.getPassword();MemberEntity memberEntity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", loginacct).or().eq("mobile", loginacct));BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();if(memberEntity == null){return null;}if (passwordEncoder.matches(password, memberEntity.getPassword())) {return memberEntity;}return null;}

1.2. 社交登录

QQ 、微博、 github 等网站的用户量非常大,别的网站为了简化自我网站的登陆与注册逻辑,引入社交登陆功能;

步骤:

1 )、用户点击 QQ 按钮

2 )、引导跳转到 QQ 授权页

3)、用户主动点击授权,跳回之前网页。

1.2.1. OAuth2.0
  • OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
  • OAuth2.0:对于用户相关的 OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。

  • 官方版流程:

(A )用户打开客户端以后,客户端要求用户给予授权。

(B )用户同意给予客户端授权。

(C )客户端使用上一步获得的授权,向认证服务器申请令牌。

(D )认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E )客户端使用令牌,向资源服务器申请获取资源。

(F )资源服务器确认令牌无误,同意向客户端开放资源。

OAuth2.0流程:

  • 使用Code换取AccessToken,Code只能用一次
  • 同一个用户的accessToken一段时间是不会变化的,即使多次获取
1.2.2. 代码实现

1)、进入微博开放平台

微博组件_微博开放平台

2)、添加社交登录回调接口

认证接口

  • 通过HttpUtils发送请求获取token,并将token等信息交给member服务进行社交登录
  • 若获取token失败或远程调用服务失败,则封装错误信息重新转回登录页

修改“com.cwh.gulimall.auth.feign.MemberFeignService”类,代码如下:

@PostMapping("/member/member/oauth2/login")
public R oauth2Login(@RequestBody SocialUser socialUser);

添加“com.cwh.gulimall.auth.vo.SocialUser”类,代码如下:

package com.cwh.gulimall.auth.vo;import lombok.Data;@Data
public class SocialUser {private String access_token;private String remind_in;private long expires_in;private String uid;private String isRealName;
}

添加“com.cwh.gulimall.auth.vo.MemberResponseVO”类,代码如下:

package com.cwh.gulimall.auth.vo;import lombok.Data;
import lombok.ToString;import java.util.Date;@ToString
@Data
public class MemberResponseVO {private Long id;/*** 会员等级id*/private Long levelId;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 昵称*/private String nickname;/*** 手机号码*/private String mobile;/*** 邮箱*/private String email;/*** 头像*/private String header;/*** 性别*/private Integer gender;/*** 生日*/private Date birth;/*** 所在城市*/private String city;/*** 职业*/private String job;/*** 个性签名*/private String sign;/*** 用户来源*/private Integer sourceType;/*** 积分*/private Integer integration;/*** 成长值*/private Integer growth;/*** 启用状态*/private Integer status;/*** 注册时间*/private Date createTime;private String socialUid;private String accessToken;private long expiresIn;
}

添加“com.cwh.gulimall.auth.controller.Oauth2Controller”类,代码如下:

@Controller
public class OauthController {@Autowiredprivate MemberFeignService memberFeignService;@RequestMapping("/oauth2.0/weibo/success")public String authorize(String code, RedirectAttributes attributes) throws Exception {// 1、使用code换取token,换取成功则继续2,否则重定向至登录页Map<String, String> query = new HashMap<>();query.put("client_id", "2144***074");query.put("client_secret", "ff63a0d8d5*****29a19492817316ab");query.put("grant_type", "authorization_code");query.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");query.put("code", code);// 发送post请求换取tokenHttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<String, String>(), query, new HashMap<String, String>());Map<String, String> errors = new HashMap<>();if (response.getStatusLine().getStatusCode() == 200) {// 2. 调用member远程接口进行oauth登录,登录成功则转发至首页并携带返回用户信息,否则转发至登录页String json = EntityUtils.toString(response.getEntity());SocialUser socialUser = JSON.parseObject(json, new TypeReference<SocialUser>() {});R login = memberFeignService.login(socialUser);// 2.1 远程调用成功,返回首页并携带用户信息if (login.getCode() == 0) {String jsonString = JSON.toJSONString(login.get("memberEntity"));MemberResponseVo memberResponseVo = JSON.parseObject(jsonString, new TypeReference<MemberResponseVo>() {});attributes.addFlashAttribute("user", memberResponseVo);return "redirect:http://gulimall.com";}else {// 2.2 否则返回登录页errors.put("msg", "登录失败,请重试");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}else {errors.put("msg", "获得第三方授权失败,请重试");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}

登录接口

  • 登录包含两种流程,实际上包括了注册和登录
  • 如果之前未使用该社交账号登录,则使用token调用开放api获取社交账号相关信息,注册并将结果返回
  • 如果之前已经使用该社交账号登录,则更新token并将结果返回

添加“com.cwh.gulimall.member.vo.SocialUser”类,代码如下:

package com.cwh.gulimall.member.vo;import lombok.Data;@Data
public class SocialUser {private String access_token;private String remind_in;private long expires_in;private String uid;private String isRealName;
}
修改“com.cwh.gulimall.member.controller.MemberController”类,代码如下:@PostMapping("/oauth2/login")public R oauth2Login(@RequestBody SocialUser socialUser){MemberEntity entity = memberService.login(socialUser);if (entity != null){return R.ok().setData(entity);}else {return R.error(BizCodeEnume.LOGINACCT_PASSWORD_INVAILD_EXCEPTION.getCode(),BizCodeEnume.LOGINACCT_PASSWORD_INVAILD_EXCEPTION.getMsg());}}

修改gulimall_ums.ums_member表结构,sql如下:

ALTER TABLE `gulimall_ums`.`ums_member` 
ADD COLUMN `social_uid` varchar(255) NULL COMMENT '社交用户id' AFTER `create_time`,
ADD COLUMN `access_token` varchar(255) NULL COMMENT '访问token' AFTER `social_uid`,
ADD COLUMN `expires_in` int NULL COMMENT '过期时间戳' AFTER `access_token`;

修改“com.cwh.gulimall.member.entity.MemberEntity”类,新增三个属性,代码如下:

修改“com.cwh.gulimall.member.service.MemberService”类,代码如下:

MemberEntity login(SocialUser socialUser);

修改“com.cwh.gulimall.member.service.impl.MemberServiceImpl”类,代码如下:

@Override
public MemberEntity login(SocialUser socialUser) {
// 1 根据 uid 判断当前用户是否以前用社交平台登录过系统
MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", socialUser.getUid()));
if (!StringUtils.isEmpty(memberEntity)) {// 说明这个用户之前已经注册过MemberEntity update = new MemberEntity();update.setId(memberEntity.getId());update.setAccessToken(socialUser.getAccess_token());update.setExpiresIn(socialUser.getExpires_in());this.baseMapper.updateById(update);memberEntity.setAccessToken(socialUser.getAccess_token());memberEntity.setExpiresIn(socialUser.getExpires_in());return memberEntity;
} else {// 未找到则注册 根据社交平台的开放接口查询用户的开放信息存储到系统MemberEntity register = new MemberEntity();try {Map<String, String> query = new HashMap<>();query.put("access_token", socialUser.getAccess_token());query.put("uid", socialUser.getUid());HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), query);if (response.getStatusLine().getStatusCode() == 200) {String json = EntityUtils.toString(response.getEntity());JSONObject jsonObject = JSON.parseObject(json);String name = jsonObject.getString("name");String gender = jsonObject.getString("gender");// ......register.setNickname(name);register.setGender("m".equals(gender) ? 1 : 0);// .....}} catch (Exception e) {log.warn("调用微博接口获取信息异常{}", e);}register.setSocialUid(socialUser.getUid());register.setAccessToken(socialUser.getAccess_token());register.setExpiresIn(socialUser.getExpires_in());this.baseMapper.insert(register);return register;
}

小结

Oauth2.0 ;授权通过后,使用 code 换取 access_token ,然后去访问任何开放 API

1 )、 code 用后即毁

2 )、 access_token 在几天内是一样的

3 )、 uid 永久固定

1.3. SpringSession

1.3.1. Session共享问题
1.3.1.1. session原理

jsessionid相当于银行卡,存在服务器的session相当于存储的现金,每次通过jsessionid取出保存的数据。

问题:但是正常情况下session不可跨域,它有自己的作用范围

1.3.1.2. 分布式下session共享问题

  • 同一个服务,复制多份,session不同步问题
  • 不同服务,session不能共享问题
1.3.2. Session共享问题解决
1.3.2.1. session复制

1.3.2.2. 客户端存储

1.3.2.3. hash一致性

1.3.2.4. 统一存储

1.3.2.5. 不同服务,子域session共享

1.3.3. SpringSession整合redis

通过SpringSession修改session的作用域

1.3.3.1. 环境搭建

gulimall-auth-server模块

pom导入依赖

<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>

修改apllication.properties配置

spring.session.store-type=redis

主配置类添加注解@EnableRedisHttpSession

修改“com.cwh.gulimall.auth.GulimallAuthServerApplication”类,代码如下:

@EnableRedisHttpSession
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallAuthServerApplication {public static void main(String[] args) {SpringApplication.run(GulimallAuthServerApplication.class, args);}
}
1.3.3.2. 自定义配置
1.3.4. SpringSession核心原理

核心原理:

1)、@EnableRedisHttpSession导入RedisHttpSessionConfiguration.class配置
1、给容器中添加了一个组件
SessionRepository=》》》 【RedisIndexedSessionRepository】=>redis操作session.session的增删改查的封装类
2、SessionRepositoryFilter=》Filter: session存储过滤器,每个请求过来都必须经过filter
1、创建的时候,就自动从容器中获取到了SessionRepository:
2、原生的request,response都被包装。SessionRepositoryRequestWrapper,SessionRepositoryResponseWrapper
3、以后获取session.request.getSession()
4、wrapperedRequest.getSession();===>SressionRepository中获取到

自动延期。redis中的数据也是有过期时间的

装饰者模式 - SessionRepositoryFilter

  • 原生的获取session时是通过HttpServletRequest获取的
  • 这里对request进行包装,并且重写了包装request的getSession()方法
1.3.5. 页面调整
1.3.5.1. 只要登录成功,缓存有用户数据,再点击登录链接,直接调转到首页;把GulimallWebConfig登录页的映射注释掉

修改“com.cwh.gulimall.auth.controller.LoginController”类,代码如下:

@GetMapping("/login.html")
public String loginPage(HttpSession session){
Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);
if (attribute == null) {//没登录return "login";
} else{return "redirect:http://gulimall.com";
}
}
1.3.5.2. 账号密码方式登录也要显示用户名

正常登录也要显示用户名,返回时也要给他放入用户信息

修改“com.cwh.gulimall.auth.controller.LoginController”类,代码如下:

@PostMapping("/login")
public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session) {log.info("登录请求参数:{}", JSON.toJSONString(vo));//远程登录R r = memberFeignService.login(vo);if (r.getCode() == 0) {MemberResponseVO loginUser = r.getData(new TypeReference<MemberResponseVO>() {});// 成功放到session中session.setAttribute(AuthServerConstant.LOGIN_USER, loginUser);return "redirect:http://gulimall.com";} else {Map<String, String> errors = new HashMap<>();errors.put("msg", r.getData("msg", new TypeReference<String>() {}));redirectAttributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}
}
1.3.5.3. 只要登陆成功每个页面都显示用户名

gulimall-search服务页面显示用户名,需要先搭建好SpringSession环境

导入依赖

     <!--整合SpringSession完成session共享问题--><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

修改配置

#配置redis
spring.redis.host=192.168.119.127
spring.redis.port=6379
#session存储格式
spring.session.store-type=redis

加注解

添加SpringSession配置类

@Configuration
public class GulimallSessionConfig {@Beanpublic CookieSerializer cookieSerializer(){DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();cookieSerializer.setDomainName("gulimall.com");cookieSerializer.setCookieName("GULISESSION");return cookieSerializer;}@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer(){return new GenericJackson2JsonRedisSerializer();}
}

1.4. SSO 单点登录

Single Sign On 一处登陆、处处可用

前置概念

1 )、单点登录业务介绍

早期单一服务器,用户认证。

缺点:单点性能压力,无法扩展

分布式,SSO(single sign on)模式

多系统

解决 :

  • 用户身份信息独立管理,更好的分布式管理。
  • 可以自己扩展安全策略
  • 跨域不是问题

缺点:

  • 认证服务器访问压力较大。

gitee参考项目:xxl-sso: 一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。 拥有"轻量级、分布式、跨域、Cookie+Token均支持、Web+APP均支持"等特性;。现已开放源代码,开箱即用。

xxl-sso流程:

  • /xxl-sso-server 登录服务器 8080 ssoserver.com
  • /xxl-sso-web-sample-springboot 项目1 8081 client1.com
  • /xxl-sso-web-sample-springboot 项目2 8082 client2.com
#----------sso----------
127.0.0.1 ssoserver.com
127.0.0.1 client1.com
127.0.0.1 client2.com

核心:三个系统即使域名不一样,想办法给三个系统同步同一个用户的票据;

1)、中央认证服务器;ssoserver.com

2)、其他系统,想要登录去ssoserver.com登录,登录成功跳转回来

3)、只要有一个登录,其他都不用登录

4)、全系统统一一个sso-sessionid;所有系统可能域名都不相同

相关文章:

微服务实战——登录(普通登录、社交登录、SSO单点登录)

登录 1.1. 用户密码 PostMapping("/login")public String login(UserLoginVo vo, RedirectAttributes redirectAttributes, HttpSession session){R r memberFeignService.login(vo);if(r.getCode() 0){MemberRespVo data r.getData("data", new Type…...

windows 安装 ElasticSearch

1、下载安装包 下载地址&#xff1a;https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.14.3-windows-x86_64.zip ElasticSearch 目录结构如下&#xff1a; 2、配置JDK环境 ES比较耗内存&#xff0c;建议虚拟机4G或以上内存&#xff0c;jvm1g以上的内存分…...

Oracle Linux 9 (CentOS Stream 9) 安装 node.js 20

Oracle Linux 的 node 默认版本为 16&#xff0c;运行dnf update也无法改变大版本&#xff0c;还需要进行额外操作1 查看支持的版本 sudo dnf module list nodejs输出如下 Last metadata expiration check: 3:37:22 ago on Fri 11 Oct 2024 09:08:18 PM JST. Oracle Linux 9 Ap…...

【Axure安装包与汉化包附带授权证书】

一、下载Axure安装包与汉化包附带授权证书 1.下载汉化包 【快传】: 点击链接即可保存 2.解压安装包 解压下载好的压缩包&#xff0c;能看到有lang也就是汉化包&#xff0c;AxureRP-Setup-RC.exe 也就是Axure9的安装程序&#xff0c;以及汉化说明和授权码。 二、安装Axure9…...

SSH隧道验证的原理及实现例子

SSH 隧道验证原理详解 **SSH 隧道&#xff08;SSH Tunneling&#xff09;**是通过 SSH 协议将数据在客户端和服务器之间加密传输的一种技术。它可以在不安全的网络上创建一个安全的、加密的通道&#xff0c;用于传输各种数据&#xff0c;例如通过不安全的网络远程登录、传输文…...

[计算机视觉]chapter1

一、什么是计算机视觉 计算机视觉就是用计算机编程,并设计算法来理解在这些图像中有什么。计算机视觉是一门研究如何使机器“看”的科学,更进一步的说,就是是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更适合人眼…...

RTKLIB学习记录【postpos、execses_b、execses_r】

本文主要记录对RTKLIB源码中postpos、execses_b、execses_r 函数的源码解读&#xff0c;不涉及其中的天线、星历等文件读取的内容&#xff0c;且为个人理解&#xff0c;如果有误&#xff0c;欢迎交流讨论。 一、postpos 函数部分 /rxn2rtkp函数 → postpos函数传递参数&#x…...

docker,docker-desktop,docker-compose download

docker docker-compose download 百度网盘获取离线包链接release-notes 参考dockerdocker-composewlspowershell...

C#_带参数的委托进入队列执行

我们经常会遇到一些函数多个地方调用,但是只能单独执行的就需要把它放到队列中执行。 1.创建对应该方法的委托(传参和回参类型需要一致)。 //委托: public delegate void CameraTaskDelegate(byte cs, ref byte[] buffer);//对应函数: public void CameraSettingRead(by…...

【OpenCV】(二)—— 图片读取展示和保存

上一小节中我们成功安装了opencv&#xff0c;我们这次学习使用opencv最基础的功能&#xff0c;读取和展示图片&#xff0c;首先准备一张用于实验的样例图片【cat.jpg】如下&#xff1a; 然后就是创建一个python项目并导入相关依赖 import cv2读取图片 读取图片使用imread方法…...

【花卉识别系统】Python+卷积神经网络算法+人工智能+深度学习+图像识别+算法模型

一、介绍 花朵识别系统。本系统采用Python作为主要编程语言&#xff0c;基于TensorFlow搭建ResNet50卷积神经网络算法模型&#xff0c;并基于前期收集到的5种常见的花朵数据集&#xff08;向日葵、玫瑰、蒲公英、郁金香、菊花&#xff09;进行处理后进行模型训练&#xff0c;最…...

k8s、prometheus、grafana数据采集和展示的链路流程

k8s集群中&#xff0c;容器级别的数据采集是由cAdvisor程序实现 cAdvisor # Container Advisor 容器顾问 cAdvisor程序是kubelet组件的一部分。 每个节点&#xff0c;包括master节点&#xff0c;都有一个kubelet系统服务&#xff0c; kukelet负责管理pod和容…...

sentinel dashboard改造落地设计实现解释(一)-分布式fetcher和metrics存储/搜索

背景 微服务是目前java主流架构,微服务架构技术栈有,服务注册中心,网关,熔断限流,服务同学,配置中心等组件,其中,熔断限流主要3个功能特性,限流,熔断,快速失败。Sentinel是阿里开源的熔断限流组件,sentinel dashboard是演示级别,表现在metrics采集是单机版,metri…...

LabVIEW提高开发效率技巧----时序分析

一、什么是时序分析&#xff1f; 时序分析是优化LabVIEW程序性能的重要步骤。它通过分析程序各个部分的执行时间&#xff0c;帮助开发者找到程序运行中的瓶颈&#xff0c;并进行有针对性的优化。在LabVIEW中&#xff0c;Profile Performance and Memory工具是进行时序分析的关…...

python不用ide也能进行调试

import pdb pdb.set_trace()import pdb 和 pdb.set_trace() 是 Python 中用于调试代码的工具。以下是它们的具体含义和用法&#xff1a; import pdb pdb 是 Python 的内置调试器模块&#xff0c;允许开发者在运行时进行代码调试。 通过 import pdb 语句&#xff0c;你可以引入…...

Django学习笔记之Django基础学习

Django笔记 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录…...

smartctl 设置硬盘的 write-caching

sg3 一、sg3查看缓存状态 您可以使用sg_modes命令来查看SAS盘和SATA盘的缓存状态。例如&#xff0c;要查看/dev/sdb设备的缓存状态&#xff0c;您可以执行以下命令&#xff1a; sg_modes -p 8,0 /dev/sdb 二、sg3关闭机械盘写缓存状态&#xff08;仅适用于SAS盘&#xff09…...

【Spring AI】Java实现类似langchain的向量数据库RAG_原理与具体实践

介绍一下RAG&#xff1a; 检索增强生成&#xff08;RAG&#xff09;是一种技术&#xff0c;它结合了检索模型和生成模型来提高文本生成的质量。通过从企业私有或专有的数据源中检索相关信息&#xff0c;并将这些信息与大型语言模型相结合&#xff0c;RAG能够显著减少模型产生幻…...

linux下使用systemctl设置开机自动运行程序

本文介绍在Linux下&#xff0c;使用systemctl设置开机自动运行程序&#xff0c;实现创建一个systemd服务单元文件&#xff0c;并启用该服务的方法。 1、创建.service文件 在/etc/systemd/system/目录下创建一个以.service结尾的文件&#xff0c;例如myapp.service&#xff1a…...

复位电路的亚稳态

复位导致亚稳态的概念&#xff1a; 同步电路中&#xff0c;输入数据需要与时钟满足setup time和hold time才能进行数据的正常传输&#xff08;数据在这个时间段内必须保持不变&#xff1a;1不能变为0&#xff0c;0也不能变为1&#xff09;&#xff0c;防止亚稳态&#xff1b; …...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...