手写RPC框架--4.服务注册
RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧)
RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧)
服务注册
- 服务注册
- a.添加服务节点和主机节点
- b.抽象注册中心
- c.本地服务列表
服务注册
a.添加服务节点和主机节点
主要完成服务注册和发现的功能,其具体流程如下:
-
1.服务提供方将服务注册到注册中心中
-
2.消费端拉取服务列表。
-
3.消费端简单的选取一个可以服务(后续会进行改造,实现负载均衡)

1.修改framework/common的Constants类:定义注册中心的路径常量
public class Constant {// 略........// 服务提供方的在注册中心的基础路径public static final String BASE_PROVIDERS_PATH = "/dcyrpc-metadata/providers";// 服务调用方的在注册中心的基础路径public static final String BASE_CONSUMERS_PATH = "/dcyrpc-metadata/consumers";
}
2.在core中引入common的依赖项
3.修改framework/core的DcyRpcBootstrap类:定义一些相关的基础配置
- 定义相关变量:应用名称, Config, 默认端口
- 定义Zookeeper实例
- 完善方法代码:application() / registry() / protocol() / publish()
- publish()发布服务:将接口与匹配的实现注册到服务中心
// 略......
private static final DcyRpcBootstrap dcyRpcBootstrap = new DcyRpcBootstrap();
// 定义一些相关的基础配置
private String applicationName = "default";
private RegistryConfig registryConfig;
private ProtocolConfig protocolConfig;
private int port = 8088;// 维护一个Zookeeper实例
private ZooKeeper zooKeeper;// 略....../*** 定义当前应用的名字* @param applicationName 应用名称* @return*/
public DcyRpcBootstrap application(String applicationName) {this.applicationName = applicationName;return this;
}/*** 配置一个注册中心* @param registryConfig 注册中心* @return this*/
public DcyRpcBootstrap registry(RegistryConfig registryConfig) {// 维护一个zookeeper实例,但是,如果这样写就会将zookeeper和当前的工程耦合zooKeeper = ZookeeperUtils.createZookeeper();this.registryConfig = registryConfig;return this;
}/*** 配置当前暴露的服务使用的协议* @param protocolConfig 协议的封装* @return this*/
public DcyRpcBootstrap protocol(ProtocolConfig protocolConfig) {this.protocolConfig = protocolConfig;if (log.isDebugEnabled()) {log.debug("当前工程使用了:{}协议进行序列化", protocolConfig.toString());}return this;
}/*** --------------------------------服务提供方的相关api--------------------------------*//*** 发布服务:将接口与匹配的实现注册到服务中心* @param service 封装需要发布的服务* @return*/
public DcyRpcBootstrap publish(ServiceConfig<?> service) {// 服务名称的节点String parentNode = Constant.BASE_PROVIDERS_PATH + "/" + service.getInterface().getName();// 判断节点是否存在,不存在则创建节点(持久)if (!ZookeeperUtils.existNode(zooKeeper, parentNode, null)) {ZookeeperNode zookeeperNode = new ZookeeperNode(parentNode, null);ZookeeperUtils.createNode(zooKeeper, zookeeperNode, null, CreateMode.PERSISTENT);}// 创建本机的临时节点,ip:port// 服务提供方的端口(一般自己设定),还需要获取ip的方法// /dcyrpc-metadata/providers/com.dcyrpc.DcyRpc/192.168.195.1:8088String node = parentNode + "/" + NetUtils.getIp() + ":" + port;if (!ZookeeperUtils.existNode(zooKeeper, node, null)) {ZookeeperNode zookeeperNode = new ZookeeperNode(node, null);ZookeeperUtils.createNode(zooKeeper, zookeeperNode, null, CreateMode.EPHEMERAL);}if (log.isDebugEnabled()) {log.debug("服务{},已经被注册", service.getInterface().getName());}return this;
}// 略....../*** 启动netty服务*/
public void start() {try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}
}// 略......}
4.修改framework/common的utils.zookeeper.ZookeeperUtils类:添加方法:判断节点是否存在
/*** 判断节点是否存在* @param zooKeeper* @param node* @param watcher* @return true:存在 false:不存在*/
public static boolean existNode(ZooKeeper zooKeeper, String node, Watcher watcher) {try {return zooKeeper.exists(node, watcher) != null;} catch (KeeperException | InterruptedException e) {log.error("判断节点:{} 是否存在时发生异常:", node, e);throw new ZookeeperException(e);}
}
5.修改framework/common的exceptions.ZookeeperException类:完善内容
public class ZookeeperException extends RuntimeException{public ZookeeperException() {super();}public ZookeeperException(Throwable cause) {super(cause);}
}
6.在framework/common的utils包下,创建NetUtils类:Network工具类
/*** Network utils*/
@Slf4j
public class NetUtils {public static String getIp() {try {// 获取所有的网卡信息Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();while (interfaces.hasMoreElements()) {NetworkInterface iface = interfaces.nextElement();// 过滤非回环接口和虚拟接口if (iface.isLoopback() || iface.isVirtual() || !iface.isUp()) {continue;}Enumeration<InetAddress> addresses = iface.getInetAddresses();while (addresses.hasMoreElements()) {InetAddress addr = addresses.nextElement();// 过滤IPv6地址和回环地址if (addr instanceof Inet6Address || addr.isLoopbackAddress()) {continue;}String ipAddress = addr.getHostAddress();System.out.println("局域网IP地址:" + ipAddress);return ipAddress;}}throw new NetworkException();} catch (SocketException e) {log.error("获取局域网IP时发送异常", e);throw new NetworkException(e);}}
}
7.在framework/common的exceptions包下创建NetworkException类:编写自定义异常
public class NetworkException extends RuntimeException{public NetworkException() {super();}public NetworkException(String message) {super(message);}public NetworkException(Throwable cause) {super(cause);}
}
b.抽象注册中心
在当前项目中我们的确使用的是zookeeper作为我们项目的注册中心。但是,我们希望在我们的项目是可以扩展使用其他类型的注册中心的,如nacos,redis,甚至是自己独立开发注册中心。为后来的扩展提供可能性,所以在整个工程中我们再也不能单独的面向具体的对象编程,而是面向抽象,我们将抽象出**【注册中心】**整个抽象的概念
1.在core下com.dcyrpc下创建discovery包,创建Registry接口:抽象注册中心接口:注册服务,发现服务,下线服务
/*** 抽象注册中心:注册服务,发现服务,下线服务*/
public interface Registry {/*** 注册服务* @param serviceConfig 服务的配置内容*/public void register(ServiceConfig<?> serviceConfig);
}
2.在core下com.dcyrpc.discovery下创建AbstractRegistry抽象类:提炼共享内容,还可以做模板方法 (待开发)
/*** 提炼共享内容,还可以做模板方法* 所有注册中心都有的公共方法*/
public abstract class AbstractRegistry implements Registry{
}
3.在core下com.dcyrpc.discovery下创建impl包,创建ZookeeperRegistry类继承AbstractRegistry
- 把
DcyRpcBootstrap类里的publish()方法,提炼到该类中
@Slf4j
public class ZookeeperRegistry extends AbstractRegistry {private ZooKeeper zooKeeper = ZookeeperUtils.createZookeeper();@Overridepublic void register(ServiceConfig<?> service) {// 服务名称的节点String parentNode = Constant.BASE_PROVIDERS_PATH + "/" + service.getInterface().getName();// 判断节点是否存在,不存在则创建节点(持久)if (!ZookeeperUtils.existNode(zooKeeper, parentNode, null)) {ZookeeperNode zookeeperNode = new ZookeeperNode(parentNode, null);ZookeeperUtils.createNode(zooKeeper, zookeeperNode, null, CreateMode.PERSISTENT);}// 创建本机的临时节点,ip:port// 服务提供方的端口(一般自己设定),还需要获取ip的方法// /dcyrpc-metadata/providers/com.dcyrpc.DcyRpc/192.168.195.1:8088// TODO:后续处理端口问题String node = parentNode + "/" + NetUtils.getIp() + ":" + 8088;if (!ZookeeperUtils.existNode(zooKeeper, node, null)) {ZookeeperNode zookeeperNode = new ZookeeperNode(node, null);ZookeeperUtils.createNode(zooKeeper, zookeeperNode, null, CreateMode.EPHEMERAL);}if (log.isDebugEnabled()) {log.debug("服务{},已经被注册", service.getInterface().getName());}}
}
4.修改DcyRpcBootstrap
// 略.....
private static final DcyRpcBootstrap dcyRpcBootstrap = new DcyRpcBootstrap();// 定义一些相关的基础配置
private String applicationName = "default";
private RegistryConfig registryConfig;
private ProtocolConfig protocolConfig;
private int port = 8088;// 注册中心
private Registry zookeeperRegistry;// 略...../*** 配置一个注册中心* @param registryConfig 注册中心* @return this*/
public DcyRpcBootstrap registry(RegistryConfig registryConfig) {// 维护一个zookeeper实例,但是,如果这样写就会将zookeeper和当前的工程耦合// 使用 registryConfig 获取一个注册中心this.zookeeperRegistry = registryConfig.getRegistry();return this;
}// 略...../*** 发布服务:将接口与匹配的实现注册到服务中心* @param service 封装需要发布的服务* @return*/
public DcyRpcBootstrap publish(ServiceConfig<?> service) {// 抽象了注册中心的概念,使用注册中心的一个实现完成注册zookeeperRegistry.register(service);return this;
}/*** 批量发布服务* @param services 封装需要发布的服务集合* @return this*/
public DcyRpcBootstrap publish(List<ServiceConfig<?>> services) {for (ServiceConfig<?> service : services) {this.publish(service);}return this;
}// 略.....}
5.修改RegistryConfig类,将类放入discovery包下
public class RegistryConfig {// 定义连接的 urlprivate final String connectString;public RegistryConfig(String connectString) {this.connectString = connectString;}/*** 可以使用简单工厂来完成* @return 具体的注册中心实例*/public Registry getRegistry() {// 1.获取注册中心// 1.获取类型// 2.获取主机地址String registryType = getRegistryType(connectString, true).toLowerCase().trim();if (registryType.equals("zookeeper")) {String host = getRegistryType(connectString, false);return new ZookeeperRegistry(host, Constant.TIME_OUT);}throw new DiscoveryException("未发现合适的注册中心");}private String getRegistryType(String connectString, boolean ifType) {String[] typeAndHost = connectString.split("://");if (typeAndHost.length != 2) {throw new RuntimeException("给定的注册中心连接url不合法");}if (ifType){return typeAndHost[0];}else {return typeAndHost[1];}}
}
6.在framework/common的exceptions中创建DiscoveryException类:服务注册与发现异常处理
/*** 服务注册与发现异常处理*/
public class DiscoveryException extends RuntimeException{public DiscoveryException() {super();}public DiscoveryException(String message) {super(message);}public DiscoveryException(Throwable cause) {super(cause);}
}
c.本地服务列表
服务调用方需要通过服务中心发现服务列表
- 1.使用Map进行服务列表的存储
- 2.使用动态代理生成代理对象
- 3.从注册中心,寻找一个可用的服务
1.修改DcyRpcBootstrap部分代码:使用Map进行服务列表的存储
// 维护已经发布且暴露的服务列表 key:interface的全限定名 value:ServiceConfig
private static final Map<String, ServiceConfig<?>> SERVERS_LIST = new HashMap<>(16);/*** 发布服务:将接口与匹配的实现注册到服务中心* @param service 封装需要发布的服务* @return*/
public DcyRpcBootstrap publish(ServiceConfig<?> service) {// 抽象了注册中心的概念,使用注册中心的一个实现完成注册zookeeperRegistry.register(service);// 1.当服务调用方,通过接口、方法名、具体的方法参数列表 发起调用,提供方怎么知道使用哪一个实现// (1) new 1 个// (2) spring beanFactory.getBean(Class)// (3) 自己维护映射关系SERVERS_LIST.put(service.getInterface().getName(), service);return this;
}
2.修改ReferenceConfig部分代码
// 略.....
private Registry registry;/*** 代理设计模式,生成一个API接口的代理对象* @return 代理对象*/
public T get() {// 使用动态代理完成工作ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Class[] classes = new Class[]{interfaceRef};// 使用动态代理生成代理对象Object helloProxy = Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1.发现服务,从注册中心,寻找一个可用的服务// 传入服务的名字,返回ip+端口 (InetSocketAddress可以封装端口/ip/host name)InetSocketAddress address = registry.lookup(interfaceRef.getName());if (log.isInfoEnabled()){log.debug("服务调用方,发现了服务{}的可用主机{}", interfaceRef.getName(), address);}// 2.使用netty连接服务器,发送 调用的 服务名字+方法名字+参数列表,得到结果return null;}});return (T) helloProxy;
}public Registry getRegistry() {return registry;
}public void setRegistry(Registry registry) {this.registry = registry;
}
3.修改Registry接口,添加发现服务的接口
/*** 从注册中心拉取一个可用的服务* @param serviceName 服务名称* @return 服务的ip+端口*/
InetSocketAddress lookup(String serviceName);
4.修改ZookeeperRegistry部分代码,实现发现服务的业务逻
@Override
public InetSocketAddress lookup(String serviceName) {// 1.找到对应服务的节点String serviceNode = Constant.BASE_PROVIDERS_PATH + "/" + serviceName;// 2.从zk中获取它的子节点,List<String> children = ZookeeperUtils.getChildren(zooKeeper, serviceNode, null);// 获取所有的可用的服务列表List<InetSocketAddress> inetSocketAddressList = children.stream().map(ipString -> {String[] ipAndPort = ipString.split(":");String ip = ipAndPort[0];int port = Integer.valueOf(ipAndPort[1]);return new InetSocketAddress(ip, port);}).toList();if (inetSocketAddressList.size() == 0){throw new DiscoveryException("未发现任何可用的服务主机");}return inetSocketAddressList.get(0);
}
5.修改ZookeeperUtils部分代码,添加与实现获取子节点的方法
/*** 查询一个节点的子元素* @param zooKeeper* @param serviceNode 服务节点* @return 子元素列表*/
public static List<String> getChildren(ZooKeeper zooKeeper, String serviceNode, Watcher watcher) {try {return zooKeeper.getChildren(serviceNode, watcher);} catch (KeeperException | InterruptedException e) {log.error("获取节点{}的子元素时发生异常:{}", serviceNode, e);throw new ZookeeperException(e);}
}
相关文章:
手写RPC框架--4.服务注册
RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧) RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧) 服务注册 服务注册a.添加服务节点和主机节点b.抽象注册中心c.本地服务列表 服务注册 a.添加服务节点和主机节点 主要完成服务注册和发现的功能,其具体流程如下&…...
oracle 解锁表
操作的前提 用 sys 用户 以 SYSDBA 角色登录 第一种解锁方式 1.查询被锁的表 select object_name,machine,s.sid,s.serial# from v$locked_object l,dba_objects o ,v$session s where l.object_id o.object_id and l.session_ids.sid;2.查询那个session引起表被锁 sele…...
使用Dbeaver连接GaussDB
1.下载DBeaver,官网地址 2.安装软件,打开软件,点击数据库->驱动管理器,具体操作如下图: 3、选择新建后进行参数设置,如下图: 具体参数如下图 驱动名称: GS #随便定义 驱动类型&#…...
WSL使用技巧 / 虚拟机对比
WSL使用技巧 / 虚拟机对比 前言虚拟机比较VMware使用技巧WSL使用技巧官方文档工具安装WSL基本命令运行命令关闭卸载磁盘管理导入导出指定安装路径 前言 本文介绍了VMware和WSL的区别,并详细介绍了WSL的使用方法和技巧。 虚拟机比较 VMware 比较灵活,拥…...
vuex_cart案例
json-server使用 在目录下新建db文件夹>里面新建index.json index.json {"cart": [{"id": 100001,"name": "低帮城市休闲户外鞋天然牛皮COOLMAX纤维","price": 128,"count": 6,"thumb": "http…...
Linux系统的安装
文章目录 1 Linux介绍1.1 Linux是什么1.2 Linux的特点1.3 Linux的应用1.4 Linux的发行版本1.5 Linux的Shell 2 Linux安装2.1 安装方式2.2 什么是VMware2.3 VMware主要功能2.4 什么是CentOS2.5 VMware与CentOS与Linux的关系2.6 VMware安装CentOS的步骤 1 Linux介绍 1.1 Linux是…...
微服务设计和高并发实践
文章目录 1、微服务的设计原则1.1、服务拆分方法1.2、微服务的设计原则1.3、微服务架构 2、高并发系统的一些优化经验2.1、提高性能2.1.1、数据库优化2.1.2、使用缓存2.1.3、服务调用优化2.1.4、动静分离2.1.5、数据库读写分离 2.2、服务高可用2.2.1、限流和服务降级2.2.2、隔离…...
2023年高教社杯数学建模思路 - 案例:粒子群算法
文章目录 1 什么是粒子群算法?2 举个例子3 还是一个例子算法流程算法实现建模资料 # 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 什么是粒子群算法? 粒子群算法(Pa…...
Tomcat 集群介绍
一.Tomcat 集群介绍 在实际生产环境中,单台 Tomcat 服务器的负载能力或者说并发能力在四五百左右。大 部分情况下随着业务增长,访问量的增加(并发量不止四五百),单台 Tomcat 服务器是 无法承受的。这时就需要将多台 Tomcat 服务器组织起来&a…...
Windows右键添加用 IDEA 打开
1.安装IDEA时 安装时会有个选项来添加,如下: 勾选即可 2.修改注册表 安装时未勾选,可以把下面代码中程序路径改为自己的,保存为对应的 idea.reg文件,双击即可 Windows Registry Editor Version 5.00[HKEY_CLASSES…...
Golang 中return和defer执行先后顺序
先给出最终结论: 执行return语句 -> 执行defer函数 -> 函数返回 这里可能会有一个疑问, 执行return语句和函数返回难道不是一回事? Golang语言中函数的return不是原子操作,而是分为了两步: 返回值赋值真正函数返回 Gol…...
业务数据模拟/采集
业务数据模拟/采集 2.2 业务数据模拟 2.2.1 连接MySQL 通过MySQL可视化客户端连接数据库。2.2.2 建表语句 1)通过SQLyog创建数据库2)设置数据库名称为gmall,编码为utf-8,排序规则为utf8_general_ci3)导入数据库结构脚本…...
qt day 5
实现局域网的网络聊天室功能 1>服务器代码 --------------------------------------------------------------- widget.h --------------------------------------------------------------- #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMes…...
Java设计模式之适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子࿰…...
每天一个工业通信协议(3)2023.8.29 (DAP接口)
文章目录 参考文献1.DAP接口介绍2.DAP接口的2/3pin3.一种DAP接口方案应用的说明,通过两步初始化把JTAG接口变成DAP接口使用4.DAP接口的协议4.1 DAP电报的分类(只用JTAG类电报)4.2 电报格式4.3 DAP有限状态机参考文献 李婧. DAP模块验证组件系统级开发和实现[D]. 陕西:西安电…...
如何将Word转成PDF?试一下这个转换方法
Word转成PDF是现代办公中常见的需求,它可以确保文件的格式和内容在不同平台上保持一致,并且更加方便共享和打印。在这个数字化时代,我们经常需要将Word文档转换为PDF格式,无论是个人用户还是商务用户都会遇到这样的需求。那么如何…...
成都睿趣科技:现在开一家抖音小店还来得及吗
随着社交媒体的迅猛发展,抖音已经成为了一个全球范围内广受欢迎的社交平台。在这个短视频应用上,人们分享着各种各样的内容,从搞笑段子到美食教程,再到时尚搭配和手工艺品制作。随着用户数量的不断增长,很多人都在思考…...
原型链中:为什么Function.proto==Function.prototype?
背景: 在 JavaScript 中,每个函数(包括构造函数)都是一个对象,而对象都有一个 __proto__ 属性,指向它们的原型。当你创建一个函数时,JavaScript 引擎会自动为该函数创建一个原型对象,并将其关联…...
原生js实现轮播图及无缝滚动
我这里主要说轮播图和无缝滚动的实现思路,就采用最简单的轮播图了,当然实现的思路有很多种,我这也只是其中一种。 简单轮播图的大概结构是这样的,中间是图片,二边是箭头可以用来切换图片,下面的小圆点也可以…...
MP中的字段还可以利用函数来查询拼接sql
//根据value查询GetMapping("getTest")public List<HashMap> getTest() {QueryWrapper<TTest> queryWrapper new QueryWrapper<>();queryWrapper.eq("substr(name,1,2)","99999");List<TTest> list1 testService.list…...
JoyCon-Driver:Windows平台上的Switch手柄完美解决方案
JoyCon-Driver:Windows平台上的Switch手柄完美解决方案 【免费下载链接】JoyCon-Driver A vJoy feeder for the Nintendo Switch JoyCons and Pro Controller 项目地址: https://gitcode.com/gh_mirrors/jo/JoyCon-Driver 还在为Nintendo Switch JoyCon控制器…...
Perplexity课程查询功能全链路拆解(从API底层到UI交互逻辑)
更多请点击: https://kaifayun.com 第一章:Perplexity课程查询功能全链路概览 Perplexity 的课程查询功能并非单一接口调用,而是一套覆盖用户意图理解、多源数据协同检索、结构化结果生成与实时反馈优化的端到端系统。其核心目标是将自然语言…...
Python(while循环)
目录 1.while 循环的基本概念 1.1 语法格式 1.2 最简单的示例 1.3 while 与 for 的对比 2. 代码执行顺序详解 3. 无限循环及其控制 3.1 无限循环的基本写法 3.2 避免无限循环的常见错误 4. break、continue 与 else 4.1 break:提前终止整个循环 4.2 cont…...
Agent+可穿戴设备:心率、睡眠、活动数据如何变成有价值的健康建议
可穿戴设备每天都会产生心率、睡眠、步数、活动强度等数据,但开发者真正要解决的不是“采集更多指标”,而是把这些指标转成可解释、可追踪、可配置的健康提示。本文从工程角度搭建一个简化版 Agent 服务,演示如何完成数据接入、趋势计算、规则…...
从物理模型到代码:用MATLAB类轻松构建你的第一个仿真对象(比如弹簧振子)
从物理模型到代码:用MATLAB类轻松构建你的第一个仿真对象 理工科研究者常面临一个核心挑战:如何将复杂的物理系统转化为可计算的数学模型?以弹簧振子为例,这个看似简单的力学系统蕴含着丰富的物理规律。传统脚本式编程往往导致代码…...
Claude Code 可观测性工程爆火全解析:AI Agent 日志、遥测、追踪、成本监控与安全治理一次讲透
导语:AI Agent 真正进入生产环境后,最重要的问题不再是“能不能跑”,而是“跑得怎么样、哪里慢、哪里错、成本多少、有没有泄露、能不能恢复”。可观测性工程,就是把这些问题变成可度量、可告警、可追踪、可治理的系统能力。一图看…...
RT-Thread实战:基于STM32与软件I2C的IST8310磁力计驱动开发与模块化设计
1. 项目概述与设计思路在RoboMaster这类对实时性和可靠性要求极高的机器人竞赛中,电控系统的稳定与高效是取胜的基石。很多队伍在初期会选择裸机开发,但随着功能模块的增加,任务调度、资源管理、驱动适配等问题会迅速让代码变得臃肿且难以维护…...
Adafruit Fritzing元件库安装与使用指南:提升硬件设计效率
1. 项目概述:为什么你需要Adafruit Fritzing元件库?如果你玩过Arduino或者树莓派,肯定对Adafruit这家公司不陌生。他们出品的各种传感器、显示屏和扩展板,几乎成了开源硬件项目的“标准件”。但每次在Fritzing里画电路图ÿ…...
避坑指南:在Ubuntu 22.04上用Anaconda配置Vision-Mamba环境,解决‘bimamba_type‘报错
深度避坑:Ubuntu 22.04下Vision-Mamba环境配置全攻略 在深度学习项目部署过程中,环境配置往往是第一个拦路虎。最近在配置Vision-Mamba环境时,我遇到了几个令人头疼的问题,特别是那个让人摸不着头脑的bimamba_type报错。经过一番折…...
RK3568开发板TB-96AI-3568CE深度评测:从核心接口到AI应用实战
1. 从芯片到板卡:TB-96AI-3568CE的设计哲学当一块芯片从图纸走向现实,成为一块可以握在手中的开发板时,这中间的路程远不止是简单的引脚引出和电源接通。我接触过不少基于RK3568的方案,但拿到贝启科技这块TB-96AI-3568CE时&#x…...
