购买云服务器这么久,直到一年快过期了才勉强搭建起来,说起来很是内疚,原计划使用Java书写后台,使用angular搭建一个web版单页面应用,同时使用Ionic做一个App应用。理想中是很美好的,可能就是当初想的太大了,加上平时在公司主要负责App的前端工作,工作也十分忙碌,使用Java搭建进展缓慢,既然平时都是使用JavaScript语言, 为什么不干脆使用Node作为后台呢。于是就有了现在的个人主页。
数据存储
nosql
优点:nosql数据库缓解服务器压力,针对频繁查询的数据
文档存储
类似于JSON,主要有mongodb数据库
内存数据库
- redis 数据持久化+分布式session
- memcached 分布式缓存
sql
- mysql
- sqlserver
- oracle
比较
mysql数据库
mysql是持久化存储,存放在磁盘里面,检索的话,会涉及到一定的IO。
memcached数据库
缓存机制,用户访问mc,如果未命中,就去访问mysql,之后像内存和硬盘一样,把数据复制到mc一部分。
redis数据库
redis和mc都是缓存,并且都是驻留在内存中运行的,这大大提升了高数据量web访问的访问速度。然而mc只是提供了简单的数据结构,比如string存储;redis却提供了大量的数据结构,比如string、list、set、hashset、sorted set这些,这使得用户方便了许多,毕竟封装了一层实用的功能,同时实现了同样的效果,当然用redis而慢慢舍弃mc。
内存和硬盘的关系,硬盘放置主体数据用于持久化存储,而内存则是当前运行的那部分数据,CPU访问内存而不是磁盘,这大大提升了运行的速度,当然这是基于程序的局部化访问
原理。
redis+mysql,它是内存+磁盘关系的一个映射,mysql放在磁盘,redis放在内存,这样的话,web应用每次只访问redis,如果没有找到的数据,才去访问Mysql。
hbase
Hbase是运行在Hadoop上的NoSQL数据库,它是一个分布式的和可扩展的大数据仓库,也就是说HBase能够利用HDFS的分布式处理模式,并从Hadoop的MapReduce程序模型中获益。
文件系统
fastdfs
开源的,高性能的的分布式文件系统,他主要的功能包括:文件存储,同步和访问,设计基于高可用和负载均衡,FastDFS非常适用于基于文件服务的站点,例如图片分享和视频分享网站。
hdfs
Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。
Java篇
SpringMVC restful
原计划书写一套RESTFUL API,因此Spring 4.0新增了一个rest注解。
springmvc的restful是通过@RequestMapping 及@PathVariable annotation提供的。
@PathVariable取url参数,不同于@requestParam取查询串参数。
spring4.0新增@RestController
此注解避免了每个方法都要加上@ResponseBody注解。也就是说@RestController自己戴上了@ResponseBody注解,看以看作是@Controller和@ResponseBody的结合体。
经过测试:
在springmvc中@RequestParam不支持multipart/form-data类型,同时不支持put和delete的提交方式。
@RequestBody支持post,put和delete!
思考: springMVC如何一次接受多个对象,并自动将json转成javaBean?
- 同样使用@RequestBody,构建内部类来达到接受多个参数目的!
- 自定义注解
- ...
跨域问题
如果不实用Nginx做代理转发的话,也可以使用过滤器解决跨域问题。
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("Filtering on...........................................................");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
Node基础
环境与工具
node环境
- n(node版本管理)
- nrm(管理npm源的工具)
npm i nrm –g nrm use cnpm
mongodb环境
- Robomongo(MongoDB 可视化管理工具)
环境变量
在 Mac 和 Linux 的终端直接输入 env,会列出当前的环境变量。
在node代码中,可通过 process.env 来获取环境变量。应用场景很多,比如代码中根据运行环境区分正式环境和开发环境
。
初始化项目
npm init
npm install xxx –save
require & export
require
- require 用来加载一个文件的代码.
- require 可加载 .js、.json 和 .node 后缀的文件.
- require 的过程是
同步的
. - require 目录的机制是:
- 如果目录下有 package.json 并指定了 main 字段,则用之
- 如果不存在 package.json,则依次尝试加载目录下的 index.js 和 index.node
- require 过的文件会加载到缓存,所以多次 require 同一个文件(模块)不会重复加载
- 判断是否是程序的入口文件有两种方式:
require.main === module(推荐) module.parent === null
exports
与此类似的还有个module.exports,区别如下:
- module.exports 初始值为一个空对象 {}
- exports 是指向的 module.exports 的引用
- require() 返回的是 module.exports 而不是 exports
promise
构造函数语法
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
即使只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数。
then方法
接受两个函数,分别表示resolve和rejected时的回调函数。在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了。
Catch方法
效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。
All方法
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then。而且顺序是按照数组中promise顺序进行组装。
有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷!
Race方法
all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词本来就是赛跑的意思。
这个race有什么用呢?使用场景还是很多的,比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。
优势
与回调函数比较的优势:
- 使用回调函数可以解决单一异步的问题,如果有多个异步,多层回调的话就会显得力不从心。而Promise的优势在于,可以在then方法中
继续写Promise对象并返回,然后继续调用then来进行回调操作
。
深入
以上只是对用法进行简单的说明,深入学习前往,Promise
路由
基础知识
app.get(path, function(req, res) {
res.send('hello, express');
});
请求方式:get,post等
占位符:/users/:name,name此时表现为占位符
常用req属性
有些需要安装依赖模块才能使用。
- req.query: 解析后的 url 中的 querystring,如 ?name=haha,req.query 的值为 {name: 'haha'}
- req.params: 解析 url 中的占位符,如 /:name,访问 /haha,req.params 的值为 {name: 'haha'}
- req.body: 解析后请求体,需使用相关的模块,如 body-parser,请求体为 {"name": "haha"},则 req.body 为 {name: 'haha'}
- req.fields:表单
- req.files:文件上传
常用res方法
- res.redirect(path)
- res.redirect(‘back’)
- res.redirect(
/posts/${post._id}
);ES6写法 - res.render
路由管理
实际开发中通常有几十甚至上百的路由,都写在 index.js 既臃肿又不好维护,这时可以使用 express.Router 实现更优雅的路由解决方案。
每个路由文件通过生成一个 express.Router 实例 router 并导出,通过 app.use 挂载到不同的路径。
var router = express.Router();
router.get('/:name', function(req, res) {
res.send('hello, ' + req.params.name);
});
module.exports = router;
var userRouter = require('./routes/users');
app.use('/users', userRouter);
模板引擎
安装ejs
npm i ejs –save
设置
app.set('views', path.join(__dirname, 'views'));// 设置存放模板文件的目录
app.set('view engine', 'ejs');// 设置模板引擎为 ejs
渲染
通过调用 res.render 函数渲染 ejs 模板,res.render 第一个参数是模板的名字,第二个参数是传给模板的数据。es.render 的作用就是将模板和数据结合生成 html,同时设置响应头中的 Content-Type: text/html,告诉浏览器我返回的是 html,不是纯文本,要按 html 展示。
常用标签
- <% code %>:运行 JavaScript 代码,不输出
- <%= code %>:显示转义后的 HTML内容
- <%- code %>:显示原始 HTML 内容
- 区别:<%= code %> 会原样输出
<h1>hello</h1>
,而 <%- code %> 则会显示 H1 大的 hello 字符串。
模板复用
include函数:<%- include('header') %>
express
中间件
express 中的中间件(middleware)就是用来处理请求的
,当一个中间件处理完,可以通过调用 next() 传递给下一个中间件,如果没有调用 next(),则请求不会往下传递,如内置的 res.render 其实就是渲染完 html 直接返回给客户端,没有调用 next(),从而没有传递给下一个中间件。
通过 app.use 加载中间件,在中间件中通过 next 将请求传递到下一个中间件,next 可接受一个参数接收错误信息,如果使用了 next(error),则会返回错误而不会传递到下一个中间件。
express 有成百上千的第三方中间件,在开发过程中我们首先应该去 npm 上寻找是否有类似实现的中间件,尽量避免造轮子,节省开发时间。
注意:中间件的加载顺序很重要!
比如:通常把日志中间件放到比较靠前的位置,后面将会介绍的 connect-flash 中间件是基于 session 的,所以需要在 express-session 后加载。
如设置静态文件目录的中间件应该放到 routes(app) 之前加载,这样静态文件的请求就不会落到业务逻辑的路由里;flash 中间件应该放到 session 中间件之后加载,因为 flash 是基于 session 的。
允许跨域
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By",' 3.2.1')
if(req.method=="OPTIONS") res.send(200);/*让options请求快速返回*/
else next();
});
res常用方法
- res.set(field,[value]) 设置请求头的字段为指定的值,或者通过一个对象一次设置多个值。
- res.get(field) 获取不区分大小写的响应头 “field”(字段)。
- res.cookie(name,value,[options]) 设置cookie name 的值为 value,接受参数可以一个字符串或者是对象转化成的JSON,path 默认设置为’/’。
- res.clearCookie(name,[options])
- res.redirect([status],url)
- res.location
- res.send([body|status],[body])
- res.json([status|body],[body])
- res.type(type)
- res.format(object)
- res.sendfile(path,[options],[fn])
- res.download(path,[filename],[fn])
- res.render(view,[locals],callback) 渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。