PHP100中文网 – 中国第一档PHP资源分享门户 http://www.php100.com PHP100中文网是国内第一家以PHP资源分享为主的专业网站,也提供了PHP中文交流社区。面向PHP学习研究者提供:最新PHP资讯、原创内容、开源代码和PHP视频教程等相关内容 Mon, 11 Nov 2019 02:38:17 +0000 zh-CN hourly 1 https://wordpress.org/?v=5.2.4 php经典趣味算法 http://www.php100.com/9/20/22/87445.html http://www.php100.com/9/20/22/87445.html#respond Mon, 11 Nov 2019 02:38:16 +0000 http://www.php100.com/?p=87445 一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴子就叫做大王。要求编程模拟此过程,输入m、n, 输出最后那个大王的编号。

function king($n, $m){

$monkeys = range(1, $n); //创建1到n数组

$i=0;

while (count($monkeys)>1) { //循环条件为猴子数量大于1

if(($i+1)%$m==0) { //$i为数组下标;$i+1为猴子标号

unset($monkeys[$i]); //余数等于0表示正好第m个,删除,用unset删除保持下标关系

else {

array_push($monkeys,$monkeys[$i]); //如果余数不等于0,则把数组下标为$i的放最后,形成一个圆形结构

unset($monkeys[$i]);

}

$i++;//$i 循环+1,不断把猴子删除,或 push到数组

}

return current($monkeys); //猴子数量等于1时输出猴子标号,得出猴王

}

echo king(6,3);

2、有一母牛,到4岁可生育,每年一头,所生均是一样的母牛,到15岁绝育,不再能生,20岁死亡,问n年后有多少头牛。

function niu($y){

static $num= 1; //定义静态变量;初始化牛的数量为1

for ($i=1; $i <=$y ; $i++) {

if($i>=4 && $i<15){ //每年递增来算,4岁开始+1,15岁不能生育

$num++;

niu($y-$i); //递归方法计算小牛$num,小牛生长年数为$y-$i

}else if($i==20){

$num–; //20岁死亡减一

}

return $num;

}

3、杨辉三角

<?php

/* 默认输出十行,用T(值)的形式可改变输出行数 */

class T{

private $num;

public function __construct($var=10) {

if ($var<3) die(“值太小啦!”);

$this->num=$var;

}

public function display(){

$n=$this->num;

$arr=array();

//$arr=array_fill(0,$n+1,array_fill(0,$n+1,0));

$arr[1]=array_fill(0,3,0);

$arr[1][1]=1;

echo str_pad(” “,$n*12,” “);

printf(“%3d”,$arr[1][1]);

echo “<br/>”;

for($i=2;$i<=$n;$i++){

$arr[$i]=array_fill(0,($i+2),0);

for($j=1;$j<=$i;$j++){

if($j==1)

echo str_pad(” “,($n+1-$i)*12,” “);

printf(“%3d”,$arr[$i][$j]=$arr[$i-1][$j-1]+$arr[$i-1][$j]);

echo ” “;

}

echo”<br/>”;

}

}

}

$yh=new T(‘3’); //$yh=new T(数量);

$yh->display();

?>

4.冒泡排序

function maopao($arr){

$len = count($arr);

for($k=0;$k<=$len;$k++)

{

for($j=$len-1;$j>$k;$j–){

if($arr[$j]<$arr[$j-1]){

$temp = $arr[$j];

$arr[$j] = $arr[$j-1];

$arr[$j-1] = $temp;

}

}

}

return $arr;

}

5.快速排序

function quickSort($arr) {

//先判断是否需要继续进行

$length = count($arr);

if($length <= 1) {

return $arr;

}

//选择第一个元素作为基准

$base_num = $arr[0];

//遍历除了标尺外的所有元素,按照大小关系放入两个数组内

//初始化两个数组

$left_array = array(); //小于基准的

$right_array = array(); //大于基准的

for($i=1; $i<$length; $i++) {

if($base_num > $arr[$i]) {

//放入左边数组

$left_array[] = $arr[$i];

else {

//放入右边

$right_array[] = $arr[$i];

}

}

//再分别对左边和右边的数组进行相同的排序处理方式递归调用这个函数

$left_array = quickSort($left_array);

$right_array = quickSort($right_array);

//合并

return array_merge($left_array, array($base_num), $right_array);

}

6.二分查找算法(折半查找算法)

function binsearch($x,$a){

$c=count($a);

$lower=0;

$high=$c-1;

while($lower<=$high){

$middle=intval(($lower+$high)/2);

if($a[$middle]>$x){

$high=$middle-1;

elseif($a[$middle]<$x){

$lower=$middle+1;

else{

return $middle;

}

}

return false;

}

7.PHP奇异算法

<?php

function test(){

$a=1;

$b=&$a;

echo (++$a)+(++$a);

}

test();

PHP7以下的版本返回的是 6,PHP7版本返回5 ,还真的算奇异,个人底层算法差,认为是PHP7以下版本的BUG

8.字符集合:输入一个字符串,求出该字符串包含的字符集合,并按顺序排序(英文)

function set($str){

//转化为数组

$arr = str_split($str);

//去除重复

$arr = array_flip(array_flip($arr));

//排序

sort($arr);

//返回字符串

return implode(”, $arr);

}

9.遍历一个文件下的所有文件和子文件夹下的文件

function AllFile($dir){

if($dh = opendir($dir)){

while (($file = readdir($dh)) !== false){

if($file !=’..’ && $file !=’.’){

if(is_dir($dir.’/’.$file)){

AllFile($dir.’/’.$file); //如果判断还是文件,则递归

}else{

echo $file; //输出文件名

}

}

}

}

}

10.从一个标准的Url提取出文件的扩展名

function getExt($url)

{

$arr = parse_url($url);

$file = basename($arr[‘path’]);// basename函数返回路径中的文件名部分

$ext = explode(‘.’, $file);

return $ext[count($ext)-1];

}

11.有个人想上一个n级的台阶,每次只能迈1级或者迈2级台阶,问:这个人有多少种方法可以把台阶走完?例如:总共3级台阶,可以先迈1级再迈2级,或者先迈2级再迈1级,或者迈3次1级总共3中方式

function jieti($num){ //实际上是斐波那契数列

return $num<2?1:jieti($num-1)+jieti($num-2);

}

12.请写一段PHP代码,确保多个进程同时写入同一个文件成功

<?php

$fp = fopen(“lock.txt”,”w+”);

if (flock($fp,LOCK_EX)) {

//获得写锁,写数据

fwrite($fp, “write something”);

// 解除锁定

flock($fp, LOCK_UN);

else {

echo “file is locking…”;

}

fclose($fp);

?>

13.无限级分类

function tree($arr,$pid=0,$level=0){

static $list = array();

foreach ($arr as $v) {

//如果是顶级分类,则将其存到$list中,并以此节点为根节点,遍历其子节点

if ($v[‘pid’] == $pid) {

$v[‘level’] = $level;

$list[] = $v;

tree($arr,$v[‘id’],$level+1);

}

}

return $list;

}

14.获取上个月第一天 和 最后一天

//获取上个月第一天

date(‘Y-m-01’,strtotime(‘-1 month’));

//获取上个月最后一天

date(‘Y-m-t’,strtotime(‘-1 month’));

15.随机输入一个数字能查询到对应的数据区间

//把区间换成数组写法,用二分法查找区间

function binsearch($x,$a){

$c=count($a);

$lower=0;

$high=$c-1;

while($lower<=$high){

$middle=intval(($lower+$high)/2);

if($a[$middle]>=$x){

$high=$middle-1;

}elseif($a[$middle]<=$x ){

$lower=$middle+1;

}

}

return ‘在区间’.$a[$high].’到’.$a[$lower];

}

$array = [‘1′,’50’,’100′,’150′,’200′,’250′,’300′];

$a = ‘120’;

echo binsearch($a,$array);

]]>
http://www.php100.com/9/20/22/87445.html/feed 0
PHP提高SESSION响应速度的方法有哪些 http://www.php100.com/9/20/87442.html http://www.php100.com/9/20/87442.html#respond Mon, 11 Nov 2019 02:36:59 +0000 http://www.php100.com/?p=87442 PHP提高SESSION响应速度的方法有哪些

1、设置多级目录存储SESSION

默认session的存储目录是1级目录,如果用户量比较大,session文件数量就比较大,我们可以设置目录数为2,使用2级目录可以提交查找和存取速度。不过这种方式对速度的提升一般不是很明显,可以通过修改php.ini,进而修改session存储目录数。

session.save_path = “2;/tmp”

2、将SESSION存储到redis中

php中的session默认是存储在文件中的,支持redis存储方式,因为redis的键值数据时存储在内存中的,可以提高session的存取速度。

session.save_handler = redis

session.save_path = “tcp://127.0.0.1:6379”

3、及时释放SESSION文件锁

我们在使用session时,需要先执行session_start()函数。

session_start()函数的作用如下:

判断http请求是否包含名为PHPSESSID的cookie,如果没有则创建该cookie并写入到http响应的头文件。

通过PHPSESSID查找对应的session文件,以读写方式打开的文件,然后读取里面的数据到内存。

然后我们一般会通过$_SESSION这个超全局变量,读取或者设置session的值,我们操作的时候,session的值都是保存在内存中的,默认在页面执行完毕之后,才会写入到对应的文件中。

我们通过下面的一段代码测试这个过程:

SESSION执行过程分析:

include “session_function.php”;

//session_function.php代码在附录

session_start();

$_SESSION[‘name’]=”koastal”;

echo “<br/>html content<br/>”;

var_dump($_SESSION);

echo “<br/>”;

输出结果:

open

read

html content

array (size=1)

‘name’ => string ‘koastal’ (length=7)

shutdown

write

close

通过上面的例子可以发现,在页面执行的过程中(页面的执行过程是指将php文件解析为对应的html文件的耗时,而不是用户在该页面上的停留时间),session文件是被锁定的。

]]>
http://www.php100.com/9/20/87442.html/feed 0
Git 2.24发布,功能亮点抢先看 http://www.php100.com/31/112/87437.html http://www.php100.com/31/112/87437.html#respond Fri, 08 Nov 2019 02:54:13 +0000 http://www.php100.com/?p=87437 Git 2.24发布,功能亮点抢先看

日前Git官方邮件组宣布发布Git客户端新版本2.24,该版本包含了来自78个贡献者的功能增加和bug修复。其中带来一些激动人心的功能,比如特性宏,commit-graph,新可选全新的仓库历史重写工具filter-repo等。本文虫虫就带大家抢先尝鲜。

Git 2.24发布,功能亮点抢先看

feature宏(feature.*)

从很开始,Git就附带一个配置子系统,可让我们配置不同的全局、用户级或者特定仓库级的设置。比如,我们第一次在电脑上做git 提交时,git会提醒要设置user.name和user.email。

实际上,git config可以做很多事情,从配置用户名称(user.name,user.email,author.name,author.email,committer.name,committer.email),设置文件断行符(ore.safecrlf)以及配置git命令别名(alias.*),git Diff的算法(diff.algorithm)等等。

一般情况下配置某些行为只需要配置一次即可,例如启用或禁用上面提到的这些参数。但是如果你不能确切的知道要更改哪些配置值时该怎么办?例如,假设你想尝试Git的最新功能,却没有发现新的可配置选项。在Git 2.24中,可以选择使用feature宏功能——一种包含许多其他功能的Git配置。这些是由Git的开发人员手动选择的,通过它可以选择加入某个功能或根据存储库的特性采用一些设置。

比如,有一个特别大的存储库,我们发现运行有些缓慢。通过参考各种文档,可能会找到答案,比如将index.version设置为4可能会有用。但是该探索的过程可能非常费劲,还不一定找得到最优的答案。但是现在你有了另外一种选择:通过下面方式启用feature.manyFiles:

这样,你就选择了Git最流畅体验的功能。通过配置该选项,表示愿意采用Git开发人员认为可以加速仓库的各种设置(包括index.version和core.untrackedCache分别启用路径前缀压缩和不跟踪的缓存)。

该配置项目以feature.开头,修改一组其他配置设置的默认值。这些组由Git开发人员社区创建,作为建议的默认值,可能会随时更改。目前feature宏功能只包括feature.experimental和feature.manyFiles,以后可能会新添加新的宏。feature.manyFiles上面已经介绍过了,feature.experimental则会默认开启以下配置:

在构造pack-file时使用了一种新算法,该算法可以提高多文件仓库时的git push性能。

可以一次跳过更多提交,从而减少往返次数,从而可以缩短获取协商的时间。

在每个从远程下载包文件的git fetch命令之后写入一个提交图。使用–split选项,大多数执行将在现有提交图文件之上创建一个非常小的提交图文件。有时,这些文件将合并,并且写入可能需要更长的时间。拥有更新的提交图文件可以提高很Git命令的性能,包括git merge-base,git push -f和git log –graph等。

默认启用提交图

在git 2.23中git引入了提交图。自从Git 2.19引入以来,此功能一直受到关注。启用提交图后可以将提交加载的性能提高一个数量级。

在Git 2.24中,默认会启用提交图,这样下次运行git gc时,你的仓库将有所改进。之前,此功能是在实验性core.commitGraph配置启用,但经过大量测试,已经可以默认启用。

除了作为新标准之外,这里还有一些提交图其他的改进:

所有提交图子命令(例如git commit-graph write,git commit-graph verify等)都支持-[no-] progress。现在,这些命令的进度表将以通常的方式运行:默认情况下仅写入终端,并遵循-[no-] progress来覆盖。

引入了一个新的配置值,用于在获取时自动更新提交图文件,该配置值利用提交图链将部分历史记录写入提交图链中,以供以后压缩。所以,每次从远程仓获取新提交时,都可以保证它们立即在提交图中,不必等下一次的auto-gc。功能可以通过设置fetch.writeCommitGraph为true来试用。

修复了许多错误,以改善commit-graph命令的性能和可靠性,尤其是在面对损坏的存储库时。

commit-graph命令现在还支持Git的最新跟踪机制trace2。

历史重写工具filter-repo

如果想对存储库的历史记录执行复杂的操作(例如从存储库的历史记录中删除文件或提取与一个目录有关的历史记录),可以试用git filter-branch。

git filter-branch是一个历史悠久的且功能强大的重写历史记录的工具。但是,,git filter-branch使用非常复杂,而且操作特别慢,并且经常会导致很多意想不到的误操作导致仓库库损坏和数据丢失。

Git 2.24发布,功能亮点抢先看

git filter-branch已经太老(12年前git 1.5.3引入)需要注入新鲜血液。

好消息是,Git 2.24中 Git项目新推荐一个更好的独立工具git filter-repo(github/newren/git-filter-repo)。git filter-repo可以避免用户在使用git filter-branch遇到的很多坑。git filter-repo无需按顺序重新处理每个提交,而是对历史记录进行高效的流表示,可以更高效地运作。该工具功能极其强大,其所有功能都具有详细的文档。下面是有关如何使用git filter-repo的一些要点:

git filter-repo –analyze提供了易于理解的度量选择,用于分析存储库的大小。这包括每种对象有多少个,文件和目录最大,扩展名占用最多空间,等等。当然,你可以试用同类的其他工具比如git size(github/github/git-sizer)。

可以使用–path-{glob,regex}和类似的选项来过滤存储库的历史记录,使其仅包含某些路径。

可以对历史记录以及大于固定阈值的blob对象执行”查找和替换”操作。

重写历史记录时,所有重写的提交(连同其祖先)将获得一个新的SHA-1来识别它们。默认情况下,git filter-repo会更新对这些SHA-1的所有其他引用,就像其他引用它们的提交消息一样。同样,git filter-repo也可以使用.mailmap选项来重写贡献者名称的。

最后,git filter-repo是可扩展的,它提供了一个灵活的接口,用于在Python中指定回调(例如,当git filter-repo遇到blob/tree/commit,新文件类型等时调用一个函数),以及完全定义新的子命令。详见其github官方仓库。

git filter-branch将在一段时间内仍包含在Git的常规发行版中。git filter-repo是对存储库的历史记录进行复杂修改的另一种方法,为Git官方建议软件。

更多功能

–end-of-options

大家可能已经知道,许多Git命令都使用一个或多个可选引用名称作为参数。例如,不带参数的git log将显示从当前已签出分支可以访问的所有内容的日志,但是git log my-feature ^master则只会显示my-feature上的内容,而不会显示master上的内容。

但是,如果分支称名为–super-dangerous-option,我们不能直接调用git log,该分支名会被解释为一个有破坏性的选项,从而导致危险操作。可以通过调用git log ‘refs/heads/–super-dangerous-option’消除歧义,但是如果是在脚本中调用,则可能不知道所获取参数属于哪个名称空间。

Git 2.24中通过使用–end-of-options提供了一种防止这种选项注入攻击的新方法。当Git命令添加此选项后,其后的所有参数将会当做git操作对象而不是选项。

因此,前面提到例子,可以使用一下命令行:

避免使用–为对象名是git中的一个共识,因为git选项中广泛使用的它做为选项,用于将引用名称与文件分开,可以使用:

以获取特定文件范围内修改的历史记录。

–rebase-merges改进

git rebase的–rebase-merges选项允许用户在保留历史结构的同时执行rebase。但是,当Git遇到一个合并点时,它将如何统一这两个历史?默认情况下,它使用内部称为”递归”的策略,很可能是当前正在使用的合并策略。

可能不知道可以告诉Git使用哪种合并策略,而选择另一种合并策略可能会导致不同的结果。新版本中git rebase –rebase-merges支持git rebase的–strategy和–strategy-option选项,因此可以在保留历史记录的结构性的同时保留其结构完整性并指定自己的合并解析策略。

pre-merge-commitGit支持许多钩子,它们是特殊名称的可执行文件,可以以在git工作流程中的各个点执行。例如,在运行git push之后但在实际执行推送之前会调用pre-push钩子。

新版本中新添加了一个钩子,用来允许调用者在执行合并之后但在写入结果提交之前与Git进行交互。可以在.git/hooks/pre-merge-commit文件中加入自己的执行逻辑,以实现这种情况的脚本调用。

部分克隆优化

之前,git引入了部分克隆。对于那些不了解Git部分克隆人,官方说明中现在加入了详细文档。克隆仓库库时,用户可以使用过滤器来指定仅希望使用某些对象。这样做时,会将用户克隆的远程对象指定为”Promisor”,这样如果用户在请求时候终端,可以可以承诺稍后继续请求剩余对象(断点续传)。

之前,Git仅支持一个远程Promisor。最新的git 2.24版本可以支持多个远程Promisor。这样用户可以配置少数几个地理上”近距离”远程仓,不必要所有的远程仓库都必须具有所有对象。

命令行补全

最后要介绍的是一个最实用的功能Git的命令行补全。​可以在每个命令配置中自动补全引擎自动补全配置变量。

Git具有可以找到gitconfig文件的位置的层次结构:存储库(通过.git/ config),主目录等等。但是,Git还支持顶级-c标志来为每个命令指定配置变量。比如:

该命令行用于在git add时候(以及其他可能调用git add的一部分其他命令)禁用Git的自动CRLF转换。如果忘记了设置Git的命令行完成引擎的变量名称 Git 2.24中提供配置变量名称的自动补全列表。因此,如果忘记了-c …中间的位置,则只需按Tab。

]]>
http://www.php100.com/31/112/87437.html/feed 0
体育生都能看懂的解说:什么是HTTPS协议 http://www.php100.com/9/87434.html http://www.php100.com/9/87434.html#respond Fri, 08 Nov 2019 02:21:43 +0000 http://www.php100.com/?p=87434 体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议

什么是HTTP协议?

HTTP协议全称Hyper Text Transfer Protocol,翻译过来就是超文本传输协议,位于TCP/IP四层模型当中的应用层。

体育生都能看懂的解说:什么是HTTPS协议

HTTP协议通过请求/响应的方式,在客户端和服务端之间进行通信。

体育生都能看懂的解说:什么是HTTPS协议

这一切看起来很美好,但是HTTP协议有一个致命的缺点:不够安全

HTTP协议的信息传输完全以明文方式,不做任何加密,相当于是在网络上“裸奔”。这样会导致什么问题呢?让我们打一个比方:

小灰是客户端,小灰的同事小红是服务端,有一天小灰试图给小红发送请求。

体育生都能看懂的解说:什么是HTTPS协议

但是,由于传输信息是明文,这个信息有可能被某个中间人恶意截获甚至篡改。这种行为叫做中间人攻击

体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议

如何进行加密呢?

小灰和小红可以事先约定一种对称加密方式,并且约定一个随机生成的密钥。后续的通信中,信息发送方都使用密钥对信息加密,而信息接收方通过同样的密钥对信息解密。

体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议

这样做是不是就绝对安全了呢?并不是。

虽然我们在后续的通信中对明文进行了加密,但是第一次约定加密方式和密钥的通信仍然是明文,如果第一次通信就已经被拦截了,那么密钥就会泄露给中间人,中间人仍然可以解密后续所有的通信内容。

体育生都能看懂的解说:什么是HTTPS协议

这可怎么办呢?别担心,我们可以使用非对称加密,为密钥的传输做一层额外的保护。

非对称加密的一组秘钥对中,包含一个公钥和一个私钥。明文既可以用公钥加密,用私钥解密;也可以用私钥加密,用公钥解密。

在小灰和小红建立通信的时候,小红首先把自己的公钥Key1发给小灰:

体育生都能看懂的解说:什么是HTTPS协议

收到小红的公钥以后,小灰自己生成一个用于对称加密的密钥Key2,并且用刚才接收的公钥Key1对Key2进行加密(这里有点绕),发送给小红:

体育生都能看懂的解说:什么是HTTPS协议

小红利用自己非对称加密的私钥,解开了公钥Key1的加密,获得了Key2的内容。从此以后,两人就可以利用Key2进行对称加密的通信了。

体育生都能看懂的解说:什么是HTTPS协议

在通信过程中,即使中间人在一开始就截获了公钥Key1,由于不知道私钥是什么,也无从解密。

体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议

是什么坏主意呢?中间人虽然不知道小红的私钥是什么,但是在截获了小红的公钥Key1之后,却可以偷天换日,自己另外生成一对公钥私钥,把自己的公钥Key3发送给小灰。

体育生都能看懂的解说:什么是HTTPS协议

小灰不知道公钥被偷偷换过,以为Key3就是小红的公钥。于是按照先前的流程,用Key3加密了自己生成的对称加密密钥Key2,发送给小红。

这一次通信再次被中间人截获,中间人先用自己的私钥解开了Key3的加密,获得Key2,然后再用当初小红发来的Key1重新加密,再发给小红。

体育生都能看懂的解说:什么是HTTPS协议

这样一来,两个人后续的通信尽管用Key2做了对称加密,但是中间人已经掌握了Key2,所以可以轻松进行解密。

体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议

是什么解决方案呢?难道再把公钥进行一次加密吗?这样只会陷入鸡生蛋蛋生鸡,永无止境的困局。

这时候,我们有必要引入第三方,一个权威的证书颁发机构(CA)来解决。

到底什么是证书呢?证书包含如下信息:

体育生都能看懂的解说:什么是HTTPS协议

为了便于说明,我们这里做了简化,只列出了一些关键信息。至于这些证书信息的用处,我们看看具体的通信流程就能够弄明白了。

流程如下:

1.作为服务端的小红,首先把自己的公钥发给证书颁发机构,向证书颁发机构申请证书。

体育生都能看懂的解说:什么是HTTPS协议

2.证书颁发机构自己也有一对公钥私钥。机构利用自己的私钥来加密Key1,并且通过服务端网址等信息生成一个证书签名,证书签名同样经过机构的私钥加密。证书制作完成后,机构把证书发送给了服务端小红。

体育生都能看懂的解说:什么是HTTPS协议

3.当小灰向小红请求通信的时候,小红不再直接返回自己的公钥,而是把自己申请的证书返回给小灰。

体育生都能看懂的解说:什么是HTTPS协议

4.小灰收到证书以后,要做的第一件事情是验证证书的真伪。需要说明的是,各大浏览器和操作系统已经维护了所有权威证书机构的名称和公钥。所以小灰只需要知道是哪个机构颁布的证书,就可以从本地找到对应的机构公钥,解密出证书签名。

接下来,小灰按照同样的签名规则,自己也生成一个证书签名,如果两个签名一致,说明证书是有效的。

验证成功后,小灰就可以放心地再次利用机构公钥,解密出服务端小红的公钥Key1。

体育生都能看懂的解说:什么是HTTPS协议

5.像之前一样,小灰生成自己的对称加密密钥Key2,并且用服务端公钥Key1加密Key2,发送给小红。

体育生都能看懂的解说:什么是HTTPS协议

6.最后,小红用自己的私钥解开加密,得到对称加密密钥Key2。于是两人开始用Key2进行对称加密的通信。

体育生都能看懂的解说:什么是HTTPS协议

在这样的流程下,我们不妨想一想,中间人是否还具有使坏的空间呢?

体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议
体育生都能看懂的解说:什么是HTTPS协议

注:最新推出的TLS协议,是SSL 3.0协议的升级版,和SSL协议的大体原理是相同的。

体育生都能看懂的解说:什么是HTTPS协议
]]>
http://www.php100.com/9/87434.html/feed 0
终于有人把 Docker 讲清楚了,万字详解 http://www.php100.com/9/87431.html http://www.php100.com/9/87431.html#respond Thu, 07 Nov 2019 01:52:20 +0000 http://www.php100.com/?p=87431 一、简介

1、了解Docker的前生LXC

LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpace。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。

与传统虚拟化技术相比,它的优势在于:

(1)与宿主机使用同一个内核,性能损耗小;

(2)不需要指令级模拟;

(3)不需要即时(Just-in-time)编译;

(4)容器可以在CPU核心的本地运行指令,不需要任何专门的解释机制;

(5)避免了准虚拟化和系统调用替换中的复杂性;

(6)轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享。

总结:Linux Container是一种轻量级的虚拟化的手段。

Linux Container提供了在单一可控主机节点上支持多个相互隔离的server container同时执行的机制。Linux Container有点像chroot,提供了一个拥有自己进程和网络空间的虚拟环境,但又有别于虚拟机,因为lxc是一种操作系统层次上的资源的虚拟化。

2、LXC与docker什么关系?

docker并不是LXC替代品,docker底层使用了LXC来实现,LXC将linux进程沙盒化,使得进程之间相互隔离,并且能够课哦内阁制各进程的资源分配。

在LXC的基础之上,docker提供了一系列更强大的功能。

3、什么是docker

docker是一个开源的应用容器引擎,基于go语言开发并遵循了apache2.0协议开源。

docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的linux服务器,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类iphone的app),并且容器开销极其低。

4、docker官方文档

https://docs.docker.com/

5、为什么docker越来越受欢迎

官方话语:

  • 容器化越来越受欢迎,因为容器是:
  • 灵活:即使是最复杂的应用也可以集装箱化。
  • 轻量级:容器利用并共享主机内核。
  • 可互换:您可以即时部署更新和升级。
  • 便携式:您可以在本地构建,部署到云,并在任何地方运行。
  • 可扩展:您可以增加并自动分发容器副本。
  • 可堆叠:您可以垂直和即时堆叠服务。
  • 镜像和容器(contalners)

通过镜像启动一个容器,一个镜像是一个可执行的包,其中包括运行应用程序所需要的所有内容包含代码,运行时间,库、环境变量、和配置文件。

容器是镜像的运行实例,当被运行时有镜像状态和用户进程,可以使用docker ps 查看。

  • 容器和虚拟机

容器时在linux上本机运行,并与其他容器共享主机的内核,它运行的一个独立的进程,不占用其他任何可执行文件的内存,非常轻量。

虚拟机运行的是一个完成的操作系统,通过虚拟机管理程序对主机资源进行虚拟访问,相比之下需要的资源更多。

终于有人把 Docker 讲清楚了,万字详解

6、docker版本

Docker Community Edition(CE)社区版

Enterprise Edition(EE) 商业版

7、docker和openstack的几项对比

终于有人把 Docker 讲清楚了,万字详解

8、容器在内核中支持2种重要技术

docker本质就是宿主机的一个进程,docker是通过namespace实现资源隔离,通过cgroup实现资源限制,通过写时复制技术(copy-on-write)实现了高效的文件操作(类似虚拟机的磁盘比如分配500g并不是实际占用物理磁盘500g)

1)namespaces 名称空间

终于有人把 Docker 讲清楚了,万字详解

2)control Group 控制组

cgroup的特点是:

  • cgroup的api以一个伪文件系统的实现方式,用户的程序可以通过文件系统实现cgroup的组件管理
  • cgroup的组件管理操作单元可以细粒度到线程级别,另外用户可以创建和销毁cgroup,从而实现资源载分配和再利用
  • 所有资源管理的功能都以子系统的方式实现,接口统一子任务创建之初与其父任务处于同一个cgroup的控制组

四大功能:

  • 资源限制:可以对任务使用的资源总额进行限制
  • 优先级分配:通过分配的cpu时间片数量以及磁盘IO带宽大小,实际上相当于控制了任务运行优先级
  • 资源统计:可以统计系统的资源使用量,如cpu时长,内存用量等
  • 任务控制:cgroup可以对任务执行挂起、恢复等操作

9、了解docker三个重要概念

1)image镜像

docker镜像就是一个只读模板,比如,一个镜像可以包含一个完整的centos,里面仅安装apache或用户的其他应用,镜像可以用来创建docker容器,另外docker提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下周一个已经做好的镜像来直接使用

2)container容器

docker利用容器来运行应用,容器是从镜像创建的运行实例,它可以被启动,开始、停止、删除、每个容器都是互相隔离的,保证安全的平台,可以吧容器看做是要给简易版的linux环境(包括root用户权限、镜像空间、用户空间和网络空间等)和运行再其中的应用程序

3)repostory仓库

仓库是集中存储镜像文件的沧桑,registry是仓库主从服务器,实际上参考注册服务器上存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)

仓库分为两种,公有参考,和私有仓库,最大的公开仓库是docker Hub,存放了数量庞大的镜像供用户下周,国内的docker pool,这里仓库的概念与Git类似,registry可以理解为github这样的托管服务。

10、docker的主要用途

官方就是Bulid 、ship、run any app/any where,编译、装载、运行、任何app/在任意地放都能运行。

就是实现了应用的封装、部署、运行的生命周期管理只要在glibc的环境下,都可以运行。

运维生成环境中:docker化。

  • 发布服务不用担心服务器的运行环境,所有的服务器都是自动分配docker,自动部署,自动安装,自动运行
  • 再不用担心其他服务引擎的磁盘问题,cpu问题,系统问题了
  • 资源利用更出色
  • 自动迁移,可以制作镜像,迁移使用自定义的镜像即可迁移,不会出现什么问题
  • 管理更加方便了

11、docker改变了什么

  • 面向产品:产品交付
  • 面向开发:简化环境配置
  • 面向测试:多版本测试
  • 面向运维:环境一致性
  • 面向架构:自动化扩容(微服务)

二、docker架构

1、总体架构

终于有人把 Docker 讲清楚了,万字详解
  • distribution 负责与docker registry交互,上传洗澡镜像以及v2 registry 有关的源数据
  • registry负责docker registry有关的身份认证、镜像查找、镜像验证以及管理registry mirror等交互操作
  • image 负责与镜像源数据有关的存储、查找,镜像层的索引、查找以及镜像tar包有关的导入、导出操作
  • reference负责存储本地所有镜像的repository和tag名,并维护与镜像id之间的映射关系
  • layer模块负责与镜像层和容器层源数据有关的增删改查,并负责将镜像层的增删改查映射到实际存储镜像层文件的graphdriver模块
  • graghdriver是所有与容器镜像相关操作的执行者

2、docker架构2

如果觉得上面架构图比较乱可以看这个架构:

从上图不难看出,用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。

而Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;而后Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。

Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。

而libcontainer是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。当执行完运行容器的命令后,一个实际的Docker容器就处于运行状态,该容器拥有独立的文件系统,独立并且安全的运行环境等。

3、docker架构3

再来看看另外一个架构,这个个架构就简单清晰指明了server/client交互,容器和镜像、数据之间的一些联系。

终于有人把 Docker 讲清楚了,万字详解

这个架构图更加清晰了架构

docker daemon就是docker的守护进程即server端,可以是远程的,也可以是本地的,这个不是C/S架构吗,客户端Docker client 是通过rest api进行通信。

docker cli 用来管理容器和镜像,客户端提供一个只读镜像,然后通过镜像可以创建多个容器,这些容器可以只是一个RFS(Root file system根文件系统),也可以ishi一个包含了用户应用的RFS,容器再docker client中只是要给进程,两个进程之间互不可见。

用户不能与server直接交互,但可以通过与容器这个桥梁来交互,由于是操作系统级别的虚拟技术,中间的损耗几乎可以不计。

三、docker架构2各个模块的功能(带完善)

主要的模块有:Docker Client、Docker Daemon、Docker Registry、Graph、Driver、libcontainer以及Docker container

1、docker client

docker client 是docker架构中用户用来和docker daemon建立通信的客户端,用户使用的可执行文件为docker,通过docker命令行工具可以发起众多管理container的请求。

docker client可以通过一下三宗方式和docker daemon建立通信:tcp://host:port;unix:path_to_socket;fd://socketfd。,docker client可以通过设置命令行flag参数的形式设置安全传输层协议(TLS)的有关参数,保证传输的安全性。

docker client发送容器管理请求后,由docker daemon接受并处理请求,当docker client 接收到返回的请求相应并简单处理后,docker client 一次完整的生命周期就结束了,当需要继续发送容器管理请求时,用户必须再次通过docker可以执行文件创建docker client。

2、docker daemon

docker daemon 是docker架构中一个常驻在后台的系统进程,功能是:接收处理docker client发送的请求。该守护进程在后台启动一个server,server负载接受docker client发送的请求;接受请求后,server通过路由与分发调度,找到相应的handler来执行请求。

docker daemon启动所使用的可执行文件也为docker,与docker client启动所使用的可执行文件docker相同,在docker命令执行时,通过传入的参数来判别docker daemon与docker client。

docker daemon的架构可以分为:docker server、engine、job。daemon

3、docker server

docker server在docker架构中时专门服务于docker client的server,该server的功能时:接受并调度分发docker client发送的请求,架构图如下:

终于有人把 Docker 讲清楚了,万字详解

在Docker的启动过程中,通过包gorilla/mux(golang的类库解析),创建了一个mux.Router,提供请求的路由功能。在Golang中,gorilla/mux是一个强大的URL路由器以及调度分发器。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。

若Docker Client通过HTTP的形式访问Docker Daemon,创建完mux.Router之后,Docker将Server的监听地址以及mux.Router作为参数,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve为请求服务。

在Server的服务过程中,Server在listener上接受Docker Client的访问请求,并创建一个全新的goroutine来服务该请求。在goroutine中,首先读取请求内容,然后做解析工作,接着找到相应的路由项,随后调用相应的Handler来处理该请求,最后Handler处理完请求之后回复该请求。

需要注意的是:Docker Server的运行在Docker的启动过程中,是靠一个名为”serveapi”的job的运行来完成的。原则上,Docker Server的运行是众多job中的一个,但是为了强调Docker Server的重要性以及为后续job服务的重要特性,将该”serveapi”的job单独抽离出来分析,理解为Docker Server。

4、engine

Engine是Docker架构中的运行引擎,同时也Docker运行的核心模块。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。

在Engine数据结构的设计与实现过程中,有一个handler对象。该handler对象存储的都是关于众多特定job的handler处理访问。举例说明,Engine的handler对象中有一项为:{“create”: daemon.ContainerCreate,},则说明当名为”create”的job在运行时,执行的是daemon.ContainerCreate的handler。

5、job

一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。例如:在容器内部运行一个进程,这是一个job;创建一个新的容器,这是一个job,从Internet上下载一个文档,这是一个job;包括之前在Docker Server部分说过的,创建Server服务于HTTP的API,这也是一个job,等等。

Job的设计者,把Job设计得与Unix进程相仿。比如说:Job有一个名称,有参数,有环境变量,有标准的输入输出,有错误处理,有返回状态等。

6、docker registry

Docker Registry是一个存储容器镜像的仓库。而容器镜像是在容器被创建时,被加载用来初始化容器的文件架构与目录。

在Docker的运行过程中,Docker Daemon会与Docker Registry通信,并实现搜索镜像、下载镜像、上传镜像三个功能,这三个功能对应的job名称分别为”search”,”pull” 与 “push”。

其中,在Docker架构中,Docker可以使用公有的Docker Registry,即大家熟知的Docker Hub,如此一来,Docker获取容器镜像文件时,必须通过互联网访问Docker Hub;同时Docker也允许用户构建本地私有的Docker Registry,这样可以保证容器镜像的获取在内网完成。

7、Graph

Graph在Docker架构中扮演已下载容器镜像的保管者,以及已下载容器镜像之间关系的记录者。一方面,Graph存储着本地具有版本信息的文件系统镜像,另一方面也通过GraphDB记录着所有文件系统镜像彼此之间的关系。

Graph的架构如下:

终于有人把 Docker 讲清楚了,万字详解

其中,GraphDB是一个构建在SQLite之上的小型图数据库,实现了节点的命名以及节点之间关联关系的记录。它仅仅实现了大多数图数据库所拥有的一个小的子集,但是提供了简单的接口表示节点之间的关系。

同时在Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。

8、driver

Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。由于Docker运行的生命周期中,并非用户所有的操作都是针对Docker容器的管理,另外还有关于Docker运行信息的获取,Graph的存储与记录等。因此,为了将Docker容器的管理从Docker Daemon内部业务逻辑中区分开来,设计了Driver层驱动来接管所有这部分请求。

在Docker Driver的实现中,可以分为以下三类驱动:graphdriver、networkdriver和execdriver。

graphdriver主要用于完成容器镜像的管理,包括存储与获取。即当用户需要下载指定的容器镜像时,graphdriver将容器镜像存储在本地的指定目录;同时当用户需要使用指定的容器镜像来创建容器的rootfs时,graphdriver从本地镜像存储目录中获取指定的容器镜像。

在graphdriver的初始化过程之前,有4种文件系统或类文件系统在其内部注册,它们分别是aufs、btrfs、vfs和devmapper。而Docker在初始化之时,通过获取系统环境变量”DOCKER_DRIVER”来提取所使用driver的指定类型。而之后所有的graph操作,都使用该driver来执行。

graphdriver的架构如下:

终于有人把 Docker 讲清楚了,万字详解

networkdriver的用途是完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。networkdriver的架构如下:

终于有人把 Docker 讲清楚了,万字详解

execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。

具体体现在Daemon启动过程中加载的ExecDriverflag参数,该参数在配置文件已经被设为”native”。这可以认为是Docker在1.2版本上一个很大的改变,或者说Docker实现跨平台的一个先兆。execdriver架构如下:

终于有人把 Docker 讲清楚了,万字详解

9、libcontainer

libcontainer是Docker架构中一个使用Go语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的API。

正是由于libcontainer的存在,Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包。libcontainer架构如下:

终于有人把 Docker 讲清楚了,万字详解

另外,libcontainer提供了一整套标准的接口来满足上层对容器管理的需求。或者说,libcontainer屏蔽了Docker上层对容器的直接管理。又由于libcontainer使用Go这种跨平台的语言开发实现,且本身又可以被上层多种不同的编程语言访问,因此很难说,未来的Docker就一定会紧紧地和Linux捆绑在一起。而于此同时,Microsoft在其著名云计算平台Azure中,也添加了对Docker的支持,可见Docker的开放程度与业界的火热度。

暂不谈Docker,由于libcontainer的功能以及其本身与系统的松耦合特性,很有可能会在其他以容器为原型的平台出现,同时也很有可能催生出云计算领域全新的项目。

10、docker container

Docker container(Docker容器)是Docker架构中服务交付的最终体现形式。

Docker按照用户的需求与指令,订制相应的Docker容器:

  • 用户通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;
  • 用户通过指定计算资源的配额,使得Docker容器使用指定的计算资源;
  • 用户通过配置网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;
  • 用户通过指定运行的命令,使得Docker容器执行指定的工作。
终于有人把 Docker 讲清楚了,万字详解

四、docker简单使用

1、安装

yum install docker -y systemctl enable dockersystemctl start docker

注意:启动前应当设置源

vim /usr/lib/systemd/system/docker.service

这里设置阿里的,注册阿里云账户号每个用户都有:

[root@web1 ~]# vim /usr/lib/systemd/system/docker.service[Unit]Description=Docker Application Container EngineDocumentation=http://docs.docker.comAfter=network.targetWants=docker-storage-setup.serviceRequires=docker-cleanup.timer[Service]Type=notifyNotifyAccess=mainEnvironmentFile=-/run/containers/registries.confEnvironmentFile=-/etc/sysconfig/dockerEnvironmentFile=-/etc/sysconfig/docker-storageEnvironmentFile=-/etc/sysconfig/docker-networkEnvironment=GOTRACEBACK=crashEnvironment=DOCKER_HTTP_HOST_COMPAT=1Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbinExecStart=/usr/bin/dockerd-current --registry-mirror=https://rfcod7oz.mirror.aliyuncs.com #这个值可以登陆阿里云账号请参考下图 --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current  --default-runtime=docker-runc  --exec-opt native.cgroupdriver=systemd  --userland-proxy-path=/usr/libexec/docker/docker-proxy-current  --init-path=/usr/libexec/docker/docker-init-current  --seccomp-profile=/etc/docker/seccomp.json  $OPTIONS  $DOCKER_STORAGE_OPTIONS  $DOCKER_NETWORK_OPTIONS  $ADD_REGISTRY  $BLOCK_REGISTRY  $INSECURE_REGISTRY  $REGISTRIESExecReload=/bin/kill -s HUP $MAINPIDLimitNOFILE=1048576LimitNPROC=1048576LimitCORE=infinityTimeoutStartSec=0Restart=on-abnormalKillMode=process[Install]WantedBy=multi-user.target 

2、docker版本查询

[root@web1 ~]# docker versionClient: Version: 1.13.1 API version: 1.26 Package version: docker-1.13.1-96.gitb2f74b2.el7.centos.x86_64 Go version: go1.10.3 Git commit: b2f74b2/1.13.1 Built: Wed May 1 14:55:20 2019 OS/Arch: linux/amd64Server: Version: 1.13.1 API version: 1.26 (minimum version 1.12) Package version: docker-1.13.1-96.gitb2f74b2.el7.centos.x86_64 Go version: go1.10.3 Git commit: b2f74b2/1.13.1 Built: Wed May 1 14:55:20 2019 OS/Arch: linux/amd64 Experimental: false

3、搜索下载镜像


docker pull alpine #下载镜像docker search nginx #查看镜像docker pull nginx

4、查看已经下载的镜像

[root@web1 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzxg/my_nginx v1 b164f4c07c64 8 days ago 126 MBzxg/my_nginx latest f07837869dfc 8 days ago 126 MBdocker.io/nginx latest e445ab08b2be 2 weeks ago 126 MBdocker.io/alpine latest b7b28af77ffe 3 weeks ago 5.58 MBdocker.io/centos latest 9f38484d220f 4 months ago 202 MB[root@web1 ~]#

5、导出镜像

docker save nginx >/tmp/nginx.tar.gz

6、删除镜像

docker rmi -f nginx

7、导入镜像

docker load </tmp/nginx.tar.gz

8、默认配置文件

vim /usr/lib/systemd/system/docker.service


[Unit]Description=Docker Application Container EngineDocumentation=http://docs.docker.comAfter=network.targetWants=docker-storage-setup.serviceRequires=docker-cleanup.timer[Service]Type=notifyNotifyAccess=mainEnvironmentFile=-/run/containers/registries.confEnvironmentFile=-/etc/sysconfig/dockerEnvironmentFile=-/etc/sysconfig/docker-storageEnvironmentFile=-/etc/sysconfig/docker-networkEnvironment=GOTRACEBACK=crashEnvironment=DOCKER_HTTP_HOST_COMPAT=1Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbinExecStart=/usr/bin/dockerd-current --registry-mirror=https://rfcod7oz.mirror.aliyuncs.com --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=/usr/libexec/docker/docker-proxy-current --init-path=/usr/libexec/docker/docker-init-current --seccomp-profile=/etc/docker/seccomp.json $OPTIONS $DOCKER_STORAGE_OPTIONS $DOCKER_NETWORK_OPTIONS $ADD_REGISTRY $BLOCK_REGISTRY $INSECURE_REGISTRY $REGISTRIESExecReload=/bin/kill -s HUP $MAINPIDLimitNOFILE=1048576LimitNPROC=1048576LimitCORE=infinityTimeoutStartSec=0Restart=on-abnormalKillMode=process[Install]WantedBy=multi-user.target~~~~

如果更改存储目录就添加

--graph=/opt/docker

如果更改DNS——默认采用宿主机的dns

--dns=xxxx的方式指定

9、运行hello world

这里用centos镜像echo一个hello word


[root@web1 overlay2]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzxg/my_nginx v1 b164f4c07c64 8 days ago 126 MBzxg/my_nginx latest f07837869dfc 8 days ago 126 MBdocker.io/nginx latest e445ab08b2be 2 weeks ago 126 MBdocker.io/alpine latest b7b28af77ffe 3 weeks ago 5.58 MBdocker.io/centos latest 9f38484d220f 4 months ago 202 MB[root@web1 overlay2]# docker run centos echo "hello world"hello world[root@web1 overlay2]#

10、运行一个容器-run


[root@web1 overlay2]# docker run -it alpine sh #运行并进入alpine/ #/ #/ #/ #/ #/ # lsbin etc lib mnt proc run srv tmp vardev home media opt root sbin sys usr/ # cd tmp/tmp # exit

后台运行(-d后台运行)(–name添加一个名字)


[root@web1 overlay2]# docker run -it -d --name test1 alpineac46c019b800d34c37d4f9dcd56c974cb82eca3acf185e5f8f80c8a60075e343[root@web1 overlay2]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESac46c019b800 alpine "/bin/sh" 5 seconds ago Up 3 seconds test1[root@web1 overlay2]#

还有一种-rm参数,ctrl+c后就删除,可以测试环境用,生成环境用的少


[root@web1 overlay2]# docker run -it --rm --name centos nginx^C[root@web1 overlay2]###另开一个窗口[root@web1 ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES3397b96ea7bd nginx "nginx -g 'daemon ..." 27 seconds ago Up 25 seconds 80/tcp centosac46c019b800 alpine "/bin/sh" 4 minutes ago Up 4 minutes test1[root@web1 ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESac46c019b800 alpine "/bin/sh" 4 minutes ago Up 4 minutes test1[root@web1 ~]#

11、如何进入容器

三种方法,上面已经演示了一种

第一种,需要容器本身的pid及util-linux,不推荐,暂时不演示了

第二种,不分配bash终端的一种实施操作,不推荐,这种操作如果在开一个窗口也能看到操作的指令,所有人都能看到。

[root@web1 overlay2]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9fc796e928d7 nginx "sh" 2 minutes ago Up 8 seconds 80/tcp mynginxac46c019b800 alpine "/bin/sh" 12 minutes ago Up 12 minutes test1[root@web1 overlay2]# docker attach mynginx##### lsbin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var# exit [root@web1 overlay2]# docker attach mynginxYou cannot attach to a stopped container, start it first[root@web1 overlay2]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESac46c019b800 alpine "/bin/sh" 13 minutes ago Up 13 minutes test1[root@web1 overlay2]#

第三种:exec方式,终端时分开的,推荐

[root@web1 overlay2]# docker exec -it mynginx sh#### lsbin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var# exit[root@web1 overlay2]#[root@web1 overlay2]#[root@web1 overlay2]#[root@web1 overlay2]# docker padocker: 'pa' is not a docker command.See 'docker --help'[root@web1 overlay2]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES6fc2d091cfe9 nginx "nginx -g 'daemon ..." 45 seconds ago Up 43 seconds 80/tcp mynginxac46c019b800 alpine "/bin/sh" 16 minutes ago Up 16 minutes test1

12、查看docker进程及删除容器

上面已经演示:

[root@web1 overlay2]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9fc796e928d7 nginx "sh" 2 minutes ago Up 8 seconds 80/tcp mynginxac46c019b800 alpine "/bin/sh" 12 minutes ago Up 12 minutes test1

[root@web1 overlay2]# docker ps -a #-a :显示所有的容器,包括未运行的CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9fc796e928d7 nginx "sh" 4 minutes ago Exited (0) About a minute ago mynginxac46c019b800 alpine "/bin/sh" 15 minutes ago Up 15 minutes test13bf234febeaa alpine "sh" 17 minutes ago Exited (0) 16 minutes ago youthful_lumiereab113c63f0b4 centos "echo 'hello world'" 31 minutes ago Exited (0) 31 minutes ago infallible_torvaldsb326027dcf42 zxg/my_nginx "nginx" 8 days ago Exited (0) 8 days ago my_nginx4f1f1ca319f2 centos "bash" 8 days ago Exited (137) 8 days ago musing_lichterman64b4e32991c7 nginx "nginx -g 'daemon ..." 12 days ago Exited (0) 12 days ago mynginx1aee506fe7b5a alpine "sh" 12 days ago Created infallible_haibt70620c73b9a0 alpine "sh" 12 days ago Created gallant_volhard7655cbf87bb0 alpine "sh" 12 days ago Created agitated_brahmagupta33fb949372e8 fce289e99eb9 "/hello" 12 days ago Created elastic_dijkstra9de47616aea4 fce289e99eb9 "/hello" 13 days ago Created confident_fermi[root@web1 overlay2]# docker rm 9fc796e928d7 #rm时删除一个或多个容器9fc796e928d7

13、查看容器详细信息

并不需要进入到容器里面,通过查看详细信息看到了刚才运行的nginx,宿主机curl ip地址访问一下运行情况。


[root@web1 overlay2]# docker inspect mynginx[ { "Id": "6fc2d091cfe9b0484da3e70db842446bbdfeb7f5e5409c2e40ae21b99498d010", "Created": "2019-08-07T08:57:48.864538933Z", "Path": "nginx", "Args": [ "-g", "daemon off;"], "State": { "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 119948, "ExitCode": 0, "Error": "", "StartedAt": "2019-08-07T08:57:49.417992182Z", "FinishedAt": "0001-01-01T00:00:00Z" }, "Image": "sha256:e445ab08b2be8b178655b714f89e5db9504f67defd5c7408a00bade679a50d44", "ResolvConfPath": "/var/lib/docker/containers/6fc2d091cfe9b0484da3e70db842446bbdfeb7f5e5409c2e40ae21b99498d010/resolv.conf", "HostnamePath": "/var/lib/docker/containers/6fc2d091cfe9b0484da3e70db842446bbdfeb7f5e5409c2e40ae21b99498d010/hostname", "HostsPath": "/var/lib/docker/containers/6fc2d091cfe9b0484da3e70db842446bbdfeb7f5e5409c2e40ae21b99498d010/hosts", "LogPath": "", "Name": "/mynginx", "RestartCount": 0, "Driver": "overlay2", "MountLabel": "", "ProcessLabel": "", "AppArmorProfile": "", "ExecIDs": , "HostConfig": { "Binds": , "ContainerIDFile": "", "LogConfig": { "Type": "journald", "Config": {} }, "NetworkMode": "default", "PortBindings": {}, "RestartPolicy": { "Name": "no", "MaximumRetryCount": 0 }, "AutoRemove": false, "VolumeDriver": "", "VolumesFrom": , "CapAdd": , "CapDrop": , "Dns": , "DnsOptions": , "DnsSearch": , "ExtraHosts": , "GroupAdd": , "IpcMode": "", "Cgroup": "", "Links": , "OomScoreAdj": 0, "PidMode": "", "Privileged": false, "PublishAllPorts": false, "ReadonlyRootfs": false, "SecurityOpt": , "UTSMode": "", "UsernsMode": "", "ShmSize": 67108864, "Runtime": "docker-runc", "ConsoleSize": [ 0, 0 ], "Isolation": "", "CpuShares": 0, "Memory": 0, "NanoCpus": 0, "CgroupParent": "", "BlkioWeight": 0, "BlkioWeightDevice": , "BlkioDeviceReadBps": , "BlkioDeviceWriteBps": , "BlkioDeviceReadIOps": , "BlkioDeviceWriteIOps": , "CpuPeriod": 0, "CpuQuota": 0, "CpuRealtimePeriod": 0, "CpuRealtimeRuntime": 0, "CpusetCpus": "", "CpusetMems": "", "Devices": , "DiskQuota": 0, "KernelMemory": 0, "MemoryReservation": 0, "MemorySwap": 0, "MemorySwappiness": -1, "OomKillDisable": false, "PidsLimit": 0, "Ulimits": , "CpuCount": 0, "CpuPercent": 0, "IOMaximumIOps": 0, "IOMaximumBandwidth": 0 }, "GraphDriver": { "Name": "overlay2", "Data": { "LowerDir": "/var/lib/docker/overlay2/937140af0aee6c43f04c2d7b72e6b5451a44fef921417e8236d9fe01e9286c7a-init/diff:/var/lib/docker/overlay2/d8e95505fc3894eb30b48e4b0f48ab5e89d99c09a07c79c0b057c611621e31eb/diff:/var/lib/docker/overlay2/b2a6a25974bf17398b698a27208711574be3c69a2cd06658bbe838359f373a27/diff:/var/lib/docker/overlay2/d4610bc89b3ba8ad6ab30ea895fc3a06efff15db493d86ac9bc100e04abbab67/diff", "MergedDir": "/var/lib/docker/overlay2/937140af0aee6c43f04c2d7b72e6b5451a44fef921417e8236d9fe01e9286c7a/merged", "UpperDir": "/var/lib/docker/overlay2/937140af0aee6c43f04c2d7b72e6b5451a44fef921417e8236d9fe01e9286c7a/diff", "WorkDir": "/var/lib/docker/overlay2/937140af0aee6c43f04c2d7b72e6b5451a44fef921417e8236d9fe01e9286c7a/work" } }, "Mounts": , "Config": { "Hostname": "6fc2d091cfe9", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "80/tcp": {} }, "Tty": true, "OpenStdin": true, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.17.2", "NJS_VERSION=0.3.3", "PKG_RELEASE=1~buster" ], "Cmd": [ "nginx", "-g", "daemon off;" ], "ArgsEscaped": true, "Image": "nginx", "Volumes": , "WorkingDir": "", "Entrypoint": , "OnBuild": , "Labels": { "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>" }, "StopSignal": "SIGTERM" }, "NetworkSettings": { "Bridge": "", "SandboxID": "3ece36008fbc5f3f46d3d251cf803c1478cc14032d74a36747e4ed8a115b81df", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": { "80/tcp": }, "SandboxKey": "/var/run/docker/netns/3ece36008fbc", "SecondaryIPAddresses": , "SecondaryIPv6Addresses": , "EndpointID": "898de81d97d54d2b60aeb6cc77ef1b4f9b481d1b72f542faa496494594024eac", "Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.3", #看到ip地址 "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "02:42:ac:11:00:03", "Networks": { "bridge": { "IPAMConfig": , "Links": , "Aliases": , "NetworkID": "2edae9131e77500a56d251b94ab2cdf0bc86f8df9f2453fa46bf4bab2f7be99f", "EndpointID": "898de81d97d54d2b60aeb6cc77ef1b4f9b481d1b72f542faa496494594024eac", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.3", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:03" } } } }][root@web1 overlay2]# curl 172.17.0.1 #访问一下<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Test Page for the Nginx HTTP Server on Fedora</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <style type="text/css"> /*<![CDATA[*/ body { background-color: #fff; color: #000; font-size: 0.9em; font-family: sans-serif,helvetica; margin: 0; padding: 0; } :link { color: #c00; } :visited { color: #c00; } a:hover { color: #f50; } h1 { text-align: center; margin: 0; padding: 0.6em 2em 0.4em; background-color: #294172; color: #fff; font-weight: normal; font-size: 1.75em; border-bottom: 2px solid #000; } h1 strong { font-weight: bold; font-size: 1.5em; } h2 { text-align: center; background-color: #3C6EB4; font-size: 1.1em; font-weight: bold; color: #fff; margin: 0; padding: 0.5em; border-bottom: 2px solid #294172; } hr { display: none; } .content { padding: 1em 5em; } .alert { border: 2px solid #000; } img { border: 2px solid #fff; padding: 2px; margin: 2px; } a:hover img { border: 2px solid #294172; } .logos { margin: 1em; text-align: center; } /*]]>*/ </style> </head> <body> <h1>Welcome to <strong>nginx</strong> on Fedora!</h1> <div class="content"> <p>This page is used to test the proper operation of the <strong>nginx</strong> HTTP server after it has been installed. If you can read this page, it means that the web server installed at this site is working properly.</p> <div class="alert"> <h2>Website Administrator</h2> <div class="content"> <p>This is the default <tt>index.html</tt> page that is distributed with <strong>nginx</strong> on Fedora. It is located in <tt>/usr/share/nginx/html</tt>.</p> <p>You should now put your content in a location of your choice and edit the <tt>root</tt> configuration directive in the <strong>nginx</strong> configuration file <tt>/etc/nginx/nginx.conf</tt>.</p> </div> </div> <div class="logos"> <a href="http://nginx.net/"><img src="nginx-logo.png" alt="[ Powered by nginx ]" width="121" height="32" /></a> <a href="http://fedoraproject.org/"><img src="poweredby.png" alt="[ Powered by Fedora ]" width="88" height="31" /></a> </div> </div> </body></html>[root@web1 overlay2]#

14、查看日志

-f 挂起这个终端,动态查看日志

[root@web1 ~]# docker logs -f mynginx
]]>
http://www.php100.com/9/87431.html/feed 0
5分钟帮你了解一下谷歌浏览器的架构 http://www.php100.com/6/87428.html http://www.php100.com/6/87428.html#respond Thu, 07 Nov 2019 01:51:08 +0000 http://www.php100.com/?p=87428 5分钟帮你了解一下谷歌浏览器的架构

chrome

在从事前端开发过程中,浏览器作为最重要的开发环境,浏览器基础是前端开发人员必须掌握的基础知识点,它贯穿着前端的整个网络体系。对浏览器原理的了解,也可以提高编写前端代码性能的上限。如果你是web前端开发人员那就来一起学习下谷歌浏览器的架构知识吧。

谷歌浏览器运行的时候会启动四个主要进程:浏览器进程、GPU进程、第三方插件进程及渲染进程。

下面来一一介绍下四个主要进程的作用。

1.浏览器进程

Controls “chrome” part of the application including address bar, bookmarks, back and forward buttons. Also handles the invisible, privileged parts of a web browser such as network requests and file access
(控制地址栏、书签前进后退按钮,标签页创建/销毁、页面显示、网络请求、资源管理下载)

  • GUI 渲染线程
    • HTML Parser 解析HTML文档,将元素转换为树结构DOM节点
    • CSS Parser 解析Style数据,包括外部的CSS文件以及在HTML元素中的样式,用于创建渲染树
    • Layout 为每个节点计算出在屏幕中展示的准确坐标
    • Painting 遍历渲染树,调用UI Backend提供的接口绘制每个节点
  • JavaScript 引擎线程
    • 解析Javascript脚本,运行代码 JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
    • GUI渲染线程与JS引擎线程互斥
      • 由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和GUI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行
  • 定时触发器线程
    • setInterval与setTimeout所在线程
    • 定时计数器并不是由 JavaScript 引擎计数的(因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时更为合理)
    • W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
  • 事件触发线程
    • 归属于浏览器而不是JS引擎,用来控制事件循环
    • 一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。
    • 这些事件可以是当前执行的代码块如定时任务(也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等),会将对应任务添加到事件线程中
    • 由于 JavaScript 的单线程关系所有这些事件都得排队等待 JavaScript 引擎处理。
  • http 异步请求线程
    • XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理

2.GPU进程

Handles GPU tasks in isolation from other processes. It is separated into different process because GPUs handles requests from multiple apps and draw them in the same surface (处理来自其他进程的GPU任务。它被分为不同的进程,因为gpu处理来自多个应用程序的请求,并将它们绘制在同一个表面上)

3.第三方插件进程

Controls any plugins used by the website, for example, flash. 控制网站使用的任何插件,每个插件对应一个进程,当插件运行时创建

4.渲染进程(所说的”浏览器内核”)

Controls anything inside of the tab where a website is displayed. 控制显示网站的选项卡内的任何内容,默认每个标签页创建一个渲染引擎实例

以上就是谷歌浏览器主要进程的相关介绍,当然有一些其他进程,这里不再展开。

]]>
http://www.php100.com/6/87428.html/feed 0
效率提高 10 倍!一份不可多得的 Lombok 学习指南 http://www.php100.com/9/87425.html http://www.php100.com/9/87425.html#respond Thu, 07 Nov 2019 01:48:18 +0000 http://www.php100.com/?p=87425 一、Lombok 简介

Lombok 是一款 Java 开发插件,使得 Java 开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的 Java 模型对象(POJO)。在开发环境中使用 Lombok 插件后,Java 开发人员可以节省出重复构建,诸如 hashCode 和 equals 这样的方法以及各种业务对象模型的 accessor 和 toString 等方法的大量时间。对于这些方法,Lombok 能够在编译源代码期间自动帮我们生成这些方法,但并不会像反射那样降低程序的性能。

二、Lombok 安装

2.1 构建工具

Gradle

在 build.gradle文件中添加 lombok 依赖:

dependencies {compileOnly 'org.projectlombok:lombok:1.18.10'annotationProcessor 'org.projectlombok:lombok:1.18.10'}

Maven

在 Maven 项目的 pom.xml文件中添加 lombok 依赖:

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version><scope>provided</scope></dependency>

Ant

假设在 lib目录中已经存在lombok.jar,然后设置 javac 任务:

<javac srcdir="src" destdir="build" source="1.8"><classpath location="lib/lombok.jar" /></javac>

2.2 IDE

由于 Lombok 仅在编译阶段生成代码,所以使用 Lombok 注解的源代码,在 IDE 中会被高亮显示错误,针对这个问题可以通过安装 IDE 对应的插件来解决。这里不详细展开,具体的安装方式可以参考 Setting up Lombok with Eclipse and IntelliJ 这篇文章。

三、Lombok 详解

注意:以下示例所使用的 Lombok 版本是 1.18.10

3.1 @Getter and @Setter 注解

你可以使用 @Getter@Setter注释任何类或字段,Lombok 会自动生成默认的 getter/setter 方法。

@Getter 注解

@Target({ElementType.FIELD, ElementType.TYPE})@Retention(RetentionPolicy.SOURCE)public @interface Getter {lombok.AccessLevel value default lombok.AccessLevel.PUBLIC;AnyAnnotation onMethod default {};boolean lazy default false;}

@Setter 注解

@Target({ElementType.FIELD, ElementType.TYPE})@Retention(RetentionPolicy.SOURCE)public @interface Setter {lombok.AccessLevel value default lombok.AccessLevel.PUBLIC;AnyAnnotation onMethod default {};AnyAnnotation onParam default {};}

使用示例

package com.semlinker.lombok;@Getter@Setterpublic class GetterAndSetterDemo {String firstName;String lastName;LocalDate dateOfBirth;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class GetterAndSetterDemo {String firstName;String lastName;LocalDate dateOfBirth;publicGetterAndSetterDemo {}public String getFirstName {return this.firstName;}public voidsetFirstName(String firstName) {this.firstName = firstName;}}

Lazy Getter

@Getter注解支持一个 lazy 属性,该属性默认为 false。当设置为 true 时,会启用延迟初始化,即当首次调用 getter 方法时才进行初始化。

Tips:关注微信公众号:Java后端,每日获取博文的推送。

示例

package com.semlinker.lombok;public class LazyGetterDemo {public static voidmain(String[] args) {LazyGetterDemo m = new LazyGetterDemo;System.out.println("Main instance is created");m.getLazy;}@Getterprivate final String notLazy = createValue("not lazy");@Getter(lazy = true)private final String lazy = createValue("lazy");private String createValue(String name) {System.out.println("createValue(" + name + ")");return ;}}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class LazyGetterDemo {private final String notLazy = this.createValue("not lazy");private final AtomicReference<Object> lazy = new AtomicReference;public String getNotLazy {return this.notLazy;}public String getLazy {Object value = this.lazy.get;if (value == ) {synchronized(this.lazy) {value = this.lazy.get;if (value == ) {String actualValue = this.createValue("lazy");value = actualValue == ? this.lazy : actualValue;this.lazy.set(value);}}}return (String)((String)(value == this.lazy ? : value));}}

通过以上代码可知,调用 getLazy 方法时,若发现 value 为 ,则会在同步代码块中执行初始化操作。

3.2 Constructor Annotations

@NoArgsConstructor 注解

使用 @NoArgsConstructor注解可以为指定类,生成默认的构造函数,@NoArgsConstructor注解的定义如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface NoArgsConstructor {String staticName default "";AnyAnnotation onConstructor default {};AccessLevel access default lombok.AccessLevel.PUBLIC;booleanforce default false;}

示例

package com.semlinker.lombok;@NoArgsConstructor(staticName = "getInstance")public class NoArgsConstructorDemo {private long id;private String name;private int age;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class NoArgsConstructorDemo {private long id;private String name;private int age;privateNoArgsConstructorDemo {}public static NoArgsConstructorDemo getInstance {return new NoArgsConstructorDemo;}}

@AllArgsConstructor 注解

使用 @AllArgsConstructor注解可以为指定类,生成包含所有成员的构造函数,@AllArgsConstructor注解的定义如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface AllArgsConstructor {String staticName default "";AnyAnnotation onConstructor default {};AccessLevel access default lombok.AccessLevel.PUBLIC;}

示例

package com.semlinker.lombok;@AllArgsConstructorpublic class AllArgsConstructorDemo {private long id;private String name;private int age;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class AllArgsConstructorDemo {private long id;private String name;private int age;publicAllArgsConstructorDemo(long id, String name, int age) {this.id = id;this.name = name;this.age = age;}}

@RequiredArgsConstructorDemo 注解

使用 @RequiredArgsConstructor注解可以为指定类必需初始化的成员变量,如 final 成员变量,生成对应的构造函数,@RequiredArgsConstructor注解的定义如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface RequiredArgsConstructor {String staticName default "";AnyAnnotation onConstructor default {};AccessLevel access default lombok.AccessLevel.PUBLIC;}

示例

package com.semlinker.lombok;@RequiredArgsConstructorpublic class RequiredArgsConstructorDemo {private final long id;private String name;private int age;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class RequiredArgsConstructorDemo {private final long id;private String name;private int age;publicRequiredArgsConstructorDemo(long id) {this.id = id;}}

3.3 @EqualsAndHashCode 注解

使用 @EqualsAndHashCode注解可以为指定类生成 equals 和 hashCode 方法,@EqualsAndHashCode注解的定义如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface EqualsAndHashCode {String exclude default {};String of default {};boolean callSuper default false;boolean doNotUseGetters default false;AnyAnnotation onParam default {};@Deprecated@Retention(RetentionPolicy.SOURCE)@Target({})@interface AnyAnnotation {}@Target(ElementType.FIELD)@Retention(RetentionPolicy.SOURCE)public @interface Exclude {}@Target({ElementType.FIELD, ElementType.METHOD})@Retention(RetentionPolicy.SOURCE)public @interface Include {String replaces default "";}}

示例

package com.semlinker.lombok;@EqualsAndHashCodepublic class EqualsAndHashCodeDemo {String firstName;String lastName;LocalDate dateOfBirth;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class EqualsAndHashCodeDemo {String firstName;String lastName;LocalDate dateOfBirth;public EqualsAndHashCodeDemo {}public boolean equals(Object o) {if (o == this) {return true;} else if (!(o instanceof EqualsAndHashCodeDemo)) {return false;} else {EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo)o;if (!other.canEqual(this)) {return false;} else {}}public int hashCode {int PRIME = true;int result = 1;Object $firstName = this.firstName;int result = result * 59 + ($firstName == ? 43 : $firstName.hashCode);Object $lastName = this.lastName;result = result * 59 + ($lastName == ? 43 : $lastName.hashCode);Object $dateOfBirth = this.dateOfBirth;result = result * 59 + ($dateOfBirth == ? 43 : $dateOfBirth.hashCode);return result;}}

3.4 @ToString 注解

使用 @ToString注解可以为指定类生成 toString 方法,@ToString注解的定义如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface ToString {booleanincludeFieldNames default true;String exclude default {};String of default {};booleancallSuper default false;booleandoNotUseGetters default false;booleanonlyExplicitlyIncluded default false;@Target(ElementType.FIELD)@Retention(RetentionPolicy.SOURCE)public @interface Exclude {}@Target({ElementType.FIELD, ElementType.METHOD})@Retention(RetentionPolicy.SOURCE)public @interface Include {int rank default 0;String name default "";}}

示例

package com.semlinker.lombok;@ToString(exclude = {"dateOfBirth"})public class ToStringDemo {String firstName;String lastName;LocalDate dateOfBirth;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class ToStringDemo {String firstName;String lastName;LocalDate dateOfBirth;publicToStringDemo {}public String toString {return "ToStringDemo(firstName=" + this.firstName + ", lastName=" +this.lastName + ")";}}

3.5 @Data 注解

@Data注解与同时使用以下的注解的效果是一样的:

  • @ToString
  • @Getter
  • @Setter
  • @RequiredArgsConstructor
  • @EqualsAndHashCode

@Data注解的定义如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface Data {String staticConstructor default "";}

示例

package com.semlinker.lombok;@Datapublic class DataDemo {private Long id;private String summary;private String description;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class DataDemo {private Long id;private String summary;private String description;publicDataDemo {}public Long getId {return this.id;}public voidsetId(Long id) {this.id = id;}public booleanequals(Object o) {if (o == this) {return true;} else if (!(o instanceof DataDemo)) {return false;} else {DataDemo other = (DataDemo)o;if (!other.canEqual(this)) {return false;} else {}}}protected booleancanEqual(Object other) {return other instanceof DataDemo;}public inthashCode {int PRIME = true;int result = 1;Object $id = this.getId;int result = result * 59 + ($id == ? 43 : $id.hashCode);Object $summary = this.getSummary;result = result * 59 + ($summary == ? 43 : $summary.hashCode);Object $description = this.getDescription;result = result * 59 + ($description == ? 43 : $description.hashCode);return result;}public String toString {return "DataDemo(id=" + this.getId + ", summary=" + this.getSummary + ", description=" + this.getDescription + ")";}}

3.6 @Log 注解

若你将 @Log的变体放在类上(适用于你所使用的日志记录系统的任何一种);之后,你将拥有一个静态的 final log 字段,然后你就可以使用该字段来输出日志。

@Logprivate static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName);@Log4jprivate static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);@Log4j2private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);@Slf4jprivate static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);@XSlf4jprivate static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);@CommonsLogprivate static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

3.7 @Synchronized 注解

@Synchronized是同步方法修饰符的更安全的变体。与synchronized一样,该注解只能应用在静态和实例方法上。它的操作类似于synchronized关键字,但是它锁定在不同的对象上。synchronized关键字应用在实例方法时,锁定的是 this 对象,而应用在静态方法上锁定的是类对象。对于 @Synchronized 注解声明的方法来说,它锁定的是$LOCK$lock@Synchronized注解的定义如下:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Synchronized {String value default "";}

示例

package com.semlinker.lombok;public class SynchronizedDemo {private final Object readLock = new Object;@Synchronizedpublic static voidhello {System.out.println("world");}@Synchronizedpublic intanswerToLife {return 42;}@Synchronized("readLock")public voidfoo {System.out.println("bar");}}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class SynchronizedDemo {private static final Object $LOCK = new Object[0];private final Object $lock = new Object[0];private final Object readLock = new Object;publicSynchronizedDemo {}public static voidhello {synchronized($LOCK) {System.out.println("world");}}public intanswerToLife {synchronized(this.$lock) {return 42;}}public voidfoo {synchronized(this.readLock) {System.out.println("bar");}}}

3.8 @Builder 注解

使用 @Builder注解可以为指定类实现建造者模式,该注解可以放在类、构造函数或方法上。@Builder注解的定义如下:

@Target({TYPE, METHOD, CONSTRUCTOR})@Retention(SOURCE)public @interface Builder {@Target(FIELD)@Retention(SOURCE)public @interface Default {}String builderMethodName default "builder";String buildMethodName default "build";String builderClassName default "";boolean toBuilder default false;AccessLevel access default lombok.AccessLevel.PUBLIC;@Target({FIELD, PARAMETER})@Retention(SOURCE)public @interface ObtainVia {String field default "";String method default "";boolean isStatic default false;}}

示例

package com.semlinker.lombok;@Builderpublic class BuilderDemo {private final String firstname;private final String lastname;private final String email;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class BuilderDemo {private final String firstname;private final String lastname;private final String email;BuilderDemo(String firstname, String lastname, String email) {this.firstname = firstname;this.lastname = lastname;this.email = email;}public static BuilderDemo.BuilderDemoBuilder builder {return new BuilderDemo.BuilderDemoBuilder;}public static class BuilderDemoBuilder {private String firstname;private String lastname;private String email;BuilderDemoBuilder {}public BuilderDemo.BuilderDemoBuilder firstname(String firstname) {this.firstname = firstname;return this;}public BuilderDemo.BuilderDemoBuilder lastname(String lastname) {this.lastname = lastname;return this;}public BuilderDemo.BuilderDemoBuilder email(String email) {this.email = email;return this;}public BuilderDemo build {return new BuilderDemo(this.firstname, this.lastname, this.email);}public String toString {return "BuilderDemo.BuilderDemoBuilder(firstname=" + this.firstname + ", lastname=" + this.lastname + ", email=" + this.email + ")";}}}

3.9 @SneakyThrows 注解

@SneakyThrows注解用于自动抛出已检查的异常,而无需在方法中使用 throw 语句显式抛出。@SneakyThrows注解的定义如下:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})@Retention(RetentionPolicy.SOURCE)public @interface SneakyThrows {Class<? extends Throwable> value default java.lang.Throwable.class;}

示例

package com.semlinker.lombok;public class SneakyThrowsDemo {@SneakyThrows@Overrideprotected Object clone {return super.clone;}}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class SneakyThrowsDemo {publicSneakyThrowsDemo {}protected Object clone {try {return super.clone;} catch (Throwable var2) {throw var2;}}}

3.10 @Non 注解

你可以在方法或构造函数的参数上使用 @Non注解,它将会为你自动生成非空校验语句。@Non注解的定义如下:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})@Retention(RetentionPolicy.CLASS)@Documentedpublic @interface Non {}

示例

package com.semlinker.lombok;public class NonDemo {@Getter@Setter@Nonprivate String name;}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class NonDemo {@Nonprivate String name;publicNonDemo {}@Nonpublic String getName {return this.name;}public voidsetName(@Non String name) {if (name == ) {throw new PointerException("name is marked non- but is ");} else {this.name = name;}}}

3.11 @Clean 注解

@Clean注解用于自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流。

@Target(ElementType.LOCAL_VARIABLE)@Retention(RetentionPolicy.SOURCE)public @interface Cleanup {String value default "close";}

示例

package com.semlinker.lombok;public class CleanupDemo {public static void main(String[] args) throws IOException {@Cleanup InputStream in = new FileInputStream(args[0]);@Cleanup OutputStream out = new FileOutputStream(args[1]);byte b = new byte[10000];while (true) {int r = in.read(b);if (r == -1) break;out.write(b, 0, r);}}}

以上代码经过 Lombok 编译后,会生成如下代码:

package com.semlinker.lombok;public class CleanupDemo {public CleanupDemo {}public static void main(String[] args) throws IOException {FileInputStream in = new FileInputStream(args[0]);try {FileOutputStream out = new FileOutputStream(args[1]);try {byte b = new byte[10000];while(true) {int r = in.read(b);if (r == -1) {return;}out.write(b, 0, r);}} finally {if (Collections.singletonList(out).get(0) != ) {out.close;}}} finally {if (Collections.singletonList(in).get(0) != ) {in.close;}}}}

3.11 @With 注解

在类的字段上应用 @With注解之后,将会自动生成一个withFieldName(newValue)的方法,该方法会基于 newValue 调用相应构造函数,创建一个当前类对应的实例。@With注解的定义如下:

@Target({ElementType.FIELD, ElementType.TYPE})@Retention(RetentionPolicy.SOURCE)public @interface With {AccessLevel value default AccessLevel.PUBLIC;With.AnyAnnotation onMethod default {};With.AnyAnnotation onParam default {};@Deprecated@Retention(RetentionPolicy.SOURCE)@Target({})public @interface AnyAnnotation {}}

示例

public class WithDemo {@With(AccessLevel.PROTECTED)@Nonprivate final String name;@Withprivate final int age;publicWithDemo(String name, int age) {if (name == ) throw new PointerException;this.name = name;this.age = age;}}

以上代码经过 Lombok 编译后,会生成如下代码:

public class WithDemo {@Nonprivate final String name;private final int age;publicWithDemo(String name, int age) {if (name == ) {throw new PointerException;} else {this.name = name;this.age = age;}}protected WithDemo withName(@Non String name) {if (name == ) {throw new PointerException("name is marked non- but is ");} else {return this.name == name ? this : new WithDemo(name, this.age);}}public WithDemo withAge(int age) {return this.age == age ? this : new WithDemo(this.name, age);}}

3.12 其它特性

val

val 用在局部变量前面,相当于将变量声明为 final,此外 Lombok 在编译时还会自动进行类型推断。val 的使用示例:

public class ValExample {public String example {val example = new ArrayList<String>;example.add("Hello, World!");val foo = example.get(0);return foo.toLowerCase;}public voidexample2 {val map = new HashMap<Integer, String>;map.put(0, "zero");map.put(5, "five");for (val entry : map.entrySet) {System.out.printf("%d: %s\n", entry.getKey, entry.getValue);}}}

以上代码等价于:

public class ValExample {public String example {final ArrayList<String> example = new ArrayList<String>;example.add("Hello, World!");final String foo = example.get(0);return foo.toLowerCase;}public voidexample2 {final HashMap<Integer, String> map = new HashMap<Integer, String>;map.put(0, "zero");map.put(5, "five");for (final Map.Entry<Integer, String> entry : map.entrySet) {System.out.printf("%d: %s\n", entry.getKey, entry.getValue);}}}

至此功能强大的 Lombok 工具就介绍完了。

]]>
http://www.php100.com/9/87425.html/feed 0
2020年UI界面设计趋势 http://www.php100.com/6/87422.html http://www.php100.com/6/87422.html#respond Wed, 06 Nov 2019 02:01:38 +0000 http://www.php100.com/?p=87422 2019年接近尾声,最近一直在研究数字设计领域的未来趋势。从各大设计网站上观察到一些最受喜爱的作品,从中研究发现未来的界面设计趋势,包括今年一些受欢迎的设计系统,从中提取一些未来可能的设计风格走向。之前看过我文章的伙伴知道,我之前写过一篇图标趋势的文章,里面就是探索明年趋势走向。那么对于界面,同样的我们需要提前掌握大致设计风格走向,为自家产品改版做一些设计储备。

Dark Mode

黑夜模式

黑夜模式无疑是今年比较热的一个设计方向,为什么这么说?首先是Google材料设计语言更新了,增加暗夜模式。同时今年Q3季度苹果发布IOS13设计语言系统,里面增加暗夜模式。不用说后续很多APP应用都会跟随两大厂商更新这个黑夜模式设计方向。无疑2020年这个将会持续。黑夜模式出现为减少用户眼睛疲劳,提升产品使用体验。

2020年UI界面设计趋势

Materials Design Dark Theme

2020年UI界面设计趋势

IOS 13 Dark mode

IOS 13 Dark mode 宣传视频

2020年UI界面设计趋势

Medium

2020年UI界面设计趋势

Instagram

2020年UI界面设计趋势
2020年UI界面设计趋势

Blank design

留白设计

今年下半年观察到Facebook旗下的Messenger应用和前段时间雅虎更新他们家品牌语言的同时,也重新设计他们家所有产品。我们可以看到如下应用截图,基本去分割线,通过留白区分层级。这样的好处可以减少分割线对内容的干扰,界面呼吸感增强,达到简约设计的目的。

2020年UI界面设计趋势

Messenger 和 Yahoo

2020年UI界面设计趋势

McDonald`s

2020年UI界面设计趋势

PINTEREST 和 VSCO

2020年UI界面设计趋势

Apple Design

苹果设计风格

苹果风一直盛行至今天,包括大圆角卡片设计、大标题、粗字体。苹果今年刚刚更新的IOS13设计语言同样的是增强了卡片设计。这种风格将会在2020年继续盛行,因此作为设计师我们更应该提前去对这种趋势熟练掌握。卡片设计作为承载内容的容器,其实更适用今天移动互联网的大方向,因为卡片可以作为一个容器封装复杂的内容,使得模块层级区分明显,适配性也是更强,更聚焦,来看下具体案例。

2020年UI界面设计趋势

PINTEREST 和 APPSTORE

Pinterest是今年9月份左右刚更新的设计,圆角卡片大范围的运用。

2020年UI界面设计趋势

BEHANCE 和 MEDIUM

Medium对重点栏目在今年更新设计时候启用了卡片。

2020年UI界面设计趋势
2020年UI界面设计趋势

Brand

品牌渗透

为什么要说品牌渗透?Google 在系统设计语言里面增加图形特征模块,是什么?就是通过连接品牌元素到产品设计中。到今天很多应用已经开始熟练掌握这种设计思路了,比如运用到图标里面或者界面设计中。苹果虽然没有很明确公开说明,但是我们可以从苹果家族设计语言可以看出,包括线下体验店、手机硬件、包装盒以及各种各种物料设计,都是一种白色语言设计风格。通过颜色与图形特征做品牌区分,苹果算是目前做的最好的,史无前例。那么这种趋势在2020将会一直保持。

2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势

Apple体系产品与LOGO相粘性强,具有超强的记忆点

Google家族品牌穿透

IBM设计跟随LOGO有一致基因

2020年UI界面设计趋势
2020年UI界面设计趋势

ibm文字特征与图标语言库某些元素一致

2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势

Grid Design

网格化布局

网格化是一切设计系统的基础,Materials Design,Apple Design,IBM Design,Naver Design,等等这几家在设计语言基础上对网格研究与应用算是比较成熟的,Naver方形网格,Google的8px网格,IBM 2PX网格,苹果其实没有很强调他们家网格系数,其实我们可以细看他们的设计规范,已经提到8px Grid。因此网格化布局在2020将是非常重要的一个工具。

IBM 2PX网格语言

2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势
2020年UI界面设计趋势

一致的网格基因能够沿用到设计体系的任何地方,包括布局、字体行高,图标网格等等

Motion

微动效

5G时代已经到来,加上目前硬件升级,动效的普及已经不再是难题。我们可以慢慢发现目前很多产品设计里面都开始加入动效,来提升产品体验,同时动效也有很强的指导性质。在某些场景下,使用动效能够很好与用户进行互动交流,因此在2020年动效的使用将会越来越多。

2020年UI界面设计趋势

Google Tab Motion

2020年UI界面设计趋势

washing-app-ui

2020年UI界面设计趋势

logo_corti_

3D Animation

3D动画

随着硬件升级,5G时代开始大面积推广与普及,在应用程序里面使用3D动画来展示产品是一个很不错的选择,3D更加贴近真实物理环境,贴近我们生活。

2020年UI界面设计趋势

restaurant_app_welcome_animation

2020年UI界面设计趋势

show_files_natura

2020年UI界面设计趋势

3D flip menu

2020年UI界面设计趋势

3D Product Swipe

]]>
http://www.php100.com/6/87422.html/feed 0
微软发布 Visual Studio Online:Web 版 VS Code + 云开发环境 http://www.php100.com/6/87420.html http://www.php100.com/6/87420.html#respond Wed, 06 Nov 2019 01:58:21 +0000 http://www.php100.com/?p=87420 在 Microsoft Ignite 2019 大会上,微软正式发布了 Visual Studio Online 公开预览版!今天发布的 Visual Studio Online 与五月份发布的 Visual Studio Code Remote Development 可以说是微软今年在开发工具领域最重磅的两个新产品了。概览Visual Studio Online 提供了由云服务支撑的开发环境。无论是一个长期项目,或是像 review pull request 这样的短期任务,都可以使用 Visual Studio Online。你可以通过 VS Code、VS IDE 或者 Web 版 VS Code 来连接上云端开发环境。此外,你还可以连接到自己的环境,不需要任何费用!简单来说,Visual Studio Online 由两部分组成:“前端”与“后端”。

  • “前端”:VS Code、VS IDE 和 Web 版 VS Code。
  • “后端”:由云服务支撑的开发环境。

云开发环境

云开发环境是 Visual Studio Online 的“后端”,背后由 Azure 云计算平台提供强有力的支持。目前,Visual Studio Online 云开发环境支持 Linux 云开发环境,有以下两种环境可供选择:

  • Standard (4 cores, 8 GB RAM, 64 GB HDD)
  • Premium (8 cores, 16 GB RAM, 64 GB HDD)

目前,Windows 云开发环境处于 Private Preview 阶段。Visual Studio Online 云开发环境包含了所有软件开发的内容:编译、调试、开发环境还原,等等。当你需要开发一个新项目,或者要 review PR 时,你可以快速地启动一个云开发环境。它会自动配置你需要在项目上工作的所有内容:源代码、运行时、编译器、调试器、编辑器、自定义的 dotfiles、相关的插件等。Visual Studio Online 云开发环境享有诸多云计算带来的好处:

  • 可以快速地创建或者关闭云端的开发环境。这使得开发者可以有更多的时间在 coding 上,而不是把时间浪费在搭建开发环境上。
  • 水平扩展:你也可以创建多个不同的开发环境。
  • 垂直扩展:如果需要更强的运算能力,可以选择更强的开发环境,使用更强的 CPU 与内存。
  • 由于是托管的环境,无维护成本。
  • 不同的项目有独立的开发与运行环境,相互不会干扰。
  • 按使用量付费。Visual Studio Online 也有内置的自动挂起机制,防止额外的开销。

Web 版 VS Code

相信读者们对 Web 版 VS Code 期待已久了。对 VS Code 熟悉的朋友应该知道,VS Code 是基于 Electron 开发的,而 Electron 是使用 Web 技术栈(JavaScript, HTML 和 CSS)来开发跨平台桌面应用的。所以,把 VS Code 搬到浏览器中,则是必然的趋势。Visual Studio Online 包含了基于 VS Code 的 Web 版编辑器,它作为 Visual Studio Online 的“前端”,有以下的这些亮点:

  • 可以直接在 Web 版 VS Code 中打开你的 Git 项目。
  • 支持 VS Code 插件。丰富的插件生态,任你挑选你喜欢的插件。
  • 内置的集成终端。犹如在本地的 Terminal 一样强大!
  • 内置的 Visual Studio IntelliCode,利用 AI 提供更强大的代码自动补全,
  • 内置的 Visual Studio Live Share,使得多个开发者可以在 VS Code、VS IDE 或者 Web 版 VS Code 中进行实时的协同开发和调试。
  • 在任何设备上编辑、运行、调试你的项目。是的!可以在 iPad 上运行 VS Code(Web 版)了!

不止 Web除了 Web 版 VS Code 之外,Visual Studio Online 还支持通过 VS Code 和 VS IDE(Private Preview)连接 Visual Studio Online 云开发环境。Linux 云开发环境(Public Preview)可以通过两种“前端”编辑器连接:VS Code 和 Web 版 VS Code。Windows 云开发环境(Private Preview)可以通过所有的三种“前端”编辑器连接:VS Code、VS IDE 和 Web 版 VS Code。目前,VS IDE 对于 Visual Studio Online 的支持处于 Private Preview 阶段。自托管的环境除了连上由 Azure 支持的 Visual Studio Online 云开发环境,开发者还可以连上自己搭建的开发环境。其实,通过 Visual Studio Code Remote Development,开发者早已可以通过 VS Code 来连上远程的开发环境了。现在,有了 Web 版 VS Code,我们可以直接在浏览器中连上自己的远程开发环境,更加地方便!而且,这是完全免费的。来体验吧!还等什么呢?快来体验下 Visual Studio Online 吧!全新的开发体验,如丝般顺滑~https://visualstudio.microsoft.com/services/visual-studio-online/

]]>
http://www.php100.com/6/87420.html/feed 0
令人过目不忘的代码注释 http://www.php100.com/6/122/87417.html http://www.php100.com/6/122/87417.html#respond Wed, 06 Nov 2019 01:49:14 +0000 http://www.php100.com/?p=87417 1、这是一个被代码耽误的诗人

令人过目不忘的代码注释

2、来一份1987年的代码看看

令人过目不忘的代码注释

3、产品经理要对此负责

令人过目不忘的代码注释

4、不敢看,也不敢问

令人过目不忘的代码注释

5、Nike robots.txt 上的注释

查看地址:https://www.nike.com/robots.txt

令人过目不忘的代码注释

6、程序员正确发牢骚的地方

令人过目不忘的代码注释

7、阅读源码的人,心里一定的崩溃的

令人过目不忘的代码注释

8、第一天上班看到这段注释就想辞职。

令人过目不忘的代码注释

9、我能说什么

令人过目不忘的代码注释

10、这个功能可以收客户多少钱?

令人过目不忘的代码注释
]]>
http://www.php100.com/6/122/87417.html/feed 0