知其然

SpringCloud + Zookeeper

Spring Cloud 与 Zookeeper的整合只需要添加相关的starter依赖和增加相关注解即可完成。

pom.xml 如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>FeignDemo</artifactId><groupId>com.hui</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>feignHello-service</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zookeeper-discovery</artifactId><!-- 需要特别主题spring cloud 和zookeeper 的版本笔者本地使用了3.4.11的zk,因此在此处排除了默认的zk,单独引入--><exclusions><exclusion><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.11</version><exclusions><exclusion><groupId>log4j</groupId><artifactId>log4j</artifactId></exclusion><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--笔者将api与service分为了独立的module,所以这里加入引用api--><dependency><groupId>com.hui</groupId><artifactId>feignHello-api</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

bootstrap.yml如下:

server:port: 8000
spring:application:name: feignHelloServicecloud:zookeeper:connect-string: 192.168.4.192:2181 #zk地址

最后开启服务的注册与发现

@SpringBootApplication
@EnableDiscoveryClient
public class HelloServiceApplication {public static void main(String[] args) {SpringApplication.run(HelloServiceApplication.class, args);}
}

service 和controller实现

@Service
public class HelloService{public HelloResponse sayHello(HelloRequest request) {return new HelloResponse(request.toString());}
}
@Api(value = "Hello 服务", tags = "Hello 服务")
@RestController
public class HelloController implements IHelloServiceClient {@AutowiredHelloService helloService;@Override@ApiOperation(value = "say hello 带默认参数")@GetMapping("/hello")//http://localhost:8000/hello?name=qqqq&age=22public String sayHello(@RequestParam(name = "name", defaultValue = "tony") String name,@RequestParam(name = "age", defaultValue = "18") int age) {HelloRequest request = new HelloRequest(name, age);return helloService.sayHello(request).toString();}@Override@GetMapping("/hello1")@PostMapping(value = "say hello 不带参数")public String sayHello() {HelloRequest request = new HelloRequest("tony", 19);return helloService.sayHello(request).toString();}@Override@PostMapping("/hello2")@ApiOperation(value = "say hello 带请求体")public HelloResponse sayHello(@RequestBody HelloRequest request) {return helloService.sayHello(request);}
}

笔者加入了swagger,如果需要只需加入如下依赖和配置:

    <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.7</version></dependency>
@Configuration
@EnableSwagger2WebMvc
public class SwaggerAutoConfiguration {@Beanpublic Docket defaultApi(){Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(new ApiInfoBuilder().title("helloService").description("RpcDemo").version("1.0").build()).select().apis(RequestHandlerSelectors.withClassAnnotation(Api.class)).paths(PathSelectors.any()).build();return docket;}}

至此,spring cloud与zookeeper的整合就完成了,调用结果如下:


helloService.jpg

SpringCloud + Zookeeper + Feign

为了测试与Feign的整合,再构建一个消费者:与上述构建的过程类似。

pom.xml 增加spring-cloud-starter-openfeign依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>FeignDemo</artifactId><groupId>com.hui</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>feignHello-test</artifactId><dependencies><dependency><groupId>com.hui</groupId><artifactId>feignHello-api</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zookeeper-discovery</artifactId><exclusions><exclusion><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.11</version><exclusions><exclusion><groupId>log4j</groupId><artifactId>log4j</artifactId></exclusion><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.4.RELEASE</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

bootstrap.yaml:

server:port: 8001
spring:application:name: feignHelloTestcloud:zookeeper:connect-string: 192.168.4.192:2181main:allow-bean-definition-overriding: true #笔者定义了两个相同FeignClient,所以bean相同

开启服务注册与发现,@EnableFeignClients注解注册FeignClient

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class HelloServiceTestApplication {public static void main(String[] args) {SpringApplication.run(HelloServiceTestApplication.class, args);}
}

@FeignClient注册声明定义FeignClient,笔者以两种方式定义了两个FeignClient:

1.通过请求路径定义FeignClient

@FeignClient(value = "feignHelloService", path = "/hello")
public interface RemoteServiceClient {@RequestMapping("/")String testHello();
}

2.通过生产者(即上述构建的helloService)暴露出来的接口定义FeignClient

@Component
@FeignClient(name = "feignHelloService")
public interface RemoteServiceRpcClient extends IHelloServiceClient {
}

controller 测试:

@Api(value = "Hello 测试 服务", tags = "Hello 测试 服务")
@RestController
public class HelloTestController {@ResourceRemoteServiceClient remoteService;@AutowiredRemoteServiceRpcClient remoteServiceRpcClient;@ApiOperation(value = "远程调用方式测试1")@GetMapping("/remote")public String remoteHello(){return remoteService.testHello();}@ApiOperation(value = "本地调用方式测试")@GetMapping("/local")public String localHello(){return "hello" + UUID.randomUUID().toString();}@ApiOperation(value = "远程调用方式测试2,带请求参数")@PostMapping("/remoteRpc")public String remoteRpcHello(){return remoteServiceRpcClient.sayHello("tony",110);
//        return remoteServiceRpcClient.sayHello();}@ApiOperation(value = "远程调用方式测试3, 带请求体")@PostMapping("/remoteRpc1")public HelloResponse remoteRpcHello1(){return remoteServiceRpcClient.sayHello(new HelloRequest("YYY",122));}}

测试结果如下:


helloServiceTest.jpg

知其所以然

知道了如何将SpringCloud, Zookeeper 和Feign进行整合,我们知道了怎么使用,更重要的是要知道里面的原理,做到知其然更要知其所以然。

通过上述对整合过程的描述中可以发现,@EnableFeignClients和@FeignClient两个注解是将Feign整合进Spring Cloud的重要组成部分,因此,从这两个注解入手来了解Feign。

@EnableFeignClients:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {/*** basePackages属性的别名*/String[] value() default {};/*** 包扫描路径,扫描该路径下被@FeignClient标记的类*/String[] basePackages() default {};/*** 指明包扫描的类*/Class<?>[] basePackageClasses() default {};/*** 对所有feign client定制的配置类*/Class<?>[] defaultConfiguration() default {};/*** 所有被@FeignClient标记的client,如果不为空,就不会基于classpath进行扫描*/Class<?>[] clients() default {};
}

@EnableFeignClients -> FeignClientsRegistrar

@EnableFeignClients注解通过@Import引入了FeignClientsRegistrar进行feign客户端的注册, 同时FeignClientsRegistrar通过实现ImportBeanDefinitionRegistrar来将bean注册spring容器中:

public interface ImportBeanDefinitionRegistrar {/*** 根据使用者的配置类的注解元数据来注册bean的定义* @param importingClassMetadata 配置类的注解元数据* @param registry 当前bean定义的注册器,一般指spring容器*/default void registerBeanDefinitions(AnnotationMetadataimportingClassMetadata, BeanDefinitionRegistry registry) {}
}
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {//注册默认的配置到spring容器中registerDefaultConfiguration(metadata, registry);//注册发现的feign client到spring容器中registerFeignClients(metadata, registry);}
}

@EnableFeignClients -> FeignClientsRegistrar —> registerDefaultConfiguration

    private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {//获取@EnableFeignClients注解的属性Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {String name;//拼接默认配置名if (metadata.hasEnclosingClass()) {name = "default." + metadata.getEnclosingClassName();}else {name = "default." + metadata.getClassName();}registerClientConfiguration(registry, name,defaultAttrs.get("defaultConfiguration"));}}private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {//将feign client 配置构建成一个bean注册到spring容器中BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());}

@EnableFeignClients -> FeignClientsRegistrar —> registerFeignClients

    public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {//定义一个基于classpath的扫描器,用来获取被@FeignClient注解标注的feign clentClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);Set<String> basePackages;//获取@EnableFeignClients注解的属性Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());//@FeignClient注解过滤器AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);//获取@EnableFeignClient注解的clients属性的值final Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");if (clients == null || clients.length == 0) {//@EnableFeignClients注解没有配置clients属性的情况//扫描器中加入注解过滤器scanner.addIncludeFilter(annotationTypeFilter);//获取@EnableFeignClients注解中的basePackages属性basePackages = getBasePackages(metadata);}else {//@EnableFeignClients注解配置了clients属性的情况final Set<String> clientClasses = new HashSet<>();basePackages = new HashSet<>();for (Class<?> clazz : clients) {//遍历client,获取器包路径和类名basePackages.add(ClassUtils.getPackageName(clazz));clientClasses.add(clazz.getCanonicalName());}//定义过滤器,只获取在clientClasses集合中的feign clientAbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {@Overrideprotected boolean match(ClassMetadata metadata) {String cleaned = metadata.getClassName().replaceAll("\\$", ".");return clientClasses.contains(cleaned);}};scanner.addIncludeFilter(new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));}//使用扫描器scanner扫描每一个basePackage,获取被@FeignClient标注的客户端for (String basePackage : basePackages) {Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(),"@FeignClient can only be specified on an interface");//获取@FeignClient注解的属性Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name = getClientName(attributes);//将针对特定feign client的配置注册到spring容器registerClientConfiguration(registry, name, attributes.get("configuration"));//注册feign client到spring容器       registerFeignClient(registry, annotationMetadata, attributes);}}}}private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();//通过FeignClientFactoryBean工厂bean构建BeanDefinitionBuilderBeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);validate(attributes);//@FeignClient注解的属性作为bean的属性definition.addPropertyValue("url", getUrl(attributes));definition.addPropertyValue("path", getPath(attributes));String name = getName(attributes);definition.addPropertyValue("name", name);String contextId = getContextId(attributes);definition.addPropertyValue("contextId", contextId);definition.addPropertyValue("type", className);definition.addPropertyValue("decode404", attributes.get("decode404"));definition.addPropertyValue("fallback", attributes.get("fallback"));definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));//定义根据类型进行自动注入definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);String alias = contextId + "FeignClient";AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);// has a default, won't be nullboolean primary = (Boolean) attributes.get("primary");beanDefinition.setPrimary(primary);String qualifier = getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias = qualifier;}BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[] { alias });//注册feign client到spring容器BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}

至此,我们知道了通过@EnableFeignClients和@FeignClient两个注解以及其相关属性,在服务启动时,将每个feign client 以及其对应的配置和每个客户端通用的配置以bean的方式注册完到spring容器中。

FeignClient的自动注入

当使用@Autowired注解自动注入FeignClient时,Spring容器会使用注册FeignClient用到的FeignClientFactoryBean为其生成FeignClient实例。

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {@Overridepublic Object getObject() throws Exception {return getTarget();}/*** @param <T> the target type of the Feign client* @return a {@link Feign} client created with the specified data and the context* information*/<T> T getTarget() {//从应用上下文中获取FeignClient的上下文FeignContext context = applicationContext.getBean(FeignContext.class);//通过FeignClient的上下文构建feignClient构造器Feign.Builder builder = feign(context);if (!StringUtils.hasText(url)) {//@FeignClient没有配置url的情况,根据name和path属性拼接成urlif (!name.startsWith("http")) {url = "http://" + name;}else {url = name;}url += cleanPath();//没有配置url属性,需要在多个服务节点之间进行负载均衡,生产Feign clientreturn (T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url));}//配置了url属性的情况if (StringUtils.hasText(url) && !url.startsWith("http")) {url = "http://" + url;}String url = this.url + cleanPath();//从上下文获取FeignClien:LoadBalancerFeignClientClient client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient) client).getDelegate();}if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((FeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}//从上下文中获取targeterTargeter targeter = get(context, Targeter.class);//通过targeter、builder生成Feign clientreturn (T) targeter.target(this, builder, context,new HardCodedTarget<>(type, name, url));}protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {//上下文获取Feign clientClient client = getOptional(context, Client.class);if (client != null) {//将builder与client关联builder.client(client);Targeter targeter = get(context, Targeter.class);//生产Feign clientreturn targeter.target(this, builder, context, target);}throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");}
}

默认使用的targeter是HystrixTargeter,根据builder的类型设置不同的属性,并生产Feign client

class HystrixTargeter implements Targeter {@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {return feign.target(target);}//....省略return feign.target(target);}
}
public abstract class Feign {public static class Builder {//构建客户端并创建feign client实例public <T> T target(Target<T> target) {return build().newInstance(target);}public Feign build() {Client client = Capability.enrich(this.client, capabilities);Retryer retryer = Capability.enrich(this.retryer, capabilities);List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream().map(ri -> Capability.enrich(ri, capabilities)).collect(Collectors.toList());Logger logger = Capability.enrich(this.logger, capabilities);Contract contract = Capability.enrich(this.contract, capabilities);Options options = Capability.enrich(this.options, capabilities);Encoder encoder = Capability.enrich(this.encoder, capabilities);Decoder decoder = Capability.enrich(this.decoder, capabilities);InvocationHandlerFactory invocationHandlerFactory =Capability.enrich(this.invocationHandlerFactory, capabilities);QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);ParseHandlersByName handlersByName =new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,errorDecoder, synchronousMethodHandlerFactory);//根据相关配置,构建ReflectiveFeignreturn new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}}
}
public class ReflectiveFeign extends Feign {private final ParseHandlersByName targetToHandlersByName;private final InvocationHandlerFactory factory;private final QueryMapEncoder queryMapEncoder;ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,QueryMapEncoder queryMapEncoder) {this.targetToHandlersByName = targetToHandlersByName;this.factory = factory;this.queryMapEncoder = queryMapEncoder;}@Overridepublic <T> T newInstance(Target<T> target) {Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {continue;} else if (Util.isDefault(method)) {//对于缺省的方法使用DefaultMethodHandlerDefaultMethodHandler handler = new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {//对于每个对应服务端的方法,使用nameToHandler获取methodHandlermethodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}//通过InvocationHandlerFactory创建FeignInvocationHandler,该handler包含了上面创建的methodTohHandler//构成dispatch,用于对应@FeignClient标注的接口方法,当调用时进行转发处理InvocationHandler handler = factory.create(target, methodToHandler);//为feign客户端实例创建动态代理对象T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);//将缺省的methodHander绑定到动态代理对象上for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}
}

总结

从上面的分析可以得出,当服务启动时,通过@EnableFeignClients注解,启动对标注了@FeignClient注解的类进行扫描和注册,通过FeignClientFactoryBean将FeignClient注册到Spring容器中。当使用@Autowired注解进行自动注入时,注册到Spring容器中FeignClient会以动态代理的形式注入,这些动态代理中包含了接口方法的methodHandler用以处理调用转发。

最后编辑于:2025-06-15 09:49:01


喜欢的朋友记得点赞、收藏、关注哦!!!

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入探索 OpenCV 图像识别:从基础到深度学习

在当今数字化时代&#xff0c;图像识别技术已经渗透到我们生活的方方面面&#xff0c;从智能手机中的拍照翻译功能到自动驾驶汽车的目标检测系统&#xff0c;图像识别的应用无处不在。作为一名算法工程师&#xff0c;我有幸深入研究并实践了 OpenCV 在图像识别领域的强大功能。…

Hadoop部署(HA)高可用集群

一、准备工作 1.把集群全部停掉 在三台节点上都做&#xff08;在xshell通过右键----> 发送输入到--->所有会话&#xff09; 2..在/export/servers下创建HA目录 sudo mkdir -p /export/servers/HA 3.创建用户和设置所属主和所属组 #创建用户 sudo adduser ygre #设置…

STM32 CAN位同步、错误处理

一、接收方数据采样 CAN总线没有时钟线&#xff0c;总线上的所有设备通过约定波特率的方式确定每一个数据位的时长发送方以约定的位时长每隔固定时间输出一个数据位接收方以约定的位时长每隔固定时间采样总线的电平&#xff0c;输入一个数据位理想状态下&#xff0c;接收方能依…

django serializer __all__中 额外添加外键里的某一个属性

在Django中使用序列化器&#xff08;Serializer&#xff09;时&#xff0c;你可能会遇到需要将模型&#xff08;Model&#xff09;中的外键字段转换成其关联对象的一部分属性的情况。默认情况下&#xff0c;序列化器会自动序列化外键字段&#xff0c;但如果你想要在序列化结果中…

Redis快的原因

Redis 高性能的核心原因 Redis 之所以能达到极高的性能&#xff08;10万 QPS&#xff09;&#xff0c;主要源于以下几个关键设计&#xff1a; 1. 纯内存操作 核心优势&#xff1a;所有数据存储在内存中&#xff0c;避免了磁盘 I/O 瓶颈 内存访问速度比磁盘快 10万倍以上&am…

【大模型微调】6.模型微调实测与格式转换导出

引言 本文继续研究 LLaMA-Factory 微调数据的流程&#xff0c;侧重于微调结果与模型导出。 数据集准备 首先参考 LLaMA-Factory 核心开发者的文章[1]&#xff0c;下载用于微调的公开的商品文案数据集 AdvertiseGen。 下载地址&#xff1a;https%3A//cloud.tsinghua.edu.cn/…

3085. 成为 K 特殊字符串需要删除的最少字符数

3085. 成为 K 特殊字符串需要删除的最少字符数 给你一个字符串 word 和一个整数 k。 如果 |freq(word[i]) - freq(word[j])| < k 对于字符串中所有下标 i 和 j 都成立&#xff0c;则认为 word 是 k 特殊字符串。 此处&#xff0c;freq(x) 表示字符 x 在 word 中的出现频…

分布式系统中的 Kafka:流量削峰与异步解耦(二)

Kafka 在分布式系统中的应用案例 电商订单系统 在电商领域&#xff0c;订单系统是核心业务模块之一&#xff0c;涉及多个复杂的业务环节和系统组件之间的交互。以常见的电商购物流程为例&#xff0c;当用户在电商平台上下单后&#xff0c;订单创建服务会首先接收到用户的订单…

从事登高架设作业需要注意哪些安全事项?

从事登高架设作业&#xff08;如脚手架搭设、高空维修、外墙清洗等&#xff09;属于高风险特种作业&#xff0c;必须严格遵守安全规范。以下是关键安全注意事项&#xff0c;涵盖作业前准备、作业中操作、应急处理三大环节&#xff0c;符合国家《高处作业安全技术规范》&#xf…

RA4M2开发IOT(6)----涂鸦模组快速上云

RA4M2开发IOT.6--涂鸦模组快速上云 概述视频教学样品申请硬件准备参考程序涂鸦官网链接创建一个项目选择对应产品产品基本配置添加标准功能APP界面配置硬件选择产品配置硬件详情PCB原理图涂鸦调试文件下载进入调试涂鸦模块串口协议心跳检测查询产品信息查询工作模式AP配网APP链…

AI时代SEO关键词革新

内容概要 在人工智能&#xff08;AI&#xff09;技术快速发展的背景下&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;关键词策略正经历根本性变革。本文将系统阐述AI如何重塑关键词研究、优化及效果评估的全流程。具体而言&#xff0c;首先解析智能研究方法在挖掘用户意…

JavaEE初阶第三期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(一)

专栏&#xff1a;JavaEE初阶起飞计划 个人主页&#xff1a;手握风云 目录 一、认识线程 1.1. 概念 1.2. 为什么要使用线程 1.3. 进程和线程的关系 1.4. 多线程模型 二、多线程的创建 2.1. 继承Thread类 2.2. 实现Runnable接口 2.3. 匿名内部类 2.4. lambda表达式 一、…

【StarRocks系列】建表优化

目录 一、数据模型选择 (核心优化) 二、分区与分桶策略 (数据分布优化) 三、字段类型与压缩 四、索引策略 五、高级特性应用 六、建表示例&#xff08;关键优化整合&#xff09; 参考官网 优化性能 | StarRocks 在 StarRocks 中创建表时&#xff0c;合理的表设计是性能优…

linux-vim编辑器

linux-vim编辑器 前言一、命令模式1. 跳转功能2. 文本编辑3. 模式切换 二、输入模式1. 进入输入模式2. 快捷键 三、末行模式1. 进入末行模式2. 文件操作3. 查找与替换4. 行操作 四、替换模式五、可视模式1. 进入可视模式2. 文本操作 六、相关配置 前言 vim - Vi IMproved, a p…

SQL关键字三分钟入门: 表结构管理与分区设计。(ALTER、MODIFY、CHANGE、DEFAULT、VALUES、LESS THAN、RANGE)

前面我们已经学习了如何查询数据&#xff08;SELECT&#xff09;、筛选数据&#xff08;WHERE&#xff09;等操作。现在我们要进入数据库的另一个重要领域 —— 表结构管理与分区设计。 本文带你快速认识以下关键字&#xff1a; ✅ ALTER✅ MODIFY✅ CHANGE✅ DEFAULT✅ VALU…

深度剖析:RTTI轻量框架实现原理与架构(C++ 17 高级编程)

&#x1f680; C RTTI反射系统深度设计文档 &#x1f30c; 核心架构图 #mermaid-svg-aWkaWoFklq1ylap6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aWkaWoFklq1ylap6 .error-icon{fill:#552222;}#mermaid-svg-a…

03-D3.js SVG text标签​

Data Visualization D3.js • SuperHiLearn how to create interactive, engaging experiences using HTML, CSS, SVG and Javascript.https://www.superhi.com/catalog/data-visualization-with-d3 text - SVG&#xff1a;可缩放矢量图形 | MDNtext元素定义了一个由文字组成…

Python 使用Gitlab Api

代码 REST API 见自带帮助文档 python 安装python-gitlab pip install --upgrade python-gitlab使用API 参考&#xff1a;https://python-gitlab.readthedocs.io/en/stable/api-usage.html import gitlab# anonymous read-only access for public resources (GitLab.com…

中医体质识别:理论、方法与应用的简要综述

中医体质识别&#xff1a;理论、方法与应用的简要综述 摘要 中医体质识别是中医“治未病”及个性化诊疗的关键环节。本文系统阐述中医体质识别&#xff0c;涵盖理论基础、常见体质类型、识别方法、现代技术应用及临床实践。中医体质理论源远流长&#xff0c;《黄帝内经》奠定…

稀疏表原理及应用场景

1 概述 稀疏表&#xff08;Sparse Table&#xff0c;ST&#xff09;是一种用于高效解决 静态区间查询&#xff08;Range Query&#xff09; 问题的数据结构&#xff0c;主要用于 可重复贡献问题&#xff08;Idempotent Range Queries&#xff09;&#xff0c;例如区间最小值&a…