Spring cloud Alibaba 从入门到放弃
ztj100 2025-05-30 19:01 8 浏览 0 评论
Spring cloud Alibaba
一. 为什么使用spring cloud alibaba
很多人可能会问,有了spring cloud这个微服务的框架,为什么又要使用spring cloud alibaba这个框架了?最重要的原因在于spring cloud中的几乎所有的组件都使用Netflix公司的产品,然后在其基础上做了一层封装。然而Netflix的服务发现组件Eureka已经停止更新,我们公司在使用的时候就发现过其一个细小的Bug;而其他的众多组件预计会在明年(即2020年)停止维护。所以急需其他的一些替代产品,也就是spring cloud alibaba,目前正处于蓬勃发展的态式。
二. 注册中心Nacos
nacos是阿里巴巴研发的一个集注册中心与配置中心于一体的管理平台,使用其他非常的简单。下载地址为:
https://github.com/alibaba/nacos/releases,nacos的主页如下图所示:
其中默认的登录名和密码是:nacos/nacos
2.1 更改用户名和密码
A. 分别执行conf目录下的nacos-mysql.sql两个脚本文件,生成的数据表如下:
B. 重新配置密码
新建一个springboot的项目,引入如下的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
生成密码的Java代码:
// 密码加密处理
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 生成的密码为:$2a$10$04MGTL.cJNZPpR3rFt/I2.43F.V75NH.5wdK.jngaO9Mc91mfonAO
System.out.println(bCryptPasswordEncoder.encode("admin"));
}
执行如下sql命令:
insert into users values('nacos','$2a$10$04MGTL.cJNZPpR3rFt/I2.43F.V75NH.5wdK.jngaO9Mc91mfonAO', 1);insert into roles values('nacos', 'ROLE_ADMIN')
C. 配置数据库的连接
在conf目录下的application.properties目录下加入如下内容:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://mysql:3306/cloud-alibaba?useSSL=false&serverTimezone=UTC&serverTimezone=Asia/Shanghai
db.user=root
db.password=
2.2 nacos集群配置
A. 将conf目录下的cluster.conf.example拷贝一份重命名为cluster.conf,在文件中加入所有集群节点的ip和端口号,文件内容如下:
127.0.0.1:8848
127.0.0.1:9948
B. 修改windows启动文件 startup.cmd 的配置,修改内容如下:
set MODE="standalone" #默认的配置
set MODE="cluster" #修改后的内容
注:如果是Linux环境不用作任何的修改。
C.启动两个nacos,界面中出现如下的内容,表示集群配置成功
三. 服务提供方
pom.xml依赖配置
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency></dependencies><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>0.9.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>
配置
spring:
application:
# 服务名
name: alibaba-provider
cloud:
nacos:
discovery:
#必须配置ip地址
server-addr: 172.18.96.177:8848,172.18.96.177:9948
启动类
@SpringBootApplication
@EnableDiscoveryClient //开启nacos的服务发现
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
服务页面结果
四. 服务消费方
pom.xml依赖配置
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency></dependencies><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>0.9.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>
配置
server: port: 8080spring: application: # 服务名 name: alibaba-consumer cloud: nacos: discovery: server-addr: 172.18.96.177:8848,172.18.96.177:9948 # 不将自己的服务注册到注册中心 register-enabled: false
启动类配置
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
调用服务提供方
@RequestMapping public Object getUsers() { List<ServiceInstance> list = discoveryClient.getInstances("alibaba-provider"); String targetUrl = list.stream().map(si -> si.getUri() + "/user").findFirst().get(); List<String> resultList = restTemplate.getForObject(targetUrl, List.class); return resultList; }
五. Ribbon负载均衡
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。其主要功能是提供客户端的负载均衡算法,并提供了完善的配置项如连接超时,重试等。简单的说,就是配置文件中列出Load Balancer后面所有的机器,Ribbon会自动的基于某种规则(如简单轮询,随机连接等)去连接这些机器,当然我们也可以使用Ribbon自定义负载均衡算法。
5.1 实现负载均衡
Ribbon只是一个客户端的负载均衡器工具,实现起来非常的简单,我们只需要在注入RestTemplate的bean上加上@LoadBalanced就可以了。如下:
@Configurationpublic class WebConfig { @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); }}
5.2 服务消费方调用
// 直接写上服务名即可
List<String> resultList = restTemplate.getForObject("http://alibaba-provider/user", List.class);
5.3 负载均衡策略
Ribbon提供了一个很重要的接口叫做IRule,其中定义了很多的负载均衡策略,默认的是轮询的方式,以下是Ribbon的负载均衡策略:
类名 | 描述 |
RoundRobbinRule | 轮询 |
RandomRule | 随机挑选 |
RetryRule | 按照轮询的方式去调用服务,如果其中某个服务不可用,但是还是会尝试几次,如果尝试过几次都没有成功,那么就不在调用该服务,会轮询调用其他的可用服务。 |
AvailabilityFilteringRule | 会先过滤掉因为多次访问不可达和并发超过阈值的服务,然后轮询调用其他的服务 |
WeightedResponseTimeRule | 根据平均响应时间计算权重,响应越快权重越大,越容易被选中。服务刚重启的时候,还未统计出权重会按照轮询的方式;当统计信息足够的时候,就会按照权重信息访问 |
ZoneAvoidanceRule | 判断server所在的区域性能和可用性选择服务器 |
BestAvailableRule | 会过滤掉多次访问都不可达的服务,然后选择并发量最小的服务进行调用,默认方式 |
改变Ribbon的负责均衡策略:
@Bean
public IRule getRule() {
return new RandomRule();
}
5.4 自定义负载均衡策略
我们自定义的负载均衡策略需要继承AbstractLoadBalancerRule这个类,然后重写choose方法,然后将其注入到容器中,如下所示:
public class Customize_Rule extends AbstractLoadBalancerRule { private static Logger logger = LoggerFactory.getLogger(Customize_Rule.class); private int currentIndex = 0; //当前调用的索引 private int num = 1; //次数 private int limit = 5; /** * 初始化工作 * @param iClientConfig */ @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } @Override public Server choose(Object key) { ILoadBalancer balancer = getLoadBalancer(); return choose(balancer, key); } private Server choose(ILoadBalancer balancer, Object key) { Server server = null; while(null == server) { //获取所有可用的服务 List<Server> reachableServers = balancer.getReachableServers(); if (0 == reachableServers.size()) { logger.error("没有可用的服务"); return null; //退出while循环 } int total = reachableServers.size(); //可用服务的数量 synchronized (this) { /** * 有种极端情况,当我们在使用最后一个服务的时候,其他的服务都不可用,可能导致索引越界异常 */ if (currentIndex + 1 > total) { currentIndex = 0; server = reachableServers.get(currentIndex); //获取第一个服务 num = 0; num++; } else { if(limit == num) { currentIndex++; num = 0; if(currentIndex == total) { currentIndex=0; server = reachableServers.get(currentIndex); //获取第一个服务 num++; }else{ server = reachableServers.get(currentIndex); num++; } }else { server = reachableServers.get(currentIndex); num++; } } } } return server; }}
将其注入到容器中,方式如下:
@Bean
public IRule getRule() {
return new Customize_Rule();
}
六. Feign负载均衡
feign是基于Ribbon的另外一个负载均衡的客户端框架,只需要在接口上定义要调用的服务名即可,使用起来非常的简单。
pom.xml依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类配置
需要在启动类上加上@EnableFeignClients这个注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
服务接口配置
@FeignClient(name="alibaba-provider")public interface UserService { @RequestMapping("/user") public List<String> getUsers();}
七. 熔断与服务降级
分布式系统中一个微服务需要依赖于很多的其他的服务,那么服务就会不可避免的失败。例如A服务依赖于B、C、D等很多的服务,当B服务不可用的时候,会一直阻塞或者异常,更不会去调用C服务和D服务。同时假设有其他的服务也依赖于B服务,也会碰到同样的问题,这就及有可能导致雪崩效应。
如下案例:一个用户通过通过web容器访问应用,他要先后调用A、H、I、P四个模块,一切看着都很美好。
由于某些原因,导致I服务不可用,与此同时我们没有快速处理,会导致该用户一直处于阻塞状态。
当其他用户做同样的请求,也会面临着同样的问题,tomcat支持的最大并发数是有限的,资源都是有限的,将整个服务器拖垮都是有可能的。
Sentinel是一个用于分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖会不可避免的调用失败,例如超时,异常等,Hystrix能保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
断路器本身是一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控(类似于保险丝),向调用者返回符合预期的,可处理的备选响应,而不是长时间的等待或者抛出无法处理的异常,这样就保证了服务调用的线程不会被长时间,不必要的占用,从而避免故障在分布式系统中的蔓延,乃至雪崩。
Sentinel在网络依赖服务出现高延迟或者失败时,为系统提供保护和控制;可以进行快速失败,缩短延迟等待时间;提供失败回退(Fallback)和相对优雅的服务降级机制;提供有效的服务容错监控、报警和运维控制手段。
下载地址:
https://github.com/alibaba/Sentinel/releases
7.1 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
7.2 配置
通常情况下我们只需要在服务提供方实现熔断或者服务降级即可,但是如果要相对服务消费方是实现限流,在服务的提供方和消费方都需要加如下配置。
spring:
cloud:
sentinel:
transport:
# sentinel控制台的地址
dashboard: localhost:8080
# 立即加载
eager: true
7.3 sentinel的控制面板
在浏览器输入localhost:8080,使用sentinel/sentinel来访问sentinel的控制面板:
接下来对sentinel的控制面板一一讲解:
实时监控
用于查看接口调用的QPS(Query Per Second)以及平均响应时间。
簇点链路
查看当前追踪的所有的访问接口,可以添加流量规则、降级规则、热点规则、授权规则。
流量规则
资源名:是需要控制的链路的名字,例如/student/all等
针对来源: 默认为default表示所有,也可以针对特定的服务进行设置。
阈值类型: 是指如何进行限制,可以是QPS,也可以是线程。
单机阈值: 是控制QPS或者线程的数量。
流量模式: 直接表示只是针对指定资源进行限制;关联是指当被关联的资源达到阈值时候,指定资源被限制访问;链路是更加细粒度的控制,控制指定资源对链路的限制。
流控效果: 快速失败是指,当无法访问的时候立即给用户一个错误响应;Warm Up(预热)是指经过指定的时间后才达到指定的阈值(sentinel内有值为 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值,参考地址:
https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8);排队等待只是匀速的通过(每秒指定的QPS),其他的请求进行排队,但是并不会一直排下去,超过指定的时间就会失败,阈值类型必须设置为QPS,不能为线程。
降级规则
资源名: 要实现降级的资源。
降级策略:
1) RT(平均响应时间)
如果在一秒钟之内进入的请求的平均响应时间小于1ms,那么在未来5s钟之内所有的请求都会熔断降级。
2) 异常比例
如果在一秒钟之内的请求数异常比例大于指定的数据,那么在未来的时间窗口内会一直熔断降级。统计单位为s.
3) 异常数
如果在一分钟之内,异常数量大于指定的值,那么在指定的时间窗口内请求一直会熔断降级,注意时间窗口的值一般设置要大于60,因为设置如果小于60,可能会一直处于熔断状态。
热点规则
热点规则是针对具体的请求参数进行设置,例如如下的方法:
@RequestMapping("/edit")
@SentinelResource("edit") //必须的有
public Object edit(@RequestParam(required = false) String id,
@RequestParam(required = false) Integer age) {
return this.studentService.commons();
}
资源名: 是@SentinelResource中设置的值
参数索引: 对那个参数进行QPS限制,通过索引来指定。
单机阈值:指定在统计时长内的阈值。
统计窗口时长: 统计的QPS的时间。
系统规则
LOAD: 仅对 Linux/Unix-like 机器生效,参考值一般是 CPU cores * 2.5
RT: 当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
线程数: 当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口QPS: 当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
授权规则
授权规则是指可以将特定的访问应用加入黑名单或者白名单,但是必须在访问的时候携带应用的名称。代码实现部分如下:
@Componentpublic class SentinelOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest httpServletRequest) { String origin = httpServletRequest.getParameter("origin"); if(StringUtil.isBlank(origin)) { throw new IllegalArgumentException("origin parameter must be specified."); } return origin; }}
加上了来源解析后,在往后的访问中必须要携带origin参数,在sentinel的dashboard中可以作如下配置:
集群流控
是否集群: 是否采用集群
均摊阈值: 就是每个集群节点每秒的QPS.
集群阈值模式: 单机均摊是集群中每个节点每秒的QPS, 总体阈值是整个集群每秒的QPS.
集群流控
7.4 @SentinelResource
@SentinelResource是sentinel中非常重要的注解,提供了简单易用的功能。其中blockHandler注解是限流的处理方法,fallback是服务降级的处理方法。
@SentinelResource(value="edit", blockHandler="editBlock", fallback = "editFallback")@RequestMapping("/edit")public Object edit(@RequestParam(required = false) String id, @RequestParam(required = false) Integer age) throws Exception { Thread.sleep(20); return this.studentService.commons();}// 限流的处理public Object editBlock(String id, Integer age, BlockException ex) { Map<String, Object> map = new HashMap<>(); map.put("msg", "限流了."); return map;}//服务降级的处理方法public Object editFallback(String id, Integer age) { Map<String, Object> map = new HashMap<>(); map.put("msg", "fallback 服务降级了."); return map;}
八. Feign与Sentinel的整合
配置
feign:
sentinel:
# 默认是没有提示的
enabled: true
8.1 服务降级后的处理
可以在@FeignClient中配置fallback,来指定服务降级后给用户返回的什么样的数据,fallback的值为Class类型的对象,该类必须要实现该对应的接口。
@FeignClient(name="alibaba-provider", fallback = UserServiceFallback.class)public interface UserService { @RequestMapping("/user") public List<String> getUsers();}
UserServiceFallback的实现如下:
@Componentpublic class UserServiceFallback implements UserService { @Override public List<String> getUsers() { return Arrays.asList("服务降级了, 这是降级后返回的信息."); }}
8.2 服务降级的异常处理
对于fallback来讲,并不能追踪到异常信息,在实际的业务处理过程中,我们往往需要记录异常的信息,那么就要使用fallbackFactory属性来实现。
@Componentpublic class UserServiceFallbackFactory implements FallbackFactory<UserService> { private static Logger logger = LoggerFactory.getLogger(UserServiceFallbackFactory.class); @Override public UserService create(Throwable cause) { return new UserService() { @Override public List<String> getUsers() { logger.info(cause.getMessage()); return Arrays.asList("服务降级的方法"); } }; }}
九. Sentinel的持久化
通过接入 Sentinel Dashboard 后,在页面上操作来更新规则,都无法避免一个问题,那就是服务重新后,规则就丢失了,因为默认情况下规则是保存在内存中的。sentinel中持久化的方式有两种,pull模式和push模式。
pull模式是指站在第三方持久化系统(redis, nacos)的角度,他们去到sentinel中定时去拉去配置信息,可能会造成数据的不一致性。
push模式是站在sentinel的角度,将其配置信息主动推送给第三方持久化系统,sentinel官方也推荐在线上使用该模式。
9.1 sentinel-dashboard改造
A. 将sentinel的源码clone到本地
B. 进入到sentinel-dashboard目录下,修改pom.xml文件
<!-- 修改之前的内容 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>test</scope>
</dependency>
<!-- 修改之后的内容 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
C. 修改 src\main\webapp\resources\app\scripts\directives\sidebar\sidebar.html 文件
未修改之前的内容
<!-- <li ui-sref-active="active" ng-if="entry.appType==0"> -->
<!-- <a ui-sref="dashboard.flow({app: entry.app})"> -->
<!-- <i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a> -->
<!-- </li> -->
<li ui-sref-active="active" ng-if="!entry.isGateway">
<a ui-sref="dashboard.flowV1({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控规则
</a>
</li>
修改之后的内容
<li ui-sref-active="active" ng-if="entry.appType==0">
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控规则
</a>
</li>
<!-- <li ui-sref-active="active" ng-if="!entry.isGateway">-->
<!-- <a ui-sref="dashboard.flow({app: entry.app})">-->
<!-- <i class="glyphicon glyphicon-filter"></i> 流控规则
</a> -->
<!-- </li>-->
D. 将 src\test\java\com\alibaba\csp\sentinel\dashboard\rule\nacos 目录下的四个Java文件拷贝到src\main\java\com\alibaba\csp\sentinel\dashboard\rule 目录下
E. 修改 src\main\webapp\resources\app\scripts\controllers\identity.js 文件,修改内容如下:
F. 重新打包生成Jar包,进入到sentinel目录下(注:不是sentinel-dashboard目录),执行如下命令:
mvn clean
mvn install -DskipTests
G. 进入到sentinel-dashboard/target目录下,执行如下内容:
java -jar sentinel-dashboard.jar
9.2 配置
spring:
cloud:
sentinel:
datasource:
# 这个名字随意,但是要有意义
flow:
nacos:
server-addr: 192.168.31.173:8848
groupId: SENTINEL_GROUP
rule-type: flow
9.3 测试
在sentinel-dashboard控制面板添加一个流量控制规则
相关推荐
- 拒绝躺平,如何使用AOP的环绕通知实现分布式锁
-
如何在分布式环境下,像用synchronized关键字那样使用分布式锁。比如开发一个注解,叫@DistributionLock,作用于一个方法函数上,每次调方法前加锁,调完之后自动释放锁。可以利用Sp...
- 「解锁新姿势」 兄dei,你代码需要优化了
-
前言在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题。甚至有些复制粘贴过来,不加以整理规范。往往导致项目后期难以维护,更别说后续接手项目的人。所以啊,我们要编写出优雅的...
- 消息队列核心面试点讲解(消息队列面试题)
-
Rocketmq消息不丢失一、前言RocketMQ可以理解成一个特殊的存储系统,这个存储系统特殊之处数据是一般只会被使用一次,这种情况下,如何保证这个被消费一次的消息不丢失是非常重要的。本文将分析Ro...
- 秒杀系统—4.第二版升级优化的技术文档二
-
大纲7.秒杀系统的秒杀活动服务实现...
- SpringBoot JPA动态查询与Specification详解:从基础到高级实战
-
一、JPA动态查询概述1.1什么是动态查询动态查询是指根据运行时条件构建的查询,与静态查询(如@Query注解或命名查询)相对。在业务系统中,80%的查询需求都是动态的,例如电商系统中的商品筛选、订...
- Java常用工具类技术文档(java常用工具类技术文档有哪些)
-
一、概述Java工具类(UtilityClasses)是封装了通用功能的静态方法集合,能够简化代码、提高开发效率。本文整理Java原生及常用第三方库(如ApacheCommons、GoogleG...
- Guava 之Joiner 拼接字符串和Map(字符串拼接join的用法)
-
Guave是一个强大的的工具集合,今天给大家介绍一下,常用的拼接字符串的方法,当然JDK也有方便的拼接字符串的方式,本文主要介绍guava的,可以对比使用基本的拼接的话可以如下操作...
- SpringBoot怎么整合Redis,监听Key过期事件?
-
一、修改Redis配置文件1、在Redis的安装目录2、找到redis.windows.conf文件,搜索“notify-keyspace-events”...
- 如何使用Python将多个excel文件数据快速汇总?
-
在数据分析和处理的过程中,Excel文件是我们经常会遇到的数据格式之一。本文将通过一个具体的示例,展示如何使用Python和Pandas库来读取、合并和处理多个Excel文件的数据,并最终生成一个包含...
- 利用Pandas高效处理百万级数据集,速度提升10倍的秘密武器
-
处理大规模数据集,尤其是百万级别的数据量,对效率的要求非常高。使用Pandas时,可以通过一些策略和技巧显著提高数据处理的速度。以下是一些关键的方法,帮助你使用Pandas高效地处理大型数据集,从而实...
- Python进阶-Day 25: 数据分析基础
-
目标:掌握Pandas和NumPy的基本操作,学习如何分析CSV数据集并生成报告。课程内容...
- Pandas 入门教程 - 第五课: 高级数据操作
-
在前几节课中,我们学习了如何使用Pandas进行数据操作和可视化。在这一课中,我们将进一步探索一些高级的数据操作技巧,包括数据透视、分组聚合、时间序列处理以及高级索引和切片。高级索引和切片...
- 原来这才是Pandas!(原来这才是薯片真正的吃法)
-
听到一些人说,Pandas语法太乱、太杂了,根本记不住。...
- python(pandas + numpy)数据分析的基础
-
数据NaN值排查,统计,排序...
- 利用Python进行数据分组/数据透视表
-
1.数据分组源数据表如下所示:1.1分组键是列名分组键是列名时直接将某一列或多列的列名传给groupby()方法,groupby()方法就会按照这一列或多列进行分组。按照一列进行分组...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)