用ROS来做无人测试平台

MIT RACECAR,最早开始做racecar的,主要基于双目摄像头+激光雷达: http://fast.scripts.mit.edu/racecar/hardware/

UPENN F1 TENTH,同MIT racecar特别像,不过很多东西需自己组装,电调模块需自己焊接: http://f1tenth.org/

 

Daniel Tobias’ CAR – Cherry Autonomous Racecar,基于普通摄像头,利用深度学习来做的

Daniel Tobias’ CAR – Cherry Autonomous Racecar

 

Jetsonhacks Racecar,仿照MIT racecar: http://www.jetsonhacks.com/category/robotics/jetson-racecar/

台湾仿照MIT-RACECAR做的低成本的hypha-racecar

伯克利大学做的不需要激光雷达的racecar

http://www.barc-project.com/setup/

autonomous bicycle

try the following configuration:

https://github.com/francisc0garcia/autonomous_bicycle

 

微服务的低风险演变

继续来深入探讨!在之前的文章(第一部分)中,我们为本篇文章建立了一个上下文环境(以便于讨论)。一个基本原则是,当微服务被引入到现有架构中时,不能也不应该破坏当前的请求流程(request flows)。“单体应用(monolish)”程序依然能带来很多商业价值(因此仍将在新的时代被使用,编者注),我们只能在迭代和扩展时,尽可能地减少其负面影响,这过程中就有一个经常被忽略的事实:当我们开始探索如何从单体应用过渡到微服务时,会遇到一些我们不愿意碰到的难题,但显然我们不能视而不见。如果你还没读过这段内容,我建议你再回去看看第一部分。同时也可以参考什么时候不要做微服务[0]。

关注推特上的(@christianposta)或访问http://blog.christianposta.com,以获取最新更新和讨论。

在此前的第一部分,想解决的问题有:

  • 如何可以有效可靠地生成微服务。以及如何建立一个持续交付的系统。
  • 如何能够对服务和单体应用等对象进行测试。
  • 如何在新的微服务中能安全地引入任何变更,包含灰度上线、金丝雀测试等等
  • 如何将流量路由到新的服务中去,以保证启用/终止任何新的特性或更改都不会出现问题
  • 如何面对许多棘手的数据集成挑战

一、技术层面

以下这些技术在我们的实践过程中将具备一定的指导作用:

• 开发人员服务框架(Spring Boot [1],WildFly [2],WildFly Swarm [3])

• API设计(APICur.io [4])

• 数据框架(Spring Boot Teiid [5],Debezium.io [6])

• 集成工具(Apache Camel [7])

• Service Mesh(Istio Service Mesh [8])

• 数据库迁移工具(Liquibase [9])

• 灰度上线/特性标记框架(FF4J [10])

• 部署/CI-CD平台(Kubernetes [11]/OpenShift [12])

• Kubernetes开发工具(Fabric8.io [13])

• 测试工具(Arquillian [14],Pact [15]/Arquillian Algeron [16],Hoverfly [17],Spring-Boot Test [18],RestAssured [19],Arquillian Cube [20])

我使用的是http://developers.redhat.com上的TicketMonster教程,显示从单体应用到微服务的演变,如果感兴趣的话可以关注,你还可以在github上找到相关的代码和文档(文档还在编写中):https://github.com/ticket-monster-msa/monolith

让我们一步步地读完第一部分 [21],具体来看看每一步应该怎么实施。中间还会引入上一部分中出现的一些注意事项,并在当前背景下再讨论一遍。

二、了解单体式应用

回顾下注意事项:

  • 单体式应用(代码和数据库模型)很难变更
  • 变更需要整体重新部署和团队间高度的协调
  • 需要进行大量测试来做回归分析
  • 需要一个全自动的部署方式

可以的话,尽可能为单体应用安排大量的测试,哪怕不是一直有效。随着演变的开始,无论是添加新功能还是替换现有功能,我们都需要清楚了解任何更改可能产生的影响。Michael Feathers 在他《重构遗留代码》[22]的书中,将“遗留代码(legacy code)”定义为没有被测试所覆盖的代码。像JUnit和Arquillian这样的工具就很能帮到大忙。使用Arquillian,可以任意选择远程方法调用的接口的颗粒大小(fine grain or coarse grain),然后打包应用程序,不过仍需要用适当的模拟等方式,来运行打算被测试的一部分程序。例如,在单体应用(TicketMonster)中,我们可以定义一个微部署(micro-deployment),用来将原有的数据库替换为内存数据库,并预加载一些样例数据。Arquillian适用于Spring Boot应用、Java EE等。在本例中,我们将测试一个Java EE的单体架构:

public static WebArchive deployment() {
  return ShrinkWrap
    .create(WebArchive.class, "test.war")
    .addPackage(Resources.class.getPackage())
    .addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
    .addAsResource("import.sql")
    .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
    // Deploy our test datasource
    .addAsWebInfResource("test-ds.xml");
}

更有意思的是,嵌入在运行环境中的测试可以用来验证内部工作的所有组件。例如,在上面的一个测试中,我们可以将BookingService注入到测试中,并直接运行:

@RunWith(Arquillian.class)
public class BookingServiceTest {

    @Deployment
    public static WebArchive deployment() {
        return RESTDeployment.deployment();
    }

    @Inject
    private BookingService bookingService;

    @Inject
    private ShowService showService;

    @Test
    @InSequence(1)
    public void testCreateBookings() {
        BookingRequest br = createBookingRequest(1l, 0, new int[]{4, 1}, new int[]{1,1}, new int[]{3,1});
        bookingService.createBooking(br);

        BookingRequest br2 = createBookingRequest(2l, 1, new int[]{6,1}, new int[]{8,2}, new int[]{10,2});
        bookingService.createBooking(br2);

        BookingRequest br3 = createBookingRequest(3l, 0, new int[]{4,1}, new int[]{2,1});
        bookingService.createBooking(br3);
    }

完整的示例请参阅TicketMonster单体应用模块[23]中的BookingServiceTest。

测试的问题解决了,那么部署呢?

Kubernetes已成为容器化服务或应用程序的实际部署平台。Kubernetes处理诸如健康度检查、扩展、重启、负载平衡等事项。对于Java开发人员来说,像fabric8-maven-plugin[24]这样的工具甚至都可以用来自动构建容器或docker镜像,并生成任意部署资源文件。OpenShift[25]是Red Hat的Kubernetes的产品化版本,其中增加了开发人员的功能,包括CI/CD pipelines等。

无论是微服务、单体应用还是其他平台(比如能够处理持续的工作负载,即数据库等),Kubernetes/OpenShift都是一个适用于应用程序/服务的部署平台。通过Arquillian,容器和OpenShift pipelines,可以持续地将变更引入生产环境。顺便来看一下openshift.io[26],它将开发经验与自动CI/CD pipelines、SCM集成、Eclipse Che[27]开发人员工作区、库扫描等结合在一起。

目前,生产负载指向单体应用。如果我们翻到它的主页,我们会看到这样的内容:

接下来,让我们开始做一些改变…

三、提取用户界面UI

回顾下注意事项:

  • 一开始,先不要变更单体式应用;只需将UI复制粘贴到单独的组件即可
  • 在UI和单体式应用间需要有一个合适的远程API—但并非所有情况下都需要
  • 增加一个安全层
  • 需要用某种方法以受控的方式将流量路由或分离到新的UI或单体式应用,以支持灰度上线(dark launch)/金丝雀测试(canary)/滚动发布(rolling release)[28]

如果我们看下TicketMonster UI v1 [29]代码,就会发现它非常简单。静态HTML/JS/CSS组件已经被移到它自己的Web服务器,还被打包到一个容器中。通过这种方式,我们可以在单体应用之外对它进行单独部署,并独立更改或更新版本。这个UI项目仍然需要与单体应用对话来执行它的功能,所以应该是公开一个REST接口,让UI可以与之交互。对于一些单体应用来说,这说起来容易做起来难。如果你想从遗留代码中打包出来一个不错的REST API,又遇到了挑战,我强烈推荐你看看Apache Camel,尤其是它的REST DSL。

比较有意思的是,实际上单体应用并没有被改变。它的代码没有变动,同时新UI也部署完成。如果查看Kubernetes,我们会看到两个单独的部署对象和两个单独的pod:一个用于单体架构,另一个用于UI。

即使tm-ui-v1用户界面部署完了,也没有任何流量进入这个新的TicketMonster UI组件。为了简单起见,即使这个部署并没有承载生产流量,而是ticket-monster这个单体应用在承担所有流量,我们仍然可以把它当作一个简单的灰度上线。相关的UI端口仍旧可以访问:

接下来,用kubectl cli 工具从本地端口转发到特定的pod(端口80上的tm-ui-v1-3105082891-gh31x),并将其映射到本地端口8080。现在,如果导航到http://localhost:8080,应该得到一个新版本UI(注意突出显示的文本部分,表明这是一个不同的UI,但它直接指向单体应用)

如果我们这个新版本还算满意,就可以开始将流量引入进来。为此,我们将使用Istio service mesh [30]。Istio是用于管理由入口点和服务代理组成的网格控制层(control plane)。我已经写了一些关于像Envoy这样的数据层[31]以及service mesh[32]的文章。我个人强烈建议看看Istio的全部功能。接下来的几段内容,我们会围绕整个项目的全过程来依次展开讨论Istio的各项功能。如果控制层和数据层之间的区分让你困惑,请查看Matt Klein[33]撰写的博客。

我们将从使用Istio Ingress Controller[34]开始。该组件允许使用Kubernetes Ingress规范来控制流量进入Kubernetes集群。一旦安装了Istio,我们可以这样创建一个入口资源,将流量指向Ticket Monster UI的Kubernetes服务,tm-ui:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: tm-gateway
  annotations:
    kubernetes.io/ingress.class: "istio"
spec:
  backend:
    serviceName: tm-ui
    servicePort: 80

一旦有了入口,就可以开始应用Istio路由规则[35]。例如,有一个规则,“任何时候有人试图与在Kubernetes中运行的tm-ui服务对话,将它们指向服务的第一版本v1”:

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: tm-ui-default
spec:
  destination:
    name: tm-ui
  precedence: 1
  route:
  - labels:
      version: v1

如此,我们能够更好地控制进入集群甚至深入集群内部的流量。在这个步骤的最后,我们会将所有的流量都转到tm-ui-v1部署。

四、从单体架构移除UI

回顾下注意事项

  • 从单体式应用中移除UI组件
  • 需要对单体式应用进行最小的变更(弃用/删除/禁用UI)
  • 不停机的前提下,再次使用受控的路由/整流方法来引入这种变更

这一步相当直接,通过删除静态UI组件来更新单体应用(删除的部分已经转移到了tm-ui-v1部署)。既然应用程序已经被释放成为一个单体应用的服务,以供UI,API或者其他一些程序调用,那么也可以对这个部署进行一些API层级的更改。而如果想对API进行一些更改,就需要部署一个新版本的UI。此处我们部署了backend-v1服务以及一个新的UI tm-ui-v2,可以利用后端服务中的这个新API。

来看看在Kubernetes集群中的部署情况:

此时,ticket-monster和tm-ui-v1正接收实时流量。backend-v1和指向它的UI–tm-ui-v2则没有流量负载。需要注意的一点是,backend-v1部署与ticket-monster部署共享数据库,但各自有略微不同的外向API(outward facing API)。

现在,新的backend-v1和tm-ui-v2组件已经部署到生产环境中。现在是时候把注意力放在一个简单而又重要的事实上:生产环境部署发生了改变,但是它们还没有发布。在turblabs.io [36]一些优秀的博客更详细地阐述了这一点[37]。现在,我们有机会部署一个非正式的灰度发布。也许我们希望这个部署慢慢来,首先面向内部用户,或者先对某个特定区域内,特定设备的部分用户进行部署等等。

既然已经有了Istio,接下来看看它能做些什么。我们只想为内部用户做一个灰度发布。我们可以用各种方式来识别内部用户,诸如headers、IP等等,在本例中,如果HTTP header带有 x-dark-launch: v2 这样的文本内容,则该请求将会被路由到新的backend-v1和tm -ui-v2服务中。以下是istio路由规则的样子:

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: tm-ui-v2-dark-launch
spec:
  destination:
    name: tm-ui
  precedence: 10
  match:
    request:
      headers:
        x-dark-launch:
          exact: "v2"
  route:
  - labels:
      version: v2

任意用户身份登录主页时,应该可以看到当前的部署(即指向ticket-monster单体应用的tm-ui-v1):

现在,如果改变浏览器中的消息头(例如使用Firefox的修改消息头工具或其他类似工具),我们应该被路由到已灰度上线的服务(指向backend-v1的tm-ui-v2):

然后点击“开始”开始修改消息头并刷新页面:

现在,我们已经被重定向到服务的灰度发布版本。由此,可以通过做一个金丝雀发布(这里也许引1%的实时流量到新部署),来向客户群发布,同时,如果没有负面效果的话,那么就缓慢增加流量负载(5%、10%、50%等)。以下是Istio路由规则的一个例子,其将v2流量以1%进行金丝雀发布:

 apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: tm-ui-v2-1pct-canary
spec:
  destination:
    name: tm-ui
  precedence: 20
  route:
  - labels:
      version: v1
    weight: 99
  - labels:
      version: v2
    weight: 1

能“看到”或“观察”这个版本的影响是至关重要的,稍后我们会进一步讨论。另外请注意,这种金丝雀发布方式目前正在架构外围完成,但是也可以通过istio控制内部服务间通讯/交互时采用金丝雀的方式。在接下来的几个步骤中,我们将开始看到。

五、引入新服务

回顾下注意事项

  • 我们要关注被抽取的服务的API设计或边界
  • 可能需要重写单体式应用中的某些内容
  • 在确定API后,将为该服务实施一个简单的脚手架或者place holder
  • 新的Orders服务将拥有自己的数据库
  • 新Orders服务目前不会承担任何流量

在这一步中,我们开始设计我们所设想的新订单服务的API,在做一些领域驱动设计练习时,我们常常需要确定一些边界(boundaries),新的API应该更多的与这种边界相一致。这里可以使用API建模工具来设计API,部署一个虚拟化的实施,并且随服务消费者的需求变化 一起迭代,而不是一开始花费大量的精力去构建,最后又发现需要不断修改。

在TicketMonster重构时,需要在单体应用中保留一个上文所说的API,以便在最初的服务拆分时尽可能轻松并且降低风险。无论是哪种情况,有两个给力的工具可以帮到我们:一个是网页式的API设计器,apicur.io[38],一个是测试/ API虚拟化工具,Hoverfly[39]。Hoverlfy是模拟API或捕获现有API流量的好工具,可以用来模拟mock端点。

如果我们正在构建一个新的API,或在使用领域驱动设计方法后,想看看API什么样,可以使用apicur.io工具建立一个Swagger/Open API的规范。

在TicketMonster这个例子中,我们通过在代理模式下启动hoverfly,并使用hoverfly捕获从应用程序到后端服务的流量。我们可以在浏览器设置中设置HTTP代理,从而通过hoverfly发送所有流量。这将把每个请求/响应对(request/response pair)的仿真存储在JSON文件中。这样我们就可以在Mock里使用这些请求/响应对,或者更进一步,用它们开始编写测试,以规范具体的实现代码中的一些行为。

对于所关注的请求或响应对(response pairs),我们可以生成一个JSON架构并用于测试中,参见https://jsonschema.net/#/editor

例如,结合使用Rest Assured和Hoverfly,可以调用hoverfly模拟,并确定该响应符合我们预期的JSON架构:

@Test
public void testRestEventsSimulation(){
    get("/rest/events").then().assertThat().body(matchesJsonSchemaInClasspath("json-schema/rest-events.json"));
}

在新的订单服务中,可以查看HoverflyTest.java [40]测试。有关测试Java微服务的更多信息,请查阅Manning这本给力的书,《测试Java微服务》[41],我的一些同事Alex Soto Bueno[42]、Jason Porter[43]和Andy Gumbrecht[44]也参与了这本书的撰写。

由于这篇博文已经很长了,我决定将最后的部分单独写成本主题的第三部分,其中将涉及在单体应用和微服务之间管理数据、服务消费的契约测试(consumer contract testing), 功能发布控制( feature flagging),甚至更复杂的istio路由等内容。本系列的第四部分将展示一个包含上述内容的实操Demo,使用负载仿真测试(load simulation tests)和故障注入(fault injections)。欢迎访问我的网站 [45]和关注我的Twitter [46]。

原文链接:http://blog.christianposta.com/microservices/low-risk-monolith-to-microservice-evolution-part-ii/

参考地址:

[0] http://blog.christianposta.com/microservices/when-not-to-do-microservices/

[1] https://projects.spring.io/spring-boot/

[2] http://wildfly.org/

[3] http://wildfly-swarm.io/

[4] http://www.apicur.io/

[5] https://github.com/teiid/teiid-spring-boot

[6] http://debezium.io/

[7] http://camel.apache.org/

[8] https://istio.io/

[9] http://www.liquibase.org/

[10] https://ff4j.org/

[11] https://kubernetes.io/

[12] https://www.openshift.org/

[13] https://fabric8.io/

[14] http://arquillian.org/

[15] https://github.com/pact-foundation/pact-specification

[16] http://arquillian.org/arquillian-algeron/

[17] https://hoverfly.io/

[18] https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

[19] http://rest-assured.io/

[20] http://arquillian.org/arquillian-cube/

[21] http://blog.christianposta.com/microservices/low-risk-monolith-to-microservice-evolution/

[22] https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052

[23] https://github.com/ticket-monster-msa/monolith/blob/master/monolith/src/test/java/org/jboss/examples/ticketmonster/test/rest/BookingServiceTest.java

[24] https://maven.fabric8.io/

[25] https://www.openshift.com/

[26] https://openshift.io/

[27] https://www.eclipse.org/che/

[28] http://blog.christianposta.com/deploy/blue-green-deployments-a-b-testing-and-canary-releases/

[29] https://github.com/ticket-monster-msa/monolith/tree/master/tm-ui-v1

[30] https://istio.io/

[31] http://blog.christianposta.com/microservices/00-microservices-patterns-with-envoy-proxy-series/

[32] http://blog.christianposta.com/microservices/application-network-functions-with-esbs-api-management-and-now-service-mesh/

[33] https://medium.com/@mattklein123/service-mesh-data-plane-vs-control-plane-2774e720f7fc

[34] https://istio.io/docs/tasks/traffic-management/ingress.html

[35] https://istio.io/docs/reference/config/traffic-rules/routing-rules.html

[36] https://www.turbinelabs.io/

[37] https://blog.turbinelabs.io/deploy-not-equal-release-part-one-4724bc1e726b

[38] http://www.apicur.io/

[39] https://hoverfly.io/

[40] https://github.com/ticket-monster-msa/monolith/blob/master/orders-service/src/test/java/org/ticketmonster/orders/HoverflyTest.java

[41] https://www.manning.com/books/testing-java-microservices

[42] https://twitter.com/alexsotob

[43] https://twitter.com/lightguardjp

[44] https://twitter.com/andygeede?lang=en

[45] http://blog.christianposta.com/

[46] https://twitter.com/christianposta

 

越忙越需要 GTD

GTD 最早于2002年由 David Allen 提出,自他撰写的畅销书 Getting Things Done 出版以来,“GTD” 已经成了一个术语,影响了大批致力于推动时间管理的图书及博客作者、企业管理者,甚至因此诞生了一批基于 GTD 的效率工具。它的内容从不过时,与时代发展同步行进,影响着全球成千上万的效率实践者。

专业人士

人们之所以对此乐此不疲,是因为 GTD 与[我想掌控自己的生活][我希望得到尊重与回报][我想要的是什么][我希望能着眼未来]关联密切——它并不仅仅解决工作问题,它试图帮人们理顺每一个日常,从全局角度审视人生。就像一个精密的神经系统,从任何角度唤醒神经元,都有对应的触发路径与反馈。

正在谱写乐曲的巴赫,正在向欧洲行军的拿破仑,正在创造艺术品的安迪沃霍尔,他们都需要处理大量信息并转化为自己的行为决策,以此体现自我意志。一个处理纷繁信息并无时无刻不在作出正确决策的人,在今天被称为专业人士。

毫无疑问,从 GTD 中获利最大的是专业人士。这里有一个虚拟形象,或许你见到过与他相似的人——前一晚工作了很长时间,他选择把工作带回家,结束已经是凌晨3点。他感到自己很少有时间去做喜欢的事情,比起这一点,没完成工作会更让他有负罪感。大部分时间他应对自如,但仍然感到重重压力。有些时候,他会把工作拖到最后时刻,再灵感迸发地完成它,每当这时候,他总后悔没有早点进入 aha moment.第二天早起赶到办公室,办公桌上堆积了许多很久没触碰过的文件。处理邮件到一半,同事拿着合同过来打断他,交谈完毕日历正好弹出5分钟后的会议提醒。结束后已经是中午,他和同事们在办公室一起用餐,想到自己上周忘记了一个重要约会。他感到很难让自己停下来,保持忙碌有一种安全感。他的勤勉为自己换来了尊重,在成功又琐碎的氛围中,他无暇着眼未来,他需要 GTD 来管理自己的工作与生活——

五个步骤

“通过行动让自己感觉良好,要比通过让自己感觉良好来实现更好的行动容易得多。”

  1. 收集引起我们注意的事务和信息

时间管理四象限法则把事务分为四种优先级:既紧急又重要、重要但不紧急、紧急但不重要、既不紧急也不重要。大部分时候,人们最优先处理第一种事务,把第二种事务作为长线供养,后两种被视为归档事务被忽略。这些事永远客观存在于生活中,人们无法如同精密仪器时刻不逾陈规。GTD 建议把所有事务不计前嫌统归到一起。有时候,正是一些前置的关联事务,让我们真正开始解决问题时毫不费力。另外 GTD 的两分钟法则认为:如果采取某项行动最多需要两分钟,就应该在作出决定的一刹那实施行动,这可以有效解决部分紧急任务。

“提升个人工作效率低最佳手段之一,就是拥有你乐于使用的管理工具。”

养成收集的习惯至关重要,匹配的管理工具能够安放所有收集到的事务。找到一款项目与任务管理工具,梳理所有引起注意的事务和信息。Teambition 的任务管理功能可以有效解决此类问题,在这里,你可以创建任何主题的事务,在下一步,我们将进行进一步归纳整理。

  1. 理清每个项目的意义和相关措施

进一步梳理经上一步统计到的所有事务。事务分为两种,一种是没有行动解决方案的,比如一个想法或者一个软件升级提醒,把这类事务安排到未来的日程,在日程下做好附注。另一类需要进一步行动解决方案的事务,比如参加会议、回顾工作或者洗车,设定每一步具体动作的完成时间。有时委派他人代劳也是完成事务的方法,如果你自己不是最佳的执行人选。在 Teambition 中,你可以指派任意任务给最适合完成它的人,无论他是你的同事、合作伙伴、学习搭档还是家庭成员。

  1. 组织整理:建立清单

到这一步事务开始变得明朗,大部分事务已经在时间线设定好位置。现在需要做的,是根据不同的情境,把事务归属到不同类别的清单。比如,“立即执行”清单,里面都是在2分钟内就能完成的简单任务。“等待”清单,指关注结果不关注过程的事,比如等待预定的电影票、用户对新提议的回复、等待新购买的空气净化器等。“外出事宜”清单,出门前看一看清单,能有效提升一路上能解决事务的数量与效率。Teambition 的自定义任务面板可以随心所欲地设定为不同主题的清单,你只需要在使用时重新打开他们就能一目了然。你有什么关于清单的好主意,请留言或邮件告诉给我们。

  1. 进行思考回顾

将所有任务在进行优先级排期与清单归纳后,进入回顾阶段——查看事务被着手解决的合理性。首先查看日程表,再看一看行动清单,根据情境选择恰当的回顾内容。人们总会放任自己的大脑纠缠于大量超过自身承载能力的任务数量。回顾的优点在于,尽可能冷静地重审自己的计划,重新夺回主动权而不至于疲于奔波。

  1. 选择行动

终于到了执行阶段,根据4种标准确认下一步的行动:情境、时间、精力、以及优先级。优先执行事先安排好的工作,再处理突发事件。有突发事件时,判断它是否值得你停下目前正在进行的工作,一旦被打断工作,重新进入工作状态将付出较高时间成本。实际上,并不存在被打断工作这回事,只是对新事物管理不善。

三个原则

三个原则的 Getting things done 精髓:

  1. 养成收集的习惯,整合所有工作与生活事务;
  2. 确定“下一步行”;
  3. 学会关注结果。

GTD 强调收集、思考并行动的重要性,五个步骤的最后一步才是真正的执行,前四步无一不是在确保事务解决的合理性与可行性。剥离开或关联或疏离的事项,一次只做一件事。

“一次只做一件事,沉浸通常伴随对特定任务的全身心关注,而且你通常会有掌控感,感到目标清晰。处于沉浸状态的个人通常明白接下来会发生什么事情,并能在执行整个任务的过程中得到及时反馈。”——认知科学家 Mihaly Csikszentmihalyi 《沉浸:关于最佳体验的心理学》

初入 GTD 的实践者,随着习惯的养成与方法的熟悉,会逐渐形成一套以自己为中心的地图,包含自己的角色、职责和兴趣,基于个人发展方向和需求而不断迭代,最终形成综合的全面生活管理系统。在任何情况下,用 GTD 的方法辅以匹配的工具,应对不同属性的事务,提升工作与生活效率。

Open source software for autonomous cars

Software

Honda Riding Assist原理及疑问讲解

在几天之前,大家看完Honda发布的自动平衡技术之后,相信心中都有不少疑问。大家都想知道其运作原理,老手会担心会否影响电单车原有的操控,买家也想知道新技术何时才能应用在市贩型号之上。今次为大家带来好消息,今天Honda进一步发表riding Assist的技术细节,保证人人都会开心满意。
当车速达到可以平衡车身时,前叉倾角收缩,回复至街车水平。

在规格表当中,我们经常会看到前倾角和拖曳距这两个名词,前倾角讲的是角度,拖曳距讲的是长度。前倾角是转向头轴线和垂线的夹角。而拖曳距就是轮轴与地面的垂线与转向头轴线与地面交点的距离。

前倾角和拖曳距直接影响着车子的操控性能,尤其是在过弯时候的表现。加大前倾角有两种效果,首先是会增加轴距。其次是会增加拖曳距。拖曳距太大会导致电单车转向效果变差,但同时直线会更稳定。

Honda所采用的名词分别是Positive和Negative trail length,比较难明和难记。看下图便较易记住:

上图是正常行车状态,头叉角度较直。
下图是慢车或停车状态,头叉较为向外哨出。
Honda的Riding Assist技术,便是主动地改变拖曳距,在慢车或静止时增长,在正常行车时变短。由于较长的拖曳距,在转动把手时会有更明显的反向作用,当车身倾倒时将把手向倾斜的方向转动,会起到明显的支撑作用。Honda便是在鹅颈位置装上两个马达,一个负责改变前倾角和拖曳距,另一个负责转动把手。透过感应器收集车身动态数据,两个马达同时运作便能自动平衡电单车,停车都不用落脚 。
好消息是,这项由本田机械人科技所延伸出来的应用技术Riding Assist,只涉及头担部份改动,其余部份包括车架、引擎和尾担都可沿用现有设计。理论上任何电单车都可以配上这套自动平衡系统,以后Honda电单车发售同一型号时,除了有ABS和DCT选择之外,还可以有Riding Assist,RA的选择。

试想像一下,座高870mm的CRF1000,当配备Riding Assist之后,身高只有160mm的女骑士,也不用担心落脚的问题。这项发明,将会彻底改变骑士选择机车的方向。

完美运行“Pokemon Go” 操作指南

出国

 

 

不多啰嗦了,内容如题,现在在中国大陆也可以正常玩儿 Pokemon Go 了!无论你用的是 iPhone 还是 Android 手机。

今天有消息称,中国区已经解开了锁区,能够正常游戏,目前在中国区连上 VPN 登陆后,就能够正常游戏,且在游戏中无需科学上网,不会出现之前搜索不到  GPS 以及没有任何精灵的情况。

但要注意的是, GPS 解锁不代表 Pokemon Go 在中国区已经上线,还是需要玩家们去美国区或澳大利亚区 App Store 进行下载。

iOS 平台下载方式

目前 Pokemon Go 在美国、澳大利亚区的 App Store 正式上线,并且在上线后就霸占了免费榜的首位。如果你已经有美区账号,直接登录 App Store 进行下载即可。

如果你还没有美区 App Store 账号,可以关注爱范儿(微信:ifanr 或扫描下方二维码)微信公众号,回复【美区】以获取最简单的美国 App Store 账号注册方法。

屏幕快照-2016-07-06-下午16.32.19-下午

Android 平台下载教程

Android 端的 Pokemon Go 是在 Google Play 商店上线的。爱范儿为读者们准备了Pokemon Go 的 Android 版本 APK 安装包,关注爱范儿微信公众号(在公众号搜索 ifanr)回复【宝可梦】或者【精灵】或者【Pokemon】就可以获取下载链接。

下载前的科普,什么是 Pokemon Go?

146566760274487800_a580xH

Pokemon Go 是任天堂推出的第一款手机增强现实游戏,玩家拿着手机,走到街上,就可以捕捉精灵宝可梦,升级精灵,进入道馆进行对战。具体玩儿法是怎样的,看下面这个视频与游戏截图就明白了。

腾讯视频

12.pic

下载游戏,登入 Google 账号进入游戏。

11.pic

设置自己的玩家形象,进入教程,先抓只妙蛙种子练练手,挥动精灵球的体验,很像曾经的游戏“扔纸团”。

13.pic

之后就可以进行游戏了,在不同的地点,玩家会碰到不同的精灵,并且在部分地标,可以收集免费的精灵球。

精灵训练师们快行动起来吧,如果你想进一步了解 Pokemon 的玩法,可以阅读下面这篇详细试玩体验。

Pokemon GO 试玩体验:要不是人民币玩家,就去苦练扔球吧

在nginx上使用php代理运行cgi程序

我们用到的很多开源程序比如mailman, nagios等等,都有WEB端管理界面。在那个Apache一家独大的年代,这个问题可以很好解决,因为apache本身可以运行cgi程序。但随着 nginx服务器的大规模应用,而恰好nginx又没有cgi模块,所以我们不得不采用一些变通的手段来解决它。

在网上广为流传的解决方法是一个老外写的perl脚本,但这个脚本本身有很多问题,而且需要在后台启动一个守护进程,本人对用perl写的网络服务守护进程的稳定性很怀疑,在看了它的代码后,发现用PHP即可很好的解决这个问题。

CGI其实本质上就是一个普通的二进制程序,你可以在后台直接运行它。而服务器要做的事就是将WEB传递的变量作为参数传递给这个程序并执行,而将执行返回的结果显示到页面上。

明白了这个道理,我们就可以开始着手解决这个问题了。其过程无非就是将PHP作为一个proxy,使其运行指定的程序,并把程序输出结果echo出来。

我们把这个PHP脚本命名为cgi.php,把它随便放到一个你认为合适的位置,然后用rewrite将后缀为cgi的请求都转发到cgi.php上。以下为参考的配置格式

#rewrite cgi请求到cgi.php上,并把cgi文件名作为php的pathinfo
rewrite ^/nagios/cgi-bin/(.*) /cgi.php/$1 break;
 
location /nagios/
{
    gzip off;
    alias /usr/local/nagios/share/;
    index index.html index.htm index.php;
}
 
location ~ .*\.php(\/.*)*$ {
    fastcgi_pass    127.0.0.1:9000;
    fastcgi_index   index.php;
    include fcgi.conf;
    fastcgi_param SCRIPT_FILENAME /usr/local/nagios/share$fastcgi_script_name;
 
    #pathinfo必须设置
    fastcgi_param  PATH_INFO $fastcgi_script_name;
 
    #以下两个为cgi.php需要用到的变量名,分别为cgi程序目录,和cgi默认index程序
    fastcgi_param  CGI_BASE  /usr/local/nagios/sbin;
    fastcgi_param  CGI_INDEX status.cgi;
}

注意上面配置文件的注释部分,在你自己设置的时候必须填上合适的值。下面就是最重要的cgi.php文件了

<?php
/*
   use php to execute mailman cgi app
   hack by 70 (magike.net@gmail.com)
   https://joyqi.com
 */
 
// get cgi base from fastcgi param
$cgi_base = '';
if (isset($_SERVER['CGI_BASE'])) {
    $cgi_base = rtrim($_SERVER['CGI_BASE'], '/') . '/';
} else {
    die('PLEASE CONFIGURE YOUR CGI_BASE PARAM');
}
 
// get pathinfo
$pathinfo = '';
if (isset($_SERVER['PATH_INFO'])) {
    $pathinfo = $_SERVER['PATH_INFO'];
} else if (isset($_SERVER['CGI_INDEX'])) {
    $pathinfo = $_SERVER['CGI_INDEX'];
} else {
    die('PLEASE CONFIGURE YOUR PATH_INFO PARAM');
}
 
// get real cgi path
$cgi_path = $cgi_base;
$cgi_file = trim($pathinfo, '/');
$cgi_file_levels = explode('/', $cgi_file);
$cgi_file_exists = false;
 
while (count($cgi_file_levels) > 0) {
    $cgi_path = $cgi_path . '/' . array_shift($cgi_file_levels);
 
    if (is_file($cgi_path)) {
        $cgi_file_exists = true;
        break;
    }
}
 
if (!$cgi_file_exists) {
    die('NOT EXISTS PAGE!' . $cgi_file);
}
 
$cgi_pathinfo = '';
if (!empty($cgi_file_levels)) {
    $cgi_pathinfo = '/' . implode('/', $cgi_file_levels);
}
 
if (is_readable($cgi_path)) {
 
    $descriptorspec = array(
            0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
            1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
            2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
            );
 
    $cwd = $cgi_base;
    $env = $_ENV;
 
    $env['SCRIPT_FILENAME'] = $cgi_path;
    $env['SCRIPT_NAME'] = $cgi_file;
    $env['DOCUMENT_ROOT'] = CGI_BASE;
    $env['PATH_INFO'] = $cgi_pathinfo;
 
    // http auth support (nagios etc.)
    if (isset($_SERVER['PHP_AUTH_USER'])) {
        $env['REMOTE_USER'] = $_SERVER['PHP_AUTH_USER'];
    }
 
    $process = proc_open($cgi_path, $descriptorspec, $pipes, $cwd, $env);
    if (is_resource($process)) {
        $stdin = file_get_contents("php://input");
 
        if (!empty($stdin)) {
            fwrite($pipes[0], $stdin);
            fclose($pipes[0]);
        }
 
        //stream_set_blocking($pipes[1], 0);
        stream_set_timeout($pipes[1], 3);
        $result = stream_get_contents($pipes[1]);
        fclose($pipes[1]);
        $return_value = proc_close($process);
 
        list($header, $body) = preg_split("/\r?\n\r?\n/", $result, 2);
 
        $headers = explode("\n", $header);
        foreach ($headers as $line) {
            header(trim($line));
        }
 
        echo $body;
    } else {
        die('ERROR APPLICATION!');
    }
 
} else {
    die('ERROR PAGE!' . $cgi_path);
}

世界,你好!

欢迎使用 WordPress。这是系统自动生成的演示文章。编辑或者删除它,然后开始您的博客!