2024年11月

1、引言

在当今的互联网时代,API(应用程序编程接口)已经成为连接不同软件系统的桥梁。作为一名开发者,掌握API测试技能至关重要。市面上的API测试工具琳琅满目,今天我们要介绍的是一款开源、跨平台的API测试工具——
Insomnia

2、Insomnia介绍

Insomnia
是一款功能丰富、易使用、强大且开源的API测试工具,广泛应用于API的开发、测试与调试。它以其简洁的界面、强大的功能和免费的开源协议赢得了众多开发者和测试人员的青睐。Insomnia不仅支持HTTP、HTTPS、REST等多种请求类型,还扩展了对GraphQL、gRPC、SOAP和WebSockets等协议的支持,使其成为一个多功能的API测试工具。通过一系列强大的功能简化了API的设计和测试流程。

3、Insomnia特点与优势

Insomnia
不仅仅是一个API测试工具,它是一个功能齐全的IDE(集成开发环境),用于设计、测试、调试和管理API。作为一个强大的开源工具,Insomnia拥有众多特点和优势,使其在开发者中获得了极大的认可和流行。

1. 开源与跨平台特性

Insomnia作为一款开源工具,‌坚持开源协议,‌用户可以在任何地方免费使用。‌它支持Windows、‌macOS和Linux等多个操作系统,‌这意味着开发者可以在自己熟悉的平台上进行API测试,‌无需担心平台兼容性问题。‌Insomnia的跨平台特性极大地提升了其适用性和灵活性。‌

2. 直观易用的用户界面

Insomnia提供了简洁直观的用户界面,‌无论是前端开发、‌后端开发还是测试人员,‌都能快速上手。‌界面清晰明了,‌使得用户可以轻松创建和管理多个API请求。‌通过直观的界面,‌用户可以设置请求方法(‌如GET、‌POST、‌PUT、‌DELETE等)‌、‌输入URL、‌添加请求头和参数,‌并发送请求查看响应结果。‌

3. 强大的功能支持

Insomnia不仅支持多种HTTP请求方法和请求头设置,‌还提供了丰富的测试和调试功能。‌例如,‌它支持环境变量和模板标签,‌允许在不同环境之间共享参数和动态生成请求数据。‌这对于在开发、‌测试和生产环境之间切换非常有用。‌此外,‌Insomnia还记录了请求和响应历史,‌方便用户查看以前的交互,‌这对于排查问题和理解API行为非常有帮助。‌

4. 团队协作与版本控制

在团队协作方面,‌Insomnia支持通过Git进行版本控制,‌方便多人共同管理和测试API。‌用户可以导出请求、‌环境变量和设置,‌或从文件导入,‌以便与团队共享或备份。‌这种灵活的协作方式极大地提升了团队的工作效率和协作能力。‌

5. 插件生态系统

Insomnia的一个显著特点是其强大的插件系统。‌通过插件,‌用户可以扩展和定制Insomnia的功能,‌以满足个性化的需求。‌Insomnia官方提供了一个插件市场Plugin Hub,‌发布官方和社区开发的各种插件。‌这些插件可以增加新的功能、‌集成第三方服务或改进用户界面。‌例如,‌通过插件,‌用户可以实现自动同步、‌Swagger导入、‌数据格式转换等高级功能。‌

4、Insomnia适用场景

Insomnia
作为一款强大的开源工具,适用于API的开发、测试与调试领域。其跨平台、直观易用的特性,使得它在多种场景下都能发挥重要作用。以下是Insomnia的主要适用场景:

1. 开发者个人使用

对于独立开发者而言,Insomnia提供了一个轻量级且高效的解决方案来测试和调试他们的RESTful API。无需安装复杂的集成开发环境(IDE)或额外的插件,Insomnia就能满足基本到高级的API测试需求。

2. 开发团队协作

在开发团队中,Insomnia支持导出和导入请求集合,这极大地方便了团队成员之间的共享和协作。团队成员可以轻松地共享API请求、测试案例和测试结果,从而提高整体的开发效率和测试质量。

3. API文档生成与验证

Insomnia不仅限于测试和调试API,它还能与Swagger等API文档工具集成,自动生成API文档。这对于需要频繁更新和维护API文档的项目来说,无疑是一个巨大的便利。同时,Insomnia的测试结果也可以作为API文档的一部分,帮助验证API的准确性和可靠性。

4. 自动化测试

虽然Insomnia本身并不直接提供自动化测试的功能,但它可以与持续集成/持续部署(CI/CD)流程结合使用。通过编写脚本或利用Insomnia提供的插件和扩展,开发者可以自动化地运行API测试,并在每次代码提交时验证API的功能和性能。

5. 学习和教育

对于初学者和教育工作者来说,Insomnia也是一个理想的工具。它提供了直观的用户界面和易于理解的文档,帮助学习者快速上手API开发和测试。同时,Insomnia的开源特性也使得它成为了一个学习和研究API测试技术的绝佳平台。

5、安装

Mac安装:

brew install --cask insomnia

Windows安装:

访问链接:
https://insomnia.rest/download
下载即可
在这里插入图片描述

更多安装方式可见:
https://docs.insomnia.rest/insomnia/install

6、小结

Insomnia是一款功能强大、易用性强的开源API测试工具,适用于各种API测试场景。掌握Insomnia的使用方法,有助于提高开发者工作效率,确保API质量。如果您还在为选择API测试工具而犹豫,不妨试试Insomnia,相信它会成为您开发过程中的得力助手。

参考文献:

  • Insomnia官方文档

    https://docs.insomnia.rest/

  • GitHub insomnia

    https://github.com/Kong/insomnia

前言

.NET 生态中有哪些值得推荐的网络通信框架?今天,给大家推荐一个非常优秀的开源项目——NetCoreServer。

NetCoreServer 是一款 .NET 开源、免费、快速且低延迟的异步套接字服务器和客户端库。无论是需要搭建高性能的服务端应用,还是开发高效的客户端程序,NetCoreServer 都能提供强大的支持。

项目介绍

NetCoreServer 是一个高性能、跨平台的异步套接字服务器和客户端库。

它支持多种传输协议,包括 TCP、SSL、UDP、HTTP、HTTPS 和 WebSocket,提供了丰富的网络通信功能。

该库的设计初衷是解决高并发和低延迟的网络通信需求,采用了异步通信模型,能够高效处理大量连接和数据传输。

提供了丰富的示例包括 TCP 聊天服务器、SSL 聊天服务器和 UDP 回显服务器等多种应用场景。

项目特性

  • 跨平台:支持 Linux、macOS 和 Windows。
  • 异步通信:利用异步编程模式提高性能和响应性。
  • 多协议支持:支持 TCP、SSL、UDP、HTTP、HTTPS 和 WebSocket 协议。
  • 高性能:专为高性能通信设计,通过减少不必要的内存分配和 CPU 占用,以及优化网络 I/O 操作来提高性能。
  • 详细文档和示例:提供详细的文档和丰富的示例代码,帮助大家快速上手。

项目组件

  • TcpServer/TcpClient:用于处理 TCP 连接。
  • SslServer/SslClient:提供 SSL/TLS 加密的 TCP 连接。
  • UdpServer/UdpClient:支持 UDP 数据报传输。
  • WsServer/WsClient:支持 WebSocket 通信。
  • WssServer/WssClient:支持安全的 WebSocket(WebSocket Secure)通信。
  • UdsServer/UdsClient:支持 Unix Domain Socket 通信(仅限 Unix/Linux 系统)。
  • HttpServer/HttpClient:提供 HTTP 和 HTTPS 服务器的实现,支持静态内容服务和自定义请求处理。

项目环境

  • 操作系统:Linux、macOS、Windows
  • 开发环境:.NET 6.0、7-Zip、CMake、Git、Visual Studio

项目示例

  • TCP 聊天服务器、聊天客户端
  • SSL 聊天服务器、聊天客户端
  • UDP 回显服务器、回显客户端
  • UDP 组播服务器、组播客户端
  • Unix Domain Socket 聊天服务器、聊天客户端
  • 简单协议
  • 简单协议服务器、简单协议客户端
  • HTTP/HTTPS服务器、客户端
  • WebSocket 聊天服务器、聊天客户端
  • WebSocket 安全聊天服务器、安全聊天客户端

性能测试

1、往返测试

  • TCP/SSL/UDP 回显服务器
  • Unix Domain Socket 回显服务器
  • 简单协议服务器
  • WebSocket 回显服务器/安全回显服务器

2、组播测试

  • TCP/SSL/UDP 组播服务器
  • Unix Domain Socket 组播服务器
  • WebSocket 组播服务器/安全组播服务器

3、Web 服务器测试

  • HTTP/HTTPS 跟踪服务器

项目使用

1、TCP 聊天服务器

它处理多个 TCP 客户端会话,并将从任何会话接收到的消息广播到所有会话。另外,还可以直接从服务器发送管理员消息。

namespaceTcpChatServer
{
classChatSession : TcpSession
{
public ChatSession(TcpServer server) : base(server) {}protected override voidOnConnected()
{
Console.WriteLine($
"Chat TCP session with Id {Id} connected!");//发送欢迎消息 string message = "Hello from TCP chat! Please send a message or '!' to disconnect the client!";
SendAsync(message);
}
protected override voidOnDisconnected()
{
Console.WriteLine($
"Chat TCP session with Id {Id} disconnected!");
}
protected override void OnReceived(byte[] buffer, long offset, longsize)
{
string message = Encoding.UTF8.GetString(buffer, (int)offset, (int)size);
Console.WriteLine(
"Incoming:" +message);//向所有已连接的会话广播消息 Server.Multicast(message);//如果接收到的消息为 '!',则断开当前会话 if (message == "!")
Disconnect();
}
protected override voidOnError(SocketError error)
{
Console.WriteLine($
"Chat TCP session caught an error with code {error}");
}
}
classChatServer : TcpServer
{
public ChatServer(IPAddress address, int port) : base(address, port) {}protected override TcpSession CreateSession() { return new ChatSession(this); }protected override voidOnError(SocketError error)
{
Console.WriteLine($
"Chat TCP server caught an error with code {error}");
}
}
classProgram
{
static void Main(string[] args)
{
//TCP 服务器端口 int port = 1111;if (args.Length > 0)
port
= int.Parse(args[0]);

Console.WriteLine($
"TCP 服务器端口: {port}");

Console.WriteLine();
//创建一个新的 TCP 聊天服务器 var server = newChatServer(IPAddress.Any, port);//启动服务器 Console.Write("服务器启动中...");
server.Start();
Console.WriteLine(
"完成!");

Console.WriteLine(
"按 Enter 停止服务器或输入 '!' 重启服务器...");//执行文本输入 for(;;)
{
string line =Console.ReadLine();if (string.IsNullOrEmpty(line))break;//重启服务器 if (line == "!")
{
Console.Write(
"服务器重启中...");
server.Restart();
Console.WriteLine(
"完成!");continue;
}
//向所有会话广播管理员消息 line = "(admin)" +line;
server.Multicast(line);
}
//停止服务器 Console.Write("服务器停止中...");
server.Stop();
Console.WriteLine(
"完成!");
}
}
}

2、TCP 聊天客户端

它连接到 TCP 聊天服务器,允许发送消息到服务器并接收新消息。

namespaceTcpChatClient
{
classChatClient : TcpClient
{
public ChatClient(string address, int port) : base(address, port) {}public voidDisconnectAndStop()
{
_stop
= true;
DisconnectAsync();
while(IsConnected)
Thread.Yield();
}
protected override voidOnConnected()
{
Console.WriteLine($
"Chat TCP client connected a new session with Id {Id}");
}
protected override voidOnDisconnected()
{
Console.WriteLine($
"Chat TCP client disconnected a session with Id {Id}");//等待一段时间... Thread.Sleep(1000);//尝试再次连接 if (!_stop)
ConnectAsync();
}
protected override void OnReceived(byte[] buffer, long offset, longsize)
{
Console.WriteLine(Encoding.UTF8.GetString(buffer, (
int)offset, (int)size));
}
protected override voidOnError(SocketError error)
{
Console.WriteLine($
"Chat TCP client caught an error with code {error}");
}
private bool_stop;
}
classProgram
{
static void Main(string[] args)
{
//TCP 服务器地址 string address = "127.0.0.1";if (args.Length > 0)
address
= args[0];//TCP 服务器端口 int port = 1111;if (args.Length > 1)
port
= int.Parse(args[1]);

Console.WriteLine($
"TCP server address: {address}");
Console.WriteLine($
"TCP server port: {port}");

Console.WriteLine();
//创建一个新的 TCP 聊天客户端 var client = newChatClient(address, port);//连接客户端 Console.Write("Client connecting...");
client.ConnectAsync();
Console.WriteLine(
"Done!");

Console.WriteLine(
"Press Enter to stop the client or '!' to reconnect the client...");//执行文本输入 for(;;)
{
string line =Console.ReadLine();if (string.IsNullOrEmpty(line))break;//断开客户端连接 if (line == "!")
{
Console.Write(
"Client disconnecting...");
client.DisconnectAsync();
Console.WriteLine(
"Done!");continue;
}
//将输入的文本发送到聊天服务器 client.SendAsync(line);
}
//断开客户端连接 Console.Write("Client disconnecting...");
client.DisconnectAndStop();
Console.WriteLine(
"Done!");
}
}
}

以上只展示的部分示例,更多示例大家可以访问项目地址获取。

项目地址

GitHub:
https://github.com/chronoxor/NetCoreServer

在线文档:
https://chronoxor.github.io/NetCoreServer

总结

本文只展示部分功能和内容,如果您对项目感兴趣可以访问系统地址获取详细信息和体验。

希望本文能在.NET 网络通信方面为各位提供有益的帮助。期待大家在评论区留言交流,分享您的宝贵经验和建议。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!

大家好,我是汤师爷~

想要深入理解零售企业的组织架构并不容易。大多数人并没有实际经营过零售企业,更不曾参与设计其组织架构。

在调研商家的过程中,我们通常只能了解他们组织架构的现状,却难以直接与企业高层沟通,深入理解组织架构设计背后的逻辑。很多时候,我们只能通过业务场景的表象,去推测商家的组织设计意图。

然而,要为商家提供有竞争力的数字化产品,首先需要确保其组织数字化的合理性。组织管理是一切业务的起点,否则会产生以下问题:商家觉得产品逻辑奇葩、难以理解,运营效率低;而对于业务端来说,许多场景的实现方式别扭,业务操作效率低。

零售企业就像一台精密的机器,可以分为三大要素:组织、流程和资源。

  • 组织
    :企业内部相互协作的团队和部门,为了共同实现企业目标而联系在一起。
  • 流程
    :为达成某个业务目标,由不同角色分工完成的一系列活动,通常具有严格的先后顺序,并明确规定了活动内容、方式及输入输出。例如,门店补货流程、订单履约流程等。
  • 资源
    :包括有形资源(如资金、设备、门店等)和无形资源(如品牌、信息、技术等)。

机器的外部环境也可抽象为几个核心要素:设计者、盈利目标、分钱规则、经营结果。

  • 设计者
    :通常是企业的决策层,即公司的创始人或合伙人。
  • 盈利目标
    :例如,先定个小目标,赚他一个亿。
  • 分钱规则
    :所有零售企业的财务目标都是盈利。盈利之后,根据事先约定的规则,将利润分配给各团队,以保障机器的正常运转。
  • 经营结果
    :财务周期结束后,机器产出经营结果,包括各类经营数据、收入、成本、利润等。

机器运转过程中,决策层不仅会在起点给出输入(盈利目标、分钱规则),还会在事前、事中、事后进行管理,调整组织、流程、资源等关键要素。

经营结果
是一个特别需要关注的要素。决策层会通过观察经营结果,依据分钱规则对各团队进行资金分配或激励,但这一切都需要数据支持,尤其是团队贡献的收入、成本、费用等数据。

此外,决策层还需复盘经营结果,进行根因分析,在新周期前将企业这台“机器”调整到最佳状态,以提升业绩表现。

所有这些分析工作都依赖于大量结构化的数据支撑,而最重要的分析维度正是
管理分类
维度。

零售管理分类法

零售企业是个相当复杂的业务和社会系统,包含了许多要素:组织、地理位置、资源、流程、经营理念,以及把它们有机结合的机制。

一种重要的管理方法是把这些要素划分成多维度的分类视图,然后分别进行管理。

只要每个分类都管理好,叠加起来,大概率就能把企业管理好。通过这些分类方法,零售企业可以更好地组织决策和执行计划,也能抽象或细化需要解决的问题。

1、商品分类法

也就是商品品类管理。长期以来,百货超市的基础经营管理就是品类管理。它是通过给商品设定不同的角色定位,确定不同的品类策略。

大多数企业一般划分五大品类策略:目标性品类、重点品类、补充性品类、季节性品类和便利性品类。不同的企业可能有不同的品类划分和策略。

商品品类管理的核心理念是通过商品来影响顾客,把门店的商品形象准确地传递给消费者。通过商品打动消费者,让他们多买或持续回购,最终提升门店的业绩。

2、责权分类法

前面提到,零售企业是一个高度复杂的业务和社会系统。责任分配是经营企业非常重要的机制,能让零售业务灵活扩展。

从模型上看,责权分类法主张把企业划分为一个个责权明确的组织单元。他们通常具备一定范围的职责和权力,计划、执行、衡量各自的业务活动。

这个概念非常重要,尤其在生成经营报表时。在财务核算中,收入中心、成本中心、利润中心必须关联到某个组织单元,这样才能产出可衡量的经营成果。

3、地理分类法

一些跨地域的零售企业,会按照地理维度来划分经营活动,比如省、市、区县、乡镇,或者华东、华中、华南大区等。

相比责权分类法,地理分类法提供了不同的管理视角。它能帮助企业洞察经营活动背后的一些额外因素,如地域差异、人文差异、地理环境差异等,这些因素可能严重影响企业的业绩。

需要注意的是,有些企业表面上按地理分类划分组织,例如华东区、华中区、华南区,但本质上可能是责权分类法。关键要看是否使用财务衡量指标(收入、成本、费用等)来考核这些组织单元。

4、部门岗位分类法

部门岗位分类法是把组织划分为一系列的部门和岗位。根据部门和岗位的工作性质、责任轻重、难易程度和所需专业资质等,划分出不同的种类和等级。对从事不同工作的人,采用不同的要求和管理方法,是一种以“因事择人”为中心的管理方式。

特别要注意的是,有些企业中,部门岗位分类法划分的组织和责权分类法划分的组织,可能看起来相似,但底层逻辑完全不同。

部门岗位分类法面向人和事,以“因事择人”为中心;而责权分类法面向组织单元,这个组织单元不是具体的人和事,更像是一个可以承载团队、流程、资源等生产要素的容器,以收益和成本为中心。

从顺序上看,先有责权架构,再有部门岗位架构。就像先立王,再招兵买马,立王象征着建立责权,随后才涉及人和事的安排。从粒度上看,责权架构的粒度比部门岗位架构更粗。

组织管理的核心概念模型

在了解了零售管理的各种分类方法后,我们需要深入探讨组织管理的核心概念模型,所有的管理分类方法,最终都会体现在组织架构上,因为组织架构是经营理念的载体。组织管理的核心概念模型如图所示。

1、基础概念

  • 组织单元
    :所有组织都是由组织单元构成的。组织单元是一个抽象的概念,类似一个可以装东西的容器,可以装入团队、业务流程、资源等生产要素。
  • 组织单元的上下级关系
    :组织单元之间有上下级关系,最基本的应用场景包括汇报和数据统计汇总。
  • 组织树
    :当组织单元通过上下级关系连接起来,就形成了一棵组织树。组织树需要明确关联一种组织视图类型。

2、组织视图类型

对于中大型零售企业来说,企业内部分工明确,通常划分为多个业务系统,例如采购系统、供应链系统、销售系统、仓配系统、CRM 系统、HR 系统、财务系统等。这些系统会以不同的视角使用组织数据。

因此,需要引入了
组织视图类型
的概念,通过多组织视图的方式来管理和使用组织数据。以下是一些常见的组织视图类型:

  • 业务组织类型
    :企业按照特定业务模式划分的组织类型。例如,零售企业的运营组织需要制定企业和分店的业绩目标,推动目标实现,管理并指导各个分店的工作。业务组织类型可以根据管理复杂度进一步细分,如运营组织、采购组织、物流组织等。
  • 行政组织类型
    :与前面提到的部门岗位分类法的管理逻辑一致。对应企业中真实存在的组织单元,包括各层级、各部门、各职位等,按照职能目标分工。每个职能岗位有明确的权责分配和工作流程,是当前企业内部的真实结构。
  • 财务组织类型
    :财务组织单元是独立的会计核算主体,主要用于财务会计系统。每个财务组织单元都有一套完整的账套,能够独立生成三大财务报表:资产负债表、损益表、现金流量表。一般情况下,每个法人主体都会对应一个财务组织。
  • 责任中心类型
    :与之前提到的责权分类法的管理逻辑一致。责任中心是管理会计中的概念,指承担一定经济责任并享有一定权利的组织单元。责任中心可以划分为成本中心、利润中心和投资中心。

3、组织单元的核心属性

我们刚才讲了组织单元的基本概念和不同的组织视图类型。现在,让我们来看看组织单元的核心属性,这些属性决定了组织单元的性质和作用。

  • 组织单元的形态
    :也就是组织单元的类型说明,比如集团、公司、分公司、事业部、部门、区域、门店、电商网店等。需要特别注意,这本质上是一种标签,对业务应用没有影响,只是方便团队理解。
  • 组织单元的法人属性
    :明确组织单元的法人性质,包括:
    • 法人企业
      (集团、公司)
    • 法人分支机构
      (分公司、加盟商)
    • 非法人机构
      (事业部、部门、门店等)
  • 组织单元的责任中心模式
    :责任中心模式可分为成本中心、利润中心和投资中心,用于管理会计分析。
  • 组织单元的业务能力
    :明确组织单元执行的业务范围,比如门店销售、加工、库存管理、要货、线上销售等能力。
  • 组织单元的业务终端属性
    :如果组织单元是业务终端,意味着它是组织树的叶子节点,直接从事一线业务活动。典型的业务终端包括门店、电商网店、配送中心。
  • 组织单元的数据共享模式
    :分为全局共享模式、部分共享模式和完全隔离模式。

本文已收录于,我的技术网站:
tangshiye.cn
里面有,算法Leetcode详解,面试八股文、BAT面试真题、简历模版、架构设计,等经验分享。

一.  新建一个自由风格的软件项目

二. General配置(参数化构建)

1. 用来选择部署的服务器(我这里只添加了一个,如果需要添加多个,一行一个就可以了)

2. 选择不同的环境变量

三、源码管理

1. 填写Github项目地址,选择Credential(Credential需要自己新建,根据自己情况添加即可)。

2. 在Branches to buid下边填写要拉取的远程分支(如图)

四、Buid Steps

1. 增加构建步骤-Send files or execute commands over SSH

(1)选择Name之前,需要提前添加需要ssh的服务器的主机信息(当前使用windows服务器演示),包括地址,用户名,密码,例如:

在Dashboard-系统管理-系统配置-Publish over SSH-新增

Name随便填,能用来分辨即可,Hostname即ip地址,Username就是ssh的用户名,如果ssh需要密码,则再“高级”中勾选 "Use password authentication, or use a different key",然后填写密码。

最后,在右下角点击“Test Configuration”,如果通过,则继续进行下一步。否则需要重复检查您自己的配置直到通过。

(2)继续回到添加构建步骤,如下图,Name选择刚刚添加的ssh服务器信息,然后选择“高级”,勾选Verbos output in console(从Jenkins控制台输出详细步骤),勾选Label,Label则填写参数化构建过程中的SSH_SERVER中的选项列表的选项对应(一个选项        对应一个ssh步骤模块)。

(3)Transfers配置

Exec command下边填写命令

d: &&
if not exist mt4-manager md mt4-manager &&
mkdir D:\\mt4-manager\\$MANAGER &&
cd "mt4-manager" &&
if not exist publish md publish &&
cd "d:\\mt4-manager\\publish" &&
if not exist $MANAGER md $MANAGER

释义:发送cmd命令

d: ->跳转到D盘

    if not exist mt4-manager md mt4-manager -> 如果不存在mt4-manager文件夹则创建
    mkdir D:\\mt4-manager\\$MANAGER -> 创建动态名称目录($MANAGER对应参数化构建过程中的MANAGER对应的选项)
    cd "mt4-manager" -> 跳转到mt4-manager目录
后边的命令同以上


“高级”菜单中,Exec timeout修改为0,并且勾选Exec in pty。

2. 在同模块下,添加Transfer Set子模块

在Exec command下边填写以下命令:

pm2 delete "mt4-manager-api-$MANAGER"

3. 添加构建步骤-执行shell

输入命令(拷贝Jenkins服务器上的代码到ssh服务器上对应的目录):

scp -r ../mt4-manager-api mt4@192.168.0.130:d:\\mt4-manager\\$MANAGER

4. 再次添加构建步骤-Send files or execute commands over SSH

配置跟刚刚配置的ssh模块一样,但是Exec command输入为:

d: &&
cd "D:\mt4-manager\$MANAGER\manager-api-v2" &&
dotnet publish -c Release -o D:\mt4-manager\publish\$MANAGER &&
cd "D:\mt4-manager\publish\$MANAGER" &&
powershell -Command "(Get-Content appsettings.json -Raw) -replace 'devDemo','$MANAGER' | Set-Content appsettings.json" &&
pm2 start "manager-api-v2.exe" --name "manager-api-$MANAGER" &&
echo start success

别忘了“高级”菜单中的配置

释义:

进入d盘,进入manager-api-v2文件夹(项目根目录),

发布项目到指定文件夹

进入发布后的文件夹项目根目录

使用powershell命令,将appsettings.json中配置的环境变量值,由devDemo替换为动态参数值$MANAGER,对应参数化构建中的$MANAGER中的选项

使用pm2命令运行.net core程序,名称为manager-api-(动态参数)

输出 start success

到此,整个的配置过程就完成了。查看结果

如有错误,请大佬指正!

MySQL--DAY04

索引

定义

索引是在数据库
表的字段
上添加的,是为了
提高查询效率
存在的一种机制。

一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。

索引相当于一本书的目录,是为了
缩小扫描范围
而存在的一种机制。

对于一本字典来说,查找某个汉字有两种方式:

  • 第一种方式:一页一页挨着找,直到找到为止,这种查找方式属于全字典扫描。效率比较低。
  • 第二种方式:先通过目录(索引)去定位一个大概的位置,然后直接定位到这个位置,做局域性扫描,缩小扫描的范围,快速的查找。这种查找方式属于通过索引检索,效率较高。
	t_user
	id(idIndex)	name(nameIndex)	email(emailIndex)		address  (emailAddressIndex)
	----------------------------------------------------------------------------------
	1				zhangsan...
	2				lisi
	3				wangwu
	4				zhaoliu
	5				hanmeimei
	6				jack

	select * from t_user where name = 'jack';

以上的这条SQL语句会去name字段上扫描,为什么?

因为查询条件是:name='jack'

如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,

MySQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低。

MySQL在查询方面主要就是两种方式:

第一种方式:全表扫描

第二种方式:根据索引检索。

注意:

在实际中,汉语字典前面的目录是
排序
的,按照a b c d e f....排序,

为什么排序呢?因为只有排序了才会有区间查找这一说!(缩小扫描范围

其实就是扫描某个区间罢了!)

在mysql数据库当中
索引也是需要排序
的,并且这个索引的排序和TreeSet

数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql

当中索引是一个
B-Tree
数据结构。

遵循左小右大原则存放。采用中序遍历方式遍历取数据。

原理

假设有一张用户表:t_user

	id(PK)					name						每一行记录在硬盘上都有物理存储编号
	----------------------------------------------------------------------------------
	100						zhangsan					0x1111
	120						lisi						0x2222
	99						wangwu						0x8888
	88						zhaoliu						0x9999
	101						jack						0x6666
	55						lucy						0x5555
	130						tom							0x7777

提醒1:在任何数据库当中
主键
上都会
自动添加索引对象
,id字段上自动有索引,因为id是PK。另外在mysql当中,一个字段上如果有
unique约束
的话,也会
自动创建索引对象

提醒2:在任何数据库当中,任何一张表的
任何一条记录
在硬盘存储上都有一个硬盘的
物理存储编号

提醒3:在mysql当中,索引是一个
单独的对象
,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个

的形式存在。(自平衡二叉树:
B-Tree

在mysql当中,主键上,以及unique字段上都会自动添加索引的!!!!

什么条件下,我们会考虑给字段添加索引呢?

  • 条件1:数据量庞大(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同)
  • 条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。
  • 条件3:该字段很少的DML(insert delete update)操作。(因为DML之后,索引需要重新排序。)

建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。

建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。

创建和删除索引

创建索引:

mysql> create index emp_ename_index on emp(ename);

给emp表的ename字段添加索引,起名:emp_ename_index

删除索引:

mysql> drop index emp_ename_index on emp;

将emp表上的emp_ename_index索引对象删除。

查看SQL语句是否使用索引

	mysql> explain select * from emp where ename = 'KING';
	+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
	| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
	+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
	|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL |   14 | Using where |
	+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

扫描14条记录:说明没有使用索引。type=ALL

	mysql> create index emp_ename_index on emp(ename);

	mysql> explain select * from emp where ename = 'KING';
	+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
	| id | select_type | table | type | possible_keys   | key             | key_len | ref   | rows | Extra       |
	+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
	|  1 | SIMPLE      | emp   | ref  | emp_ename_index | emp_ename_index | 33      | const |    1 | Using where |
	+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+

索引失效

索引什么时候失效?

失效的第1种情况

select * from emp where ename like '%T';

ename上即使添加了索引,也不会走索引,为什么?

原因是因为模糊匹配当中以“%”开头了!

尽量避免模糊查询的时候以“%”开始。

这是一种优化的手段/策略。

		mysql> explain select * from emp where ename like '%T';
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL |   14 | Using where |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

失效的第2种情况

使用or的时候会失效,如果使用or那么要求
or两边的条件字段都要有索引
,才会走索引,如果其中一边有一个字段没有索引,那么另一个字段上的索引也会实现。所以这就是为什么不建议使用or的原因。

mysql> explain select * from emp where ename = 'KING' or job = 'MANAGER';
	+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
	| id | select_type | table | type | possible_keys   | key  | key_len | ref  | rows | Extra       |
	+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
	|  1 | SIMPLE      | emp   | ALL  | emp_ename_index | NULL | NULL    | NULL |   14 | Using where |
	+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+

失效的第3种情况

使用复合索引的时候,没有使用左侧的列查找,索引失效

什么是复合索引?

两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。

		create index emp_job_sal_index on emp(job,sal);
		
		mysql> explain select * from emp where job = 'MANAGER';
		+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
		| id | select_type | table | type | possible_keys     | key               | key_len | ref   | rows | Extra       |
		+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
		|  1 | SIMPLE      | emp   | ref  | emp_job_sal_index | emp_job_sal_index | 30      | const |    3 | Using where |
		+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
		
		mysql> explain select * from emp where sal = 800;
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL |   14 | Using where |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

失效的第4种情况

在where当中索引列参加了运算,索引失效。

		mysql> create index emp_sal_index on emp(sal);

		explain select * from emp where sal = 800;
		+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
		| id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra       |
		+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
		|  1 | SIMPLE      | emp   | ref  | emp_sal_index | emp_sal_index | 9       | const |    1 | Using where |
		+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+

		mysql> explain select * from emp where sal+1 = 800;
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL |   14 | Using where |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

失效的第5种情况

在where当中索引列使用了函数

		explain select * from emp where lower(ename) = 'smith';
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
		|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL |   14 | Using where |
		+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

失效的第...种情况

索引分类

  1. 单一索引:一个字段上添加索引。
  2. 复合索引:两个字段或者更多的字段上添加索引。
  3. 主键索引:主键上添加索引。
  4. 唯一性索引:具有unique约束的字段上添加索引。
    .....

注意:唯一性比较弱的字段上添加索引用处不大。

视图

什么是视图?

view:站在不同的角度去看待同一份数据。

创建和删除视图对象

	表复制:
	mysql> create table dept2 as select * from dept;

	dept2表中的数据:
	mysql> select * from dept2;
	+--------+------------+----------+
	| DEPTNO | DNAME      | LOC      |
	+--------+------------+----------+
	|     10 | ACCOUNTING | NEW YORK |
	|     20 | RESEARCH   | DALLAS   |
	|     30 | SALES      | CHICAGO  |
	|     40 | OPERATIONS | BOSTON   |
	+--------+------------+----------+

创建视图对象:

	create view dept2_view as select * from dept2;

删除视图对象:

	drop view dept2_view;	

注意:只有DQL语句才能以view的形式创建。

create view view_name as // 这里的语句必须是DQL语句;

视图的作用

我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致
原表被操作

(视图的特点:通过对视图的操作,会影响到原表数据。)

	//面向视图查询
	select * from dept2_view; 

	// 面向视图插入
	insert into dept2_view(deptno,dname,loc) values(60,'SALES', 'BEIJING');

	// 查询原表数据
	mysql> select * from dept2;
	+--------+------------+----------+
	| DEPTNO | DNAME      | LOC      |
	+--------+------------+----------+
	|     10 | ACCOUNTING | NEW YORK |
	|     20 | RESEARCH   | DALLAS   |
	|     30 | SALES      | CHICAGO  |
	|     40 | OPERATIONS | BOSTON   |
	|     60 | SALES      | BEIJING  |
	+--------+------------+----------+

	// 面向视图删除
	mysql> delete from dept2_view;

	// 查询原表数据
	mysql> select * from dept2;
	Empty set (0.00 sec)
	

	// 创建视图对象
	create view 
		emp_dept_view
	as
		select 
			e.ename,e.sal,d.dname
		from
			emp e
		join
			dept d
		on
			e.deptno = d.deptno;

	// 查询视图对象
	mysql> select * from emp_dept_view;
	+--------+---------+------------+
	| ename  | sal     | dname      |
	+--------+---------+------------+
	| CLARK  | 2450.00 | ACCOUNTING |
	| KING   | 5000.00 | ACCOUNTING |
	| MILLER | 1300.00 | ACCOUNTING |
	| SMITH  |  800.00 | RESEARCH   |
	| JONES  | 2975.00 | RESEARCH   |
	| SCOTT  | 3000.00 | RESEARCH   |
	| ADAMS  | 1100.00 | RESEARCH   |
	| FORD   | 3000.00 | RESEARCH   |
	| ALLEN  | 1600.00 | SALES      |
	| WARD   | 1250.00 | SALES      |
	| MARTIN | 1250.00 | SALES      |
	| BLAKE  | 2850.00 | SALES      |
	| TURNER | 1500.00 | SALES      |
	| JAMES  |  950.00 | SALES      |
	+--------+---------+------------+

	// 面向视图更新
	update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING';

	// 原表数据被更新
	mysql> select * from emp;
	+-------+--------+-----------+------+------------+---------+---------+--------+
	| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |
	+-------+--------+-----------+------+------------+---------+---------+--------+
	|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |
	|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |
	|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |
	|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |
	|  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |
	|  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |    NULL |     30 |
	|  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 1000.00 |    NULL |     10 |
	|  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |    NULL |     20 |
	|  7839 | KING   | PRESIDENT | NULL | 1981-11-17 | 1000.00 |    NULL |     10 |
	|  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |
	|  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |    NULL |     20 |
	|  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |    NULL |     30 |
	|  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |    NULL |     20 |
	|  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1000.00 |    NULL |     10 |
	+-------+--------+-----------+------+------------+---------+---------+--------+

实际开发中的作用?

《方便,简化开发,利于维护》

		create view 
			emp_dept_view
		as
			select 
				e.ename,e.sal,d.dname
			from
				emp e
			join
				dept d
			on
				e.deptno = d.deptno;

假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。

每一次使用这个sql语句的时候都需要重新编写,很长,很麻烦,怎么办

可以把这条复杂的SQL语句以视图对象的形式新建。

在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。

并且
利于后期的维护
,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的SQL语句。

我们以后面向视图开发的时候,使用视图的时候可以像使用table一样。

可以对视图进行增删改查等操作。视图不是在内存当中,视图对象也是存储在
硬盘
上的,不会消失。

再提醒一下

视图对应的语句
只能是DQL语句
。但是视图对象创建完成之后,可以对视图进行增删改查等操作。

小插曲:

增删改查,又叫做:CRUD。

CRUD是在公司中程序员之间沟通的术语。一般我们很少说增删改查。

一般都说CRUD。

C:Create(增)
R:Retrive(查:检索)
U:Update(改)
D:Delete(删)

DBA常用命令?

重点掌握:
数据的导入和导出

数据的备份

其它命令了解一下即可。

数据导出

注意:在windows的dos命令窗口中:

mysqldump study>D:\study.sql -uroot -p123456

可以导出指定的表吗?

mysqldump study emp>D:\study.sql -uroot -p123456

数据导入

注意:需要先登录到mysql数据库服务器上。

然后创建数据库:

create database study;

使用数据库:

use study

然后初始化数据库:source D:\study.sql

数据库设计三范式

什么是数据库设计三范式?

数据库表的
设计依据
。教你怎么进行数据库表的设计。

数据库设计三范式有几类?

3个。

  1. 第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
  2. 第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
  3. 第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。

声明:三范式是面试官经常问的,所以一定要熟记在心!

设计数据库表的时候,按照以上的范式进行,可以
避免表中数据的冗余,空间的浪费

第一范式

最核心,最重要的范式,所有表的设计都需要满足。

必须有主键
,并且每一个字段都是
原子性
不可再分。

	学生编号 	学生姓名 			联系方式
	---------------------------------------------------
	1001		张三			zs@gmail.com,1359999999
	1002		李四			ls@gmail.com,13699999999
	1001		王五			ww@163.net,13488888888

以上是学生表,满足第一范式吗?

不满足,第一:没有主键。第二:联系方式可以分为邮箱地址和电话

	学生编号(pk) 		学生姓名		邮箱地址				联系电话
	--------------------------------------------------------------------
	1001				张三			zs@gmail.com		1359999999
	1002				李四			ls@gmail.com		13699999999
	1003				王五			ww@163.net			13488888888

第二范式

建立在第一范式的基础之上,要求
所有非主键字段必须完全依赖主键

不要产生部分依赖

学生编号 		学生姓名 	教师编号 	教师姓名
----------------------------------------------------
1001			张三		001			王老师
1002			李四		002			赵老师
1003			王五		001			王老师
1001			张三		002			赵老师

这张表描述了学生和老师的关系:(1个学生可能有多个老师,1个老师有多个学生)

这是非常典型的:
多对多
关系!

分析以上的表是否满足第一范式?

不满足第一范式。

怎么满足第一范式呢?修改

	学生编号+教师编号(pk)			学生姓名  		教师姓名
	----------------------------------------------------
	1001			001				张三			王老师
	1002			002				李四			赵老师
	1003			001				王五			王老师
	1001			002				张三			赵老师

学生编号 教师编号,两个字段联合做主键,
复合主键
(PK: 学生编号+教师编号)

经过修改之后,以上的表满足了第一范式。但是满足第二范式吗?

不满足,“张三”依赖1001,“王老师”依赖001,显然产生了
部分依赖

产生部分依赖有什么缺点?

数据冗余
了。
空间浪费
了。“张三”重复了,“王老师”重复了。

为了让以上的表满足第二范式,你需要这样设计:

使用三张表来表示多对多的关系!!!!

		学生表
		学生编号(pk)			学生名字
		------------------------------------
		1001					张三
		1002					李四
		1003					王五
		
		教师表
		教师编号(pk)		教师姓名
		--------------------------------------
		001					王老师
		002					赵老师

		学生教师关系表
		id(pk)					学生编号(fk)			教师编号(fk)
		-----------------------------------------------------------
		1						1001						001
		2						1002						002
		3						1003						001
		4						1001						002

背口诀:

多对多怎么设计?

多对多,三张表,关系表两个外键!!!!!!!!!!!!!!!

第三范式

第三范式建立在第二范式的基础之上

要求所有非主键字典必须直接依赖主键,不要产生
传递依赖

		学生编号(PK) 		学生姓名 班级编号  		班级名称
	---------------------------------------------------------
		1001				张三		01			一年一班
		1002				李四		02			一年二班
		1003				王五		03			一年三班
		1004				赵六		03			一年三班

以上表的设计是描述:班级和学生的关系。很显然是1对多关系!

一个教室中有多个学生。

分析以上表是否满足第一范式?

满足第一范式,有主键。

分析以上表是否满足第二范式?

满足第二范式,因为主键不是复合主键,没有产生部分依赖。主键是
单一主键

分析以上表是否满足第三范式?

第三范式要求:不要产生传递依赖!

一年一班依赖01,01依赖1001,产生了传递依赖。

不符合第三范式的要求。产生了数据的冗余。

那么应该怎么设计一对多呢?

		班级表:一
		班级编号(pk)					班级名称
		----------------------------------------
		01								一年一班
		02								一年二班
		03								一年三班

		学生表:多

		学生编号(PK) 		学生姓名 	班级编号(fk)
		-------------------------------------------
		1001				张三			01			
		1002				李四			02			
		1003				王五			03			
		1004				赵六			03		

背口诀:

一对多,两张表,多的表加外键!!!!!!!!!!!!

总结

一对多:一对多,两张表,多的表加外键!!!!!!!!!!!!

多对多:多对多,三张表,关系表两个外键!!!!!!!!!!!!!!!

一对一:一对一放到一张表中不就行了吗?为啥还要拆分表?

在实际的开发中,可能存在一张表字段太多,太庞大。这个时候要拆分表。

一对一怎么设计?

			没有拆分表之前:一张表
				t_user
				id		login_name		login_pwd		real_name		email				address........
				---------------------------------------------------------------------------
				1			zhangsan		123				张三				zhangsan@xxx
				2			lisi			123				李四				lisi@xxx
				...
			
			这种庞大的表建议拆分为两张:
				t_login 登录信息表
				id(pk)		login_name		login_pwd	
				---------------------------------
				1				zhangsan		123			
				2				lisi			123			

				t_user 用户详细信息表
				id(pk)		real_name		email				address........	login_id(fk+unique)
				-----------------------------------------------------------------------------------------
				100			张三				zhangsan@xxx								1
				200			李四				lisi@xxx										2

口诀:一对一,外键唯一!!!!!!!!!!

叮嘱

数据库设计三范式是
理论
上的。

实践和理论有的时候有偏差。

最终的目的都是为了
满足客户的需求
,有的时候会拿冗余换执行速度。

因为在sql当中,表和表之间连接次数越多,效率越低。(笛卡尔积)

有的时候可能会存在冗余,但是为了
减少表的连接次数
,这样做也是合理的,并且对于开发人员来说,sql语句的编写难度也会降低。

面试的时候把这句话说上:他就不会认为你是初级程序员了!