All posts by dotte

么用API网关构建微服务|架构

当选择将应用程序构建为一组微服务时,需要确定应用程序客户端如何与微服务交互。在单体应用程序中,只有一组(通常是重复的、负载均衡的)端点。然而,在微服务架构中,每个微服务都会暴露一组通常是细粒度的端点。在本文中,我们将讨论一下这对客户端与应用程序之间的通信有什么影响,并提出一种使用API网关的方法。

让我们想象一下,你要为一个购物应用程序开发一个原生移动客户端。你很可能需要实现一个产品详情页面,上面展示任何指定产品的信息。

例如,下图展示了在Amazon Android移动应用中滚动产品详情时看到的内容。

虽然这是个智能手机应用,产品详情页面也显示了大量的信息。例如,该页面不仅包含基本的产品信息(如名称、描述、价格),而且还显示了如下内容:

购物车中的件数;

订单历史;

客户评论;

低库存预警;

送货选项;

各种推荐,包括经常与该产品一起购买的其它产品,购买该产品的客户购买的其它产品,购买该产品的客户看过的其它产品;

可选的购买选项。

当使用单体应用程序架构时,移动客户端将通过向应用程序发起一次REST调用(GET api.company.com/productdetails/<productId>)来获取这些数据。负载均衡器将请求路由给N个相同的应用程序实例中的一个。然后,应用程序会查询各种数据库表,并将响应返回给客户端。

相比之下,当使用微服务架构时,产品详情页面显示的数据归多个微服务所有。下面是部分可能的微服务,它们拥有要显示在示例中产品详情页面上的数据:

购物车服务——购物车中的件数;

订单服务——订单历史;

目录服务——产品基本信息,如名称、图片和价格;

评论服务——客户的评论;

库存服务——低库存预警;

送货服务——送货选项、期限和费用,这些单独从送货方的API获取;

推荐服务——建议的产品。

我们需要决定移动客户端如何访问这些服务。让我们看看都有哪些选项。

客户端与微服务直接通信

从理论上讲,客户端可以直接向每个微服务发送请求。每个微服务都有一个公开的端点(https ://<serviceName>.api.company.name)。该URL将映射到微服务的负载均衡器,由它负责在可用实例之间分发请求。为了获取产品详情,移动客户端将逐一向上面列出的N个服务发送请求。

遗憾的是,这种方法存在挑战和局限。一个问题是客户端需求和每个微服务暴露的细粒度API不匹配。在这个例子中,客户端需要发送7个独立请求。在更复杂的应用程序中,可能要发送更多的请求。例如,按照Amazon的说法,他们在显示他们的产品页面时就调用了数百个服务。然而,客户端通过LAN发送许多请求,这在公网上可能会很低效,而在移动网络上就根本不可行。这种方法还使得客户端代码非常复杂。

客户端直接调用微服务的另一个问题是,部分服务使用的协议不是Web友好协议。一个服务可能使用Thrift二进制RPC,而另一个服务可能使用AMQP消息传递协议。不管哪种协议都不是浏览器友好或防火墙友好的,最好是内部使用。在防火墙之外,应用程序应该使用诸如HTTP和WebSocket之类的协议。

这种方法的另一个缺点是,它会使得微服务难以重构。随着时间推移,我们可能想要更改系统划分成服务的方式。例如,我们可能合并两个服务,或者将一个服务拆分成两个或更多服务。然而,如果客户端与微服务直接通信,那么执行这类重构就非常困难了。

由于这些问题的存在,客户端与微服务直接通信很少是合理的。

使用API网关

通常,一个更好的方法是使用所谓的API网关。API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、“请求整形(request shaping)”与管理、静态响应处理。

下图展示了API网关通常如何融入架构:

API网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过API网关,然后由它将请求路由到合适的微服务。API网管经常会通过调用多个微服务并合并结果来处理一个请求。它可以在Web协议(如HTTP与WebSocket)与内部使用的非Web友好协议之间转换。

API网关还能为每个客户端提供一个定制的API。通常,它会向移动客户端暴露一个粗粒度的API。例如,考虑下产品详情的场景。API网关可以提供一个端点(/productdetails?productid=xxx),使移动客户端可以通过一个请求获取所有的产品详情。API网关通过调用各个服务(产品信息、推荐、评论等等)并合并结果来处理请求。

Netflix API网关是一个很好的API网关实例。Netflix流服务提供给数以百计的不同类型的设备使用,包括电视、机顶盒、智能手机、游戏系统、平板电脑等等。最初,Netflix试图为他们的流服务提供一个通用的API。然而他们发现,由于各种各样的设备都有自己独特的需求,这种方式并不能很好地工作。如今,他们使用一个API网关,通过运行特定于设备的适配器代码来为每个设备提供一个定制的API。通常,一个适配器通过调用平均6到7个后端服务来处理每个请求。Netflix API网关每天处理数十亿请求。

API网关的优点和不足

如你所料,使用API网关有优点也有不足。使用API网关的最大优点是,它封装了应用程序的内部结构。客户端只需要同网关交互,而不必调用特定的服务。API网关为每一类客户端提供了特定的API。这减少了客户端与应用程序间的交互次数,还简化了客户端代码。

API网关也有一些不足。它增加了一个我们必须开发、部署和维护的高可用组件。还有一个风险是,API网关变成了开发瓶颈。为了暴露每个微服务的端点,开发人员必须更新API网关。API网关的更新过程要尽可能地简单,这很重要。否则,为了更新网关,开发人员将不得不排队等待。

不过,虽然有这些不足,但对于大多数现实世界的应用程序而言,使用API网关是合理的。

实现API网关

到目前为止,我们已经探讨了使用API网关的动机及其优缺点。下面让我们看一下需要考虑的各种设计问题。

性能和可扩展性

只有少数公司有Netflix的规模,每天需要处理数十亿请求。不管怎样,对于大多数应用程序而言,API网关的性能和可扩展性通常都非常重要。因此,将API网关构建在一个支持异步、I/O非阻塞的平台上是合理的。有多种不同的技术可以用于实现一个可扩展的API网关。在JVM上,可以使用一种基于NIO的框架,比如Netty、Vertx、Spring Reactor或JBoss Undertow中的一种。一个非常流行的非JVM选项是Node.js,它是一个以Chrome JavaScript引擎为基础构建的平台。另一个选项是使用NGINX Plus。NGINX Plus提供了一个成熟的、可扩展的、高性能Web服务器和一个易于部署的、可配置可编程的反向代理。NGINX Plus可以管理身份验证、访问控制、负载均衡请求、缓存响应,并提供应用程序可感知的健康检查和监控。

使用响应式编程模型

API网关通过简单地将请求路由给合适的后端服务来处理部分请求,而通过调用多个后端服务并合并结果来处理其它请求。对于部分请求,比如产品详情相关的多个请求,它们对后端服务的请求是独立于其它请求的。为了最小化响应时间,API网关应该并发执行独立请求。然而,有时候,请求之间存在依赖。在将请求路由到后端服务之前,API网关可能首先需要调用身份验证服务验证请求的合法性。类似地,为了获取客户意愿清单中的产品信息,API网关必须首先获取包含那些信息的客户资料,然后再获取每个产品的信息。关于API组合,另一个有趣的例子是Netflix Video Grid。

使用传统的异步回调方法编写API组合代码会让你迅速坠入回调地狱。代码会变得混乱、难以理解且容易出错。一个更好的方法是使用响应式方法以一种声明式样式编写API网关代码。响应式抽象概念的例子有Scala中的Future、Java 8中的CompletableFuture和JavaScript中的Promise,还有最初是微软为.NET平台开发的Reactive Extensions(RX)。Netflix创建了RxJava for JVM,专门用于他们的API网关。此外,还有RxJS for JavaScript,它既可以在浏览器中运行,也可以在Node.js中运行。使用响应式方法将使你可以编写简单但高效的API网关代码。

服务调用

基于微服务的应用程序是一个分布式系统,必须使用一种进程间通信机制。有两种类型的进程间通信机制可供选择。一种是使用异步的、基于消息传递的机制。有些实现使用诸如JMS或AMQP那样的消息代理,而其它的实现(如Zeromq)则没有代理,服务间直接通信。另一种进程间通信类型是诸如HTTP或Thrift那样的同步机制。通常,一个系统会同时使用异步和同步两种类型。它甚至还可能使用同一类型的多种实现。总之,API网关需要支持多种通信机制。

服务发现

API网关需要知道它与之通信的每个微服务的位置(IP地址和端口)。在传统的应用程序中,或许可以硬连线这个位置,但在现代的、基于云的微服务应用程序中,这并不是一个容易解决的问题。基础设施服务(如消息代理)通常会有一个静态位置,可以通过OS环境变量指定。但是,确定一个应用程序服务的位置没有这么简单。应用程序服务的位置是动态分配的。而且,单个服务的一组实例也会随着自动扩展或升级而动态变化。总之,像系统中的其它服务客户端一样,API网关需要使用系统的服务发现机制,可以是服务器端发现,也可以是客户端发现。下一篇文章将更详细地描述服务发现。现在,需要注意的是,如果系统使用客户端发现,那么API网关必须能够查询服务注册中心,这是一个包含所有微服务实例及其位置的数据库。

处理局部失败

在实现API网关时,还有一个问题需要处理,就是局部失败的问题。该问题在所有的分布式系统中都会出现,无论什么时候,当一个服务调用另一个响应慢或不可用的服务,就会出现这个问题。API网关永远不能因为无限期地等待下游服务而阻塞。不过,如何处理失败取决于特定的场景以及哪个服务失败。例如,在产品详情场景下,如果推荐服务无响应,那么API网关应该向客户端返回产品详情的其它内容,因为它们对用户依然有用。推荐内容可以为空,也可以,比如说,用一个固定的TOP 10列表取代。不过,如果产品信息服务无响应,那么API网关应该向客户端返回一个错误信息。

如果缓存数据可用,那么API网关还可以返回缓存数据。例如,由于产品价格不经常变化,所以如果价格服务不可用,API网关可以返回缓存的价格数据。数据可以由API网关自己缓存,也可以存储在像Redis或Memcached那样的外部缓存中。通过返回默认数据或者缓存数据,API网关可以确保系统故障不影响用户的体验。

在编写代码调用远程服务方面,Netflix Hystrix是一个异常有用的库。Hystrix会将超出设定阀值的调用超时。它实现了一个“断路器(circuit breaker)”模式,可以防止客户端对无响应的服务进行不必要的等待。如果服务的错误率超出了设定的阀值,那么Hystrix会切断断路器,在一个指定的时间范围内,所有请求都会立即失败。Hystrix允许用户定义一个请求失败后的后援操作,比如从缓存读取数据,或者返回一个默认值。如果你正在使用JVM,那么你绝对应该考虑使用Hystrix。而如果你正在使用一个非JVM环境,那么你应该使用一个等效的库。

小结

对于大多数基于微服务的应用程序而言,实现一个API网关是有意义的,它可以作为系统的唯一入口。API网关负责服务请求路由、组合及协议转换。它为每个应用程序客户端提供一个定制的API。API网关还可以通过返回缓存数据或默认数据屏蔽后端服务失败。在本系列的下一篇文章中,我们将探讨服务间通信。

from:http://www.tuicool.com/articles/bMnEbmv

Must-Watch JavaScript

This is a collection of well-received talks about JavaScript, covering topics such as ES6, JavaScript frameworks, client-side apps, mobile integration, JavaScript performance, tooling, leveling up, and more.

Like CSS? Check out Must-Watch CSS! For other great lists check out @sindresorhus‘s curated list of awesome lists.

2018

  1. In the Loop: Jake Archibald, JSConf.Asia 35:11
  2. 10 Things I Regret About Node.js: Ryan Dahl, JSConf.EU 26:41
  3. Deep Learning in JS: Ashi Krishnan, JSConf.EU 31:30

2017

  1. Immutable Data Structures for Functional JS: Anjana Vakil, JSConf.EU 26:32
  2. JavaScript Engines – How Do They Even?: Franziska Hinkelmann, JSConf.EU 25:13
  3. Async + Await: Wes Bos, dotJS 15:51
  4. Advanced Async and Concurrency Patterns in JavaScript: Kyle Simpson, js.la Meetup 39:42
  5. The Browser Hackers Guide to Instantly Loading Everything: Addy Osmani, JSConf.EU 28:09

2016

  1. The Rise of Async JavaScript: Jeremy Fairbank, FluentConf 28:58
  2. Reasonable JavaScript: Preethi Kasireddy, Nodevember 50:12
  3. Learning Functional Programming with JavaScript: Anjana Vakil, JSUnconf 29:56
  4. Choosing a JavaScript Framework: Rob Eisenberg, NDC Oslo 1:01:13
  5. The Myth of The “Real JavaScript Developer”: Brenna O’Brien, Front-Trends 27:05
  6. An Angular 2 Force Awakens: John Papa, ng-conf 20:39
  7. React.js for TV UIs: Steve McGuire, Netflix JavaScript Talks 35:02
  8. The Hitchhiker’s Guide to All Things Memory in JavaScript: Safia Abdalla, JSConf Budapest 26:16
  9. SVG and GreenSock for Complex Animation: Sarah Drasner, ForwardJS Summit 40:16

2015

  1. JavaScript in 2015: Glen Maddern, (screencast) 10:32
  2. Angular + React = Speed: Dave Smith, ng-conf 19:26
  3. Parallelism Experiments in JavaScript: Naveed Ihsanullah, JSConf.US 32:39
  4. Eliminate JavaScript Code Smells: Elijah Manor, FluentConf 29:15
  5. Pocket-Sized JS: Henrik Joreteg, dotJS 18:44
  6. What the… JavaScript?: Kyle Simpson, ForwardJS 38:16
  7. Real World jQuery: Ben Foxall, jQuery UK 26:45
  8. JavaScript State of the Union: Geoff Schmidt, Meteor Devshop SF 48:47
  9. Dirty Performance Secrets of HTML5: Andreas Gal, FluentConf 14:15
  10. You Should Use <Insert Library/Framework>, It’s the Bestestest!: Paul Lewis, ffconf 33:31
  11. Async Programming in ES7: Jafar Husain, JSConf.US 35:56
  12. Live React: Hot Reloading with Time Travel: Dan Abramov, ReactEurope 30:40
  13. JavaScript Transformation: Sebastian McKenzie, JSConf.US 20:23
  14. Node.js at Netflix: Kim Trott, Node.js Interactive 25:17
  15. If You Wish to Learn ES6/2015 From Scratch, You Must First Invent the Universe: Ashley Williams, JSConf.US 25:48

2014

  1. Enemy of the State: Amy Palamountain,Forward JS 32:40
  2. Mary Live-Codes a JavaScript Game from Scratch: Mary Rose Cook, Front-Trends 32:16
  3. Unorthodox Performance: John-David Dalton, ForwardJS 43:39
  4. What the Heck Is the Event Loop Anyway?: Philip Roberts, JSConf.EU 26:53
  5. Building Isomorphic Apps: Spike Brehm, JSConf.Asia 45:01
  6. JavaScript for Everybody: Marcy Sutton, JSConf.EU 28:59
  7. JavaScript ♥ Unicode: Mathias Bynens, JSConf.EU 25:41
  8. Using AngularJS to Create iPhone & Android Applications with PhoneGap: Daniel Zen, ng-conf 21:34
  9. Virtual Machines, JavaScript and Assembler: Scott Hanselman, FluentConf 25:56
  10. User Interface Algorithms: Mark DiMarco, JSConf.US 27:41
  11. End to End Angular Testing with Protractor: Julie Ralph, ng-conf 18:46
  12. Async JavaScript at Netflix: Jafar Husain, Netflix JavaScript Talks 28:38
  13. Building Realtime Apps with Firebase and Angular: Anant Narayanan, ng-conf 21:08

2013

  1. A JavaScript Web App Deconstructed: Alex MacCaw, JSConf.Asia 36:24
  2. JavaScript in Your Native Mobile Apps: Allen Pike, JSConf.EU 25:47
  3. JavaScript Masterclass: Angelina Fabbro, JSConf.US 22:33
  4. A Comparison of the Two-Way Binding in AngularJS, EmberJS and KnockoutJS: Marius Gundersen, JSConf.EU 19:16
  5. Hacker Way: Rethinking Web App Development at Facebook: Tom Occhino, Jing Chen, and Pete Hunt, F8 44:35
  6. Promises and Generators: Control Flow Utopia: Forbes Lindesay, JSConf.EU 31:26
  7. How to Rewrite Your JS App (at Least) 10 Times: Garann Means, Fronteers 47:45
  8. Front-End Development in Node.js: Raquel Vélez, jQuery Conference Portland 34:01
  9. Front-End Tools for the Young Developer: Christian Vuerings, SF HTML5 User Group 14:16
  10. Rethinking Best Practices: Pete Hunt, JSConf.Asia 40:57
  11. Righteous Javascript, Dude!: Zach Bruggerman, Cascadia JS 18:15
  12. Transitioning Groupon to NodeJS: Sean McCullough, EmpireJS 28:23
  13. Building Modular Web Applications: How To Build a Good Component: Angelina Fabbro, jQuery Conference Portland 35:02
  14. Making JS More Learnable: Pamela Fox, dotJS 28:46
  15. The Web Experience in the Autistic Spectrum: Natalia Berdys, JSConf.EU 30:37
  16. Return of Inspector Web: Web Components a Year Later: Angelina Fabbro, Fronteers 49:44
  17. Develop High Performance Sites and Apps with JavaScript and HTML5: Dr. Doris Chen, HTML5DevConf Meetup 1:01:39
  18. Building Reflow: Kristofer Joseph, BackboneConf 45:41
  19. Levelling Up in AngularJS: Alicia Liu, HTML5DevConf 40:31

2012

  1. A Novel, Efficient Approach to JavaScript Loading: Malte Ubl and John Hjelmstad, JSConf.EU 26:36
  2. To Hell with jQuery: Karolina Szczur, JSConf.EU 20:00
  3. Is Node.js Better?: Brian Ford, JSConf.US 41:42
  4. Inspector Web and the Mystery of the Shadow DOM: Angelina Fabbro, JSConfEU 28:42
  5. Maintainable JavaScript: Nicholas Zakas, FluentConf 47:04
  6. Client Side Internationalization: Alex Sexton, JSConf.EU 24:08

from:https://github.com/AllThingsSmitty/must-watch-javascript

函数式编程术语解析

函数式编程蔚然成风,越来越多的开源项目、技术交流在使用函数式编程的术语降低开发或沟通成本,这无形中对不了解函数式编程的开发者造成了一定的学习门槛,翻译本文的初衷就是要普及函数式编程的基本知识,从新的角度扩展编程思维。至于为什么要使用 JavaScript 演示函数式编程,一方面是因为 JavaScript 的特性在很多方面与函数式编程浑然天成,另一方面是因为 JavaScript 是世界上最 XX 的语言……

Arity

指函数的参数数量,由 -ary 和 -ity 这两个英文后缀拼接而成:

const sum = (a, b) => a + b;const arity = sum.length;console.log(arity); // => 2

Higher-Order Functions

高阶函数,此类函数可以接收其他函数作为参数,也可以返回一个函数作为返回值:

const filter = (pred, xs) => {   const result = [];   for (let idx = 0; idx < xs.length; idx++) {       if (pred(xs[idx])) {            result.push(xs[idx]);        }    }   return result;};const is = (type) => (x) => Object(x) instanceof type;filter(is(Number), [0, ‘1’, 2, null]); // => [0, 2]

Partial Application

偏函数,在原函数的基础上预填充(pre-filling)部分参数并返回的新函数:

// 下面是一个创建偏函数的辅助函数const partial = (f, …args) => (…moreArgs) => f(…args, …moreArgs);const add3 = (a, b, c) => a + b + c;// 预填充 (add3, 2, 3) 三个参数,空置最后一个参数,返回一个新的函数const fivePlus = partial(add3, 2, 3); // (c) => 2 + 3 + cfivePlus(4); // => 9

JavaScript 中的 Function.prototype.bind() 函数是创建偏函数的最简单方式:

const add1More = add3.bind(null, 2, 3); // => (c) => 2 + 3 + c

Currying

柯里化,将一个接收多个参数的函数转化为单参数函数的方式,转化后的函数每次只接收一个参数,然后返回一个新函数,新函数可以继续接收参数,直到接收到所有的参数:

const sum = (a, b) => a + b;sum(2, 3)// => 6const curriedSum = (a) => (b) => a + b;curriedSum(40)(2) // => 42.const add2 = curriedSum(2); // (b) => 2 + badd2(10) // => 12

Function Composition

函数合成,接收多个函数作为参数并返回一个新函数的方式,新函数按照传入的参数顺序,从右往左依次执行,前一个函数的返回值是后一个函数的输入值:

const compose = (f, g) => (a) => f(g(a))const floorAndToString = compose((val) => val.toString(), Math.floor)floorAndToString(121.212121) // => “121”

Purity

一个纯函数需要满足两个条件,第一是函数的返回值只能由输入值(函数接收的参数)决定,也就是说纯函数接收相同的参数会返回相同的值;第二是纯函数不会对自身作用域之外的运行环境产生副作用(side effects),比如说不会改变外部环境中变量的值,这会被认为是不安全的行为:

let greeting;const greet = () => greeting = “Hi, ” + window.name;// greet() 执行时更改了外部环境的变量greet(); // => “Hi, Brianne”

纯函数示例:

const greet = (name) => “Hi, ” + name ;greet(“Brianne”) // => “Hi, Brianne”

Side effects

如果函数或表达式与其自身作用域之外的可变数据(mutable data)发生了读写操作,那么此时函数和表达式就产生了副作用:

let greeting;const greet = () => greeting = “Hi, ” + window.name;// greet() 执行时更改了外部环境的变量greet(); // => “Hi, Brianne”// new Date() 是可变数据const differentEveryTime = new Date();// 这里表示系统接收到的输入值是不确定的,是一种可变数据console.log(“IO is a side effect!”);

Idempotent

幂等,同一个函数使用相同的参数嵌套执行多次的结果与执行一次的结果相同:

$$f(…f(f(x))…)=f(x)$$

Math.abs(Math.abs(10))sort(sort(sort([2,1])))

Point-Free Style

point-free style 是一种不显式向函数传递参数的代码风格,通常需要柯里化和高阶函数来实现:

const map = (fn) => (list) => list.map(fn);const add = (a) => (b) => a + b;// Not points-free// numbers 是一个显式传递的参数const incrementAll = (numbers) => map(add(1))(numbers);// Points-free// add(1) 的返回值隐式传递给了 map,作为 map 的 list 参数const incrementAll2 = map(add(1));

point-free style 的函数看起来就像是一个赋值表达式,没有使用我们常见的 function 或 => 等来声明其接收的参数。

Predicate

断言,一个返回布尔值的函数:

const predicate = (a) => a > 2;[1, 2, 3, 4].filter(predicate); // => [3, 4]

Contracts

TODO

Guarded Functions

TODO

Categories

categories 内部都绑定了具体的函数用于约束或执行特定的逻辑,比如 Monoid。

Value

任何可以赋值给变量的值都可以称为 value:

5Object.freeze({name: ‘John’, age: 30}) // The `freeze` function enforces immutability.(a) => a[1]undefined

Constant

常量,初始化后不能再次执行赋值操作的数据类型:

const five = 5const john = { name: ‘John’, age: 30 }// 因为常量不可变,所以下面表达式一定为 truejohn.age + five === ({ name: ‘John’, age: 30 }).age + (5)

常量具有 referentially transparent 的特性,也就是说将程序中出现的常量替换为它们实际的值,并不会影响程序的结果。译者话外:实际上在 JavaScript 中的 const 所声明的常量并不是完全稳定的,使用 Immutable.js 演示更加恰当:

const five = fromJS(5);const john = fromJS({name: ‘John’, age: 30})john.get(‘age’) + five === ({ name: ‘John’, age: 30 }).age + (5)

f(g()) === g

Functor

functor 都拥有 map 函数,并且在执行 map 之后会返回一个新的 functor:

object.map(x => x) === objectobject.map(x => f(g(x))) === object.map(g).map(f)

JavaScript 中最常见的 functor 就是数组类型的实例:

[1, 2, 3].map(x => x); // => [1, 2, 3]const f = x => x + 1;const g = x => x * 2;[1, 2, 3].map(x => f(g(x))); // => [3, 5, 7][1, 2, 3].map(g).map(f);     // => [3, 5, 7]

Pointed Functor

pointed functor 都拥有 of 函数,用于接收和构建 functor。ES2015 提供了 Array.of 函数,所以数组实例就可以看成是 pointed functor:

Array.of(1) // => [1]

Lift

lift 发生在你将值放入 functor 的时候,如果你将函数 lift 进了 Applicative Functor,那么就可以使用这个函数处理传递给这个 functor 的值。某些 lift 的实现拥有 lift 或 liftA2 函数,便于在 functor 上执行相关的函数:

const mult = (a, b) => a * b;const liftedMult = lift(mult); // => this function now works on functors like arrayliftedMult([1, 2], [3]); // => [3, 6]lift((a, b) => a + b)([1, 2], [3, 4]); // => [4, 5, 5, 6]

lift 一个单参数的函数非常类似于 map 操作:

const increment = (x) => x + 1;lift(increment)([2]); // => [3][2].map(increment); // => [3]

Referential Transparency

如果一个表达式可以被替换为实际的值而不影响程序的运行结果,那么我们就说这个表达式是 referentially transparent:

const greet = () => “Hello World!”;

以上面代码为例,任何调用 greet() 的地方都可以替换为 “Hello World!” 而不影响程序的执行结果。

Equational Reasoning

如果一个应用由多个表达式组合而成,且每个表达式都没有 side effect,那么这个应用就可以由部分推导出整体。

Lambda

匿名函数,本质上是一个 value:

function(a){   return a + 1;};(a) => a + 1;// Lambda 常用语高阶函数中[1, 2].map((a) => a + 1); // = [2, 3]// Lambda 作为 value 被赋值给变量let addOne = (a) => a + 1;

Lambda Calculus

数学的分支之一,使用函数创建通用的计算模型(universal model of computation)。

Lazy evaluation

惰性求值,是一种按需执行的求值策略,只有需要某个值时才会执行相关的表达式。在函数式编程语言中,这一特性可用于构造无限列表。

const rand = function*() {   while (true) {       yield Math.random();    }}const randIter = rand();randIter.next().value; // 每次执行 next() 函数都会返回一个新的随机数// 有且只有在执行 next() 的时候才会返回新值

Monoid

Monoid,通过一个函数“合并”两个同类型数据后返回相同的数据类型。最简单的 monoid 就是两数相加:

1 + 1; // => 2

这里的 + 就是上面所说的“合并”函数。Monoid 中存在恒等式的概念:

1 + 0// => 1// 这里的 0 就是恒等式// Monoid 还必须满足结合律1 + (2 + 3) === (1 + 2) + 3; // => true// 数组的 concat() 操作可以构造一个 monoid[1, 2].concat([3, 4]); // => [1, 2, 3, 4]// 空数组可以视为是恒等式[1, 2].concat([]); // => [1, 2]

如果知道了一个函数的的恒等式和“合并”函数 compose,函数本身就是一个 monoid:

const identity = (a) => a;const compose = (f, g) => (x) => f(g(x));compose(foo, identity) ≍ compose(identity, foo) ≍ foo

Monad

Monad,是一个拥有 of 和 chain 函数的数据类型,chain 类似于 map,但它会输出非嵌套形式的结果:

[‘cat,dog’, ‘fish,bird’].chain((a) => a.split(‘,’)) // => [‘cat’, ‘dog’, ‘fish’, ‘bird’][‘cat,dog’, ‘fish,bird’].map((a) => a.split(‘,’)) // => [[‘cat’, ‘dog’], [‘fish’, ‘bird’]]

在其他函数式编程语言中,of 也被称为 return,chain 也被称为 flatmap 和 bind。

Comonad

Comonad,拥有 extract 和 extend 函数的数据类型:

const CoIdentity = (v) => ({   val: v,    extract() { return this.val },    extend(f) { return CoIdentity(f(this)) }})// extract() 可以从 functor 中取值CoIdentity(1).extract() // => 1// extend() 可以返回新的 comonadCoIdentity(1).extend(co => co.extract() + 1) // => CoIdentity(2)

Applicative Functor

Applicative Functor,是拥有 ap 函数的数据类型,ap 函数可以将 functor 中的值转化为其他 functor 中的同类型值:

[(a) => a + 1].ap([1]) // => [2]

这一特性对于多个 applicative functor 需要接收多个参数时,就显得很有用:

const arg1 = [1, 2];const arg2 = [3, 4];const add = (x) => (y) => x + y;const partiallyAppliedAdds = [add].ap(arg1); // => [(y) => 1 + y, (y) => 2 + y]partiallyAppliedAdds.ap(arg2); // => [4, 5, 5, 6]

Morphism

态射,一个转换函数。

Isomorphism

同构转换,相同数据下不同结构之间的转换。举例来说,2D 坐标既可以存储为数组 [2, 3] 也可以存储为 { x: 2, y: 3 }:

const pairToCoords = (pair) => ({x: pair[0], y: pair[1]})const coordsToPair = (coords) => [coords.x, coords.y]coordsToPair(pairToCoords([1, 2])) // => [1, 2]pairToCoords(coordsToPair({x: 1, y: 2})) // => { x: 1, y: 2 }

Setoid

Setoid,拥有 equals 函数的数据类型,可用于与其他同类型的数据进行比较。为 Array 类型添加 equals 函数使其成为 Setoid:

Array.prototype.equals = (arr) => {   const len = this.length   if (len !== arr.length) {       return false    }   for (let i = 0; i < len; i++) {       if (this[i] !== arr[i]) {           return false        }    }   return true}[1, 2].equals([1, 2]) // => true[1, 2].equals([0]) // => false

Semigroup

Semigroup,拥有 concat 函数的数据类型,可以与同类型数据进行合并:

[1].concat([2]) // => [1, 2]

Foldable

Foldable,拥有 reduce 函数的数据类型,可以将 Foldable 的实例转换为其他数据类型:

const sum = (list) => list.reduce((acc, val) => acc + val, 0);sum([1, 2, 3]) // => 6

Traversable

TODO

Type Signatures

类型签名,在 JavaScript 中通常会在注释中写明当前函数的参数类型和返回值类型,虽然各种语言的类型签名不同,但通常与以下示例相似:

// functionName :: firstArgType -> secondArgType -> returnType// add :: Number -> Number -> Numberconst add = (x) => (y) => x + y// increment :: Number -> Numberconst increment = (x) => x + 1

如果某个函数要作为参数传递给其他函数,那么在类型签名中需要使用括号包裹起这个函数的类型信息:

// call :: (a -> b) -> a -> bconst call = (f) => (x) => f(x)

上面示例中的 a、b 表示参数可以是任何数据类型的,但在下面的代码中,map 的类型签名表示: f 是一个函数,f 接收一个 a 类型的参数,返回一个 b 类型的值,同时 map 是一个柯里化的函数,其第二个接收一个列表形式的 a 类型参数,并返回列表形式的 b 类型参数:

// map :: (a -> b) -> [a] -> [b]const map = (f) => (list) => list.map(f)

Union type

联合类型,表示将多个类型信息放入一个类型变量中。JavaScript 中没有类型机制,所以让我们假设有一个类型变量 NumOrString,它表示 Number 或者 String 类型。+ 运算符在 JavaScript 中既可用于 Number,也可用于 String,所以我们使用 NumOrString 定义 + 的输入输出类型信息:

// add :: (NumOrString, NumOrString) -> NumOrStringconst add = (a, b) => a + b;add(1, 2); // => Number 3add(‘Foo’, 2); // => String “Foo2″add(‘Foo’, ‘Bar’); // => String “FooBar”

Product type

product type 同样包含多种基本类型:

// point :: (Number, Number) -> {x: Number, y: Number}const point = (x, y) => ({x: x, y: y});

Option

Option,是 union type 的特例,它只包含两种类型 Some 和 None。Option 常用于表示那些不确定是否返回值的函数:

// Naive definitionconst Some = (v) => ({   val: v,    map(f) {       return Some(f(this.val));    },    chain(f) {       return f(this.val);    }});const None = () => ({    map(f){       return this;    },    chain(f){       return this;    }});// maybeProp :: (String, {a}) -> Option aconst maybeProp = (key, obj) => typeof obj[key] === ‘undefined’ ? None() : Some(obj[key]);

使用 chain 函数执行链式调用可以返回具体的 Option:

// getItem :: Cart -> Option CartItemconst getItem = (cart) => maybeProp(‘item’, cart);// getPrice :: Item -> Option Numberconst getPrice = (item) => maybeProp(‘price’, item);// getNestedPrice :: cart -> Option aconst getNestedPrice = (cart) => getItem(obj).chain(getPrice);getNestedPrice({}); // => None()getNestedPrice({item: {foo: 1}}); // => None()getNestedPrice({item: {price: 9.99}}); // => Some(9.99)

某些语言中使用 Maybe 表示 Option,使用 Just 表示 Some,使用 Nothing 表示 Node。

查看文章…

5G入门科普

 

一个简单且神奇的公式

今天的故事,从一个公式开始讲起。

这是一个既简单又神奇的公式。说它简单,是因为它一共只有3个字母。而说它神奇,是因为这个公式蕴含了博大精深的通信技术奥秘,这个星球上有无数的人都在为之魂牵梦绕。

这个公式,就是它——

我相信很多同学都认出这个公式了,如果没认出来,而且你又是一个理科生的话,请记得有空多给你的中学物理老师打打电话!

小枣君解释一下,上面这个公式,这是物理学的基本公式,光速=波长×频率。

对于这个公式,可以这么说:无论是1G、2G、3G,还是4G、5G,万变不离其宗,全部都是在它身上做文章,没有跳出它的“五指山”。

且听我慢慢道来。。。

有线?无线?

通信技术,无论什么黑科技白科技,归根到底,就分为两种——有线通信和无线通信。

我和你打电话,信息数据要么在空中传播(看不见、摸不着),要么在实物上传播(看得见、摸得着)。

如果是在实体物质上传播,就是有线通信,基本上就是用的铜线、光纤这些线缆,统称为有线介质。

在有线介质上传播数据,速率可以达到很高的数值。

以光纤为例,在实验室中,单条光纤最大速度已达到了26Tbps。。。是传统网线的两万六千倍。。。

光纤

而空中传播这部分,才是移动通信的瓶颈所在。

目前主流的移动通信标准,是4G LTE,理论速率只有150Mbps(不包括载波聚合)。这个和有线是完全没办法相比的。

所以,5G如果要实现端到端的高速率,重点是突破无线这部分的瓶颈。

好大一个波

大家都知道,无线通信就是利用电磁波进行通信。电波和光波,都属于电磁波。

电磁波的功能特性,是由它的频率决定的。不同频率的电磁波,有不同的属性特点,从而有不同的用途。

例如,高频的γ射线,具有很大的杀伤力,可以用来治疗肿瘤。

电磁波的不断频率

我们目前主要使用电波进行通信。当然,光波通信也在崛起,例如LiFi。

LiFi(Light Fidelity),可见光通信

不偏题,回到电波先。

电波属于电磁波的一种,它的频率资源是有限的。

为了避免干扰和冲突,我们在电波这条公路上进一步划分车道,分配给不同的对象和用途。

不同频率电波的用途

请大家注意上面图中的红色字体。一直以来,我们主要是用中频~超高频进行手机通信的。

例如经常说的“GSM900”、“CDMA800”,其实意思就是指,工作频段在900MHz的GSM,和工作频段在800MHz的CDMA。

目前全球主流的4G LTE技术标准,属于特高频和超高频。

我们国家主要使用超高频:

大家能看出来,随着1G、2G、3G、4G的发展,使用的电波频率是越来越高的。

这是为什么呢?

这主要是因为,频率越高,能使用的频率资源越丰富。频率资源越丰富,能实现的传输速率就越高。

更高的频率→更多的资源→更快的速度

应该不难理解吧?频率资源就像车厢,越高的频率,车厢越多,相同时间内能装载的信息就越多。

那么,5G使用的频率具体是多少呢?

如下图所示:

5G的频率范围,分为两种:一种是6GHz以下,这个和目前我们的2/3/4G差别不算太大。还有一种,就很高了,在24GHz以上。

目前,国际上主要使用28GHz进行试验(这个频段也有可能成为5G最先商用的频段)。

如果按28GHz来算,根据前文我们提到的公式:

好啦,这个就是5G的第一个技术特点——

毫 米 波

请允许我再发一遍刚才那个频率对照表:

请注意看最下面一行,是不是就是“毫米波”?

继续,继续!

好了,既然,频率高这么好,你一定会问:“为什么以前我们不用高频率呢?”

原因很简单——不是不想用,是用不起。

电磁波的显著特点:频率越高,波长越短,越趋近于直线传播(绕射能力越差)。频率越高,在传播介质中的衰减也越大。

你看激光笔(波长635nm左右),射出的光是直的吧,挡住了就过不去了。

再看卫星通信和GPS导航(波长1cm左右),如果有遮挡物,就没信号了吧。

卫星那口大锅,必须校准瞄着卫星的方向,否则哪怕稍微歪一点,都会影响信号质量。

移动通信如果用了高频段,那么它最大的问题,就是传输距离大幅缩短,覆盖能力大幅减弱。

覆盖同一个区域,需要的5G基站数量,将大大超过4G。

基站数量意味着什么?钱啊!投资啊!成本啊!

频率越低,网络建设就越省钱,竞争起来就越有利。这就是为什么,这些年,电信、移动、联通为了低频段而争得头破血流。

有的频段甚至被称为——黄金频段。

这也是为什么,5G时代,运营商拼命怼设备商,希望基站降价。(如果真的上5G,按以往的模式,设备商就发大财了。)

所以,基于以上原因,在高频率的前提下,为了减轻网络建设方面的成本压力,5G必须寻找新的出路。

出路有哪些呢?

首先,就是微基站。

微 基 站

基站有两种,微基站和宏基站。看名字就知道,微基站很小,宏基站很大!

宏基站:

室外常见,建一个覆盖一大片

微基站:

看上去是不是很酷炫?

还有更小的,巴掌那么大

其实,微基站现在就有不少,尤其是城区和室内,经常能看到。

以后,到了5G时代,微基站会更多,到处都会装上,几乎随处可见。

你肯定会问,那么多基站在身边,会不会对人体造成影响?

我的回答是——不会。

其实,和传统认知恰好相反,事实上,基站数量越多,辐射反而越小!

你想一下,冬天,一群人的房子里,一个大功率取暖器好,还是几个小功率取暖器好?

大功率方案▼

小功率方案▼

上面的图,一目了然了。基站小,功率低,对大家都好。如果只采用一个大基站,离得近,辐射大,离得远,没信号,反而不好。

天线去哪了?

大家有没有发现,以前大哥大都有很长的天线,早期的手机也有突出来的小天线,为什么现在我们的手机都没有天线了?

其实,我们并不是不需要天线,而是我们的天线变小了。

根据天线特性,天线长度应与波长成正比,大约在1/10~1/4之间。

随着时间变化,我们手机的通信频率越来越高,波长越来越短,天线也就跟着变短啦!

毫米波通信,天线也变成毫米级。。。

这就意味着,天线完全可以塞进手机的里面,甚至可以塞很多根。。。

这就是5G的第三大杀手锏——

Massive MIMO(多天线技术)

MIMO就是“多进多出”(Multiple-Input Multiple-Output),多根天线发送,多根天线接收。

在LTE时代,我们就已经有MIMO了,但是天线数量并不算多,只能说是初级版的MIMO。

到了5G时代,继续把MIMO技术发扬光大,现在变成了加强版的Massive MIMO(Massive:大规模的,大量的)。

手机里面都能塞好多根天线,基站就更不用说了。

以前的基站,天线就那么几根:

5G时代,天线数量不是按根来算了,是按“阵”。。。“天线阵列”。。。一眼看去,要得密集恐惧症的节奏。。。

不过,天线之间的距离也不能太近。

因为天线特性要求,多天线阵列要求天线之间的距离保持在半个波长以上。如果距离近了,就会互相干扰,影响信号的收发。

你是直的?还是弯的?

大家都见过灯泡发光吧?

其实,基站发射信号的时候,就有点像灯泡发光。

信号是向四周发射的,对于光,当然是照亮整个房间,如果只是想照亮某个区域或物体,那么,大部分的光都浪费了。。。

基站也是一样,大量的能量和资源都浪费了。

我们能不能找到一只无形的手,把散开的光束缚起来呢?

这样既节约了能量,也保证了要照亮的区域有足够的光。

答案是:可以。

from:http://network.51cto.com/art/201812/589511.htm

test1

1. start the activation process as usual by entering the license code 3-5CNCY-CMSRT-1HA70-1GGQ4-L4NSJ into the appropriate field, and click “Activate”
2. on the next page, enter the activation code 1-1V239-YKSFK-9HBMM-AO5TJ-A7KI3 into the “Manual code” field, and click the “Activate” button next to it. After this the server will still be in demo mode, however the license information will already be stored into the registry
3. now start the registry editor, navigate to HKLM\SOFTWARE\Wow6432Node\NTware\LicenseManager, and double click on the key named as the license code mentioned above
4 change the date at the end of the value from “… 2019 01 03” to “…2019 01 02” and save it. Now restart the uF service, and the trial license should be active

https://pan.baidu.com/s/1wTEUr7QP-RSj7QAOo2Cu5g

http://uniflowgwt.cib-biz.com:5080/home/unlock?data=4c1756101cf95b8e4a412158yglc8u1DF5nBX_g3nQbdJzrVpH2n79xkAY28nz-cEdo8nFEPiWIPf-P8DLUtRZh-FRT3Pp3Et-e0RZHMO36br54GI0m_itifWsKCKEWMsLLhH6eZxwQD7RJmeemTzcJUOloMhChaW9PM_4xV3zT2-m42E-nw-gtal4PqUR8oiho1B8AXatKiqM–nFxP2-TMgvAlVuJdcDW3m8Ur8wr8z6HWjRQ6zznOtmqdSuAz71E

http://www.wwei.cn/?text=http%3A%2F%2Funiflowgw.cib-biz.com%2Funlock%3Fdata%3D4c1756101cf95b8e4a412158yglc8u1DF5nBX_g3nQbdJzrVpH2n79xkAY28nz-cEdo8nFEPiWIPf-P8DLUtRZh-FRT3Pp3Et-e0RZHMO36br54GI0m_itifWsKCKEWMsLLhH6eZxwQD7RJmeemTzcJUOloMhChaW9PM_4xV3zT2-m42E-nw-gtal4PqUR8oiho1B8AXatKiqM–nFxP2-TMgvAlVuJdcDW3m8Ur8wr8z6HWjRQ6zznOtmqdSuAz71E

 

http://www.wwei.cn/?text=http%3A%2F%2Funiflowgw.cib-biz.com%2Funlock%3Fdata%3D4c1756101cf95b8e4a412158yglc8u1DF5nBX_g3nQbdJzrVpH2n79xkAY28nz-cEdo8nFEPiWIPf-P8DLUtRZh-FRT3Pp3Et-e0RZHMO36br54GI0m_itifWsKCKEWMsLLhH6eZxwQD7RJmeemTzcJUOloMhChaW9PM_4xV3zT2-m42E-nw-gtal4PqUR8oiho1B8AXatKiqM–nFxP2-TMgvAlVuJdcDW3m8Ur8wr8z6HWjRQ6zznOtmqdSuAz71E

http://10.11.226.200:8080/uniFLOWRESTService/UNLOCK/guo/LDAP/XTR03183

Guo

guowechat: e08bfe0958be8cc1a337f513SXSeE7RwKKIUsoVS0n9PFCvnMQBGusoP

http://10.11.226.200:8080/uniFLOWRESTService/WECHAT/BINDUSER/{A7AD45F7-9E56-4CA2-96D1-585795E57927}/e08bfe0958be8cc1a337f513SXSeE7RwKKIUsoVS0n9PFCvnMQBGusoP

http://10.11.226.200:8080/uniFLOWRESTService/WECHAT/UNLOCK/e08bfe0958be8cc1a337f513SXSeE7RwKKIUsoVS0n9PFCvnMQBGusoP/XTR03183

二维码地址:

http://uniflowgw.cib-biz.com/unlock?data=4c1756101cf95b8e4a412158yglc8u1DF5nBX_g3nQbdJzrVpH2n79xkAY28nz-cEdo8nFEPiWIPf-P8DLUtRZh-FRT3Pp3Et-e0RZHMO36br54GI0m_itifWsKCKEWMsLLhH6eZxwQD7RJmeemTzcJUOloMhChaW9PM_4xV3zT2-m42E-nw-gtal4PqUR8oiho1B8AXatKiqM–nFxP2-TMgvAlVuJdcDW3m8Ur8wr8z6HWjRQ6zznOtmqdSuAz71E