Sentinel 授权规则规则持久化
本篇博客我们来学习授权规则,授权规则是对请求者的一种身份的判断。
1、授权规则
授权规则是对请求者的身份做一个判断。你有没有权限来访问我?那就有人可能会说这个功能,好像以前我们在学习微服务的时候讲过网关他不就是把门的吗?
所有请求都要经过网关,网关去做身份的认证,看你有没有权限访问,我怎么到这儿又要整一个呢?
所有请求经过网关路由的微服务,这个时候网关当然可以对请求做身份的认证了。但是万一啊,你们公司里出了个内鬼,他把你们微服务的地址泄露给了外边的那些不怀好意的人。
那这些哥们儿啊,他们是不是就可以绕过网关直接访问微服务了?那你网关里做的安全校验再严密,还有用吗?你的微服务赤裸裸的暴露在别人面前。
所以呢,我们Sentinel的授权规则可以解决这个问题,因为它可以去验证你的请求是从哪来的。
如果说你是从网关过来的,我让你走,如果你是从别的地方过来的呢,我拦截你,这不就解决了吗?
1.1.基本规则
而Sentinel的授权规则里啊,配置也比较简单,主要就是白名单和黑名单两种白名单。
-
白名单:来源(origin)在白名单内的调用者允许访问
-
黑名单:来源(origin)在黑名单内的调用者不允许访问
那下边呢,是一个配置的示例。

大家可以看到资源名称就是受保护的那个资源,流控应用。就是名单了。
如果你勾选白名单,这儿就是许可的调用者。比如说呀,我现在只允许从网关来的请求访问orderService。

如果呢,你是从浏览器过来的,我就禁止你访问。那这个时候啊,资源名填的就是order service里边的受保护资源,比方说我们之前的那个order里的 {orderID}。那流控应用里填的呢,就是你允许的调用者的名字了。那这里我们允许网关禁止浏览器,那这是不是要填网关的名字Geteway呢?
并不是啊,这里比较特殊,在sentinel里边,这里让填的调用者名称。
其实是origin。你的请求来源名称。
1.2 如何获取origin
那么,这个请求来源是怎么得到的呢?在我们的Sentinel 里边有一个接口啊,叫RequestOriginParser。
public interface RequestOriginParser {/*** 从请求request对象中获取origin,获取方式自定义*/String parseOrigin(HttpServletRequest request);
}
请求来源解析器。
它里边有一个方法叫parseOrigin。它的参数是HttpServletRequest,那这个方法的作用就是从你这个请求的request对象里。想办法解析出origin的值,也就是来源者的名称。
不过可惜的是啊,在默认情况下sentinel这个方法的返回结果永远是default。
也就是说。你从网关过来也好,从浏览器过来也好。它的来源名称都叫default。sentinel根本没有办法去区分这两个请求。
你这怎么填?所以呀,我们必须想办法自己实现这个接口编写,它的业务逻辑,然后让从网关过来的请求和从浏览器过来的请求返回不同的结果。
那这样来它们的来源名称就不一样了?我们不就可以去编写授权规则了。但是这个业务逻辑该怎么写?
其实业务逻辑很好写,你无论是从请求头也好,还是从请求参数也好,cookie也好,只要你能够去区分。浏览器和网关不就行了吗?
比如说我这写了一个示例啊。
package com.jie.order.sentinel;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import javax.servlet.http.HttpServletRequest;/*** 通过解析请求头中的Origin字段,获取请求来源信息*/
@Component
public class HeaderOriginParser implements RequestOriginParser {/*** 解析请求头中的Origin字段,获取请求来源信息* @param request HTTP请求* @return 请求来源信息*/@Overridepublic String parseOrigin(HttpServletRequest request) {// 1.获取请求头中的Origin字段String origin = request.getHeader("origin");// 2.如果请求头中的Origin字段为空,则设置默认值为"blank"if (StringUtils.isEmpty(origin)) {origin = "blank";}// 3.返回请求来源信息return origin;}
}
名字叫HeaderOriginParser,这个类实现了RequestOriginParser接口,并重写了其中的parseOrigin方法。
parseOrigin方法的参数是一个HttpServletRequest对象,表示HTTP请求,它会获取请求头中的Origin字段,并返回该字段的值作为请求来源信息。
如果为空啊,我就返回blank。
如果不为空,我就把origin这个头的结果,作为来源名称返回。
如果浏览器获取的origin头与网关过来的请求。
获取的origin头不一样,那它们的来源名称是不是就不一样啊?我是不是就能编写授权规则了?那它们两个到底一样不一样呢?
事实上啊,网关也好,浏览器也好,默认都没有这个头,因为是我瞎编的。
1.3.给网关添加请求头
那现在那如果我给网关过来的请求,加上这样的头,那这里是不是就能有了?是不是就区分开了?所以这个规则是什么不重要,只要你约定好将来呢,我们给网关开点特权加一下就行。那问题来了,我们怎么给网关过来的请求都带上这个头呢?
大家还记不记得以前学习网关时学过一个过滤器啊?
SpringCloud 之 Gateway 服务网关_gateway mono_阿杰的代码空间的博客-CSDN博客
名字叫AddRequestHeader。
那么,加上这个过滤器以后啊,凡是通过网关路由到微服务的请求,必然会带上请求头。那这个头呢,是这么定义的啊。

spring:cloud:gateway:default-filters:- AddRequestHeader=origin,gateway
逗号隔开,前面是头的名字,后面头的值,所以呢,我这个请求头的名字叫origin,值就是gateway。
现在我们明确的知道了,经过网关的所有请求,一定会带上这样的头,那它的来源名称一定是叫gateWay,而从浏览器过来的就不一定了吧?
1.4 配置授权规则
那这样两者是不是就区分开了?那因此我们授权规则里边白名单,该填谁?

配置如下:

是不是填gateway?
诶,很好啊,所以是其实你这个头里边的值是什么?这就填什么啊,
因此呢,这个只要约定好就可以了,不是说非得这么写啊。
当然了,你这里怎么填的?怎么规定的?千万不能暴露给外界的人,别人知道了就能伪造请求头了。
1.5 测试
现在,我们直接跳过网关,访问order-service服务:

通过网关访问:

2 、自定义异常结果
刚刚呢,我们演示完了这个授权规则哦,那我们发现当我们被授权拦截时,页面上拿到的是个异常。

而且结果竟然是flow limiting限流的异常。这不就有问题了吗?你明明是授权拦截,你给人家返回了一个限流异常,这用户就一脸懵逼了。
而且不仅仅是授权啊,事实上我们所讲的限流也好,降级也好,各种异常,最终拿到的都是这个限流。
不够友好,结果不够清楚。
2.1 异常类型
那接下里我们就要来学习一下,如何自定义异常?自定义异常非常的简单,你只需要去实现一个接口叫BlockExceptionHandler就行了。
public interface BlockExceptionHandler {/*** 处理请求被限流、降级、授权拦截时抛出的异常:BlockException*/void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;
}
就是阻塞异常的一个处理器。为什么是它呢?
因为当Sentinel里面发生限流降级,授权拦截等等各种异常时,它都会抛出BlockException。
你只要实现了BlockExceptionHandler,那么你就能够去处理这个异常了,你看这里接口里只有一个方法叫handle啊。
这个方法有三个参数:
- HttpServletRequest request:request对象
- HttpServletResponse response:response对象
- BlockException e:被sentinel拦截时抛出的异常
那你在这个方法里啊,就可以去判断一下这个异常到底是哪一种,而后呢?根据异常的类型不同返回不同的结果,通过response呀,写到前端。
那我该怎么样去判断这个异常是哪种类型的异常呢?
其实这里的BlockException包含多个不同的子类:
| 异常 | 说明 |
|---|---|
| FlowException | 限流异常 |
| ParamFlowException | 热点参数限流的异常 |
| DegradeException | 降级异常 |
| AuthorityException | 授权规则异常 |
| SystemBlockException | 系统规则异常 |
所以我们就可以对异常类型做个判断,看它是这5种中的哪一种,从而去返回不同的结果,做不同处理。
2.2 自定义异常处理
下面,我们就在order-service定义一个自定义异常处理类:
package com.jie.order.sentinel;import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Sentinel的异常处理器,用于处理Sentinel的流量控制、熔断降级和授权管理等异常*/
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {/*** 处理Sentinel的异常** @param request HTTP请求* @param response HTTP响应* @param e Sentinel的异常* @throws Exception 抛出异常*/@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {String msg = "未知异常";int status = 429;// 根据异常类型,设置不同的响应信息和状态码if (e instanceof FlowException) {msg = "请求被限流了";} else if (e instanceof ParamFlowException) {msg = "请求被热点参数限流";} else if (e instanceof DegradeException) {msg = "请求被降级了";} else if (e instanceof AuthorityException) {msg = "没有权限访问";status = 401;}// 设置HTTP响应的内容类型和状态码,并输出响应内容response.setContentType("application/json;charset=utf-8");response.setStatus(status);response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");}
}
重启测试,在不同场景下,会返回不同的异常消息.(这里就不演示添加流控规则了)


3、规则持久化
经过前边的学习啊,我们已经掌握了sentinel的常用玩法。
在使用的过程中,我们发现有一个问题啊,就是每当我们的服务重启,我们所配的各种各样的规则,它就丢失了。
这是因为sentinel默认会把这些规则保存在内存里,重启自然就丢失了。那我们在生产环境下肯定无法容忍这样的问题啊。
所以们就来学习一下如何将sentinel的规则持久化。
3.1 规则管理模式
规则管理呢,它有三种模式:
- 原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
- pull模式
- push模式
原始模式是sentinel的默认模式,这种模式呢sentinel。会把规则保存在内存里,那这样一重启自然就丢失了。
而pull和push这两种模式啊,都可以实现规则的持久化,只不过实现的方式上有差异。
3.1.1 pull模式
我们首先首先来说一下pull模式啊。

Sentinel的Sentinel Dashboard 控制台,后面是一个微服务。它是Sentinel的客户端。
你要知道我们在实际生产当中,微服务一定是集群同一个微服务是不是也会部署多份啊?
那当你向一个Dashboard 里编写规则时,那会把这个规则推送给这个微服务的某一个Sentinel 的客户端。而它就会将这个规则持久化到一个本地的文件或者是数据库里去,那这样我们就实现了规则的持久化。
但是呢,如果说我还有一个服务,也需要这个规则呢?我怎么知道这个规则有没有变化呢?所以呢,我们的微服务呢,就会去定时轮询啊,这个文件或者是数据库。
当监听到数据库或者文件的内容发生变化时,我就知道规则更新了,那我是不是就可以去更新我自己的这个规则缓存了?
这样呢,就可以实现多个Sentinel 客户端的规则同步了,但是呢,这种定时轮询的方式。它存在一些缺点啊,第一呢,是时效性比较差,你想你这边刚写进去。那边那个服务它还不一定去读取呢,对不对?
它是定时的呀,那如果它现在还没轮到去读取,那现在你的服务与服务之间。是不是数据就不一致了呀?规则就不一致了。
所以这种模式存在一个时效性的问题,从而就导致了一个数据的不一致问题啊。那因此,这种方案也不是非常的推荐。
3.1.2 push模式
那我们再来看一下最后一种模式 push模式。

push模式 Sentinel Dashboard 并不会把这个规则推送给任意一个客户端。而是啊,把这个规则保存到远程的一个配置中心里,比如说我们之前所学习的 nacos。
这是一个统一配置中心,那 Sentinel Dashboard把这个东西推到nacos。而我们的微服务都可以去监听nacos,一旦发现nacos有变化,是不是立即监听并且更新这些数据。
那他们本地的规则是不是跟着都变化了,自然就生效了,这种方式利用了nacos数据更新的这样一种特性来实现配置的一个更新和持久化。
所以也是我们所推荐的方式啊。
3.2.实现push模式
这个push模式实现起来还是挺复杂的。

这个图是我们已经讲过的啊,这个部署模式的流程图,那我们知道啊,在这种模式当中Sentinel Dashboard 需要把规则推送到nacos,而不再是推送到Sentinel 的客户端。
但是呢,在Sentinel 的默认实现当中啊,它都是推到客户端去的。
而推到nacos 的这些功能并没有在Sentinel Dashboard的源码中实现。所以如果我们要实现push模式,我们不得不自己去改动Sentinel Dashboard的源代码。
这足以说明啊,这个阿里巴巴还是挺鸡贼的,是不是你实现了一个开源的框架,结果你却不去实现它最佳模式?
然后给的都是demo,还得让我们自己改编码。不仅如此啊,那我们的渗透客户端是不是也要去监听nacos呀?
因此你还将来还要去改什么哎?微服务端微服务端要去监听nacos。
所以要改动的地方还是挺多的啊,当然如果你不想改也行,那阿里巴巴呢,还提供了一个云服务啊。
这个云服务肯定是收钱的了,你如果你用它的云服务,那你就不需要自己去搭建Sentinel 了。
那我们这里肯定是不会去给他掏钱的,我们来自己搭建一下。
3.2.1 修改order-service服务
首先修改OrderService,让其监听Nacos中的sentinel规则配置。
具体步骤如下:
1.引入依赖
在order-service中引入sentinel监听nacos的依赖:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.配置nacos地址
在order-service中的application.yml文件配置nacos地址及监听的配置信息:
spring:cloud:sentinel:datasource:flow:nacos:server-addr: localhost:8848 # nacos地址dataId: orderservice-flow-rulesgroupId: SENTINEL_GROUPrule-type: flow # 还可以是:degrade、authority、param-flow
好,这里配完,那我们这个服务其实就准备好了啊,那我们就可以去重启这个服务了。
3.2.2 修改sentinel-dashboard源码
SentinelDashboard默认不支持nacos的持久化,需要修改源码。
sentinel-dashboard源码 在我的公众号 《KnowledgeSeeker》里给大家提供了。

只需要回复 Sentinel规则持久化 就可以获取了。
1. 解压
解压 sentinel源码包,然后并用IDEA打开这个项目,结构如下:

2. 修改nacos依赖
在sentinel-dashboard源码的pom文件中,nacos的依赖默认的scope是test,只能在测试时使用,这里要去除:

3. 添加nacos支持
在sentinel-dashboard的test包下,已经编写了对nacos的支持,我们需要将其拷贝到main下。

4. 修改nacos地址
然后,还需要修改测试代码中的NacosConfig类:

代码放在下面了。
package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;/*** @author Eric Zhao* @since 1.4.0*/
@Configuration
@ConfigurationProperties(prefix = "nacos")
public class NacosConfig {/*** nacos 地址*/private String addr;public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}@Beanpublic ConfigService nacosConfigService() throws Exception {return ConfigFactory.createConfigService(addr);}
}
在sentinel-dashboard的application.properties中添加nacos地址配置:
nacos.addr=localhost:8848

5. 配置nacos数据源
另外,还需要修com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2类:

让我们添加的Nacos数据源生效:

6. 修改前端页面
接下来,还要修改前端页面,添加一个支持nacos的菜单。
修改src/main/webapp/resources/app/scripts/directives/sidebar/目录下的sidebar.html文件:

将其中的这部分注释打开:

修改其中的文本:

7. 重新编译、打包项目
运行IDEA中的maven插件,编译和打包修改好的Sentinel-Dashboard:

8.启动
启动方式跟官方一样:
java -jar sentinel-dashboard.jar
如果要修改nacos地址,需要添加参数:
java -jar -Dnacos.addr=localhost:8848 sentinel-dashboard.jar
3.3 测试

这里现在什么都没有啊。我们去访问一下http://localhost:8088/order/103

然后在回去刷新一下。

可以看到啊,现在是不是多出了一个流控规则了,就是Nacos的流控规则那如果你点这个表单啊,在这添加的流控规则。最终就会进入Nacos了。
但是呢,如果你现在是在这边去添加啊。

它就还是原始模式,所以呢,这里其实我只改了一个页面啊,理论上讲你要想实现,你这里面每个都得改,所以改动特别的大,那现在呢,我们就来测一下这个流控规则。
那我们再来点击新增流控规则。

我们给那个/order/{orderId}加一个流控规则。

我们一定要在我们这个流控规则-NACOS 这里加,到其他的地方还是走的原始模式。
我们到NACOS里去刷新看看。

发现已经多出了一个配置了。
我再去浏览器疯狂刷新看看,有没有限流规则。

我们后面现在去重启服务,看看我们的配置会不会丢失。
相关文章:
Sentinel 授权规则规则持久化
本篇博客我们来学习授权规则,授权规则是对请求者的一种身份的判断。 1、授权规则 授权规则是对请求者的身份做一个判断。你有没有权限来访问我?那就有人可能会说这个功能,好像以前我们在学习微服务的时候讲过网关他不就是把门的吗࿱…...
C#大型HIS医院LIS管理系统源码
▶ 一、实验室信息管理系统(LIS)是什么? 实验室信息管理系统也就是平时所说的LIS(Laboratory Information System)系统,其主要服务的对象主要是医院检验科工作人员,也是医院信息化建设必…...
Java基础学习(5)
Java基础学习一 面向对象1.1 介绍对象1.2 设计对象并使用1.2.1定义类的补充注意事项1.3 封装好处:1.3 private关键字1.4 this关键字1.5 构造方法构造方法的注意事项:1.6 标准的JavaBean1.7 对象内存图1.7.1 一个对象的内存图1.7.2 两个对象内存图1.7.两个引用指向同一个对象1.8…...
SpringBoot接口 - 如何生成接口文档之Swagger技术栈
SpringBoot开发Restful接口,有什么API规范吗?如何快速生成API文档呢?Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上&am…...
JavaScript execCommand函数
execCommand函数命令execCommand方法是执行一个对当前文档,当前选择或者给出范围的命令。处理Html数据时常用如下格式:document.execCommand(sCommand[,交互方式, 动态参数]) ,其中:sCommand为指令参数(如下例中的”2D…...
2023年安徽省中职网络安全跨站脚本攻击
B-4:跨站脚本攻击 任务环境说明: √ 服务器场景:Server2125(关闭链接) √ 服务器场景操作系统:未知 √ 用户名:未知 密码:未知 1.访问服务器网站目录1,根据页面信息完成条件&am…...
Jmeter之常用断言总结篇
在使用Jmeter进行性能测试或者接口自动化测试工作中,经常会用到的一个功能,就是断言。断言是在请求的返回层面增加一层判断机制,因为请求成功了,并不代表结果一定正确,因此需要判断机制提高测试准确性。本文主要介绍6种…...
Elasticsearch:如何在 Elastic 中实现图片相似度搜索
作者:Radovan Ondas 在本文章,我们将了解如何通过几个步骤在 Elastic 中实施相似图像搜索。 开始设置应用程序环境,然后导入 NLP 模型,最后完成为你的图像集生成嵌入。 Elastic 图像相似性搜索概览 >> 如何设置环境 第一步…...
一起Talk Android吧(第五百二十三回:获取位置信息经验总结)
文章目录 整体概述位置权限与蓝牙权限综合使用特殊机型的使用方法官方建议各位看官们大家好,上一回中咱们说的例子是"如何有效地获取位置权限",这一回中咱们说的例子是" 获取位置信息经验总结"。闲话休提,言归正转, 让我们一起Talk Android吧! 整体概…...
发光立方体效果 html+css
一.话不多,看效果 css简单创意特效,关注我看更多简单创意特效~ 二.实现(附完整代码) 定义标签: <div class"container"><div class"q1"></div><div class"h2"&…...
READ: Large-Scale Neural Scene Rendering for Autonomous Driving
READ: Large-Scale Neural Scene Rendering for Autonomous Driving :面向自动驾驶的大规模神经场景绘制 门卷积 https://www.jianshu.com/p/09fc8490104d https://blog.csdn.net/weixin_44996354/article/details/117409438摘要:论文提出了一种大规模神…...
Linux环境C语言开发基础
C语言是一门面向过程的计算机编程语言,与C、C#、Java等面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言诞生于美国的贝尔实验室,由丹…...
string类(上)
string类(上)1.标准库中的string类2.string类对象的常见构造①string()②string(const char* s)③string(size_t n,char c)④string(const string&s)⑤string(const string& str,size_t pos,size_t lennpos)⑥string(const char* s,s…...
ElasticSearch快速入门详解(亲测好用,强烈推荐收藏)
3.快速入门 接下来快速看下elasticsearch的使用 3.1.概念 Elasticsearch虽然是一种NoSql库,但最终的目的是存储数据、检索数据。因此很多概念与MySQL类似的。 ES中的概念数据库概念说明索引库(indices)数据库(Database)ES中可…...
出入了解——Vue.js
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。座右铭:海不辞水,故能成其大;山不辞石,故能成其高。个人主页:小李会科技的…...
MySQL8 双主(主主)架构部署实战
前言 大家好,我是 沐风晓月 本文收录于《数据库入门到精通系列》专栏, 更多内容可以关注我的csdn博客。 本文主要讲解MySQL主主架构实战,在开始之前需要根据下面的提示来配置环境: Linux基础命令不熟参考: 《linux基本功-基础…...
【数据结构】第三站:单链表
目录 一、顺序表的缺陷 二、链表 1.链表的概念以及结构 2.链表的分类 3.单链表的逻辑结构与物理结构 三、单链表的实现 1.单链表的定义 2.单链表的接口定义 3.单链表的接口实现 四、单链表的实现完整代码 一、顺序表的缺陷 在上一篇文章中,我们了解了顺序…...
【蓝桥杯2020】七段码
【题目描述】 七段码 HUSTOJ 题目导出文件 [蓝桥杯2020] 第十一届蓝桥杯第二次省赛—填空题E题 七段码 小蓝要用七段码数码管来表示一种特殊的文字。 上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二 极管,分别标记为 a, b, c,…...
Spark读取JDBC调优
Spark读取JDBC调优,如何调参一、场景构建二、参数设置1.灵活运用分区列实际问题:工作中需要读取一个存放了三四年历史数据的pg数仓表(缺少主键id),需要将数据同步到阿里云 MC中,Spark在使用JDBC读取关系型数…...
【文心一言】什么是文心一言,如何获得内测和使用方法。
文心一言什么是文心一言怎么获得内测资格接下来就给大家展示一下文学创作商业文案创作数理逻辑推算中文理解多模态生成用python写一个九九乘法表写古诗前言: 🏠个人主页:以山河作礼。 📝📝:本文章是帮助大家了解文心…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...
