Wetts's blog

Stay Hungry, Stay Foolish.

0%

查找php.ini位置

  1. php -i | grep "Loaded Configuration File"
  2. 1
    2
    3
    <?php
    phpinfo();
    ?>

php.ini在php7中放置位置

1
/usr/local/php/bin
阅读全文 »

快捷键


Atom

  • 使用 Atom 查看 Markdown 预览

    1. windows:ctrl + shift + p

      mac:cmd + shift + p
    2. 输入 markdown
阅读全文 »

转自:http://www.2cto.com/database/201503/385669.html

Innodb 引擎

Innodb 引擎提供了对数据库 ACID 事务的支持,并且实现了 SQL 标准的四种隔离级别。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于 MySQL 后台的完整数据库系统,MySQL 运行时 Innodb 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持 FULLTEXT 类型的索引,而且它没有保存表的行数,当 SELECT COUNT(*) FROM TABLE 时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用Innodb引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个 SQL 语句时 MySQL 不能确定要扫描的范围,InnoDB表同样会锁全表。

MyIASM 引擎

MyIASM 是 MySQL 默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和 Innodb 不同,MyIASM 中存储了表的行数,于是 SELECT COUNT(*) FROM TABLE 时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么 MyIASM 也是很好的选择。

两种引擎的选择

大尺寸的数据集趋向于选择 InnoDB 引擎,因为它支持事务处理和故障恢复。数据库的大小决定了故障恢复的时间长短,InnoDB 可以利用事务日志进行数据恢复,这会比较快。主键查询在 InnoDB 引擎下也会相当快,不过需要注意的是如果主键太长也会导致性能问题,关于这个问题我会在下文中讲到。大批的 INSERT 语句(在每个 INSERT 语句中写入多行,批量插入)在 MyISAM 下会快一些,但是 UPDATE 语句在 InnoDB 下则会更快一些,尤其是在并发量大的时候。

Index——索引

索引(Index)是帮助MySQL高效获取数据的数据结构。MyIASM 和 Innodb 都使用了树这种数据结构做为索引。下面我接着讲这两种引擎使用的索引结构,讲到这里,首先应该谈一下 B-Tree 和 B+Tree。

B-Tree 和 B+Tree

B+Tree 是 B-Tree 的变种,那么我就先讲 B-Tree 吧,相信大家都知道红黑树。其实红黑树类似2,3-查找树,这种树既有 2 叉结点又有 3 叉结点。B-Tree 也与之类似,它的每个结点做多可以有 d 个分支(叉),d 称为 B-Tree 的度,如下图所示,它的每个结点可以有 4 个元素,5 个分支,于是它的度为 5。B-Tree 中的元素是有序的,比如图中元素 7 左边的指针指向的结点中的元素都小于 7,而元素 7 和 16 之间的指针指向的结点中的元素都处于 7 和 16 之间,正是满足这样的关系,才能高效的查找:首先从根节点进行二分查找,找到就返回对应的值,否则就进入相应的区间结点递归的查找,直到找到对应的元素或找到 null 指针,找到 null 指针则表示查找失败。这个查找是十分高效的,其时间复杂度为 $O(logN)$(以 d 为底,当 d 很大时,树的高度就很低),因为每次检索最多只需要检索树高 h 个结点。

1

接下来就该讲 B+Tree 了,它是 B-Tree 的变种,如下面两张图所示:

2

3

从图中就可以看出,B+Tree 的内部结点不存储数据,只存储指针,而叶子结点则只存储数据,不存储指针。并且在其每个叶子节点上增加了一个指向 MyISAM 引擎的索引结构

MyISAM 引擎的索引结构为 B+Tree,其中 B+Tree 的数据域存储的内容为实际数据的地址,也就是说它的索引和实际的数据是分开的,只不过是用索引指向了实际的数据,这种索引就是所谓的非聚集索引。

Innodb 引擎的索引结构

MyISAM 引擎的索引结构同样也是 B+Tree,但是 Innodb 的索引文件本身就是数据文件,即 B+Tree 的数据域存储的就是实际的数据,这种索引就是聚集索引。这个索引的 key 就是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。

因为 InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有主键(MyISAM 可以没有),如果没有显式指定,则 MySQL 系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,这个字段长度为 6 个字节,类型为长整形。

并且和 MyISAM 不同,InnoDB 的辅助索引数据域存储的也是相应记录主键的值而不是地址,所以当以辅助索引查找时,会先根据辅助索引找到主键,再根据主键索引找到实际的数据。所以 Innodb 不建议使用过长的主键,否则会使辅助索引变得过大。建议使用自增的字段作为主键,这样 B+Tree 的每一个结点都会被顺序的填满,而不会频繁的分裂调整,会有效的提升插入数据的效率。


转自:https://www.zhihu.com/question/20596402/answer/211492971

时间:2021/07/13 01:57:48

区别:

  1. InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
  2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
  3. InnoDB 是聚集索引,MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而 MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
  5. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;

如何选择:

  1. 是否要支持事务,如果要请选择 InnoDB,如果不需要可以考虑 MyISAM;
  2. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读写也挺频繁,请使用 InnoDB。
  3. 系统奔溃后,MyISAM 恢复起来更困难,能否接受,不能接受就选 InnoDB;
  4. MySQL5.5 版本开始 Innodb 已经成为 MySQL 的默认引擎(之前是 MyISAM),说明其优势是有目共睹的。如果你不知道用什么存储引擎,那就用 InnoDB,至少不会差。

  1. Shiro页面依赖jstl

转自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

协议

API与用户的通信协议,总是使用HTTPs协议。

域名

应该尽量将API部署在专用域名之下。

1
https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

1
https://example.org/api/

版本(Versioning)

应该将API的版本号放入URL。

1
https://api.example.com/v1/

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

路径(Endpoint)

路径又称”终点”(endpoint),表示API的具体网址。

在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的”集合”(collection),所以API中的名词也应该使用复数。

举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

  • https://api.example.com/v1/zoos
  • https://api.example.com/v1/animals
  • https://api.example.com/v1/employees

HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

  • GET /zoos:列出所有动物园
  • POST /zoos:新建一个动物园
  • GET /zoos/ID:获取某个指定动物园的信息
  • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  • DELETE /zoos/ID:删除某个动物园
  • GET /zoos/ID/animals:列出某个指定动物园的所有动物
  • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

  • ?limit=10:指定返回记录的数量
  • ?offset=10:指定返回记录的开始位置。
  • ?page=2&per_page=100:指定第几页,以及每页的记录数。
  • ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  • ?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT - [DELETE]:用户删除数据成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*]:表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH]:当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里。

错误处理(Error handling)

如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

1
2
3
{
error: "Invalid API key"
}

返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • PATCH /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档

Hypermedia API

RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。

1
2
3
4
5
6
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}

上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

1
2
3
4
5
{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

1
2
3
4
{
"message": "Requires authentication",
"documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。

其他

  1. API的身份认证应该使用 OAuth 2.0 框架。
  2. 服务器返回的数据格式,应该尽量使用 JSON,避免使用 XML。

转自:http://blog.csdn.net/newjueqi/article/details/44037011

app和后端的交互,一般都是通过后端提供的api实现。api的设计,估计很多刚进入app后端的小伙伴会一无头绪,不知道怎么入门。下面根据自己3年的app后端经验,总结出下几个api设计原则,给小伙伴参考。

什么是api?

这个问题在以前发表的文章“7.app和app后端的通讯”中其实已经回答了,这里再重复一次。

相信大家都用过银行的柜员机(ATM)的查询余额,转帐,取款等操作。

当在柜员机取款的时候,我们输入要取款的金额,隔一会钱就出来了,如果因为有什么问题不能取款(例如超过取款金额的限制),屏幕上也会显示出错误的信息。

在整个过程中,我们只要输入金额,获得结果(取款成功或不成功),就行了,至于柜员机内部是怎么处理,我们不需要理会。  柜员机这种把内部的处理遮蔽的做法极大方便了我们的使用。

同样的,在后端,也只提供了一系列的功能给app使用,这系列的功能以api的形式提供。

api的定义:API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

当app调用api的时候,只需要明确下面3点:

  1. 这个api是干啥的(柜员机例子中,是取款功能,还是查询余额,还是转账)
  2. 知道要输入什么(柜员机例子中,取款要输入金钱)
  3. 知道结果是什么(柜员机例子中,取款是成功还是失败)

至于api内部是怎么处理的,app根本无需理会。

从这里可看出,api能在最大程度遮蔽了app后端复杂性,极大提高了app前端的开发效率。

api设计的8点

Restful设计原则

Restful风格:RESTfu设计原则,它被Roy Felding提出(在他的”基于网络的软件架构“论文中第五章)。而REST的核心原则是将你的API拆分为逻辑上的资源。这些资源通过http被操作(GET ,POST,PUT,DELETE)。

在实际的开发过程中发现,程序员由于在web端养成的习惯,api操作中通常就只有两种方式”POST””GET”。大家可看一下微博的api例子”statuses/destroy”,这个很明显是delete操作的api却是用post方式提交。不是完全遵守Restful风格的风格。

这个设计原则最简单的应用就是根据object而不是页面来设计api。最开始的时候,app的一个页面需要什么数据,api就返回什么数据。结果随着app的UI不断改版,需要的数据不断变化,不停地修改api,最后当api的改动会影响以前的版本的时候,只能写一个新的api版本,最后弄得api中有很多V2,V3这样的标志,恶梦!

后来在网站的重构过程中,就根据object来设计api,但根据object来设计,又有一个问题,一个大object可能包含很多小object,是一个api返回全部小object,还是分为多个api返回?根据业务和技术,带宽等仔细考虑吧。

api的命名

一看api名字就知道这个api是干啥。在创业团队中,一般就只有一两个人负责后台,当你要负责几十甚至上百个api,你就知道不能“望名知api”是个什么样的痛苦。

api的命名,我是挺喜欢微博的命名风格,例如删除微博的api “statuses/destroy”,第一个是对象,第二个是对象的操作删除

api的安全性

这点会在以后的“怎么保证app通讯的安全性”一文中详细论述。

api返回数据

app客户端的语言 java 和object-c都是强类型语言,所以怎么处理空值显得特别重要,不合理的设计很容易造成app的闪退。

从后台的角度来说,api中返回的数据中,正确值和空值的类型必须一样,举例,用户名的字段是“realname”: “xxx”,如果用户名为空,则应该返回“realname”: “”。如果返回值是一个array,空数据则返回一个空array,绝对禁止null值。

对于客户端,必须用个全局的函数来处理所有api的返回数据,需要有一个机制:对于某个客户端需要数据,如果api中缺失,客户端自动补上并给予默认值。这个机制在我们的实践中大大减少了app的闪退。

同时,在数据库设计的时候,一个合理的设计必须是所有字段都有默认值,不应该允许null值。null在大量的语言和数据库中,会带来无穷的问题。对于这个数据库设计原则,我以前不太明白,现在经历了一年的api设计后,终于懂得。

如果客户端是php,还有一个问题,php中数组和字典都是array,但在java 和object-c中是不一样,这个问题一定要注意。

图片的处理

在不同版本的app中,各种不同尺寸的手机中,同一张图片显示的尺寸可能是不一样,如果每次都需要用返回原图,然后在客户端处理,则极大浪费网络资源。而如果是后台处理好图片才返回,则又是一个挑战,怎么有效保存和裁剪多种图片尺寸呢

例如,一开始头像只需要返回6060的尺寸,后来在新的版本需要返回7070, 又出了一个新版本,需要返回80*80, 每次增加一个新的尺寸,怎么在数据库上记录下来。这个问题在一开始做api的时候没考虑,后来不得不用了一个极端的方法,没增加新的图片尺寸,就在数据库中增加一个新的字段,保存并生成新的图片尺寸,结果最后数据库的头像字段有”avatar”,”avatar_60_60”,”avatar_70_70”,”avatar_80_80”,这种极度恶虐的设计。

最后,针对图片,我们才用了这样的策略:

  1. 客户端本地缓存图片,只有没有合适的图片,才去服务器取。
  2. 当客户端需要某种尺寸的图片,由客户端告诉服务端图片的尺寸,服务端动态生成并缓存起来。

例如,客户端需要图片(http://www.baidu.com/img/bdlogo.gif)的80*80的尺寸,则在图片的路径加上宽和高的参数(类似于CDN的机制) http://www.baidu.com/img/bdlogo.gif?w=80&h=80, 则服务器就生成80*80的尺寸并返回。

采用了这样的图片处理机制,数据库中只要有一个字段保存原图就行了,其它尺寸就由客户端告诉服务端动态生成。以后无论什么尺寸的图片,数据库中都不需要记录,数据库只有原图就行了。

注意,现在的文件云存储服务(例如七牛,又拍云)等都提供了这个文件的缩放功能,而且能加速文件的上传下载速度,极大提升了app的用户体验,强烈推荐使用。

返回的提示信息

最科学的情况,服务端只返回信息代码,具体的文字提示由客户端决定。

如果文字信息是由服务端返回,则最起码要区分2种信息:提示用户的信息,提示客户端程序员的信息。这两者的区别:

  1. 提示用户的信息是要在让客户知道的,提示客户端程序员的信息不需要让客户知道的。
  2. 提示用户的信息文字很友好,客户不需要专业基础一看就知道是什么,提示客户端程序员的信息则很专业,例如告诉客户端少传了哪个参数?哪个参数有问题等等。

在线api测试文档

我们网站的api在线测试文档,是使用既是一份在线api文档,也是一个在线测试工具,极大方便沟通和测试。每次客户端程序员觉得某个api有什么问题,我们就是这个在线工具上讨论沟通的。客户端程序员最喜欢这个玩意了^-^。

这个api在线测试文档,是使用了Swagger-UI搭建的。Swagger-UI简单而一目了然。它能够纯碎的基于html+javascript实现,只要稍微整合一下便能成为方便的API在线测试工具。项目的设计架构中一直提倡使用TDD(测试驱动)原则来开发,swagger-ui在这方面更是能提供很大帮助。

下面是用Swagger-UI搭建的api文档中的一个api的例子,可看到,整个api的提交方式,作用,参数都非常清晰明了。

swagger输入

当按了“测试”,就以post方式调用这个api,返回的结果如下:

swagger输出

所有的返回结果,一目了然,cool!!!

api返回的数据,是以json格式返回的。用json格式,最省流量,而且几乎每种计算机语言都支持json格式。用xml的话,太耗费流量了,而且冗余数据多,不适合移动端。

在”app后端”的qq群,有个app创始人使用了这个api在线测试文档后赞不决口,称赞虽然前期的搭建需要花一段时间,但极大提高了app前后端工作的效率。以后有小伙伴问相关的问题,他都强烈推荐这个Swagger-UI。

在app启动时,调用一个初始化api获取必要的信息

通过这个初始化api,获取一下必要的信息,例如,最新的app版本。当发现本地app的版本已经低于最新的app版本,可提示用户更新。当然了,这个提示版本更新的功能很多第三方sdk都提供。

如何处理api的版本升级

当app做了大改版后,可能会出现一个问题,发现现在的api已经不适了,就考虑到api的升级,同时为了兼容已经发布的app,原来的api必须要保留。为了避免同一个app中调用不同版本的api,一般就会全部升级api的版本,例如:原来的是“test.com/v1/statuses/destroy”,升级为“test.com/v2/statuses/destroy”。

在api的版本升级时,需要注意以下2点:

  1. v2版本的api的controller必须要继承v1版的controller,v2版本的api只重写需要改动的api。
  2. 在线api测试文档中详细标明返回内容,已作对比,方便客户端人员的调试。

  1. @Autowired@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

  2. @Autowired默认按 类型 装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:

    1
    2
    @Autowired() @Qualifier("baseDao")    
    private BaseDao baseDao;
  3. @Resource是JDK1.6支持的注解,默认按照 名称 进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名,按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

  4. @Autowired属于Spring的;@Resource为JSR-250标准的注释,属于J2EE的。

PX为单位

在Web页面初期制作中,我们都是使用“px”来设置我们的文本,因为他比较稳定和精确。但是这种方法存在一个问题,当用户在浏览器中浏览我们制作的Web页面时,他改变了浏览器的字体大小,这时会使用我们的Web页面布局被打破。这样对于那些关心自己网站可用性的用户来说,就是一个大问题了。因此,这时就提出了使用“em”来定义Web页面的字体

Em为单位

这种技术需要一个参考点,一般都是以的“font-size”为基准。比如说我们使用“1em”等于“10px”来改变默认值“1em=16px”,这样一来,我们设置字体大小相当于“14px”时,只需要将其值设置为“1.4em”。

1
2
3
body{ font-size:62.5%;}  /*10/16*100%*/  
.font1{ font-size:1.2em;}  
.font2{ font-size:12px;}  

Rem为单位

CSS3的出现,他同时引进了一些新的单位,包括我们今天所说的rem。

前面说了“em”是相对于其父元素来设置字体大小的,这样就会存在一个问题,进行任何元素设置,都有可能需要知道他父元素的大小,在我们多次使用时,就会带来无法预知的错误风险。而rem是相对于根元素<html>

1
2
3
html{font-size:62.5% ;}  
.fon1{ font-size:1.2rem;}  
.fon2{ font-size:12px;}  

参考下图:

Rem对照表

但也想兼容IE下的效果,可你可考虑“px”和“rem”一起使用,用”px”来实现IE6-8下的效果,然后使用“Rem”来实现代浏览器的效果

出于数据安全考虑,对 Oracle 数据库的 IP 做一些限制,只有固定的 IP 才能访问。

  1. 修改$JAVA_HOME/NETWORK/ADMIN/sqlnet.ora文件
  2. 增加以下内容(红色表示注释):
1
#开启ip限制功能tcp.validnode_checking=yes#允许访问数据库的IP地址列表,多个IP地址使用逗号分开tcp.invited_nodes=(192.168.1.110)#禁止访问数据库的IP地址列表,多个IP地址使用逗号分开tcp.excluded_nodes=(192.168.1.111)
  1. 然后重启监听即可。

注:

  1. 第一行必需写,任何平台都可以,但是只适用于 TCP/IP。
  2. 第二行和第三行任写一行即可,如果 tcp.invited_nodes 与 tcp.excluded_nodes 都存在,以 tcp.invited_nodes 为主。
  3. 不要禁止服务器本机的 IP 地址,否则通过 lsnrctl 将不能启动或停止监听,因为该过程监听程序会通过本机的 IP 访问监听器。

转自:http://blog.csdn.net/cpf2016/article/details/50150205

区别

可以接受的任务类型

  • execute只能接受Runnable类型的任务
  • submit不管是Runnable还是Callable类型的任务都可以接受,但是Runnable返回值均为void,所以使用Future的get()获得的还是null

返回值

由Callable和Runnable的区别可知:

  • execute没有返回值
  • submit有返回值,所以需要返回值的时候必须使用submit

异常

  • submit()能在返回的Future对象调用get()方法的时候再次抛出线程中的异常
  • execute()会交由线程的UncaughtExceptionHandler去处理。

execute中抛出异常

execute中的是Runnable接口的实现,所以只能使用try、catch来捕获CheckedException,通过实现UncaughtExceptionHande接口处理UncheckedException

即和普通线程的处理方式完全一致

submit中抛出异常

不管提交的是Runnable还是Callable类型的任务,如果不对返回值Future调用get()方法,都会吃掉异常

先来看下Callable定义

1
2
3
public interface Callable<V> {  
V call() throws Exception;
}

其中的call能够抛出Exception异常,所以不管是CheckedException还是UncheckedException,直接抛出即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.concurrent.Callable;  
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadExceptionTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();

Future<Boolean> future = executor.submit(new CallableTask());
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}

executor.shutdown();//必须不能忘,否则会一致卡在那里
}
}

class CallableTask implements Callable<Boolean>{
public Boolean call() throws Exception {
// InputStream in = new FileInputStream(new File("xx.pdf"));
int num = 3/0;
return false;
}

}

结果为:

1
2
3
4
5
6
7
8
9
10
11
12
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero  
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at my.ThreadExceptionTest.main(ThreadExceptionTest.java:21)
Caused by: java.lang.ArithmeticException: / by zero
at my.CallableTask.call(ThreadExceptionTest.java:57)
at my.CallableTask.call(ThreadExceptionTest.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)