Spring Cloud Open Feign 超时配置及源码分析

news2024/9/24 13:21:42

前言:

在开发 Spring Cloud 微服务项目时候,Feign 调用是非常常见的,Feign 调用的底层还是 HTTP 的远程调用,会有超时问题,如果没有搞清楚超时问题,生产环境的调用肯那个会有种种问题出现,本篇我们来分享一下 Feign 调用的超时配置。

连接超时和读取超时配置

  • ConnectTimeout(连接超时):Feign 是基于 HTTP 的远程调用,众所周知,HTTP 调用会进行 TCP 的三次握手,连接超时时间,就是多少秒没连接上,就会抛出超时异常,Feign 连接超时默认 10 秒。
  • ReadTimeout(读取超时):HTTP 成功连接后,客户端发会送请求报文,服务端收到后解析并返回响应报文,在写出响应报文时,如果超过了设置的时间还没写完,就会抛出读取超时异常,在某些接口请求数据量大的时候,就很容易出现读取超时,Feign 读取超时默认 60 秒。

超时演示

我们在被 Feign 调用的接口中让线程 sleep 61 秒,调用者服务就抛出超时异常了。

在这里插入图片描述

Feign 默认超时时间源码

通过源码 debugger 我们可以知道 Feign 的默认读取超时时间是 60 秒,默认连接超时时间是 10 秒。

在这里插入图片描述

在这里插入代码片

自定义 Feign 超时时间

Feign 支持在 ribbon 或者 feign 配置项下配置超时时间,feign 下配置优先级最高,但是新版 Spring Cloud 已经移除了 ribbon,因此建议配置在 feign中,如下配置:

#默认连接超时时间
feign.client.config.default.connect-timeout=5000
#默认读取超时时间
feign.client.config.default.read-timeout=3000

debugger 如下图,Feign 超时配置已经生效,default 表示作用于所有客户端,也可替换 default 为具体的客户端名称,表示作用于单个客户端,可以给每个客户端配置不同的超时时间。

在这里插入图片描述

给指定的服务端配置超时时间如下:

#默认连接超时时间
feign.client.config.order-service.connect-timeout=4000
#默认读取超时时间
feign.client.config.order-service.read-timeout=4000

debugger 验证如下:

在这里插入图片描述

Feign 源码分析

Feign 参数的初始化

Feign 通过接口生成代理对象,扫描到 Feign 接口构建代理对象,在 Feign#builder 创建构建者时,Feign 客户端相关的参数都是在这个时候初始化的,超时时间也是在这个时候初始化的,Feign#builder 会创建一个 Options 对象,源码如下:

//feign.Feign.Builder#Builder
public Builder() {
	//日志级别
	this.logLevel = Level.NONE;
	this.contract = new Default();
	//客户端
	this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
	//重试器
	this.retryer = new feign.Retryer.Default();
	//日志
	this.logger = new NoOpLogger();
	//编码器
	this.encoder = new feign.codec.Encoder.Default();
	//解码器
	this.decoder = new feign.codec.Decoder.Default();
	this.queryMapEncoder = new FieldQueryMapEncoder();
	//错误解码器
	this.errorDecoder = new feign.codec.ErrorDecoder.Default();
	//超时配置
	this.options = new Options();
	//处理器工厂
	this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
	this.closeAfterDecode = true;
	//传播策略
	this.propagationPolicy = ExceptionPropagationPolicy.NONE;
	//是否强制解码
	this.forceDecoding = false;
	this.capabilities = new ArrayList();
}

前面我们说 Feign 的默认连接超时时间是 10 秒,默认读取超时时间是 60 秒,我们来从源码证明,Options 构造方法源码如下:

//feign.Request.Options#Options()
public Options() {
	this(10L, TimeUnit.SECONDS, 60L, TimeUnit.SECONDS, true);
}

//feign.Request.Options#Options(long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, boolean)
public Options(long connectTimeout, TimeUnit connectTimeoutUnit, long readTimeout, TimeUnit readTimeoutUnit, boolean followRedirects) {
	this.connectTimeout = connectTimeout;
	this.connectTimeoutUnit = connectTimeoutUnit;
	this.readTimeout = readTimeout;
	this.readTimeoutUnit = readTimeoutUnit;
	this.followRedirects = followRedirects;
}

从 Options 构造方法源码中可以证明 Feign 的默认超时时间。

SentinelFeign.Builder#build 方法源码分析

SentinelFeign.Builder#build 方法主要逻辑如下:

  • 获取应用程序上下文。
  • 通过应用程序上下文获取到 target 对象的 BeanDefinition 。
  • 通过 BeanDefinition 获取到 FeignClientFactoryBean 。
  • 获取 FallBack 类和工厂。
  • 创建代理对象的处理器 SentinelInvocationHandler(项目中引入了 Sentinel)。
//com.alibaba.cloud.sentinel.feign.SentinelFeign.Builder#build
public Feign build() {
	super.invocationHandlerFactory(new InvocationHandlerFactory() {
		public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
			//target 就是我们定义的 feign 接口 例如 OrderFeign
			//dispatch 就是我们的接口方法  例如  com.user.service.feign.OrderFeign.queryOrder()
			//通用应用程序上下文
			GenericApplicationContext gctx = (GenericApplicationContext)Builder.this.applicationContext;
			//target 对象的 beanDefinition 对象  
			BeanDefinition def = gctx.getBeanDefinition(target.type().getName());
			//FeignClient bean 工厂
			FeignClientFactoryBean feignClientFactoryBean = (FeignClientFactoryBean)def.getAttribute("feignClientsRegistrarFactoryBean");
			//Feign 接口中配置的的 fallback 
			Class fallback = feignClientFactoryBean.getFallback();
			//fallback 工厂
			Class fallbackFactory = feignClientFactoryBean.getFallbackFactory();
			//目标服务id  order-service
			String beanName = feignClientFactoryBean.getContextId();
			if (!StringUtils.hasText(beanName)) {
				beanName = feignClientFactoryBean.getName();
			}
			//创建 代理对象处理器 SentinelInvocationHandler
			if (Void.TYPE != fallback) {
				//获取 fallback 实例
				Object fallbackInstance = this.getFromContext(beanName, "fallback", fallback, target.type());
				return new SentinelInvocationHandler(target, dispatch, new org.springframework.cloud.openfeign.FallbackFactory.Default(fallbackInstance));
			} else if (Void.TYPE != fallbackFactory) {
				FallbackFactory fallbackFactoryInstance = (FallbackFactory)this.getFromContext(beanName, "fallbackFactory", fallbackFactory, FallbackFactory.class);
				return new SentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);
			} else {
				return new SentinelInvocationHandler(target, dispatch);
			}
		}
		
		//fallback 的处理
		private Object getFromContext(String name, String type, Class fallbackType, Class targetType) {
			Object fallbackInstance = Builder.this.feignContext.getInstance(name, fallbackType);
			if (fallbackInstance == null) {
				throw new IllegalStateException(String.format("No %s instance of type %s found for feign client %s", type, fallbackType, name));
			} else if (!targetType.isAssignableFrom(fallbackType)) {
				throw new IllegalStateException(String.format("Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s", type, fallbackType, targetType, name));
			} else {
				return fallbackInstance;
			}
		}
	});
	super.contract(new SentinelContractHolder(this.contract));
	return super.build();
}


源码 Debugger 如下:

在这里插入图片描述

fallback 就是我们接口中配置的 @FeignClient(value = “order-service”,fallback = OrderFeignFallback.class) ,源码 Debugger 如下:

在这里插入图片描述

SentinelInvocationHandler#invoke 方法源码分析

我们在分析 Feign 初始化的过程最后创建一个 SentinelInvocationHandler,Feign 的实现是依赖代理的,因此真正执行 Feign 请求的是 SentinelInvocationHandler#invoke 方法,SentinelInvocationHandler#invoke 主要逻辑如下:

  • 首先对方法名称进行判断,看是否是 equals、hashCode、toString 这些类型的方法。
  • 对代理类对象型进行判断,看代理对象是否是 HardCodedTarget 类型的,是否是 HardCodedTarget 类型的代理对象的区别是否执行 Sentinel 的资源监控,最终都会调用 SynchronousMethodHandler#invoke 方法。
//com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler#invoke
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
	//proxy 代理对象 就是我们 Feign 接口的代理对象
	//method 就是我们的 Feign 接口的方法
	//args Feign 接口参数
	//是否是 equals 方法
	if ("equals".equals(method.getName())) {
		try {
			Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
			return this.equals(otherHandler);
		} catch (IllegalArgumentException var21) {
			return false;
		}
	//是否是 hashCode 方法
	} else if ("hashCode".equals(method.getName())) {
		return this.hashCode();
	// 是否是 toString 方法
	} else if ("toString".equals(method.getName())) {
		return this.toString();
	} else {
		//都不是 一般我们定义的 feign 接口方法都会走到这里
		//MethodHandler 方法处理器
		MethodHandler methodHandler = (MethodHandler)this.dispatch.get(method);
		Object result;
		//target 类型判断
		if (!(this.target instanceof HardCodedTarget)) {
			//调用方法
			result = methodHandler.invoke(args);
		} else {
			//硬编码target
			HardCodedTarget hardCodedTarget = (HardCodedTarget)this.target;
			//方法元数据
			MethodMetadata methodMetadata = (MethodMetadata)SentinelContractHolder.METADATA_MAP.get(hardCodedTarget.type().getName() + Feign.configKey(hardCodedTarget.type(), method));
			//方法元数据为空判断
			if (methodMetadata == null) {
				result = methodHandler.invoke(args);
			} else {
				//资源名称 GET:http://order-service/query-order  sentinel 资源监控使用
				String resourceName = methodMetadata.template().method().toUpperCase() + ":" + hardCodedTarget.url() + methodMetadata.template().path();
				Entry entry = null;

				Object var12;
				try {
					Throwable ex;
					try {
						//进入 sentinel 的逻辑
						ContextUtil.enter(resourceName);
						entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
						//调用方法
						result = methodHandler.invoke(args);
						return result;
					} catch (Throwable var22) {
						ex = var22;
						if (!BlockException.isBlockException(var22)) {
							Tracer.trace(var22);
						}
					}

					if (this.fallbackFactory == null) {
						throw var22;
					}

					try {
						//fallback 逻辑
						Object fallbackResult = ((Method)this.fallbackMethodMap.get(method)).invoke(this.fallbackFactory.create(ex), args);
						var12 = fallbackResult;
					} catch (IllegalAccessException var19) {
						throw new AssertionError(var19);
					} catch (InvocationTargetException var20) {
						throw new AssertionError(var20.getCause());
					}
				} finally {
					if (entry != null) {
						entry.exit(1, args);
					}

					ContextUtil.exit();
				}

				return var12;
			}
		}

		return result;
	}
}

SynchronousMethodHandler#invoke 方法源码分析

SynchronousMethodHandler#invoke 方法的逻辑十分简单,构建请求的 RequestTemplate 对象,包括封装参数、路径 、请求方式等信息,超时时间对象、重试对象的获取,然后执行 SynchronousMethodHandler#executeAndDecode 方法,或者执行重试。

//feign.SynchronousMethodHandler#invoke
public Object invoke(Object[] argv) throws Throwable {
	//构建方法请求的 RequestTemplate 封装参数 路径 请求方式等信息
	RequestTemplate template = this.buildTemplateFromArgs.create(argv);
	//feign 超时时间
	Options options = this.findOptions(argv);
	//重试配置
	Retryer retryer = this.retryer.clone();

	while(true) {
		try {
			//执行请求
			return this.executeAndDecode(template, options);
		} catch (RetryableException var9) {
			RetryableException e = var9;

			try {
				//重试
				retryer.continueOrPropagate(e);
			} catch (RetryableException var8) {
				Throwable cause = var8.getCause();
				if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
					throw cause;
				}

				throw var8;
			}

			if (this.logLevel != Level.NONE) {
				this.logger.logRetry(this.metadata.configKey(), this.logLevel);
			}
		}
	}
}

SynchronousMethodHandler#executeAndDecode 方法源码分析

SynchronousMethodHandler#executeAndDecode 方法的主要作用是从 RequestTemplate 获取 Request 对象,调用 FeignBlockingLoadBalancerClient#execute 方法发起请求调用。

//feign.SynchronousMethodHandler#executeAndDecode
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
	//从 RequestTemplate 中获取 request 信息
	Request request = this.targetRequest(template);
	if (this.logLevel != Level.NONE) {
		this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
	}

	long start = System.nanoTime();
	//response
	Response response;
	try {
		//执行请求 
		response = this.client.execute(request, options);
		//获取响应
		response = response.toBuilder().request(request).requestTemplate(template).build();
	} catch (IOException var13) {
		if (this.logLevel != Level.NONE) {
			this.logger.logIOException(this.metadata.configKey(), this.logLevel, var13, this.elapsedTime(start));
		}

		throw FeignException.errorExecuting(request, var13);
	}
	//请求耗时
	long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
	//解码器
	if (this.decoder != null) {
		//解码
		return this.decoder.decode(response, this.metadata.returnType());
	} else {
		//解码器为空的逻辑
		CompletableFuture<Object> resultFuture = new CompletableFuture();
		this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);

		try {
			if (!resultFuture.isDone()) {
				throw new IllegalStateException("Response handling not done");
			} else {
				return resultFuture.join();
			}
		} catch (CompletionException var12) {
			Throwable cause = var12.getCause();
			if (cause != null) {
				throw cause;
			} else {
				throw var12;
			}
		}
	}
}

FeignBlockingLoadBalancerClient#execute 方法源码分析

FeignBlockingLoadBalancerClient#execute 方法是核心方法,其中包含负载均衡的逻辑,具体逻辑如下:

  • 获取原始请求地址和服务实例id。
  • 灰度处理。
  • 使用负载均衡获取服务实例(在 LoadBalancer 篇章中有分析过)。
  • 服务实例为空判断,服务实例为空返回服务不可用,否则调用 LoadBalancerUtils#executeWithLoadBalancerLifecycleProcessing 方法发起 HTTP 调用。
//org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient#execute
public Response execute(Request request, Options options) throws IOException {
	//原始请求地址 例如:http://order-service/query-order
	URI originalUri = URI.create(request.url());
	//服务实例id order-service
	String serviceId = originalUri.getHost();
	Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
	//灰度处理
	String hint = this.getHint(serviceId);
	//lbRequest
	DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest(new RequestDataContext(LoadBalancerUtils.buildRequestData(request), hint));
	//生命周期处理器
	Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(this.loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class), RequestDataContext.class, ResponseData.class, ServiceInstance.class);
	supportedLifecycleProcessors.forEach((lifecycle) -> {
		lifecycle.onStart(lbRequest);
	});
	//使用负载均衡获取服务实例
	ServiceInstance instance = this.loadBalancerClient.choose(serviceId, lbRequest);
	org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(instance);
	String message;
	//服务实例为空判断
	if (instance == null) {
		//服务实例为空 
		message = "Load balancer does not contain an instance for the service " + serviceId;
		if (LOG.isWarnEnabled()) {
			LOG.warn(message);
		}

		supportedLifecycleProcessors.forEach((lifecycle) -> {
			lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, lbResponse));
		});
		return Response.builder().request(request).status(HttpStatus.SERVICE_UNAVAILABLE.value()).body(message, StandardCharsets.UTF_8).build();
	} else {
		//服务实例不为空
		message = this.loadBalancerClient.reconstructURI(instance, originalUri).toString();
		//创建新的 request 目标服务的请求地址  例如: http://192.168.123.132:8086/query-order
		Request newRequest = this.buildRequest(request, message);
		return LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(this.delegate, options, newRequest, lbRequest, lbResponse, supportedLifecycleProcessors);
	}
}

LoadBalancerUtils#executeWithLoadBalancerLifecycleProcessing 方法源码分析

LoadBalancerUtils#executeWithLoadBalancerLifecycleProcessing 方法就是使用 feignClient 对象发起 HTTP 请求调用了,再往下就是HTTP 调用的逻辑了。

//org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils#executeWithLoadBalancerLifecycleProcessing(feign.Client, feign.Request.Options, feign.Request, org.springframework.cloud.client.loadbalancer.Request, org.springframework.cloud.client.loadbalancer.Response<org.springframework.cloud.client.ServiceInstance>, java.util.Set<org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle>, boolean)
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Options options, Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest, org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse, Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced) throws IOException {
	supportedLifecycleProcessors.forEach((lifecycle) -> {
		lifecycle.onStartRequest(lbRequest, lbResponse);
	});

	try {
		//使用 feignClient 发出请求 往下就是 http 底层调用逻辑了
		Response response = feignClient.execute(feignRequest, options);
		if (loadBalanced) {
			supportedLifecycleProcessors.forEach((lifecycle) -> {
				lifecycle.onComplete(new CompletionContext(Status.SUCCESS, lbRequest, lbResponse, buildResponseData(response)));
			});
		}

		return response;
	} catch (Exception var8) {
		if (loadBalanced) {
			supportedLifecycleProcessors.forEach((lifecycle) -> {
				lifecycle.onComplete(new CompletionContext(Status.FAILED, var8, lbRequest, lbResponse));
			});
		}

		throw var8;
	}
}

如有不正确的地方请各位指出纠正。

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

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

相关文章

pymysql cursor使用教程

Python之PyMySQL的使用&#xff1a; 在python3.x中&#xff0c;可以使用pymysql来MySQL数据库的连接&#xff0c;并实现数据库的各种操作&#xff0c;本次博客主要介绍了pymysql的安装和使用方法。 PyMySQL的安装 一、.windows上的安装方法&#xff1a; 在python3.6中&…

图像字幕Image Captioning——使用语法和语义正确的语言描述图像

1. 什么是图像字幕 Image Captioning&#xff08;图像字幕生成&#xff09; 是计算机视觉和自然语言处理&#xff08;NLP&#xff09;领域的一个交叉研究任务&#xff0c;其目标是自动生成能够描述给定图像内容的自然语言句子。这项任务要求系统不仅要理解图像中的视觉内容&…

NLP从零开始------文本中阶序列处理之语言模型(完整版)

语言模型( language model) 用于计算一个文字序列的概率&#xff0c; 评估该序列作为一段文本出现在通用或者特定场景中的可能性。每个人的语言能力蕴涵了一个语言模型&#xff0c;当我们说出或写下一段话的时候&#xff0c;已经在不自觉地应用语言模型来帮助我们决定这段话中的…

ceph-rgw zipper的设计理念(2)

本文简介 书接上文。本文以CreateBucket为例进行详细讲述设计理念以及接口变化趋势。 1、接收请求和协议处理请求 rgw_asio_frontend.cc 主要功能&#xff1a;回调函数注册和请求处理 void handle_connection(boost::asio::io_context& context,RGWProcessEnv& env…

如何使用IDEA搭建Mybatis框架环境(详细教程)

文章目录 ☕前言为什么学习框架技术Mybatis框架简介 &#x1f379;一、如何配置Mybatis框架环境1.1下载需要MyBatis的jar文件1.2部署jar文件1.3创建MyBatis核心配置文件configuration.xml1.4.创建持久类(POJO)和SQL映射文件1.5.创建测试类 &#x1f9cb;二、 MyBatis框架的优缺…

GAN Inversion(GAN 反演)

什么是Inversion&#xff1f; 来龙去脉&#xff1a; 在生成过程中&#xff0c;我们通过将z输入G&#xff0c;然后得到图像&#xff0c;但是你这个Z是不定的&#xff08;随机的高斯分布噪声&#xff09;&#xff0c;所以即使你得到了质量好的生成图像&#xff0c;但是依然无法…

页面间对象传递的几种方法

页面间对象传递的几种方法 1. 使用request对象传递2. 使用session对象传递3. 使用application对象传递4. 使用cookie传递 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Web开发中&#xff0c;页面间的数据传递是一个常见的需求。本文将…

Java | Leetcode Java题解之第381题O(1)时间插入、删除和获取随机元素-允许重复

题目&#xff1a; 题解&#xff1a; class RandomizedCollection {Map<Integer, Set<Integer>> idx;List<Integer> nums;/** Initialize your data structure here. */public RandomizedCollection() {idx new HashMap<Integer, Set<Integer>>…

搜索引擎通过分析网页标题中的关键词来判断内容的相关性

在网站链接上的标题&#xff0c;‌写关键词对SEO更适合&#xff0c;‌这一观点是基于多个方面的考虑。‌以下是对这一观点的详细讲解&#xff1a;‌ 关键词优化与SEO相关性 首先&#xff0c;‌搜索引擎的工作原理是通过抓取和分析网页内容来确定其在搜索结果中的排名。‌在这个…

显示中文字体问题解决:ImportError: The _imagingft C module is not installed

使用opencv写入中文时&#xff0c;用以下代码会导致乱码&#xff1a; cv2.putText(im0, f"{label}:{score}", (xmin, ymin), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 3)因此需要借助PIL库写入中文字符&#xff0c;用法如下&#xff1a; import cv2 from PIL impo…

Java 输入与输出之 NIO【非阻塞式IO】【NIO网络编程】探索之【二】

上一篇博客我们介绍了NIO的核心原理、FileChannel和Buffer, 对Buffer的用法有了清晰的了解。上篇博客&#xff1a; Java 输入与输出之 NIO【非阻塞式IO】【NIO核心原理】探索之【一】 本篇博客我们将继续来探索NIO&#xff0c;介绍如何使用SocketChannel和ServerSocketChannel来…

完全自由的栏目设计

亮点功能&#xff1a; 可以将任一栏目拖动到其它栏目下 被拖动的栏目其包含的子栏目和文章将一起拖过去。 快来试试吧&#xff01;

原来这么多行业都可以转行大模型,大模型从入门到精通,非常详细收藏我这一篇就够了

转行到大模型&#xff08;Large Model&#xff09;领域已经成为当前科技发展的一大趋势。所谓“大模型”&#xff0c;通常指的是那些包含数亿甚至数十亿参数的深度学习模型&#xff0c;例如自然语言处理中的GPT系列、BERT等模型&#xff0c;以及计算机视觉领域的EfficientNet、…

[Pyplot]设置图中字体为TimesNewRoman

一、简介 本文介绍了如何在linux环境下在python中使用matplotlib.pyplot 绘制图表时&#xff0c;令其中的文字字体为Times New Roman。 二、设置步骤 1. Linux下安装Times New Roman字体 $ sudo apt install ttf-mscorefonts-installer # 安装字体 $ sudo fc-cache # 使新安…

Python与Biome-BGC:生态模型分析的未来趋势

近年来&#xff0c;Python编程语言受到越来越多科研人员的喜爱&#xff0c;在多个编程语言排行榜中持续夺冠。同时&#xff0c;伴随着深度学习的快速发展&#xff0c;人工智能技术在各个领域中的应用越来越广泛。机器学习是人工智能的基础&#xff0c;因此&#xff0c;掌握常用…

扩博智能× Milvus:图像检索助力零售商品图像高效标注

大家好&#xff0c;我是上海扩博智能技术有限公司的Frank&#xff0c;负责算法工程相关的工作。很高兴能在 Milvus 社区和大家分享我们在图像检索方面的经验。 01 扩博智能公司简介 扩博智能 Clobotics 成立于 2016 年&#xff0c;总部位于上海长宁。我们聚焦计算机视觉和机器学…

SOMEIP_ETS_071: Union_Length_too_long

测试目的&#xff1a; 验证当设备&#xff08;DUT&#xff09;接收到一个联合&#xff08;union&#xff09;长度超出实际联合长度的SOME/IP消息时&#xff0c;是否能够返回错误消息。 描述 本测试用例旨在检查DUT在处理一个echoUNION方法的SOME/IP消息时&#xff0c;如果消…

基于DashScope+Streamlit构建你的机器学习助手(入门级)

前言 在LLM&#xff08;大语言模型&#xff09;盛行的今天&#xff0c;博主越来越感觉到AI&#xff08;人工智能&#xff09;的潜力被“无限”激发了。它为什么会突然间完成“鱼跃龙门”呢&#xff1f; 博主认为基础设施&#xff08;也可以称为算力&#xff09;的完善和“天才…

Java-异常处理try catch finally throw和throws

在 Java 中,异常处理机制是通过 try, catch, finally, throw和 throws 这几个关键字来实现的。以下 是这些关键字的基本用途和它们之间的区别: public class ExceptionHandlingExample {public static void main(String[] args) {try {processSomething();} catch (Exceptio…

【视频讲解】SMOTEBoost、RBBoost和RUSBoost不平衡数据集的集成分类酵母数据集、治癌候选药物|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p37502 分析师&#xff1a;Zilin Wu 在当今的大数据时代&#xff0c;科研和实际应用中常常面临着海量数据的处理挑战。在本项目中&#xff0c;我们拥有上万条数据&#xff0c;这既是宝贵的资源&#xff0c;也带来了诸多难题。一方面&a…