秒杀项目之服务调用分布式session

news2024/11/17 12:49:58

目录

nginx动静分离

服务调用

创建配置zmall-cart购物车模块

创建配置zmall-order订单模块

服务调用

spring session实战

什么是Spring Session

为什么要使用Spring Session

错误案例展示

配置spring-session

二级域名问题

用户登录


nginx动静分离

第1步:通过SwitchHosts新增二级域名:images.zmall.com

第2步:将本次项目的易买网所有静态资源js/css/images复制到nginx中的html目录下

第3步:在nginx的核心配置文件nginx.conf中新增二级域名images.zmall.com访问映射,用于实现nginx动静分离

注意:修改成功之后,重启nginx服务使其配置生效!!!

server{
    listen 80;
    server_name images.zmall.com;
    location / {
        root html;
        index index.html;
    }
}

检测静态资源服务器配置成功

http://images.zmall.com/css/style.css

 

第4步:删除zmall-product商品服务和zmall-gateway网关服务下的static静态资源,改用nginx中配置的静态资源

第5步:修改zmall-product商品服务中的templates/common/head.html

<#assign ctx>
    <#--域名,动态请求时需加入该前缀-->
    http://product.zmall.com
</#assign>
<#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源-->
<base href="http://images.zmall.com/"/>

第6步:分别重启zmall-product、zmall-gateway以及nginx后输入请求地址:zmall.com/product-serv/index.html访问商品服务首页,如下所示:

 如果出现IIS7,那么cmd窗口中执行下列指令

net stop w3svc

服务调用

创建配置zmall-cart购物车模块

第1步:基于Spring initializr创建zmall-cart购物车模块

第2步:将zmall-order订单模块配置到主模块中

<modules>
    ...
    <module>zmall-cart</module>
    ...
</modules>

第3步:修改pom.xml

<parent>
    <groupId>com.ycx.zmall</groupId>
    <artifactId>zmall</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<artifactId>zmall-cart</artifactId>

<dependencies>
    <dependency>
        <groupId>com.ycx.zmall</groupId>
        <artifactId>zmall-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

第4步:配置application.yml(端口:8030)

server:
  port: 8030
spring:
  application:
    name: zmall-cart
  datasource:
    #type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/zmall?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    username: root
    password: 1234
  freemarker:
    suffix: .html
    template-loader-path: classpath:/templates/
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
#mybatis-plus配置
mybatis-plus:
  #所对应的 XML 文件位置
  mapper-locations: classpath*:/mapper/*Mapper.xml
  #别名包扫描路径
  type-aliases-package: com.ycx.zmall.model
  configuration:
    #驼峰命名规则
    map-underscore-to-camel-case: true
#日志配置
logging:
  level:
    com.ycx.zmall.mapper: debug

第5步:在启动类上加入@EnableDiscoveryClient

第6步:分别将购物车页面和common/head.html导入到templates目录,并修改head.html中的ctx局部变量

<#assign ctx>
    <#--一级域名,动态请求时需加入该前缀-->
    http://cart.zmall.com
</#assign>
<#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源-->
<base href="http://images.zmall.com/"/>

 

第7步:在zmall-gateway网关服务中配置购物车的路由转发规则(重启gateway网关服务)

spring:
  application:
    name: zmall-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        ...
        - id: cart_route
          uri: lb://zmall-cart # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
          predicates:
            - Path=/cart-serv/**
          filters:
            - StripPrefix=1
            #此过滤器设置路由过滤器检查的请求属性,以确定是否应发送原始主机头,而不是由 HTTP 客户端确定的主机头
            - PreserveHostHeader

注意:这里要配置过滤器PreserveHostHeader,用于处理重定向时依然已原始主机头发送请求。

第8步:创建CartController并定义请求方法

@Controller
public class CartController {

    @RequestMapping("/cart.html")
    public String toCart(){
        return "buyCar";
    }

    @RequestMapping("/addCart")
    public String addCart(Integer pid,Integer num){
        return "redirect:/cart.html";
    }
}

注意:这里使用redirect重定向方式跳转页面,在SpringCloud gateway路由转发过程中会导致域名跳转变成了http请求方式,所以必须在Gateway网关服务中进行相关的配置。具体请参考第8步的gateway网关路由配置。

 

第9步:在zmall-product模块中修改加入购物车的请求方法,定向到购物车

<td><a href="http://cart.zmall.com/addCart?pid=${(product.id)!}&num=3" class="b_sure">去购物车结算</a><a href="#" class="b_buy">继续购物</a></td>

创建配置zmall-order订单模块

第1步:基于Spring initializr创建zmall-order订单模块

第2步:将zmall-order订单模块配置到主模块中

<modules>
    ...
    <module>zmall-order</module>
    ...
</modules>

第3步:修改pom.xml

<parent>
    <groupId>com.ycx.zmall</groupId>
    <artifactId>zmall</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
<artifactId>zmall-order</artifactId>

<dependencies>
    <dependency>
        <groupId>com.ycx.zmall</groupId>
        <artifactId>zmall-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

第4步:配置application.yml(端口:8040)

server:
  port: 8040
spring:
  application:
    name: zmall-order
  datasource:
    #type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/zmall?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    username: root
    password: 1234
  freemarker:
    suffix: .html
    template-loader-path: classpath:/templates/
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
#mybatis-plus配置
mybatis-plus:
  #所对应的 XML 文件位置
  mapper-locations: classpath*:/mapper/*Mapper.xml
  #别名包扫描路径
  type-aliases-package: com.ycx.zmall.model
  configuration:
    #驼峰命名规则
    map-underscore-to-camel-case: true
#日志配置
logging:
  level:
    com.ycx.zmall.mapper: debug

第5步:在启动类上加入@EnableDiscoveryClient@MapperScan({"com.ycx.zmall.mapper"})

第6步:定义订单接口,可从公共模块zmall-common中直接复制过来

第7步:创建OrderController并定义请求接口

@Controller
public class OrderController {
    @Autowired
    private IOrderService orderService;

    @RequestMapping("/orderUserList")
    @ResponseBody
    public List<Order> orderUserList(){
        return orderService.list(new QueryWrapper<Order>()
                .eq("userId",18));
    }
}

第8步:在zmall-gateway网关服务中配置购物车的路由转发规则(重启gateway网关服务)

spring:
  application:
    name: zmall-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        ...
        - id: order_route
          uri: lb://zmall-order # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
            - PreserveHostHeader

服务调用

在zmall-user中通过openfeign方式访问order服务接口

  • 定义openfeign接口

@FeignClient("zmall-order")
public interface IOrderFeignService {

    @RequestMapping("/orderUserList")
    List<Order> orderUserList();
}
  • 启动类上设置@EnableDiscoveryClient@EnableFeignClients

  • 调用接口并测试接口

@Controller
public class UserController {

    @Autowired
    private IOrderFeignService orderFeignService;

    @RequestMapping("/login.html")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/order.html")
    @ResponseBody
    public List<Order> orderUserList(){
        return orderFeignService.orderUserList();
    }
}

测试链路

http://product.zmall.com/index.html
http://product.zmall.com/product.html?pid=733
http://cart.zmall.com/cart.html

界面能够熊 商品微服务 直接跳转到 购物车微服务中

 

http://localhost:8010/order.html 

调用 用户微服务接口 ,能访问 订单微服务的数据

 

spring session实战

什么是Spring Session

SpringBoot整合Spring-Session的自动配置可谓是开箱即用,极其简洁和方便。这篇文章即介绍SpringBoot整合Spring-Session,这里只介绍基于RedisSession的实战。

Spring Session 是Spring家族中的一个子项目,Spring Session提供了用于管理用户会话信息的API和实现。它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,默认Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中

spring session官网地址:Spring Session

Spring Session的特性:

  • 提供用户session管理的API和实现

  • 提供HttpSession,以中立的方式取代web容器的session,比如tomcat中的session

  • 支持集群的session处理,不必绑定到具体的web容器去解决集群下的session共享问题

为什么要使用Spring Session

 SpringCloud微服务将一个完整的单体应用拆解成了一个个独立的子服务,而每一个独立的微服务子模块都将部署到不同的服务器中,而服务与服务之间是独立隔离的,这个时候使用要实现服务与服务之间的session会话共享,则需要借助于spring-session框架来解决分布式session管理与共享问题。

错误案例展示

在用户服务zmall-user中编写登录控制器,登录时创建session,并将当前登录用户存储sesion中。

@Controller
public class UserController {
    
    @RequestMapping("/login.html")
    public String toLogin(HttpSession session){
        session.setAttribute("username","admin");
        return "login";
    }
}

在Gateway网关服务中添加用户服务的路由转发规则

spring:
  application:
    name: zmall-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        ...
        - id: user_route
          uri: lb://zmall-user # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
          predicates:
            - Path=/user-serv/**
          filters:
            - StripPrefix=1
            - PreserveHostHeader

在商品服务zmall-product中编写查询控制器,在登录创建session后,使用将sessionId置于cookie中访问。如果没有session将返回错误。

@Controller
public class ProductController {

    @RequestMapping("/index.html")
    public String index(Model model, HttpSession session){
        Object username = session.getAttribute("username");
        System.out.println("**********"+username);
        return "index";
    }
}

测试链路

#1.session信息存储
http://localhost:8010/login.html
#2.session信息获取
http://product.zmall.com/index.html

配置spring-session

在公共模块zmall-common中引入spring-session的pom配置,由于spring-boot包含spring-session的starter模块,所以pom中依赖: 注意:公共模块作为所有微服务子模块的依赖支持,如果不在各服务模块中配置redis支持,会导致启动其他微服务时出现报错情况。

<!--redis-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring session-->
<dependency>
	<groupId>org.springframework.session</groupId>
	<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--commons-pool2-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

分别在商品服务zmall-product和用户服务zmall-user中配置application.yml

spring:
  session:
    redis:
      flush-mode: on_save
      namespace: session.zmall
      cleanup-cron: 0 * * * * *
    store-type: redis
    timeout: 1800
  redis:
    host: localhost
    port: 6379
    password: 123456
    jedis:
      pool:
        max-active: 100
        max-wait: 10
        max-idle: 10
        min-idle: 10
    database: 0

重新启动zmall-user和zmall-product服务,先访问:http://zmall.com/user-serv/login.html,然后在访问:http://zmall.com/product-serv/index.html;回到zmall-product模块控制台查看session获取情况。

注意:借助网关微服务内部可以访问,不同二级域名之间不可以访问

二级域名问题

测试在用户模块中保存用户信息,然后在产品模块中读取,但是使用二级域名方式访问读取失败

 

请分别在用户服务和商品服务中该配置类,解决二级域名访问session无效问题。

@Configuration
public class SessionConfig {
    @Bean
    public CookieSerializer cookieSerializer(){
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        cookieSerializer.setDomainName("zmall.com");
        cookieSerializer.setCookieName("ZMALLSESSION");
        return cookieSerializer;
    }
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new GenericJackson2JsonRedisSerializer();
    }
}

测试链路

#1.先访问
http://user.zmall.com/login.html
#2.后访问
http://product.zmall.com/index.html

用户登录

第1步:在zmall-common公共模块中创建全局异常处理、响应封装类

第2步:在zmall-user模块中定义IUserService及UserServiceImpl

IUserServic

public interface IUserService extends IService<User> {
    JsonResponseBody<?> userLogin(UserVo user, HttpServletRequest req, HttpServletResponse resp);
}

UserServiceImpl

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public JsonResponseBody<?> userLogin(UserVo user,
                                         HttpServletRequest req,
                                         HttpServletResponse resp) {
        //1.判断用户账号和密码是否为空
        if(StringUtils.isEmpty(user.getLoginName())||
                StringUtils.isEmpty(user.getPassword()))
            return new JsonResponseBody<>(JsonResponseStatus.USERNAME_OR_PWD_EMPTY);
        //2.根据用户名查询数据对应的用户信息
        User us = this.getOne(new QueryWrapper<User>()
                .eq("loginName", user.getLoginName()));
        //3.判断us用户对象是否为空
        if(null==us)
            return new JsonResponseBody<>(JsonResponseStatus.USERNAME_ERROR);
        try {
            //MD5加密转换处理
            String pwd=MD5Utils.md5Hex(user.getPassword().getBytes());
            //4.判断输入密码与数据库表存储密码是否一致
            if(!us.getPassword().equals(pwd)){
                return new JsonResponseBody<>(JsonResponseStatus.PASSWORD_ERROR);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonResponseBody<>(JsonResponseStatus.ERROR);
        }
        //5.通过UUID生成token令牌并保存到cookie中
        String token= UUID.randomUUID().toString().replace("-","");
        //将随机生成的Token令牌保存到Cookie中,并设置1800秒超时时间
        CookieUtils.setCookie(req,resp,"token",token,7200);
        //6.将token令牌与spring session进行绑定并存入redis中
        HttpSession session = req.getSession();
        session.setAttribute(token,us);
        return new JsonResponseBody<>(token);
    }
}

 第3步:创建UserVo类

@Data
public class UserVo {
    private String loginName;
    private String password;
}

第4步:在UserController中定义用户登录方法

/**
* 用户登陆功能实现
* @return
*/
@RequestMapping("/userLogin")
@ResponseBody
public JsonResponseBody<?> userLogin(UserVo user,
    HttpServletRequest req,
    HttpServletResponse resp){
    return userService.userLogin(user,req,resp);
}

第5步:在前端login.html页面中定义登录js方法

<script>
    $(function(){
        $('.log_btn').click(function(){
            let loginName=$('.l_user').val();
            let password=$('.l_pwd').val();

            if(''===loginName){
                alert('请输入用户名!');
                return false;
            }
            if(''===password){
                alert('请输入密码!');
                return false;
            }
            console.log({
                loginName:loginName,
                password:password
            });
            $.post('http://zmall.com/user-serv/userLogin',{
                loginName:loginName,
                password:password
            },function(rs){
                console.log(rs);
                if(rs.code===200){
                    location.href='http://zmall.com/product-serv/index.html';
                }else{
                    alert(rs.msg);
                }
            },'json');
        });
    });
</script>

登录成功后,跳转到商品首页

http://zmall.com/user-serv/login.html

如果用二级域名进行测试,那么需要解决跨域问题;

url: jdbc:mysql://localhost:3306/zmall?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC

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

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

相关文章

如何用演示程序检测K100|K720|K750电动发卡读写一体机性能

K100|K720|K750电动发卡读写一体机采用工业级设 计&#xff0c;表面烤漆处理&#xff0c;具有良好的耐磨耐腐蚀性。适应各种高、低温&#xff0c; 多灰尘等 恶劣环境。其发卡原理为拟人型摩擦式发卡&#xff0c;对各类变形卡有非常好的适应性。 CNC的精度保证卡距的绝对一致性、…

Filter过滤器完成验证代码的封装

Filter过滤器完成验证代码的封装filter是什么1 使用filter2 filter配置到项目中验证用户权限是需要反复使用的代码块&#xff0c;把他封装到filter中&#xff0c;减少代码冗余filter是什么 init()方法&#xff1a;初始化方法&#xff0c;在创建Filter后立即调用。可用于完成初始…

刚刚,体验了一把Bing chat很爽

文章目录刚刚&#xff0c;体验了一把Bing chat很爽你能做啥&#xff1f;与chatgpt有什么不同&#xff1f;以下是Bingchat的 10个新功能1⃣️在网上搜索结果2⃣️摘要链接3⃣️对话助手4⃣️向您发送实际信息&#xff0c;正确的链接5⃣️在单个查询中执行多个搜索6⃣️玩冒险游戏…

内网渗透(十八)之Windows协议认证和密码抓取-本地认证(NTML哈希和LM哈希)

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

【Web测试】各类web控件测试点汇总,软测人必备

一 、界面检查 进入一个页面测试&#xff0c;首先是检查title&#xff0c;页面排版&#xff0c;字段等&#xff0c;而不是马上进入文本框校验 1、页面名称title是否正确 2、当前位置是否可见 您的位置&#xff1a;xxx>xxxx 3、文字格式统一性 4、排版是否整齐 5、列表项显示…

leetcode: 3Sum

leetcode: 3Sum1. 题目描述2. 思考3. 解题3. 总结1. 题目描述 Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i ! j, i ! k, and j ! k, and nums[i] nums[j] nums[k] 0. Notice that the solution set must not contain …

爬虫Python入门好学吗?学什么?

爬虫Python入门好学吗&#xff1f;学爬虫需要具备一定的基础&#xff0c;有编程基础学Python爬虫更容易学。但要多看多练&#xff0c;有自己的逻辑想法。用Python达到自己的学习目的才算有价值。如果是入门学习了解&#xff0c;开始学习不难&#xff0c;但深入学习有难度&#…

2022年12月电子学会Python等级考试试卷(一级)答案解析

青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;一级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 关于Python语言的注释&#xff0c;以下选项中描述错误的是&#xff1f;&#xff08; &#xff09; A. Python语言有两种注释方式&…

JavaEE16-Spring事务和事务传播机制

目录 1.为什么需要事务&#xff1f; 2.MySQL中事务的使用 3.Spring中事务的实现 3.1.编程式事务&#xff08;手动写代码操作事务&#xff09; 3.2.声明式事务&#xff08;利用注解自动开启和提交事务&#xff09;(使用为主) 3.2.1.Transactional作用范围 3.2.2.Transact…

JetpackCompose从入门到实战学习笔记8—ConstraintLayout的简单使用

JetpackCompose从入门到实战学习笔记8—ConstraintLayout的简单使用 1.简介&#xff1a; Compose 中的 ConstraintLayout ConstraintLayout 是一种布局&#xff0c;让您可以相对于屏幕上的其他可组合项来放置可组合项。它是一种实用的替代方案&#xff0c;可代替使用多个已嵌…

JVM垃圾回收机制GC理解

目录JVM垃圾回收分代收集如何识别垃圾引用计数法可达性分析法引用关系四种类型&#xff1a; 强、软、弱、虚强引用软引用 SoftReference弱引用 WeakReferenceWeakHashMap软引用与虚引用的使用场景虚引用与引用队列引用队列虚引用 PhantomReference垃圾回收算法引用计数复制 Cop…

06- 梯度下降(SGDRegressor) (机器学习)

梯度下降算法也是先构建误差值的函数, 通过求误差值函数的最小值来达到误差最小的目的, 不过梯度下降是先随机取值, 然后求函数在该点的导数, 如果导数为正, 下一次取值向反方向移动, 如果导数为负, 正向移动, 直到导数取值极小时, 认定误差达到一个可以接受的范围, 然后导出相…

解读手机拍照的各个参数(拍照时,上面会有6个符号)

1第一个符号是闪光灯符号&#xff0c;如下图所示。有四种模式&#xff0c; 手机的闪光灯分别为关闭、自动、开启和常亮四种状态。 关闭&#xff1a;就是在任何情况下都不会闪光 自动&#xff1a;由手机来判断此时的光线强弱&#xff0c;若手机测光认为光线太弱&#xff0c;则…

前端限制 git commit 提交格式

团队开发中&#xff0c;每个人 git commit 的习惯都不一样&#xff0c;这样不利于对更新日志的筛选&#xff0c;也可以防止同事跑路后&#xff0c;出现 bug 后&#xff0c;看不懂他当时提交的日志究竟是改了个 bug 还是新增了一个功能&#xff0c;影响开发效率。 这时候就需要…

springboot驾校报名系统 微信小程序

系统分为用户和管理员&#xff0c;驾校教练三个角色 用户微信小程序端的主要功能有&#xff1a; 1.用户注册和登陆系统 2.用户查看驾校教练信息 3.用户查看驾校信息 4.用户查看驾校的车辆信息 5.用户查看驾校考试信息&#xff0c;在线报名考试 6.用户可以在线预约驾校的教练 7.…

深度学习笔记--修改、增加和删除预训练模型的特定层

目录 1--前言 2--初始化 VGG16 模型 3--修改特定层 4--新增特定层 5--删除特定层 6--固定预训练模型的权重 7--综合应用 1--前言 基于 Pytorch&#xff0c;以 VGG16 模型为例&#xff1b; 2--初始化 VGG16 模型 from torchvision import models# 初始化模型 vgg16 mo…

Java 反射详解

一、反射 1、什么是反射 反射允许对成员变量、成员方法和构造器的信息进行编程访问。 补充:暴力反射,非public修饰需要打开权限 setAccessible(boolean) 2、反射的作用 利用反射创建的对象可以无视修饰符调用类里面的内容可以跟配置文件结合起来使用&#xff0c;把要创建的对象…

【Jest】Jest单元测试环境搭建

文章目录前言1、项目环境搭建初始化仓库安装ts环境安装jest环境2、初始化项目文件夹前言 今天开始&#xff01;&#xff01;&#xff01;学习vue源码&#xff0c;那么要学习源码前首先要了解Jest。 https://www.jestjs.cn/ 官网自带中文非常友好&#xff01; 1、项目环境搭建…

【C++面试问答】搞清楚深拷贝与浅拷贝的区别

问题 深拷贝和浅拷贝的区别是面试中的常见问题之一&#xff0c;对于不同的编程语言&#xff0c;这个问题的回答可能稍有差别&#xff0c;下面我们就来探索一下它们之间的异同吧。 先来看看在JavaScript对象的深拷贝与浅拷贝的区别&#xff1a; 浅拷贝&#xff1a;只是复制了…

Postgresql 根据单列或几列分组去重row_number() over() partition by

Postgresql 根据单列或几列分组去重row_number() over() partition by 一般用于单列或者几列需要去重后进行计算值的 count(distinct(eid)) 可以 比如有个例子&#xff0c;需要根据名称&#xff0c;城市去筛选覆盖的道路长度&#xff0c;以月因为建立了唯一索引是ok的&#…