keep going with us.

yf-fpm-server 是一个面向小微团队产品开发的前后端分离框架。

让你们少走一些弯路
前后端分离框架可以解决的问题
  • 支持异构项目的集成【App与管理平台数据交互】
  • 前后端分工更明确
  • 提高前端开发效率【mock】
  • 减少后端运维成本

对于一个小微型的产品研发团队来说,这些问题通常交给一个全栈工程师去解决的;而随着产品的不断迭代,这些问题会不断产生新的问题,通常意义上的填坑过程。


一个健康的产品研发过程应该在一开始就考虑前后端分离的事情。

控制成本
node.js
  • 使用javascript语法,对web开发者来说已经驾轻就熟
  • 运行在服务端,依赖V8引擎和C++的天然优势,它可以承受少量并发
  • 近2年来在开源社区很活跃,被大部分公司深度应用,并开源了一些产品

团队引入nodejs是一个如虎添翼的过程,加以合理利用,它可以解决95%的事情,也就意味着它能快速的将生产力转化成输出。

即使在小的团队也要尽可能的快速迭代

多版本同时在线
清晰的架构
  • 框架研发之初,团队面临的最大的问题就是应对快速迭代
  • 框架已被用于多个项目的生产环境,最长的已运行2年多
  • 通过2年多线上运营过程,不断的升级和优化
  • 代码开源,核心代码简洁,通过插件无侵入的扩展功能和模块

与引入资本的团队不同是,初创阶段,团队选择框架更看中其上手难度和维护成本,一个合适的框架可以减少很多工作。

采用taobao和jd的开放平台的设计方案,定义统一的入口,通过参数定位业务接口,实现灵活的业务开发。

统一入口
提高业务实现效率
  • 提供一个/api的路由来处理业务请求
  • 非restful范式,不用维护冗长的路由
  • 统一的全局异常处理
  • 丰富的客户端SDK
Read More

框架设计之初为了提供一个相对标准的api入口,节约项目开发的前后端沟通成本,前端可以通过已实现好的sdk库可以很方便的与后端进行数据交互

yf-fpm-server 框架一直在做的就是:

  • 如果你是个人,小规模,实力不强,但可以使用nodejs,想做一个规模不是很大的项目,这个框架就很适合你(们)。它是个能将项目从初期到中后期过度的很好的框架。当然,我们是实践了若干的商业项目之后才会如此力荐的。
  • 充分结合nodejs的特性和koa框架的简洁,不断的精简和优化代码

    • 最终的依赖:
      1
      2
      3
      4
      5
      6
      "koa": "^2.0.0",
      "koa-bodyparser": "^3.2.0",
      "koa-router": "^7.1.0",
      "koa2-cors": "^2.0.3",
      "lodash": "^4.16.1",
      "pubsub-js": "^1.5.5"
  • 一些变动的api

    • extendModule(name, module, version) 该函数多用于开发插件的过程中很快的注入业务函数
    • getPlugins() 获取已安装的插件信息
    • isPluginInstalled(name) 检测是否已安装了某个插件
    • _counter 该字段用于记录api被调用的次数
    • _prject_info 该字段包含了项目的package.json信息
    • _start_time 该字段记录了项目启动的时间戳 通过 _.now() 生成

    WARNING:这里有一些重要的变化,对于所有的业务函数增加了2个参数

    1
    2
    3
    4
    5
    6
    /*
    * args: 前端传来的业务参数
    * ctx: koa 的上下文
    * before: 前置钩子函数执行的结果,通过是个 Array
    */
    (args, ctx, before) => { //.. }

    再此之前是没有后面2个参数的

    1
    (args) => { //.. }

    需要注意的是,一些通过插件调用了系统的fpm.execute()函数,通常没有ctx参数,所以需要做个谨慎的非空判断

  • 利用插件的机制将非必要的功能拆分到插件库中,项目开发过程中结合业务添加插件到系统中即可,已实现的插件有:


    除此之外,还有一些插件正在开发过程中,比如用于后台查看的 fpm-plugin-admin,方便用户直观的查看系统运行的状态。

Read More

本次更新主要是移除了一些内核中可插件化的代码,并新增了2个插件 fpm-plugin-qiniu-upload,fpm-plugin-socketio 。2.2.3版本将是一个lts版本。

依赖变更

  • remove qiniu

最新的依赖

1
2
3
4
5
6
7
8
9
"koa": "^2.0.0",
"koa-bodyparser": "^3.2.0",
"koa-multer": "^1.0.1",
"koa-router": "^7.1.0",
"koa2-cors": "^2.0.3",
"lodash": "^4.16.1",
"md5": "^2.1.0",
"moment": "^2.13.0",
"pubsub-js": "^1.5.5"

移除的特性

  • remove upload 的路由
    1
    this.app.use(upload.routes()).use(upload.allowedMethods())

插件

  • 上传文件到七牛的插件 fpm-plugin-qiniu-upload

  • 集成socketio服务插件 fpm-plugin-socketio

  • 已实现的可用插件列表

    1
    2
    3
    4
    5
    6
    "fpm-plugin-baidu": "^0.0.1",
    "fpm-plugin-emailer": "^1.0.3",
    "fpm-plugin-mysql": "^1.0.1",
    "fpm-plugin-qiniu-upload": "^1.0.0",
    "fpm-plugin-schedule": "^1.0.0",
    "fpm-plugin-socketio": "^0.0.2",
Read More

本次版本更新主要针对文件上传下载进行了初步的实现。

增加的依赖

新的特性

  • 添加新的 /upload , /download/:hash 路由
  • 限制文件类型
    • zip
    • json
    • txt,log等文本类型
  • 限制文件大小
    • 2 x 1024 x 1024 (B)

完整代码

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import _ from 'lodash'
import Router from 'koa-router'
import multer from 'koa-multer'
import fs from 'fs'
import E from '../error.js'

const router = Router()

//将上传的信息保存在内存中
var datas = {}

// 上传的文件类型限制
const ALLOW_MIMETYPES = ['application/octet-stream', 'application/zip', 'application/x-zip-compressed']

const fileFilter = (req, file, cb) =>{
if(_.indexOf(ALLOW_MIMETYPES, file.mimetype)> -1){
cb(null, 1)
}else{
cb(E.Upload.TYPE_NOT_ALLOWD)
}
}

const upload = multer({ dest: 'uploads/' , fileFilter: fileFilter, limits: { fileSize: 2 * 1024 * 1024 }})

// 上传表单以file为文件的字段
const defaultHandler = upload.single('file')

// 捕获异常
const handler = async (ctx, next) => {
try{
await defaultHandler(ctx, next)
}catch(e){
ctx.error = e
}
if(ctx.error){
ctx.fail(ctx.error)
}else{
let data = ctx.req.file
datas[data.filename] = data
ctx.success({data: {hash: data.filename, url: '/download/' + data.filename}})
}
}

// 上传文件的路由
router.post('/upload', handler)

// 下载文件的路由
router.get('/download/:id', async (ctx, next) => {
let data = datas[ctx.params.id]
ctx.type = data.mimetype
ctx.attachment(data.originalname)
ctx.body = await fs.createReadStream(data.path)
await next()
})

export default router
Read More
  • 1.合并analyse.js 和 auth.js代码
  • 2.增强权限匹配的模式
  • 3.添加系统异常捕捉绑定函数
  • 4.去除2个多余的函数
  • 5.新增2个钩子埋入点

1.合并analyse.js 和 auth.js代码

将2个中间件的代码合并到auth.js中,在auth中间件中验证key和root权限

2.增强权限匹配的模式

使用正则表达式来匹配接口权限

1
2
3
4
5
6
7
8
let method = postData.method
// 使用正则表达式来匹配信息
let root = '^' + approot + '$'
// 涵盖权限
if(new RegExp(root).test(method)){
await next()
return
}

如:

  • order.* 可授权order模块下的所有接口权限
  • app.* 可授权app模块下的所有接口权限
  • (order|app).* 可授权 order 和 app 两个模块下所有的接口
  • * 可授权所有权限

3.添加系统异常捕捉绑定函数

在 fpm 的核心api中添加 bindErrorHandler(handler) 绑定koa的系统错误回调。

1
2
3
bindErrorHandler(handler){
this.errorHandler = handler
}

4.去除2个多余的函数

删除了 use(middleware) , addRouter(routers, methods) 2个多余的函数。

5.新增2个钩子埋入点

添加了 FPM_MIDDLEWARE , FPM_ROUTER 2个钩子埋入点,可在插件开发中利用,可为fpm绑定更多的路由和中间件。

1
2
this.runAction('FPM_MIDDLEWARE', this, this.app)
this.runAction('FPM_ROUTER', this, this.app)

除了传入 fpm , 还会将koa的引用一起传入到插件的 bind() 函数中。

Read More
post @ 2017-03-01
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
32
// 1.获取app,即koa对象
getApp()

// 2.添加业务模块,要求biz为 Biz 类型
addBizModules(biz)

// 3.注册系统级action钩子,传入可用的action钩子名和钩子代码
registerAction(actionName, action)

// 4.执行系统级钩子
runAction(actionName)

// 5.直接执行业务代码,业务函数名,参数,版本号
async execute(method, args, v)

// 6.添加业务函数的钩子,要求hook为 Hook 类型
addHook(hook)

// 7.获取config.json中的配置信息,或通过插件注入的配置信息
getConfig(c)

// 8.扩展config.json的配置信息,注意:此函数只会将信息暂存到内存中,重启之后会失效
extendConfig(c)

// 9.获取配置信息中的所有的客户端授权信息
getClients()

// 10.绑定系统错误函数
bindErrorHandler(handler)

// 11.启动服务
run()
Read More

0. 什么是yf-fpm-server

yf-fpm-server是一款轻量级的api服务端,可通过插件集成数据库(mysql,mongodb)的数据操作,灵活扩展自定义业务逻辑


BETA版更新概要

  • 1.更新依赖
  • 2.支持插件集成
  • 3.添加config.json静态配置文件
  • 4.文件改动
  • 5.示例代码

1. 更新依赖

为了让核心代码更轻便,去除了很多臃肿的依赖;除了Babel相关的以外,最终依赖如下一些模块:

1
2
3
4
5
6
7
8
9
{
"koa": "^2.0.0",
"koa-bodyparser": "^3.2.0",
"koa-router": "^7.1.0",
"koa2-cors": "^2.0.3",
"lodash": "^4.16.1",
"md5": "^2.1.0",
"moment": "^2.13.0"
}

2. 支持插件集成

在一些主要的操作节点上添加了钩子,可进行插件开发集成。

钩子列表如下:
  • INIT 服务初始化时
  • BEFORE_ROUTER_ADDED 路由添加之前
  • AFTER_ROUTER_ADDED 路由添加之后
  • BEFORE_MODULES_ADDED 业务模块添加之前
  • AFTER_MODULES_ADDED 业务模块添加之后
  • BEFORE_SERVER_START 服务启动之前
  • AFTER_SERVER_START 服务启动之后
默认实现的插件包括:

(点击可进入github)

Read More

为什么需要插件扩展?

  • 模块化思想

任何一个易用的系统都强调易用,易维护,模块化是nodejs的核心思想(显然并不是nodejs首先提出的);nodejs有很多优秀的package都支持插件式开发:hexo(非常流行的静态博客生成工具),egg(ali团队推出的企业级开发框架),etc…


当然模块化不仅仅限于使用插件化的方式;还有node web开发中非常常见的中间件模式。


总之,无论是oop、中间件、插件化这些设计模式都是为了更加解耦、更加抽象、更加易读、易用、易维护。

  • 扩展更加便捷

好的设计模式可以做到不需要对现有的系统代码做任何修改,对现有代码无侵入的情况下做到功能拓展。插件机制就是一种很常见的设计模式,可以用最小的成本满足大部分的应用场景。

  • 核心代码更纯粹

插件可以随时添加,任意添加,对已封装的核心代码没有任何改变,核心代码更加纯粹,保持最精简的状态,极大程度的避免了快速迭代过程中造成核心崩溃的情况。

如何实现插件扩展机制?

Read More

最近完成了一个ionic项目,服务端是用的websocket,之前还没应用过ws来做前后端的交互;经过这个项目的实践,将这段代码进行封装,并做简要的说明。

1.添加依赖[$websocket]

这个不多说,这个模块是ng1中使用websocket的不二之选,官方文档也很详细:传送门

官方实例是这么写的:

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
angular.module('YOUR_APP', [
'ngWebSocket' // you may also use 'angular-websocket' if you prefer
])
// WebSocket works as well
.factory('MyData', function($websocket) {
// Open a WebSocket connection
var dataStream = $websocket('ws://website.com/data');

var collection = [];

dataStream.onMessage(function(message) {
collection.push(JSON.parse(message.data));
});

var methods = {
collection: collection,
get: function() {
dataStream.send(JSON.stringify({ action: 'get' }));
}
};

return methods;
})
.controller('SomeController', function ($scope, MyData) {
$scope.MyData = MyData;
});

如果你的项目中没有复杂的交互,仅仅是在一个页面中存取数据,这样已经足够了。

但往往项目稍微复杂的应用都无法通过这个来满足。

以下是我个人的简单实现;也仅仅是一种思路的实现,仅供参考 :D

Read More
⬆︎TOP