为了账号安全,请及时绑定邮箱和手机立即绑定
3. 将暂存区内容提交

接着上一点,我们准备将 test2.txt 进行正式提交。但是,但是,但是,我就不提交(微笑脸)。直接提交岂不是太简单了?现在我在提交之前又反悔了,突然想起来还想加一句 “hello world”,那就加呗,加完我们再看看会变成什么鬼样子。$ echo 'hello world' >> test2.txt$ git statusOn branch masterYour branch is up to date with 'origin/master'.Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: test2.txtChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: test2.哎!不对啊。test2.txt 这个文件刚刚不是在 “Changes to be committed” 下面吗?现在怎么同时又存在于 “Changes not staged for commit” 下面了。这个文件怎么同时存在 “已暂存” 和 “未暂存” 两种状态了?附:这部分演示流程如下:别急,跟我往下继续看!那要不我们先执行下提交看看会变成什么状态?好!说干就干!对了,忘了说了,提交命令是 “git commit -m ‘这是提交说明’”,hiahia~~~$ git commit -m 'commit 1'[master bba6ffb] commit 1 1 file changed, 1 insertion(+) create mode 100644 test2.txt$ git statusOn branch masterYour branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits)Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: test2.txtno changes added to commit (use "git add" and/or "git commit -a")仔细看:我在执行 "git commit" 之后,提示 test2.txt 已提交。但是我执行 “git status” 之后,却发现在 “Changes not staged for commit” 部分还存在一个 test2.txt,这是为啥?其实,提交后只把前面步骤中执行了 “git add” 那部分修改进行了提交,而后面我再追加的 “hello world” 的内容由于没有执行 “git add”,并没有纳入 git 的暂存区,提交也就自然提交不了。那想要再提交这部分内容怎么办?简单啊!继续执行 “git add”,“git commit” 就好了。$ git add test2.txt$ git statusOn branch masterYour branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits)Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: test2.txt$ git commit -m 'commit 2'[master 446c174] commit 2 1 file changed, 1 insertion(+)$ git statusOn branch masterYour branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits)nothing to commit, working tree clean看,现在状态正常了。是不是一切都是那么熟悉?就像初恋般的感觉。附:这部分演示流程如下:通过这个过程,我们可以看到,“git commit” 只把暂存区的修改提交了,也就是第一次执行了 “git add” 的修改被提交了,第二次的修改不会被提交。

4. 提交 Pull Request

可以进入到你自己的远端项目主页,点击 New pull request 按钮,然后选择你的 bugfix 分支和原项目仓库的 master 分支,提交申请,等待作者进行代码 review 并合并,如果 review 不合格,作者会提示你修改,按着作者的已经修改好后重新提交代码即可。

3.1 提交本地更改

主菜单 VCS -> View -> Tool Windows -> Commit,选择要提交的文件或者changelist, 填写提交信息,选择 commit 或者 Amend Commit通过工具栏上按钮,可以Rollback 代码,可以比较文件,也可以设置提交前与提交后的操作。有时候我们可能只想提交部分文件更改,比如在 test_rectangle.py, 增加了两个方法 test_width 与 test_height, 然后只提交 test_width。未选择的改变仍然在当前的Changelist 里。除此以外,也可以将一个文件更改放入不同的更改列表,以实现文件的部分提交。

2.8 提交按钮

提交按钮相当于表单 form 的开关,点击这个开关相当于将表单中的数据提交给服务器。通过设置 type=submit 可以定义一个提交表单按钮,这个按钮必须包裹在 form 标签中才能生效,例如:1023

1. 自动提交

默认情况下,MySQL 是自动提交(autocommit)的。也就意味着:如果不是显式地开始一个事务,每个查询都会被当做一个事务执行 commit。这是和 Oracle 的事务管理明显不同的地方,如果应用是从Oracle 数据库迁移至 MySQL 数据库,则需要确保应用中是否对事务进行了明确的管理。在当前连接中,可以通过设置 autocommit 来修改自动提交模式:mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> set autocommit = 1;Query OK, 0 rows affected (0.00 sec)-- 1或ON表示启用自动提交模式,0或OFF表示禁用自动提交模式如果设置了autocommit=0,当前连接所有事务都需要通过明确的命令来提交或回滚。对于 MyISAM 这种非事务型的表,修改 autocommit 不会有任何影响,因为非事务型的表,没有 commit或 rollback 的概念,它会一直处于 autocommit 启用的状态。有些命令,在执行之前会强制执行 commit 提交当前连接的事务。比如 DDL 中的 alter table,以及lock tables 等语句。

1. 工作流程介绍

我们先从工作流程说起,基本的 Git 工作流程如下:a. 修改:修改工作区(Working Directory)中的文件。b. 暂存:选择要作为下一次提交部分的那些更改,仅将这些更改添加到暂存区域(Staging Area)。c. 提交:执行一次提交,该提交将获取本次所在版本的文件并将该快照永久存储到你的 Git 目录(.git directory)中。将这三个流程串起来,可以通俗地理解为:先将本地文件做一修改,修改之后我们要怎么告诉 Git 呢?所以就需要通过命令将本地修改先添加到暂存区,将暂存区理解为一个临界区。但提交到暂存区之后,还未正式纳入 Git 的管理,等你确认这部分内容需要正式提交到 Git 仓库的时候,再通过提交命令执行提交操作。那么,一个最基本的工作流程就结束了。对于一些内容大家是不是还有点懵呢?接下来我挑其中涉及到的概念详细说明下,或许对大家理解有帮助。请继续往下看。

2. 什么是交换机 ?

在 RabbitMQ 中,交换机主要用来将生产者生产出来的消息,传送到对应的频道中,即交换机是一个消息传送的媒介,其英文被称为 exchange 。交换机在 RabbitMQ 中起着承上启下的作用。RabbitMQ 根据不同业务场景,为我们内置了多种交换机,但是这些交换机并不是每一种都会用到,常用的交换机也就 3 种,接下来让我们看一下都有哪三种吧。交换机名称类型使用频率直通交换机Direct高扇形交换机Fanout高主题交换机Topic高

5. 跳过暂存区直接提交

我们知道,如果对一个文件做了修改,想要提交至版本库之前必须先加入暂存区。但是,git 提供了一个偷懒的办法(毕竟,程序员效率至上嘛),就是可以跳过暂存区提交,使用如下命令:$ git commit -a -m '备注'演示流程如下:注意:“git commit -a -m” 这个命令必须在文件内容修改后使用,如果你的文件是刚新建的并且还没提交过,那么还是需要先 “git add”。正常的开发过程中,对于提交日志的规范都有要求。日志是我们作为历史版本维护的一个很重要的内容,一个好的日志模板可以方便进行代码 review,以及后续开发者快速了解历史问题等等。本教程只是演示,实际开发中请遵守具体规范!

4. 比较工作区和版本库的文件差异

前面步骤我们操作了修改文件后如何去添加到暂存区、提交版本库等流程,那么如果我们提交后又在本地做了修改,怎么比较版本库和本地工作区的差异呢?自然也是有办法的,执行如下命令就可以了:$ git diff HEAD -- test.txt我们试着在提交版本库后又做一些修改,比如我追加一行内容,再看下与版本库的差异,演示流程如下:

3.1 读取未提交(Read Uncommitted)

字面上理解,可以读取没有提交的数据,这是最低的事务隔离级别:读事务不会阻塞读事务和写事务,写事务也不会阻塞读事务,但是会阻塞写事务;写事务不阻塞读事务,可以读取未提交的数据。后果是,会出现脏读现象。如果财务工作人员在更新公司员工工资时,不小心把 A 同事的原工资 2000 改成了 8000,但马上发现了错误,立马更正过来。改过就好,不伤大雅。问题是,如果在财务工作人员修改时,A 同事恰好也在查看自己的账号,因为可看到别的事务没有提交的数据,天降财富呀,眼睛没花吧,工资涨成 8000 元了。如果晚上回家告诉了老婆,第二天再一查,工资还是 2000 。请问下班后该如何向老婆交待!这就叫脏读。当然,并不是没有解决的办法。脏读解决方案:在财务人员的事务提交前,任何其他事务不可读取其修改过的值,则可以避免 A 同事回家挨骂的悲剧。

2. web 交互的前期

在 Ajax 出现之前,我们也有网页,我们的前后端信息也需要交互,那么我们是怎么做的呢?当时的做法是:刷新(重载)或跳转页面。举两个例子:当我们填写完 form 表单的时候,submit 提交表单,这个时候浏览器会进行页面刷新或跳转,反馈给用户表单提交是否成功。当我们在页面上点击跳转慕课网链接(一般是 a 标签,或者导航栏输入),那么页面会刷新或者跳转到慕课网上去。也许这个时候你会觉得奇怪,交互一次就需要刷新一次?这未免也太过笨拙!是的,如你所见,在 Ajax 之前,HTTP 请求对应着页面,一次 HTTP 请求也就意味着需要请求一个页面。当然,人往高处走,总会有更加先进的办法来解决当前的问题。因此,逐渐的,我们有了 Ajax。

2.3. 对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:store.commit({ type: 'incrementByCount', count: 10})当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:mutations: { incrementByCount (state, payload) { state.count = state.count + payload.count }}完整示例:798代码解释JS 代码第 9-11 行,定义了一个无参的 mutation,对 state.count + 1。JS 代码第 12-14 行,定义一个传入 Number 类型参数 count 的 mutation,对 state.count + count。JS 代码第 16-18 行,定义一个传入 Object 类型参数 payload 的 mutation,对 state.count + payload.count。JS 代码第 26 行,提交 mutation increment。JS 代码第 28 行,提交 mutation incrementByCount 并传入数量 10。JS 代码第 31-33 行,提交 mutation incrementByObject 并传入参数 {count: 5}。JS 代码第 36-39 行,以对象的形式提交 mutation incrementByObject。

4.1 例1 提交事务

请书写 SQL 语句,通过一个事务向imooc_user表插入一个用户,用户名为lucy,年龄为21,并提交事务。分析:先显示通过 BEGIN 开始一个事务,然后使用 INSERT 语句插入用户,再通过 COMMIT 提交事务即可。语句:整理可得语句如下:BEGIN;INSERT INTO imooc_user(id,username,age) VALUES (6,'lucy',21);COMMIT;执行成功后,结果如下:+----+----------+-----+| id | username | age |+----+----------+-----+| 6 | lucy | 21 |+----+----------+-----+

2. 什么是服务资源隔离

在我们正式介绍什么是服务资源隔离之前,我们先来了解一些前置的概念,这些概念是理解服务资源隔离的前提。进程与线程进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。我们可以把进程理解为我们项目运行的载体,就比如我们乘坐的公交车,公交车相对于我们来说就是一个载体,来承载我们到达不同的目的地,进程就是如此,只不过在进程中被承载的是线程罢了。线程:线程(Thread)是操作系统能够进行运算调度的最小单位。我们可以把线程理解为,执行某一具体的计算机系统任务的执行者,比如在公交车中,负责开公交车的司机师傅就可以被当做一个线程。在一个进程中,可以存在多个线程,即在一辆公交车中可以存在多名乘客,他们分别都去往不同的目的地,但是,一个线程只能属于一个进程,不属于不同的两个进程,即一名乘客同一时刻只能乘坐一辆公交车,不可能在同一时刻乘坐两辆公交车。在理解了什么是进程与线程之后,我们来看一下在我们的 Web 项目中,进程与线程都是怎样存在的,以及他们之间的关系是怎样的。Web 项目中的进程与线程我们的每一个 Web 项目都可以被看作一个进程,且一个 Web 项目只能是一个进程。在我们的 Web 项目中,常规情况下只有两个线程,分别是主线程和工作线程,其中,主线程负责我们的项目启动以及一些项目初始化工作,而工作线程则主要负责项目中的请求处理与业务逻辑执行,项目中进程与线程的关系如下图所示:Java Web 项目中进程与线程的关系根据上图,我们可以这样理解:一个 Web 项目在计算机系统中就是一个进程,而在这个进程中,存在一个主线程和一个工作线程,并且主线程主要负责项目启动,而工作线程主要负责请求的处理。在理解了这个关系之后,让我们来看一下什么是服务资源隔离。服务资源隔离在介绍服务资源隔离之前,我们需要先了解什么是服务资源。服务资源一般来讲,指的是项目正常运行所需要的基础环境、基础设施、静态资源文件等内容,而对于 Web 项目来说,其项目本身即是一种服务资源,服务调用者通过调用项目提供的服务来满足他们的业务需求。而对于服务提供者来说,这些业务需求的实现在项目中一般就是我们所开发的接口,所以,在项目中所实现的业务接口即是我们这里所说的服务资源。那么,为什么需要把服务资源进行隔离呢?我们知道,正常情况下,在 Web 项目中只有一个工作线程,且这个工作线程负责接口请求的处理。在正常应用场景下,服务调用者会调用我们项目所提供的接口来满足业务需求,这里假设我们的一个 Web 项目中具有 5 个接口,服务调用者会根据业务顺序来调用我们的接口,如果一切顺利,则业务即可正常顺利地进行下去。但是,如果服务调用者在调用接口时,其中一个接口所需要处理的业务比较复杂,导致这个接口不能及时的结束,这就导致我们后续的接口调用只能等待,直到该接口处理完毕后才能继续向下执行,如果该接口一直不能处理完毕,则后续接口就会一直等待,从而影响业务的正常开展,这种现象就被称为服务资源等待,如下图所示:服务资源等待产生原理我们可以把上图中的工作线程访问理解为服务调用者,在服务调用者调用接口 2 时,由于接口 2 迟迟不能处理,导致接口 2 出现服务等待,并最终影响后续的接口 3、接口 4、接口 5 的调用,从而影响了业务的顺利进行。如果通过采取某种措施,使满足同一业务需求的不同服务资源间进行隔离,来有效缓解或解决服务资源等待问题,那么业务就可以正常顺利地开展下去,所以人们就提出了服务资源隔离的概念。

3.4 对象风格的提交方式

提交 action 的另一种方式是直接使用包含 type 属性的对象:store.dispatch({ type: 'increment', count: 10})当使用对象风格的提交方式,整个对象都作为载荷传给 action 函数,因此 handler 保持不变:actions: { increment ({commit}, payload) { // 具体 action 内容 }}完整示例:799代码解释JS 代码第 9-11 行,我们定义了 mutation 事件 increment,事件对 state.count + 1。JS 代码第 15-17 行,我们定义了同步 Action increment,Action 中直接提交事件 increment。JS 代码第 18-22 行,我们定义了异步 Action incrementAsync,1 秒后提交事件 increment。JS 代码第 23-27 行,我们定义了接收参数的异步 Action incrementAsyncParams。JS 代码第 35 行,分发 Action 事件 increment。JS 代码第 38-40 行,以对象的形式分发 Action 事件 incrementAsync。JS 代码第 43-45 行,分发 Action 事件 incrementAsyncParams,并传入对应参数。

4.3 例3 回滚且提交事务

前面谈到,事务的回滚是可以指定保存点的,因此我们可以只回滚部分操作,提交其它操作。如下:BEGIN;INSERT INTO imooc_user(id,username,age) VALUES (8,'stark', 20);SAVEPOINT savepoint1;INSERT INTO imooc_user(id,username,age) VALUES (9,'alice', 20);ROLLBACK TO savepoint1;COMMIT;上面的 SQL 语句的大致执行流程如下:BEGIN 显示的开启了一个事务。向 imooc_user 表中插入了用户 stark ,并且通过 SAVEPOINT 新建了保存点 savepoint1。又向 imooc_user 中插入了另外一个用户 alice。通过 ROLLBACK 将事务回滚到了 savepoint1,即第2个插入语句将会失效。提交事务。执行成功后,数据如下:+----+----------+-----+| id | username | age |+----+----------+-----+| 8 | stark | 20 |+----+----------+-----+从结果中可以得出,alice 用户并没有插入成功,这与我们的分析一致,也验证了事务的强大。

3.实际高并发业务场景实现

在了解了秒杀抢购的业务场景流程之后,接下来我们就需要实现这一业务场景了,那么,这种业务场景我们应该怎么用 RabbitMQ 和 Redis 去实现呢?在使用 RabbitMQ 打造扛得住的高并发环境系列小节内容的第二小节中,我们使用 RabbitMQ 消息通信中间件和 Redis 缓存中间件,对 RabbitMQ 自身的消息队列进行了改造,改造成了一种 Redis 承载的高可用的消息队列,在本节,我们就会用到这一高可用的消息队列。在实现上述实际高并发业务场景时,由于篇幅原因,我们并不会从用户登录开始,逐步地去实现每一个过程,我们只实现在秒杀抢购业务场景中,最核心的部分,也就是,当我们在秒杀抢购商品区域,点击立即购买这个秒杀按钮时,我们后台所需要应对高并发处理的内容。让我们来看看具体应该怎么设计实现吧。

3.2 读取已提交 (Read Committed)

这个比前面的要严格点,只能读已经提交的,就不会出现脏读情况。写事务会阻塞读事务和写事务,但是读事务不会阻塞读事务和写事务。读事务不阻塞写事务但是有可能造成不可重复读。如果 A 同事听说老板要给自己涨工资,查看了一下账号,唉!还是只有 1000 元,心情好糟糕。此时财务人员对 A 同事的账号进行了修改了,变成 2000,并提交了事务。A 同事再次读取时,工资变成 2000。此时就不知道是自己眼花还是在做梦。这就是不可重复,意思就是说,在这种隔离下,你最好不要有事没事的反复读,可能会出现前后读出来的数据不一致的情况。

2. 为什么要封装独立 Handler?

即使我们把编码和解码封装成了方法,但是还是需要在 Handler 业务逻辑里面进行手工调用,虽然看似不怎么影响,但是业务 Handler 不够纯粹,应该让 Handler 只是专心的负责处理业务逻辑就好。实例:ch.pipeline().addLast(new MyEncoderHandler());//解码Handlerch.pipeline().addLast(new MyDecoderHandler());//编码Handlerch.pipeline().addLast(new MyBusiHandler());//业务Handlerpublic class MyBusiHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //1.接受参数,可以直接强转 UserReq userReq=(UserReq)msg; //2.相应数据,直接写对象 UserRes res=new UserRes(); res.setCode(0); res.setMsg("接受成功"); ctx.writeAndFlush(res); }}通过以上的代码,我们把编码和解码封装成两个独立的 Handler,并且加入到 ChannelPipeline 里面进行管理。在我们的业务 Handler 里面就可以直接操作实体数据,无需手工转换成字节数组了。思考:那么如何进行封装 Handler 呢?

3.1 第一种业务场景

在上述发布订阅消息发送模式中,我们介绍了发布订阅模式的基础概念,同时,引入了两种发布订阅模式的业务场景,并且对不同的业务场景做了基本的介绍,下面让我们来看一下如何使用代码来实现这两种不同的业务场景。实现代码:// 发布订阅模式-第一种业务场景ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost("192.168.0.1");connectionFactory.setPort(5672);connectionFactory.setUsername("guest");connectionFactory.setPassword("guest");Connection connection = connectionFactory.newConnection();Channel channel = connection.createChannel();channel.exchangeDeclare(EXCHANGE_NAME, "fanout");channel.close();connection.close();代码解释:第 1-5 行,我们使用了 RabbitMQ 的 ConnectionFactory 连接工厂,来初始化了一些获取 RabbitMQ 连接的必要参数。第 6 行,我们从 RabbitMQ 的连接工厂中,获取到了一个 RabbitMQ Conneciton 连接实例。第 7 行,我们通过 RabbitMQ 连接实例,创建了一个 channel 通道,为之后的消息通信提供媒介。第 8 行,我们使用了 channel 通道中的 exchangeDeclare 方法,来为我们的 channel 通道绑定了一个 exchange 交换机。由于在第一种业务场景中,我们不会在 exchange 交换机上绑定任何的消息队列,所以在上述代码中,我们看不到 channel 通道与 queue 消息队列进行绑定的方法。在此种业务场景下,当生产者生产出来消息之后,在将消息发布到我们的 exchange 交换机上,整个流程就结束了,没有消费者可以拿到这一消息,所以,这种业务场景毫无意义。最后,让我们来看第二种业务场景。

4. 不使用 form 提交表单

不使用 form 标签来提交表单,通常都是使用 AJAX 进行数据交互的情况。这个时候就不需要拦截 form 的提交行为了。<style> h3 {margin-top: 0;color: #4caf50;} .login {width: 300px;padding: 32px;box-shadow: 2px 2px 10px rgba(0, 0, 0, .1);position: fixed;top: 40%;left: 50%;transform: translate(-50%, -50%);} .form-item {display: flex;margin-bottom: 16px;border-bottom: 1px solid #ccc;} .form-item .title {width: 70px;color: #666;font-size: 14px;} .form-item .content {flex: 1;} .form-item .content input {width: 100%;border: 0 none;padding: 2px 8px;outline: none;font-size: 16px;} .login-btn {width: 100%;border: 0 none;background-color: #4caf50;color: white;margin-top: 16px;outline: none;height: 32px;} .login-btn:active {background-color: #2da050;}</style><div class="login"> <h3>登入</h3> <label class="form-item"> <div class="title">用户名</div> <div class="content"> <input autocomplete="off" id="account" class="account" name="account" type="text"> </div> </label> <label class="form-item"> <div class="title">密码</div> <div class="content"> <input autocomplete="off" name="pwd" type="password"> </div> </label> <div> <button class="login-btn" type="button">登入</button> </div></div><script>var loginBtn = document.querySelector('.login-btn');var pwdEle = document.querySelector('[name="pwd"]');function login(cb) { // 假装登入花了 1 秒 setTimeout(function() { alert('登入成功'); cb && cb(); }, 1000);}loginBtn.addEventListener('click', function() { var pwd = pwdEle.value; if (pwd.length < 6 || pwd.length > 16) { alert('密码长度 6-16'); return; } login(function() { window.location.href = 'https://imooc.com'; });});</script>使用这种方式,就可以自主控制流程,不需要再考虑 form 标签的行为。

1. Git 的前世今生

我们学一样技术或者一种工具,刚开始最好先了解一下它的来龙去脉。虽然无关使用,但通过它背后的故事可能会激发起我们学习它的浓厚兴趣,从这一点来讲还是有实际意义的。提起 Git,就不得不提起 Linus 和他的 Linux。计算机相关从业者大多都知道,linus 创建了 Linux 系统,但是 Linux 系统并不是由 Linus 一个人完成的,它依赖于广大的开发者源源不断的贡献代码来共同开发维护。那么问题来了,这么多人来共同维护一个系统,当时大家是怎样来保持高效的协作呢?这么多人提交代码,是怎么保证代码不冲突呢?你可能会想到使用过的一些版本控制系统,不过可能要让你失望了,虽然当时有很多版本控制系统像 CVS、SVN 等,但是都被 linus 舍弃了,因为这些集中式的版本控制系统需要联网,而且速度很受影响。所以,很长一段时间内,都是靠 linus 来自己手工合并的(默默送上大拇指)。但是,Linux 系统越来越庞大,这么下去不是个问题。直到 2002 年左右,BitMover 公司将他们的商业的版本控制系统 BitKeeper 给 Linux 社区免费使用,这下大家的工作量稍微减轻了些。但是好景不长,社区牛人聚集,还没一两个手脚乱动的?有人试图破解 BitKeeper 的协议,但是被 BitMover 公司发现了,于是乎 BitMover 公司一怒之下收回了他们的使用权。本来单车变摩托,现在又骑单车了。不过,Linus 就是 Linus,怎么可能重走旧路呢,毕竟合并代码手很疼。痛定思痛,没花多久自己用 C 开发了一套分布式版本管理系统,没错就是 Git!后来 Git 越来越流行,比如程序员大型交友网站 Github 使用的就是 Git 存储。

3. 小结

DOM 是一套标准,其定义了怎么操作 HTML 元素,浏览器则根据 DOM 标准实现了一系列的接口提供给前端开发者用 JavaScript 调用。DOM 提供了前端开发者与页面元素交互的能力,如果失去了 DOM 接口,JavaScirpt 就无法参与网页的编写了。

4.4 工作队列模式

定义:工作队列模式,和普通队列模式有点像,都是不使用任何交换机,由生产者、队列、消费者组合完成消息的发送和接收,只不过工作队列支持存在多个消费者,而普通队列模式只支持一个消费者。描述:工作队列模式下,生产者生产出消息后,直接将消息发送到消息队列中,然后多个消费者按照一个随机的顺序来依次接收消息并消费,存在多个消费者消费消息时,下一个消费者只能等待上一个消费者消费结束后才能接收消息并进行消费。这就提示我们,在实际工作中,我们可以把费时的业务操作交给 RabbitMQ 去做,这样可以提升代码的执行效率。实现伪代码:// 生产者channel.basicPublish(QUEUE_NAME, null, message.getBytes());Thread.sleep(1000);// 消费者Delivery delivery = consumer.nextDelivery();Thread.sleep(1000);代码解释:第 2 行,我们使用 channel 的 basicPublish 方法来生成一条消息。第 3 行,在生成一条消息之后,我们等待 1000 毫秒,即 1 秒后再次生成一条消息。第 5 行,我们使用 consumer 的 nextDelivery 方法来依次获取生产者生产的消息。第 6 行,在消费完一条消息之后,我们让下一个消费者等待 1 秒钟,再去消费下一条消息。Tips: 1.工作队列模式与发布订阅模式有相同之处,既他们都是经过一个队列来向多个消费者发送消息,不同之处在于,前者不用绑定交换机,而后者则需要使用交换机;2.应用工作队列模式,一定要根据实际业务需求和实际业务场景,设置好多个消费者间等待消费消息的时间,如果这个间隔时间设置太久,则容易造成下一个消费者持续等待,严重占用CPU资源,如果设置时间太短,则业务逻辑还没执行完成就开始了下一个消息的消费,这两种业务场景都是不应该出现的

4.4 交换机

定义:交换机,即 exchange ,是传递消息的中间工具,我们可以把交换机理解为,传递消息的媒介,即我们发到 RabbitMQ 服务器中的消息,在经过虚拟主机之后,会首先到达 exchange 中,然后由 exchange 根据不同的匹配策略来将消息传递到对应的频道中去。针对不同的业务场景,RabbitMQ 为我们内置了多种交换机供我们选择,这里只是对交换机的概念做一个简单的介绍,后续我们再详细介绍 RabbitMQ 中所有的交换机。代码实现:我们应该如何选择一种交换机呢?如下代码所示:channel.exchangeDeclare("test_exchange", "direct");代码解释:第 1 行,我们使用了 channel 的 exchangeDeclare 方法,来选择了一种类型的交换机,该交换机类型为直接交换机,方法的第一个参数表示交换机的名称,我们可以根据实际情况自定义交换机名称,方法的第二个参数为交换机的类型,这个我们不可以自定义,必须要和 RabbitMQ 中所支持的交换机类型的名称保持一致才行。

3. 本地更改的提交与推送(Commit and Push)

Git 需要用户名才能将提交与标识关联。如果尚未设置用户名,PyCharm 将在首次尝试提交更改时提示您指定它。若要为计算机上的每个 Git 存储库设置名称git config --global user.name "xuxh"若要为单个存储库设置名称git config user.name "xuxh"

3.3 交互方便,完善沟通

我们在开发 Java 项目的时候,主要的目的就是对外暴露我们的数据传输接口,来实现前后台数据交互的目的。针对于我们编写的接口,往往我们需要撰写接口文档来说明具体接口所做的业务是什么,以及这个接口如何使用。这样在无形之中就加重了我们的工作内容,而有了 Swagger 之后,我们只需要在相应的地方添加 Swagger 的注解来对接口进行简单的说明,它就会帮助我们生成一份完整的接口说明,见上图。这样一来我们就不用再编写一份几十页甚至几百页的接口文档了,提升了交互性能,同时也提升了前后台开发者的沟通效率。总结:Swagger 它是一个帮助开发人员来简化开发 RESTFUL API 的一款开发工具,其核心是基于 NPM 实现,在 Spring 项目中则是通过封装 SpringFox 依赖来完成对接口请求方式、请求参数、响应数据的加载,最终通过前端界面的方式来展现给用户。Swagger 具有简单、方便、高效的特性,用 Swagger 构建 RESTFUL API 可快速又方便。

2. 为什么需要内容提供者

首先我们聊聊 Content Provider 存在的意义,在 Android 中每个 App 运行于一个独立的进程,而不同进程之间一般是无法直接通信的,所以一个 App 里的数据不能直接共享给其他 App 使用,这就会让很多功能难以实现。在 Android 系统中我们可以很简单的创建 DataBase 来管理我们的数据,但是出于安全性的考虑,DataBase 只能在 App 内部使用,App 之间是无法共享内存的。因此,为了能够方便的将自己的数据共享出去,Android 系统引入了 Content Provider 组件,让我们可以和其他的 App 很方便的进行数据交换,甚至可以用来做 IPC(进程间通信),这就是内容提供者存在的意义。不适合的使用场景:当你使用了内容提供者,就表示你的数据是对外暴露的,所以这是一个相对敏感的操作,很多 App 的漏洞都是由于 Content Provider 使用不当导致的,所以这里需要多加注意。Content Provider 不仅仅可以很方便的对其他 App 提供数据,当然也可以给 App 内部其他模块、或者 App 的其他进程提供数据,在多人、多业务合作的场景下使用 Content Provider 确实很便利。但是要注意的是,如果共享的数据仅仅是一个 App 私有的数据,最好不要使用 Content Provider。

7.学生信息提交处理方法

上述模板中的表单数据提交至 Student\addPost 方法中,方法定义如下: public function addPost() { try { $studentModel = new StudentModel(); $studentModel->name = $this->request->param('name', ""); $studentModel->age = $this->request->param('age', 0, 'intval'); $studentModel->id_number = $this->request->param('id_number', ''); $studentModel->save(); } catch (\Exception $exception) { return $this->error($exception->getMessage()); } return $this->success('请求成功'); }如下图所示:Tips:学生信息添加成功就会录入到数据库中。

2.1 什么是 Swagger Validator ?

什么是 Swagger Validator 呢?在 Swagger 官网中是这么介绍的:Swagger Validator 是一个 Swagger 验证器,用于验证你的 Swagger 文档。 —官网我们来看一下 Swagger Validator 官方提供的一款在线体验平台(https://validator.swagger.io/):从图中我们可以看到,Swagger Validator 的显示界面非常类似于 Swagger-UI 的显示界面,风格和排版都很相似,那么我们应该怎么来用呢 ?

首页上一页1234567下一页尾页
直播
查看课程详情
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号