Java在SpringCloud中自定义Gateway负载均衡策略
Java在SpringCloud中自定义Gateway负载均衡策略
一、前言
spring-cloud-starter-netflix-ribbon已经不再更新了,最新版本是2.2.10.RELEASE,最后更新时间是2021年11月18日,详细信息可以看maven官方仓库:org.springframework.cloud/spring-cloud-starter-netflix-ribbon,SpringCloud官方推荐使用spring-cloud-starter-loadbalancer进行负载均衡。
背景:大文件上传做切片文件上传;
流程:将切片文件上传到服务器,然后进行合并任务,合并完成之后上传到对象存储;现在服务搞成多节点以后,网关默认走轮循,但是相同的服务在不同的机器上,这样就会导致切片文件散落在不同的服务器上,会导致文件合并失败;所以根据一个标识去自定义gateway对应服务的负载均衡策略,可以解决这个问题;
我的版本如下:
<spring-boot.version>2.7.3</spring-boot.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
二、参考默认实现
springCloud原生默认的负载均衡策略是这个类:
org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer
我们参考这个类实现自己的负载均衡策略即可,RoundRobinLoadBalancer实现了ReactorServiceInstanceLoadBalancer这个接口,实现了choose这个方法,如下图:

在choose方法中调用了processInstanceResponse方法,processInstanceResponse方法中调用了getInstanceResponse方法,所以我们我们可以复制RoundRobinLoadBalancer整个类,只修改getInstanceResponse这个方法里的内容就可以实现自定义负载均衡策略。
三、实现代码
原理:根据请求头当中设备的唯一标识传递到下游,唯一标识做哈希取余,可以指定对应的服务器节点,需要的服务设置自定义负载策略,不需要的服务设置默认的轮循机制即可.我这里是根据单独的接口请求地址去自定义,也可以根据服务名称自定义
package com.wondertek.gateway.loadBalancer;import cn.hutool.core.util.ObjectUtil;
import com.wondertek.web.exception.enums.HttpRequestHeaderEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class RequestFilter implements GlobalFilter, Ordered {@Overridepublic int getOrder() {// 应该小于LoadBalancerClientFilter的顺序值return Ordered.HIGHEST_PRECEDENCE;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String clientDeviceUniqueCode = request.getHeaders().getFirst(HttpRequestHeaderEnum.CLIENT_DEVICE_UNIQUE_CODE.getCode());// 存入Reactor上下文String resultCode = clientDeviceUniqueCode;//路径String pathUrl = request.getURI().getPath();/*** ^ 锚点匹配输入字符串的开始位置。* /(oms-api|unity-api|cloud-api) 匹配以 /oms-api 或 /unity-api 或 /cloud-api 开始的任何字符串。* replaceFirst() 方法用空字符串替换第一次匹配的内容,也就是我们想要去掉的服务名称。*/String resultPathUrl = pathUrl.replaceFirst("^/(oms-api|unity-api|cloud-api)", "");return chain.filter(exchange).contextWrite(context -> {if (ObjectUtil.isNotEmpty(resultCode) && ObjectUtil.isNotEmpty(resultPathUrl)) {log.info("开始将request中的唯一标识封装到上下游中:{},请求path是:{}", resultCode, resultPathUrl);return context.put("identification", resultCode).put("pathUrl", resultPathUrl);} else {//根据需求进行其他处理return context;}});}
}
package com.wondertek.gateway.loadBalancer;import cn.hutool.core.util.ObjectUtil;
import com.wondertek.center.constants.BusinessCenterApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;@Slf4j
public class ClientDeviceUniqueCodeInstanceLoadBalancer implements ReactorServiceInstanceLoadBalancer {private final String serviceId;final AtomicInteger position;private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;public ClientDeviceUniqueCodeInstanceLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, AtomicInteger position) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = position;}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {//在 choose 方法中,使用 deferContextual 方法来访问上下文并提取客户端标识。这里的 getOrDefault 方法尝试从上下文中获取一个键为 "identification" 的值,如果不存在则返回 "default-identification"return Mono.deferContextual(contextView -> {String identification = contextView.getOrDefault("identification", "");log.info("上下游获取到的identification的值为:{}", identification);String pathUrl = contextView.getOrDefault("pathUrl", "");log.info("上下游获取到的pathUrl的值为:{}", pathUrl);ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances, identification, pathUrl));});}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances, String identification, String pathUrl) {Response<ServiceInstance> serviceInstanceResponse;//特定接口走自定义负载策略Boolean status = ObjectUtil.isNotEmpty(identification) && ObjectUtil.isNotEmpty(pathUrl) &&(pathUrl.contains(BusinessCenterApi.WEB_UPLOAD_SLICE_FILE) ||pathUrl.contains(BusinessCenterApi.WEB_MERGE_SLICE_FILE) ||pathUrl.contains(BusinessCenterApi.UNITY_UPLOAD_SLICE_FILE) ||pathUrl.contains(BusinessCenterApi.UNITY_MERGE_SLICE_FILE) ||pathUrl.contains(BusinessCenterApi.CLOUD_UPLOAD_SLICE_FILE) ||pathUrl.contains(BusinessCenterApi.CLOUD_MERGE_SLICE_FILE));if (status) {serviceInstanceResponse = this.getIpInstanceResponse(serviceInstances, identification);} else {serviceInstanceResponse = this.getInstanceResponse(serviceInstances);}if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance((ServiceInstance) serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else {//创建一个新的列表以避免在原始列表上排序,避免了修改共享状态可能带来的线程安全问题List<ServiceInstance> sortedInstances = new ArrayList<>(instances);// 现在对新列表进行排序,保持原始列表的顺序不变Collections.sort(sortedInstances, Comparator.comparing(ServiceInstance::getHost));//log.info("获取到的实例个数的值为:{}", sortedInstances.size());sortedInstances.forEach(instance -> log.info("排序后的实例: {},{}", instance.getHost(), instance.getPort()));int pos = Math.abs(this.position.incrementAndGet());//log.info("默认轮循机制,pos递加后的值为:{}", pos);int positionIndex = pos % instances.size();//log.info("取余后的positionIndex的值为:{}", positionIndex);ServiceInstance instance = instances.get(positionIndex);//log.info("instance.getUri()的值为:{}", instance.getUri());log.info("特殊服务,默认轮循机制,routed to instance: {}:{}", instance.getHost(), instance.getPort());return new DefaultResponse(instance);}}private Response<ServiceInstance> getIpInstanceResponse(List<ServiceInstance> instances, String identification) {if (instances.isEmpty()) {log.warn("No servers available for service: " + this.serviceId);return new EmptyResponse();} else {//创建一个新的列表以避免在原始列表上排序,避免了修改共享状态可能带来的线程安全问题List<ServiceInstance> sortedInstances = new ArrayList<>(instances);// 现在对新列表进行排序,保持原始列表的顺序不变Collections.sort(sortedInstances, Comparator.comparing(ServiceInstance::getHost));//log.info("获取到的实例个数的值为:{}", sortedInstances.size());sortedInstances.forEach(instance -> log.info("排序后的实例: {},{}", instance.getHost(), instance.getPort()));//log.info("多个服务实例,使用客户端 identification 地址的哈希值来选择服务实例");// 使用排序后的列表来找到实例int ipHashCode = Math.abs(identification.hashCode());//log.info("identificationHashCode的值为:{}", ipHashCode);int instanceIndex = ipHashCode % sortedInstances.size();//log.info("instanceIndex的值为:{}", instanceIndex);ServiceInstance instanceToReturn = sortedInstances.get(instanceIndex);//log.info("instanceToReturn.getUri()的值为:{}", instanceToReturn.getUri());log.info("特殊服务,自定义identification负载机制,Client identification: {} is routed to instance: {}:{}", identification, instanceToReturn.getHost(), instanceToReturn.getPort());return new DefaultResponse(instanceToReturn);}}}
package com.wondertek.gateway.loadBalancer;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;@Slf4j
public class DefaultInstanceLoadBalancer implements ReactorServiceInstanceLoadBalancer {private final String serviceId;private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;final AtomicInteger position;public DefaultInstanceLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, AtomicInteger position) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = position;}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + serviceId);}return new EmptyResponse();}//创建一个新的列表以避免在原始列表上排序,避免了修改共享状态可能带来的线程安全问题List<ServiceInstance> sortedInstances = new ArrayList<>(instances);// 现在对新列表进行排序,保持原始列表的顺序不变Collections.sort(sortedInstances, Comparator.comparing(ServiceInstance::getHost));//log.info("获取到的实例个数的值为:{}", sortedInstances.size());sortedInstances.forEach(instance -> log.info("排序后的实例: {},{}", instance.getHost(), instance.getPort()));int pos = Math.abs(this.position.incrementAndGet());//log.info("默认轮循机制,pos递加后的值为:{}", pos);int positionIndex = pos % instances.size();//log.info("取余后的positionIndex的值为:{}", positionIndex);ServiceInstance instance = instances.get(positionIndex);//log.info("instance.getUri()的值为:{}", instance.getUri());log.info("默认轮循机制,routed to instance: {}:{}",instance.getHost(), instance.getPort());return new DefaultResponse(instance);}}
package com.wondertek.gateway.loadBalancer;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;import java.util.concurrent.atomic.AtomicInteger;@Configuration
//单台服务
//@LoadBalancerClient(name = "oms-api", configuration = CustomLoadBalancerConfig.class)
//多台服务
@LoadBalancerClients({@LoadBalancerClient(name = "oms-api", configuration = CustomLoadBalancerConfig.class),@LoadBalancerClient(name = "unity-api", configuration = CustomLoadBalancerConfig.class),@LoadBalancerClient(name = "cloud-api", configuration = CustomLoadBalancerConfig.class),@LoadBalancerClient(name = "open-api", configuration = CustomLoadBalancerConfig.class),@LoadBalancerClient(name = "server-api", configuration = CustomLoadBalancerConfig.class),@LoadBalancerClient(name = "center-service", configuration = CustomLoadBalancerConfig.class),
})
@Slf4j
public class CustomLoadBalancerConfig {// 定义一个Bean来提供AtomicInteger的实例@Beanpublic AtomicInteger positionTracker() {// 这将在应用上下文中只初始化一次return new AtomicInteger(0);}//自定义优先级负载均衡器@Beanpublic ReactorServiceInstanceLoadBalancer customPriorityLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,Environment environment,AtomicInteger positionTracker) {String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);//目的为解决文件上传切片文件分散上传的问题if ("oms-api".equals(serviceId)||"unity-api".equals(serviceId)||"cloud-api".equals(serviceId)){//log.info("服务名称:serviceId:{},走自定义clientDeviceUniqueCode负载模式", serviceId);return new ClientDeviceUniqueCodeInstanceLoadBalancer(serviceInstanceListSupplierProvider, serviceId, positionTracker);}//log.info("服务名称:serviceId:{},走默认负载模式", serviceId);return new DefaultInstanceLoadBalancer(serviceInstanceListSupplierProvider, serviceId,positionTracker);}
}
【SpringCloud系列】开发环境下重写Loadbalancer实现自定义负载均衡
相关文章:
Java在SpringCloud中自定义Gateway负载均衡策略
Java在SpringCloud中自定义Gateway负载均衡策略 一、前言 spring-cloud-starter-netflix-ribbon已经不再更新了,最新版本是2.2.10.RELEASE,最后更新时间是2021年11月18日,详细信息可以看maven官方仓库:org.springframework.clou…...
前端 js 基础(1)
js 结果输出 (点击按钮修改文字 ) <!DOCTYPE html> <html> <head></head><body><h2>Head 中的 JavaScript</h2><p id"demo">一个段落。</p><button type"button" onclic…...
Android : 使用GestureOverlayView进行手势识别—简单应用
示例图: GestureOverlayView介绍: GestureOverlayView 是 Android 开发中用于识别和显示手势的视图组件。它允许用户在屏幕上绘制手势,并且应用程序可以检测和响应这些手势。以下是关于 GestureOverlayView 的主要特点: 手势识别…...
API集群负载统计 (100%用例)C卷 (JavaPythonNode.jsC语言C++)
某个产品的RESTful API集合部署在服务器集群的多个节点上, 近期对客户端访问日志进行了采集,需要统计各个API的访问频次, 根据热点信息在服务器节点之间做负载均衡,现在需要实现热点信息统计查询功能。 RESTful API的由多个层级构成,层级之间使用/连接,如/A/B/C/D这个地址…...
小梅哥Xilinx FPGA学习笔记18——专用时钟电路 PLL与时钟向导 IP
目录 一:IP核简介(具体可参考野火FPGA文档) 二: 章节导读 三:PLL电路原理 3.1 PLL基本实现框图 3.2 PLL倍频实现 3.3 PLL分频实现 四: 基于 PLL 的多时钟 LED 驱动设计 4.1 配置 Clocking Wizard 核 4.2 led …...
低代码平台在金融银行中的应用场景
随着数字化转型的推进,商业银行越来越重视技术在业务发展中的作用。在这个背景下,白码低代码平台作为一种新型的开发方式,正逐渐受到广大商业银行的关注和应用。白码低代码平台能够快速构建各类应用程序,提高开发效率,…...
Css基础内容
<!DOCTYPE html> <html> <head> <meta charset"UTF-8" /> <title>CSS</title> <!-- <link rel"stylesheet" href"Html5与Css3\CSS\my.css"> --> <!-- link引入外部样式表:rel&…...
微服务(11)
目录 51.pod的重启策略是什么? 52.描述一下pod的生命周期有哪些状态? 53.创建一个pod的流程是什么? 54.删除一个Pod会发生什么事情? 55.k8s的Service是什么? 51.pod的重启策略是什么? 可以通过命令kub…...
连锁门店管理需要信息化系统
连锁门店管理的信息化系统可以提供以下功能,以满足连锁企业日常管理的需求: 1. 连锁线下收银:信息化系统可以提供线下收银功能,包括商品扫码、价格结算、支付方式选择等。通过系统记录每笔交易数据,方便对销售情况进行…...
UTF-8编码:打破字符编码的国界
UTF-8编码:打破字符编码的国界 大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天,让我们一同探讨编程世界中一项至关重要的技术——“UTF-…...
HTML进阶
列表、表格、表单 文章目录 列表、表格、表单01-列表无序列表有序列表定义列表 02-表格表格结构标签-了解合并单元格 03-表单input 标签input 标签占位文本单选框上传文件多选框下拉菜单文本域label 标签按钮 04-语义化无语义的布局标签有语义的布局标签 05-字符实体 01-列表 …...
基于策略模式和简单工厂模式实现zip、tar、rar、7z四种压缩文件格式的解压
推荐语 这篇技术文章深入探讨了基于策略模式和简单工厂模式实现四种常见压缩文件格式的解压方法。通过阅读该文章,你将了解到如何利用这两种设计模式来实现灵活、可扩展的解压功能,同时适应不同的压缩文件格式。如果你对设计模式和文件处理感兴趣或刚好…...
修改jenkins的目录(JENKINS_HOME)
默认JENKINS_HOME是/var/lib/jenkins/ 现要修改为/home/jenkins_data/jenkins 最开始 sudo cp -a /var/lib/jenkins/ /home/jenkins_data/ 然后如下操作: 1、首先 /etc/sysconfig/jenkins:jenkins配置文件,“端口”,“JENKIN…...
Bytebase:统一数据库 CI/CD 解决方案 | 开源日报 No.128
bytebase/bytebase Stars: 7.9k License: NOASSERTION Bytebase 是一个数据库 CI/CD 解决方案,为开发人员和 DBA 提供统一的工具来管理不同数据库系统的开发生命周期。其主要功能包括标准化操作流程、SQL 代码审查、GitOps 集成以及数据访问控制等。关键特性和核心…...
History对象常用方法
文章目录 一、什么是History对象二、使用History对象 一、什么是History对象 history 对象来保存浏览器历史记录信息,也就是用户访问的页面。浏览器的前进与后退功能本质上就是 history 的操作。history 对象记录了用户浏览过的页面,通过该对象提供的 A…...
修改源码,element的el-table合并,处理合并产生的hover样式问题
1、确认自己element-ui的版本号 2、此element-ui下的lib包是修改过hover样式的包,如何替换自己文件下的node_modules中的包 修改后将lib文件夹中文件替换你项目中/node_module/element-ui/Lib中的文件问题??如果替换开发环境中的node_module的包无法升级到测试环境,因为nod…...
IoT 物联网常用协议
物联网协议是指在物联网环境中用于设备间通信和数据传输的协议。根据不同的作用,物联网协议可分为传输协议、通信协议和行业协议。 传输协议:一般负责子网内设备间的组网及通信。例如 Wi-Fi、Ethernet、NFC、 Zigbee、Bluetooth、GPRS、3G/4G/5G等。这些…...
使用java备份和恢复SQLServer表数据
需求 近来工作中遇到一个问题,内网办公系统中的数据需要导出到外网中进行查询,外网的数据库中还有一些表存储外网的数据,因此无法使用全库备份恢复功能来满足需求。即只从内网数据库中导出若干表的内容至外网数据库的对应表。 其他解决方案…...
27 UVM queue
uvm_queue类构建一个动态队列,该队列将按需分配并通过引用传递。 uvm_queue类声明: class uvm_queue #( type T int ) extends uvm_object 1 uvm_queue class hierarchy 2 uvm_queue class Methods 3 UVM Queue Example 在下面的示例中,…...
聊聊自动化测试的分层实践
技术群里,有同学聊起了各自在实践自动化测试时遇到的各种问题,最典型的就是落地难度和投入产出比。毕竟在当前这个时间节点,单纯的技术实践如果不能带来实际可见的业务价值,确实很影响个人绩效和团队产出。 这篇文章,…...
ARM Debug Interface v5.1架构解析与调试实践
1. ARM Debug Interface v5.1架构深度解析1.1 调试接口技术演进与核心价值ARM调试接口(ADI)技术历经多次迭代,v5.1版本作为当前主流标准,在嵌入式系统调试领域确立了关键地位。调试接口本质上是处理器核与外部调试工具之间的标准化通信桥梁,其…...
汽车该多久换一代
汽车该多久换一代 买车的人其实不怕四年换代,怕的是刚提车半年就被新款打成旧款。李想这句话能引起讨论,原因也在这里:车企说的是研发验证周期,车主感受到的是价格、配置和二手残值。 汽车确实没法完全照着手机节奏跑。手机坏了可…...
【目标检测系统网页版】基于YOLOv8的淡水鱼检测系统
【目标检测系统网页版】基于YOLOv8的淡水鱼检测系统 一、系统介绍 1、系统简介 基于YOLOv8的淡水鱼检测 Web 系统,支持单图检测、批量图片检测、视频检测与实时视频流检测。 2、功能特性 智能识别:YOLOv8 封装,单例加载,自动设…...
别再用游戏卡炼丹了!手把手教你给台式机装上Tesla P4/P40,搞定Ubuntu 20.04深度学习环境
低成本打造专业级AI工作站:Tesla P4/P40在Ubuntu 20.04的完整实战指南 当你在二手市场以不到2000元的价格淘到一张Tesla P40时,可能会被它12GB GDDR5显存和3840个CUDA核心的参数所吸引——这相当于RTX 2080 Ti约70%的性能,价格却只有其三分之…...
Svelte动态光标实现:状态驱动与Spring动画的交互设计
1. 项目概述:一个会“思考”的鼠标指针如果你在开发一个需要高度沉浸感和交互反馈的Web应用,比如一个设计工具、一个游戏界面,或者一个希望用户能“感受”到页面元素质感的网站,那么一个静态的、系统默认的鼠标指针就显得有些格格…...
Hermes Agent 工具如何配置接入 Taotoken 提供的模型服务
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Hermes Agent 工具如何配置接入 Taotoken 提供的模型服务 Hermes Agent 是一个流行的开源智能体框架,它允许开发者通过…...
在Adafruit Fruit Jam微控制器上移植运行经典游戏DOOM的完整指南
1. 项目概述:当经典FPS遇上迷你计算机作为一名在嵌入式系统和复古计算领域折腾了十多年的老玩家,我始终对“它能不能跑DOOM?”这个梗抱有极大的热情。这不仅仅是一句玩笑,更是对硬件性能和软件移植能力的终极试金石。最近…...
AI教材生成神器来袭!低查重工具一键搞定30万字教材编写!
利用 AI 工具高效编写教材 整理教材的知识点真的需要“精雕细琢”,最难的地方在于平衡与衔接!我们要么会担忧重要的知识点遗漏,要么又很难掌握合适的难度梯度。小学教材常常内容晦涩难懂,学生们难以理解;而高中教材往…...
数字电路跨时钟域信号传输:从亚稳态到同步器设计实践
1. 跨时钟域信号传输:从亚稳态到可靠同步在数字芯片和FPGA设计中,只要系统里存在多个时钟,就绕不开跨时钟域(CDC)信号传输这个经典问题。这可不是什么高深莫测的理论,而是每个硬件工程师在画第一块板子、写…...
Spoolman:终极3D打印线轴管理解决方案,让您的打印工作更高效 [特殊字符]
Spoolman:终极3D打印线轴管理解决方案,让您的打印工作更高效 🚀 【免费下载链接】Spoolman Keep track of your inventory of 3D-printer filament spools. 项目地址: https://gitcode.com/gh_mirrors/sp/Spoolman Spoolman是一个强大…...
