Net Core Ocelot+Consul实现网关、服务注册、服务发现

news2024/11/27 16:49:23

什么是Ocelot?

Ocelot是一个开源的ASP.NET Core微服务网关,它提供了API网关所需的所有功能,如路由、认证、限流、监控等。

Ocelot是一个简单、灵活且功能强大的API网关,它可以与现有的服务集成,并帮助您保护、监控和扩展您的微服务。

以下是Ocelot的一些主要功能:

  1. 路由管理:Ocelot允许您定义路由规则,将请求路由到正确的微服务。
  2. 认证和授权:Ocelot支持多种认证机制,如JWT、OAuth等,并允许您定义访问控制策略,确保只有授权的用户才能访问特定的API。
  3. 限流和速率限制:Ocelot提供了一些内置的限流和速率限制功能,以确保您的服务不会受到过度的请求压力。
  4. 监控和日志:Ocelot可以收集和显示各种度量指标,帮助您了解您的服务的性能和行为。此外,它还可以将日志记录到各种日志源,以便您进行分析和故障排除。
  5. 集成:Ocelot可以与现有的服务集成,包括Kubernetes、Consul等。
  6. 易于扩展:Ocelot的设计使其易于扩展,您可以编写自己的中间件来处理特定的逻辑,例如修改请求或响应、添加自定义的认证机制等。
  7. 可扩展的配置:Ocelot使用JSON配置文件进行配置,这意味着您可以轻松地根据需要进行配置更改,而无需重新编译代码。

总之,Ocelot是一个功能强大且易于使用的API网关,可以帮助您保护、监控和扩展您的微服务。

Asp .Net Core 集成 Ocelot

要在ASP.NET Core中集成Ocelot,您可以按照以下步骤进行操作:

  1. 安装Ocelot NuGet包:
    在您的ASP.NET Core项目中,打开终端或NuGet包管理器控制台,并运行以下命令来安装Ocelot的NuGet包:
dotnet add package Ocelot
  1. 添加Ocelot配置文件:
{  
  "Routes": [ //这里注意一下版本(旧版本用ReRoutes)
    {
      "DownstreamPathTemplate": "/api/{controller}", //下游路径模板
      "DownstreamScheme": "http", //下游方案
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "localhost",
      //    "Port": "5014"
      //  }
      //], //下游主机和端口
      "UpstreamPathTemplate": "/api/product/{controller}", //上游路径模板
      "UpstreamHttpMethod": [], //上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法
      "ServiceName": "api-product-service", //请求服务名称
      "LoadBalancerOptions": {
        "Type": "LeastConnection" //负载均衡算法:目前 Ocelot 有RoundRobin 和LeastConnection算法
      }
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "127.0.0.1", //你的Consul的ip地址
      "Port": 8500, //你的Consul的端口
      "Type": "Consul" //类型
      //"ConfigurationKey": "Consul" //指定配置键,键入您的配置
    }
  }
}
  1. 配置Ocelot服务:
builder.Services.AddOcelot();

Configure方法中配置请求管道并添加Ocelot中间件:

app.UseOcelot().Wait();

Ocelot 集成 Consul

要将Ocelot集成到Consul中,您可以按照以下步骤进行操作:

  1. 安装依赖项:
    在您的ASP.NET Core项目中,安装Ocelot和Consul的NuGet包。您可以使用以下命令来安装这些包:
dotnet add package Ocelot.Provider.Consul
  1. 配置Ocelot:
    在Ocelot的配置中,您需要指定Consul作为服务发现和配置的提供者。在Ocelot的配置文件(例如appsettings.json)中,添加以下内容:
{  
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "127.0.0.1", //你的Consul的ip地址
      "Port": 8500, //你的Consul的端口
      "Type": "Consul" //类型
      //"ConfigurationKey": "Consul" //指定配置键,键入您的配置
    }
  } 
}

确保将ConsulHostConsulPort设置为Consul服务器的正确地址和端口。

  1. 启动Ocelot:
    在您的ASP.NET Core应用程序中启动Ocelot。您可以在Startup.cs文件中添加以下代码:
builder.Services.AddOcelot().AddConsul(); 

全部代码

Consul相关代码

namespace MCode.Common.Extensions.Consul
{
    public class ConsulOptions
    {
        /// <summary>
        /// 当前应用IP
        /// </summary>
        public string IP { get; set; }

        /// <summary>
        /// 当前应用端口
        /// </summary>
        public int Port { get; set; }

        /// <summary>
        /// 当前服务名称
        /// </summary>
        public string ServiceName { get; set; }

        /// <summary>
        /// Consul集群IP
        /// </summary>
        public string ConsulIP { get; set; }

        /// <summary>
        /// Consul集群端口
        /// </summary>
        public int ConsulPort { get; set; }

        /// <summary>
        /// 权重
        /// </summary>
        public int? Weight { get; set; }
    }
}
using Consul.AspNetCore;
using Consul;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Builder;

namespace MCode.Common.Extensions.Consul
{
    public static class ConsulServiceExtensions
    {
        /// <summary>
        /// 向容器中添加Consul必要的依赖注入
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static IServiceCollection AddMCodeConsul(this IServiceCollection services)
        {
            var configuration = services.BuildServiceProvider().GetRequiredService<IConfiguration>();
            // 配置consul服务注册信息
            var consulOptions = configuration.GetSection("Consul").Get<ConsulOptions>();
            // 通过consul提供的注入方式注册consulClient
            services.AddConsul(options => options.Address = new Uri($"http://{consulOptions.ConsulIP}:{consulOptions.ConsulPort}"));

            // 通过consul提供的注入方式进行服务注册
            var httpCheck = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
                HTTP = $"http://{consulOptions.IP}:{consulOptions.Port}/health",//健康检查地址
                Timeout = TimeSpan.FromSeconds(10)
            };

            // Register service with consul
            services.AddConsulServiceRegistration(options =>
            {
                options.Checks = new[] { httpCheck };
                options.ID = Guid.NewGuid().ToString();
                options.Name = consulOptions.ServiceName;
                options.Address = consulOptions.IP;
                options.Port = consulOptions.Port;
                options.Meta = new Dictionary<string, string>() { { "Weight", consulOptions.Weight.HasValue ? consulOptions.Weight.Value.ToString() : "1" } };
                options.Tags = new[] { $"urlprefix-/{consulOptions.ServiceName}" }; //添加
            });

            return services;
        }

        /// <summary>
        /// 配置Consul检查服务
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseConsulCheckService(this IApplicationBuilder app)
        {
            app.Map("/health", app =>
            {
                app.Run(async context =>
                {
                    await Task.Run(() => context.Response.StatusCode = 200);
                });
            });

            return app;
        }
    }
}

Cors

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Cors
{
    public static class CorsServiceExtensions
    {
        private readonly static string PolicyName = "MCodeCors";

        /// <summary>
        /// 添加跨域
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <returns></returns>
        public static IServiceCollection AddMCodeCors(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));
            //origin microsoft.aspnetcore.cors      
            return services.AddCors(options =>
            {
                options.AddPolicy(PolicyName, policy =>
                {
                    policy.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
                });
            });
        }
        /// <summary>
        /// 使用跨域
        /// </summary>
        /// <param name="app">应用程序建造者</param>
        /// <returns></returns>
        public static IApplicationBuilder UseMCodeCors(this IApplicationBuilder app)
        {
            return app.UseCors(PolicyName);
        }
    }
}

Swagger

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// 网关Swagger配置
    /// </summary>
    public class OcelotSwaggerOptions
    {
        public List<SwaggerEndPoint> SwaggerEndPoints { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// 网关Swagger配置
    /// </summary>
    public class OcelotSwaggerOptions
    {
        public List<SwaggerEndPoint> SwaggerEndPoints { get; set; }
    }
}
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// Swagger配置
    /// </summary>
    public class SwaggerOptions
    {
        /// <summary>
        /// 服务名称
        /// </summary>
        public string ServiceName { get; set; }

        /// <summary>
        /// API信息
        /// </summary>
        public OpenApiInfo ApiInfo { get; set; }

        /// <summary>
        /// Xml注释文件
        /// </summary>
        public string[] XmlCommentFiles { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="serviceName">服务名称</param>
        /// <param name="apiInfo">API信息</param>
        /// <param name="xmlCommentFiles">Xml注释文件</param>
        public SwaggerOptions(string serviceName, OpenApiInfo apiInfo, string[] xmlCommentFiles = null)
        {
            ServiceName = !string.IsNullOrWhiteSpace(serviceName) ? serviceName : throw new ArgumentException("serviceName parameter not config.");
            ApiInfo = apiInfo;
            XmlCommentFiles = xmlCommentFiles;
        }
    }
}
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// Swagger 服务扩展
    /// </summary>
    public static class SwaggerServiceExtensions
    {
        /// <summary>
        /// 添加 Swagger 服务
        /// </summary>
        /// <param name="services"></param>
        /// <param name="swaggerOptions"></param>
        /// <returns></returns>
        public static IServiceCollection AddMCodeSwagger(this IServiceCollection services, SwaggerOptions swaggerOptions)
        {
            services.AddSingleton(swaggerOptions);

            SwaggerGenServiceCollectionExtensions.AddSwaggerGen(services, c =>
            {
                c.SwaggerDoc(swaggerOptions.ServiceName, swaggerOptions.ApiInfo);

                if (swaggerOptions.XmlCommentFiles != null)
                {
                    foreach (string xmlCommentFile in swaggerOptions.XmlCommentFiles)
                    {
                        string str = Path.Combine(AppContext.BaseDirectory, xmlCommentFile);
                        if (File.Exists(str)) c.IncludeXmlComments(str, true);
                    }
                }

                SwaggerGenOptionsExtensions.CustomSchemaIds(c, x => x.FullName);

                c.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer",
                    BearerFormat = "JWT",
                    Description = "请输入 bearer 认证"
                });


                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                                              {
                                                  {
                                                      new OpenApiSecurityScheme
                                                      {
                                                          Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
                                                      },
                                                      new string[] {}
                                                  }
                                              });
            });

            return services;
        }

        /// <summary>
        /// 使用 Swagger UI
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseMCodeSwagger(this IApplicationBuilder app)
        {
            string serviceName = app.ApplicationServices.GetRequiredService<SwaggerOptions>().ServiceName;

            SwaggerUIBuilderExtensions.UseSwaggerUI(SwaggerBuilderExtensions.UseSwagger(app), c =>
            {
                c.SwaggerEndpoint("/swagger/" + serviceName + "/swagger.json", serviceName);
            });
            return app;
        }


        public static IServiceCollection AddMCodeOcelotSwagger(this IServiceCollection services, OcelotSwaggerOptions ocelotSwaggerOptions)
        {
            services.AddSingleton(ocelotSwaggerOptions);
            SwaggerGenServiceCollectionExtensions.AddSwaggerGen(services);
            return services;
        }

        public static IApplicationBuilder UseMCodeOcelotSwagger(this IApplicationBuilder app)
        {
            OcelotSwaggerOptions ocelotSwaggerOptions = app.ApplicationServices.GetService<OcelotSwaggerOptions>();

            if (ocelotSwaggerOptions == null || ocelotSwaggerOptions.SwaggerEndPoints == null)
            {
                return app;
            }

            SwaggerUIBuilderExtensions.UseSwaggerUI(SwaggerBuilderExtensions.UseSwagger(app), c =>
            {
                foreach (SwaggerEndPoint swaggerEndPoint in ocelotSwaggerOptions.SwaggerEndPoints)
                {
                    c.SwaggerEndpoint(swaggerEndPoint.Url, swaggerEndPoint.Name);
                }
            });
            return app;
        }
    }
}

MCode.ApiGateway.Program

using MCode.Common.Extensions.Swagger;
using MCode.Common.Extensions.Consul;
using MCode.Common.Extensions.Cors;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
using Microsoft.AspNetCore.Hosting;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseUrls(builder.Configuration.GetValue<string>("Url") ?? "");

//builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("ocelot.json", false, true);

// Add services to the container.
builder.Services.AddMCodeConsul();

builder.Services.AddMCodeCors();

builder.Services.AddMCodeOcelotSwagger(new OcelotSwaggerOptions { SwaggerEndPoints = new List<SwaggerEndPoint> { new SwaggerEndPoint { Name = "api-product-service", Url= "http://localhost:5014/swagger/api-product-service/swagger.json" } } });

builder.Services.AddOcelot().AddConsul();

builder.Services.AddControllers();

var app = builder.Build();

app.UseMCodeCors();

app.UseMCodeOcelotSwagger();

app.UseConsulCheckService();

// Configure the HTTP request pipeline.

app.UseAuthorization();

app.UseOcelot().Wait();

app.Run();

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "Routes": [ //这里注意一下版本(旧版本用ReRoutes)
    {
      "DownstreamPathTemplate": "/api/{controller}", //下游路径模板
      "DownstreamScheme": "http", //下游方案
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "localhost",
      //    "Port": "5014"
      //  }
      //], //下游主机和端口
      "UpstreamPathTemplate": "/api/product/{controller}", //上游路径模板
      "UpstreamHttpMethod": [], //上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法
      "ServiceName": "api-product-service", //请求服务名称
      "LoadBalancerOptions": {
        "Type": "LeastConnection" //负载均衡算法:目前 Ocelot 有RoundRobin 和LeastConnection算法
      }
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "127.0.0.1", //你的Consul的ip地址
      "Port": 8500, //你的Consul的端口
      "Type": "Consul" //类型
      //"ConfigurationKey": "Consul" //指定配置键,键入您的配置
    }
  },
  "Url": "http://localhost:5015",
  "Consul": {
    "ConsulIP": "127.0.0.1",
    "ConsulPort": "8500",
    "ServiceName": "api-gateway",
    "Ip": "localhost",
    "Port": "5015",
    "Weight": 1
  },
  "AllowedHosts": "*"
}

MCode.Product.Api.Program

using MCode.Product.Api;
using MCode.Common.Extensions.Consul;
using MCode.Common.Extensions.Cors;
using MCode.Common.Extensions.Swagger;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.WebHost.UseUrls(builder.Configuration.GetValue<string>("Url") ?? "");

builder.Services.AddControllers();

builder.Services.AddMCodeConsul();

builder.Services.AddMCodeCors();

builder.Services.AddMCodeSwagger(new SwaggerOptions("api-product-service", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "产品服务" }, new string[] { "MCode.Product.Api.xml" }));


var app = builder.Build();

app.UseAuthorization();


app.UseMCodeCors();

app.UseMCodeSwagger();

app.UseConsulCheckService();


app.MapControllers();

app.Run();

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Url": "http://localhost:5014",
  "Consul": {
    "ConsulIP": "127.0.0.1",
    "ConsulPort": "8500",
    "ServiceName": "api-product-service",
    "Ip": "localhost",
    "Port": "5014",
    "Weight": 1
  }
}

效果

image

image

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

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

相关文章

人工智能 AI 如何让我们的生活更加便利

每个人都可以从新技术中获益&#xff0c;一想到工作或生活更为便利、简捷且拥有更多空余时间&#xff0c;谁会不为之高兴呢&#xff1f;借助人工智能&#xff0c;每天能够多一些空余时间&#xff0c;或丰富自己的业余生活&#xff0c;为培养日常兴趣爱好增添一点便利。从电子阅…

小程序 常用组件

文章目录 常见组件viewtextimageswipernavigatorrich-textnodes属性 buttoniconfromradiocheckbox 常见组件 重点讲解⼩程序中常⽤的布局组件 view,text,rich–text,button,image,navigator,icon,swiper,radio,checkbox。 等 view 代替 原来的 div 标签 <view hover-clas…

【C语言基础考研向】05 scanf读取标准输入超详解

文章目录 一.scanf函数的原理 样例问题原因解决方法 二.多种数据类型混合输入 错误样例正确样例 一.scanf函数的原理 C语言未提供输入/输出关键字&#xff0c;其输入和输出是通过标准函数库来实现的。C语言通过scanf函数读取键盘输入&#xff0c;键盘输入又被称为标准输入。…

Qt绘画的使用

1. 绘图 绘图组件&#xff1a; 1、绘画对象 2、绘画位置 3、绘画工具 4、绘画时机 绘画时机&#xff1a; 当整个窗口或窗口的一部分需要重新绘制时&#xff0c;会调用绘制事件处理函数 void QWidget::paintEvent(QPaintEvent *event) 绘画对象&#xff1a; QPainter类&#xff…

java基础:求数组的最值

方法一&#xff1a;顺序查找 先假设数组第一个元素为最值&#xff0c;然后和数组里的数按顺序进行比较得出最值&#xff0c;所以叫顺序查找。 代码如下 package idea;public class arr_int {public static void main(String[] args) { // 初始化一个数组int[] arr {12…

【MYSQL】事务隔离级别

脏读、幻读、不可重复读 脏读 一个事务正在对一条记录做修改&#xff0c;在这个事务完成并提交前&#xff0c;另一个事务也来读取同一条记录&#xff0c;读取了这些未提交的“脏”数据&#xff0c;并据此做进一步的处理&#xff0c;就会产生未提交的数据依赖关系。这种现象被形…

渗透测试之Mimikatz2.2 如何抓取Win11登录明文密码

环境: 1.攻击者IP:192.168.1.35 系统: KALI2022(vmware 16.0) 2.靶机IP:192.168.1.16 系统: Windows11 3.USB无线网卡 4.Mimikatz 2.2 (win版) 问题描述: Mimikatz2.2 如何抓取Win11登录明文密码 解决方案: Wdigest WDigest协议是在WindowsXP中被引入的,旨在与H…

Kafka 消息不能正常消费问题排查

订单宽表数据不同步 事情的起因是专员在 ze app 上查不到订单了&#xff0c;而订单数据是从 mysql 的 order_search_info 查询的&#xff0c;order_search_info 表的数据是从 oracel 的 BZ_ORDER_INFO 表同步过来的&#xff0c;查不到说明同步有问题 首先重启&#xff0c;同步…

如何快速打造属于自己的接口自动化测试框架

1 接口测试 接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及相互逻辑依赖关系。 接口自动化相对于UI自动化来说&#xff0c;属于更底层的测试&#xff0c;这样带来的好处就是测试收益更大&#xff…

系统配置dns主从服务器

一、准备两台主机&#xff0c;区分主从 二、完全区域传送 1、主DNS服务器配置 #安装相关的包 [rootoula1 ~]# yum install bind -y#关闭防火墙 [rootoula1 ~]# systemctl stop firewalld [rootoula1 ~]# setenforce 0#修改配置主文件 [rootoula1 ~]# vim /etc/named.conf opt…

oop面向对象编程python

1.定义一个圆类&#xff08;Circle&#xff09;,求圆的面积和周长 import mathclass Circle():def __init__(self, R ,name):self.radius Rself.name namedef girth(self):return 2 * self.radius * math.pidef area(self):return self.radius ** 2 * math.pic1 Circle(10,&q…

全球移动通信市场,正在经历哪些新变化?

2023年已经结束了。回顾这一年的全球移动通信市场&#xff0c;如果让我用一个词来总结&#xff0c;那就是——“厚积薄发”。 从表面上来看&#xff0c;似乎并没有什么大事情发生。但实际上&#xff0c;平静的湖面之下&#xff0c;却是一片波涛汹涌、风云激荡。 无论是消费互联…

springcloud Eureka服务注册与发现

文章目录 代码地址Eureka基础知识什么是服务治理什么是服务注册与发现 单机版eurekaServerIDEA生成eurekaServer端服务注册中心类似物业公司EurekaClient端cloud-provider-payment8001修改EurekaClient端cloud-consumer-order80 集群Eureka构建步骤新建cloud-eureka-server7002…

Android平台Unity下如何通过WebCamTexture采集摄像头数据并推送至RTMP服务器或轻量级RTSP服务

技术背景 我们在对接Unity下推送模块的时候&#xff0c;遇到这样的技术诉求&#xff0c;开发者希望在Android的Unity场景下&#xff0c;获取到前后摄像头的数据&#xff0c;并投递到RTMP服务器&#xff0c;实现低延迟的数据采集处理。 在此之前&#xff0c;我们已经有了非常成…

Jxls 实现动态导出功能

目录 引言前端页面后端代码excel模板导出效果 引言 在实际做项目的过程中&#xff0c;导出报表时需要根据每个人所关注的点不一样&#xff0c;所需导出的字段也不一样&#xff0c;这时后端就需要根据每个所选的字段去相应的报表&#xff0c;这就是本文要讲的动态导出报表。 前端…

线程的使用

线程的创建方式 1、实现Runnable Runnable规定的方法是run()&#xff0c;无返回值&#xff0c;无法抛出异常 实现Callable 2、Callable规定的方法是call()&#xff0c;任务执行后有返回值&#xff0c;可以抛出异常 3、继承Thread类创建多线程 继承java.lang.Thread类&#xff0…

Docker registry镜像仓库,私有仓库及harbor管理详解

目录 registry镜像仓库概述 Docker 镜像仓库&#xff08;Docker Registry&#xff09;&#xff1a; registry 容器&#xff1a; 私有仓库概述 搭建本地私有仓库示例 Harbor概述 harbor架构 详解构成 Harbor由容器构成 Harbor部署示例 环境准备 部署Docker-Compose服…

uniapp中uview组件库的NoticeBar 滚动通知 使用方法

目录 #平台差异说明 #基本使用 #配置主题 #配置图标 #配置滚动速度 #控制滚动的开始和暂停 #事件回调 #API #Props #Events 该组件用于滚动通告场景&#xff0c;有多种模式可供选择 #平台差异说明 AppH5微信小程序支付宝小程序百度小程序头条小程序QQ小程序√√√√…

【不需要网络不需要显卡】本地部署GPT

【不需要网络/不需要显卡】本地部署GPT 大家好&#xff0c;我是老 J 我们都知道ChatGPT目前只有两种使用方式&#xff0c;一种是直接去官网访问&#xff0c;适合个人用户&#xff1b;另一种是API调用&#xff0c;适合企业或者网站使用。这两种方式的门槛都比较高&#xff0c;…

10个用于Android开发的有用的Kotlin库及示例

10个用于Android开发的有用的Kotlin库及示例 在Android开发领域&#xff0c;Kotlin已成为一门领先的语言&#xff0c;带来了现代语法和功能的浪潮。随着Kotlin的崛起&#xff0c;涌现出了许多专为其定制的库&#xff0c;进一步增强了开发体验。本文将深入介绍其中的10个库&…