Caddy-用Go写的新一代可扩展WebServer

前几天用 Netmaker 的时候发现它用 Caddy 替换掉了 Nginx,用了后发现确实简单好用,就安利一下。

Caddy 是一个强大的、可扩展的平台,用 Go 编写,可以为你的站点、服务和应用程序提供服务。如果你是 Caddy 的新手,你服务网络的方式将会改变。

引言

大多数人使用 Caddy 作为网络服务器或代理,但在其核心,Caddy 是一个服务器的服务器(a server of servers)。通过必要的模块,它可以承担任何长时间运行的进程的角色!

配置是动态的和可通过 Caddy 的 API 导出 。虽然不需要配置文件,但是您仍然可以使用它们; 大多数人最喜欢的配置 Caddy 的方法是使用 Caddyfile。配置文档的格式通过配置适配器采用多种形式,但 Caddy 的本地配置语言是 JSON。

Caddy 为所有主流平台编译,并且没有运行时依赖项。

新手指南

我们建议每个人不管经验如何都要看一下我们的入门指南。它将为你提供一个全面的视角来看待你的新网络服务器,这对你继续学习是无价的。本教程将探索使用 Caddy 的基础知识,并帮助您在更高的层次上熟悉它。

目的:

  • 运行守护进程
  • 试试 API
  • 给 Caddy 一个配置
  • 测试配置
  • 制作一个 Caddyfile
  • 使用配置适配器(config adapter)
  • 从一个初始配置开始
  • 比较 JSON 和 Caddyfile
  • 比较 API 和配置文件
  • 在后台运行
  • 零停机时间配置重载

先决条件

  • 已安装 caddycurl(安装 Caddy 可以参考这里)

要启动 Caddy 作为一个守护程序,使用 run 子命令:

caddy run

默认情况下,Caddy 的配置(“ config”)为空。我们可以使用另一个终端访问管理 API 来验证这一点:

curl localhost:2019/config/

{% note info %}
ℹ️ 信息

上面地址不是你的网站,localhost:2019 是用来控制 Caddy 的管理端点,并被默认限制为本机访问。

我们可以通过给它一个配置来使 Caddy 变得有用。这可以通过多种方式完成,但是我们将在下一节使用 curl/load 端点发出 POST 请求。

你的第一个配置

为了准备我们的请求,我们需要做一个配置。

将其保存到 JSON 文件中(例如 caddy.JSON) :

{
	"apps": {
		"http": {
			"servers": {
				"example": {
					"listen": [":2015"],
					"routes": [
						{
							"handle": [{
								"handler": "static_response",
								"body": "Hello, world!"
							}]
						}
					]
				}
			}
		}
	}
}

然后上传:

curl localhost:2019/load \
	-X POST \
	-H "Content-Type: application/json" \
	-d @caddy.json

我们可以通过如下命令验证 Caddy 将我们的新配置应用到另一个 GET 请求:

curl localhost:2019/config/

然后测试新的配置:

$curl localhost:2015
Hello, world!

如果你看到 Hello, world! 那恭喜了,成功了!确保配置按预期的方式工作总是一个好主意,尤其是在部署到生产环境之前。

你的第一个 Caddyfile

另一种配置 Caddy 的方法是 Caddyfile。上面我们在 JSON 编写的配置可以简单地表达为:

:2015

respond "Hello, world!"

将其保存到工作目录文件中名为 Caddyfile (无扩展名)的文件中。

如果 Caddy 已经在运行, (Ctrl + c)停止它,然后运行:

caddy adapt

或者你把 Caddyfile 存储在别的地方,或者给它取了别的名字:

caddy adapt --config /path/to/Caddyfile

您将看到 JSON 输出! 这里发生了什么?

我们只是使用配置适配器将 Caddyfile 转换为 Caddy 的原生 JSON 结构。

虽然我们可以获得这个输出并发出另一个 API 请求,但是我们可以跳过所有这些步骤,因为 caddy 命令可以为我们完成这些操作。如果工作目录中有一个叫 Caddyfile 的文件,并且没有指定其他配置,Caddy 会加载 Caddyfile,为我们改编,然后马上运行。

现在当前文件夹中有一个 Caddyfile,让我们再次运行 caddy:

caddy run

或者如果你的 Caddyfile 在其他地方:

caddy run --config /path/to/Caddyfile

(如果调用的是不以“ Caddyfile”开头的其他名称,则需要指定 --adapter caddyfile)

正如你所看到的,有几种方法可以让你使用初始配置启动 Caddy:

  • 在工作目录一个名为 Caddyfile 的文件
  • --config flag (可选项,带有--adapter flag)
  • --resume flag (如果先前加载了配置)

JSON vs. Caddyfile

现在您知道了,Caddyfile 刚刚为您转换为 JSON。

Caddyfile 看起来比 JSON 简单,但是你应该一直使用它吗?每种方法都有利有弊。答案取决于您的需求和用例。

JSON Caddyfile
完整的 Caddy 功能 最常见的 Caddy 功能部件
易于生成 易于手工制作
易于编程 难以自动化
非常有表现力 适度的表达
允许配置遍历 不能在 Caddyfile 间转换
部分配置更改 只能修改整个配置
可以导出 无法导出
与所有 API 端点兼容 与某些 API 端点兼容
自动生成的文档 文档是手写的
无处不在 小众
更有效率 更多的计算
有点无聊 挺有意思的
了解更多:JSON 结构 了解更多:Caddyfile 文档

您将需要决定哪一个最适合您的用例。

需要注意的是,JSON 和 Caddyfile (以及任何其他支持的配置适配器)都可以与 Caddy 的 API 一起使用。然而,如果您使用 JSON,您将获得 Caddy 的全部功能和 API 特性。如果使用配置适配器,使用 API 加载或更改配置的唯一方法是 /load 端点。

API vs. 配置文件

{% note info %}
ℹ️ 信息

实际上,即使是配置文件也要经过 Caddy 的 API 端点,命令只是为您包装了这些 API 调用。

您还需要决定您的工作流是基于 API 的还是基于 CLI 的。(您可以在同一台服务器上同时使用 API 和配置文件,但我们不推荐这样做: 最好有一个真实的来源。)

API 配置文件
使用 HTTP 请求修改配置 使用 shell 命令修改配置
易于扩大规模 难以规模化
手工操作难度大 易于手工操作
真的很有趣 也很有趣
了解更多:API 教程 了解更多:Caddyfile 教程

{% note info %}
ℹ️ 信息

使用 API 手动管理服务器配置完全可以通过适当的工具实现,例如: 任何 REST 客户端应用程序

或配置文件工作流的选择与配置适配器的使用是正交的: 你可以使用 JSON,但存储在一个文件中,并使用命令行界面; 相反,你也可以使用 Caddyfile 与 API。

但是大多数人会使用 json + api 或 Caddyfile + CLI 组合。

如您所见,Caddy 非常适合于各种各样的用例和部署!

Start,stop,run

因为 Caddy 是一个服务器,所以它可以无限期地运行。这意味着在执行 caddy run 之后,终端不会解除阻塞,直到进程终止(通常使用 Ctrl + c)。

虽然 caddy run 是最常见的,通常是推荐的(特别是在进行系统服务时!),你也可以选择使用 caddy start 启动 Caddy,并让它在后台运行:

caddy start

这将允许您再次使用您的终端,这在一些交互式无头环境中非常方便。

然后你必须自己停止这个过程,因为 Ctrl + c 不会为你停止:

caddy stop

或者使用 API 的/stop 端点。

重新加载配置

您的服务器可以执行零停机时间配置重载/更改。

加载或更改配置的所有 API 端点都是完美的,并且没有停机时间。

但是,在使用命令行时,可能很容易使用 Ctrl + c 来停止服务器,然后再重新启动服务器以获取新的配置。不要这样做: 停止和启动服务器与配置更改是正交的,并将导致停机。

相反,使用 caddy reload 命令来优雅地修改配置:

caddy reload

这实际上只是在引擎盖下使用了 API。它将加载并在必要时将配置文件调整为 JSON,然后在不停机的情况下优雅地替换活动配置。

如果加载新配置时出现任何错误,Caddy 将回滚到上次工作的配置。

{% note info %}
ℹ️ 信息

从技术上讲,新配置是在停止旧配置之前启动的,因此在很短的时间内,两个配置都在运行!如果新配置失败,它将中止一个错误,而旧配置则根本不会停止

Caddy 常用功能

  1. 静态文件访问
  2. 反向代理
  3. HTTPS

静态文件访问

命令行方式

在终端中,切换到站点的根目录并运行:

caddy file-server

如果你得到一个权限错误,这可能意味着你的操作系统不允许你绑定到低端口---- 所以改用高端口:

caddy file-server --listen :2015

然后在浏览器中打开 (或 localhost:2015)查看您的网站!

如果你没有索引文件,但是你想要显示一个文件列表,可以使用 --browse 选项:

caddy file-server --browse

您可以使用另一个文件夹作为站点根目录:

caddy file-server --root ~/mysite

Caddyfile 方式

在站点的根目录中,创建一个名为 Caddyfile 的文件,其中包含以下内容:

localhost

file_server

或:

localhost

file_server browse

localhost

root * /home/me/mysite
file_server

分别对应以上的几个命令。

反向代理

本教程假设您有一个运行在 127.0.0.1:9000 上的后端 HTTP 服务。

命令行方式

很直白,直接能看明白:

caddy reverse-proxy --to 127.0.0.1:9000

如果你没有权限绑定到低端口,你可以从高端口代理:

caddy reverse-proxy --from :2016 --to 127.0.0.1:9000

Caddyfile 方式

直接上配置:

localhost

reverse_proxy 127.0.0.1:9000

caddy run 运行即可

更改代理的地址很容易:

:2016

reverse_proxy 127.0.0.1:9000

更改 Caddyfile 时,请确保重新加载 Caddy (或停止并重新启动它)。

使用反向代理指令以做很多事情。

HTTPS

本指南将向您展示如何立即使用完全自管理的 HTTPS 启动和运行。

{% note info %}
ℹ️ 信息

在默认情况下,Caddy 对所有站点使用 HTTPS,只要在配置中提供了主机名。本教程假设您希望通过 HTTPS 获得一个公共受信任的站点(即不是“ localhost”) ,因此我们将使用一个公共域名和外部端口

先决条件:

  • 对 DNS 的基本了解
  • 注册的公共域名
  • 对端口80和443的外部访问
  • 已安装 caddycurl

在本教程中,将 example.com 替换为您的实际域名。

设置域名的 A/AAAA 记录指向服务器。您可以通过登录到您的 DNS 提供商和管理您的域名来做到这一点。

在继续之前,用权威 lookup 验证正确的记录。用你的域名替换 example.com,如果你使用的是 IPv6,把 type=A 替换为 type=AAAA:

curl "http://cloudflare-dns.com/dns-query?name=example.com&type=A" \
  -H "accept: application/dns-json"

{% note info %}
ℹ️ 提示

一切的前提是你是在 cloudflare 上买的这个域名。
如果不是的话,步骤会复杂一些。
参见这篇 域名在 DNSPod 上的证书申请方式

还要确保您的服务器在端口80和443上是可以从公共接口访问的。

{% note info %}
ℹ️ 提示

如果您在您的家庭或其他受限制的网络,您可能需要转发端口或调整防火墙设置

所有我们需要做的是开始用您的域名配置 Caddy。有几种方法可以做到这一点。

Caddyfile

这是获取 HTTPS 的最常用方法。

创建一个名为 Caddyfile (无扩展名)的文件,其中第一行是您的域名,例如:

example.com

respond "Hello, privacy!"

然后从同一个目录中运行:

caddy run

您将看到 Caddy 提供一个 TLS 证书,并通过 HTTPS 服务您的站点。这是可能的,因为你的网站在 Caddyfile 中的地址包含一个域名。

file-server 命令

caddy file-server --domain example.com

可以了。

reverse-proxy 命令

caddy reverse-proxy --from example.com --to localhost:9000

总结

Caddy 吸引我的地方:

  1. 自动申请续约证书
  2. 简单命令的 Caddyfile
  3. Go 编写,Caddy 为所有主流平台编译,并且没有运行时依赖项。

???

参考资料

  • Install — Caddy Documentation (caddyserver.com)
  • Build from source — Caddy Documentation (caddyserver.com)
  • Getting Started — Caddy Documentation (caddyserver.com)
  • Quick-starts — Caddy Documentation (caddyserver.com)
  • How to use DNS provider modules in Caddy 2 - Wiki - Caddy Community

三人行, 必有我师; 知识共享, 天下为公. 本文由东风微鸣技术博客 EWhisper.cn 编写.

本文转载于网络 如有侵权请联系删除

相关文章

  • 浏览器指纹追踪技术,如何完整修改浏览器指纹?

    来源|http://www.fly63.com/article/detial/10479 什么是浏览器指纹“浏览器指纹”是一种通过浏览器对网站可见的配置和设置信息来跟踪Web浏览器的方法,浏览器指纹就像我们人手上的指纹一样,具有个体辨识度,只不过现阶段浏览器指纹辨别的是浏览器。人手上的指纹之所以具有唯一性,是因为每个指纹具有独特的纹路、这个纹路由凹凸的皮肤所形成。每个人指纹纹路的差异造就了其独一无二的特征。那么浏览器指纹也是同理,获取浏览器具有辨识度的信息,进行一些计算得出一个值,那么这个值就是浏览器指纹。辨识度的信息可以是UA、时区、地理位置或者是你使用的语言等等,你所选取的信息决定了浏览器指纹的准确性。对于网站而言,拿到浏览器指纹并没有实际价值,真正有价值的是这个浏览器指纹对应的用户信息。作为网站站长,收集用户浏览器指纹并记录用户的操作,是一个有价值的行为,特别是针对没有用户身份的场景。例如在一个内容分发网站上,用户A喜欢浏览二次元的内容,通过浏览器指纹记录这个兴趣,那么下次用户不需要登录即可向A用户推送二次元的信息。在个人PC如此普及的当下,这也是一种内容分发的方式。对于用户而言

  • 基于Jira的运维发布平台的设计与实现

    上线发布是运维的日常工作,常见的发布方式有:手动发布Jenkins发布平台GitlabCI......除此之外还有需要开源软件,他们都有非常不错的发布管理功能。面临的问题作为运维人员,上线发布是必不可少的一环,一个正常的发布流程是怎么样的?需求方提发布任务,走发布流程供应方执行发布上线环节看似简单,但是中间其实是有断层的。一般企业在走上线流程都是通过一些公共渠道,比如邮件、钉钉、飞书的流程,这些都很难和运维执行上线发布平台进行关联上,而且也不够直观。所以我们就需要解决以下几个问题:流程和运维平台建立连接从发起到结束形成闭环为了选择JIRA?JIRA优秀的项目管理,问题跟踪的工具,另外它的流程管理和看板模式也能够非常直观看到目前流程处在什么位置。另外它可以通过webhook和其他平台建立友好的连接,方便扩展。再者对于开发、测试、项目管理人员等来说Jira是他们日常的工具,使用熟练度非常高,降低了额外的学习成功。鉴于此,我们选择JIRA作为运维发布平台,争取做到一个平台做所有事。方案设计设计思路充分利用Jira、Gitlab的webhook功能,以及Jenkins的灵活性。Jira上更新状

  • 优秀的 Java 项目,代码都是如何分层的?

    !1、背景说起应用分层,大部分人都会认为这个不是很简单嘛就controller,service,mapper三层。看起来简单,很多人其实并没有把他们职责划分开,在很多代码中,controller做的逻辑比service还多,service往往当成透传了,这其实是很多人开发代码都没有注意到的地方,反正功能也能用,至于放哪无所谓呗。这样往往造成后面代码无法复用,层级关系混乱,对后续代码的维护非常麻烦。的确在这些人眼中分层只是一个形式,前辈们的代码这么写的,其他项目代码这么写的,那么我也这么跟着写。但是在真正的团队开发中每个人的习惯都不同,写出来的代码必然带着自己的标签,有的人习惯controller写大量的业务逻辑,有的人习惯在service中之间调用远程服务,这样就导致了每个人的开发代码风格完全不同,后续其他人修改的时候,一看,我靠这个人写的代码和我平常的习惯完全不同,修改的时候到底是按着自己以前的习惯改,还是跟着前辈们走,这又是个艰难的选择,选择一旦有偏差,你的后辈又维护你的代码的时候,恐怕就要骂人了。所以一个好的应用分层需要具备以下几点:方便后续代码进行维护扩展;分层的效果需要让整个

  • C++类和对象的概念

    参考链接:C++类和对象C++类和对象的概念 面向对象程序设计  概念:(ObjectOrientedProgramming,缩写:OOP)是一种程序开发的方法。对象指的是类的实例,将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性 在c语言中使用struct可以对数据进行抽象封装,在c++中用class封装程序和数据,其访问权限体现了封装性。 封装:隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互 struct和class的区别? struct不能存放函数,class可以存放成员函数struct默认的变量和函数都是public,也就是不限制权限,可在结构外任意访问,而class默认privateclass里的变量可以先使用可以在定义之前 c++有四个作用域 局部,全局,类域和命名空间。其中类的作用域很关键。 类的成员在类的作用域内,可以任意互相访问。对象可以通过.运算符访问共有成员,对象指针用->。在类外定义成员,用::指明成员函数所属类域。 类和对象的区别? 类就像是一张图纸,限定了类的成员,并未分配实际内存来存储,也像是一种自定义数据类

  • Pblock可以这么画

    手工布局应该算是一项高级技能,在某些场合是不可或缺的,例如PartialReconfiguration。同时,它也是实现时序收敛的一种可选方法。首先,打开综合后的设计,将Vivado切换到Floorplanning模式,如下图所示。一旦切换到Floorplanning模式,Vivado会自动打开PhysicalConstraints窗口(也可以通过Window->PhysicalConstraints打开此窗口)和Device窗口,如下图所示。至此,我们就可以开始手工布局。手工布局的本质是对指定逻辑单元设定面积约束,在Vivado下就是对其画一个Pblock。Pblock的大小限定了该逻辑单元所能使用的FPGA资源;Pblock的位置限定了该逻辑单元在FPGA中的位置;Pblock中所包含的FPGA资源类型限定了该逻辑单元所能使用的资源。第二步,在Netlist窗口中选择需要手工布局的逻辑单元,这里我们选取了模块arnd1,如下图所示。之后在Device窗口中点击蓝色椭圆标记的快捷键。第三步,一旦点击上图标记2的按钮,就可以开始画Pblock,如下图所示。图中Grids显示了该P

  • 4.定义并执行方法

    1.定义并执行方法<template> <divid="app"> <!--执行自定义方法--> <buttonv-on:click=run1()>执行方法的第一种写法</button> <buttonv-on:click=run2()>执行方法的第二种写法</button> </div> </template> <script> exportdefault{ name:'app', data(){ return{ } }, methods:{ run1(){ alert('第一种写法') }, run2(){ alert('第2种写法') } } } </script> <style> </style>复制

  • 浅谈指数增强

    什么是指数增强? 在说指数增强之前,首先要说明什么是指数。指数在各个领域中都广为应用,是一种重要的参考指标,比如衡量经济发展水平的GDP指数、反映消费水平/通胀水平的CPI指数、反映股票市场价格水平的价格指数等等。都是将大量个体的指标通过某种规则进行合成,用来反映整体的情况。以股票指数为例,股票指数实际上是通过某种选股规则,筛选出一篮子股票,计算股票组合的总市值后,归一化为股票的点数,用来代表某一特定风格的股票组合价值随时间的变化情况,比如反映大盘走势的沪深300,中小盘的中证500,各行业的行业指数等等。 解释完股票,还需要说明基金的一些基本概念,这里完全是个人理解,不是标准定义,需要标准概念的可以百度。基金是为了某种目的设立的一定数量资金。二级市场的基金设立的目的是用来投资股票、债券等等金融产品。基金从管理方式来说有两种,一种是被动管理,也叫指数基金,投资方式是跟踪市场上的某一种指数,用资金去复制指数的成分股进行投资。另一种是主动管理,希望通过个人的主观能动性获取超越指数的收益。 从CAPM的角度来说,被动管理赚的是beta,风险小,主动管理赚的是alpha,风险高。从有效市场假设

  • Mybatis深入了解(五)----动态SQL

    什么是动态SQL?实例Mapper.xml测试代码sql片段定义sql片段<!--定义sql片段 id:sql片段的唯一标识 经验:是基于单表来定义sql片段,这样话这个sql片段可重用性才高 在sql片段中不要包括where --> <sqlid="query_user_where"> <iftest="userCustom!=null"> <iftest="userCustom.sex!=nullanduserCustom.sex!=''"> anduser.sex=#{userCustom.sex} </if> <iftest="userCustom.username!=nullanduserCustom.username!=''"> anduser.usernameLIKE'%${userCustom.username}%' </if> </if&g

  • Rainbond插件体系设计简介

    过去几年,利用容器打包和部署代码的方式日益流行,越来越多企业开始测试或是已经在生产环境中运行了微服务架构应用,开始直接面对和解决分布式服务化架构演变中出现的各种问题。在这样的趋势和大环境下,无服务器PaaS**Rainbond**围绕着服务的拓展、监控、治理等角度,进行了一系列思考和尝试,插件体系正是其中的重要一环。Rainbond的插件体系抽象集中在平台的业务层面,理论基础源于Kubernetes的pod机制和一部分容器概念。针对平台业务层面对kubernetes容器编排进行抽象,转变为一个对用户体验友善的Rainbond插件产品的过程,方便用户在不需要懂Kubernetes原理的情况下使用。设计原则Rainbond插件体系的设计遵循**易于理解**和**易于使用**的原则:易于理解在Rainbond插件体系中,插件使用的过程即主容器与init或sidecar等容器结合的过程,原理是将插件容器以sidecar容器(大部分)的形式编排至主应用的pod中,共享主应用容器的网络和环境变量,因此可以插件化实现某些附加功能,例如对主应用进行流量分析等。PodPod是Kubernetes中模块化

  • 21天实战人工智能系列:人工智能产品经理最佳实践(1)

    一、前言打算面向想从事人工智能产品经理职位的人,写一个系列的专题,对人工智能产品经理做一个全面的介绍,初步计划写21个专题,每天一篇,算是对自己的一种鞭策,每天的任务定性,定量,希望自己能够坚持下来。适应人群:想要转型做人工智能的传统产品经理;RD想要转型做AIPM的人群;一切想从事或了解人工智能产品经理工作的人;屏蔽人群:希望通过本课程学习编码能力的人。 二、正文2.1章节目标 了解是什么是人工智能?了解人工智能核心概念?了解人工智能发展简史?了解人工智能当前的市场格局?2.2内容2.2.1人工智能的概念定义 人工智能的概念定义2.2.2人工智能、机器学习和深度学习的关系人工智能相关概念的关系2.2.3人工智能的主要特征:自动化+智能化人工智能两大主要特征2.2.4人工智能的行业格局中国人工智能市场生态图谱2.2.5人工智能发展简史人工智能发展简史三、未完待续下期预告:人工智能产品经理能力的概念定义和能力模型作者介绍张子良现百度在线大数据产品设计师毕业于烟台大学计算机科学与技术专业,原天云融创数据科技有限公司高级咨询顾问,拥有5年以上金融和互联网广告行业经验。资深数据产品专家,数据仓

  • Microsoft 线性回归分析算法

    前言在此系列中涵盖了微软在商业智能(BI)模块系统所能提供的所有挖掘算法,当然此框架完全可以自己扩充,可以自定义挖掘算法,不过目前此系列中还不涉及,只涉及微软提供的算法,当然这些算法已经基本涵盖大部分的商业数据挖掘的应用场景,也就是说熟练了这些算法大部分的应用场景都能游刃有余的解决,每篇算法总结包含:算法原理、算法特点、应用场景以及具体的操作详细步骤。本篇介绍的为Microsoft线性回归分析算法,此算法其实原理和Microsoft神经网络分析算法一样,只是侧重点不一样,Microsoft神经网络算法是基于某种目的,利用现有数据进行“诱探”分析,侧重点是分析,而Microsoft线性回归分析算法侧重的是“预测”,也就是基于神经网络分析出来的规则,进行结果的预测。应用场景介绍该算法的应用场景和上一篇的Microsoft神经网络分析算法一样,不清楚的可以点击查看,可以简单列举:营销和促销分析,如评估直接邮件促销或一个电台广告活动的成功情况。根据历史数据预测股票升降、汇率浮动或其他频繁变动的金融信息。分析制造和工业流程。文本挖掘。分析多个输入和相对较少的输出之间的复杂关系的任何预测模型。其实

  • c#之线程总结(一)

    在我们做项目的时候会经常用到线程,但线程也不是万能的,用线程需要注意的东西也很多,自己做了一下总结这次总结主要说三个部分1线程之委托方法2给线程传参3三种方法控制线程同步我们先看一下小例子:usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading; namespaceThreadMethod { classProgram { staticvoidMain(string[]args) { ThreadStart_ts=newThreadStart(MyThread); Thread_thread1=newThread(_ts); _thread1.Start(); Console.WriteLine("otherMetod"); Console.ReadLine(); } publicstaticvoidMyThread() { for(inti=0;i<10;i++) { Console.WriteLine

  • 【原】vue开发笔记

    vue.js获取dom元素高度的方法 <divref="test"></div> lettestHeight=this.$refs.test.offsetHeight复制 vue.js中内联样式style、class三元表达式 //style三元表达式 <div:style="{'color':(isActive?'#000':'#fff')}"></div> //class三元表达式 <div:class="[isActive?'test1':'test2']"></div>复制 vue-router中params和query的区别 1.引入方式不同 query要用path来引入 this.$router.push({ path:'test', query:{ type:2, detail:'哈哈' } })复制 params要用name来引入 this.$router.push({ name:'test', params:{ type:2, detail:'哈哈' } })复制 2.

  • 1

          Hello,world!      

  • 时间&gt;金钱【抱歉,博客消息未及时回复,可直接加Q316187205】

    时间>金钱! 如果有机会,用你的金钱去换取别人的成功经验,一定要抓住一切机会向顶尖人士学习。 仔细选择你接触的对象,因为这会节省你很多时间。 假设与一个成功者在一起,他花了10年时间成功,你跟10个这样的人交往,你不是就浓缩了100年的经验? 从小时候的幼儿园到现在的大学,获取知识的背后都是由金钱支撑着,课外辅导班更是一笔不菲的数字。 知识可以改变命运,这句话没有错。 用部分金钱获取更多的知识,更快的掌握知识,不是一种浪费,而是对自己一种更好的投资。 故事一: 一个女孩刚毕业不久便想学游泳。表姐说我会,有时间我教你。 可是两人空余时间总是不在一起。 半年后女孩自己花费两千多元,报了一个游泳培训班。 仅用了十几天的时间,就掌握了游泳技巧,可以在水中畅游。 表姐知道了,就说她:我可以教你干嘛还要花那冤枉钱? 表妹说,这不是冤枉钱呀!花钱去请专业教练教我,学的动作标准,成效快节省时间,而且花了钱我会更努力的去学呀!     故事二: 技术员老王在SA的IT部门呆了5年,前两年学EBS开发,两年下来磕磕碰碰,仅具备报表和功能基础开发能力。后三年仍然沉浸在技术领域,

  • Jenkins系统上的时间不正确问题

    ### 1、点击系统管理,选择执行脚本命令: 1、打开【系统管理】->【脚本命令行】运行下面的命令 System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone','Asia/Shanghai')2、修改容器时区dockerrun-d-uroot--namejenkins\-p8088:8080-p50000:50000\-v /home/wx/jenkins:/var/jenkins_home\--envJAVA_OPTS="-Xms2048m-Xmx2048m-Xmn512m-XX:MetaspaceSize=256m-XX:MaxMetaspaceSize=256m-Duser.timezone=Asia/Shanghai"\jenkinszh/jenkins-zh:2.267复制 2、修改位置 复制 3、修改后效果 复制     ###

  • 需求管理(REQM,Requirements Management)工具(转)

    需求管理(REQM,RequirementsManagement)属于成熟度2级(受管理级)的过程域,是其他许多过程域实施的前提。对于暂未实施CMMI的企业,同样也可以借鉴CMMI的原则,实施和优化需求管理。 许多IT企业都有过需求失控的痛苦经历,我们不难体会,没有好的需求管理会给我们带来什么: ☹ 需求以失控的状态进入软件过程,从源头上失去了项目的质量保证; ☹  需求范围界定不清,使项目缺乏计划性,导致成本、研制周期失控; ☹  需求变更失控,使组织处于被动反应式的环境中,项目组成为救火队; ☹  需求管理不当,导致项目延期、士气低落,增加了项目的失败风险; ☹  ……   为了避免上述情况的出现,CMMI对需求管理提出了明确的目的:一是管理项目的产品和产品构件的需求;二是标识哪些需求与项目计划及工作产品之间不一致。通过适当的步骤,确保需求在项目的各个层面上动态地保持一致,一旦出现不一致,则启动相关的处理过程域,使其调整到一致。   需求管理的工具包括: ①需求及相关文档管

  • Maven最佳实践

    1.插件配置 1.1.Copy-Dependency 用maven来管理应用,经常会用到这个插件,他的功能很强大,暂说说他的一个功能吧。就是可以将依赖的jar文件拷贝到你指定的文件夹。 使用例子如下: 1<build> 2<plugins> 3<plugin> 4<artifactId>maven-dependency-plugin</artifactId> 5<version>2.1</version> 6<executions> 7<execution> 8<id>copy-dependencies</id> 9<phase>prepare-package</phase> 10<goals> 11<goal>copy-dependencies</goal> 12</goals> 13</execution> 14</executions> 15<co

  • 对于(function(){}())和function(){}实例的作用域分析(里面有很多问题……)

      今天在群里看到一个问题,让我纠结了好一会。下面是我的分析,感觉里面还有很多问题,关于作用域还是不太理解,希望大家看到问题第一时间反馈给我,看到实在受不了的地方说几句都没关系,谢谢。   请看题: 1.对象字面量中fn1函数是立即执行的函数表达式。 $(function(){ varnumber=2; varobj={ number:4, fn1:(function(){ this.number*=2; number=number*2; varnumber=3; returnfunction(){ this.number*=2; number*=3; alert(number); } })() }; varf=obj.fn1; alert(number);/*2*/ f();/*9*/ obj.fn1();/*27*//*因为是自执行的,number值是可以保存下来的*/ alert(window.number);/*NaN*/         alert(obj.number);/*8*/ }) </script>复制   输出2,9,27,NaN,8。   分析

  • GEO代码分析流程 - 3. 数据检查(质控) - PCA图、相关性热图

    0.准备 setwd("D:/R/CHOL") rm(list=ls()) load(file="step1output.Rdata") load(file="step2output.Rdata")复制  1. 主成分分析图(PrincipalComponentAnalysis,PCA) 输入数据:exp(表达矩阵)和group_list(分组信息) PCA代码来源:http://www.sthda.com/english/articles/31-principal-component-methods-in-r-practical-guide/112-pca-principal-component-analysis-essentials dat=as.data.frame(t(exp)) library(FactoMineR) library(factoextra) dat.pca<-PCA(dat,graph=FALSE) pca_plot<-fviz_pca_ind(dat.pca, geom.ind="point", col.ind=gro

  • 找不对自定义typeHandler:java.lang.IllegalStateException: No typehandler found for mapping ...

    1、自定义typeHandler packagecom.apollo.cloud.common.core.mybatis.typehandler; importjava.sql.CallableStatement; importjava.sql.PreparedStatement; importjava.sql.ResultSet; importjava.sql.SQLException; importcn.hutool.json.JSONObject; importcn.hutool.json.JSONUtil; importorg.apache.ibatis.type.BaseTypeHandler; importorg.apache.ibatis.type.JdbcType; importorg.apache.ibatis.type.MappedJdbcTypes; importorg.apache.ibatis.type.MappedTypes; @MappedTypes({JSONObject.class}) @MappedJdbcTypes({JdbcType.VARC

相关推荐

推荐阅读