服务雪崩预防Sentinel

news2025/1/17 23:13:56

服务雪崩效应

在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了
问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等
待,进而导致服务瘫痪。
由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是
服务故障的 “雪崩效应” 。

服务容错核心思想

  • 不被上游请求压垮
  • 不被下游响应拖垮
  • 不被外界环境影响(运维配置系统规则)

常见容错方案Sentinel

在pom.xml中加入下面依赖

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

安装Sentinel控制台

Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。

  1. 下载jar包,解压到文件夹
    https://github.com/alibaba/Sentinel/releases
  2. 启动控制台
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
  1. 修改服务配置,加入控制台的配置
spring:
	cloud:
		sentinel:
			transport:
				port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
				dashboard: localhost:8080 # 指定控制台服务的地址
  1. 通过浏览器访问localhost:8080 进入控制台 ( 默认用户名密码是 sentinel/sentinel )
    注意:sentinel控制台界面,是懒加载,必须先访问服务后,在sentinel控制台界面才会显示
    在这里插入图片描述

流控规则

在这里插入图片描述
在这里插入图片描述
表示:每秒请求量大于3的时候开始限流
在这里插入图片描述
sentinel共有三种流控模式,分别是:
直接(默认):接口达到限流条件时,开启限流
关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]
链路:当从某个接口过来的资源达到限流条件时,开启限流

降级规则

在这里插入图片描述
表示:响应时间超过1ms时,接下来5s内服务降级,5s后服务恢复正常,进行下一轮判断。
在这里插入图片描述
降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:

  1. 平均响应时间 :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。
    如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口
    (以 s 为单位)之内,就会对这个方法进行服务降级。

  2. 异常比例:当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的
    时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0,
    1.0]。
    在这里插入图片描述

  3. 异常数 :当资源近 1 分钟的异常数目超过阈值之后会进行服务降级。注意由于统计时间窗口是分
    钟级别的,若时间窗口小于 60s,则结束熔断状态后仍可能再进入熔断状态。

在这里插入图片描述

热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

热点规则简单使用
第1步: 编写代码

    @RequestMapping("/order/message3")
    @SentinelResource("message3") //注意这里必须使用这个注解标识,否则热点规则不生效
    public String message3(String name, Integer age) {
        return "message3" + name + age;
    }

第2步: 配置热点规则
表示:1s内超过1个请求,接下来10s内服务限流,10s后服务恢复正常,进行下一轮判断。
在这里插入图片描述
第3步: 分别用两个参数访问,会发现只对第一个参数限流了
在这里插入图片描述
在这里插入图片描述
热点规则增强使用
参数例外项允许对一个参数的具体值进行流控
在这里插入图片描述
表示:第一个参数值为zhu的时候,限流阈值为20,即每秒超过20个请求才会限流。

授权规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源
访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

  • 若配置白名单,则只有请求来源位于白名单内时才可通过;
  • 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。

比如:A调用服务C都放行,B调用服务C都限流。
在这里插入图片描述
在这里插入图片描述
上面的资源名和授权类型不难理解,但是流控应用怎么填写呢?
其实这个位置要填写的是来源标识,Sentinel提供了 RequestOriginParser 接口来处理来源。
只要Sentinel保护的接口资源被访问,Sentinel就会调用 RequestOriginParser 的实现类去解析
访问来源。

第1步: 自定义来源处理规则

@Component
public class RequestOriginParserDefinition implements RequestOriginParser {

    //定义区分来源: 本质作用是通过request域获取到来源标识
    //app  pc
    //然后 交给流控应用 位置进行匹配
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String serviceName = request.getParameter("serviceName");
        if (StringUtils.isEmpty(serviceName)){
            throw new RuntimeException("serviceName is not empty");
        }
        return serviceName;
    }
}

第2步: 授权规则配置
这个配置的意思是只有serviceName=pc不能访问(黑名单)
在这里插入图片描述
第3步: 访问 http://localhost:8091/order/message1?serviceName=pc观察结果
在这里插入图片描述
在这里插入图片描述

自定义规则异常返回

//自定义异常返回页面(区分各种限流和降级等异常)
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");//处理中文乱码

        ResponseData responseData = null;
        //BlockException  异常接口,包含Sentinel的五个异常
        //  FlowException  限流异常
        //  DegradeException  降级异常
        //  ParamFlowException  参数限流异常
        //  AuthorityException  授权异常
        //  SystemBlockException  系统负载异常
        if (e instanceof FlowException) {
            responseData = new ResponseData(-1, "接口被限流了...");
        } else if (e instanceof DegradeException) {
            responseData = new ResponseData(-2, "接口被降级了...");
        }
        response.getWriter().write(JSON.toJSONString(responseData));
    }
}

@Data
@AllArgsConstructor //全参构造
@NoArgsConstructor //无参构造
class ResponseData {
    private int code;
    private String message;
}

@SentinelResource的使用

在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能
通过@SentinelResource来指定出现异常时的处理策略。

@Service
@Slf4j
public class OrderServiceImpl3 {

    int i = 0;

    //定义一个资源
    //定义当资源内部发生异常的时候的处理逻辑
    //blockHandler  定义当资源内部发生了BlockException应该进入的方法[捕获的是Sentinel定义的异常]
    //fallback      定义当资源内部发生了Throwable应该进入的方法
    @SentinelResource(
            value = "message",
            blockHandlerClass = OrderServiceImpl3BlockHandler.class,
            blockHandler = "blockHandler",
            fallbackClass = OrderServiceImpl3Fallback.class,
            fallback = "fallback"
    )
    public String message(String name) {
        i++;
        if (i % 3 == 0) {
            throw new RuntimeException();
        }
        return "message";
    }
}
//OrderServiceImpl3对应的BlockException处理的类
@Slf4j
public class OrderServiceImpl3BlockHandler {
    //blockHandler
    //要求:
    //1 当前方法的返回值和参数要跟原方法一致(且是静态方法)
    //2 但是允许在参数列表的最后加入一个参数BlockException, 用来接收原方法中发生的异常
    public static String blockHandler(String name, BlockException e) {
        //自定义异常处理逻辑
        log.error("触发了BlockException,内容为{}", e);
        return "BlockException";
    }

}
//OrderServiceImpl3对应的Throwable处理的类
@Slf4j
public class OrderServiceImpl3Fallback {

    //fallback
    //要求:
    //1 当前方法的返回值和参数要跟原方法一致
    //2 但是允许在参数列表的最后加入一个参数Throwable, 用来接收原方法中发生的异常
    public static String fallback(String name, Throwable e) {
        //自定义异常处理逻辑
        log.error("触发了Throwable,内容为{}", e);
        return "Throwable";
    }
}

Sentinel规则持久化

通过前面的讲解,我们已经知道,可以通过Dashboard来为每个Sentinel客户端设置各种各样的规
则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。

  1. 编写处理类
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * 流控规则持久化
 */
public class FilePersistence implements InitFunc {

    @Value("spring.application.name")
    private String appcationName;

    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + appcationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);

        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}
  1. 添加配置
    在resources下创建配置目录 META-INF/services ,然后添加文件
    com.alibaba.csp.sentinel.init.InitFunc
    在文件中添加配置类的全路径
    com.itheima.config.FilePersistence
    在这里插入图片描述

Feign整合Sentinel

第1步: 引入sentinel的依赖

<!--sentinel客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

第2步: 在配置文件中开启Feign对Sentinel的支持

# 开启feign对sentinel的支持
feign:
  sentinel:
    enabled: true

第3步: 创建容错类

//这是容错类,他要求我们要是实现一个FallbackFactory<要为哪个接口产生容错类>
@Slf4j
@Service
public class ProductServiceFallbackFactory implements FallbackFactory<ProductService> {

    //Throwable  这就是fegin在调用过程中产生异常
    @Override
    public ProductService create(Throwable throwable) {
        return new ProductService() {
            @Override
            public Product findByPid(Integer pid) {
                log.error("{}",throwable);//打印异常
                Product product = new Product();
                product.setPid(-100);
                product.setPname("商品微服务调用出现异常了,已经进入到了容错方法中");
                return product;
            }

            @Override
            public void reduceInventory(Integer pid, Integer number) {

            }
        };
    }
}

第4步: 为接口指定容错类

//value用于指定调用nacos下哪个微服务
//fallback 指定当调用出现问题之后,要进入到哪个类中的同名方法之下执行备用逻辑
//fallbackFactory 指定当调用出现问题之后,要进入到哪个类中的同名方法之下执行备用逻辑,并且可以在日志中打印异常信息
@FeignClient(
        value = "service-product",
//        fallback = ProductServiceFallback.class,
        fallbackFactory = ProductServiceFallbackFactory.class
)
public interface ProductService {
    //@FeignClient的value +  @RequestMapping的value值  其实就是完成的请求地址  "http://service-product/product/" + pid
    //指定请求的URI部分
    @RequestMapping("/product/{pid}")
    Product findByPid(@PathVariable Integer pid);

    //扣减库存
    //参数一: 商品标识
    //参数二:扣减数量
    @RequestMapping("/product/reduceInventory")
    void reduceInventory(@RequestParam("pid") Integer pid,
                         @RequestParam("number") Integer number);
}

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

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

相关文章

【HTML+CSS+JavaScript】实现鼠标点击烟花效果

文章目录【HTMLCSSJavaScript】实现鼠标点击烟花效果&#xff08;爆炸型、心型、圆形&#xff09;一. 效果图二. 鼠标点击烟花效果 - 心型实现代码(1) HTML部分代码(2) CSS部分代码(3) 内部的JavaScript部分代码三. 鼠标点击烟花效果 - 圆型实现代码(1) HTML部分代码(2) CSS部分…

【mysql】优化系列文章之一-索引

mysql优化系列 不是教程&#xff0c;不是官方文档&#xff0c;而是自己实战的点滴记录&#xff0c;不一定适合新手和系统学习者 第一章 mysql索引 文章目录mysql优化系列前言1、Mysql索引2、B Tree2.1.特点2.2. 结构分解2.3. 例题分析2.4. 验证索引2.5.索引插入耗时3. MySQL 中…

Oracle数据库同步复制工具Beedup产品功能(一)

1、全量复制 Beedup全量复制功能通过遍历比对主从库用户模式及其下包含的各类对象来保证主从库的相关对象一致性。 支持角色、用户、架构、登录用户、表 (列定义 主外键 索引)、视图存储过程、函数、触发器、类型、类型体、包、包体、序列、同义词、数据库链接等对象复制全量…

技术干货 | 人大金仓KFS基于分区索引的分片入库技术解析

在之前的文章《技术干货&#xff1a;人大金仓KFS精准过滤和分片并行入库技术解析》中&#xff0c;KFS利用分片并行入库技术&#xff0c;解决了某金融POC数据同步项目中数据入库持续积压问题。经过优化后&#xff0c;在200并发的压测场景中&#xff0c;整体同步性能指标从压测30…

基于BP神经网络、RBF神经网络以及PSO优化的RBF神经网络进行数据的预测(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…

IT30--IT之职能部门(3年之约已满)

IT作为一个职能部门&#xff0c;肯定跟业务不同&#xff0c;具体有哪些事情要做&#xff0c;我们一一道来。 年终规划的二三事 组织体系规划及梳理 预算编制 今天我们先来说说组织体系规划。 1 从愿景使命价值观到行动及思考方法 无论是企业还是部门&#xff0c;软文件建设肯…

【信息检索与数据挖掘期末笔记】(六)Link Analysis

Web图 将Web当做有向图 节点&#xff1a;网页 边&#xff1a;超链接 PageRank 不同网页的重要性是不同的&#xff0c;在web-graph中&#xff0c;节点之间的连接性有巨大的差异。我们根据链接结果来对网页进行排序 想法&#xff1a;用链接来投票 如果有更多的链接指向一个网…

Linux Shell 编程,Shell 变量详解

Linux Shell 编程&#xff0c;Shell 变量详解1.第一个shell脚本2.Shell 变量初探3.位置参数变量4.预定义变量1.第一个shell脚本 打开文本编辑器(可以使用 vi/vim 命令来创建文件)&#xff0c;新建一个文件 hello.sh&#xff0c;扩展名为 sh&#xff08;sh代表shell&#xff09…

【算法】面试题 - 链表(附讲解视频)

链表相关面试题876. 链表的中间结点206. 反转链表86. 分隔链表160. 相交链表141. 环形链表问题&#xff1a;快慢指针为什么一定会相遇142. 环形链表 II问题&#xff1a;如何确认入口237. 删除链表中的节点19. 删除链表的倒数第 N 个结点21. 合并两个有序链表23. 合并K个升序链表…

【记忆增强深度条件展开网络】

Memory-augmented Deep Conditional Unfolding Network for Pan-sharpening &#xff08;面向全色锐化的记忆增强深度条件展开网络&#xff09; 全色锐化旨在为遥感系统获取高分辨率的多光谱图像&#xff0c;基于深度学习的方法已经取得了显著的成功。然而&#xff0c;大多数…

使用iServer rest api如何实现构建巷道效果

作者&#xff1a;刘大 背景 在实际生产环境中&#xff0c;特别是在采矿&#xff0c;公路建设项目上&#xff0c;我们往往会接触下图所示的巷道&#xff0c;那么在Web端如何快速通过线数据构建巷道模型呢&#xff1f;下面我们来详细说下 使用方式 第一步&#xff1a; 在iServe…

反垃圾邮件系统|基于Springboot+vue 实现反垃圾邮件系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

servlet(二)文件的上传

servlet实现文件的上传 文件上传是一个web应用常见的功能 比如&#xff1a;QQ头像&#xff0c;就使用了上传。 邮箱中也有附件的上传和下载功能。 OA系统中审批有附件材料的上传。 1.1前端需要有个form表单标签 methodpost请求 (因为post请求是没有长度限制&#xff0c;get…

Computer Graphics From Scratch - Chapter 7

系列文章目录 简介&#xff1a;Computer Graphics From Scratch-《从零开始的计算机图形学》简介 第一章: Computer Graphics From Scratch - Chapter 1 介绍性概念 第二章&#xff1a;Computer Graphics From Scratch - Chapter 2 基本光线追踪 第三章&#xff1a;Computer Gr…

新生儿喝奶后不要马上放回床上睡觉,为宝宝健康着想,先做1件事

看到一个问题&#xff0c;题主问&#xff0c;新生儿喝完奶能马上放回床上睡觉吗&#xff1f;可以吗&#xff1f;也许每个人都认为照顾新生儿是一件非常简单的事情&#xff0c;因为新生儿几乎整天都在睡觉。当他们饿的时候&#xff0c;他们会让他们的宝宝吃牛奶。他们吃饱了就要…

博客搭建教程(一):静态博客 GitHub + Gridea

同步blog文章 注册一个 Github 账号并创建仓库 注册Github账号 如果你没有 Github 的账号&#xff0c;那么可以进入 官网 开始注册&#xff08;注意一下用户名的填写&#xff0c;如果不使用自定义域名&#xff0c;用户名将会是你的 Github 分配给你的域名&#xff0c;例如你的…

D. George and Interesting Graph(最大匹配)

Problem - 387D - Codeforces 乔治喜欢图表。最重要的是&#xff0c;他喜欢有趣的图。我们将假设一个有向图是有趣的&#xff0c;如果它符合以下标准。 该图不包含任何多弧。 有一个顶点v&#xff08;我们称她为中心&#xff09;&#xff0c;这样对于图形u的任何顶点&#xff…

信息网络传播视听节目服务单位的设立与经营

一、行业准入 &#xff08;一&#xff09;网络视听业务准入范围 利用公共互联网&#xff08;含移动互联网&#xff09;向计算机、手机用户提供视听节目服务&#xff08;不含交互式网络电视&#xff08;IPTV&#xff09;、互联网电视、专网手机电视业务&#xff09;业务的&…

几个小设置让 mac 更好用

今天在 youtube 上看到一个视频[1]&#xff0c;讲新 mac 到手后一定要做的几个设置&#xff0c;有几个之前我不知道的小设置&#xff0c;非常好用&#xff0c;看完马上就用上了。一些常用的就不列了&#xff0c;比如说设置点按、三指拖拽&#xff0c;不知道的可以去搜索了解&am…

【Python数据可视化】使用geoplotlib绘制地理空间数据

geoplotlib前言一、安装geoplotlib包二、读取csv数据使用1.点密度可视化2.直方图3. Voronoi图总结前言 ❤️❤️希望大家能多多点赞。❤️❤️ 需要数据集的可以评论。 Geoplotlib 是地理空间数据可视化的开源Python库&#xff0c;包含了大量的地理空间可视化操作&#xff0c…