Api网关Kong集成Consul做服务发现及在Asp.Net Core中的使用



1622219047536

写在前面

  Api网关我们之前是用 .netcore写的 Ocelot的,使用后并没有完全达到我们的预期,花了些时间了解后觉得kong可能是个更合适的选择。

简单说下kong对比ocelot打动我的:

1、kong可以直接代替Nginx/OpenRestry做前端服务器。

2、kong的功能强大,性能不俗,生态不错,操作面板,插件丰富,社区活跃;

本文目的

1、对kong和consul做个基本介绍;

2、kong集成consul 做服务发现;

3、Asp.net core WebApi 服务自动注册到Consul;

4、Asp.net core WebApi 自动注册路由规则到kong,实现程序启动即部署;

运行环境

172.16.1.30  CentOS Linux release 7.6.1810 (Core) (虚拟机单核2g)

Docker version 18.09.3, build 774a1f4

kong apigateway(enterprise) 2.3.x (docker安装)

kong

kong的简介

KONG — The Microservice API Gateway | by faren | Medium

我们熟悉Nginx;

有个一个加强版的Nginx叫做OpenRestry,OpenRestry ≈ lua脚本+Nginx;

那么Kong 网关就是满血版的 OpenRestry,它有许许多多的的插件和各种丰富的功能,且提供对应的Rest Api,让你轻松打造你所能想象到的 网关+ web前端服务器的功能;

特点(翻译)

  • 云原生:平台无关,kong支持任意平台,裸机容器或云平台;

  • k8s原生:原生支持k8s,有kong-ingress,支持l4+l7协议;

  • 动态负载均衡:负载均衡到多个upstream;

  • Hash-based的负载均衡:根据cookie、session,ip等hash负载均衡;

  • 断路器:自动剔除不健康的服务;

  • 心跳检测:主动和被动心跳检测;

  • 服务发现:通过第三方dns解析做服务发现,如consul;

  • Serverless:调用和保护 AWS Lambda or OpenWhisk functions directly ;

  • WebSockets:支持ws、wss协议;

  • gRPC:支持gRPC协议,并通过日志和插件监控流量;

  • OAuth2.0:轻松添加OAuth2.0支持;

  • 日志:轻松记录请求和响应,通过HTTP, TCP, UDP, 或 直接到硬盘;

  • 安全性:访问控制,爬虫检测、ip黑白名单等等;

  • Syslog:记录到系统日志;

  • SSL: 安装不同的SSL证书到服务;

  • 监控:实时监控,提供关机负责负载均衡和性能指标;

  • 整箱代理:kong可以作为正向代理服务器;

  • 身份认证:HMAC, JWT, Basic, 各种奇奇怪怪的规则都支持.

  • 限制器:流量限制功能;

  • 传输转换:新增、删掉、或者修改你的请求或者响应;

  • 缓存:请求缓存;

  • CLI:命令行控制支持;

  • Rest Api:Rest Api控制支持;

  • Geo-Replicated:夸时区请求支持;

  • 故障检测与恢复:数据库(Cassandra /postgres)节点挂掉不影响kong的服务;

  • 集群:所有kong节点都自动加入集群保持配置同步;

  • 拓展性:分布式拓展原生支持,水平伸缩加减节点就行;

  • 高性能:使用Nginx作为核心负载均衡组件,高性能可伸缩;

  • 插件:高拓展性,插件式添加功能;

详细请看

github:https://github.com/Kong/kong

官方文档:https://docs.konghq.com

kong的安装

拉取镜像

docker pull kong/kong-gateway:2.3.3.2-alpine

给镜像改个名

docker tag <IMAGE_ID> kong-ee

创建一个网络

docker network create kong-ee-net

运行一个postgresSql 9.6,用来存取kong的配置

docker run -d --name kong-ee-database \--network=kong-ee-net \-p 5432:5432 \-e "POSTGRES_USER=kong" \-e "POSTGRES_DB=kong" \-e "POSTGRES_PASSWORD=kong" \postgres:9.6

启动kong

  docker run -d --name kong-ee2--network=kong-ee-net \-e "KONG_DATABASE=postgres" \-e "KONG_PG_HOST=172.16.1.30" \-e "KONG_PG_PASSWORD=kong" \-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \-e "KONG_ADMIN_GUI_URL=http://172.16.1.30:8002" \ -e "KONG_DNS_RESOLVER=172.16.1.30:8600" \  #注意按需使用,consul的才配-p 8000:8000 \-p 8443:8443 \-p 8001:8001 \-p 8444:8444 \-p 8002:8002 \-p 8445:8445 \-p 8003:8003 \-p 8004:8004 \kong-ee//-e "KONG_DNS_RESOLVER=172.16.1.30:8600" 注意这个配置,这是我需要用的consul的dns配置,如果不想用consul做服务发现,删掉这行

这里说明一下,kong的配置是用postgres(或者Cassandra )来存配置,但每一次请求都不需要去读取数据库的。修改的配置会直接 reload 到内存中,不影响性能;

另外说说kong的集群;

因为kong 网关其实最终 表现为一个超级前端服务器+网关,所以每个连接到同个数据库的kong实例配置一样,连接同个数据库的kong作为一个集群;

一般在kong的前面是直接做dns解析就行,如果dns不支持多ip的话做keepalive + vip就行;

验证

#admin api 获取所有服务
curl -i -X GET --url http://127.0.0.1:8001/services#admin 管理后台 
curl -i -X GET --url http://127.0.0.1:8002
1622292530454
1622292862508

管理后台

1622297057459

consul

consul简介

Consul Service Mesh with Paul Banks - Software Engineering Daily

  Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,比如 Airbnb的SmartStack等相比,Consul的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等),使用起来也较 为简单。

其实就是做服务治理的。

github: https://github.com/hashicorp/consul

官方文档: https://www.consul.io/

consul的安装

直接docker安装

*这是作为开发节点安装

docker run  -d --name=dev-consul1 --network=host -e CONSUL_BIND_INTERFACE=eth0 consul:1.8

安装成功

1622295177798

运行一个WebApi服务

先在服务运行一个Asp.net Core WebApi (就是是新建的一个包含),我的版本是3.1的,我给服务命名:DemoApi31,监听端口5002

1622294659671

将服务注册到Consul

curl --location --request PUT 'http://172.16.1.30:8500/v1/agent/service/register' \
--header 'Content-Type: application/json' \
--data-raw '{"ID": "DemoApi31_172.16.1.30:5002","Name": "DemoApi31","Address": "172.18.1.30","Port": 5002,"EnableTagOverride": false,"Weights": {"Passing": 10,"Warning": 1}
}'

注册成功:

1622295901281

Dns解析验证

# 如果没安装dig 安装:yum install bind-utils
dig @172.16.1.30 -p 8600 Demoapi31.service.consul SRV
1622294451840

ok,我们这里已经把服务注册到consul,且能通过dns常解析到了,我们做跟kong的集成吧。

consul提供内置Dns解析和Rest Api 两种方式集成做服务发现,我们这里跟kong的集成选用的Dns方式。

kong集成consul做服务发现

因为consul的角色是dns服务器,所以非常简单,我们已注册好的 DemoApi31为例:

1、创建一个名为consul的服务

DemoApi31.service.consul 是consu要求的格式

1622297254563

2、创建一个名为consul的路由

1622297366317

验证

访问我们配置的kong路由:http://172.16.1.30:8000/consul/api/values

1622297455784

ok

到目前为止我们只完成了本文目的1、2

3,和4三请往下看;

在Asp.net Core中的使用

  以之前的DemoApi31为例,换成5003端口,我需要达到的效果是,程序启动的时候就把服务注册到Consul 做好心跳检测,并同时部署到网关Kong,直接对外服务。

Asp.net Core 服务自动注册到Consul

安装nuget包

Install-Package Passport.Infrastructure -Version 0.1.4.7-preview-1

加入配置appsettings.json

大家主要各服务器要改成自己的

  "ServiceDiscovery": {"ServiceName": "DemoApi31","Consul": {"HttpEndpoint": "http://172.16.1.30:8500", "HttpHeathCheck": {"Path": "/healthcheck","TimeOunt": 10,"Interval": 10},"Tags": ["NetCore","DemoApi","v1.0"]}}

StartUp.cs  ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
{//第一行PassportConfig.InitPassportConfig(Configuration, Environment);......services.AddHealthChecks();services.AddConsul();
}

StartUp.cs  Configure方法

app.UseHealthChecks("/healthcheck");

启动程序

dotnet DemoApi.Core3.1.dll --healthhost 172.16.1.30 --urls http://*:5003 
1622299506567
1622299584100

源码解析

/// <summary>
/// 加入consul做服务管理
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddConsul(this IServiceCollection services)
{var options = PassportConfig.GetSection("ServiceDiscovery").Get<ServiceDiscoveryOptions>();if (options?.Disable != true){var healthHost = PassportConfig.GetHealthHost();if (string.IsNullOrWhiteSpace(options?.ServiceName) || string.IsNullOrWhiteSpace(options?.Consul?.HttpEndPoint)){throw new ArgumentNullException("ServiceDiscovery.ServiceName/Consul.HttpEndpoint cannot be null or empty!");}//实例化kongclientvar consulClient = new ConsulClient(x => x.Address = new Uri(options.Consul.HttpEndPoint));services.AddSingleton(consulClient);services.Configure(new Action<ConsulOptions>(op =>{op.HttpEndPoint = options.Consul.HttpEndPoint;op.Token = options.Consul.Token;op.TcpEndPoint = options.Consul.TcpEndPoint;}));var checkOptions = options.Consul.HttpHeathCheck;var checkUrl = $"http://{healthHost}:{PassportConfig.GetCurrentPort()}{checkOptions.Path}";new ConsulBuilder(consulClient).AddHttpHealthCheck(checkUrl, checkOptions.TimeOunt, checkOptions.Interval).RegisterService(options.ServiceName, healthHost, PassportConfig.GetCurrentPort(), options.Consul.Tags).Wait();}return services;
}

ConsulBuilder.cs  参考晓晨大佬

 public class ConsulBuilder{private readonly ConsulClient _client;private readonly List<AgentServiceCheck> _checks = new List<AgentServiceCheck>();public ConsulBuilder(ConsulClient client){_client = client;}public ConsulBuilder AddHealthCheck(AgentServiceCheck check){_checks.Add(check);return this;}/// <summary>////// </summary>/// <param name="url"></param>/// <param name="timeout">unit: second</param>/// <param name="interval">check interval. unit: second</param>/// <returns></returns>public ConsulBuilder AddHttpHealthCheck(string url, int timeout = 10, int interval = 10){_checks.Add(new AgentServiceCheck(){DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(timeout * 3),Interval = TimeSpan.FromSeconds(interval),HTTP = url,Timeout = TimeSpan.FromSeconds(timeout)});PassportConsole.Success($"[Consul]Add Http Healthcheck Success! CheckUrl:{url}");return this;}/// <summary>////// </summary>/// <param name="endpoint">GPRC service address.</param>/// <param name="grpcUseTls"></param>/// <param name="timeout">unit: second</param>/// <param name="interval">check interval. unit: second</param>/// <returns></returns>public ConsulBuilder AddGRPCHealthCheck(string endpoint, bool grpcUseTls = false, int timeout = 10, int interval = 10){_checks.Add(new AgentServiceCheck(){DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20),Interval = TimeSpan.FromSeconds(interval),GRPC = endpoint,GRPCUseTLS = grpcUseTls,Timeout = TimeSpan.FromSeconds(timeout)});PassportConsole.Success($"[Consul]Add GRPC HealthCheck Success! Endpoint:{endpoint}");return this;}public async Task RegisterService(string name, string host, int port, string[] tags){var registration = new AgentServiceRegistration(){Checks = _checks.ToArray(),ID = $"{name}_{host}:{port}",Name = name,Address = host,Port = port,Tags = tags};await _client.Agent.ServiceRegister(registration);PassportConsole.Success($"[Consul]Register Service Success! Name:{name} ID:{registration.ID}");AppDomain.CurrentDomain.ProcessExit += async (sender, e) =>{PassportConsole.Information($"[Consul] Service Deregisting ....  ID:{registration.ID}");await _client.Agent.ServiceDeregister(registration.ID);};}/// <summary>/// 移除服务/// </summary>/// <param name="serviceId"></param>public async Task Deregister(string serviceId){await _client?.Agent?.ServiceDeregister(serviceId);}}

逻辑简单,确定自己需要用的是注册服务功能,调Consul Api 注册,然后程序退出的时候注销consul的服务就行;

Asp.net core WebApi 自动注册路由规则到kong

通过Consul

安装nuget包

#已安装跳过
Install-Package Passport.Infrastructure -Version 0.1.4.7-preview-1

**加入配置appsettings.json**

guid顺便去https://www.guidgen.com/ 生成一个

"Kong": {//"Disable": false, //true=禁用"Host": "http://172.16.1.30:8001","Services": [{"Id": "72e21af8-283f-44c4-a766-53de8bb35c21", //guid"Name": "service-autoapi","Retries": 5,"Protocol": "http","Host": "DemoApi31.service.consul", "Port": 0,"Path": null,"Connect_timeout": 60000, //毫秒"Write_timeout": 60000,"Read_timeout": 60000,"Tags": null}],"Routes": [{"Id": "5370e1b7-6c43-442d-9a44-23c249f958f7","Name": "route-autoapi","Protocols": [ "http" ],"Methods": null,"Hosts": null,"Paths": [ "/autoapi" ],"Https_redirect_status_code": 307,"Regex_priority": 0,"Strip_path": true,"Preserve_host": false,"Tags": null,"Service": {"Id": "72e21af8-283f-44c4-a766-53de8bb35c21" //这个id跟关联的Services的id一致}}]}

StartUp.cs  ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
{......services.AddConsul();services.RouteRegistToKong();
}

启动程序

dotnet DemoApi.Core3.1.dll --healthhost 172.16.1.30 --urls http://*:5003 

验证

查看kong管理后台:

1622301181490

访问 http://172.16.1.30:8000/auto/api/values

1622301195836

大功告成。

不通过Consul,直接配置路由到kong

StartUp.cs  ConfigureServices方法

public void ConfigureServices(IServiceCollection services)
{......//删掉这行services.AddConsul();services.RouteRegistToKong();
}

配置变为

"Kong": {//"Disable": false, //true=禁用"Host": "http://172.16.1.30:8001","Services": [{"Id": "0f86015b-b170-4ada-b045-740ae7d77ed6", //guid"Name": "configupapi","Retries": 5,"Protocol": "http","Host": "configupapi","Port": 0,"Path": null,"Connect_timeout": 60000, //毫秒"Write_timeout": 60000,"Read_timeout": 60000,"Tags": null}],"Routes": [{"Id": "1be79a57-af87-43b0-a0a0-b7a6cc0c5ade","Name": "configupapi","Protocols": [ "http" ],"Methods": null,"Hosts": null,"Paths": [ "/configupapi" ],"Https_redirect_status_code": 307,"Regex_priority": 0,"Strip_path": true,"Preserve_host": false,"Tags": null,"Service": {"Id": "0f86015b-b170-4ada-b045-740ae7d77ed6" //这个id跟Services的id一致}}],"Upstream": {"Id": "8efd15af-df78-422f-97a0-9072fa7e7431","Tags": [ "exampleapi", "v1.0" ],"Name": "configupapi","Hash_on": "none","Healthchecks": {"Active": {"Unhealthy": {"Http_statuses": [ 429, 500, 501, 502, 503, 504, 505 ],"Tcp_failures": 1,"Timeouts": 2,"Http_failures": 1,"Interval": 5},"Type": "http","Http_path": "/healthcheck","Timeout": 1,"Healthy": {"Successes": 1,"Interval": 20,"Http_statuses": [ 200, 302 ]},"Https_verify_certificate": true,"Concurrency": 1},"Passive": {"Unhealthy": {"Http_statuses": [ 429, 500, 501, 502, 503, 504, 505 ]},"Healthy": {"Http_statuses": [ 200, 201, 302 ]},"Type": "http"}},"Hash_on_cookie_path": "/","Hash_fallback": "none","Slots": 10000},"Target": {"Tags": [ "exampleapi", "v1.0" ],"Weight": 100}}

源码解析

/// <summary>
/// 路由注册到kong;
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection RouteRegistToKong(this IServiceCollection services)
{if (!PassportConfig.GetBool("Kong:Disable")){var konghost = PassportConfig.Get("Kong:Host") ?? throw new ArgumentNullException("Kong:Host cannot be null or empty!");var options = new KongClientOptions(HttpClientFactory.Create(), konghost);var client = new KongClient(options);services.AddSingleton<KongClient>(client);var upStream = PassportConfig.GetSection("Kong:Upstream").Get<UpStream>();var target = PassportConfig.GetSection("Kong:Target").Get<TargetInfo>();if (upStream != null && target != null){upStream.Created_at = DateTime.Now;upStream = client.UpStream.UpdateOrCreate(upStream).Result;target.Target = $"{PassportConfig.GetHealthHost()}:{PassportConfig.GetCurrentPort()}";target.Id = PassportTools.GuidFromString($"{Dns.GetHostName()}{target.Target}");target.Created_at = DateTime.Now;target.UpStream = new TargetInfo.UpStreamId { Id = upStream.Id.Value };client.Target.Add(target).Wait();PassportConsole.Success($"[Kong]UpStream registered:{upStream.Name} Target:{target.Target}");// app.UseKongHealthChecks(upStream, onExecuter);}var kongServices = PassportConfig.GetSection("Kong:Services").Get<ServiceInfo[]>();var kongRoutes = PassportConfig.GetSection("Kong:Routes").Get<RouteInfo[]>();if (kongServices?.Length > 0 == true){foreach (var item in kongServices){item.Updated_at = DateTime.Now;item.Path = string.IsNullOrWhiteSpace(item.Path) ? null : item.Path;client.Service.UpdateOrCreate(item).Wait();PassportConsole.Success($"[Kong]Service registered:{item.Name}");}}if (kongRoutes?.Length > 0 == true){foreach (var item in kongRoutes){item.Updated_at = DateTime.Now;client.Route.UpdateOrCreate(item).Wait();PassportConsole.Success($"[Kong]Route registered:{item.Name}");}}}return services;
}

  逻辑也简单,也是调用kong配置把本该手工配置的路由,分别调用upstream、service、route Api修改配置。有区别的是程序退出时不会去删对应的路由;

总结

  我在各技术博客都没有看到总结的比较好的kong+consul+asp.net core的集成文章,特此总结。期待您的点赞留意;

[参考]

https://docs.konghq.com/

https://www.cnblogs.com/stulzq/p/11942691.html

https://github.com/lianggx/Kong.Net

https://www.consul.io/docs


文章博客园地址请点击“阅读原文”

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

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

相关文章

view 背景透明

引用&#xff1a;http://www.eoeandroid.com/thread-37112-1-1.html surfaceView.setZOrderOnTop(true);surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);win.setBackgroundColor(255);

女生学高铁和计算机哪个更好,2020铁路最好的5个专业 女生上铁路学什么专业好...

铁路专业一直是比较受欢迎的&#xff0c;有些同学都想学习铁路专业&#xff0c;应为一旦找到和铁路相关的工作&#xff0c;工资待遇都是非常好的&#xff0c;但是铁路里面也不是所有的专业都非常好&#xff0c;那么铁路里面最好的专业有哪些呢&#xff1f;女生适合学习铁路的哪…

ML.NET Cookbook:(10)如何使用模型做出一个预测?

因为任何ML.NET模型都是一个转换器&#xff0c;所以您当然可以使用model.Transform将该模型应用于“数据视图”并以这种方式获得预测。不过&#xff0c;更典型的情况是&#xff0c;没有我们想要预测的“数据集”&#xff0c;而是一次只接收一个样本。例如&#xff0c;我们将模型…

裤子换裙子,就问你GAN的这波操作秀不秀

全世界只有3.14 % 的人关注了数据与算法之美把照片里的绵羊换成长颈鹿、牛仔长裤换成短裙。听起来有点不可思议&#xff0c;但韩国科学技术院和浦项科技大学的研究人员目前已实现了这一骚操作。他们开发的一种机器学习算法可在多个图像数据集上实现这种操作。其论文《InstaGAN:…

python中字典长度可变吗_python中列表长度可变吗

python中列表长度可变吗&#xff1f;下面给大家介绍一下python中可变和不可变的类型&#xff1a;可变、不可变可变/不可变类型,指的是&#xff1a;内存id不变&#xff0c;type也不变的前提下&#xff0c;value是否是可变的。int()和str()都是不可变类型列表、字典是可变类型对于…

Windows UI风格的设计(11)

转载于:https://blog.51cto.com/8382359/1342271

教之初计算机考试函数应用题,教之初计算机考试系统

教之初计算机考试系统官方版是一款发布长达12年之久的免费考试系统软件&#xff0c;已经有数千万次使用的软件。教之初考试系统是专业的考试软件&#xff0c;您所考虑的功能&#xff0c;教之初都已经非常贴心地替您想好&#xff0c;如果您发现需要的功能并不存在&#xff0c;那…

这里聚集了优秀的数学老师、家长,有超多惊喜在等你!

全世界有3.14 % 的人已经关注了数据与算法之美今天的这篇文章&#xff0c;是向大家推荐一个数学公众号“少年数学家”。“少年数学家”是一个致力为数学老师与家长&#xff0c;提供丰富的数学课外知识、数学人物、数学趣谈、科技与数学的公众号&#xff0c;希望通过这些万物背后…

ASP.NET Core文件上传IFormFile于Request.Body的羁绊

前言在上篇文章深入探究ASP.NET Core读取Request.Body的正确方式[1]中我们探讨了很多人在日常开发中经常遇到的也是最基础的问题&#xff0c;那就是关于Request.Body的读取方式问题&#xff0c;看是简单实则很容易用不好。笔者也是非常荣幸的得到了许多同学的点赞支持&#xff…

librosa能量_语音MFCC提取:librosa amp;amp; python_speech_feature(2019.12)

最近在阅读语音方向的论文&#xff0c;其中有个被提及很多的语音信号特征MFCC(Mel-Frequency Cepstral Coefficients)&#xff0c;找到了基于python的语音库librosa(version0.7.1)和python_speech_features(version0.6)&#xff0c;下文对这两个库计算MFCC的流程细节稍作梳理。…

线性代数与矩阵论 习题 1.2.2

试利用辗转相除法&#xff0c;求有理系数多项式$u(x)$和$v(x)$,使得$u(x)f(x)v(x)g(x)(f(x),g(x))$. (1)$f(x)3x^3-2x^2x2$,$g(x)x^2-x1$. 解:\begin{align*} 3x^3-2x^2x2&3x(x^2-x1)(x^2-2x2)\\x^2-x1&(x^2-2x2)(x-1)\\x^2-2x2&x(x-1)-(x-2)\\x-1&x-21\\\end{…

Uno 平台 一 WinUI终极跨平台方案(一)

以下是 Uno 平台的官方介绍&#xff1a;关于 Uno 平台Uno平台能够创建像素级完美的&#xff0c;只通过C#XAML编写的应用程序&#xff0c;能够跨平台运行在Windows&#xff0c;iOS&#xff0c;安卓&#xff0c;macOS&#xff0c;Linux和Web上&#xff0c;Uno 平台是免费和开源的…

Python程序员的30个常见错误

全世界只有3.14 % 的人关注了数据与算法之美在这篇文章中&#xff0c;我将总结新老Python程序员常犯的一些错误&#xff0c;以帮助你们在自己的工作避免犯同样或类似错误。推荐阅读《Python3.0科学计算指南》首先我要说明一下的是&#xff0c;这些都是来源于第一手的经验。我以…

米兰大学计算机科学,米兰大学

很多去意大利留学的学生会选择去米兰大学留学&#xff0c;那么留学米兰大学需要了解哪些重要事项呢&#xff1f;跟着出国留学网来看看吧!欢迎阅读。意大利留学米兰大学解析院校基本信息欧洲排名7所在省州伦巴第大区所在城市Milano学校性质公立建校年代1923年学校人数60406人官方…

650c公路车推荐_2020最具性价比的中高端公路整车盘点

当你从小白成为进阶玩家后&#xff0c;发现陪伴自己多年公路车逐渐不能满足自己需求时&#xff0c;你渐渐将目光转向高端公路车&#xff0c;当你被“坑蒙拐骗”试骑了顶级公路车后&#xff0c;换车的想法在脑中油然而生。然而理想很丰满&#xff0c;现实很骨感。干瘪腰包把你从…

Java程序员从笨鸟到菜鸟之(一百零四)java操作office和pdf文件(二)利用POI实现数据导出excel报表...

在上一篇博客中&#xff0c;我们简单介绍了java读取word&#xff0c;excel和pdf文档内容 &#xff0c;但在实际开发中&#xff0c;我们用到最多的是把数据库中数据导出excel报表形式。不仅仅简单的读取office中的数据.尤其是在生产管理或者财务系统中用的非常普遍&#xff0c;因…

为什么 HTTP3.0 使用 UDP 协议?

还记得以前我提过的常见面试题么&#xff1a;从浏览器地址栏输入网址&#xff0c;到网页彻底打开&#xff0c;中间都发生了什么&#xff1f;从浏览器输入网址&#xff0c;到网页打开&#xff0c;发生了什么&#xff0c;这题有多经典&#xff0c;很多业内技术大牛说用过这题面试…

程序员为啥365天都背电脑包?这答案我服!

全世界只有3.14 % 的人关注了数据与算法之美最近微博上有个最新热门话题“关于报BUG&#xff08;漏洞&#xff09;的礼仪”不要跟程序员说程序有BUG他们第一反应是&#xff1a;你的环境有问题吧&#xff1f;接着就是&#xff1a;XXX你会用吗&#xff01;&#xff08;此处不可描…

html li 做瀑布流,js实现瀑布流效果(自动生成新的内容)

当滚动条接近底部会自动生成新的内容(色块)效果图&#xff1a;代码如下&#xff1a;Title*{list-style: none;}div{overflow: hidden;}ul{float: left;}li{width:300px; margin-bottom:10px;}function rnd(n,m){return parseInt(Math.random()*(m-n))n;}function cl(){var li …

六元均匀直线阵的各元间距为_实验二 均匀直线阵

实验二均匀直线阵一、实验目的&#xff1a;通过MATLAB编程&#xff0c;了解均匀直线阵的辐射特性&#xff0c;熟悉影响天线阵辐射的各种因素及其产生的影响。二、实验环境&#xff1a;MATLAB软件三、实验原理&#xff1a;单个天线的方向性是有限的&#xff0c;为了加强天线的定…