• 一、了解pipeline
    • 1、什么是pipeline
    • 2、为什么要使用pipeline
    • 3、迁移pipeline有什么好处
  • 二、jenkins pipeline 语法
  • 三、创建pipeline项目的html项目
    • 1、新建仓库,将代码上传到Gitlab
    • 2、新建一个简单的pipeline项目
      • 2.1、生成获取git代码的pipeline脚本语句
        • 2.1.1、pipeline参数化构建方式1--使用pipeline语法拉取代码
        • 2.1.1、pipeline参数化构建方式1-- 利用 Jenkinsfile 上传代码仓库进行构建 (建议使用这种,不容易出错)
      • 2.2、生成代码质量检测的pipeline代码
      • 2.3、整合pipeline语法
    • 2、开始构建,并验证钉钉通知
    • 3、进一步优化pipeline
      • 3.1、优化SonarQube代码质检

一、了解pipeline

1、什么是pipeline

  • 可以理解成将我们开发部署环境生态链的每一步都通过pipeilne流水线串联起来,并代码化,是的我们开发人员一键就能将本地的代码发布到测试环境中进行测试发布,最终实现持续集成持续发布

2、为什么要使用pipeline

  • 传统的开发部署流程,每一步都需要人为干预,迁移到pipeline能实现每一步自动化,不需要人为干预,所有流程都可以通过代码自动化

3、迁移pipeline有什么好处

  • 可以实现持续集成持续部署,节省产品发布的时间,优化部署策略,节省人力成本,自动化脚本复用等等

二、jenkins pipeline 语法

  • 所有代码都包裹在 pipeline{} 层内
  • agent{} 定义任务在那台jenkins主机上运行,可以是any、none等
  • environment{} 变量名称=变量值,用于定义环境变量,比如PATH路径等
  • stages{} 类似一个大项目的集合,主要用来包含所有 stage 子任务
  • stage{} 类似一个项目中的单个任务,主要用来包含 step{} 子层
  • step{} 用来实现具体执行的动作

示例如下:

pipeline{// 定义在哪台主机上运行agent any// 定义变量environment{host1='10.4.7.14'host2='10.4.7.15'}stages{stage("分发代码"){step{echo “分发代码给 $host1 和 $host2”}}stage("开始构建"){step{echo "Build Success"}}}
}

三、创建pipeline项目的html项目

  • 步骤:代码拉取 - - -> 代码检测 - - -> 代码构建 - - -> 代码部署 - - -> 消息通知
  • 创建一个简单的html的项目

1、新建仓库,将代码上传到Gitlab

# 修改index.html文件先将"代码1"作为版本1.0上传
[root@deploy pipehtml]# git add .
[root@deploy pipehtml]# git commit -m 'html-v1.0'
[root@deploy pipehtml]# git tag -a 'v1.0' -m 'lizitexiao'
[root@deploy pipehtml]# git push -u origin master
[root@deploy pipehtml]# git push -u origin v1.0# 修改index.html文件再将"代码2"作为版本1.1上传
[root@deploy pipehtml]# git add .
[root@deploy pipehtml]# git commit -m 'html-v1.1'
[root@deploy pipehtml]# git tag -a 'v1.1' -m 'pttexiao'
[root@deploy pipehtml]# git push -u origin master
[root@deploy pipehtml]# git push -u origin v1.1

html代码示例:
代码1

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>star</title>
<script type="text/javascript">
window.onload = function () {C = Math.cos; // cache Math objects
S = Math.sin;
U = 0;
w = window;
j = document;
d = j.getElementById("c");
c = d.getContext("2d");
W = d.width = w.innerWidth;
H = d.height = w.innerHeight;
c.fillRect(0, 0, W, H); // resize <canvas> and draw black rect (default)
c.globalCompositeOperation = "lighter";  // switch to additive color application
c.lineWidth = 0.2;
c.lineCap = "round";
var bool = 0,
t = 0; // theta
d.onmousemove = function (e) {if(window.T) {if(D==9) { D=Math.random()*15; f(1); }
clearTimeout(T);
}
X = e.pageX; // grab mouse pixel coords
Y = e.pageY;
a=0; // previous coord.x
b=0; // previous coord.y
A = X, // original coord.x
B = Y; // original coord.y
R=(e.pageX/W * 999>>0)/999;
r=(e.pageY/H * 999>>0)/999;
U=e.pageX/H * 360 >>0;
D=9;
g = 360 * Math.PI / 180;
T = setInterval(f = function (e) { // start looping spectrum
c.save();
c.globalCompositeOperation = "source-over"; // switch to additive color application
if(e!=1) {c.fillStyle = "rgba(0,0,0,0.02)";
c.fillRect(0, 0, W, H); // resize <canvas> and draw black rect (default)
}
c.restore();
i = 25; while(i --) {c.beginPath();
if(D > 450 || bool) { // decrease diameter
if(!bool) { // has hit maximum
bool = 1;
}
if(D < 0.1) { // has hit minimum
bool = 0;
}
t -= g; // decrease theta
D -= 0.1; // decrease size
}
if(!bool) {t += g; // increase theta
D += 0.1; // increase size
}
q = (R / r - 1) * t; // create hypotrochoid from current mouse position, and setup variables (see: http://en.wikipedia.org/wiki/Hypotrochoid)
x = (R - r) * C(t) + D * C(q) + (A + (X - A) * (i / 25)) + (r - R); // center on xy coords
y = (R - r) * S(t) - D * S(q) + (B + (Y - B) * (i / 25));
if (a) { // draw once two points are set
c.moveTo(a, b);
c.lineTo(x, y)
}
c.strokeStyle = "hsla(" + (U % 360) + ",100%,50%,0.75)"; // draw rainbow hypotrochoid
c.stroke();
a = x; // set previous coord.x
b = y; // set previous coord.y
}
U -= 0.5; // increment hue
A = X; // set original coord.x
B = Y; // set original coord.y
}, 16);
}
j.onkeydown = function(e) { a=b=0; R += 0.05 }
d.onmousemove({pageX:300, pageY:290})
}</script>
</head><body style="margin:0px;padding:0px;width:100%;height:100%;overflow:hidden;">
<canvas id="c"></canvas>
</body>
</html>

代码2

<!doctype html>
<html>
<head><meta charset="utf-8"><title>H5,200行代码实现粒子漩涡特效</title><style>html,body{margin:0px;width:100%;height:100%;overflow:hidden;background: #000000;}#canvas{position:absolute;width:100%;height:100%;}</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>function project3D(x,y,z,vars){var p,d;x-=vars.camX;y-=vars.camY-8;z-=vars.camZ;p=Math.atan2(x,z);d=Math.sqrt(x*x+z*z);x=Math.sin(p-vars.yaw)*d;z=Math.cos(p-vars.yaw)*d;p=Math.atan2(y,z);d=Math.sqrt(y*y+z*z);y=Math.sin(p-vars.pitch)*d;z=Math.cos(p-vars.pitch)*d;var rx1=-1000;var ry1=1;var rx2=1000;var ry2=1;var rx3=0;var ry3=0;var rx4=x;var ry4=z;var uc=(ry4-ry3)*(rx2-rx1)-(rx4-rx3)*(ry2-ry1);var ua=((rx4-rx3)*(ry1-ry3)-(ry4-ry3)*(rx1-rx3))/uc;var ub=((rx2-rx1)*(ry1-ry3)-(ry2-ry1)*(rx1-rx3))/uc;if(!z)z=0.000000001;if(ua>0&&ua<1&&ub>0&&ub<1){return {x:vars.cx+(rx1+ua*(rx2-rx1))*vars.scale,y:vars.cy+y/z*vars.scale,d:(x*x+y*y+z*z)};}else{return { d:-1 };}}function elevation(x,y,z){var dist = Math.sqrt(x*x+y*y+z*z);if(dist && z/dist>=-1 && z/dist <=1) return Math.acos(z / dist);return 0.00000001;}function rgb(col){col += 0.000001;var r = parseInt((0.5+Math.sin(col)*0.5)*16);var g = parseInt((0.5+Math.cos(col)*0.5)*16);var b = parseInt((0.5-Math.sin(col)*0.5)*16);return "#"+r.toString(16)+g.toString(16)+b.toString(16);}function interpolateColors(RGB1,RGB2,degree){var w2=degree;var w1=1-w2;return [w1*RGB1[0]+w2*RGB2[0],w1*RGB1[1]+w2*RGB2[1],w1*RGB1[2]+w2*RGB2[2]];}function rgbArray(col){col += 0.000001;var r = parseInt((0.5+Math.sin(col)*0.5)*256);var g = parseInt((0.5+Math.cos(col)*0.5)*256);var b = parseInt((0.5-Math.sin(col)*0.5)*256);return [r, g, b];}function colorString(arr){var r = parseInt(arr[0]);var g = parseInt(arr[1]);var b = parseInt(arr[2]);return "#"+("0" + r.toString(16) ).slice (-2)+("0" + g.toString(16) ).slice (-2)+("0" + b.toString(16) ).slice (-2);}function process(vars){if(vars.points.length<vars.initParticles) for(var i=0;i<5;++i) spawnParticle(vars);var p,d,t;p = Math.atan2(vars.camX, vars.camZ);d = Math.sqrt(vars.camX * vars.camX + vars.camZ * vars.camZ);d -= Math.sin(vars.frameNo / 80) / 25;t = Math.cos(vars.frameNo / 300) / 165;vars.camX = Math.sin(p + t) * d;vars.camZ = Math.cos(p + t) * d;vars.camY = -Math.sin(vars.frameNo / 220) * 15;vars.yaw = Math.PI + p + t;vars.pitch = elevation(vars.camX, vars.camZ, vars.camY) - Math.PI / 2;var t;for(var i=0;i<vars.points.length;++i){x=vars.points[i].x;y=vars.points[i].y;z=vars.points[i].z;d=Math.sqrt(x*x+z*z)/1.0075;t=.1/(1+d*d/5);p=Math.atan2(x,z)+t;vars.points[i].x=Math.sin(p)*d;vars.points[i].z=Math.cos(p)*d;vars.points[i].y+=vars.points[i].vy*t*((Math.sqrt(vars.distributionRadius)-d)*2);if(vars.points[i].y>vars.vortexHeight/2 || d<.25){vars.points.splice(i,1);spawnParticle(vars);}}}function drawFloor(vars){var x,y,z,d,point,a;for (var i = -25; i <= 25; i += 1) {for (var j = -25; j <= 25; j += 1) {x = i*2;z = j*2;y = vars.floor;d = Math.sqrt(x * x + z * z);point = project3D(x, y-d*d/85, z, vars);if (point.d != -1) {size = 1 + 15000 / (1 + point.d);a = 0.15 - Math.pow(d / 50, 4) * 0.15;if (a > 0) {vars.ctx.fillStyle = colorString(interpolateColors(rgbArray(d/26-vars.frameNo/40),[0,128,32],.5+Math.sin(d/6-vars.frameNo/8)/2));vars.ctx.globalAlpha = a;vars.ctx.fillRect(point.x-size/2,point.y-size/2,size,size);}}}}vars.ctx.fillStyle = "#82f";for (var i = -25; i <= 25; i += 1) {for (var j = -25; j <= 25; j += 1) {x = i*2;z = j*2;y = -vars.floor;d = Math.sqrt(x * x + z * z);point = project3D(x, y+d*d/85, z, vars);if (point.d != -1) {size = 1 + 15000 / (1 + point.d);a = 0.15 - Math.pow(d / 50, 4) * 0.15;if (a > 0) {vars.ctx.fillStyle = colorString(interpolateColors(rgbArray(-d/26-vars.frameNo/40),[32,0,128],.5+Math.sin(-d/6-vars.frameNo/8)/2));vars.ctx.globalAlpha = a;vars.ctx.fillRect(point.x-size/2,point.y-size/2,size,size);}}}}}function sortFunction(a,b){return b.dist-a.dist;}function draw(vars){vars.ctx.globalAlpha=.15;vars.ctx.fillStyle="#000";vars.ctx.fillRect(0, 0, canvas.width, canvas.height);drawFloor(vars);var point,x,y,z,a;for(var i=0;i<vars.points.length;++i){x=vars.points[i].x;y=vars.points[i].y;z=vars.points[i].z;point=project3D(x,y,z,vars);if(point.d != -1){vars.points[i].dist=point.d;size=1+vars.points[i].radius/(1+point.d);d=Math.abs(vars.points[i].y);a = .8 - Math.pow(d / (vars.vortexHeight/2), 1000) * .8;vars.ctx.globalAlpha=a>=0&&a<=1?a:0;vars.ctx.fillStyle=rgb(vars.points[i].color);if(point.x>-1&&point.x<vars.canvas.width&&point.y>-1&&point.y<vars.canvas.height)vars.ctx.fillRect(point.x-size/2,point.y-size/2,size,size);}}vars.points.sort(sortFunction);}function spawnParticle(vars){var p,ls;pt={};p=Math.PI*2*Math.random();ls=Math.sqrt(Math.random()*vars.distributionRadius);pt.x=Math.sin(p)*ls;pt.y=-vars.vortexHeight/2;pt.vy=vars.initV/20+Math.random()*vars.initV;pt.z=Math.cos(p)*ls;pt.radius=200+800*Math.random();pt.color=pt.radius/1000+vars.frameNo/250;vars.points.push(pt);}function frame(vars) {if(vars === undefined){var vars={};vars.canvas = document.querySelector("canvas");vars.ctx = vars.canvas.getContext("2d");vars.canvas.width = document.body.clientWidth;vars.canvas.height = document.body.clientHeight;window.addEventListener("resize", function(){vars.canvas.width = document.body.clientWidth;vars.canvas.height = document.body.clientHeight;vars.cx=vars.canvas.width/2;vars.cy=vars.canvas.height/2;}, true);vars.frameNo=0;vars.camX = 0;vars.camY = 0;vars.camZ = -14;vars.pitch = elevation(vars.camX, vars.camZ, vars.camY) - Math.PI / 2;vars.yaw = 0;vars.cx=vars.canvas.width/2;vars.cy=vars.canvas.height/2;vars.bounding=10;vars.scale=500;vars.floor=26.5;vars.points=[];vars.initParticles=700;vars.initV=.01;vars.distributionRadius=800;vars.vortexHeight=25;}vars.frameNo++;requestAnimationFrame(function() {frame(vars);});process(vars);draw(vars);}frame();
</script>
</body>
</html>

2、新建一个简单的pipeline项目

  • pipeline代码有两种填写方式

    • 1、直接将构建代码写在流水线框栏中(“流水线”–>选择"Pipeline scripts")
    • 2、将pipeline脚本文件卸载git代码仓库中(选择"Pipeline scripts from SCM",填写git的url及连接秘钥,并且将jenkinsfile上传到git仓库。jenkinsfile中有pipeline代码)

2.1、生成获取git代码的pipeline脚本语句

  • pipeline代码可以点击"流水线"下的"流水线语法"来自动生成 ( 比如我们的第一步是获取代码,获取pipeline语法步骤如下)

    • 那么就选择"示例步骤"下的"checkout: Check Out from version control"版本控制
    • 填写git仓库ssh地址和秘钥
    • 点击"生成流水线脚本",就会自动生成代码


将语句保存下来,后面整合到完整的pipeline语句中

checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: 'git_02', url: 'git@gitlab.prod.com:devops/pipehtml.git']]])
2.1.1、pipeline参数化构建方式1–使用pipeline语法拉取代码

注意:如果需要配置参数化构建,需要在自动生成的代码上添加配置信息(示例如下)

steps {checkout([$class: 'GitSCM', // ${git_version} 为git的tag,在选择git参数是自定义的变量branches: [[name: "${git_version}"]], doGenerateSubmoduleConfigurations: false, extensions: [], gitTool: 'Default', submoduleCfg: [], userRemoteConfigs: [[url: 'git@gitlab.prod.com:devops/pipehtml.git',credentialsId: 'git_02',]]])}
2.1.1、pipeline参数化构建方式1-- 利用 Jenkinsfile 上传代码仓库进行构建 (建议使用这种,不容易出错)
  • 选择"Pipeline Scripts from SCM",然后填写对应信息,pipeline语法写在Jenkinsfile中(这种方式的构建,就不需要在pipeline中写拉取代码的语句了)

2.2、生成代码质量检测的pipeline代码

  • SonarQube进行代码质检是通过shell命令的方式执行的,所以这里选择"sh:Shell Script",在框栏中填入shell命令

sh '/usr/local/sonar-scanner/bin/sonar-scanner  -Dsonar.projectName=${JOB_NAME} -Dsonar.projectKey=html -Dsonar.sources=.'

2.3、整合pipeline语法

  • Pipeline Scripts form SCM 方式
# 编写Jenkinsfile并上传
[root@deploy pipehtml]# vim Jenkinsfile
pipeline{// 定义在哪台主机上运行agent any// 定义变量environment{hosts='10.4.7.14 10.4.7.15'host1='10.4.7.14'host2='10.4.7.15'}stages{stage("代码质检"){steps {sh '/usr/local/sonar-scanner/bin/sonar-scanner  -Dsonar.projectName=${JOB_NAME} -Dsonar.projectKey=html -Dsonar.sources=.'}}stage("开始构建"){steps {echo "使用maven编译"}}stage("代码部署"){steps {sh '/root/scripts/html-deploy.sh'}}}post {success {dingtalk (robot: 'robot-1',type: 'TEXT',text: ['流水线构建部署成功','SUCCESS'],btns: [[title: 'Blue Ocean',actionUrl: 'http://jenkins.prod.com/blue/organizations/jenkins/pipeline-html/activity'],[title: '查看',actionUrl: 'http://jenkins.prod.com/']],atAll: true)}failure {dingtalk (// robot参数:是我们在"系统管理"--“系统配置”--"钉钉"配置中定义的机器人名称robot: 'robot-1',type: 'ACTION_CARD',text: ['流水线构建部署失败','FAILED'],btns: [[title: 'Blue Ocean',actionUrl: 'http://jenkins.prod.com/blue/organizations/jenkins/pipeline-html/activity'],[title: '查看',actionUrl: 'http://jenkins.prod.com/']],atAll: true)}}
}
  • 查看脚本内容
[root@deploy ~]# cat scripts/html-deploy.sh
#!/bin/bashDATE=$(date +%Y-%m-%d-%H-%M-%S)
# hosts='10.4.7.14 10.4.7.15'  # 在pipeline中定义了
Sdir=/opt
Ddir=/usr/local/html
Postfix=${DATE}-${git_version}# 到项目目录,将拉取下来的项目文件进行打包(打包的目的是为了方便传输给其他worker节点)
get_code(){cd ${WORKSPACE} && \tar czf ${Sdir}/html-${Postfix}.tar.gz ./*
}# 将内容scp到各个节点
scp_hosts(){for host in ${hosts};doscp ${Sdir}/html-${Postfix}.tar.gz root@${host}:${Sdir}ssh ${host} "mkdir ${Ddir}-${Postfix} &&\tar xf ${Sdir}/html-${Postfix}.tar.gz -C ${Ddir}-${Postfix} &&\rm -rf ${Ddir} &&\ln -s ${Ddir}-${Postfix} ${Ddir}"done
}rollback(){rollback_dir=$(find $(echo ${Ddir} |sed 's/html//') -maxdepth 1 -type d -name "*-${git_version}")for host in ${hosts};dossh ${host} "rm -rf ${Ddir} &&\ln -s ${rollback_dir} ${Ddir}"done
}deploy(){get_codescp_hosts
}if [[ ${action} == deploy ]];thenif [[ ${GIT_COMMIT} == ${GIT_PREVIOUS_SUCCESSFUL_COMMIT} ]];thenecho "这个版本已经部署过,若要使用旧版本,请选择rollback版本回退"exit 1elsedeployfi
elif [[ ${action} == rollback ]];thenrollback
fi

2、开始构建,并验证钉钉通知

  • 可以看到构建的详细过程
  • 使用 Blue Ocean 来构建
    • 要安装"Blue Ocean"插件才能使用
  • 点击"运行",就开始执行构建了
  • 点击任务名称,还可以查看详细的构建过程
  • 构建完成后,收到钉钉信息

3、进一步优化pipeline

3.1、优化SonarQube代码质检

  • 修改代码
stage("代码质检"){steps {//  withSonarQubeEnv():括号中的名称是在"系统管理"--"系统配置"--"SonarQubeSonarQubeServer"配置的自定义名称.(这个参数添加后,就可以在Jenkins终端上链接到sonarqube上。)withSonarQubeEnv('SonarQube') {// sh后面的SonarQube命令也可以用'+'拼接起来,类似python,也可以直接一行长命令sh '/usr/local/sonar-scanner/bin/sonar-scanner -Dsonar.projectName=${JOB_NAME} -Dsonar.projectKey=html -Dsonar.sources=.'}// 万一发生错误,pipeline 将在超时后被终止 (这个代码质检返回状态的配置,需要在sonarqube上开启webhook才可用)timeout(time: 30, unit: 'MINUTES') {// 告诉 Jenkins 等待 SonarQube 返回的分析结果。当 abortPipeline=true,表示质量不合格,将 pipeline 状态设置为 UNSTABLE。终止后面的任务执行waitForQualityGate abortPipeline: true}}
}

CICD -- pipeline 流水线相关推荐

  1. Pipeline流水线-通过Jenkinsfile构建任务

    写在前面 作为CI/CD工具的宠儿,jenkins深受java程序员.k8s领域的喜爱.jenkins有广泛的插件,可以支撑多种应用场景.虽然jenkins的权限管理让人感到可惜,但是基于庞大的用户群 ...

  2. pipeline流水线及分布式流水线发布php项目

    创建一个基于pipeline流水线的项目 第一个选项是流水线脚本(不是shell脚本),右上角有一个脚本的范例 范例: 1:尝试自己写一个脚本 2:hello world脚本(里面是函数,输出的内容, ...

  3. Pipeline流水线及分布式流水线发布PHP项目及JAVA项目

    Jenkins的Pipeline流水线 主机名 IP地址 备注 Git 192.168.146.136 Git服务器 Jenkins 192.168.146.137 Jenkins服务器 Pipeli ...

  4. Jenkins骚操作第四章构建maven项目和Pipeline流水线项目构建

    文章目录 Jenkins构建Maven项目 1.Jenkins项目构建类型-自由风格项目构建 1.1.拉取代码 1.2.编译打包 1.3.部署 2.Jenkins项目构建类型(3)--Maven项目构 ...

  5. Pipeline流水线项目构建

    目录 Pipeline简介 概念 安装Pipeline插件 Scripted脚本式Pipeline Declarative声明式Pipeline 使用代码生成器生成流水线脚本 管理Jenkinsfil ...

  6. DevOps流水线(1)什么是Pipeline流水线?

    从头开始构建 DevOps 流水线.推动该计划的核心技术是 Jenkins,这是一个用于建立持续集成和持续交付(CI/CD)流水线的开源工具. 在花旗,有一个单独的团队为专用的 Jenkins 流水线 ...

  7. Pipeline(流水线)模式

    模式名称 Pipeline(流水线)模式 原文:http://www.uml.org.cn/j2ee/201909271.asp 模式解决的问题 有时一些线程的步奏比较冗长,而且由于每个阶段的结果与下 ...

  8. Jenkins之Pipeline流水线构建项目

    1.Pipeline简介 1)概念 Pipeline,简单来说,就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排和可视 ...

  9. Redis 学习 - 05 Node.js 客户端操作 Redis、Pipeline 流水线

    使用编程语言客户端操作 Redis 目前我们进行的操作都是通过 Redis 的命令行客户端 redis-cli 进行的. 开发者也可以通过 Redis 图形管理软件操作,例如 RDM(Redis De ...

最新文章

  1. BitCask 持久化hash存储引擎 原理介绍
  2. Windos 10 下,应用版ubuntu系统, 访问windos文件系统
  3. 重庆python就业工资待遇-重庆python培训多少钱?
  4. 对Python认识和对我专业的影响
  5. Acwing104. 货仓选址:贪心(绝对值不等式)
  6. perl正则表达式-1
  7. sqli-lib1-36关 精选篇
  8. [css] 异步加载CSS的方式有哪些?
  9. 技术干货 | “选图预览并上传”的场景如何解?全网最全方案汇总来了
  10. ue4 怎么传递变量到另一个蓝图_UE4中用Niagara实现procedural浪花
  11. thinkserver TS250安装centos7.5经验
  12. 【Qt+ OpenGL】实现人体3D显示与控制
  13. 软考中级网络工程师怎么复习?
  14. Ajax不执行回调函数
  15. 看完这个故事终于知道区块链是什么了
  16. 史上最牛最强的linux学习笔记 4.linux常用命令
  17. 动态规划算法实现0/1背包问题
  18. 苹果手机没有备份怎么恢复照片?
  19. 机器学习中的置信区间与置信度
  20. 用Mahout构建职位推荐引擎

热门文章

  1. 基于html+js实现轮播图(自动轮播、左右按钮、小圆点点击及切换图片)
  2. Fluent案例:肾动脉RDN治疗过程的仿真
  3. CFileDialog过滤的用法
  4. IBM 华为等薪资福利规定
  5. html列表横向变纵向,手机版利用 CSS 将横向表格转换成竖向列表显示
  6. WebSecurityConfigurerAdapter简单分析
  7. 关联分析(Apriori,FP-growth)
  8. JavaScript 原型链总结(一)
  9. windows系统:oracle 10g的下载、安装、配置 (提供绝版安装文件下载)
  10. DCloud 使用chrome调试