为了账号安全,请及时绑定邮箱和手机立即绑定
2. 数据库连接池

面对大量的数据库操作请求,数据库连接池能很好地帮助 Hibernate 避开网络开销所产生的性能消耗。什么是数据库连接池?一般讲池子是用来养鱼的,但数据库连接池不是养鱼的,而是养了好多的 Connection 对象。当应用程序需要一个连接对象时,便向连接池租用一个。用完后,再返回给连接池,这样连接池中的连接对象便可以反复使用,达到重用连接对象的目的。Connection 的功能本质是通过网络 API 完成进程和进程之间的远程连接,每一次连接的性能消耗都是很大的。如果每一次需要时都重开一个连接,用完后便立马销毁,其代价是非常大的,如果使用连接池便可以减少这种性能消耗。Hibernate 本身没有提供较佳的数据库连接池实现,其实也没有必要重新造轮子。因为有行业认可的、稳定可靠的第三方数据库连接池可用。如:DBCP;C3P0;Proxool。几位都是久经沙场考验、绝对忠诚可靠的老同志。因为 Hibernate 3.0 后的版本不再支持 DBCP 数据库连接池,DBPC 在此略过不提。但是,不能质疑 DBCP 在行业内的领导性。本节课就和大家一起讲解在 Hibernate 中使用数据库连接池,让其 Hibernate 的起飞姿势更优雅。

3.2 SQL 预处理

SQL Prepare 是一种在数据库层面上防止 SQL 注入的方式,它简单且高效,且无需三方支持就能够有效的断绝掉 SQL 注入。3.2.1 Prepare 如何防止 SQL 注入那么 Prepare 是如何防止 SQL 注入的呢?在本小节的开头,我们提到 SQL注入的主要方式是将 SQL 代码注入到参数中,什么是 SQL 代码呢?像0 OR 1=1这样的 SQL 段就是 SQL 代码,SQL 引擎会将它解析后再执行,这样OR 1=1就会生效。想要从根源上解决 SQL 注入的问题,那么必须要让OR 1=1失效,而 Prepare 正是这样的一种处理方式。Prepare 会先将 SQL 模板传递给 SQL 引擎,SQL 引擎拿到 SQL 模板后,会编译模板生成相应的SQL执行计划,此时 SQL 已经被编译了。当EXECUTE再携带0 OR 1=1这样的参数时,OR 1=1不会再被编译,数据库只会单纯的将它视为一个普通的字符串参数,因此OR就会失效,OR 1=1也会失效,这样 SQL 注入的问题就从根本上解决了。3.2.2 Prepare 防止 SQL 注入实例我们还是以 imooc_user 为例来说明 Prepare 的用法。SQL 注入的语句如下:SELECT * FROM imooc_user WHERE id = 0 OR 1=1;不论是参数校验,还是预处理都能够解决掉这次 SQL 注入,预处理的解决方式如下。预处理会先编译 SQL 模板语句:PREPARE finduserbyid FROM 'SELECT * FROM imooc_user WHERE id = ?'; 预编译后,数据库已经生成了该 SQL 语句的执行计划,你可以简单地理解为:数据库: 嘿!老铁,语句我已经收到了,执行计划已经搞好了,你只需要按照?占位符传入相应的参数就行了。应用程序: 我传入的参数如果是0 OR 1=1,你会怎么处理啊?数据库: 老铁放心,执行计划已经生成好了,不会再解析了,参数里面的OR和=也不会再被解析,我们直接把它当成一个参数处理了。SQL 语句如下:SET @id='0 OR 1=1';EXECUTE finduserbyid USING @id;结果如下:+----+----------+-----+| id | username | age |+----+----------+-----+从结果中可以得出,即使注入了OR 1=1,查询结果仍然为空,用户数据没有泄漏。

4.1 例3 全连接

请书写 SQL 语句,返回imooc_class和imooc_user表的全连接。SELECT * FROM imooc_class FULL OUTER JOIN imooc_user ON imooc_class.id = imooc_user.class_id;查询结果如下:+--------+----------+----------+--------+---------------+| id | username | class_id | id | class_name |+--------+----------+----------+--------+---------------+| 1 | pedro | 1 | 1 | SQL必知必会 || 2 | peter | 1 | 1 | SQL必知必会 || 3 | faker | 2 | 2 | C语言入门 || 4 | lucy | 4 | 4 | JVM花落知多少 || 5 | jery | <null> | <null> | <null> || <null> | <null> | <null> | 3 | JAVA高效编程 |+--------+----------+----------+--------+---------------+一些数据库,比如 MySQL 是不支持全连接的,但可以通过左、右外连接的并集(Union)来模拟实现,如下:SELECT *FROM imooc_userLEFT JOIN imooc_class ON imooc_class.id = imooc_user.class_idUNIONSELECT *FROM imooc_userRIGHT JOIN imooc_classON imooc_class.id = imooc_user.class_idWHERE imooc_user.class_id IS NULL;提示: SQLite 不支持右连接和全连接,也可以通过左连接来模拟右连接,从而实现全连接。

2.2 什么是弱外键

在 join 一节中,我们提到外键的最终落脚点是使用 Join 来连接数据,不过 SQL 连接并非只支持强外键,它其实也支持弱外键,甚至无外键,只要连接的字段能够对应上,连接都是可行的。那么什么是弱外键了?答案其实很简单,强外键是数据库层面上的外键,而弱外键是逻辑层面的上的外键。如下,我们新建两表:CREATE TABLE imooc_user( id int PRIMARY KEY, username varchar(20), age int);CREATE TABLE imooc_user_score( id int PRIMARY KEY, user_id int NOT NULL, score int);在新建 imooc_user_score 表的 SQL 语句中,我们并未声明 user_id 是外键,但是在逻辑层面上我们认为它就是外键,在连接的时候知道其对应关系就行了。

2.1 内连接

SQL 语法:inner join table_name on table_name构建一条测试 SQL:select * from test_a a inner join test_b b on a.name = b.name;,执行结果如下图:内连接结果执行结果解释:组装两张表满足 a.name = b.name 的查询结果。我们以数据中的集合类比,表 test_a 和表 test_b 是两个数据集合,内连接则表示查询两个表都符合条件的数据,即集合的交集操作。集合交集

3.1 例2 右连接

请书写SQL语句,查询imooc_user表中每一门课程和该课程下参与的用户。分析:由题干可知,imooc_class 应作为保留表,考虑到使用右连接,因此 imooc_class 是右表;连接条件是外键 class_id。语句:整理可得语句如下:SELECT class_name,username FROM imooc_user RIGHT OUTER JOIN imooc_class ON imooc_class.id = imooc_user.class_id;结果如下:+---------------+----------+| class_name | username |+---------------+----------+| SQL必知必会 | pedro || SQL必知必会 | peter || C语言入门 | faker || JVM花落知多少 | lucy || JAVA高效编程 | <null> |+---------------+----------+提示:SQLite 是不支持右连接的,却可以通过更换保留表的位置用左连接来模拟右连接。

2.3 右连接

SQL 语法:right join table_name on table_name构建一条测试 SQL:select * from test_a a right join test_b b on a.name = b.name; ,执行结果如下图:右连接执行结果执行结果解释:右连接(right join)是右外连接(right outer join)的简写,右连接会将右表(test_b)的所有记录都展示出来,而左表(test_a)只展示符合后置条件(on condition)的记录展示,其他记录以 NULL 作为补全。即展示两个集合的交集以及右边集合的剩余部分:集合右交集

4. Spring Boot 集成 RabbitMQ 消息通信中间件是否成功的必要性测试

无论使用哪一种集成方式,验证 RabbitMQ 集成成功与否的方式都是类似下方的内容:当我们的 Maven 包管理工具解析完成 RabbitMQ 的依赖之后,此时,需要运行我们的 SpringBoot 项目,在项目启动完成之后,我们可以在本地浏览器地址栏中,输入以下地址:http://localhost:15672如果看到 RabbitMQ 提示的登录信息框,如下图所示,则表示我们已经成功把 RabbitMQ 消息通信中间件集成到了 SpringBoot 框架中去了。RabbitMQ 内置的默认账号和密码都是 guest ,我们可以登录进去看下,如下图所示:Tips:1.各位同学请务必按照老师要求的内容来安装 RabbitMQ-Server ,当然,如果你足够清楚不同的安装包中的内容,那么,你可以通过下载 Binary 形式的完全安装包进行安装 RabbitMQ ,否则,请使用老师提供的安装包进行安装。2.在安装 RabbitMQ 之前,请确保 erlang 语言支持库已经成功安装,如果 erlang 语言支持库没有安装成功,请不要安装 RabbitMQ ,如果已经在错误的环境安装 RabbitMQ 导致报错,那么请完全删除掉已经安装的 RabbitMQ 内容,从头开始安装。3.请确保下载的 erlang 语言支持库的版本为 V23.0 最新版本,否则可能会导致 RabbitMQ 无法安装成功。4.由于 RabbitMQ 是基于 AMQP 协议的(后续会介绍),所以,Maven 依赖的名称为 amqp-client ,即 AMQP 客户端,这一点,同学们简单了解即可。5.在启动项目之前,请先启动自己本地电脑中的 RabbitMQ 服务,否则,项目无法启动。

2.2 左连接

SQL 语法:...left join table_name on table_name构建一条测试 SQL:select * from test_a a left join test_b b on a.name = b.name; ,执行结果如下图:左连接执行结果执行结果解释:左连接(left join)是左外连接(left outer join)的简写,左连接会将左表(test_a)的所有记录都展示出来,而右表(test_b)只会展示符合搜索条件(上图中的 on condition)的搜索记录,其他记录以 NULL 作为补全。即展示两个集合的交集以及左边集合的剩余部分数据:集合左交集

4.2 下载压缩包

4.2.1 压缩包下载页面说明当我们选择第 2 种方式的时候,我们需要在下载页面中点击 Download Packages 选项。点击后,我们将进入压缩包下载页面:页面中需要关注的信息用红色框框了起来,并且标注了序号,其中:点击该选项我们将切换到 Eclipse 安装器的下载界面。仍然是 Eclipse 安装器的下载选项,这里可以让我们手动选择对应各个操作系统的安装器。这里显示的是我们即将下载的压缩包的名称,本Wiki中,我们将只关注两个。Eclipse IDE for Java Developers 是面向 Java 开发的 Eclipse IDE。Eclipse IDE for Enterprise Java Developers 这个就是我们的 Java 企业版。这里显示了我们可以下载的操作系统版本,注意,目前都只支持 64 位操作系统。现在,我们只需要选择我们对应操作系统的版本来进行下载就可以了。4.2.2 选择对应的操作系统版本进行下载三个操作系统版本的下载步骤都一致,我们将以 macOS 系统举例。在上一小节 4.2.1 中打开的压缩包下载页面上,如果我们需要下载对应 macOS 系统的 Eclipse IDE for Java Developers 压缩包,那么我们可以点击 Mac Cocoa 右边的 64-bit(注意点击的是 64-bit,如果你是 Windows,那么你需要点击的是 Windows 右边的 64-bit):点击后,我们将会打开一个新的页面:该页面的信息和 4.1.1 安装器下载页面说明 中的一样,此处不再赘述。4.2.3 选择镜像地址开始下载同样地,和安装器的下载一样,为了获得更好的下载速度从而避免下载失败的状况出现,我们将点击 Select Another Mirror 重新选择镜像的地址,然后也是选择国内速度较快的 大连东软信息学院:接着我们将看到如下信息:此时,压缩包将自动开始下载,如果没有,则点击图中的 click here 手动开始下载。至此,下载压缩包的介绍就结束了。

4.1 例2 内连接

请书写 SQL 语句,返回imooc_class和imooc_user两表的内连接集合。分析:使用 Select 搭配 Inner Join 即可。语句SELECT * FROM imooc_user INNER JOIN imooc_class ON imooc_user.class_id = imooc_class.id;结果如下:+----+----------+----------+----+---------------+| id | username | class_id | id | class_name |+----+----------+----------+----+---------------+| 1 | pedro | 1 | 1 | SQL必知必会 || 2 | peter | 1 | 1 | SQL必知必会 || 3 | faker | 2 | 2 | C语言入门 || 4 | lucy | 4 | 4 | JVM花落知多少 |+----+----------+----------+----+---------------+一般情况下,交叉连接是默认的连接方式,因此我们可以省略INNER关键字:SELECT * FROM imooc_user JOIN imooc_class ON imooc_user.class_id = imooc_class.id;这样也能得到同样的结果;使用 Join 和 On 关键字可以显式连接两表,我们也可以通过 Where 进行隐式的连接:SELECT * FROM imooc_user, imooc_class WHERE imooc_user.class_id = imooc_class.id;

3. 接口的好处

过去我们写 JavaScript 定义一个函数:function getClothesInfo(clothes) { console.log(clothes.price)}let myClothes = { color: 'black', size: 'XL', price: 98 }getClothesInfo(myClothes)之前我们写 JavaScript 这样是很正常的,但同时你可能会遇到下面这些问题:getClothesInfo() // Uncaught TypeError: Cannot read property 'price' of undefinedgetClothesInfo({ color: 'black' }) // undefined相信原因你也知道,JavaScript 是 弱类型 语言,并不会对传入的参数进行任何检测,错误在运行时才被发现。那么通过定义 接口,在编译阶段甚至开发阶段就避免掉这类错误,接口将检查类型是否和某种结构做匹配。

2. 什么是 SQL 语句

SQL 是一种语言,一般地,我们会通过书写 SQL 语句的方式来描述数据操作。例如:SELECT first_name FROM person;这条语句会从名为person的数据表中取出所有的first_name字段。SQL 语句是用来操作数据的基本单位,绝大多数情况下,都推荐使用 SQL 语句的方式操作数据库。SQL 语句是核心,后续的章节里,其实就是不断的讲解和实操 SQL 语句。注意:SQL 语句区分大小写每一条 SQL 语句的末尾必须带上分号(;)

2. 左连接、右连接、全连接

面试官: 请阐述下 MySQL 中左连接、右连接、全连接的定义和区别?题目解析:① 定义:MySQL 的连接表示多表(一般就是两张表)之间联合查询的操作。② 分类:根据操作性质的不同,分为内连接和外连接,外连接又可以细分为左外连接和右外连接。除此之外,还有一种全连接操作,不过 MySQL 数据库并不支持。定义解释比较抽象,下面我们通过实战来讲解这几种连接的区别,首先进入 MySQL 终端,首先创建一个测试数据库:CREATE DATABASE mooc_demo;创建一张测试表 test_a:DROP TABLE IF EXISTS `test_a`;CREATE TABLE `test_a` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '数据库主键', `name` varchar(32) DEFAULT NULL COMMENT '姓名', `part` varchar(32) DEFAULT NULL COMMENT '部门') ENGINE=InnoDB DEFAULT CHARSET=utf8;然后插入一些测试数据:insert into test_a (`name`, `part`) values ('小明','文艺部');insert into test_a (`name`, `part`) values ('小红','学习部');insert into test_a (`name`, `part`) values ('小王','体育部');继续创建另外一张测试表 test_b:DROP TABLE IF EXISTS `test_b`;CREATE TABLE `test_b` ( `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '数据库主键', `name` varchar(32) DEFAULT NULL COMMENT '姓名', `group` varchar(32) DEFAULT NULL COMMENT '小组') ENGINE=InnoDB DEFAULT CHARSET=utf8;插入一些测试数据:insert into test_b (`name`, `group`) values ('小明', '1号小组');insert into test_b (`name`, `group`) values ('小红', '2号小组');insert into test_b (`name`, `group`) values ('小李', '3号小组');执行完成之后,两张表的数据如下:两张表数据

5. 连接符

Where 后面的条件子句不仅可以为单条件子句,还可以是多条件子句。多条件子句需要通过AND或者OR两个连接符进行连接。这两个的运算符的作用如下:运算符描述AND连接两个条件表达式,若二者都为True,返回True,否则返回FalseOR连接两个条件表达式,若二者都为False,返回False,否则返回TrueAND 和 OR 两个连接符让多条件查询变得更加灵活,从而让 Where 也变得更加灵活。

1.1 良好的美感是网页设计的基础

可能在前端工程师眼中,不同的网页设计风格各有千秋。但在大众眼中,对一个网页的美观度理解往往不能够做到全面甚至专业。如何在大众美感和设计师艺术之间找到一个平衡,则是我们设计网页过程中必须关注的问题。这一点在学习过程中,要有意无意的培养。同学们要明白,网页毕竟不是做完给我们自己欣赏的,而是放到服务器上给其他人使用,浏览信息的。从这个角度出发,我们就需要明白,大众的视野是如何看网页的。静态内容的排布我们主要参考现有主流网站的设计风格,有余力的同学可以多多学习一些平面设计师的设计思想,对我们对网页风格的原创性将会有很大提高。

3. 如何进行优化

思考:如何解决单 Reactor 性能问题呢?以 Tomcat 作为案例来进行分析:1.1 问题: 我们平时把项目打包成 war 部署到单个 Tomcat 来进行运行,在并发量很小的情况下是正常运行的,但是一旦并发量达到 1k 以上,单个 Tomcat 就会很吃力了,那怎么办呢?1.2 解决: 很简单,只需要在 Tomcat 前面加 Nginx 做负载转发,这样的话,多个 Tomcat 同时对外提供服务,不但整体的性能得到提高,即使其中一个 Tomcat 宕机,但是整个 Tomcat 集群还是能正常对外提供服务。生活中饭馆的案例进行说明:还是以饭馆经营模型说明,方便大家更好的理解。2.1 一个饭馆只有一个老板,老板即兼职服务员和厨师的工作,整体效率很低,这就是单 Reactor 单线程模型;2.2 一个负责迎接客户、点菜、上菜的服务员(Reactor 线程),几个厨师负责炒菜(Worker 线程),厨师轻松了,但是服务员依然忙不过来,这就是单 Reactor 多线程模型;2.3 一个负责迎接在门口迎接小妹妹(好比:Reactor 主线程),几个专门负责点菜和上菜的服务员(好比:Reactor 从线程),几个负责超出厨师(Worker 线程),那么每个岗位都会很轻松,并且还能服务更多的客户进行就餐,这就是主从 Reactor 多线程模型。其实,Reactor 模型也是类似道理,哪个环节性能存在瓶颈,那么将其功能再细分,并且增加执行数量(集群)即可。

1.1 监听连接

浏览器每发起一次请求都需要跟服务端建立连接,服务端要时刻监听有没有客户端连接。传输层协议有 TCP/UDP 两种,实现起来并没有强制说用哪一种,下面是官方文档对 Http 连接的说明:HTTP communication usually takes place over TCP/IP connections. The default port is TCP 80 .文档中指明了连接通常用的是 TCP, TCP 不用考虑数据包乱序,丢失这些问题,实现起来更简单,高效。在代码层我们可以用 Socket 来实现我们的 TCP 传输服务。

5.2 Client 连接

接下来编写 Android 程序,xml 里面只放置一个 Button 用于触发连接,这里就不列出来了。点击 button 之后按照上面的步骤来依次创建 Socket,设置 IP 和 port,接着获取输入输出流即可。**注意:**网络请求属于耗时操作, Android 要求网络请求必须在子线程中执行,所以我们需要在onClick()中 new 一个 thread。package com.emercy.myapplication;import android.app.Activity;import android.os.Bundle;import android.view.View;import java.io.IOException;import java.io.OutputStream;import java.net.Socket;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { //1. Create Client super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { Socket socket; try { //1. create socket socket = new Socket("10.64.210.51", 12345); //2. output stream OutputStream os = socket.getOutputStream(); //3. Send data os.write("Hello world".getBytes()); System.out.println("send message"); os.flush(); socket.shutdownOutput(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } }); }}首先运行 Server,接着打开 App,点击“连接”Button 就可以和 Server 通信了。

4. Nginx中的负载均衡配置

Nginx 的 stream 模块和 http 模块分别支持四层和七层模块的负载均衡。其用法和支持的负载均衡策略大致相同。首先使用 upstream 指令块 和 server 指令指定上游的服务,upstream 指令的用法如下:Syntax: upstream name { ... }Default: —Context: http官网示例如下:upstream backend { server backend1.example.com weight=5; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; server backup1.example.com backup;}这里定义了4台上游服务器,分别用域名, ip+port、socket 形式指定地址,后面跟上若干配置参数。默认情况下,upstream 指令块中采用的是加权 Round-Robin 负载均衡算法。该算法通过加权轮询的方式访问 upstream 中 server 指令指定的上游服务。此时,在server 指令中我们可以添加一些关于服务的静态配置,比如指定服务的权重(weight)、server 的最大并发连接数(max_conns)、max_fails 和 fail_timeout 等。除了默认的 Round-Robin 算法外,Nginx 中常用的负载均衡策略还有基于客户端 ip 地址的 Hash 算法。该算法以客户端的 ip 地址作为 hash 算法的关键字,映射到特定的上游服务器中,当然也可以根据客户段的其他 key 来进行 hash 算法。涉及的配置指令为 ip_hash 和 hash,用法如下:Syntax: ip_hash;Default: —Context: upstreamSyntax: hash key [consistent];Default: —Context: upstream最后 Nginx 中一种常用的动态负载均衡算法是最少连接数算法。该算法会从所有的上游服务器中找到并发连接数最少的一个,然后将请求转发给它,如果出现多个最少连接数的服务器,则会在这些最少连接数的服务器中继续应用 Round-Robin 算法。配置该策略的指令为 least_conn,其指令格式如下:Syntax: least_conn;Default: —Context: upstream当然, Nginx 中的负载均衡策略还有很多,就不在此一一介绍了。可以仔细研读官方文档进行进一步学习

3.1 临时节点和最小连接数策略实现负载均衡

首先我们需要在集群的每一个 Server 中都使用 Zookeeper 客户端 Curator 来连接 Zookeeper 服务端,当 Server 启动时,使用 Curator 连接 Zookeeper 服务端,并用自身的地址信息创建临时节点到 Zookeeper 服务端。我们还可以提供手动下线 Server 的方法,需要 Server 下线时可以手动调用删除节点的方法,需要 Server 上线时再次使用自身的地址信息来创建临时节点。除了维护 Server 的地址信息外,我们还需要维护请求的会话连接数,我们可以使用节点的 data 来保存请求会话的连接数。我们使用在 Zookeeper Curator 一节创建的 Spring Boot 测试项目来实现:/** * 最小连接数策略 Demo * Server 服务端注册地址 */@Componentpublic class MinimumConnectionsStrategyServer implements ApplicationRunner { @Autowired private CuratorService curatorService; // Curator 客户端 public CuratorFramework client; // 当前服务地址的临时节点 public static String SERVER_IP; // 当前服务地址临时节点的父节点,节点类型为持久节点 public static final String IMOOC_SERVER = "/imooc-server"; /** * 服务启动后自动执行 * * @param args args * @throws Exception Exception */ @Override public void run(ApplicationArguments args) throws Exception { // Curator 客户端开启会话 client = curatorService.getCuratorClient(); client.start(); // 注册地址信息到 Zookeeper registerAddressToZookeeper(); } /** * 注册地址信息到 Zookeeper * 服务启动时和服务手动上线时调用此方法 * * @throws Exception Exception */ public void registerAddressToZookeeper() throws Exception { // 判断父节点是否存在,不存在则创建持久节点 Stat stat = client.checkExists().forPath(IMOOC_SERVER); if (stat == null) { client.create().creatingParentsIfNeeded().forPath(IMOOC_SERVER); } // 获取本机地址 String address = InetAddress.getLocalHost().getHostAddress(); // 创建临时节点,节点路径为 /IMOOC_SERVER/address,节点 data 为 请求会话数,初始化时为 0. // /imooc-server/192.168.0.77 SERVER_IP = client.create() .withMode(CreateMode.EPHEMERAL) .forPath(IMOOC_SERVER + "/" + address, "0".getBytes()); } /** * 注销在 Zookeeper 上的注册的地址 * 服务手动下线时调用此方法 * * @throws Exception Exception */ public void deregistrationAddress() throws Exception { // 检查该节点是否存在 Stat stat = client.checkExists().forPath(SERVER_IP); // 存在则删除 if (stat != null) { client.delete().forPath(SERVER_IP); } }}在客户端的请求调用集群服务之前,先使用 Curator 获取 IMOOC_SERVER 下所有的临时节点,并寻找出 data 最小的临时节点,也就是最小连接数的服务。在客户端发送请求时,我们可以让当前 Server 的请求会话数加 1,并更新到临时节点的 data,完成请求时,我们可以让当前 Server 的请求会话数减 1,并更新到临时节点的 data 。/** * 最小连接数策略 Demo * Client 客户端发送请求 */@Componentpublic class MinimumConnectionsStrategyClient implements ApplicationRunner { @Autowired private CuratorService curatorService; // Curator 客户端 public CuratorFramework client; // 服务列表节点的 父节点 public static final String IMOOC_SERVER = "/imooc-server"; @Override public void run(ApplicationArguments args) throws Exception { // Curator 客户端开启会话 client = curatorService.getCuratorClient(); client.start(); } /** * 获取最小连接数的服务 * 发送请求前调用此方法,获取服务地址 * * @return String * @throws Exception Exception */ public String getTheMinimumNumberOfConnectionsService() throws Exception { // 获取所有子节点 List<String> list = client.getChildren().forPath(IMOOC_SERVER); // 新建 Map Map<String, Integer> map = new HashMap<>(); // 遍历服务列表,保存服务地址与请求会话数的映射关系 for (String s : list) { byte[] bytes = client.getData().forPath(IMOOC_SERVER + "/" + s); int i = Integer.parseInt(new String(bytes)); map.put(s, i); } // 寻找 map 中会话数最小的值 Optional<Map.Entry<String, Integer>> min = map.entrySet().stream().min(Map.Entry.comparingByValue()); // 不为空的话 if (min.isPresent()) { // 返回 服务地址 ip Map.Entry<String, Integer> entry = min.get(); return entry.getKey(); } else { // 没有则返回服务列表第一个服务地址 ip return list.get(0); } } /** * 增加该服务的请求会话数量 * 使用服务地址处理业务前调用此方法 * * @param ip 服务地址 * @throws Exception Exception */ public void increaseTheNumberOfRequestedSessions(String ip) throws Exception { byte[] bytes = client.getData().forPath(IMOOC_SERVER + "/" + ip); int i = Integer.parseInt(new String(bytes)); i++; client.setData().forPath(IMOOC_SERVER + "/" + ip, String.valueOf(i).getBytes()); } /** * 减少该服务的请求会话数量 * 请求结束时调用此方法减少会话数量 * * @param ip 服务地址 * @throws Exception Exception */ public void reduceTheNumberOfRequestedSessions(String ip) throws Exception { byte[] bytes = client.getData().forPath(IMOOC_SERVER + "/" + ip); int i = Integer.parseInt(new String(bytes)); i--; client.setData().forPath(IMOOC_SERVER + "/" + ip, String.valueOf(i).getBytes()); }}这样我们就使用 Zookeeper 的临时节点完成了一个简单的最小连接数策略的负载均衡。

2. 连接数据库

若想进行数据库操作,必须创建 数据源 连接。以下以连接MySQL为例介绍如何连接数据库。其它类型数据库连接基本是类似的,更多细节请参考。step1: 在数据库工具窗口中 View -> Tool Windows -> Database,单击"Data Source Properties"图标。step2: 在"Data Sources and Drivers “对话框中,单击”+"图标并选择 MySQL。然后根据下图的提示输入相应的信息。如果没有事先下载 所选数据库 JDBC Driver, 点击窗口底部 Download missing driver files 链接直接下载即可。Tips:每种类型的数据库的 JDBC Driver 是可以安装多个不同版本的,除此以外,如上图显示:系统也会自动提示更新。如想更改使用版本,在当前窗口 Drivers 下选择要更改的数据库类型。step3: 测试成功后,点击ok, 新的数据源 MySql 创建成功。查看 Database 与 Service 工具窗口。

3. Zookeeper 实现负载均衡

Zookeeper 实现负载均衡,我们可以使用 Zookeeper 的临时节点来维护 Server 的地址列表,然后选择负载均衡策略来对请求进行分配。我们回顾一下临时节点的特性:当创建该节点的 Zookeeper 客户端与 Zookeeper 服务端断开连接时,该节点会被 Zookeeper 服务端移除。使用临时节点来维护 Server 的地址列表就保证了请求不会被分配到已经停机的服务上。在上面的讲解中,轮询策略,随机策略和一致性哈希策略都使用 Java 简单的实现了 Demo,那么接下来我们就使用最小连接数策略来实现请求的分配。

1. 实现观察者模式

实现观察者模式,在目标对象中需要维护所有他的观察者引用。观察者可以观察多个不同目标对象的,所以需要让观察者知道是哪个目标对象发送的通知。下面我们通过一个简单的例子来看看如何实现观察者模式。这个例子叫老师点名了。上大学时候,经常有同学旷课在宿舍打游戏,并且嘱咐去上课的同学,老师要是点名了给我打电话。还好宿舍离教学楼近,接到通知的同学赶紧跑去教室也能赶上。有的胆子大点的同学,接到通知后也不去上课,而是找个关系好的同学帮忙喊声到。去上课的同学是通知者(目标对象),他持有所有需要他通知老师点名的同学(观察者)的引用,才能在老师点名的时候通知到每个人。程序中我们一般用容器存储观察者。当通知的时候循环调用所有观察者暴露出的更新方法。“老师点名了” 目标对象代码如下:public class TeacherRollCallSubject { private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer observer){ observers.add(observer); } public void removeObserver(Observer observer){ observers.remove(observer); } public void notifyObservers (){ observers.forEach(Observer::update); }}观察者只需要实现一个方法,供通知者做通知的时候调用。我们先定义观察者的接口。public interface Observer { void update();}我们定义第一类观察者的实现,他接到通知后,会马上去教室。public class GotoClassObserver implements Observer { @Override public void update() { System.out.println("老师点名了!"); System.out.println("我要马上赶到教室去!"); }}第二类观察者,接到通知后,会通知自己的好朋友帮自己答到。public class AskForHelpObserver implements Observer { @Override public void update() { System.out.println("老师点名了!"); System.out.println("赶紧给XX发信息,让他替我答到!"); }}客户端代码中,分别声明两个不同的观察者,然后让这两个观察者都观察老师点名了目标对象。最后出发目标对象的通知方法。客户端代码如:public class Client { public static void main(String[] args) { Observer studentOne = new GotoClassObserver(); Observer studentTwo = new AskForHelpObserver(); TeacherRollCallSubject subject = new TeacherRollCallSubject(); subject.addObserver(studentOne); subject.addObserver(studentTwo); subject.notifyObservers(); }}运行后输出如下:老师点名了!我要马上赶到教室去!老师点名了!赶紧给XX发信息,让他替我答到!可以看到每个观察者都接到了通知,并且按照自己实现的响应方式作出不同的逻辑处理。第一个同学会赶到教室。第二个同学则是给好朋友发信息,让其替他答到。

2. 接口的好处

编写 API 有什么好处呢?由于 API 就是把 Web 应用的功能全部封装了,所以,通过 API 操作数据,可以极大地把前端和后端的代码隔离,使得后端代码更易于测试,前端代码编写更加简单。此外,如果我们把前端页面看作是一种用于展示的客户端,那么 API 就是为客户端提供数据、操作数据的接口。这种设计可以获得极高的扩展性。例如:我们经常使用的淘宝商城就有很多的客户端,Web, iOS 和 Android 。这些客户端其实都是共用的一套后端代码。但是当我们在 Web 端搜索商品时得到的结果和在 iOS 和 Android 端得到的结果却是一样的。这是因为,我们在不同用户端搜索的时候,访问了后端同一个 API 。这样后端针对前端的同一种需求,只需开发一种接口,就可满足前端不同终端对于该资源的调用,而无需针对不同终端做差异化开发,这便大大降低了开发工作量,节约了开发时间。

1.1 什么是超链接

超链接作为一种特殊的网页元素,指的是对内容的链接。它的概念里就包含了内容和链接两个要义。内容:表明它能将用户带到下一个或者新的内容中去。链接:说的是超链接这种元素在引导你去新的内容过程中起到的锁链般的作用。你可以把它想象成载你通向别的内容的船,也可以是把你拽进别的网页的锁链。这里的解释仅适用于初学者。对于初学者来说,理解超链接是第一步。

2. 什么是 SQL

SQL,全称 Structured Query Language,是一种结构化查询语言。SQL,是一种数据库访问语言。SQL,是一种 半衰期很长的语言,自 1970 年代诞生到现在,经久不衰,日久弥新。

1. 下载 MySQL

1.1 来到官网下载 Windows 版本 MySQL 压缩包,如图所示选择 Windows 版本的安装包并下载:1.2 点击下载之后会提示让你登录,如图所示。选择不用登陆,直接下载即可:

2.1 连接池

首先,我们通过 mysql 这个客户端工具进行数据库的连接,这时遇到的是连接池。连接池负责客户端的连接管理、授权认证。连接命令如下(输入完连接命令后,需要输入用户密码):mysql -h localhost -u root -p如果用户名和密码认证通过,连接池会通过权限表获取这个用户名所拥有的权限信息;如果用户名或密码认证不通过,则会收到一个错误提示:“ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’”;建立连接后,会产生相应的连接信息,可以通过 show processlist 命令查看。下方图中 Id 为 5048 这一行,即为成功连接数据库所建立的连接信息,请注意 Command 这一列,值为”Sleep“,表明这是一个空闲连接。成功连接数据库后,如果没有任何动作,这个连接就会变成空闲状态。root@localhost [(none)]>show processlist;+------+------+-----------+------+---------+------+----------+------------------+| Id | User | Host | db | Command | Time | State | Info |+------+------+-----------+------+---------+------+----------+------------------+| 4771 | root | localhost | NULL | Query | 0 | starting | show processlist || 5048 | root | localhost | NULL | Sleep | 3 | | NULL |+------+------+-----------+------+---------+------+----------+------------------+2 rows in set (0.00 sec)

1. 下载安装包

我们首先打开Oracle官网的 JDK 下载地址,找到 Java SE 15 版块,点击 JDK Download 按钮。点击 JDK Download按钮后,我们会跳转到 JDK 下载详情页面。在下载详情页面可以找到如下图这样的一个表格,在最右侧 Download 一列中找到jdk-15.0.1_linux-x64_bin.rpm一项,单击鼠标左键。此时网页上会弹出如下对话框,提示如果你想要下载必须遵守其协议,先勾选上复选框,再使用鼠标右键点击下载按钮,复制链接地址。Tips:本小节的操作动图中,使用的 Java 版本为 14,由于安装 Java15 和 安装 Java14 的操作流程完全相同,我并没有进行统一替换。登录至我们要安装 Java 的 Linux 主机,使用 wget 命令来下载我们刚刚复制的 JDK 链接地址。执行如下命令,将 JDK 下载至服务器:wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" https://download.oracle.com/otn-pub/java/jdk/14+36/076bab302c7b4508975440c56f6cc26a/jdk-14_linux-x64_bin.rpmTips: 如果你的主机没有安装 wget 命令,执行上述命令会报错:-bash: wget: command not found这是因为主机还没有安装wget命令,执行 yum install wget 命令来进行安装。成功安装后再执行下载命令。下载过程如下:使用 ls 命令查看当前目录下的内容,jdk-14_linux-x64_bin.rpm就是我们刚刚下载好的安装包。[root@Colorful ~]# lsjdk-14_linux-x64_bin.rpm

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

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

帮助反馈 APP下载

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

公众号

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