40分钟学 Go 语言高并发:服务注册与发现

服务注册与发现

一、系统架构设计

让我们先通过流程图了解服务注册与发现的整体架构:
在这里插入图片描述

二、核心组件实现

1. 服务注册中心

package discoveryimport ("context""sync""time"
)// ServiceInstance 服务实例
type ServiceInstance struct {ID          string            `json:"id"`Name        string            `json:"name"`Version     string            `json:"version"`Endpoints   []string          `json:"endpoints"`Metadata    map[string]string `json:"metadata"`Status      string            `json:"status"`LastHeartbeat time.Time       `json:"last_heartbeat"`
}// Registry 注册中心
type Registry struct {services  map[string]map[string]*ServiceInstance // serviceName -> instanceID -> instancemutex     sync.RWMutexwatchers  map[string][]chan *ServiceInstance
}// NewRegistry 创建注册中心
func NewRegistry() *Registry {r := &Registry{services: make(map[string]map[string]*ServiceInstance),watchers: make(map[string][]chan *ServiceInstance),}go r.checkHealth()return r
}// Register 注册服务实例
func (r *Registry) Register(ctx context.Context, instance *ServiceInstance) error {r.mutex.Lock()defer r.mutex.Unlock()if _, exists := r.services[instance.Name]; !exists {r.services[instance.Name] = make(map[string]*ServiceInstance)}instance.LastHeartbeat = time.Now()instance.Status = "UP"r.services[instance.Name][instance.ID] = instance// 通知观察者r.notifyWatchers(instance.Name, instance)return nil
}// Deregister 注销服务实例
func (r *Registry) Deregister(ctx context.Context, serviceName, instanceID string) error {r.mutex.Lock()defer r.mutex.Unlock()if instances, exists := r.services[serviceName]; exists {if instance, ok := instances[instanceID]; ok {instance.Status = "DOWN"delete(instances, instanceID)// 如果服务没有实例了,删除服务if len(instances) == 0 {delete(r.services, serviceName)}return nil}}return nil
}// GetService 获取服务实例列表
func (r *Registry) GetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {r.mutex.RLock()defer r.mutex.RUnlock()if instances, exists := r.services[serviceName]; exists {result := make([]*ServiceInstance, 0, len(instances))for _, instance := range instances {if instance.Status == "UP" {result = append(result, instance)}}return result, nil}return nil, nil
}// Watch 监听服务变化
func (r *Registry) Watch(ctx context.Context, serviceName string) (<-chan *ServiceInstance, error) {r.mutex.Lock()defer r.mutex.Unlock()ch := make(chan *ServiceInstance, 10)if _, exists := r.watchers[serviceName]; !exists {r.watchers[serviceName] = make([]chan *ServiceInstance, 0)}r.watchers[serviceName] = append(r.watchers[serviceName], ch)return ch, nil
}// Heartbeat 服务心跳
func (r *Registry) Heartbeat(ctx context.Context, serviceName, instanceID string) error {r.mutex.Lock()defer r.mutex.Unlock()if instances, exists := r.services[serviceName]; exists {if instance, ok := instances[instanceID]; ok {instance.LastHeartbeat = time.Now()instance.Status = "UP"return nil}}return nil
}// checkHealth 健康检查
func (r *Registry) checkHealth() {ticker := time.NewTicker(10 * time.Second)for range ticker.C {r.mutex.Lock()now := time.Now()for serviceName, instances := range r.services {for id, instance := range instances {if now.Sub(instance.LastHeartbeat) > 30*time.Second {instance.Status = "DOWN"delete(instances, id)if len(instances) == 0 {delete(r.services, serviceName)}}}}r.mutex.Unlock()}
}// notifyWatchers 通知观察者
func (r *Registry) notifyWatchers(serviceName string, instance *ServiceInstance) {if watchers, exists := r.watchers[serviceName]; exists {for _, ch := range watchers {select {case ch <- instance:default:// 如果channel已满,跳过}}}
}

2. 服务发现实现

package discoveryimport ("context""errors""sync""time"
)// ServiceDiscovery 服务发现
type ServiceDiscovery struct {registry       *RegistrylocalCache     map[string][]*ServiceInstancecacheMutex     sync.RWMutexupdateInterval time.Duration
}// NewServiceDiscovery 创建服务发现实例
func NewServiceDiscovery(registry *Registry, updateInterval time.Duration) *ServiceDiscovery {sd := &ServiceDiscovery{registry:       registry,localCache:     make(map[string][]*ServiceInstance),updateInterval: updateInterval,}go sd.startCacheUpdate()return sd
}// GetService 获取服务实例
func (sd *ServiceDiscovery) GetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {// 首先检查本地缓存sd.cacheMutex.RLock()if instances, exists := sd.localCache[serviceName]; exists && len(instances) > 0 {sd.cacheMutex.RUnlock()return instances, nil}sd.cacheMutex.RUnlock()// 如果本地缓存没有,从注册中心获取instances, err := sd.registry.GetService(ctx, serviceName)if err != nil {return nil, err}// 更新本地缓存if len(instances) > 0 {sd.cacheMutex.Lock()sd.localCache[serviceName] = instancessd.cacheMutex.Unlock()}return instances, nil
}// startCacheUpdate 开始缓存更新
func (sd *ServiceDiscovery) startCacheUpdate() {ticker := time.NewTicker(sd.updateInterval)for range ticker.C {sd.updateCache()}
}// updateCache 更新缓存
func (sd *ServiceDiscovery) updateCache() {sd.cacheMutex.Lock()defer sd.cacheMutex.Unlock()ctx := context.Background()for serviceName := range sd.localCache {instances, err := sd.registry.GetService(ctx, serviceName)if err == nil && len(instances) > 0 {sd.localCache[serviceName] = instances}}
}// WatchService 监听服务变化
func (sd *ServiceDiscovery) WatchService(ctx context.Context, serviceName string) error {instanceCh, err := sd.registry.Watch(ctx, serviceName)if err != nil {return err}go func() {for {select {case instance := <-instanceCh:sd.handleInstanceUpdate(serviceName, instance)case <-ctx.Done():return}}}()return nil
}// handleInstanceUpdate 处理实例更新
func (sd *ServiceDiscovery) handleInstanceUpdate(serviceName string, instance *ServiceInstance) {sd.cacheMutex.Lock()defer sd.cacheMutex.Unlock()instances := sd.localCache[serviceName]updated := false// 更新或添加实例for i, inst := range instances {if inst.ID == instance.ID {instances[i] = instanceupdated = truebreak}}if !updated {instances = append(instances, instance)}// 过滤掉已下线的实例activeInstances := make([]*ServiceInstance, 0)for _, inst := range instances {if inst.Status == "UP" {activeInstances = append(activeInstances, inst)}}sd.localCache[serviceName] = activeInstances
}

3. 负载均衡实现

package discoveryimport ("context""errors""math/rand""sync""sync/atomic"
)// LoadBalancer 负载均衡接口
type LoadBalancer interface {Select(ctx context.Context, instances []*ServiceInstance) (*ServiceInstance, error)
}// RoundRobinLoadBalancer 轮询负载均衡器
type RoundRobinLoadBalancer struct {counter uint64
}// NewRoundRobinLoadBalancer 创建轮询负载均衡器
func NewRoundRobinLoadBalancer() *RoundRobinLoadBalancer {return &RoundRobinLoadBalancer{}
}// Select 选择服务实例
func (lb *RoundRobinLoadBalancer) Select(ctx context.Context, instances []*ServiceInstance) (*ServiceInstance, error) {if len(instances) == 0 {return nil, errors.New("no available instances")}count := atomic.AddUint64(&lb.counter, 1)return instances[int(count)%len(instances)], nil
}// RandomLoadBalancer 随机负载均衡器
type RandomLoadBalancer struct {}// NewRandomLoadBalancer 创建随机负载均衡器
func NewRandomLoadBalancer() *RandomLoadBalancer {return &RandomLoadBalancer{}
}// Select 选择服务实例
func (lb *RandomLoadBalancer) Select(ctx context.Context, instances []*ServiceInstance) (*ServiceInstance, error) {if len(instances) == 0 {return nil, errors.New("no available instances")}return instances[rand.Intn(len(instances))], nil
}// WeightedRoundRobinLoadBalancer 加权轮询负载均衡器
type WeightedRoundRobinLoadBalancer struct {mutex    sync.Mutexweights  map[string]intcurrent  map[string]int
}// NewWeightedRoundRobinLoadBalancer 创建加权轮询负载均衡器
func NewWeightedRoundRobinLoadBalancer() *WeightedRoundRobinLoadBalancer {return &WeightedRoundRobinLoadBalancer{weights: make(map[string]int),current: make(map[string]int),}
}// UpdateWeight 更新权重
func (lb *WeightedRoundRobinLoadBalancer) UpdateWeight(instanceID string, weight int) {lb.mutex.Lock()defer lb.mutex.Unlock()lb.weights[instanceID] = weight
}// Select 选择服务实例
func (lb *WeightedRoundRobinLoadBalancer) Select(ctx context.Context, instances []*ServiceInstance) (*ServiceInstance, error) {if len(instances) == 0 {return nil, errors.New("no available instances")}lb.mutex.Lock()defer lb.mutex.Unlock()var total intfor _, instance := range instances {weight := lb.weights[instance.ID]if weight <= 0 {weight = 1}lb.current[instance.ID] += weighttotal += lb.current[instance.ID]}max := 0var selected *ServiceInstancefor _, instance := range instances {current := lb.current[instance.ID]if current > max {max = currentselected = instance}}// 更新current值if selected != nil {weight := lb.weights[selected.ID]if weight <= 0 {weight = 1}lb.current[selected.ID] -= totalreturn selected, nil}return nil, errors.New("failed to select instance")
}// ConsistentHashLoadBalancer 一致性哈希负载均衡器
type ConsistentHashLoadBalancer struct {hashRing *ConsistentHashmutex    sync.RWMutex
}// NewConsistentHashLoadBalancer 创建一致性哈希负载均衡器
func NewConsistentHashLoadBalancer(replicas int) *ConsistentHashLoadBalancer {return &ConsistentHashLoadBalancer{hashRing: NewConsistentHash(replicas),}
}// Select 选择服务实例
func (lb *ConsistentHashLoadBalancer) Select(ctx context.Context, instances []*ServiceInstance, key string) (*ServiceInstance, error) {if len(instances) == 0 {return nil, errors.New("no available instances")}lb.mutex.Lock()// 更新哈希环lb.hashRing.Clear()for _, instance := range instances {lb.hashRing.Add(instance.ID)}lb.mutex.Unlock()// 根据key选择节点targetID := lb.hashRing.Get(key)for _, instance := range instances {if instance.ID == targetID {return instance, nil}}return nil, errors.New("failed to select instance")
}// LoadBalancerClient 负载均衡客户端
type LoadBalancerClient struct {discovery *ServiceDiscoverybalancer  LoadBalancer
}// NewLoadBalancerClient 创建负载均衡客户端
func NewLoadBalancerClient(discovery *ServiceDiscovery, balancer LoadBalancer) *LoadBalancerClient {return &LoadBalancerClient{discovery: discovery,balancer:  balancer,}
}// Call 调用服务
func (c *LoadBalancerClient) Call(ctx context.Context, serviceName string) (*ServiceInstance, error) {// 获取服务实例列表instances, err := c.discovery.GetService(ctx, serviceName)if err != nil {return nil, err}// 选择实例return c.balancer.Select(ctx, instances)
}

三、健康检查实现

package discoveryimport ("context""fmt""net/http""sync""time"
)// HealthChecker 健康检查器
type HealthChecker struct {registry        *RegistrycheckInterval  time.Durationtimeout        time.Durationendpoints      map[string]string // instanceID -> health check endpointmutex          sync.RWMutex
}// NewHealthChecker 创建健康检查器
func NewHealthChecker(registry *Registry, checkInterval, timeout time.Duration) *HealthChecker {checker := &HealthChecker{registry:      registry,checkInterval: checkInterval,timeout:      timeout,endpoints:    make(map[string]string),}go checker.start()return checker
}// RegisterEndpoint 注册健康检查端点
func (hc *HealthChecker) RegisterEndpoint(instanceID, endpoint string) {hc.mutex.Lock()defer hc.mutex.Unlock()hc.endpoints[instanceID] = endpoint
}// UnregisterEndpoint 注销健康检查端点
func (hc *HealthChecker) UnregisterEndpoint(instanceID string) {hc.mutex.Lock()defer hc.mutex.Unlock()delete(hc.endpoints, instanceID)
}// start 启动健康检查
func (hc *HealthChecker) start() {ticker := time.NewTicker(hc.checkInterval)defer ticker.Stop()for range ticker.C {hc.checkAll()}
}// checkAll 检查所有实例
func (hc *HealthChecker) checkAll() {hc.mutex.RLock()endpoints := make(map[string]string)for id, endpoint := range hc.endpoints {endpoints[id] = endpoint}hc.mutex.RUnlock()var wg sync.WaitGroupfor instanceID, endpoint := range endpoints {wg.Add(1)go func(id, ep string) {defer wg.Done()hc.checkInstance(id, ep)}(instanceID, endpoint)}wg.Wait()
}// checkInstance 检查单个实例
func (hc *HealthChecker) checkInstance(instanceID, endpoint string) {ctx, cancel := context.WithTimeout(context.Background(), hc.timeout)defer cancel()status := hc.doHealthCheck(ctx, endpoint)// 更新实例状态if status {hc.registry.Heartbeat(ctx, "", instanceID) // 服务名为空,因为我们只需要instanceID}
}// doHealthCheck 执行健康检查
func (hc *HealthChecker) doHealthCheck(ctx context.Context, endpoint string) bool {req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)if err != nil {return false}client := &http.Client{Timeout: hc.timeout,}resp, err := client.Do(req)if err != nil {return false}defer resp.Body.Close()return resp.StatusCode == http.StatusOK
}// CustomHealthCheck 自定义健康检查接口
type CustomHealthCheck interface {Check(ctx context.Context) (bool, error)
}// CustomHealthChecker 自定义健康检查器
type CustomHealthChecker struct {checker CustomHealthCheckmutex   sync.RWMutex
}// NewCustomHealthChecker 创建自定义健康检查器
func NewCustomHealthChecker(checker CustomHealthCheck) *CustomHealthChecker {return &CustomHealthChecker{checker: checker,}
}// Check 执行健康检查
func (chc *CustomHealthChecker) Check(ctx context.Context) (bool, error) {chc.mutex.RLock()defer chc.mutex.RUnlock()return chc.checker.Check(ctx)
}// HealthCheckServer 健康检查服务器
type HealthCheckServer struct {port    inthandler http.Handlerserver  *http.Server
}// NewHealthCheckServer 创建健康检查服务器
func NewHealthCheckServer(port int, handler http.Handler) *HealthCheckServer {return &HealthCheckServer{port:    port,handler: handler,}
}// Start 启动服务器
func (s *HealthCheckServer) Start() error {s.server = &http.Server{Addr:    fmt.Sprintf(":%d", s.port),Handler: s.handler,}return s.server.ListenAndServe()
}// Stop 停止服务器
func (s *HealthCheckServer) Stop(ctx context.Context) error {return s.server.Shutdown(ctx)
}

四、系统配置和策略

1. 服务注册配置

配置项说明默认值推荐值
心跳间隔服务实例向注册中心发送心跳的间隔10s5-15s
注册超时服务注册的超时时间5s3-10s
实例缓存本地缓存服务实例信息的时间60s30-120s
重试次数注册失败时的重试次数33-5

2. 负载均衡策略对比

策略优点缺点适用场景
轮询实现简单,请求分布均匀不考虑服务器能力差异服务器性能相近
随机实现简单,负载较均衡可能造成不均衡大规模集群
加权轮询考虑服务器能力差异权重设置需要经验服务器性能差异大
一致性哈希请求分布稳定实现复杂需要会话保持

五、服务注册流程图

在这里插入图片描述

六、最佳实践建议

1. 服务注册

  1. 合理设置心跳间隔
  2. 实现优雅关闭
  3. 添加重试机制
  4. 使用服务分组

2. 服务发现

  1. 使用本地缓存
  2. 实现故障转移
  3. 监控服务健康
  4. 定期清理无效实例

3. 负载均衡

  1. 选择合适的策略
  2. 动态调整权重
  3. 实现熔断机制
  4. 监控服务质量

七、常见问题处理

1. 注册中心故障

  • 问题描述:注册中心不可用
  • 解决方案:
    1. 使用注册中心集群
    2. 本地缓存备份
    3. 定期同步数据
    4. 自动故障转移

2. 服务实例异常

  • 问题描述:服务实例不稳定
  • 解决方案:
    1. 实现健康检查
    2. 自动下线机制
    3. 故障恢复策略
    4. 监控告警系统

3. 网络分区

  • 问题描述:网络故障导致分区
  • 解决方案:
    1. 多数据中心部署
    2. 网络状态监控
    3. 自动切换机制
    4. 数据一致性保证

八、监控指标

1. 关键指标

  1. 服务注册延迟
  2. 服务发现延迟
  3. 心跳成功率
  4. 服务可用性
  5. 负载均衡效果
  6. 健康检查成功率

2. 告警阈值

  1. 服务注册延迟 > 3s
  2. 服务发现延迟 > 1s
  3. 心跳成功率 < 95%
  4. 服务可用性 < 99.9%
  5. 负载不均衡度 > 20%
  6. 健康检查失败率 > 5%

九、扩展建议

  1. 开发阶段:

    • 完善的单元测试
    • 模拟各种故障场景
    • 性能压力测试
    • 可观测性设计
  2. 运维阶段:

    • 监控系统部署
    • 告警规则配置
    • 故障演练
    • 容量规划
  3. 优化方向:

    • 性能优化
    • 可靠性提升
    • 可扩展性增强
    • 运维自动化

怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

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

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

相关文章

ESP8266作为TCP客户端或者服务器使用

ESP8266模块&#xff0c;STA模式&#xff08;与手机搭建TCP通讯&#xff0c;EPS8266为服务端&#xff09;_esp8266作为station-CSDN博客 ESP8266模块&#xff0c;STA模式&#xff08;与电脑搭建TCP通讯&#xff0c;ESP8266 为客户端&#xff09;_esp8266 sta 连接tcp-CSDN博客…

基于DFA算法实现敏感词过滤

1、什么是DFA&#xff1f; DFA&#xff08;Deterministic Finite Automaton&#xff09;&#xff0c;即确定有穷自动机。其特征为&#xff1a;有一个有限状 态集合和一些从一个状态通向另一个状态的边&#xff0c;每条边上标记有一个符号&#xff0c;其中一个状态是 初态&#…

详解MySQL安装

目录 Ubantu 1. 使⽤apt安装MySQL 2.查看MySQL状态 3. MySQL 安装安全设置 4.设置密码 卸载MySQL Centos 1. 确认当前的系统版本 2.下载MySQL源 3.安装MySQL 4.启动mysqld 5.查看MySQL状态 6.设置开机自启动 7.查看MySQL密码&#xff0c;并登录 8.修改密码 Ubant…

Android 实现中英文切换

在开发海外项目的时候&#xff0c;需要实现app内部的中英文切换功能&#xff0c;所有的英文都是内置的&#xff0c;整体思路为&#xff1a; 创建一个sp对象&#xff0c;存储当前系统的语言类型&#xff0c;然后在BaseActivity中对语言进行判断&#xff1b; //公共Activitypubl…

使用uniapp开发小程序场景:在百度地图上调用接口返回的设备相关信息并展示

首先在百度地图开发者平台注册微信小程序开发密钥下载百度地图SDK-bmap-wx.min.js,下载地址在项目入口index.html页面进行引入页面中进行调用&#xff0c;代码示例如下<map id"map" longitude"108.95" latitude"34.34" scale"3" :m…

如何使用brew安装phpredis扩展?

如何使用brew安装phpredis扩展&#xff1f; phpredis扩展是一个用于PHP语言的Redis客户端扩展&#xff0c;它提供了一组PHP函数&#xff0c;用于与Redis服务器进行交互。 1、cd到php某一版本的bin下 /usr/local/opt/php8.1/bin 2、下载 phpredis git clone https://githu…

【Vulkan入门】01-列举物理设备

目录 先叨叨git信息主要逻辑VulkanEnvEnumeratePhysicalDevices()PrintPhysicalDevices() 编译并运行程序 先叨叨 上一篇已经创建了VkInstance&#xff0c;本篇我们问问VkInstance&#xff0c;在当前平台上有多少个支持Vulkan的物理设备。 git信息 repository: https://gite…

写NFC标签支持Android安卓Ohos纯血鸿蒙唤醒微信小程序

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.52de2c1b8bEEGz&ftt&id61539185785 Python languagecodestr "en".encode(gbk) titlestrself.lineEdit_title.text().strip().encode(gbk) uriheaderindex sel…

51c自动驾驶~合集39

我自己的原文哦~ https://blog.51cto.com/whaosoft/12707676 #DiffusionDrive 大幅超越所有SOTA&#xff01;地平线DiffusionDrive&#xff1a;生成式方案或将重塑端到端格局&#xff1f; 近年来&#xff0c;由于感知模型的性能持续进步&#xff0c;端到端自动驾驶受到了来…

沃德云商协系统微信小程序PHP+Uniapp

“多组织”的云服务平台&#xff0c;打造总商会、总协会、总校友会、工商联等多组织无障碍沟通合作平台&#xff0c;让各大分会、各大分校友会、分组织实现轻松管理&#xff0c;线上宣传展示、商机挖掘、会员管理、会员服务、跨界交流等, 借助沃德云商协平台系统&#xff0c;让…

网页设计--axios作业

根据以下mock地址中的json数据&#xff0c;使用axios异步方式获取并显示在页面中。 https://apifoxmock.com/m1/3761592-3393136-default/peotfindAll?apifoxApiId171582689 {"code": 1,"msg": "success","data": [{"id": …

【uni-app 微信小程序】新版本发布提示用户进行更新

知识准备 uni.getUpdateManager文档介绍 不支持APP与H5&#xff0c;所以在使用的时候要做好平台类型的判断&#xff0c;如何判断&#xff0c;参考条件编译处理多端差异 代码参考 export const updateApp () > {const updateManager uni.getUpdateManager()updateManag…

ip地址显示本地局域网什么意思?ip地址冲突怎么解决

在日常使用网络的过程中&#xff0c;我们可能会遇到IP地址显示“本地局域网”的情况&#xff0c;同时&#xff0c;局域网内IP地址冲突也是一个常见且令人头疼的问题。本文将首先解释IP地址显示本地局域网的含义&#xff0c;随后详细探讨局域网IP地址冲突的解决方法&#xff0c;…

超清4K视频素材哪里找?优质下载资源网站分享

我是你们的自媒体UP主小李。现在是高清、4K视频大行其道的时代&#xff0c;想要制作出吸引眼球的优质内容&#xff0c;超清4K视频素材必不可少。今天就为大家分享几个宝藏网站&#xff0c;让你的视频创作更轻松、更出彩&#xff01; 蛙学网 首先推荐 蛙学网&#xff0c;这是国内…

Qt Qtablewidget 标题 QHeaderView 增加可选框 QcheckBox

创建自定义QHeaderView #pragma once#include <QObject> #include <QHeaderView> #include <QPainter> #include <QMouseEvent>class SSHeaderView : public QHeaderView {Q_OBJECTprivate:bool isChecked;int m_checkColIdx; public:SSHeaderView(i…

关于Chrome自动同步书签的解决办法

前言 并不一定适用所有用户&#xff0c; 目前我在网上搜集了一些资料&#xff0c;也做了一些尝试。 就我个人总结的经验来讲&#xff0c;分享大家以下几种办法&#xff1a; 1.书签同步插件 点击如下&#x1f517;&#xff1a; Chrome书签同步https://bm.famend.cn/ …

MFC扩展库BCGControlBar Pro v36.0新版亮点:黑色主题中的自动反转图标

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v36.0已全新发布了&#xff0c;这个版本在黑暗主题中添加自动图标反转、新增一个全新的S…

调用大模型api 批量处理图像 保存到excel

最近需要调用大模型&#xff0c;并将结果保存到excel中&#xff0c;效果如下&#xff1a; 代码&#xff1a; import base64 from zhipuai import ZhipuAI import os import pandas as pd from openpyxl import Workbook from openpyxl.drawing.image import Image from io i…

debian ubuntu armbian部署asp.net core 项目 开机自启动

我本地的环境是 rk3399机器&#xff0c;安装armbian系统。 1.安装.net core 组件 sudo apt-get update && \sudo apt-get install -y dotnet-sdk-8.0或者安装运行库&#xff0c;但无法生成编译项目 sudo apt-get update && \sudo apt-get install -y aspnet…

声音克隆GPT-SoVITS

作者&#xff1a;吴业亮 博客&#xff1a;wuyeliang.blog.csdn.net 一、原理介绍 GPT-SoVITS&#xff0c;作为一款结合了GPT&#xff08;生成预训练模型&#xff09;和SoVITS&#xff08;基于变分信息瓶颈技术的歌声转换&#xff09;的创新工具&#xff0c;正在声音克隆领域掀…