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

分布式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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【漏洞复现】WordPress Country State City Dropdown CF7插件 SQL注入漏洞(CVE-2024-3495)

0x01 产品简介 Country State City Dropdown CF7插件是一个功能强大、易于使用的 WordPress 插件&#xff0c;它为用户在联系表单中提供国家.州/省和城市的三级下拉菜单功能&#xff0c;帮助用户更准确地填写地区信息。同时&#xff0c;插件的团队和支持也非常出色&#xff0c…

启智CV机器人,ROS,ubuntu 20.04 【最后一步有问题】

资料&#xff1a; https://wiki.ros.org/kinetic/Installation/Ubuntu https://blog.csdn.net/qq_44339029/article/details/120579608 装VM。 装ubuntu20.04 desktop.iso系统。 装vm工具&#xff1a; sudo apt update sudo dpkg --configure -a sudo apt-get autoremove o…

热敏电阻的设计

热敏电阻(NTC)的作用&#xff1a;抑制开机时的浪涌电流。防止开机瞬间产生的浪涌电流损坏后面的元件。 取值依据:根据对开机的脉冲电流&#xff08;浪涌电流&#xff09;小于多少A&#xff1f; 由,这个U是指最大输入电压&#xff0c;I为要求的浪涌电流。 NTC是负温度系数的热…

生成树协议STP(Spanning Tree Protocol)

为了提高网络可靠性&#xff0c;交换网络中通常会使用冗余链路。然而&#xff0c;冗余链路会给交换网络带来环路风险&#xff0c;并导致广播风暴以及MAC地址表不稳定等问题&#xff0c;进而会影响到用户的通信质量。生成树协议STP&#xff08;Spanning Tree Protocol&#xff0…

Flutter基础 -- Dart 语言 -- 注释函数表达式

目录 1. 注释 1.1 单行注释 1.2 多行注释 1.3 文档注释 2. 函数 2.1 定义 2.2 可选参数 2.3 可选参数 默认值 2.4 命名参数 默认值 2.5 函数内定义 2.6 Funcation 返回函数对象 2.7 匿名函数 2.8 作用域 3. 操作符 3.1 操作符表 3.2 算术操作符 3.3 相等相关的…

安卓SystemServer进程详解

目录 一、概述二、源码分析2.1 SystemServer fork流程分析2.1.1 [ZygoteInit.java] main()2.1.2 [ZygoteInit.java] forkSystemServer()2.1.3 [Zygote.java] forkSystemServer()2.1.4 [com_android_internal_os_Zygote.cpp]2.1.5 [com_android_internal_os_Zygote.cpp] ForkCom…

ssm 校园商店管理系-计算机毕业设计源码28326

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;校园商店当然也不能排除在外。校园商店是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;采用…

迁移ISE ChipScope逻辑分析器到Vivado硬件管理器

迁移ISE ChipScope逻辑分析器到Vivado硬件管理器 介绍 本章介绍AMD Vivado™Design Suite硬件管理器&#xff0c;以及这些工具之间的关系 到ISE™设计套件ChipScope™逻辑分析器工具&#xff0c;以及如何迁移IP核 从ISE ChipScope环境到Vivado Design Suite。 Vivado硬件管理器…

API测试工具领域,Postman的10个最佳替换

Postman 赢得了流行且有效的 API 工具的声誉。然而&#xff0c;对于那些寻求更符合特定需求和偏好的替代方案的人来说&#xff0c;存在一些值得注意的选择。这些 Postman 替代方案提供了独特的特性和功能&#xff0c;可满足测试过程的各个方面的需求。 在本博客中&#xff0c;…

Oracle Linux上安装ORDS

ORDS就是Oracle REST Data Services。 环境如下&#xff1a; Oracle Linux 8Oracle Database 19cIP地址为A.B.C.D 要安装最新版本的ORDS&#xff0c;当前为24.1.1。 全程参考文档&#xff1a;Installing and Configuring Oracle REST Data Services 安装ORDS 添加reposit…

视频汇聚管理平台EasyCVR程序报错“create jwtSecret del server class:0xf98b6040”的原因排查与解决

国标GB28181协议EasyCVR安防视频监控平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流…

FreeRtos进阶——软件定时器内部逻辑

在FreeRtos软件定时器&#xff0c;是根据Systick来判断定时是否到达&#xff0c;可以是单次定时器也可以是循环定时器。在创建定时器任务后&#xff0c;在每一次SysTick中断中&#xff0c;会将定时器时钟到的任务写入定时器任务队列。在prvTimerTask任务&#xff08;守护任务&a…

微信小程序-页面配置

一、页面配置文件的作用 小程序中&#xff0c;每个页面都有自己的.json配置文件&#xff0c;用来对当前页面的窗口外观、页面效果等进行配置 二、页面配置和全局配置的关系 小程序中&#xff0c;app.json中的window节点&#xff0c;可以全局配置小程序中每个页面的窗口表现 …

算法(一)递归

文章目录 递归的概念递归三要素递归demo打印100次“hello word”斐波那契数列 递归的概念 递归算法是一种直接或者间接调用自身函数或者方法的算法。 递归三要素 递归条件结束 因为递归是循环调用自身&#xff0c;因此就必须要有结束条件&#xff0c;或者就会OOM。 函数的功…

线性代数|机器学习-P1课程简介

文章目录 1. 书籍下载2. 正文 1. 书籍下载 链接&#xff1a;https://pan.baidu.com/s/1QbK0enLh0x4nU1c4Tqwlkw 提取码&#xff1a;r7ft 本课程回顾线性代数在概率论、统计学、优化和深度学习中的应用。是GILBERT STRANG教授的有一个经典的课程。课程将线性代数分为如下部分&a…

为什么都说视频号小店值得做,具体该怎么做?新手必学

大家好&#xff0c;我是电商花花。 所有人都在告诉你2024年应该做视频号小店&#xff0c;但没有人告诉你到底应该怎么做。 今天给大家说一下为什么2024年都推荐大家去做视频号小店&#xff0c;以及分享一些视频号小店的实操干货&#xff0c;可以帮助大家更快更稳的做店。 首先…

Linux网络-Socket套接字_Windows与Linux端双平台基于Udp传输协议进行多线程跨平台的服务器与客户端网络通信的简易聊天室实现

文章目录 一、Socket套接字二、Udp 常见API1. int socket(int domain, int type, int protocol);2. int bind(int socket, const struct sockaddr *address, socklen_t address_len);struct sockaddr 3. ssize_t recvfrom(int socket, void *restrict buffer, size_t length, i…

【数据结构与算法】算法优化、时间复杂度、空间复杂度

文章目录 一、什么是复杂度&#xff1f;二、大O表示法三、时间复杂度计算四、常见复杂度的比较五、算法优化的核心方法论六、常见算法复杂度五、总结 一、什么是复杂度&#xff1f; 复杂度是衡量代码运行效率的重要的度量因素。 而复杂度主要就是指时间复杂度和空间复杂度。 …

三菱MR-J4系列伺服驱动器E7.1和32.3故障报警处理总结

三菱MR-J4系列伺服驱动器E7.1和32.3故障报警处理总结 三菱MR-J4系列伺服驱动器出现报警,故障代码为:E7.1和32.3,查阅手册可以看到E7.1和32.3的报警解释信息, 如下图所示,此时简单运动控制模块上的ERROR灯亮, 如下图所示,用GX WORKS3打开备份程序,找到FX5-80SSC-…

一维时间序列信号的广义傅里叶族变换(Matlab)

广义傅里叶族变换是一种时频变换方法&#xff0c;傅里叶变换、短时傅里叶变换、S变换和许多小波变换都是其特殊情况&#xff0c;完整代码及子函数如下&#xff0c;很容易读懂&#xff1a; % Run a demo by creating a signal, transforming it, and plotting the results% Cre…