鄢倩

(conj clojurians me)

Promise对象最早被C++工程师使用,接着以Deferred对象出现在Python中。随着NodeJS的兴起,Promise在javascript中的应用越来越广。

大凡技术成熟之后都会逐渐形成一个标准,Promise的标准定义在CommonJS中,全称是Promise/A,不过新近出现了改良版的Promise/A+,只不过它仅仅对原来的标准部分澄清,同时依据实践稍作扩展。

The core Promises/A+ specification does not deal with how to create, fulfill, or reject promises, choosing instead to focus on providing an interoperable(协作的) then method. Future work in companion specifications may touch on these subjects.

但是使用Promise还是有些地方值得留意的。

1. Then的Resovler Function不返回任何值


举个例子,打印deferred数组的首个元素

promise_sample.js
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
function printFirstAndLast(itemsDeferred){
findFirst(itemsDeferred).then(util.puts); //=> 0

last(itemsDeferred).then(function(last){
var deferred = defer();
deferred.resolve(last);
findFirst(deferred).then(util.puts) //=> 1
}).then(function(result){
console.log('result:', result); //=> 2
}, function(reason){
console.log('reason:', reason);
});
}

function last(itemsDeferred) {
return itemsDeferred.then(function(items){
return items.slice(1, items.length);
});
}

function findFirst(itemsDeferred){
return itemsDeferred.then(function(items){
return items[0];
});
}
---
var deferred = defer();
deferred.resolve([1, 2, 3, 4, 5]);
printFirstAndLast(deferred);

->
1
result: undefined
2

从输出看,由于我故意没有返回1处的Promise对象,最后一个then 的2处代码先被执行,打印出undefined,然后才会执行完1处的代码,打印出数字2。

这样的现象有两处值得注意的地方

  • 执行顺序混乱
  • 最后then的结果是undefined

顺序混乱是由于标识为1处的resolve的时间较长,回调的时间较久,所以后执行。

2处为undefined,是因为1处没有返回值,我们知道javascript的function始终会返回值且这两种方式:return;(返回void)和不显式写return语句都会返回undefined。所以将值为undefined的promise对象传给下一个then方法。

2. Then的Rejector Function不返回Reject Promise


reject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function printFirstAndLast(itemsDeferred){
return itemDeferred.then(function(){}, function(reason){
util.puts('1:'+reason);
return 'error-1';
}).then(function(result){
util.puts('resolve 2:'+result);
}, function(reason){
util.puts('reject 2:'+reason);
});
}

var deferred = defer();
deferred.reject('error-0');
printFirstAndLast(deferred);

->
1: error-0
resolve 2:error-1

从输出看,如果reject function不返回reject promise,而只是error-1这样的值,那么调用then方法,执行的还是resolve方法。

从Promise/A+参考描述可证明上述

promise2 = promise1.then(onFulfilled, onRejected);

If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).

If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.

3. Then的Resolver Function返回的是对象,而且包含then方法


obj_then.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var obj = {then: function(resolver, reject){
return resolver('i am an obj.');
}};

function printFirstAndLast(itemsDeferred){
return itemDeferred.then(function(){}, function(reason){
util.puts('1:'+reason);
return obj; //=> 1
}).then(function(result){
util.puts('resolve 2:'+result);
}, function(reason){
util.puts('reject 2:'+reason);
});
}

var deferred = defer();
deferred.reject('error-0');
printFirstAndLast(deferred);

->
1: error-0
resolve 2:i am an obj

从结果看,obj这个含有then方法的对象,在1处被调用了then,同时将resolver('i am an obj.')作为Promise返回。

从Promise/A+可知

If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:

If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)

If/when rejectPromise is called with a reason r, reject promise with r

那么如果是以下这些对象

  • {then: ‘something’}
  • {then: function(){}}
  • {A: ‘A’}
  • {then: function(resolver, rejector){return rejector(“error”);}}

结果会是

  • resolve 2:[Object Object]
  • 忽略
  • resolve 2:[Object Object]
  • reject 2: error

singletons suffer state pollution between tests

singletons.js
1
2
3
4
5
6
module.exports = {
counter: 0,
inc: function() {
return ++this.counter;
}
};
test.js
1
2
3
4
5
6
7
8
9
10
11
var singleton = require('singletons');
module.exports = testCase({
"should equal one after calling inc": function (test) {
test.equal(1, singleton.inc());
test.done();
},
"should get one after calling inc": function(test) {
test.equal(1, singleton.inc());
test.done();
}
});

结果第二个测试会失败,但是从测试本身是看不出为什么第一个可以通过,而第二个则相反。当然,我们可以通过setUptearDown方法来重置对象的状态,但是这无疑增加了维护测试的成本,你得记着重置对象这件事本身就是负担。

下面就是解决方案

singletons_impr.js
1
2
3
4
5
6
7
8
module.exports = function() {
return {
counter: 0,
inc: function() {
return ++this.counter;
}
};
};

再看我们的测试

test.js
1
2
3
4
5
6
7
8
9
10
11
var singleton = require('singletons_impr');
module.exports = testCase({
"should equal one after calling inc": function (test) {
test.equal(1, singleton().inc());
test.done();
},
"should get one after calling inc": function(test) {
test.equal(1, singleton().inc());
test.done();
}
});

这样两次返回的对象都是全新的,测试通过。

不过,似乎这样让测试变得更加麻烦了,好处不明显。但是结合这篇文章[Javascript Modularize 2nd],就会巧妙地解决多个模块依赖某一个模块,某个模块修改被传染至其他模块的问题。因为每个被引入的模块都将被重新执行生成一遍,成为独立的对象。

结论

  • module.exports最好使用function方式导出。

参考链接
[1] Writing Testable JavaScript

The extent of a scope refers to the lifetime of a variable (i.e., how long a variable holds a certain value)

Global Scope

Javascript中任意不使用var创建的变量具有全局作用域。

global
1
2
3
4
5
6
7
8
9
10
globalVariable = "global";
(function() {
console.log(globalVariable);
})()

-> global

delete globalVariable;

-> true

当然,任意定义在文件最顶层的变量,事实上,由于Javascript Hositing作用,所有定义在最外层的变量都会被提升至作用域首部,都具有全局作用域。

值得注意的是使用var定义的变量是不能delete的,它不是全局变量的属性:

del_variable
1
2
3
4
var globalVariable = 'global';
delete globalVariable;

-> false

除此之外,任意被暴露到最外层的变量,一旦被全局作用域捕获,其本身都会存在被随意修改的风险。

Lexical Scope

Lexical scope refers to the visibility of a variable and its value analogous to its textual representation.

Javascript会从内向外寻找变量的绑定,所以变量的定义距离使用处最近,则该变量会被使用。

lexical_scope
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function lexicalScope() {
var lexicalVariable = "outer";
if(true) {
var lexicalVariable = "inner";
console.log(lexicalVariable);
(function(){
var lexicalVariable = "innerMost";
console.log(lexicalVariable);
})();
console.log(lexicalVariable);
}
}

lexicalScope();
-> inner
innerMost
inner

Dynamic Scope

the value of any given binding cannot be known until the caller of any given function is known— which may be too late.

dynamic_scope
1
2
3
4
5
6
7
function globalThis() { return this; }
globalThis();
-> some global object, probably Window
globalThis.call('barnabas');
-> 'barnabas'
globalThis.apply('orsulak', [])
-> 'orsulak'

在Javascript中,this所处的作用域就是动态作用域。也就是说,globalThis()返回值完全由调用方决定,this变量的对照表是不断改变的。更多参考这里

Function Scope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function strangerIdentity(n) {
for(this['i'] = 0; this['i'] < n; this['i']++);
return this['i'];
}

--- case 1
strangerIdentity(100);
-> 100
i
-> 100

--- case 2
var id = new strangerIdentity(100);

id
-> strangerIdentity {i: 100}
id.i
-> 100
i
-> Uncaught ReferenceError: i is not defined

在Javascript中,只有一个Function才会产生新的作用域。直接调用一个funcation,其内部的指针指向global对象。但当使用new后,其作用域绑定到一个Function Object上。

Javascript的赋值操作就是引用修改,举个例子:

reference.jsgrunt-properties
1
2
3
4
5
6
7
8
9
10
11
12
13
function splitKeys(obj, splitter) {
var keys, value, parent, result = {};
for (var key in obj) {
keys = key.split(splitter);
value = obj[key].replace(/"/g, '\\"');
parent = result;
for (var j = 0; j < keys.length-1; j++) {
parent = parent[keys[j]] = parent[keys[j]] || {};
}
parent[keys[keys.length-1]] = value;
}
return JSON.stringify(result, null, 2);
}

这段代码将Java程序中的常见的Properties文件中的键值映射成了嵌套的JSON对象:

test.js
1
2
3
4
5
6
7
8
9
10
var obj = {"country.province.city": "Chengdu"};
splitKeys(obj, '.');

-> "{
"country": {
"province": {
"city": "Chengdu"
}
}
}"

我们从实现代码的这行开始看起parent = result;,很简单地把parent指向result这个空对象。

再看接下来的代码,也是最巧妙的地方:

parts
1
2
3
for (var j = 0; j < keys.length-1; j++) {
parent = parent[keys[j]] = parent[keys[j]] || {};
}

第一次循环:
parent开始是空对象,parent = parent['country'] = parent['country'] || {};,将会生成一个parent的country属性,该属性的值还是一个空对象。于此同时,parent会指向新生成的、也即它的country属性。

第二次循环:
parent指向了country,parent = parent['province'] = parent['province'] || {};也即parent = country['province'] = country['province'] || {};,此时,将会生成一个country的province属性,该属性的值还是一个空对象,而parent则指向了province对象。

所以 parent[keys[keys.length-1]] = value;等价为province['city'] = "Chengdu";

最后,return JSON.stringify(result, null, 2);,这最后的result就是具有嵌套效果的JSON对象了。

编程过程中,别人谈到Javascript中的funcation是顺序执行的,所以书写的顺序要留心,不然会出未定义就执行的错误。当然,C语言出身的程序员不以为意,会认为理所当然,C语言就是这么干的。但Javascript并非如此!

ints.js
1
2
3
4
5
6
7
8
9
10
function ints(arrOfNumberStr) {
return arrOfNumberStr.map(toInt);

function toInt(str) {
return parseInt(str, 10);
}
}

ints(['11', '11', '11']);
-> [11, 11, 11]

从执行结果上来看,先调用后执行时正确的。但是JS是解析执行的,如果没有预先定义,执行到调用处必然会失败,那么为什么这段代码可以工作呢?

先看一段略做修改之后的代码

ints_fail.js
1
2
3
4
5
6
7
8
9
10
function ints(arrOfNumberStr) {
return arrOfNumberStr.map(toInt);

var toInt = function(str) {
return parseInt(str, 10);
}
}

ints(['11', '11', '11']);
-> TypeError: undefined is not a function

执行结果失败了。回头看这两段代码的定义,不难发现程序对于toInt的定义方式有差别,一种用常规的函数定义方式,一种使用var定义。

接着再看一段代码

variables.js
1
2
3
4
5
6
7
8
function loop(n) {
for(var i=0; i<n; i++);

return i;
}

loop(10);
-> 10

估计学习C语言的人开始惊讶了:这个i的作用域不是应该只在for loop中吗?

那么到底是什么原因造成的呢?这就是Javascript Hoisting!简单解释,声明提升到作用域的最前面。

接上面的variable.js

variables_hoisting.js
1
2
3
4
5
6
function loop(n) {
var i;
for(i=0; i<n; i++);

return i;
}

这和原来的写法是等价的,所以i的作用域在整个function中都是有效的。依次类推,init_fail.js中的var toInt这样的函数声明也会被提升到作用域的最上方,但是只是声明被提升了,真正赋值还是在后面,所以此时调用该函数,它还是undefined

但是对于init.js而言,它的声明方式会将整个函数都提升,所以它是先于调用方赋值初始化的,所以能工作。

‘use strict’ 可以防止 only reference hoisting?


如果变量未声明,use strict模式下赋值会报错。

use_strict.js
1
2
3
4
5
(function(){
'use strict';
var foo = 123;//works fine
bar = 345;//ReferenceError: bar is not defined
}());

如果在以前例子中使用use strict,并不能提前报出toInt未赋值的错误。直到运行期间,才会发现toInt依然undefined

use_strict_for_hoisting.js
1
2
3
4
5
6
7
8
(function ints(arrOfNumberStr) {
'use strict';
return arrOfNumberStr.map(toInt);
var toInt = function(str) {
return parseInt(str, 10);
}
})(['11', '11']);
-> Uncaught TypeError: undefined is not a function()

优先使用function declaration


Why? Function declarations are named, so they’re easier to identify in call stacks. Also, the whole body of a function declaration is hoisted, whereas only the reference of a function expression is hoisted. This rule makes it possible to always use Arrow Functions in place of function expressions. – from javascript style guide

下篇会记录Javascript的作用域Global Scope, Lexical Scope, Dynamic Scope, Function Scope

NodeJS Module require

require实现了CommonJS标准,在Node中作为一个全局的函数来支持模块管理。
但是开发中遇到了一个有意思的场景:

假如ModuleA和ModuleB同时依赖一个第三方库,简单起见,设为var Foo = {request: 'foo'}

现在ModuleA引入Foo.js

ModuleA
1
2
var Foo = require('Foo');
Foo.request = 'bar';

ModuleB中也引入Foo.js

ModuleB
1
2
var Foo = require('Foo');
console.log(Foo.request); //bar

结果是这个对象的属性被改变了!这不就相当于全局变量了吗?

不过,据此可以推测NodeJS是如何实现require的:

  1. 内部把依赖的module用function包装了一遍,使得require的变量变成私有的;
  2. 内部使用缓存机制,防止重复加载相同的JS文件。
module.jsNodeJs
1
2
3
4
5
Module.prototype.require = function(path) {
assert(util.isString(path), 'path must be a string');
assert(path, 'missing path');
return Module._load(path, this);
};

上面这段代码就是require源代码,直接进入_load:

Module._load
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
Module._load = function(request, parent, isMain) {
if (parent) {
debug('Module._load REQUEST ' + (request) + ' parent: ' + parent.id);
}

var filename = Module._resolveFilename(request, parent);

var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
}
...
var module = new Module(filename, parent);
...
Module._cache[filename] = module;

var hadException = true;

try {
module.load(filename); //invoke extensions['.js|json'] -> _compile js
hadException = false;
} finally {
if (hadException) {
delete Module._cache[filename];
}
}

return module.exports;
};

我省略一些无关紧要的代码,仅从以上代码可以看出,确实有cache使用了,所以require到内存中的对象其实只有一个!

Module.prototype._compile
1
2
3
4
5
6
// create wrapper function
var wrapper = Module.wrap(content);
var compiledWrapper = runInThisContext(wrapper, { filename: filename });
...
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);

上面是一小段compile的代码,执行到这里的顺序是_load -> _extensions[‘.js’] -> _compile.
可以看到确实进行了wrap操作,将js文件中的内容wrap成一个function, 最后传入self.exports (module.exports),这样module.exports便继承了原JS执行的结果。

Closure

什么是闭包?

一个包含了自由变量的开发表达式,和该自由变量的约束环境组合之后,产生了一种封闭的状态。

举个例子,如下

1
2
3
4
5
6
7
8
9
10
11
12
function inc() {
var counter = 0;
return function() {
return counter++;
}
}

//invoke
var id = inc();
id(); //0
id(); //1
...

变量counter只在inc中可见,inc就是counter的约束环境,而内部的function就是开放的表达式,达到的封闭状态就是闭包。

Javascript Closure

1
2
3
4
5
6
7
(function(){
console.log(window);
}())
or
(function(){
console.log(window);
})()

注意两种闭包的写法: **()**一个在内,另一个在外,两者是等价的。
这种写法有两个作用

  1. 立即执行(Immediately-Invoked Function Expression, IIFE)
  2. 封装内部变量,使之对外不可见

NOTICE
对外不可见,不代表闭包内部不能访问全局变量。闭包内部是可以访问外部变量的,如上面的例子所示

JavaScript has a feature known as implied globals. Whenever a name is used, the interpreter walks the scope chain backwards looking for a var statement for that name. If none is found, that variable is assumed to be global. If it’s used in an assignment, the global is created if it doesn’t already exist. This means that using or creating global variables in an anonymous closure is easy. Unfortunately, this leads to hard-to-manage code, as it’s not obvious (to humans) which variables are global in a given file.

以上就是理由。这种做法不好的地方有三个:

  1. 代码不便管理,这是全局变量的弊病
  2. 依赖关系不明确
  3. 性能损失,因为需要向后查找变量声明

正确的做法是导入作用域

1
2
3
(function(window){
console.log(window);
})(window)

借助closure,我们可以简单实现JS的模块化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var module = (function(){
var counter = 0;
return {
inc: function() {
return counter++;
}
};
})();

//invoke
module.counter //undefined
module.inc(); //0
module.inc(); //1
...

Augmentation (mixin, trait …)

模块化之后,如何扩展这部分的功能?

Loose augmentation

1
2
3
4
5
6
7
var module = (function(self){

self.todo = function() {
console.log('cannot access counter');
}
return self;
})(module || {})

之所以称为宽松augmentation,是因为多个modules可以异步加载。如果变量没有声明,就会重新创建。与之对应的就是Tight augmentation

Tight augmentation

1
2
3
4
5
6
7
var module = (function(self){

self.todo = function() {
console.log('cannot access counter');
}
return self;
})(module)

这样就要求严格的加载顺序了。

更多模式参考 JavaScript Module Pattern: In-Depth

通用的JS规范共有两种:CommonJS和AMD

CommonJS

该标准中,有一个自由函数require,用于加载模块。

1
2
3
4
5
6
7
8
//math.js
exports.add = function() {
var sum = 0, i = 0, args = arguments, l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};

这样就可以在其他模块中引用

1
2
3
4
5
//increment.js
var add = require('math').add;
exports.increment = function(val) {
return add(val,1);
};

NOTICE

任何require进来的模块,其变量都是私有的

举个例子

1
2
3
4
5
6
//program.js
var inc = require('increment').increment;
var a = 1;
inc(a); // 2
inc.add //undefined
module.id == "program";

这里的inc.add对于program.js是不可见的。

NodeJs的模块系统实现了改标准 Nodejs Module

AMD (Asynchronous Module Definition)

对于浏览器环境,由于js文件在服务端,异步加载使得CommonJS不实用了。

1
2
3
4
5
6
7
8
define("alpha", ["require", "exports", "beta"],
function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//Or:
return require("beta").verb();
}
});

这样,只有等到所有的模块加载成功后,才会调用回调函数。

maven phases and plugins goal

three built-in Lifecycle

  • default
  • clean
  • site

built-in Lifecycle is made up of phases

  • default
    • validate - validate project is correct and all neccessary infomation is avaiable
    • compile
    • test
    • package
    • integration-test
    • verify - run and checks to verify the package is valid and meets quality criteria
    • install - install to local repository
    • deploy - copy the final package to the remote repository for sharing.

a build phase is made up of plugin goals

  • a plugin goal represents a specific task, and bound to those build phases.
  • it’s bound to zero or more build phases
  • mvn clean dependency:copy-dependencies package
  • a phase can also have zero or more goals bound to it. if a build phase has no goals bound to it, it cannot execute.

编写插件的时候,我们会给每一个goal默认绑定一个phase,所以在对应的plugin中,我们或许不需要显式绑定两者

maven core concept: coordinate and dependency

coordinate

  • groupId
  • artifactId
  • version
  • packaging - jar(default), war
  • classfier - javadoc.jar, sources.jar. cannot defined directly

dependency

  • dependency scope: (compile, test, provided, runtime, system, import), provided(servlet-api) system should with systemPath.
  • transitive dependency
  • dependency mediate (the shortest path, otherwise declartive order in pom)
  • execlusion dependency(groupId & artifactId)

How to look for dependency

mvn dependency:list mvn dependency:tree

mvn dependency:analyze : 标注声明但未使用的依赖或者使用但未声明的依赖(传递依赖)

dependency management

1
2
3
4
5
<dependencyManagement>
<dependencies>
...
</dependencies>
</dependencyManagement>

防止subModules继承不必要的依赖,同时如果需要,只要在subModule中声明此依赖的groupId, artifactId,无需对应的versionId,因为这是可以继承自parentModule的

funny Q

$q

$q can be used in two fashions — one which is more similar to Kris Kowal’s Q or jQuery’s Deferred implementations, and the other which resembles ES6 promises to some degree.

ES6 style

The streamlined ES6 style promise is essentially just using $q as a constructor which takes a resolver function as the first argument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// assume that $q and $resource are available.
function = asyncGo() {
return $q( function(resolve, reject) {
$resource("www.example.com", {}, {})
.get({}, function(result) {
resolve("hello, " + result);
}, function(error) {
reject("sorry, " + error + " is not allowed");
})
});
};

var promise = asyncGo();
promise.then(function(greeting){
console.log(greeting);
}, function(err){
console.log(err);
});

Deferred style

The purpose of the deferred object is to expose the associated Promise instance as well as APIs that can be used for signaling the successful or unsuccessful completion, as well as the status of the task.

  • resolve(value) – resolves the derived promise with the value. If the value is a rejection constructed via $q.reject, the promise will be rejected instead.

  • reject(reason) – rejects the derived promise with the reason. This is equivalent to resolving it with a rejection constructed via $q.reject.

  • notify(value) - provides updates on the status of the promise’s execution. This may be called multiple times before the promise is either resolved or rejected.

1
2
3
4
5
6
promiseB = promiseA.then(function(result) {
return result + 1;
});

// promiseB will be resolved immediately after promiseA is resolved and its value
// will be the result of promiseA incremented by 1

$q vs. $rootScope

$q is integrated with the $rootScope.Scope Scope model observation mechanism in angular

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
it('should simulate promise', inject(function($q, $rootScope) {
var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;

promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).toBeUndefined();

// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).toBeUndefined();

// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).toEqual(123);
}));

then

then that can accept three arguments(three callback types)–success, failure, progress.
for defer object, resolve will apply to success, reject apply to failure and notify apply to progress.

a defer object contains always, done, fail methods! so the always and done will trigger if resovle be called and return a promise object, which contains resolved result.

谈谈我对microservice的理解

1. 在比较中理解

功能上

传统的service

  • 功能全面,大而全;
  • 系统内部耦合严重,数据服务部分互相依赖,逻辑错综复杂;
  • 表现层和服务层耦合,解除耦合难以实现;
  • 整个应用程序独立存在,完全不可以复用。

新型的microservice

  • 多模块,每个模块功能单一;
  • 采用统一的数据传输格式;
  • 数据服务单独部署,可以互相依赖;
  • 采用RESTful服务标准,表现层和服务层解耦;
  • 分布式部署,服务可以冗余,容灾
  • 采用分布式事务:BASE原则,1. Basiclly Available 2. Soft state 3.Eventually Consistent

开发中

传统的service

  • 代码组织结构和逻辑复杂,难以阅读;
  • 功能性测试简单;
  • 系统对外暴露较少,沟通成本较低。

新型的microservice

  • 代码结构简单,易于阅读;
  • 需要大量的mock service,以完成功能性测试;
  • web技术上可能存在跨域问题;
  • 系统对外依赖较重,交互频繁;
  • 人员沟通成本巨大。

Service Dependency Management

既然服务之间的依赖很困扰,那么为何不学习maven或者gradle,将服务依赖统一管理起来?
Taobao中configServer是一个独立的中央管理服务器,其作用无非让应用知晓其他应用,然后去调用;这就是依赖!
而且假设不同服务是有版本号的,事实上很多人已经这么做了,这个实在太类似jar的依赖管理了。

0%