6.结果处理组件之ResponseHandler
前言
feign发送完请求后, 拿到返回结果, 那么这个返回结果肯定是需要经过框架进一步处理然后再返回到调用者的, 其中ResponseHandler
就是用来处理这个返回结果的, 这也是符合正常思维的处理方式, 例如springmvc部分在调用在controller端点前后都会增加扩展点。
从图中可以看得feign的返回处理应该不会很复杂, 并且可以自定义日志对象,和日志级别,对返回值进行解码, 并允许我们使用责任链来处理返回结果。
代码解析
老规矩, 查看类结构, 看该对象给我们提供了哪些功能
ResponseHandler
public class ResponseHandler {/*** 日志等级; 默认是Logger.Level.NONE*/private final Level logLevel;/*** 日志对象; 默认是NoOpLogger*/private final Logger logger;/*** 对正常响应数据进行解码的解码器; 默认是Decoder.Default*/private final Decoder decoder;/*** 404异常时, 对错误信息进行解码的解码器, 默认是ErrorDecoder.Default*/private final ErrorDecoder errorDecoder;/*** 当返回类型不为void, 并且响应状态码是404 1.如果dismiss404为true时, 那么忽略异常 2.如果dismiss404为false,则会抛异常*/private final boolean dismiss404;/*** 是否在解码返回数据后关闭相应的流*/private final boolean closeAfterDecode;/*** 是否对void类型返回值进行解码*/private final boolean decodeVoid;/*** 响应拦截链*/private final ResponseInterceptor.Chain executionChain;/*** 唯一构造器*/public ResponseHandler(Level logLevel, Logger logger, Decoder decoder, ErrorDecoder errorDecoder,boolean dismiss404, boolean closeAfterDecode, boolean decodeVoid,ResponseInterceptor.Chain executionChain) {...}public Object handleResponse(String configKey,Response response,Type returnType,long elapsedTime)throws Exception {...}
}
ResponseHandler
类是同步请求结果处理器
- 它提供了一个参数非常多且唯一一的构造器
- 提供了一个方法
handleResponse
来处理返回对象
其中有个属性是ResponseInterceptor.Chain
, 它是用来处理返回对象的责任链, 我们简单认识一下它
ResponseInterceptor.Chain
public interface ResponseInterceptor {interface Chain {Chain DEFAULT = InvocationContext::proceed;Object next(InvocationContext context) throws Exception;}/*** 拦截器套娃包装*/default ResponseInterceptor andThen(ResponseInterceptor nextInterceptor) {return (ic, chain) -> intercept(ic,nextContext -> nextInterceptor.intercept(nextContext, chain));}/*** 执行拦截器*/Object intercept(InvocationContext invocationContext, Chain chain) throws Exception;/*** 执行责任链*/default Chain apply(Chain chain) {return request -> intercept(request, chain);}
}
它是内聚在ResponseInterceptor
中的一个接口。
- 定义了一个默认链
Chain.DEFAULT
- 提供了一个获取下一节点的方法
- ResponseInterceptor提供了一个静态的包装方法
andThen
, 用来拦截器套娃 - ResponseInterceptor提供了一个用于执行拦截器的方法
intercept
, - ResponseInterceptor提供了一个用于执行责任链的方法
apply
它在Feign.Builder
的父类BaseBuilder
中实例化
public abstract class BaseBuilder<B extends BaseBuilder<B, T>, T> implements Cloneable {/*** 返回结果拦截器*/protected final List<ResponseInterceptor> responseInterceptors = new ArrayList<>();// 用新的拦截器替换掉原有的拦截器public B responseInterceptors(Iterable<ResponseInterceptor> responseInterceptors) {this.responseInterceptors.clear();for (ResponseInterceptor responseInterceptor : responseInterceptors) {this.responseInterceptors.add(responseInterceptor);}return thisB;}/*** 添加单个拦截器*/public B responseInterceptor(ResponseInterceptor responseInterceptor) {this.responseInterceptors.add(responseInterceptor);return thisB;}/*** response拦截器组成链条*/protected ResponseInterceptor.Chain responseInterceptorChain() {ResponseInterceptor.Chain endOfChain =ResponseInterceptor.Chain.DEFAULT;ResponseInterceptor.Chain executionChain = this.responseInterceptors.stream().reduce(ResponseInterceptor::andThen).map(interceptor -> interceptor.apply(endOfChain)).orElse(endOfChain);return (ResponseInterceptor.Chain) Capability.enrich(executionChain,ResponseInterceptor.Chain.class, capabilities);}
}
该方法responseInterceptorChain
是protected修饰的
, 子类可以重写它。允许我们每次添加一个拦截器, 或者直接全部替换。
构建责任链
ResponseInterceptor.Chain executionChain = this.responseInterceptors.stream().reduce(ResponseInterceptor::andThen).map(interceptor -> interceptor.apply(endOfChain)).orElse(endOfChain);
这和我们传统看到的责任链有点不同, 传统的责任链一般是有前后节点以及上下文, 然后用责任链触发调用, 这里的区别在于责任链中节点的构建方式有点不同, 这里是嵌套包装的性质.
为了让大家更好的理解这坨代码, 我把它平铺开, 写段伪代码
public ResponseInterceptor.Chain buildRespChain() {ResponseInterceptor.Chain endOfChain = ResponseInterceptor.Chain.DEFAULT;// 合并所有拦截器成一个 ResponseInterceptorResponseInterceptor combinedInterceptor = null;for (ResponseInterceptor interceptor : this.responseInterceptors) {if (combinedInterceptor == null) {combinedInterceptor = interceptor;} else {ResponseInterceptor previousCombinedInterceptor = combinedInterceptor;combinedInterceptor = new ResponseInterceptor() {@Overridepublic Object intercept(InvocationContext ic, Chain chain) throws Exception {return previousCombinedInterceptor.intercept(ic, new Chain() {@Overridepublic Object next(InvocationContext context) throws Exception {return interceptor.intercept(context, chain);}});}};}}// 如果没有拦截器,直接返回 endOfChainif (combinedInterceptor == null) {return endOfChain;}ResponseInterceptor temp = combinedInterceptor;// 使用 apply 构造最终责任链return new ResponseInterceptor.Chain() {@Overridepublic Object next(InvocationContext request) throws Exception {return temp.intercept(request, endOfChain);}};}
-
核心就是调用每一个过滤节点(这里是拦截器)的时候把下一个传节点封装成Chain传进去, 然后我们就可以在拦截器的
intercept
方法中通过Chain的next方法调用下一个节点拦截器了 -
最后再构建一个最终的
Chain
, 在next
方法中调用构建出来的拦截器链, 并传入默认节点endOfChain
, 也就是说我们自定义的拦截器会先执行 -
最后再执行这个
endOfChain
(取决于各拦截器执行的行为, 可以决定是正序还是倒序), 返回整条连的执行结果
说到责任链, 我的这篇文章也介绍了一个我之前写的拦截器项目通用责任链在项目中使用
那么这个默认的的节点endOfChain
长什么样呢? 我们得研究一下
InvocationContext
它是用lambda表达式表示的一个Chain, 定义在响应链处理器的最后一个节点, 用来处理最终的返回结果
关于lambda表达式, 如果大家不是很了解, 可以去看看我的这篇文章 lambda表达式原理
Chain DEFAULT = InvocationContext::proceed;
public class InvocationContext {public Object proceed() throws Exception {// 方法返回类型是Response(一般也不会是这个)if (returnType == Response.class) {// 读取流中的数据; 1.如果响应体为空或者大于8k, 直接返回response 2.响应数据不为空且小于8k, 将数据流取出来return disconnectResponseBodyIfNeeded(response);}try {// 1.响应正常 或者 2.响应状态码是404 并且 dismiss404为true 并且 返回值类型不是void类型final boolean shouldDecodeResponseBody =(response.status() >= 200 && response.status() < 300)|| (response.status() == 404 && dismiss404&& !isVoidType(returnType));// shouldDecodeResponseBody为false的几种情况如下// 1.状态码不是200-300 并且 2.状态码不是404; 例如status为500// 2.状态码不是200-300 并且 状态码是404且dismiss404为false// 3.状态码不是200-300 并且 状态码是404且dismiss404为true 并且 返回类型是void类型if (!shouldDecodeResponseBody) {// 抛异常, 这里可能是重试异常RetryableExceptionthrow decodeError(configKey, response);}// 1.返回值类型是void 2.不允许对void类型进行解码if (isVoidType(returnType) && !decodeVoid) {// 关闭流ensureClosed(response.body());return null;}// 获取返回值的原始类型Class<?> rawType = Types.getRawType(returnType);// 返回类型是TypedResponse类型; 类似于泛型中的 <rawType extend TypedResponse>if (TypedResponse.class.isAssignableFrom(rawType)) {// 获取TypedResponse中参数泛型的类型, 也就是TypedResponse<T>中的TType bodyType = Types.resolveLastTypeParameter(returnType, TypedResponse.class);// 这里把response解码成TypedResponse<T>中的T类型然后设置给bodyreturn TypedResponse.builder(response).body(decode(response, bodyType)).build();}// 把response解码成bodyType类型return decode(response, returnType);} finally {// decode之后关闭流if (closeAfterDecode) {ensureClosed(response.body());}}}
}private static Response disconnectResponseBodyIfNeeded(Response response) throws IOException {// 如果数据小于8k, 证明数据已经返回完了, 不需要再读取数据; 否则返回response本身继续读取数据final boolean shouldDisconnectResponseBody = response.body() != null&& response.body().length() != null&& response.body().length() <= MAX_RESPONSE_BUFFER_SIZE;// 1.如果响应体为空或者大于8k, 直接返回responseif (!shouldDisconnectResponseBody) {return response;}try {// 响应数据不为空且小于8k, 将数据流取出来final byte[] bodyData = Util.toByteArray(response.body().asInputStream());return response.toBuilder().body(bodyData).build();} finally {// 关闭响应流(inputStream)ensureClosed(response.body());}}
小结一下
- 如果返回类型是
Response
类型
- 如果返回数据小于8k, 证明数据已经返回完了, 不需要再读取数据;
- 否则返回response本身继续读取数据, 并关闭响应流数据
- 如果请求失败, 如下情况将会抛异常(可能是重试异常)
- 状态码不是200-300 并且 2.状态码不是404; 例如status为500
- 状态码不是200-300 并且 状态码是404且dismiss404为false
- 状态码不是200-300 并且 状态码是404且dismiss404为true 并且 返回类型是void类型
- 当方法返回值不是空时, 如果不想404报错, dismiss404参数设置为true就行
- 如果返回类型是void, 并且不允许对void类型进行解码, 直接关闭流
- 如果返回值类型是
TypedResponse
, 那么对返回数据的body解码(这里只支持String和byte[]类型) - 如果返回类型既不是
Response
, 也不是TypedResponse
, 直接将返回的响应体数据解码成方法返回类型(这里只支持String和byte[]类型)
这里正常数据解码器是
Decoder.Default
, 异常数据解码器是ErrorDecoder.Default
泛型的解析工具类是
Types
, 如果想要更多的了解泛型, 可以看我的这篇文章 java泛型探究
这里了解一下Response
和TypedResponse
的区别
public final class Response implements Closeable {// ...忽略其它属性private final Body body;
}public final class TypedResponse<T> {// ...忽略其它属性private final T body;public Builder body(T body) {this.body = body;return this;}
}
- Response实现了
Closeable
接口, 可以用来自动关闭流 - Response的响应体对象是一个Body类型, 而
TypedResponse
的响应体是一个泛型, 该泛型是body进行解码转换的结果
关于异常状态码的响应的重试处理
// shouldDecodeResponseBody为false的几种情况如下
// 1.状态码不是200-300 并且 2.状态码不是404; 例如status为500
// 2.状态码不是200-300 并且 状态码是404且dismiss404为false
// 3.状态码不是200-300 并且 状态码是404且dismiss404为true 并且 返回类型是void类型
if (!shouldDecodeResponseBody) {// 抛异常throw decodeError(configKey, response);
}private Exception decodeError(String methodKey, Response response) {
try {// 默认是ErrorDecoder.Default; 如果有重试, 会抛RetryableExceptionreturn errorDecoder.decode(methodKey, response);
} finally {// 关闭响应流(inputStream)ensureClosed(response.body());
}
}
这里errorDecoder
默认是ErrorDecoder.Default
public class Default implements ErrorDecoder {public Exception decode(String methodKey, Response response) {FeignException exception = errorStatus(methodKey, response, maxBodyBytesLength,maxBodyCharsLength);// 重试的时间 毫秒类型; RETRY_AFTER:Retry-AfterLong retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));if (retryAfter != null) {return new RetryableException(response.status(),exception.getMessage(),response.request().httpMethod(),exception,retryAfter,response.request());}return exception;}
}
-
这里
retryAfterDecoder
默认是ErrorDecoder.RetryAfterDecoder
, 定义在RetryAfterDecoder
内部. -
firstOrNull(response.headers(), RETRY_AFTER)
用与获取响应头中Retry-After
属性的值(毫秒) -
如果返回了正确的重试时间, 那么抛
RetryableException
异常, 否则抛FeignException
异常
static class RetryAfterDecoder {public Long apply(String retryAfter) {if (retryAfter == null) {return null;}// 也就是数字 或者数字.?0*, 例如 匹配:100、100.、100.0、100.00if (retryAfter.matches("^[0-9]+\\.?0*$")) {// 去掉小数部分, 例如 100.00 -> 100retryAfter = retryAfter.replaceAll("\\.0*$", "");// 转毫秒long deltaMillis = SECONDS.toMillis(Long.parseLong(retryAfter));// 下次重试时间return currentTimeMillis() + deltaMillis;}try {// 否则就是时间格式return ZonedDateTime.parse(retryAfter, dateTimeFormatter).toInstant().toEpochMilli();} catch (NullPointerException | DateTimeParseException ignored) {// 其它格式不重试return null;}}
}
- 响应头
Retry-After
属性为空或者对应的值为空, 返回null - 如果``Retry-After
属性返回的是数字, 那么计算下次重试的时间点(当前时间+响应头
Retry-After`设置的时间) - 如果``Retry-After
属性返回的是时间格式, 那么它只能是
Day-of-Week, DD Month YYYY HH:mm:ss GMT例如
Tue, 3 Jun 2008 11:05:30 GMT`这种
响应责任链的部分介绍完了, 下面回归到ResponseHandler
类
ResponseHandler详情
ResponseHandler
public Object handleResponse(String configKey,Response response,Type returnType,long elapsedTime)throws Exception {try {// 打印响应相关的日志response = logAndRebufferResponseIfNeeded(configKey, response, elapsedTime);// 执行责任链, 处理响应数据return executionChain.next(new InvocationContext(configKey, decoder, errorDecoder, dismiss404, closeAfterDecode,decodeVoid, response, returnType));} catch (final IOException e) {// 打印日常日志if (logLevel != Level.NONE) {logger.logIOException(configKey, logLevel, e, elapsedTime);}// 抛FeignException异常throw errorReading(response.request(), response, e);} catch (Exception e) {// 关闭响应流ensureClosed(response.body());throw e;}}
小结一下
- 打印相应结果相关的日志
- 使用响应责任链处理返回结果
- 异常情况打印日志然后包装成FeignException异常异常抛出
- 最后关闭响应流
logAndRebufferResponseIfNeeded
在执行相应责任链调用之前, 先打印了一段日志
private Response logAndRebufferResponseIfNeeded(String configKey,Response response,long elapsedTime)throws IOException {// 默认是NoOpLogger, 也就是不打印日志if (logLevel == Level.NONE) {return response;}return logger.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);}
- 默认的
logLevel
是Level.NONE
, 也就是不打印日志 - 以Slf4j举例, Slf4j在feign中是以
Slf4jLogger
对象存在, 它包装了org.slf4j.Logger对象
Slf4jLogger#logAndRebufferResponse
@Overrideprotected Response logAndRebufferResponse(String configKey,Level logLevel,Response response,long elapsedTime)throws IOException {if (logger.isDebugEnabled()) {// 这里调用feign.Logger中的方法return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);}return response;}
举例
@Test
void logFunc() {DemoClient client = Feign.builder().logLevel(feign.Logger.Level.FULL).logger(new Slf4jLogger()).dismiss404().target(DemoClient.class, "http://localhost:8080");client.getDemo1("uncleqiao");
}
这里将打印请求和响应的所以信息, 包括响应体; 这里dismiss404是为了测试时候, 接口不存在时的404不报错
总结
- Feign通过
ResponseHandler
来处理响应的结果 ResponseHandler
主要通过响应责任链来处理响应结果, 我们可以自定义其中的节点拦截器做自定义的事情- 责任链中默认添加了一个节点
InvocationContext
, 用来真正处理返回结果
- 如果方法返回类型是
Response
, 那么根据返回的数据长度是否为空或者大于8k, 如果满足, 直接返回Response
, 如果小于8k, 重新构建新的Response
返回, 并关闭响应流; 这里重新构建是因为流只能读取一次, 如果关闭了就读取不到了
- 如果状态码是404, 并且设置dismiss404为true, 那么将忽略异常
- 如果状态码不是200-300也不是404, 那么根据响应头是否鞋带
Retry-After
, 并有正确的值, 那么将会抛重试异常进行重试, 否则抛FeignException
, 然后关闭响应流 - 如果返回类型是void, 并且不允许对void进行编码(decodeVoid=false), 那么关闭响应流, 直接返回null
- 如果方法返回值是
TypedResponse
类型, 那么将响应体通过Decoder.Default
解码转为TypedResponse
中的参数泛型类型, 并返回TypedResponse
- 如果返回值是其它类型(非TypedResponse和Response), 那么直接将响应体通过
Decoder.Default
解码转为返回类型, 然后直接返回 - 如果开启了解码后关闭流的动作(closeAfterDecode=treu), 那么关闭响应流(一般情况下会走这个逻辑)
相关文章:
6.结果处理组件之ResponseHandler
前言 feign发送完请求后, 拿到返回结果, 那么这个返回结果肯定是需要经过框架进一步处理然后再返回到调用者的, 其中ResponseHandler就是用来处理这个返回结果的, 这也是符合正常思维的处理方式, 例如springmvc部分在调用在controller端点前后都会增加扩展点。 从图中可以看得…...
【C#】调用外部应用
以WINFORM应用程序为例,在C#应用程序中调用PYTHON程序(Matplotlib绘制图形程序),将调用PYTHON程序生成的窗口程序嵌入在WINFORM窗口中 窗口程序类 using System; using System.Collections.Generic; using System.Data; using S…...
JavaWeb--JDBC
JDBC(Java Database Connectivity,Java数据库连接)是一种Java API,可以让Java程序连接到数据库并进行数据的操作。它是Java平台的一部分,由Sun Microsystems(现为Oracle Corporation的一部分)开…...
神经网络归一化方法总结
在深度学习中,归一化 是提高训练效率和稳定性的关键技术。以下是几种常见的神经网络归一化方法的总结,包括其核心思想、适用场景及优缺点。 四种归一化 特性Batch NormalizationGroup NormalizationLayer NormalizationInstance Normalization计算维度…...
Debian/Ubuntu 、Fedora 、Arch Linux, 在Linux上,对文本文件进行多线程压缩 xz、pxz、zstd、7z、lrzip
Debian/Ubuntu 、Fedora 、Arch Linux, 在Linux上,对文本文件进行多线程压缩 xz、pxz、zstd、7z、lrzip 前言对比多线程压缩1. 使用 pxz安装 pxz使用 pxz 2. 使用 xz 的 -T 选项使用 xz -T 3. 其他压缩命令1. 使用 gzip2. 使用 bzip23. 使用 xz4. 使用 7…...
前端使用fontfaceobserver库实现字体设置
要使用FontFaceObserver来加载设置项目本地的字体,先确保字体文件位于项目中或者可以从服务端获取到,这样就可以使用FontFaceObserver来检测并加载这些字体 主要有以下几步: npm或者yarn安装引入fontfaceobserver字体资源引入和font-face配置…...
SSM--SpringMVC复习(二)
请求 URL匹配: RequestMapping RequestMapping 负责将请求映射到对应的控制器方法上。 RequestMapping 注解可用于类或方法上。用于类上,表示类中的所有响应请求的方法都以该地址作为父路径。 在整个 Web 项目中,RequestMapping 映射的请求…...
Oracle 11gR2 坏块修复实例一则
背景 前段时间在 Oracle 11gR2 数据库中发现了坏块问题。环境是 64 位 Linux 平台。本文将详细介绍如何使用 DBMS_REPAIR 进行在线修复,当然也可以基于备份和 RMAN 的修复方法这里暂时不做介绍。 发现坏块 1. 从 alert.log 中发现错误 在 alert.log 文件中发现了…...
使用 Docker Compose 来编排部署LMTNR项目
使用 Docker Compose 来部署一个包含 Linux、MySQL、Tomcat、Nginx 和 Redis 的完整项目的例子。假设我们要部署一个简单的 Java Web 应用,并且使用 Nginx 作为反向代理服务器。 项目目录结构 首先需要确保 Docker 和docker-compose已经安装并正在运行。docker --v…...
el-table 根据屏幕大小 动态调整max-height 的值
<template><div><p>窗口高度:{{ windowHeight }} px</p></div> </template><script> export default {data() {return {// 下面的 -250 表示减去一些表单元素高度 这个值需要自己手动调整windowHeight: document.docume…...
el-cascader 使用笔记
1.效果 2.官网 https://element.eleme.cn/#/zh-CN/component/cascader 3.动态加载(官网) <el-cascader :props"props"></el-cascader><script>let id 0;export default {data() {return {props: {lazy: true,lazyLoad (…...
Cookie概念和API
Cookie概念 Cookie在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie就是一个键和一个值构成的,随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器…...
Linux服务器安装mongodb
因为项目需要做评论功能,领导要求使用mongodb,所以趁机多学习一下。 在服务器我们使用docker安装mongodb 1、拉取mongodb镜像 docker pull mongo (默认拉取最新的镜像) 如果你想指定版本可以这样 docker pull mongo:4.4&#…...
32.4 prometheus存储磁盘数据结构和存储参数
本节重点介绍 : prometheus存储磁盘数据结构介绍 indexchunkshead chunksTombstoneswal prometheus对block进行定时压实 compactprometheus 查看支持的存储参数 prometheus存储示意图 内存和disk之间的纽带 wal WAL目录中包含了多个连续编号的且大小为128M的文件,…...
两个生活中的例子反向理解正/反向代理?
正向代理 场景:你在学校里想访问一个被限制的网站,比如某个社交媒体平台。 操作方式: 你把访问请求发送给学校的代理服务器(正向代理)。代理服务器代表你向互联网发出请求,去访问那个受限的网站。网站的响…...
数据结构-线性表
数据结构-线性表 线性表的任意元素存放地址:Ai a1 L *(i-1) 当i0 则 Aia0L*i 少了一次计算 按照计算方法,当下标为0,可以少执行一次减法,这也是c数组下标取0的原因。 无论n多大都是一个固定时间称之为O(1) 时间复杂度 顺序表的运…...
Python酷库之旅-第三方库Pandas(245)
目录 一、用法精讲 1156、pandas.tseries.offsets.MonthEnd.is_month_start方法 1156-1、语法 1156-2、参数 1156-3、功能 1156-4、返回值 1156-5、说明 1156-6、用法 1156-6-1、数据准备 1156-6-2、代码示例 1156-6-3、结果输出 1157、pandas.tseries.offsets.Mon…...
贵阳思普信息技术有限公司 OA系统 apilogin 接口存在SQL注入漏洞风险
免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...
重学 Android 自定义 View 系列(九):侧边字母选择器
前言 本文来实现一个侧边字母选择器,很常见的一个控件,和上篇文章星星评分用到的关键技术点类似,难度不大,本篇再来温故知新一下。 最终效果如下: 1. 效果分析 每个字母被均匀分布在整个控件区域中;触摸…...
网络原理->DNS协议和NAT协议解
前言 大家好我是小帅,今天我们来了解应用层的DNS协议和NAT技术 个人主页:再无B~U~G 文章目录 1.重要应⽤层协议DNS(Domain Name System)1.1 DNS背景 2. NAT技术3. 总结 1.重要应⽤层协议DNS(Domain Name System) DNS是⼀整套从域…...
亚马逊开发视频人工智能模型,The Information 报道
根据《The Information》周三的报道,电子商务巨头亚马逊(AMZN)已开发出一种新的生成式人工智能(AI),不仅能处理文本,还能处理图片和视频,从而减少对人工智能初创公司Anthropic的依赖…...
【JS】React与Vue的异步编程对比:深度解析与实战案例全面指南
文章目录 前言更多实用工具React与Vue概述ReactVue 异步编程基础回调函数PromiseAsync/Await React中的异步编程使用Axios进行数据请求异步操作与组件生命周期React Hooks中的异步处理 Vue中的异步编程使用Axios进行数据请求异步操作与Vue生命周期Vue Composition API中的异步处…...
CTF-WEB: 2024强网杯青少年专项赛 ezFindShell writeup
打开直接下载www.zip 通过百度网盘分享的文件:ezFindShell.zip 链接:https://pan.baidu.com/s/1JQjOk-qxaOf0s4f3Fgww7w?pwd1111 提取码:1111 --来自百度网盘超级会员V2的分享使用阿里webshell进行检测,找到可利用文件,或者直接全全局搜索…...
docker网络配置
文章目录 前言一、docker网络访问原理二、docker配置多台机器可以相互访问三、高级网络配置四、最佳实践总结前言 在当今的软件开发和运维领域,Docker 已经成为了容器化服务的标准之一。它不仅简化了应用的部署过程,还大大提高了资源利用率。然而,随着Docker应用的深入,网…...
AI生成的一个.netcore 经典后端架构
下面是一个完整的 .NET Core 后端项目示例,使用 Dapper 作为轻量级 ORM 访问 Oracle 数据库,并实现高性能架构。我们将实现学生表、课程表、成绩表和班级表的基本增删改查功能,以及查询某个班级学生成绩的功能,并使用自定义缓存来…...
docker学习的初识
一、docker官方安装地址: 根据官网找对应的环境,相关的安装命令自行官网不在累赘; 查看Docker的版本号:docker -v Docker version 27.3.1, build ce12230查看相应的镜像docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker/wel…...
【Git】Git 完全指南:从入门到精通
Git 完全指南:从入门到精通 Git 是现代软件开发中最重要的版本控制工具之一,它帮助开发者高效地管理项目,支持分布式协作和版本控制。无论是个人项目还是团队开发,Git 都能提供强大的功能来跟踪、管理代码变更,并保障…...
webrtc ios h264 硬编解码
webrtc ios h264 硬编解码 一 ios 系统支持 从ios8开始,苹果公司开放了硬解码和硬编码API(即 VideoToolbox.framework API) 二 主要api 1 主要解码函数 VTDecompressionSessionCreate // 创建解码 session VTDecompressionSession…...
ubuntu20.04更换安装高版本CUDA以及多个CUDA版本管理
Ubuntu 20.04下多版本CUDA的安装与切换 CUDA安装配置环境变量软连接附上参考博客CUDA安装 cuda官方下载地址 因为我需要安装的是11.1版本的,所以这里按着11.1举例安装 安装命令如下: wget https://developer.download.nvidia.com/compute/cuda/11.1.0/local_installers/cu…...
生鲜食品o2o商城系统|Java|SSM|VUE| 前后端分离
【重要1⃣️】前后端源码万字文档部署文档 【重要2⃣️】正版源码有问题包售后 【重要3⃣️】可复制品不支持退换货 【包含内容】 【一】项目提供非常完整的源码注释 【二】相关技术栈文档 【三】源码讲解视频 【其它服务】 【一】可…...
C 语言静态库与动态库的生成和使用
在 YouTube 上找到一个视频 动态链接库静态链接库的生成和使用,它把用 GCC 生成静态库和动态库,以及如何使用他们说的很明白,有条件的可以直接看那个视频。本文就是一个观后的实操和笔记,加添了更多如何查看动态库,静态…...
分布式锁的实现方案有哪些?各自的原理是怎样的?使用场景有哪些?与单体架构中锁区别?存在哪些问题?如何解决?注意事项?
一、分布式锁的实现方案 分布式锁的实现方案主要包括以下几种: 基于数据库的分布式锁: 利用数据库的事务特性来实现锁功能。在数据库中创建一个具有唯一约束的锁表,加锁时插入一行记录,释放锁时删除这行记录。简单易用…...
企业如何落地搭建商业智能BI系统
随着新一代信息化、数字化技术的应用,引发了新一轮的科技革命,现代化社会和数字化的联系越来越紧密,数据也变成继土地、劳动力、资本、技术之后的第五大生产要素,这一切都表明世界已经找准未来方向,前沿科技也与落地并…...
python学习——元组的创建于删除
在 Python 中,元组(tuple)是一种内置的数据类型,用于存储不可变的有序元素集合。以下是关于 Python 元组的一些关键点: 文章目录 定义元组1. 使用圆括号 ()2. 使用 tuple() 函数3. 使用单个元素的元组4. 不使用圆括号…...
智能化图书馆导航系统方案之系统架构与核心功能设计
hello~这里是维小帮,点击文章最下方获取图书馆导航系统解决方案!如有项目需求和技术交流欢迎大家私聊我们~撒花! 针对传统图书馆在图书查找困难、座位紧张、空间导航不便方面的问题,本文深入剖析了基于高精度定位、3D建模、图书搜…...
Linux网络——IO模型和多路转接
通常所谓的IO,其本质就是等待通信和进行通信,即IO 等 拷贝。 那么想要做到高效的IO,就要在单位时间内,减少“等”的比重。 一.五种IO模型 阻塞 IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方…...
浅谈网络 | 应用层之HTTP协议
目录 HTTP 请求的准备HTTP 请求的构建HTTP 请求的发送过程HTTP 返回的构建HTTP 2.0QUIC 协议HTTP 3.0 在讲完传输层之后,我们接下来进入应用层的内容。应用层的协议种类繁多,那从哪里开始讲起呢?不妨从我们最常用、最熟悉的 HTTP 协议 开始。…...
【CSS】页面滚动到一定位置时,指定区域固定不变
一. 需求 移动端:下滑时,当下滑到一定位置时,指定区域不跟随下滑-【固定跟随区域】一直在顶部效果 (1)未滚动前 (2)滚动后 二. 实现 <template><div class"global-application…...
瀚高创库建表pgsql
1.瀚高下载地址: 下载 (highgo.com)https://www.highgo.com/down_main.html 2.瀚高linux安装 上传deb文件到ubuntu系统中 执行 dpkg -i hgdb-see-4.5.8-fe4791c.x86_64.deb 命令安装数据库 安装完成后,会在/opt 目录下生成安装目录 数据库安装完毕后…...
Vim 高级操作与技巧指南
在上一篇文章中,我们了解了 Vim 的基本操作和模式,掌握了如何进行文件编辑、光标移动、文本操作等基本技能。现在,我们将深入探讨 Vim 的一些高级功能,包括插件管理、脚本编写、定制快捷键等内容,以进一步提高你的工作…...
Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
Vue 3.0 之所以使用 Proxy API 替代 Object.defineProperty,主要是为了提升性能、减少代码复杂性,并解决 Vue 2.x 在响应式处理中的一些局限性。下面我们通过对比这两种方式的工作原理、优缺点,并结合实际项目代码示例来详细讲解。 1. Object.defineProperty 的局限性 在 …...
【基于规则】n-sigma
在正态分布中: 约68%的数据点会落在均值1σ的范围内约95%的数据点会落在均值2σ的范围内约99.7%的数据点会落在均值3σ的范围内这称为68-95-99.7法则(Empirical Rule)。 假设我们有一组数据,其均值为μ,标准差为σ。某个数据点x的n-sigma计算公式如下: 若z=1.2,说明该…...
JavaScript 判断字符串是否包含子字符串的几种方法
这里写目录标题 方法 1: 使用 includes()方法 2: 使用 indexOf()方法 3: 使用正则表达式方法 4: 使用 search()方法 5: 用 startsWith() 或 endsWith()推荐使用 JavaScript 判断字符串是否包含子字符串,不要只知道 indexOf() ,还可以尝试一下其他写法。 …...
goframe框架bug-记录
implement not found for interface ICompany, forgot register? 错误解决检查: 1.有没有init 2. 注入问题 3. 注入问题...
Docker:在 ubuntu 系统上生成和加载 Docker 镜像
本文将介绍在 ubuntu系统上进行 Docker 镜像的生成和加载方法和代码。 文章目录 一、下载和安装 docker二、加载 docker 文件三、保存你的镜像四、将镜像上传到云端并通过连接下载和加载 Docker 镜像五、Docker 容器和本地的文件交互5.1 从容器复制文件到本地宿主机5.1.1 单个文…...
长时间无事可做是个危险信号
小马加入的是技术开发部,专注于Java开发。团队里有一位姓隋的女同事,是唯一的web前端工程师,负责页面开发工作,比小马早两个月入职。公司的项目多以定制化OA系统为主,后端任务繁重,前端工作相对较少。在这样…...
【小白学机器学习39】如何用numpy生成总体,生成样本samples
目录 1 目的:研究 样本和总体之间的关系 2 先生成1个理论总体 2.0 下面是关于这一步的完整代码 2.1 一般情况下,我们先生成一个符合正态分布的总体 2.1.1 设置总体 ,或者说生成一个总体 2.2 为什么一定要是一个符合正态分布的总体&…...
redis的主从复制
redis主从复制 一、主从复制概念二、主从模式运行原理2.1主从复制的演示:2.2查看主从结构信息:2.3AOF文件对主从关系的影响2.4主从节点建立复制流程图 三、主从复制的拓扑结构3.1 一主一从结构3.2 一主多从结构3.3 树形主从结构 四、数据同步psync4.1全量…...
数据结构与算法(排序算法)
排序的概念 1. 排序是指将一组数据,按照特定的顺序进行排列的过程。 2. 这个过程通常是为了使数据更加有序,从而更容易进行搜索、比较或其他操作。 常见的排序算法 插入排序 1. 把待排序的记录,按其关键码值的大小,逐个插入到一…...
极狐GitLab 17.6 正式发布几十项与 DevSecOps 相关的功能【三】
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料: 极狐GitLab 官网极狐…...