高并发项目-分布式Session解决方案

news2025/1/21 4:54:00

分布式Session解决方案

1.保存Session,进入商品列表页面

1.保存Session
1.编写工具类
1.MD5Util.java
package com.sxs.seckill.utils;

import org.apache.commons.codec.digest.DigestUtils;

/**
 * Description: MD5加密工具类
 *
 * @Author sun
 * @Create 2024/5/5 14:23
 * @Version 1.0
 */
public class MD5Util {
    /**
     * 将一个字符串转换为MD5
     * @param src
     * @return
     */
    public static String md5(String src) {
        return DigestUtils.md5Hex(src);
    }

    // 固定的salt
    public static final String SALT = "4tIY5VcX";
    // 第一次加密加盐
    public static String inputPassToMidPass(String inputPass) {
        // 加盐
        String str = SALT.charAt(0) + inputPass + SALT.charAt(6);
        return md5(str);
    }

    /**
     * 第二次加密加盐
     * @param midPass
     * @param salt
     * @return
     */
    public static String midPassToDBPass(String midPass, String salt) {
        String str = salt.charAt(0) + midPass + salt.charAt(5);
        return md5(str);
    }

    /**
     * 两次加密
     * @param input
     * @param saltDB
     * @return
     */
    public static String inputPassToDBPass(String input, String saltDB) {
        String midPass = inputPassToMidPass(input);
        String dbPass = midPassToDBPass(midPass, saltDB);
        return dbPass;
    }
}

2.CookieUtil.java
package com.sxs.seckill.utils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * Description: Cookie工具类
 *
 * @Author sun
 * @Create 2024/5/6 16:04
 * @Version 1.0
 */


public class CookieUtil {
    /**
     * 得到 Cookie 的值, 不编码
     *
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String
            cookieName) {
        return getCookieValue(request, cookieName, false);
    }

    /**
     * 得到 Cookie 的值, *
     *
     * @param request
     * @param cookieName
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String
            cookieName, boolean isDecoder) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                    } else {
                        retValue = cookieList[i].getValue();
                    }
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     * 得到 Cookie 的值, *
     *
     * @param request
     * @param cookieName
     * @param encodeString
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String
            cookieName, String encodeString) {
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || cookieName == null) {
            return null;
        }
        String retValue = null;
        try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList[i].getName().equals(cookieName)) {
                    retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                    break;
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return retValue;
    }

    /**
     * 设置 Cookie 的值 不设置生效时间默认浏览器关闭即失效,也不编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }

    /**
     * 设置 Cookie 的值 在指定时间内生效,但不编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, int cookieMaxage) {
        setCookie(request, response, cookieName, cookieValue, cookieMaxage,
                false);
    }

    /**
     * 设置 Cookie 的值 不设置生效时间,但编码
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, boolean isEncode) {
        setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }

    /**
     * 设置 Cookie 的值 在指定时间内生效, 编码参数
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, int cookieMaxage, boolean
                                         isEncode) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage,
                isEncode);
    }

    /**
     * 设置 Cookie 的值 在指定时间内生效, 编码参数(指定编码)
     */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, int cookieMaxage, String
                                         encodeString) {
        doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }

    /**
     * 删除 Cookie 带 cookie 域名
     */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
        doSetCookie(request, response, cookieName, "", -1, false);
    }

    /**
     * 设置 Cookie 的值,并使其在指定时间内生效
     *
     * @param cookieMaxage cookie 生效的最大秒数
     */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue,
                                          int cookieMaxage, boolean isEncode) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0) {
                cookie.setMaxAge(cookieMaxage);
            }
            // if (null != request) {// 设置域名的 cookie
            // String domainName = getDomainName(request);
            // if (!"localhost".equals(domainName)) {
            // cookie.setDomain(domainName);
            // }
            // }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置 Cookie 的值,并使其在指定时间内生效
     *
     * @param cookieMaxage cookie 生效的最大秒数
     */
    private static final void doSetCookie(HttpServletRequest request,
                                          HttpServletResponse response, String cookieName, String cookieValue,
                                          int cookieMaxage, String encodeString) {
        try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0) {
                cookie.setMaxAge(cookieMaxage);
            }
            if (null != request) {// 设置域名的 cookie
                String domainName = getDomainName(request);
                System.out.println(domainName);
                if (!"localhost".equals(domainName)) {
                    cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 得到 cookie 的域名
     */
    private static final String getDomainName(HttpServletRequest request) {
        String domainName = null;
        // 通过 request 对象获取访问的 url 地址
        String serverName = request.getRequestURL().toString();
        if ("".equals(serverName)) {
            domainName = "";
        } else {
            // 将 url 地下转换为小写
            serverName = serverName.toLowerCase();
            // 如果 url 地址是以 http://开头 将 http://截取
            if (serverName.startsWith("http://")) {
                serverName = serverName.substring(7);
            }
            int end = serverName.length();
            // 判断 url 地址是否包含"/"
            if (serverName.contains("/")) {
                // 得到第一个"/"出现的位置
                end = serverName.indexOf("/");
            }
            // 截取
            serverName = serverName.substring(0, end);
            // 根据"."进行分割
            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3) {
                // www.abc.com.cn
                domainName = domains[len - 3] + "." + domains[len - 2] + "." +
                        domains[len - 1];
            } else if (len > 1) {
                // abc.com or abc.cn
                domainName = domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }
        if (domainName.indexOf(":") > 0) {
            String[] ary = domainName.split("\\:");
            domainName = ary[0];
        }
        return domainName;
    }
}

2.关于session和cookie关系的回顾
  • 当浏览器请求到服务端时cookie会携带sessionid
  • 然后在服务端getSession时会得到当前用户的session
  • cookie-sessionid 连接到session
3.修改UserServiceImpl.java的doLogin方法,增加保存信息到session的逻辑

image-20240506180654918

4.测试,用户票据成功保存到cookie中

image-20240506164400785

2.访问到商品列表页面
1.编写GoodsController.java 验证用户登录后进入商品列表页
package com.sxs.seckill.controller;

import com.sxs.seckill.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

/**
 * Description:
 *
 * @Author sun
 * @Create 2024/5/6 18:16
 * @Version 1.0
 */
@Controller
@Slf4j
@RequestMapping("/goods")
public class GoodsController {
    // 进入到商品首页
    @RequestMapping("/toList")
    public String toList(HttpSession session, Model model, @CookieValue("userTicket") String ticket) {
        // 首先判断是否有票据
        if (null == ticket) {
            return "login";
        }
        // 根据票据来获取用户信息
        User user = (User) session.getAttribute(ticket);
        if (null == user) {
            return "login";
        }
        // 将用户信息存入model中,返回到前端
        model.addAttribute("user", user);
        return "goodsList";
    }
}

2.商品列表页goodsList.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>商品列表</title>
</head>
<body>
<h1>商品列表</h1>
<p th:text="'hi: ' + ${user.nickname}"></p>
</body>
</html>
3.测试登录成功后进入商品列表页

image-20240506182533204

image-20240506182619668

2.分布式session解决方案

1.session绑定/粘滞(不常用)

image-20240507084426254

image-20240507084410603

2.session复制

image-20240507084606158

image-20240507084657719

3.前端存储

image-20240507084727017

4.后端集中存储

image-20240507084749944

3.方案一:SpringSession实现分布式Session

1.安装使用redis-desktop-manager
1.一直下一步,安装到D盘

image-20240507085744926

2.首先要确保redis集群的端口是开放的并使其支持远程访问(之前配置过)
3.使用telnet指令测试某个服务是否能够连接成功
telnet 140.143.164.206 7489

image-20240507090655656

4.连接Redis,先测试连接然后确定

image-20240507091103602

5.在redis命令行设置两个键

image-20240507091252851

6.在可视化工具查看

image-20240507091332860

2.项目整合Redis并配置分布式session
1.pom.xml引入依赖
        <!--spring data redis 依赖, 即 spring 整合 redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.4.5</version>
        </dependency>
        <!--pool2 对象池依赖-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.9.0</version>
        </dependency>
        <!--实现分布式 session, 即将 Session 保存到指定的 Redis-->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>
2.application.yml配置Redis
spring:
  redis:
    password:  # Redis服务器密码
    database: 0 # 默认数据库为0号
    timeout: 10000ms # 连接超时时间是10000毫秒
    lettuce:
      pool:
        max-active: 8 # 最大活跃连接数,使用负值表示没有限制,最佳配置为核数*2
        max-wait: 10000ms # 最大等待时间,单位为毫秒,使用负值表示没有限制,这里设置为10秒
        max-idle: 200 # 最大空闲连接数
        min-idle: 5 # 最小空闲连接数
    cluster:
      nodes:
        - 
        - 

image-20240507094324857

3.启动测试
1.登录

image-20240507094643899

2.Redis可视化工具发现session成功存到redis

image-20240507094628773

image-20240507095339215

4.方案二:统一存放用户信息到Redis

1.修改pom.xml,去掉分布式springsession的依赖

image-20240507103646670

2.将用户信息放到Redis
1.添加Redis配置类 com/sxs/seckill/config/RedisConfig.java
package com.sxs.seckill.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * Description:
 *
 * @Author sun
 * @Create 2024/4/29 21:29
 * @Version 1.0
 */
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template =
                new RedisTemplate<>();
        System.out.println("template=>" + template);
        RedisSerializer<String> redisSerializer =
                new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        // key 序列化方式
        template.setKeySerializer(redisSerializer);
        // value 序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // value hashmap 序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer =
                new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                Jackson2JsonRedisSerializer(Object.class);
        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间 600 秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}
2.修改 com/sxs/seckill/service/impl/UserServiceImpl.java
1.注入RedisTemplate

image-20240507104718538

2.修改doLogin方法,将用户信息放到Redis中

image-20240507104748489

3.启动测试
1.登录

image-20240507105110074

2.可视化工具查看用户信息

image-20240507105302615

3.实现使用Redis + Cookie实现登录,可以访问商品列表页面
1.刚才已经实现了Redis记录信息的功能,但是校验还没实现,修改GoodsController.java完成校验
1.注入RedisTemplate

image-20240507110303645

2.从Redis中获取校验信息,进行校验

image-20240507110342489

2.测试
1.登录成功后访问商品列表页面

image-20240507110534647

5.扩展:自定义参数解析器,直接获取User

1.修改 com/sxs/seckill/controller/GoodsController.java 使参数直接为User
    // 进入到商品首页
    @RequestMapping("/toList")
    public String toList(Model model, User user) {
        // 判断是否有用户信息
        if (null == user) {
            return "login";
        }
        // 将用户信息存入model中,返回到前端
        model.addAttribute("user", user);
        return "goodsList";
    }

2.service层添加方法,通过票据从Redis中获取User对象
1.UserService.java
    /**
     * 根据cookie获取用户
     * @param userTicket
     * @param request
     * @param response
     * @return
     */
    public User getUserByCookie(String userTicket, HttpServletRequest request, HttpServletResponse response);

2.UserServiceImpl.java
  • 这里需要注意,每次获取完User,需要重新设置Cookie,来刷新Cookie的时间
  • 原因是,调用这个的目的是为了校验,而用户访问每个页面都要进行校验,如果每次校验之后都不刷新Cookie的时间,一旦Cookie失效了,用户就要重新登陆
    @Override
    public User getUserByCookie(String userTicket, HttpServletRequest request, HttpServletResponse response) {
        // 判断是否有票据
        if (null == userTicket) {
            return null;
        }
        // 根据票据来获取用户信息,从redis中获取
        User user = (User) redisTemplate.opsForValue().get("user:" + userTicket);
        if (null == user) {
            return null;
        }
        // 重新设置cookie的有效时间
        CookieUtil.setCookie(request, response, "userTicket", userTicket);
        return user;
    }

3.编写自定义参数解析器对User类型参数进行解析 config/UserArgumentResolver.java
package com.sxs.seckill.config;

import com.sxs.seckill.pojo.User;
import com.sxs.seckill.service.UserService;
import com.sxs.seckill.utils.CookieUtil;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Description: 自定义参数解析器
 *
 * @Author sun
 * @Create 2024/5/7 14:39
 * @Version 1.0
 */
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
    @Resource
    private UserService userService;
    /*
     * 判断是否支持要转换的参数类型,简单来说,就是在这里设置要解析的参数类型
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        // 如果参数类型是User,则进行解析
        Class<?> parameterType = methodParameter.getParameterType();
        if (parameterType == User.class) {
            return true;
        }
        return false;
    }

    /**
     * 编写参数解析的逻辑
     *
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        // 首先获取request和response
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
        // 从cookie中获取票据
        String userTicket = CookieUtil.getCookieValue(request, "userTicket");
        // 如果票据为空,则返回null
        if (null == userTicket) {
            return null;
        }
        // 如果票据不为空,则根据票据获取用户信息
        User user = this.userService.getUserByCookie(userTicket, request, response);
        return user;
    }
}

4.编写config/WebConfig.java 将自定义参数解析器放到 resolvers 才能生效
package com.sxs.seckill.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.util.List;

/**
 * Description:
 *
 * @Author sun
 * @Create 2024/5/7 14:53
 * @Version 1.0
 */
@Configuration
// @EnableWebMvc 使用这个注解会导致SpringBoot的自动配置失效,一切都要自己配置,所以建议不要使用这个注解
public class WebConfig implements WebMvcConfigurer {
    // 注入自定义参数解析器
    @Resource
    private UserArgumentResolver userArgumentResolver;
    /**
     * 静态资源加载,静态资源放在哪里就怎么配置
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

    /**
     * 添加自定义参数解析器到resolvers中,才能生效
     * @param resolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(userArgumentResolver);
    }
}

5.测试
1.在登录之后,可以正常访问商品列表页面

image-20240507151255032

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

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

相关文章

uni-app实现页面通信EventChannel

uni-app实现页面通信EventChannel 之前使用了EventBus的方法实现不同页面组件之间的一个通信&#xff0c;在uni-app中&#xff0c;我们也可以使用uni-app API —— uni.navigateTo来实现页面间的通信。注&#xff1a;2.8.9 支持页面间事件通信通道。 1. 向被打开页面传送数据…

【Linux】操作系统之冯诺依曼体系

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 操作系统中 冯诺依曼体系 的相关内容。 如果看到最后您觉得这篇文…

File(文件)

File对象表示一个路径&#xff0c;可以是文件的路径&#xff0c;也可以是文件夹的路径。 这个路径可以存在&#xff0c;也允许不存在。 创建File对象的方法 public class test {public static void main(String [] args) {//根据字符串创建文件String str"C:\\Users\\PC…

每日一题——力扣141. 环形链表(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 专业点评 时间复杂度分析 空间复杂度分析 总结 我要更强 方法2&#x…

cdo | 常用命令

整理一下平时经常会使用的cdo命令 如何来更改netcdf数据中的变量名呢&#xff1f; 假设我现在有一个sst月平均数据,希望将里面的变量名称sst修改为sst_new netcdf oisst_monthly { dimensions:lat 180 ;lon 360 ;time UNLIMITED ; // (476 currently)nbnds 2 ; variable…

V90 PN总线伺服通过FB285速度控制实现正弦位置轨迹运动(解析法和数值法对比测试)

V90总线伺服相关内容请参考专栏系列文章,这里不在详述 1、V90伺服PN总线速度随动控制 V90伺服PN总线速度随动控制(手摇轮功能)_手摇轮可以接总线plc吗?-CSDN博客文章浏览阅读632次。V90PN总线控制相关内容,请参考下面文章链接:博途1200/1500PLC V90 PN通信控制 (FB284功能…

【html】用html模拟微信布局

您做的这个模拟微信布局的作品很不错,使用了Flexbox布局来实现元素的灵活排列。以下是关于您代码的一些分析和建议: 效果图: 代码分析: 全局样式重置: 您使用了* { margin: 0; padding: 0; }来重置所有元素的边距。这是一个常见的做法,可以避免不同浏览器默认样式的差…

ARM32开发——LED点灯

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 点灯的两种方式灌入电流法输出电流法扩展板点灯点灯方式点亮LED1-4完整实现 点灯的两种方式 不同颜色LED&#xff0c;达到相同亮度…

LLM Agent提效揭秘4:多智能体协作工作流深度剖析

在此之前&#xff0c;我们已经从论文到代码深度解读了吴恩达&#xff1a;GPT-4Agents Workflow&#xff0c;性能比肩GPT-5的三种大语言模型Agent工作流&#xff1a;反思、工具使用和规划。今天我们将深入最后一种Agent工作流&#xff1a;多智能体协作工作流。 想象一个未来&am…

3.spring源码:refresh()第一个方法prepareRefresh()

重点: 1.了解prepareRefresh()方法的作用:就是为容器刷新做了准备工作 2.整体了解refresh()方法 synchronized加锁的原因:刷新和销毁( "refresh" and "destroy" 都是一个完整的过程,需要加锁 改方法进入可以看到是空的,是为了扩展用的 该方法我们可以自己重…

visual studio code 全局搜索

VScode写代码的时候&#xff0c;会经常性的需要进行查找代码&#xff0c;那么怎么在Visual Studio Code中进行查找呢&#xff0c;下面就来大家vscode全局搜索的方法。 想要在vscode全局搜索进行全局搜索&#xff0c;使用快捷键CTRLSHIFTF即可进行搜索&#xff0c;也可以在左边…

GSM信令流程(附着、去附着、PDP激活、修改流程)

1、联合附着流程 附着包括身份认证、鉴权等 2、去附着流程 用户发起去附着 SGSN发起去附着 HLR发起去附着 GSSN使用S4发起去附着 3、Activation Procedures(PDP激活流程) 4、PDP更新或修改流程 5、Deactivate PDP Context 6、RAU(Routeing Area Update)流程 7、鉴权加…

NATS-研究学习

NATS-研究学习 文章目录 NATS-研究学习[toc]介绍说明提供的服务内容各模式介绍测试使用发布订阅&#xff08;Publish Subscribe&#xff09;请求响应&#xff08;Request Reply&#xff09;队列订阅&分享工作&#xff08;Queue Subscribers & Sharing Work&#xff09;…

vue+vant移动端显示table表格加横向滚动条

vant移动端显示table效果&#xff0c;增加复选框&#xff0c;可以进行多选和全选。加横向滚动条&#xff0c;可以看全部内容。 <template><div class"app-container"><div class"nav_text" style"position: relative;"><…

简单介绍QKeySequenceEdit的使用

QKeySequenceEdit是Qt框架中的一个便捷用户界面组件&#xff0c;用于输入和显示键盘快捷键。它提供了一个简单的界面&#xff0c;允许用户输入一个键盘快捷键&#xff0c;并将其显示为一个字符串。这在需要配置快捷键的应用程序中非常有用。在本文中&#xff0c;我们将详细介绍…

【C++】——string模拟实现

前言 string的模拟实现其实就是增删改查&#xff0c;只不过加入了类的概念。 为了防止与std里面的string冲突&#xff0c;所以这里统一用String。 目录 前言 一 初始化和销毁 1.1 构造函数 1.2 析构函数 二 迭代器实现 三 容量大小及操作 四 运算符重载 4.1 bool…

二叉树的顺序实现-堆

一、什么是堆 在数据结构中&#xff0c;堆&#xff08;Heap&#xff09;是一种特殊的树形数据结构&#xff0c;用数组存储&#xff0c;通常被用来实现优先队列。 堆具有以下特点&#xff1a; 堆是一棵完全二叉树&#xff08;Complete Binary Tree&#xff09;&#xff0c;即…

uni-app的网络请求库封装及使用(同时支持微信小程序)

其实uni-app中内置的uni.request()已经很强大了&#xff0c;简单且好用。为了让其更好用&#xff0c;同时支持拦截器&#xff0c;支持Promise 写法&#xff0c;特对其进行封装。同时支持H5和小程序环境&#xff0c;更好用啦。文中给出使用示例&#xff0c;可以看到使用变得如此…

算法(六)计数排序

文章目录 计数排序技术排序简介算法实现 计数排序 技术排序简介 计数排序是利用数组下标来确定元素的正确位置的。 假定数组有10个整数&#xff0c;取值范围是0~10&#xff0c;可以根据这有限的范围&#xff0c;建立一个长度为11的数组。数组下标从0到10&#xff0c;元素初始…

智慧校园有哪些特征

随着科技的飞速进步&#xff0c;教育领域正经历着一场深刻的变革。智慧校园&#xff0c;作为这场变革的前沿代表&#xff0c;正在逐步重塑我们的教育理念和实践方式。它不仅仅是一个概念&#xff0c;而是一个集成了物联网、大数据、人工智能等先进技术的综合生态系统&#xff0…