首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

Kubernetes Helm入门指南

什么是Helm?这可不是暗黑破坏神里装备的名称:头盔,而是Kubernetes的一个包管理工具,用来简化Kubernetes应用的部署和管理。我们Helm和Kubernetes的关系,我们可以理解成yum和CentOS,apt-get和Ubuntu的关系。 Helm由两部分组成,客户端helm和服务端tiller。 其中tiller运行在Kubernetes集群上,管理chart,而客户端helm就是一个命令行工具,可在本地运行,一般运行在持续集成/持续交付的服务器上 。 下图是helm的架构图。 我们现在就来试用下helm。 首先安装helm客户端。 下载helm执行文件的压缩包: wget -O helm.tar.gz https://storage.googleapis.com/kubernetes-helm/helm-v2.11.0-l

优秀的个人博客,低调大师

Java入门—多线程

线程是比进程还要小的运行单位,一个进程包含多个线程。 什么是多线程 线程的创建 两种方式: 创建一个Thread类,或者一个Thread子类的对象 创建一个实现Runnable接口的类的对象 Thread类: Thread是一个线程类,位于java.lang包下 image.png Thread类的常用方法 image.png Runnable接口 只有一个方法run() Runnable是java中用以实现线程的接口 任何实现线程功能的类都必须实现该接口 继承Thread类的方式创建线程类,重写run方法: package com.imooc.thread; class MyThread extends Thread{ public void run(){ System.out.println(getName()+"该线程正在执行!"); } } public class ThreadTest{ public static void main(String[] args){ System.out.println("主线程1"); MyThread mt = new MyThread(); mt.start(); System.out.println("主线程2"); } } Runnable创建线程: 为什么要实现Runnable接口? 因为: Java不支持多继承 当你不打算重写Thread类的其他方法 package com.imooc.thread; class PrintRunnable implements Runnable{ @Override public void run() { int i = 1; while (i<=5){ System.out.println(Thread.currentThread().getName()+"正在运行"); i++; } } } public class ThreadTest{ public static void main(String[] args){ PrintRunnable pr = new PrintRunnable(); Thread t1 = new Thread(pr); t1.start(); Thread t2 = new Thread(pr); t2.start(); } } 线程的状态和生命周期 线程的状态 新建 可运行 正在运行 阻塞 终止 线程的生命周期 image.png sleep方法:在指定的毫秒数内,让线程休眠。(暂停执行) public static void sleep(long millis) 参数单位:毫秒 示例: class MyThread implements Runnable{ @Override public void run() { for(int i = 0; i<10;i++){ System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args){ MyThread m = new MyThread(); Thread t = new Thread(m); t.start(); } } 调用sleep时需要捕获异常。 join()方法:优先执行线程。(抢占资源) public final void join() join方法的参数为毫秒,代表:超过此时间交出资源使用权。 示例: class MyThread implements Runnable{ @Override public void run() { for(int i = 0; i<10;i++){ System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Test { public static void main(String[] args){ MyThread m = new MyThread(); Thread t = new Thread(m); t.start(); try { t.join(1000); } catch (InterruptedException e) { e.printStackTrace(); } for(int j = 0; j<10;j++){ System.out.println("主线程"); } } } 线程的调度 线程优先级 Java为线程类提供了10个优先级 优先级可以用整数1-10表示,超出范围会抛出异常 主线程默认优先级为5 优先级常量 MAX_PRIORITY : 最高优先级(10) MIN_PRIORITY : 最低优先级(1) NORM_PRIORITY :默认优先级(5) image.png 测试代码: class MyThread implements Runnable{ private String name; public MyThread(String name){ this.name = name; } @Override public void run() { for(int i = 0; i<10;i++){ System.out.println(Thread.currentThread().getName()+i+name); } } } public class Test { public static void main(String[] args){ int i = Thread.currentThread().getPriority(); MyThread m = new MyThread("Allen"); Thread t = new Thread(m); MyThread m1 = new MyThread("ming"); Thread t1 = new Thread(m1); t.setPriority(Thread.MAX_PRIORITY); t1.setPriority(Thread.MIN_PRIORITY); int y = t.getPriority(); int y1 = t1.getPriority(); System.out.println("Allen线程优先级"+y); System.out.println("ming线程优先级"+y1); t.start(); t1.start(); } } 线程同步 多线程运行时的问题: 各个线程是通过竞争CPU时间而获得运行机会的 各个线程什么时候得到CPU时间、占用多久,是不可预测的 一个正在运行着的线程在什么地方被暂停是不确定的 使用synchronized关键字加锁。同一时刻只能单个操作。 synchronized关键字可以用在:成员方法、静态方法、语句块 public synchronized void F(){} public static synchronized void F(){} synchronized (){} 线程间通信 Java中的线程通信和Python中的差不多,通过中间队列类存储,由消费者和生产者负责放入和取出。 image.png wait方法:中断方法的执行,使线程等待 notify方法:唤醒处于等待的某一个线程 notifyAll方法:唤醒处于等待的全部线程 小结 创建线程的两种方式: 继承Thread类创建线程 new Thread().start() 实现Runnable接口创建线程 new Thread(new MyRunable()).start() 线程的5种状态: 新建 可运行 运行 阻塞 终止 sleep、join、wait、notify、notifyAll方法: 线程优先级: 最高、最低。 获取优先级、设置优先级。 先启动的线程尽管优先级低,也会可能先执行。 线程同步: synchronized关键字,可以用在:成员方法、静态方法、语句块。 上述文章如有帮助记得点赞呦,关注后第一时间获得最新笔记,持续更新~

优秀的个人博客,低调大师

docker 摘要(入门版)

Docker 安装 macOS或者windows 下载boot2docker工具 CentOS yum install docker-io -y systemctl start docker docker摘要 docker虚拟化 只虚拟User space 一台机器可以运行20-50个container 启动速度快 对内核有要求(不能像VMware一样可以安装mac,windows和linux) 用于大数据,分布式和集群 container实现 cgroup(内核中对资源的限制机制,通过控制进程(一个container实例)来限制) namespace(每一个容器都是有一个自己网络进程的独立的虚拟环境,实现容器间的隔离 chroot(文件系统的隔离,有一个真实的物理文件系统(物理机上),其他的都是虚拟的文件系统(虚拟的文件系统在内存中)) 以上,cgroup,namespace,chroot都是在linux中,也就是直接调用linux,后来为了跨平台,将上面的封装成了libcontainer库,让docer依赖于它 pid,容器有自己独立的进程表和1号进程 net,容器有自己的network info ipc,container之间的通信 mnt,每个容器都有自己唯一的目录挂载,是真实的挂载目录的子目录 utc,有独立的hostname和domain 层次 server -> hostOS -> docker engine -> app1 使用aufs实现分层的文件系统管理 Image是一种文件系统,里面必须有操作系统或者软件,docker中的系统Image很小,因为只包含很好的最核心的部分,每一个那么多的外围 只读为Image,可写为container,image是java中的class,container是对象 Image类似一个单链表系统,每一个image包含一个指向parent image的指针 每一个parent image的image是base image 层次: debain image(base image) -> apache image -> container 一般base image是操作系统镜像 建议:一个container干一件事情 dockerfile(类似于makefile,是命令的集合) 描述一个Image的层次,就提描述了一个依赖次序,centos -> apache -> container 有了它可以完美的重现出开发环境,方便了测试人员 docker hub(镜像仓库,类似于github) C/S运行 client request -> server -> http server -> router -> job -> driver 使用 docker pull {image name} docker images --no-trunc docker run [OPTIONS] IMAGE[:TAG] [CMD][ARG...] -name -i: iteract -t: tag -d: detach docker ps 不加上-a选项显示的正在运行的容器 加上-a显示所有的容器,包括停止的 docker inspect [ID] docker logs docker build docker rm/kill/stop id, 关闭容器 批量操作: docker rm/kill/stop $(docker ps -a -q) docker run -t -i centos /bin/bash, -t表示是伪tty(就是给一个终端,如果每一个则容器一start就stop了),-i表示iteract(在有了终端这个前提之下,如果attach到容器中,可以执行交互式的命令,如果没有则什么都干不了),-d就是在后台运行,不会在执行了上面这个命令之后直接进入到容器中 进入到了交互之界面,使用exit退出,但是也会stop容器,如果要在一次进入到该容器,需要先start容器,则attach容器,因为-i会前台运行,使用-d则是相反的 docker cp id:path hostpath 自己的摘要 docker在某些方面和git是一样的,不如说对于image镜像都是通过版本控制的,我们从dockeruhb中pull下来镜像文件,如果我们将其启动为一个容器,如果我们在这个容器中进行了修改,docker会通过文件将其修改记录下来,我们可将当当前的容器commit成一个新的image,这就类似于git中的一个commit点 在确定一个容器的时候是使用container id 在确定一个image的时候,是通过name:tag,如果只有name则会选中所有的tag,tag表示一个版本 生成一个image(提交点)的方法有两种,docker commit 和 docker build,在docker build中就是在一个image中执行一个命令的集合在提交成一个新的image

优秀的个人博客,低调大师

MySQL 使用入门 (全)

数据库简介 1.数据库概述 ①数据库:是按照某种数据结构对数据进行组织,存储和管理的容器,简单的说就是用来存储和管理数据的容器 ②数据库系统:是指在计算机中引入数据库后的系统,一般由数据库,数据库管理系统,应用程序和数据库管理员组成 ③数据库管理系统:是一个管理,控制数据库容器中各种数据库对象的系统软件 2.结构化查询语言SQL ①结构化查询语言:是一种用来与数据库通信的语言,其利用一些简单的句子构成基本的语法来存取数据库的内容,便于用户从数据库中获得和操作所需数据 ②SQL语言特点:非过程化语言,统一的语言,关系数据库的公共语言 ③SQL语言的组成:数据库定义语言(DDL),数据库操纵语言(DML),数据控制语言(DCL) 3.数据库设计基本步骤 ①需求分析阶段 ②概念结构设计阶段 ③逻辑结构设计阶段 ④数据库的物理结构设计阶段 ⑤数据库的实施阶段 ⑥数据库运行与维护阶段 4.MySQL数据库 它是一种关系型数据库管理系统,关系型数据库将数据保存在不同的表中,而不是将所有数据存放在一个大仓库,提高了速度和灵活性 特点: 体积小,运行速度快,成本低,开放源码 单进程,多线程架构,属于轻量级的数据库 5.MySQL体系结构 包括连接池组件,管理服务和工具组件,SQL接口组件,查询分析组件,优化器组件,缓存组件,插件式存储引擎以及物理组件 6.数据库系统解决的问题:持久化存储,优化读写,保证数据的有效性 7.数据库的分类 ①文档型,如sqlite,就是一个文件,通过对文件的复制完成数据库的复制 ②服务型,如mysql、postgresql,数据存储在一个物理文件中,但是需要使用终端以tcp/ip协议连接,进行数据库的读写操作 8.数据库的操作 ①数据库的操作,包括创建、删除 ②表的操作,包括创建、修改、删除 ③数据的操作,包括增加、修改、删除、查询,简称crud 10.常用的可视化操作工具:MySQL-Front,MySQL Workbench及Navicat for MySQL 11. SQL的基本书写规则 ①关键字大写 ②表名的首字大写 ③其余(列名)小写 E-R模型 1.E表示entry,实体;R表示relationship,关系当前物理的数据库都是按照E-R模型进行设计的 2.关系描述两个实体之间的对应规则,包括: ①一对一 ②一对多 ③多对多 3.三范式 ①第一范式(1NF):列不可拆分 ②第二范式(2NF):唯一标识 ③第三范式(3NF):引用主键 说明:后一个范式,都是在前一个范式的基础上建立的 4.数据完整性 一个数据库就是一个完整的业务单元,可以包含多张表,数据被存储在表中,在表中为了更加准确的存储数据,保证数据的正确有效,可以在创建表的时候,为表添加一些强制性的验证,包括数据字段的类型、约束 5.字段类型 ①数字:int,decimal:如decimal(5,2),表示数字长度不超过5位,小数不超过2位 ②字符串:char,varchar,text:其中char和varchar都是有限字符的,而text是不确定的。char和varchar都是8个字符,但char如果不够会补充空格,varchar是不会补充空格的 ③日期:datetime ④布尔:bit 6.约束 ①主键primary key ②非空not null ③惟一unique ④默认default ⑤外键foreign key ⑥auto_increment表示自动增长 7. 数据库的选型 ①开发人员的熟练程度,费用,数据规模,性能要求,集群能力等,也可参考数据库管理员的建议 ②对于有1:1关系的两个表,为两个表设置相同的主键列 ③对于有1:N关系的两个表,在N表中添加一个外键列,该列与1表的主键列向关联 ④对于M:N关系,生成一个单独的表表示该关系,该关系的列由两个表的主键组成 命令脚本命令 1.远程连接 一般在公司开发中,可能会将数据库统一搭建在一台服务器上,所有开发人员共用一个数据库,而不是在自己的电脑中配置一个数据库 运行命令: mysql-hip地址-uroot-p -h后面写要连接的主机ip地址 -u后面写连接的用户名 -p回车后写密码 2.数据库操作 ①创建数据库 createdatabase数据库名charset=utf8; ②删除数据库 dropdatabase数据库名; ③切换数据库 use数据库名; ④查看当前选择的数据库 selectdatabase(); 3.表操作 ①查看当前数据库中所有表 showtables; ②创建表 createtable表名(列及类型); ③修改表 增,改,删:altertable表名add|change|drop列名类型; 增加约束条件:altertable表名addconstraint约束名,类型(字段名); 删除约束条件:altertable表名dropprimarykey; 添加外键约束:altertable表名add约束名constraintforeignkey(字段名)references父表名(字段名); 修改存储引擎:altertable表名engine=新的存储引擎;(MyISAM,InooDB) 修改默认字符集:altertable表名defaultcharset=新的字符集; ④删除表 droptable表名; ⑤查看表结构 desc表名; ⑥更改表名称 renametable原表名to新表名; ⑦查看表的创建语句 showcreatetable表名; 4.数据操作 ①查询 selectdistinct列名fromstudents;消除重复 select*from表名where条件; ②增加 全列插入:insertinto表名values(...) 缺省插入:insertinto表名(列1,...)values(值1,...) 同时插入多条数据:insertinto表名values(...),(...)...; 或insertinto表名(列1,...)values(值1,...),(值1,...)...; 主键列是自动增长,但是在全列插入时需要占位,通常使用0,插入成功后以实际数据为准 ③修改 update表名set列1=值1,...where条件 ④删除 deletefrom表名where条件 ⑤逻辑删除,本质就是修改操作update altertablestudentsaddisdeletebitdefault0; ⑥如果需要删除则 updatestudentsisdelete=1where...; 5.备份与恢复 数据备份 ①进入超级管理员 sudo-s ②进入mysql库目录 cd/var/lib/mysql ③运行mysqldump命令 mysqldump–uroot–p数据库名>~/Desktop/备份文件.sql; ④按提示输入mysql的密码 数据恢复 ①连接mysql,创建数据库 ②退出连接,执行如下命令 mysql-uroot–p数据库名<~/Desktop/备份文件.sql ③根据提示输入mysql密码 查询(条件) 1.比较运算符 select*fromstudentswhereid>3; 2.逻辑运算符 select*fromstudentswhereid>3andgender=0; 3.模糊查询 Like:select*fromstudentswheresnamelike'黄_'; ①%表示任意多个任意字符 ②_表示一个任意字符 4.范围查询 ①in表示在一个非连续的范围内 select*fromstudentswhereidin(1,3,8); ②between ... and ...表示在一个连续的范围内 select*fromstudentswhereidbetween3and8; 5.空判断 ①判空is null select*fromstudentswherehometownisnull; ②判非空is not null select*fromstudentswherehometownisnull; 6.优先级 ①小括号,not,比较运算符,逻辑运算符 ②and比or先运算,如果同时出现并希望先算or,需要结合()使用 查询(聚合) 1.count(*)表示计算总行数,括号中写星与列名,结果是相同的 selectcount(*)fromstudents; 2.max(列)表示求此列的最大值 selectmax(id)fromstudentswheregender=0; 3.min(列)表示求此列的最小值 selectmin(id)fromstudentswhereisdelete=0; 4.sum(列)表示求此列的和 selectsum(id)fromstudentswheregender=1; 5.avg(列)表示求此列的平均值 selectavg(id)fromstudentswhereisdelete=0andgender=0; 查询(分组) 1.语法: select列1,列2,聚合...from表名groupby列1,列2,列3... 分组后的数据筛选 2.语法: select列1,列2,聚合...from表名 groupby列1,列2,列3... having列1,...聚合... having后面的条件运算符与where的相同,不同的是having对分组的结果集进行筛选,where对原始集筛选 3.与聚合函数和Group by子句有关的常见错误 在select子句中书写了多余的列 selectproduct_id,purchase_price,count(*)fromProductGroupbypurchase_price; 在Group by子句中写了列的别名 selectproduct_typeaspt,count(*)FromProductGroupbypt; ①Group by的子句是随机的,不能人为排序 ②不能在where子句中使用聚合函数 ③只有select子句和having子句以及(order by)中才能够使用count等聚合函数 查询(排序) 语法: select*from表名 orderby列1asc|desc,列2asc|desc,... 将行数据按照列1进行排序,如果某些行列1的值相同时,则按照列2排序,以此类推默认按照列值从小到大排列,asc从小到大排列,即升序,desc从大到小排序,即降序查询(分页) 语法: select*from表名 limitstart,count 查询(总结) 1.完整的select语句 selectdistinct* from表名 where.... groupby...having... orderby... limitstar,count From字句是否真的有必要? 并不是,只使用select子句也是可以的,如select(100+300)*3 as calculation,但在oracle中不允许 2.子查询 ①子查询就是一次性视图.与视图不同,子查询在select语句执行完毕之后就会消失 ②标量子查询:标量就是单一的意思,标量子查询有一个特殊的限制,必须而且只能返回1行1列的结果 ③关联子查询:在子查询中添加where子句条件,where p1.product_type=p2.product_type 关系 1.外键 为stuid添加外键约束 altertablescoresaddconstraintstu_scoforeignkey(stuid)referencesstudents(id); 2.外键的级联操作 ①在删除students表的数据时,如果这个id值在scores中已经存在,则会抛异常 ②推荐使用逻辑删除,还可以解决这个问题 ③可以创建表时指定级联操作,也可以在创建表后再修改外键的级联操作 语法: altertablescoresaddconstraintstu_scoforeignkey(stuid)referencesstudents(id)ondeletecascade; 级联操作的类型包括: ①restrict(限制):默认值,抛异常 ②cascade(级联):如果主表的记录删掉,则从表中相关联的记录都将被删除 ③set null:将外键设置为空 ④no action:什么都不做 连接查询 1.连接查询分类 ①表A inner join 表B:表A与表B匹配的行会出现在结果中 ②表A left join 表B:表A与表B匹配的行会出现在结果中,外加表A中独有的数据,未对应的数据使用null填充 ③表A right join 表B:表A与表B匹配的行会出现在结果中,外加表B中独有的数据,未对应的数据使用null填充 ④查询学生的姓名、平均分 selectstudents.sname,avg(scores.score) fromscores innerjoinstudentsonscores.stuid=students.id groupbystudents.sname; 2.自关联 创建areas表的语句如下: createtableareas( idintprimarykey, atitlevarchar(20), pidint, foreignkey(pid)referencesareas(id) ); 视图 1.视图:从SQL的角度来看,视图和表是相同的两者的区别在于表中保存的是实际数据,而视图中保存的是select语句(视图本身并不存储数据) 定义视图: createviewstuscoreas selectstudents.*,scores.scorefromscores innerjoinstudentsonscores.stuid=students.id; 2.视图的用途就是查询 select*fromstuscore; 3.视图的优点: 视图无需保存数据,因此可以节省存储设备的容量 可以将频繁使用的select语句保存成视图,这样就不用每次都重写了 4.视图的限制: ①定义视图时不能使用order by子句,这是因为视图和表一样数据行都是没有顺序的 ②对视图进行更新(未被汇总得到的视图) ③条件: select子句未使用Dintinct From子句中只有一张表 未使用group by子句 未使用having子句 ④删除视图(多重视图) dropview视图名cascade 事务 1.事务:是恢复和并发控制的基本单位,什么是事务,就是需要在一个处理单元中执行的一系列更新处理的集合,通过使用事务,可以对数据库中的数据更新处理提交和取消进行<beigin,commit,rollback>视图本质就是对查询的一个封装 2.使用事务的请况:当数据被更改时,包括insert,update,delete,使用事务可以完成退回的功能,保证业务逻辑的正确性 3.事务四大特性(简称ACID) ①原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行 ②一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致 ③隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明 ④持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障 ⑤要求:表的类型必须是innodb或bdb类型,才可以对此表使用事务 ⑥事务语句: begin;//开启 commit;//提交 rollback;//回滚 索引 1.单列索引和组合索引 ①单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引 ②组合索引,即一个索包含多个列 2.操作 ①查看索引: showindexfromtable_name; ②创建索引: createindexindexNameonmytable(usrname(length)); ③删除索引: dropindex[indexName]onmytable; 3.缺点 ①虽然索引提高了查询速度,同时却会降低更新表的速度,如对表进行insert,update和delete。 ②因为更新表时,mysql不仅要保存数据,还要保存一下索引文件 ③建立索引会占用磁盘空间的索引文件 4.检测运行时间 ①开启运行时间检测: setprofiling=1; ②执行查询语句: ③查看执行的时间: showprofiles; 谓词 1.就是返回值为真值的函数 like,in,not in,between,is null,is not null 函数 1.根据用途大致分为以下 ①算术函数(用来进行数值计算的函数) ②字符串函数(用来进行字符串操作的函数) ③日期函数(用来进行日期操作的函数) ④转换函数(用来转换数值类型和值的函数) ⑤聚合函数(用来进行数据聚合的函数) 2.case表达式 case表达式分为简单case表达式和搜索case表达式两种 搜索case表达式包含简单case表达式的全部功能 case搜索表达式; casewhen<求值表达式>then<表达式> when<求值表达式>then<表达式> .... else<表达式> end case简单表达式: case<表达式> when<表达式>then<表达式> when<表达式>then<表达式> when<表达式>then<表达式> ..... else<表达式> end 集合运算 1.集合运算:集合在数据库领域表示记录的集合,具体来说表,视图和查询执行的结果都是记录的集合 2.集合运算注意事项 ①作为运算对象的记录的列数必须相同 ②作为运算对象的记录中列的类型必须一致 ③可以使用任何select语句,但order by子句只能在最后使用一次 3.表的加法-union并集 selectproduct-id,product_namefromProductunion(all)selectproduct_id,product_namefromproduct2; 4.选取表中的公共部分-intersect交集 selectproduct_id,product_namefromProductintersectselectproduct_id,product_namefromProduct2orderbyproduct_id; 5.记录的减法-except差集 selectproduct_id,product_namefromProductexceptselectproduct_id,product_namefromProduct2orderbyproduct_id; 6.表的联结 联结join就是将其他表的列添加过来,进行添加列的集合运算,union是以行(纵向)为单位进行操作,而联结则是以列(横向)为单位进行的 与python交互 1.安装引入模块 ①安装mysql模块 sudoapt-getinstallpython-mysqldb,pymysql ②在文件中引入模块 importMysqldb 2.Connection对象 用于建立与数据库的连接 创建对象:调用connect()方法 conn=connect(参数列表) 参数host:连接的mysql主机,如果本机是'localhost' 参数port:连接的mysql主机的端口,默认是3306 参数db:数据库的名称 参数user:连接的用户名 参数password:连接的密码 参数charset:通信采用的编码方式,默认是'gb2312',要求与数据库创建时指定的编码一致 3.对象的方法 close()关闭连接 commit()事务所以需要提交才会生效 rollback()事务放弃之前的操作 cursor()返回Cursor对象用于执行sql语句并获得结果 4.Cursor对象 执行sql语句 创建对象:调用Connection对象的cursor()方法 cursor1=conn.cursor() 5.对象的方法 close()关闭 execute(operation[,parameters])执行语句,返回受影响的行数 fetchone()执行查询语句时,获取查询结果集的第一个行数据,返回一个元组 next()执行查询语句时,获取当前行的下一行 fetchall()执行查询时,获取结果集的所有行,一行构成一个元组,再将这些元组装入一个元组返回 scroll(value[,mode])将行指针移动到某个位置 mode表示移动的方式 mode的默认值为relative表示基于当前行移动到value,value为正则向下移动,value为负则向上移动 mode的值为absolute表示基于第一条数据的位置,第一条数据的位置为0 6.对象的属性 ①rowcount只读属性,表示最近一次execute()执行后受影响的行数 ②connection获得当前连接对象 7.sql语句参数化 sname=raw_input("请输入学生姓名:") params=[sname] count=cs1.execute('insertintostudents(sname)values(%s)',params)

优秀的个人博客,低调大师

EOS docker 部署 入门

1.首先准备一台内存大于8Gib的机器,安装docker 2.然后从docker hub上拉最新的eos镜像 开发镜像 docker pull eosio/eos-dev 正式镜像 docker pull eosio/eos 默认拉取的是last的tag,如有需要也可以更改其他tag docker pull eosio/eos:lastest 为了同时运行nodeos和keosd,我们需要从eos github上下载一个文件 https://github.com/EOSIO/eos/blob/master/Docker/docker-compose-latest.yml 检查文件内的镜像名称是否与你刚才pull下来的一样,如果不一样要改成一样的 version: "3" services: nodeosd: image: eosio/

优秀的个人博客,低调大师

python-模块入门

一、模块介绍 模块:模块就是一系列功能的集合体 模块有三种来源: 1.内置模块 2.第三方的模块 3.自定义模块 模块的格式: 1使用python编写的.py文件 2.已被编译为共享库或DLL的C或C++扩展 3.把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件称之为包) 4.使用C编写并链接到python解释器的内置模块 为何要用模块? 1.使用内置的或者第三方模块的好处是:拿来主义,可以极大提升开发效率 2.使用自定义模块的好处是:可以减少代码冗余(抽取我们自己程序中要公用的一些功能定义成模块,然后程序的各部分组件都去模块中调用共享功能) 注: 一定要区分开谁是执行文件,谁是被导入模块 二、import导入模块 首次导入模块: 1.会产生一个模块的名称空间 2.执行模块文件,将执行过程中产生的名字都放到模块的名称空间中 3.在当前执行文件的名称空间中拿到一个模块名,该名字指向模块的名称空间 后面的导入,都是直接引用第一次导入的成果,不会重新执行文件 spam.money 在执行文件中访问模块名称空间中名字的语法: 模块名.名字(注:模块名应该全部为小写) 指名道姓地跟spam要money 不会与当前执行文件中的名字冲突 为模块起别名 import xxx as x 总结import导入模块: 优点:指名道姓地向某一个名称空间要名字,不会与当前名称空间中的名字冲突 缺点:但凡应用模块中的名字都需要前缀,不够简洁 三、from...import...导入模块 首次导入模块: 1.会产生一个模块的名称空间 2.执行模块文件,将执行过程中产生的名字都放到模块的名称空间中 3.在当前执行文件的名称空间中拿到一个名字,该名字就是执行模块中对应的名字 总结from...import... 优点:使用时,无需再加前缀,更简洁 缺点:容易与当前名称空间中的名字冲突 补充: from xxx import * 导入全部 *代表从被导入模块中拿到所有名字(不推荐使用) 在导入模块时,如果未找到__all__中的成员,抛出attributeError # __all__ = ['money','read1'] 焚膏油以继晷,恒兀兀以穷年。

优秀的个人博客,低调大师

Kafka入门经典教程

一、基本概念 介绍 Kafka是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能,但具有自己独特的设计。 这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统术语: Kafka将消息以topic为单位进行归纳。 将向Kafka topic发布消息的程序成为producers. 将预订topics并消费消息的程序成为consumer. Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker. producers通过网络将消息发送到Kafka集群,集群向消费者提供消息,如下图所示: 客户端和服务端通过TCP协议通信。Kafka提供了Java客户端,并且对多种语言都提供了支持。 Topics 和Logs 先来看一下Kafka提供的一个抽象概念:topic. 一个topic是对一组消息的归纳。对每个topic,Kafka 对它的日志进行了分区,如下图所示: 每个分区都由一系列有序的、不可变的消息组成,这些消息被连续的追加到分区中。分区中的每个消息都有一个连续的序列号叫做offset,用来在分区中唯一的标识这个消息。 在一个可配置的时间段内,Kafka集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为2天,那么在一个消息被发布的两天时间内,它都是可以被消费的。之后它将被丢弃以释放空间。Kafka的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。 实际上每个consumer唯一需要维护的数据是消息在日志中的位置,也就是offset.这个offset有consumer来维护:一般情况下随着consumer不断的读取消息,这offset的值不断增加,但其实consumer可以以任意的顺序读取消息,比如它可以将offset设置成为一个旧的值来重读之前的消息。 以上特点的结合,使Kafka consumers非常的轻量级:它们可以在不对集群和其他consumer造成影响的情况下读取消息。你可以使用命令行来"tail"消息而不会对其他正在消费消息的consumer造成影响。 将日志分区可以达到以下目的:首先这使得每个日志的数量不会太大,可以在单个服务上保存。另外每个分区可以单独发布和消费,为并发操作topic提供了一种可能。 分布式 每个分区在Kafka集群的若干服务中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的。副本使Kafka具备了容错能力。 每个分区都由一个服务器作为“leader”,零或若干服务器作为“followers”,leader负责处理消息的读和写,followers则去复制leader.如果leader down了,followers中的一台则会自动成为leader。集群中的每个服务都会同时扮演两个角色:作为它所持有的一部分分区的leader,同时作为其他分区的followers,这样集群就会据有较好的负载均衡。 Producers Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。使用的更多的是第二种。 Consumers 发布消息通常有两种模式:队列模式(queuing)和发布-订阅模式(publish-subscribe)。队列模式中,consumers可以同时从服务端读取消息,每个消息只被其中一个consumer读到;发布-订阅模式中消息被广播到所有的consumer中。Consumers可以加入一个consumer 组,共同竞争一个topic,topic中的消息将被分发到组中的一个成员中。同一组中的consumer可以在不同的程序中,也可以在不同的机器上。如果所有的consumer都在一个组中,这就成为了传统的队列模式,在各consumer中实现负载均衡。如果所有的consumer都不在不同的组中,这就成为了发布-订阅模式,所有的消息都被分发到所有的consumer中。更常见的是,每个topic都有若干数量的consumer组,每个组都是一个逻辑上的“订阅者”,为了容错和更好的稳定性,每个组由若干consumer组成。这其实就是一个发布-订阅模式,只不过订阅者是个组而不是单个consumer。 由两个机器组成的集群拥有4个分区 (P0-P3) 2个consumer组. A组有两个consumerB组有4个 相比传统的消息系统,Kafka可以很好的保证有序性。 传统的队列在服务器上保存有序的消息,如果多个consumers同时从这个服务器消费消息,服务器就会以消息存储的顺序向consumer分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各consumer上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用“专用consumer”的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。 在这方面Kafka做的更好,通过分区的概念,Kafka可以在多个consumer组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个consumer组,这样一个分区就只被这个组的一个consumer消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个consumer组之间进行负载均衡。注意consumer组的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。 Kafka只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要topic中所有消息的有序性,那就只能让这个topic只有一个分区,当然也就只有一个consumer组消费它。 二、环境搭建 Step 1: 下载Kafka 点击下载最新的版本并解压. tar -xzf kafka_2.9.2-0.8.1.1.tgz cd kafka_2.9.2-0.8.1.1 复制代码 Step 2: 启动服务 Kafka用到了Zookeeper,所有首先启动Zookper,下面简单的启用一个单实例的Zookkeeper服务。可以在命令的结尾加个&符号,这样就可以启动后离开控制台。 bin/zookeeper-server-start.sh config/zookeeper.properties & [2013-04-22 15:01:37,495] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig) ... 复制代码 现在启动Kafka: bin/kafka-server-start.sh config/server.properties [2013-04-22 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties) [2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties) ... 复制代码 Step 3: 创建 topic 创建一个叫做“test”的topic,它只有一个分区,一个副本。 bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test 复制代码 可以通过list命令查看创建的topic: bin/kafka-topics.sh --list --zookeeper localhost:2181 test 复制代码 除了手动创建topic,还可以配置broker让它自动创建topic. Step 4:发送消息. Kafka 使用一个简单的命令行producer,从文件中或者从标准输入中读取消息并发送到服务端。默认的每条命令将发送一条消息。 运行producer并在控制台中输一些消息,这些消息将被发送到服务端: bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test This is a messageThis is another message 复制代码 ctrl+c可以退出发送。 Step 5: 启动consumer Kafka also has a command line consumer that will dump out messages to standard output. Kafka也有一个命令行consumer可以读取消息并输出到标准输出: bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning This is a message This is another message 复制代码 你在一个终端中运行consumer命令行,另一个终端中运行producer命令行,就可以在一个终端输入消息,另一个终端读取消息。 这两个命令都有自己的可选参数,可以在运行的时候不加任何参数可以看到帮助信息。 Step 6: 搭建一个多个broker的集群 刚才只是启动了单个broker,现在启动有3个broker组成的集群,这些broker节点也都是在本机上的: 首先为每个节点编写配置文件: cp config/server.properties config/server-1.properties cp config/server.properties config/server-2.properties 复制代码 在拷贝出的新文件中添加以下参数: config/server-1.properties: broker.id=1 port=9093 log.dir=/tmp/kafka-logs-1 复制代码 config/server-2.properties: broker.id=2 port=9094 log.dir=/tmp/kafka-logs-2 复制代码 broker.id在集群中唯一的标注一个节点,因为在同一个机器上,所以必须制定不同的端口和日志文件,避免数据被覆盖。 We already have Zookeeper and our single node started, so we just need to start the two new nodes: 刚才已经启动可Zookeeper和一个节点,现在启动另外两个节点: bin/kafka-server-start.sh config/server-1.properties & ... bin/kafka-server-start.sh config/server-2.properties & ... 复制代码 创建一个拥有3个副本的topic: bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic 复制代码 现在我们搭建了一个集群,怎么知道每个节点的信息呢?运行“"describe topics”命令就可以了: bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic 复制代码 Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs: Topic: my-replicated-topic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0 复制代码 下面解释一下这些输出。第一行是对所有分区的一个描述,然后每个分区都会对应一行,因为我们只有一个分区所以下面就只加了一行。 leader:负责处理消息的读和写,leader是从所有节点中随机选择的. replicas:列出了所有的副本节点,不管节点是否在服务中. isr:是正在服务中的节点. 在我们的例子中,节点1是作为leader运行。 向topic发送消息: bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic 复制代码 ... my test message 1my test message 2^C 复制代码 消费这些消息: bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic ... my test message 1 my test message 2 ^C 测试一下容错能力.Broker 1作为leader运行,现在我们kill掉它: ps | grep server-1.properties7564 ttys002 0:15.91 /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java... kill -9 7564 复制代码 另外一个节点被选做了leader,node 1 不再出现在 in-sync 副本列表中: bin/kafka-topics.sh --describe --zookeeper localhost:218192 --topic my-replicated-topic Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs: Topic: my-replicated-topic Partition: 0 Leader: 2 Replicas: 1,2,0 Isr: 2,0 复制代码 虽然最初负责续写消息的leader down掉了,但之前的消息还是可以消费的: bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic ... my test message 1 my test message 2 复制代码 看来Kafka的容错机制还是不错的。 三、搭建Kafka开发环境 我们搭建了kafka的服务器,并可以使用Kafka的命令行工具创建topic,发送和接收消息。下面我们来搭建kafka的开发环境。 添加依赖 搭建开发环境需要引入kafka的jar包,一种方式是将Kafka安装包中lib下的jar包加入到项目的classpath中,这种比较简单了。不过我们使用另一种更加流行的方式:使用maven管理jar包依赖。 创建好maven项目后,在pom.xml中添加以下依赖: org.apache.kafka kafka_2.10 0.8.0 复制代码 添加依赖后你会发现有两个jar包的依赖找不到。没关系我都帮你想好了,点击这里下载这两个jar包,解压后你有两种选择,第一种是使用mvn的install命令将jar包安装到本地仓库,另一种是直接将解压后的文件夹拷贝到mvn本地仓库的com文件夹下,比如我的本地仓库是d:mvn,完成后我的目录结构是这样的: 配置程序 首先是一个充当配置文件作用的接口,配置了Kafka的各种连接参数: package com.sohu.kafkademon; public interface KafkaProperties { final static String zkConnect = "10.22.10.139:2181"; final static String groupId = "group1"; final static String topic = "topic1"; final static String kafkaServerURL = "10.22.10.139"; final static int kafkaServerPort = 9092; final static int kafkaProducerBufferSize = 64 * 1024; final static int connectionTimeOut = 20000; final static int reconnectInterval = 10000; final static String topic2 = "topic2"; final static String topic3 = "topic3"; final static String clientId = "SimpleConsumerDemoClient"; } 复制代码 producer package com.sohu.kafkademon; import java.util.Properties; import kafka.producer.KeyedMessage; import kafka.producer.ProducerConfig; /** @author leicui bourne_cui@163.com */ public class KafkaProducer extends Thread { private final kafka.javaapi.producer.Producer producer; private final String topic; private final Properties props = new Properties(); public KafkaProducer(String topic) { props.put("serializer.class", "kafka.serializer.StringEncoder"); props.put("metadata.broker.list", "10.22.10.139:9092"); producer = new kafka.javaapi.producer.Producer(new ProducerConfig(props)); this.topic = topic; } @Override public void run() { int messageNo = 1; while (true) { String messageStr = new String("Message_" + messageNo); System.out.println("Send:" + messageStr); producer.send(new KeyedMessage(topic, messageStr)); messageNo++; try { sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 复制代码 consumer package com.sohu.kafkademon; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import kafka.consumer.ConsumerConfig; import kafka.consumer.ConsumerIterator; import kafka.consumer.KafkaStream; import kafka.javaapi.consumer.ConsumerConnector; /** @author leicui bourne_cui@163.com */ public class KafkaConsumer extends Thread { private final ConsumerConnector consumer; private final String topic; public KafkaConsumer(String topic) { consumer = kafka.consumer.Consumer.createJavaConsumerConnector( createConsumerConfig()); this.topic = topic; } private static ConsumerConfig createConsumerConfig() { Properties props = new Properties(); props.put("zookeeper.connect", KafkaProperties.zkConnect); props.put("group.id", KafkaProperties.groupId); props.put("zookeeper.session.timeout.ms", "40000"); props.put("zookeeper.sync.time.ms", "200"); props.put("auto.commit.interval.ms", "1000"); return new ConsumerConfig(props); } @Override public void run() { Map topicCountMap = new HashMap(); topicCountMap.put(topic, new Integer(1)); Map>> consumerMap = consumer.createMessageStreams(topicCountMap); KafkaStream stream = consumerMap.get(topic).get(0); ConsumerIterator it = stream.iterator(); while (it.hasNext()) { System.out.println("receive:" + new String(it.next().message())); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 复制代码 简单的发送接收 运行下面这个程序,就可以进行简单的发送接收消息了: package com.sohu.kafkademon; /** @author leicui bourne_cui@163.com */ public class KafkaConsumerProducerDemo { public static void main(String[] args) { KafkaProducer producerThread = new KafkaProducer(KafkaProperties.topic); producerThread.start(); KafkaConsumer consumerThread = new KafkaConsumer(KafkaProperties.topic); consumerThread.start(); } } 复制代码 高级别的consumer 下面是比较负载的发送接收的程序: package com.sohu.kafkademon; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import kafka.consumer.ConsumerConfig; import kafka.consumer.ConsumerIterator; import kafka.consumer.KafkaStream; import kafka.javaapi.consumer.ConsumerConnector; /** @author leicui bourne_cui@163.com */ public class KafkaConsumer extends Thread { private final ConsumerConnector consumer; private final String topic; public KafkaConsumer(String topic) { consumer = kafka.consumer.Consumer.createJavaConsumerConnector( createConsumerConfig()); this.topic = topic; } private static ConsumerConfig createConsumerConfig() { Properties props = new Properties(); props.put("zookeeper.connect", KafkaProperties.zkConnect); props.put("group.id", KafkaProperties.groupId); props.put("zookeeper.session.timeout.ms", "40000"); props.put("zookeeper.sync.time.ms", "200"); props.put("auto.commit.interval.ms", "1000"); return new ConsumerConfig(props); } @Override public void run() { Map topicCountMap = new HashMap(); topicCountMap.put(topic, new Integer(1)); Map>> consumerMap = consumer.createMessageStreams(topicCountMap); KafkaStream stream = consumerMap.get(topic).get(0); ConsumerIterator it = stream.iterator(); while (it.hasNext()) { System.out.println("receive:" + new String(it.next().message())); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 四、数据持久化 不要畏惧文件系统! Kafka大量依赖文件系统去存储和缓存消息。对于硬盘有个传统的观念是硬盘总是很慢,这使很多人怀疑基于文件系统的架构能否提供优异的性能。实际上硬盘的快慢完全取决于使用它的方式。设计良好的硬盘架构可以和内存一样快。 在6块7200转的SATA RAID-5磁盘阵列的线性写速度差不多是600MB/s,但是随即写的速度却是100k/s,差了差不多6000倍。现代的操作系统都对次做了大量的优化,使用了 read-ahead 和 write-behind的技巧,读取的时候成块的预读取数据,写的时候将各种微小琐碎的逻辑写入组织合并成一次较大的物理写入。对此的深入讨论可以查看这里,它们发现线性的访问磁盘,很多时候比随机的内存访问快得多。 为了提高性能,现代操作系统往往使用内存作为磁盘的缓存,现代操作系统乐于把所有空闲内存用作磁盘缓存,虽然这可能在缓存回收和重新分配时牺牲一些性能。所有的磁盘读写操作都会经过这个缓存,这不太可能被绕开除非直接使用I/O。所以虽然每个程序都在自己的线程里只缓存了一份数据,但在操作系统的缓存里还有一份,这等于存了两份数据。 另外再来讨论一下JVM,以下两个事实是众所周知的: •Java对象占用空间是非常大的,差不多是要存储的数据的两倍甚至更高。 •随着堆中数据量的增加,垃圾回收回变的越来越困难。 基于以上分析,如果把数据缓存在内存里,因为需要存储两份,不得不使用两倍的内存空间,Kafka基于JVM,又不得不将空间再次加倍,再加上要避免GC带来的性能影响,在一个32G内存的机器上,不得不使用到28-30G的内存空间。并且当系统重启的时候,又必须要将数据刷到内存中( 10GB 内存差不多要用10分钟),就算使用冷刷新(不是一次性刷进内存,而是在使用数据的时候没有就刷到内存)也会导致最初的时候新能非常慢。但是使用文件系统,即使系统重启了,也不需要刷新数据。使用文件系统也简化了维护数据一致性的逻辑。 所以与传统的将数据缓存在内存中然后刷到硬盘的设计不同,Kafka直接将数据写到了文件系统的日志中。 常量时间的操作效率 在大多数的消息系统中,数据持久化的机制往往是为每个cosumer提供一个B树或者其他的随机读写的数据结构。B树当然是很棒的,但是也带了一些代价:比如B树的复杂度是O(log N),O(log N)通常被认为就是常量复杂度了,但对于硬盘操作来说并非如此。磁盘进行一次搜索需要10ms,每个硬盘在同一时间只能进行一次搜索,这样并发处理就成了问题。虽然存储系统使用缓存进行了大量优化,但是对于树结构的性能的观察结果却表明,它的性能往往随着数据的增长而线性下降,数据增长一倍,速度就会降低一倍。 直观的讲,对于主要用于日志处理的消息系统,数据的持久化可以简单的通过将数据追加到文件中实现,读的时候从文件中读就好了。这样做的好处是读和写都是 O(1) 的,并且读操作不会阻塞写操作和其他操作。这样带来的性能优势是很明显的,因为性能和数据的大小没有关系了。 既然可以使用几乎没有容量限制(相对于内存来说)的硬盘空间建立消息系统,就可以在没有性能损失的情况下提供一些一般消息系统不具备的特性。比如,一般的消息系统都是在消息被消费后立即删除,Kafka却可以将消息保存一段时间(比如一星期),这给consumer提供了很好的机动性和灵活性,这点在今后的文章中会有详述。 五、消息传输的事务定义 之前讨论了consumer和producer是怎么工作的,现在来讨论一下数据传输方面。数据传输的事务定义通常有以下三种级别: 最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输。 最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输. 精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被一次而且仅仅被传输一次,这是大家所期望的。 大多数消息系统声称可以做到“精确的一次”,但是仔细阅读它们的的文档可以看到里面存在误导,比如没有说明当consumer或producer失败时怎么样,或者当有多个consumer并行时怎么样,或写入硬盘的数据丢失时又会怎么样。kafka的做法要更先进一些。当发布消息时,Kafka有一个“committed”的概念,一旦消息被提交了,只要消息被写入的分区的所在的副本broker是活动的,数据就不会丢失。关于副本的活动的概念,下节文档会讨论。现在假设broker是不会down的。 如果producer发布消息时发生了网络错误,但又不确定实在提交之前发生的还是提交之后发生的,这种情况虽然不常见,但是必须考虑进去,现在Kafka版本还没有解决这个问题,将来的版本正在努力尝试解决。 并不是所有的情况都需要“精确的一次”这样高的级别,Kafka允许producer灵活的指定级别。比如producer可以指定必须等待消息被提交的通知,或者完全的异步发送消息而不等待任何通知,或者仅仅等待leader声明它拿到了消息(followers没有必要)。 现在从consumer的方面考虑这个问题,所有的副本都有相同的日志文件和相同的offset,consumer维护自己消费的消息的offset,如果consumer不会崩溃当然可以在内存中保存这个值,当然谁也不能保证这点。如果consumer崩溃了,会有另外一个consumer接着消费消息,它需要从一个合适的offset继续处理。这种情况下可以有以下选择: consumer可以先读取消息,然后将offset写入日志文件中,然后再处理消息。这存在一种可能就是在存储offset后还没处理消息就crash了,新的consumer继续从这个offset处理,那么就会有些消息永远不会被处理,这就是上面说的“最多一次”。 consumer可以先读取消息,处理消息,最后记录offset,当然如果在记录offset之前就crash了,新的consumer会重复的消费一些消息,这就是上面说的“最少一次”。 “精确一次”可以通过将提交分为两个阶段来解决:保存了offset后提交一次,消息处理成功之后再提交一次。但是还有个更简单的做法:将消息的offset和消息被处理后的结果保存在一起。比如用Hadoop ETL处理消息时,将处理后的结果和offset同时保存在HDFS中,这样就能保证消息和offser同时被处理了。

优秀的个人博客,低调大师

大话RabbitMQ 基础入门

----------写在前面---------- 近些年微服务越来越火,让我也忍不住想去一窥微服务究竟,讲到微服务,就离不开分布式,而分布式,也离不开消息队列,在消息队列中,RabbitMQ可以说是比较具有代表性的一款。 这里是一篇介绍消息队列以及各种消息队列产品对比的文章,讲得很好,有兴趣的可以看一看。 https://cloud.tencent.com/developer/article/1006035 在讲RabbitMQ之前,首先需要在电脑上安装和配置RabbitMQ,网络上已经有很多这类文章,如果懒得去搜索,可以看看这篇介绍如何安装配置RabbitMQ的文章。 https://blog.csdn.net/weixin_39735923/article/details/79288578 其中,在安装RabbitMQ的过程中,遇到了一个坑,在启用RabbltMQ的管理界面执行 rabbitmq-plugins enable rabbitmq_management 命令时,出现了以下这样的报错 可以在该指令前加上 .\ 即 .\rabbitmq-plugins enable rabbitmq_management 祝安装顺利 !! -------正文------ 基本概念 下面是在.Net中使用RabbitMQ要明白的一些名词概念。 综上所诉,他们之间的关系可以用我下面的 丑图 表示。 在图中,没有吧Routing key画出。Producer每一次发送消息,除了发出消息本身,还会随着消息带上一个routingKey,而且每一次将Exchange和Queue绑定,大体需要三个参数, string queueName, string exchangeName, string routingKey 其中也有一个routingKey,但此RoutingKey非彼Routingkey。 大白话 对这个过程,我们可以理解为国家给灾区发送救灾物资,国家给当地政府划拨物资的时候,会规定,谁才能拿到这批物资,如(房子倒了的.家里有人受伤了的.家庭经济困难的)。 而当地政府在分配这批物资之前,为了方便物资的分配,会给每个家庭贴上一个标签,如 家庭A 经济困难 家庭B 房子倒了.经济困难 家庭C 家庭富有.房子倒了 家庭D 房子倒了的.家里有人受伤了的.家庭经济困难的 所以,发送消息时候的routingKey就是国家规定的那批物质分配规则。 而Exchange和Queue绑定时的RoutingKey可以理解为当地政府给每个家庭贴上的一个标签。 Exchange(交换机)转发消息的规则也有很多种:direct, topic, headers(不常用) 和 fanout,我们称之为交换类型。 我们可以把Exchange理解为分配这批物质的政府,现在国家规定了宏观的分配方向(发送消息时的routingKey),每个家庭也有了家庭情况的标签(绑定Exchange时的routingKey),但是这个物资具体怎么分,还是当地政府说了算。 Direct 严格按照国家规定来,只有房子倒了的,家里有人受伤了的而且家庭经济困难的才能分到救灾物资。 家庭D能分到 Fanout 只要是灾区的居民都能分到, 不管家庭情况如何。 家庭A\B\C\D都能分到 Topic 主题匹配: 只要家庭情况在国家规定分配规则内的,都能分到物资,但是家庭C分不到,因为他家太有钱了,这个条件不在国家的分配规则里。家庭A\B\D能分到 所以,我们在声明一个Exchange(交换机)的同时,还要指定该交换机的类型,即(当地政府怎么来分救灾物资) 其实,用这个例子,我是想说,生产者和消费者之间,就像国家与难民之间一样,国家只知道,我要帮助难民,但是难民有谁,物资能不能分到难民手里,还得当地政府说了算,你就说我这个例子恰不恰当吧!哈哈 好了,懂了概念,我们再来结合具体例子看看。 Fanout Producer.cs的代码 using System; using System.Text; using RabbitMQ.Client; namespace _2_Publish { class Program { static void Main(string[] args) { //创建连接工厂 var factory = new ConnectionFactory() { HostName = "localhost" }; //创建连接 using (var connection = factory.CreateConnection()) { //创建会话 using (var chancel = connection.CreateModel()) { //生命交换机 chancel.ExchangeDeclare(exchange: "FanoutDemo", type: ExchangeType.Fanout); string readMsg = "helloWorld"; while (readMsg.ToLower() != "exit") { var body = Encoding.UTF8.GetBytes(readMsg); //给交换机发送消息 chancel.BasicPublish(exchange: "FanoutDemo", routingKey: "", body: body); Console.WriteLine($"成功发送消息{readMsg}"); Console.WriteLine("请输入要发送的内容!"); readMsg = Console.ReadLine(); } } } } } } Customer.cs代码 using System; using System.Text; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace _2_Receiver { class Program { static void Main(string[] args) { //创建连接工厂 var factory = new ConnectionFactory() { HostName = "localhost" }; //创建连接 using (var connection = factory.CreateConnection()) { //创建会话 using (var channel = connection.CreateModel()) { //声明一个Fanout类型的交换机 channel.ExchangeDeclare(exchange: "FanoutDemo", type: ExchangeType.Fanout); //声明一个消息队列并获取它的名字 var queueName = channel.QueueDeclare().QueueName; //把消息队列和交换机绑定 channel.QueueBind(exchange: "FanoutDemo", queue: queueName, routingKey: ""); //创建消费者 var consume = new EventingBasicConsumer(channel); //把消费者和队列绑定 channel.BasicConsume(queue: queueName, autoAck: true,consumer: consume); consume.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body); Console.WriteLine($"收到消息{message}"); }; Console.ReadLine(); } } } } } 在上面的代码中,无论是在生产者的发送消息里 //给交换机发送消息 chancel.BasicPublish(exchange: "FanoutDemo", routingKey: "", body: body); 还是消费者所在的Queue的绑定里 //把消息队列和交换机绑定 channel.QueueBind(exchange: "FanoutDemo", queue: queueName, routingKey: ""); 我们都没有制定routingKey,因为没个人都能获取消息,所以此处,声明routingKey就没有意义了。 我们看看运行效果。 运行了三个消费者,当生产者发出消息时,三个消费者都收到了相同的消息。可以理解为广播模式。(Customer单词拼写错了,图片修改不方便,就不改了,大家将就一下) Direct Direct时严格匹配的,只有队列绑定的RoutingKey与生产者发送消息时指定的RoutingKey完全相同,才能接收成功。 Producer.cs using System; using System.Text; using RabbitMQ.Client; namespace _2_Publish { class Program { static void Main(string[] args) { //创建连接工厂 var factory = new ConnectionFactory() { HostName = "localhost" }; //创建连接 using (var connection = factory.CreateConnection()) { //创建会话 using (var chancel = connection.CreateModel()) { //生命交换机 chancel.ExchangeDeclare(exchange: "DirectDemo", type: ExchangeType.Direct); string readMsg = "helloWorld"; while (readMsg.ToLower() != "exit") { var body = Encoding.UTF8.GetBytes(readMsg); //给交换机发送消息 chancel.BasicPublish(exchange: "DirectDemo", routingKey: "Direct.Key", body: body); Console.WriteLine($"成功发送消息{readMsg}"); Console.WriteLine("请输入要发送的内容!"); readMsg = Console.ReadLine(); } } } } } } 我把Exchange的类型更改为Direct类型,并且发送消息的routingKey设置为Direct.Key。 然后我们来定义消费者 Customer.CS using System; using System.Text; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace _2_Receiver { class Program { static void Main(string[] args) { //创建连接工厂 var factory = new ConnectionFactory() { HostName = "localhost" }; //创建连接 using (var connection = factory.CreateConnection()) { //创建会话 using (var channel = connection.CreateModel()) { //声明一个Fanout类型的交换机 channel.ExchangeDeclare(exchange: "DirectDemo", type: ExchangeType.Direct); //声明一个消息队列并获取它的名字 var queueName = channel.QueueDeclare().QueueName; Console.WriteLine("请输入RoutingKey!"); var routingKey = Console.ReadLine(); //把消息队列和交换机绑定 channel.QueueBind(exchange: "DirectDemo", queue: queueName, routingKey: routingKey); //创建消费者 var consume = new EventingBasicConsumer(channel); //把消费者和队列绑定 channel.BasicConsume(queue: queueName, autoAck: true,consumer: consume); Console.WriteLine("开始监听消息"); consume.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body); Console.WriteLine($"收到消息{message}"); }; Console.ReadLine(); } } } } } 消费者的RoutingKey再控制台输入, 运行效果如下: 可以看到,只有RoutingKey为Direct.Key的消费者才收到了生产者发出的消息。 Topic RabbitMQ 中的 RouteKey 支持绑定键表达式写法,有两种主要的绑定键: *(星号)可以代替一个单词. # (井号) 可以代替0个或多个单词. 比如在下面这个图中(P为发送者,X为RabbitMQ中的Exchange,C为消费者,Q为队列) 在这个示例中,我们将发送一条关于动物描述的消息,也就是说 Name(routeKey) 字段中的内容包含 3 个单词。第一个单词是描述速度的(celerity),第二个单词是描述颜色的(colour),第三个是描述哪种动物的(species),它们组合起来类似:“..”。 然后在使用CapSubscribe绑定的时候,Q1绑定为CapSubscribe["*.orange.*"], Q2 绑定为CapSubscribe["*.*.rabbit"]和[CapSubscribe["lazy.#]。 那么,当发送一个名为 "quick.orange.rabbit" 消息的时候,这两个队列将会同时收到该消息。同样名为lazy.orange.elephant的消息也会被同时收到。另外,名为 "quick.orange.fox" 的消息将仅会被发送到Q1队列,名为 "lazy.brown.fox" 的消息仅会被发送到Q2。"lazy.pink.rabbit" 仅会被发送到Q2一次,即使它被绑定了2次。"quick.brown.fox" 没有匹配到任何绑定的队列,所以它将会被丢弃。 另外一种情况,如果你违反约定,比如使用 4个单词进行组合,例如 "quick.orange.male.rabbit",那么它将匹配不到任何的队列,消息将会被丢弃。 但是,假如你的消息名为 "lazy.orange.male.rabbit",那么他们将会被发送到Q2,因为 #(井号)可以匹配 0 或者多个单词。 我们结合代码来看一看。 Producer.cs using System; using System.Text; using RabbitMQ.Client; namespace _2_Publish { class Program { static void Main(string[] args) { //创建连接工厂 var factory = new ConnectionFactory() { HostName = "localhost" }; //创建连接 using (var connection = factory.CreateConnection()) { //创建会话 using (var chancel = connection.CreateModel()) { //生命交换机 chancel.ExchangeDeclare(exchange: "TopicDemo", type: ExchangeType.Topic); string readMsg = "helloWorld"; while (readMsg.ToLower() != "exit") { var body = Encoding.UTF8.GetBytes(readMsg); //给交换机发送消息 chancel.BasicPublish(exchange: "TopicDemo", routingKey: "Topic.Demo.Key", body: body); Console.WriteLine($"成功发送消息{readMsg}"); Console.WriteLine("请输入要发送的内容!"); readMsg = Console.ReadLine(); } } } } } } 我给发送消息的routingKey指定为Topic.Demo.Key 再来看看消费者 Cuustomer.cs using System; using System.Text; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace _2_Receiver { class Program { static void Main(string[] args) { //创建连接工厂 var factory = new ConnectionFactory() { HostName = "localhost" }; //创建连接 using (var connection = factory.CreateConnection()) { //创建会话 using (var channel = connection.CreateModel()) { //声明一个Fanout类型的交换机 channel.ExchangeDeclare(exchange: "TopicDemo", type: ExchangeType.Topic); //声明一个消息队列并获取它的名字 var queueName = channel.QueueDeclare().QueueName; Console.WriteLine("请输入RoutingKey!"); var routingKey = Console.ReadLine(); //把消息队列和交换机绑定 channel.QueueBind(exchange: "TopicDemo", queue: queueName, routingKey: routingKey); //创建消费者 var consume = new EventingBasicConsumer(channel); //把消费者和队列绑定 channel.BasicConsume(queue: queueName, autoAck: true,consumer: consume); Console.WriteLine("开始监听消息"); consume.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body); Console.WriteLine($"收到消息{message}"); }; Console.ReadLine(); } } } } } 其RoutingKey也是在外部输入。 我们看看运行效果 因为Producer发布消息的RoutingKey是Topic.Demo.Key 又因为#可以代表0个或者多个单词 ,*能代表一个单词 所以*.*.Key Topic.#与Topic.Demo.Key匹配,而其他两个*.Key和test.1.2当然是不匹配的,所以没有收到消息。 总结 对于上面的例子,我们可以总结出,编写一个生产者的过程如下: 创建连接工厂-》创建连接-》创建会话(Chanel)-》创建交换机(Exchange)-》发送消息 编写一个生产者的过程如下: 创建连接工厂-》创建连接-》创建会话(Chanel)-》创建交换机(Exchange)-》创建队列-》绑定队列和交换机-》创建消费者-》把消费者和队列绑定-》监听消息 掌握这个大的方向,不管交换机怎么分配,代码应该都会写了。 为什么在生产者中和消费者中都要创建交换机呢? 因为我们不确定是生产者先执行还是消费者先执行,所以提前创建一下,避免连接时发现没有创建交换机,出现错误,如果交换机已经创建了,那么默认不会再次创建的。 另外,交换机创建后,同一名称的交换机使用完不会自动删除,但是第二次如果创建的名称和上次一样,但是交换机类型不一样了,那么便会出现报错。 这里总结的是一些RabbitMQ的基础知识,后面还会继续写一些更深入的使用技巧,如果不想错过精彩信息,点击关注一下吧(๑¯◡¯๑)!

优秀的个人博客,低调大师

MaxCompute Studio 使用入门

MaxCompute Studio 是MaxCompute 平台提供的安装在开发者客户端的大数据集成开发环境工具,是一套基于流行的集成开发平台IntelliJ IDEA的开发插件,可以帮助您方便地进行数据开发。下面我们一起来看一看Studio的简单用法。 用户登录 Studio提供用户登录功能。常规登录入口在Intellij toolbar中右上角的Sign In。登录完成后可直接使用答疑机器人、添加D2项目、同步D2等功能。 答疑机器人 点击右侧“FAQ Robot”按钮,进入答疑机器人页面,如下图所示。答疑机器人支持自助答疑和人工答疑两种方式,支持图片和文件上传,如果对自助答疑的结果不满意,可以在聊天框发送“人工”呼叫ODPS值班同学。或者直接发送图片或文件唤起人工服务。 资源管理 Studio 的一大核心功能就是浏览 MaxCompute 项目空间(Project)的资源,包括Table、UDF、Resource等。为了能够在 Studio 中访问它们,以及其它很多功能,我们首先需要在新建项目连接。在Project Explorer中点击"+"添加连接,目前Studio支持同步D2项目和通过accessId/Key添加项目连接。 建立项目空间连接后,可以浏览Table&View、Function、Resource等项目资源,双击对应的元素可以在Intellij中查看资源详情。 Table 双击打开某个table,如下图所示。有两个tab页面,“表信息”页面如下如所示,该页面展示table的基本信息、schema信息,提供数据预览功能。 选择对应的分区,设置预览行数,点击“预览”按钮可以进行数据预览。同时,右键点击表头支持数据导入/导出功能。 “分区信息”页面如下所示,显示分区的详细信息,提供分区的查询、刷新功能。同时提供导出分区信息、分区数据导入导出等功能。 在project explorer中右键点击某张表,可以看到建表、改表、数据操作等一系列比较实用的功能。 在Studio中不光可以通过写SQL进行DDL相关的操作,也可以通过可视化界面创建和修改table:Project Explorer中右键点击项目名称或者“Table & Views”选择“Create a new table”进行建表操作。 右键点击某个table选择“open table editor”可以对表信息进行相应修改,如下图所示,Studio可以将用户操作转化为对应的SQL语句。 Functions Functions分为BuiltIn Function和UserDefied Function, 双击对应的function可以展开详情: 可以通过如下途径添加Function: Resources 双击对应的resource,可以在editor中打开文件。对于UDF文件数大于5的情况,只打开前5个class。 可以通过如下途径添加resource: SQL编辑器与作业提交 如何帮助用户高效愉快的编写SQL是MaxCompute studio的核心使命,下面就让我们来一起看一下SQL编辑器的使用。首先需要创建一个MaxCompute Script Module,如下图所示: Module创建完成后,新建SQL脚本文件如下图所示。 最后双击SQL文件就可以进行我们的脚本开发了,这里可以自己设置模式(单句模式|脚本模式)、系统类型(旧有系统类型|MaxCompute系统|Hive系统)、编译器类型(默认编译器|实验性编译器)等。右上角需要选择对应的project。 下图所示三个按钮分别表示将SQL同步到D2,在Cosonle中打开ODPS CMD,打开SQL History。 SQL编辑完成后,点击下方的graph tab按钮,可以显示该SQL的执行计划,双击Task节点会展开对应的Operator级别的信息。 点击绿色的提交按钮,Studio会先编译脚本,编译通过后提交Server后显示运行信息并打开Job分析页面,如下图所示: 作业详情 上面我们说到了如何编辑SQL和提交作业,接下来一起看看如何查看作业详情。作业详情页可以通过多条路径打开,比如上面说到的提交作业,会自动打开作业详情页。其次通过Job Explorer也可以打开作业详情页,再有就是如果已经知道logview,可以通过logview打开作业详情页,如下图所示: 作业详情页面分为两部分,左侧是作业的一些基本信息,右侧包含多个tab页,包含graph信息、时序图、详情、脚本、摘要、结果、分析等内容。下面对graph图、时序图、详情页和作业分析进行进一步介绍。 graph页面 graph页面展示作业的Job|Task|Operator三层结构。点击左上角的导航可切换不同level的视图,双击Job节点进入Task视图,双击Task节点进入Operator视图。 graph页面用户可以通过鼠标滚轮或者左侧缩放按钮对图像进行缩放,同时提供鹰眼和拖拽等功能。点击task节点显示task级别信息,点击task之间连线显示schema信息。 在task level视图中右键点击节点,选择"expand all"展开所有的operator。 同时,单击table节点会显示table的基本信息和分区信息,双击table节点会跳转到对应的table详情页。 时序图 时序图画的是所有Fuxi Instance的甘特图,通过时序图可以对作业的运行时间等进行详细的分析,左侧的Filter可以对instances进行过滤,鼠标悬停到对应的instance上会显示对应的信息,双击则会跳转到"详情”tab页并选中对应的instance,对用户分析十分方便。同时时序图支持缩放功能。 详情页 详情页展示的是Task级别和Instance级别的详细信息。 作业分析页面 作业分析页面提供作业分析的结论,比如长尾节点、数据倾斜等。并提供散点图、长尾图和数据倾斜图供用户分析。 作业队列 Maxcompute 作业从提交到开始执行之前,需要经历很多状态,如:执行编译、优化、排队等待资源等。作业队列提供了查看当前详细状态的功能。对于正在排队等待调度的作业,可也从队列窗口中查看排队位置和前序作业等信息。点击"Job Explorer"打开作业队列页面,该页面提供了项目名称、状态、日期等Filter,选择对应的条件后会得到对应的作业队列,双击某一个作业会打开对应的作业详情页面。

优秀的个人博客,低调大师

Python入门

1.1 Python介绍 1.1.1 Python简介 Python,是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多·范罗苏姆(中文名字:龟叔)创造,第一版发布于 1991 年。可以视之为一种改良 (加入一些其他编程语言的优点,如面向对象) 的 LISP。 作为一种解释型语言,Python 的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而非使用大括号或者关键词)。 数据来源:https://www.tiobe.com/tiobe-index/ 由上图可见,Python整体呈上升趋势,反映出Python应用越来越广泛并且也逐渐得到业内的认可。相比于 C++ 或 Java,Python 让开发者能够用更少的代码表达想法。不管是小型还是大型程序,该语言都试图让程序的结构清晰明了。 1.1.2 Python设计哲学与定位 Python的设计哲学是“优雅”、“明确”、“简单”。 Python开发者的哲学是“用一种方法,最好是只有一种方法来做一件事”,也因此它和拥有明显个人风格的其他语言很不一样。在设计Python语言时,如果面临多种选择,Python开发者一般会拒绝花俏的语法,而选择明确没有或者很少有歧义的语法。 这些准则被称为“Python格言”。在Python解释器内运行import this可以获得完整的列表。 >>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! 1.1.3 Python应用范围 Web程序: Python经常被用于Web开发。比如,通过mod_wsgi模块,Apache可以运行用Python编写的Web程序。使用Python语言编写的Gunicorn作为Web服务器,也能够运行Python语言编写的Web程序。 Python定义了WSGI标准应用接口来协调Http服务器与基于Python的Web程序之间的沟通。一些Web框架,如Django、Pyramid、TurboGears、Tornado、web2py、Zope、Flask等,可以让程序员轻松地开发和管理复杂的Web程序。 GUI开发: Python本身包含的Tkinter库能够支持简单的GUI开发。但是越来越多的Python程序员选择wxPython或者PyQt等GUI包来开发跨平台的桌面软件。使用它们开发的桌面软件运行速度快,与用户的桌面环境相契合。通过PyInstaller还能将程序发布为独立的安装程序包。 其他: YouTube、Google、Yahoo!、NASA都在内部大量地使用Python。OLPC的作业系统Sugar项目的大多数软件都是使用Python编写。 1.1.4 Python的种类 1、CPython Python的官方版本,使用C语言实现,使用最为广泛,CPython实现会将源文件(py文件)转换成字节码文件(pyc文件),然后运行在Python虚拟机上。 1、 Jython Python的Java实现,Jython会将Python代码动态编译成Java字节码,然后在JVM上运行。 3、IronPython Python的C#实现,IronPython将Python代码编译成C#字节码,然后在CLR上运行(与Jypthon类似)。 4、PyPy(特殊) Python实现的Python,将Python的字节码字节再编译成机器码。 5、RubyPython,Brython .... 注意:本文中都的为Python 3.6.2版本:https://www.python.org/downloads/release/python-362/ 关于Python环境部署可以参考:http://www.runoob.com/python/python-install.html 1.2 Python的语法 Python的设计目标之一是让代码具备高度的可阅读性。它设计时尽量使用其它语言经常使用的标点符号和英文单字,让代码看起来整洁美观。因为Python是动态语言,它不像其他的静态语言如C、Pascal那样需要书写声明语句。 1.2.1 缩进语法 Python开发者有意让违反了缩进规则的程序不能通过解释,以此来强迫程序员养成良好的编程习惯,也方便所有人查找和阅读。例如: if age < 21: print("你不能买酒。") print("不过你能买口香糖。") print("这句话处于if语句的外面。") 如果想要类似于执行shell脚本一样执行python脚本,例: ./clsn.py ,那么就需要在 clsn.py文件的头部指定解释器,如下: #!/usr/bin/env python # -*- coding: utf-8 -*- if age < 21: print("你不能买酒。") print("不过你能买口香糖。") print("这句话处于if语句的外面。") 注:上述例子为Python 3.0以上版本的代码。 1.2.2 变量说明 变量定义的规则: 1、变量名只能是 字母、数字或下划线的任意组合 2、变量名的第一个字符不能是数字 3、以下关键字不能声明为变量名 ['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] 4、变量的定义要具有可描述性。 1.2.3 语句和控制流 语句类型 说明 if语句 当条件成立时运行语句块。经常与else, elif(相当于else if)配合使用。 for语句 遍列列表、字符串、字典、集合等迭代器,依次处理迭代器中的每个元素。 while语句 当条件为真时,循环运行语句块。 try语句 与except, finally, else配合使用处理在程序运行中出现的异常情况。 class语句 用于定义类型。 def语句 用于定义函数和类型的方法。 pass语句 表示此行为空,不运行任何操作。 assert语句 用于程序调适阶段时测试运行条件是否满足。 with语句 Python2.6以后定义的语法,在一个场景中运行语句块。比如,运行语句块前加锁,然后在语句块运行结束后释放锁。 yield语句 在迭代器函数内使用,用于返回一个元素。自从Python 2.5版本以后。这个语句变成一个运算符。 raise语句 抛出一个异常。 import语句 导入一个模块或包。常用写法:from module import name, import module as name, from module import name as anothername 1.2.4 程序交互 input进行交互 #!/usr/bin/env python # -*- coding: utf-8 -*- # 将用户输入的内容赋值给 name 变量 name = input("请输入用户名:") # 打印输入的内容 print(name) 可以让用户输入多个信息,如下 #!/usr/bin/env python # -*- coding: utf-8 -*- name = input("What is your name?") age = input("How old are you?") hometown = input("Where is your hometown?") print("Hello ",name , "your are ", age , "years old, you came from",hometown) 1.2.5 数据类型与动态类型 Python内置多种数据类型。下面这个列表简要地描述了Python内置数据类型(适用于Python 3.x): 类型 描述 例子 str 一个由字符组成的不可更改的有序列。在Python 3.x里,字符串由Unicode字符组成。 'Wikipedia' "Wikipedia" """Spanning multiple lines""" bytes 一个由字节组成的不可更改的有序列。 b'Some ASCII' b"Some ASCII" list 可以包含多种类型的可改变的有序列 [4.0, 'string', True] tuple 可以包含多种类型的不可改变的有序列 (4.0, 'string', True) set, frozenset 与数学中集合的概念类似。无序的、每个元素唯一。 {4.0, 'string', True} frozenset([4.0, 'string', True]) dict或map 一个可改变的由键值对组成的无序列。 {'key1': 1.0, 3: False} int 精度不限的整数 42 float 浮点数。精度与系统相关。 3.1415927 complex 复数 3+2.7j bool 逻辑值。只有两个值:真、假 True False 除了各种数据类型,Python语言还用类型来表示函数、模块、类型本身、对象的方法、编译后的Python代码、运行时信息等等。因此,Python具备很强的动态性。 1.2.6 运算符 计算机可以进行的运算有很多种,可不只加减乘除这么简单,运算按种类可分为算数运算、比较运算、逻辑运算、赋值运算、成员运算、身份运算、位运算,今天我们暂只学习算数运算、比较运算、逻辑运算、赋值运算 算数运算 以下假设变量:a=10,b=20 比较运算 以下假设变量:a=10,b=20 赋值运算 以下假设变量:a=10,b=20 逻辑运算 关于逻辑运算 在没有()的情况下not 优先级高于 and,and优先级高于or,即优先级关系为( )>not>and>or,同一优先级从左往右计算。 in,not in : 判断子元素是否在原字符串(字典,列表,集合)中: 例如: #print('喜欢' in 'dkfljadklf喜欢hfjdkas') #print('a' in 'bcvd') #print('y' not in 'ofkjdslaf') 常见的占位符有: 1.3 流程控制语句 1.3.1 流程控制之--if 假如把写程序比做走路,那我们到现在为止,一直走的都是直路,还没遇到过分叉口,想象现实中,你遇到了分叉口,然后你决定往哪拐必然是有所动机的。你要判断那条岔路是你真正要走的路,很简单,只需要在程序里预设一些条件判断语句,满足哪个条件,就走哪条岔路。这个过程就叫流程控制。 单分支 if 条件: 满足条件后要执行的代码 双分支 """ if 条件: 满足条件执行代码 else: if条件不满足就走这段 """ MemInfo = 48 if MemInfo > 95 : print("内存即将用光") else: print("没事,放心用!") 多分支 if 条件: 满足条件执行代码 elif 条件: 上面的条件不满足就走这个 elif 条件: 上面的条件不满足就走这个 elif 条件: 上面的条件不满足就走这个 else: 上面所有的条件不满足就走这段 示例:猜猜我的年龄 age_of_clsn = 24 guess = int(input(">>:")) if guess > age_of_clsn : print("猜的太大了,往小里试试...") elif guess < age_of_clsn : print("猜的太小了,往大里试试...") else: print("恭喜你,猜对了...") 1.3.2 流程控制之--while循环 基本while循环 while 条件: # 循环体 # 如果条件为真,那么循环体则执行 # 如果条件为假,那么循环体不执行 循环中止语句 如果在循环的过程中,因为某些原因,你不想继续循环了,怎么把它中止掉呢?这就用到break 或 continue 语句 break用于完全结束一个循环,跳出循环体执行循环后面的语句 continue和break有点类似,区别在于continue只是终止本次循环,接着还执行后面的循环,break则完全终止循环。 示例: 之前猜年龄的代码运行一次使用一次,但稍加修改就能一自运行。 age_of_clsn = 24 while True: guess = int(input(">>:")) if guess > age_of_clsn: print("猜的太大了,往小里试试...") elif guess < age_of_clsn: print("猜的太小了,往大里试试...") else: print("恭喜你,猜对了...") break 1.3.3 格式化输出 现有一练习需求,问用户的姓名、年龄、工作、爱好 ,然后打印成以下格式 ------------ info of Alex Li ----------- Name : CLSN Age : 24 job : Linux Ops Hobbie: girl ------------- end ----------------- 这样的格式如何输出呢? name = input("Name:") age = input("Age:") job = input("Job:") hobbie = input("Hobbie:") info = ''' ------------ info of %s ----------- #这里的每个%s就是一个占位符,本行的代表 后面拓号里的 name Name : %s #代表 name Age : %s #代表 age job : %s #代表 job Hobbie: %s #代表 hobbie ------------- end ----------------- ''' %(name,name,age,job,hobbie) # 这行的 % 号就是 把前面的字符串 与拓号 后面的 变量 关联起来 print(info) 在格式化输出里面,怎么表示%? msg = "我是%s,年龄%d,目前工作进度为80%%"%('clsn',24) print(msg) 使用%% 将 % 转义. 1.4 编码说明 字符 ASCII Unicode utf-8 GBK 英文 1个字节 2-4个字节 1个字节 1个字节 中文 x 2-4个字节 3个字节 2个字节 其他 x 2-4个字节 2个字节 x 注: 1个字节 = 8 位bit 1.5 参考文献 [1]https://zh.wikipedia.org/wiki/Python [2]http://www.cnblogs.com/jin-xin/articles/7459977.html [3]http://www.cnblogs.com/wang-yc/articles/6419907.html [4]http://www.runoob.com/python/python-install.html [5]https://www.cnblogs.com/iDouble/p/8467738.html 我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。 作者: 惨绿少年 出处: https://www.nmtui.com 本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册