月度归档:2018年04月

从锦囊妙计想到的08–分布式计算

——分布式任务协作, 数量老是不正确的问题! 每次都不对, 谁来救救我哦!!

前情回顾

前面几回都是在烙饼, 一开始烙饼给关羽累坏了,  原因是关羽不会循环语句, 把循环用顺序结构写, 造成400张饼的锦囊要写很久, 累坏了关羽。 关羽学会了循环语句, 但是立刻把张飞累死了, 原来关羽的循环语句没有退出语句,因此张飞永远干不完(这个类型的循环是死循环), 只有关羽改进了循环,在循环中添加了判断语句若是满足了数量的要求, 就退出循环,这样张飞就不那么累了。可是问题是张飞烙饼的速度还是慢, 因此恰巧天神哪咤经过传送张飞三头六臂的办法(其实就是开多线程),这样张飞就可以在单位时间内生产出3倍数量的饼来, 可以加快时间。

出名的张飞

俗话说, 人怕出名猪怕壮, 张飞自从有三头六臂的烙饼神功后, 烙饼速度和手艺大进, 全军都已经尽人皆知了。一日,训练归来, 大家训练的很累, 大家也都知道三爷(张飞), 有烙饼的神艺, 因此纷纷说要品尝一下三爷的烙饼。张飞心想, 俺老张有神艺在身, 正好显示一下。

三爷数了数, 这些人吃饼 , 正正好5000张饼。心想看来我要大显身手了!

狭小的厨房

张飞心里算计了一下, 上次5头10臂2个小时烙饼200张, 那么1小时1个人(1个头)20张饼。

那么5000张饼, 按照这个速度烙下去就需要250个小时, 那么大家都的饿晕了。

看来6头  需要41小时

12头  20小时

...

100头 约2.5小时

老张考虑到这里心想看来 需要变为100头200条臂膀才能在2.5小时内完成烙饼任务, 说变就变。变变变。。。

结果:

jb

任何资源, 东西也好都是, 有限的, 当到一定时候就容不下了。 厨房自然有大小, 10几个人的厨房已经非常大了, 90多人, 哪里能装的下。 厨房外面自然无法烙饼了。

我们也该说道正题了, 厨房有大小, 那计算机的资源也是有大小的。 厨房一般也就是最多容纳10多人的吧,目前计算机普通的也就是相当于8个cpu,或者16个等, 都是比较正常的, 在多的, 就不常见了, 我们就不讨论了。

一个cpu就相当于一个张飞同志, 16个, 就相当于16个张飞。

张飞的妙招

张飞看了一下, 我家厨房是没这么大了, 看来, 还得在找几个厨房,然后把我老张的分身都放过去, 然后排我的传令兵去下达命令了。

如下图:

dc

如上图, 张飞同志租赁了, 若干个厨房(一共租了10个厨房), 每个厨房都有10多个张飞(图中省略一些人, 没办法画出来)。

厨房租赁好了,张飞给每个厨房分配了两个传令兵,来传达张飞的命令。

分布式烙饼

为什么叫做分布式烙饼, 主要是烙饼的工作, 分别分布在不同地点的不同厨房中, 因此叫做分布式烙饼。

计算机中,很多程序也是由分布到不同机房或者不同房间,不同机架的计算机(厨房)而共同完成一个任务, 这样的任务叫做分布式计算,或者分布式系统。

张飞说, 小的们准备好了吗(好像西游记里面老妖怪叫小妖怪)? 小妖们回答, 准备好了!

张飞说 开始烙饼, 然后各个传令兵就分别开始走向自己负责传令的厨房吩咐开始烙饼。

传令兵走后, 张飞根据 现在的厨房数量, 和每个厨房中 头数(三头六臂中,每个头都都可以独立烙饼),因此计算了一下, 2小时30分钟后烙饼就够了。

然后等一个小时25分钟后,张飞再次大喊一声,停止烙饼, 然后第二批传令兵也出发了, 分别去他们自己的厨房传达停止烙饼的命令, 然后同一次去的传令兵一起, 把每个厨房的烙饼都 抬了回来。

张飞把这些烙饼都汇总到一起, 数了数, 5332张饼。

张飞心里在想, 我明明计算好的 1小时25分钟后, 就可以烙5000张饼, 为什么多出一些??

------------------------------

这个问题非常非常重要, 为什么多出来?  为什么不是正好? 有没有可能下次在进行还是多, 或者下次用这个办法会变少?

-----------------------------

分析一下可能原因。

首先, 张飞算错了 时间,  若是发生这个情况 很可能数量不对, 当然存在多的可能。(这个情况下, 也有可能数量刚刚好的!  更深的原因我们现在不谈, 也可能存在少的可能)。

另外, 张飞若是把时间算的正确了, 就是刚刚好这些厨房都用2小时30分钟(这个就是2.5小时) 时间烙饼, 然后停止烙饼, 等等,

那么在这个情况下 是否烙饼数了就刚刚好 是 5000张呢??

其实, 无论做多多少遍,  最后大部分 都不是  5000张饼??? 为什么?

这个是非常严重的一个问题, 我们要彻底的分析出这个问题的原因, 才能解决这个问题。

(像烙饼这个, 数量不准确还可以勉强接受, 但是, 若是出售一些水果给用户, 人家用户就要5000个, 那么多采下来的卖不掉 , 就会烂掉的!!  有经济损失的!)

因此我们把 为什么数量不准的 问题要分析一下!

如下图,是 这个分布式烙饼的一个基本的 示意图

分布式烙饼时间示意图

 

如上图,  有10个厨房, 他们不在一个地点, 有可能距离很远。这里为了说明问题, 特定选择了距离较远。其实即使距离在近也存在这个问题。 相关情况我们后面会说明。

张飞在 t1的时候发布开始烙饼的命令, 然后不同厨房的传令兵开始沿着自己的最近的路线, 用最快的速度去传令。

传令

然后, 张飞在t2的时候  下达结束烙饼的命令, 然后第二批传令兵去下达停止烙饼的命令

传令2

然后各个厨房在收到 结束烙饼命令后, 就停止烙饼的工作。

在开始烙饼命令开始, 到停止烙饼命令结束直接的时间烙的饼最后汇总到一起。统计一下, 一般不会达到预期的5000个的。 为什么?

传令3

如上图, 以最后一个厨房为例, 若是前后两次传令兵  都能走出  途中  红色箭头的路线, 那么他生产出饼数才能满足5000个的要求。

但是实际最后一个传令兵会走出 蓝色箭头的  时间来, 那么这个误差就会导致 总体上的 饼的数量 同5000个不同

无论什么都需要时间

从上面分析我们可以得出:

1. 无论什么事情都需要时间,  无论你距离多么近, 无论你多么快,时间肯定是需要的。

因此传令一定需要时间, 没有办法避免的。

即使现代的计算机也是需要时间才能传递信息的。 计算机A(张飞)=》 计算机10(厨房10)传递命令, 也是需要时间的, 只是需要的少而已。

但是仍然有。

2. 无论任何人, 任何事物都难保证 沿着相同的路线运动中, 会重复前一次的所用时间的, 不同人就更难。

因此两个传令兵,沿着相同的路线到达厨房10的时间一定是不相等, 必然有早有慢

计算机也好, 人也好, 什么东西都没办法保持始终不变的速度。即使你自己能做到这一点, 也会受到来自外界的干扰, 比如路上有人找你打招呼, 比如天下雨了。。。

结论

1. 这个分布式的烙饼方案能极大的提高烙饼的速度

2. 任何事物,任何东西都会有极限,人也好, 计算机也好, 厨房也好都有极限。

当我们一个事情(烙饼)在一个有限的资源下满足不了我们(例如例子中, 一个特定厨房中), 我们就会寻求在更大的范围内进行, 然后通过一定的手段协调这些事情(例如,通过传令兵来传递命令,协调10个厨房)

3. 任何人, 任何事物, 做事情都需要时间的。

4. 任何人, 任何事物, 即使用相同的路线, 相同条件下, 也无法达到前一次的 所用的时间, 就是就是传递开始烙饼的时间, 同结束烙饼的时间一定是不同的。

更何况是不同人传令, 并且他们传令走的路线可能不同。

5. 即使传令的时间相同, 开始时间相同, 也不能保证生产出烙饼的数量刚刚好的, 因为不同人烙饼速度是不同的。

最后以上全部的因素加到一起, 决定了,上述烙饼数量上一定是 不满足要求的!!!

 

有没有  满足要求的办法, 有!!  下篇我们给出办法来。

 

相关文章

计算机介绍                                  从锦囊妙计想到的01

流程图(分支结构)介绍          从锦囊妙计想到的02

线程介绍                                     从锦囊妙计想到的03

循环结构介绍                             从锦囊妙计想到的05

流程线程总结                             从锦囊妙计想到的06

cpu和线程定义、开始               从锦囊妙计想到的07

分布式计算                                  从锦囊妙计想到的08

分布式中事件和计数                 从锦囊妙计想到的09

内容总结                                     从锦囊妙计想到的10

数据类型,变量简介                从锦囊妙计想到的11

函数和参数                                从锦囊妙计想到的12

用户交互与数据输入输出       从锦囊妙计想到的13

人机交互界面                            从锦囊妙计想到的15 

过程与对象                                从锦囊妙计想到的16

同步和异步                                从锦囊妙计想到的17

顺序打印                                    从锦囊妙计想到的18

数据输入输出                            从锦囊妙计想到的19

屏幕坐标和打印                        从锦囊妙计想到的20

java函数控制输出                     从锦囊妙计想到的21

逐步细化解决复杂问题           从锦囊妙计想到的22

java入门                                    从锦囊妙计想到的23

java复杂过程分析                   从锦囊妙计想到的25

中间辅助功能解决问题          从锦囊妙计想到的26

叠加操作输出复杂图形          从锦囊妙计想到的27

时间和空间                               从锦囊妙计想到的28

编写边测解决问题                  从锦囊妙计想到的29

让程序动起来                          从锦囊妙计想到的30

程序往复运动                           从锦囊妙计想到的31

spring mvc 2.5.6两例

spring 2.5.6用的已经很少了, 相关例子已经很少也不好找, 完整的能好用的就更少了, 下面是两个相对完整的, 并且有源代码的

另外, 下面项目最好在 jdk1.6的环境运行, 高版本的jdk可能有些问题, 例如jdk1.8  会报异常

Spring MVC hello world annotation example

This tutorial is based on Spring 2.5.6, quite outdated. Try considering the new tutorials :

  1. Gradle + Spring 4 MVC Hello World
  2. Maven + Spring 3 MVC Hello World

In this tutorial, we will take the previous Spring MVC hello world XML-based, and convert it to a annotation-based project.

Technologies used :

  1. Spring 2.5.6
  2. JDK 1.6
  3. Maven 3
  4. Eclipse 3.6

1. Directory Structure

如下图
s1

2. Maven

Spring’s annotation is bundled in the same spring-webmvc.jar.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
	http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mkyong.common</groupId>
	<artifactId>spring2-mvc-annotation-hello-world</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>
	<name>Spring 2 MVC</name>
	
	<properties>
		<jdk.version>1.6</jdk.version>
		<spring.version>2.5.6</spring.version>
		<jstl.version>1.2</jstl.version>
		<servletapi.version>2.5</servletapi.version>
	</properties>
	
	<dependencies>

		<!-- Spring MVC framework -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- JSTL -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>${jstl.version}</version>
		</dependency>

		<!-- for compile only, your container should have this -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>${servletapi.version}</version>
			<scope>provided</scope>
		</dependency>
		
	</dependencies>

	<build>
		
	  <plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.3</version>
			<configuration>
				<source>${jdk.version}</source>
				<target>${jdk.version}</target>
			</configuration>
		</plugin>

		<plugin>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-maven-plugin</artifactId>
			<version>9.2.11.v20150529</version>
			<configuration>
				<scanIntervalSeconds>10</scanIntervalSeconds>
				<webApp>
					<contextPath>/spring2</contextPath>
				</webApp>
			</configuration>
		</plugin>

		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-eclipse-plugin</artifactId>
			<version>2.9</version>
			<configuration>
				<downloadSources>true</downloadSources>
				<downloadJavadocs>true</downloadJavadocs>
				<wtpversion>2.0</wtpversion>
				<wtpContextName>spring2</wtpContextName>
			</configuration>
		</plugin>
			
	  </plugins>
		
	</build>

</project>

3. Controller & Handler Mapping

Now, you can use @Controller and @RequestMapping to replace the XML configuration.

  1. Controller – The controller class is no longer need to extend the base controller like AbstractController or SimpleFormController, just simply annotate the class with a @Controller annotation.
  2. Handler Mapping – No more declaration for the handler mapping like BeanNameUrlHandlerMapping, ControllerClassNameHandlerMapping or SimpleUrlHandlerMapping, all are replaced with a standard @RequestMapping annotation.
HelloWorldController.java
package com.mkyong.common.controller;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/welcome")
public class HelloWorldController{
 
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView helloWorld(){
 
		ModelAndView model = new ModelAndView("HelloWorldPage");
		model.addObject("msg", "hello world");
 
		return model;
	}
}

If the @RequestMapping is applied at the class level (can apply at method level with multi-actions controller), it required to put a RequestMethod to indicate which method to handle the mapping request.

In this case, if a URI pattern /welcome is requested, it will map to this HelloWorldController, and handle the request with helloWorld() method.

4. Spring XML Configuration

You still need to configure the view resolver and component scanning in XML file.

/WEB-INF/spring-mvc-config.xml
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd


http://www.springframework.org/schema/context

	http://www.springframework.org/schema/context/spring-context-2.5.xsd">

	<bean id="viewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix">
			<value>/WEB-INF/pages/</value>
		</property>
		<property name="suffix">
			<value>.jsp</value>
		</property>
	</bean>
	
	<context:component-scan base-package="com.mkyong.common.controller" />

</beans>

5. JSP Page

A simple JSP page for demonstration.

HelloWorldPage.jsp.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
	<h1>Spring MVC Hello World Annotation Example</h1>

	<h2>${msg}</h2>
</body>
</html>

6. web.xml

web.xml
<web-app id="WebApp_ID" version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

	<display-name>Spring Web MVC Application</display-name>

	<servlet>
		<servlet-name>mvc-dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>mvc-dispatcher</servlet-name>
		<url-pattern>*.htm</url-pattern>
	</servlet-mapping>

</web-app>

7. Demo

6.1 To run with the embedded Jetty, type :

$ mvn jetty:run

URL : http://localhost:8080/spring2/welcome.htm

s2

6.2 To import into Eclipse IDE.

$ mvn eclipse:eclipse
Note
If you compare this Spring MVC annotation-based hello world example with previously XML-based example, you can see that this annotation approach is easier and flexible in wiring the controller class and URL handler mapping, because you do not need to declare the controller class explicitly or extends any particular class.

Download Source Code

Download it – spring2-mvc-annotation.zip (15 KB)

References

  1. Controller Javadoc
  2. RequestMapping Javadoc
  3. Spring MVC hello world XML-based example
  4. Spring auto scanning components

分布式跟踪系统Zipkin的Span模型

分布式跟踪系统(二)Zipkin的Span模型:本文将详细介绍Zipkin的Span模型,以及其他“另类”Span模型的设计。

这里多一句嘴,其实专业点的叫法应该是分布式追踪系统——Distributed TracingSystem,跟踪比较适合用于人的场景,比如某人被跟踪了,而追踪更适合用于计算机领域。然并卵?本文将继续使用“跟踪”。

Zipkin的Span模型几乎完全仿造了Dapper中Span模型的设计,我们知道,Span用来描述一次RPC调用,所以一个RPC调用只应该关联一个spanId(不算父spanId),Zipkin中的Span主要包含三个数据部分:

基础数据:用于跟踪树中节点的关联和界面展示,包括traceId、spanId、parentId、name、timestamp和duration,其中parentId为null的Span将成为跟踪树的根节点来展示,当然它也是调用链的起点,为了节省一次spanId的创建开销,让顶级Span变得更明显,顶级Span中spanId将会和traceId相同。timestamp用于记录调用的起始时间,而duration表示此次调用的总耗时,所以timestamp+duration将表示成调用的结束时间,而duration在跟踪树中将表示成该Span的时间条的长度。

需要注意的是,这里的name用于在跟踪树节点的时间条上展示。Annotation数据:用来记录关键事件,只有四种,cs(Client Send)、sr(Server Receive)、ss(Server Send)、cr(Client Receive),所以在Span模型中,Annotation是一个列表,长度最多为4。每种关键事件包含value、timestamp和endpoint,value就是cs、sr、ss和cr中的一种,timestamp表示发生的时间,endpoint用于记录发生的机器(ip)和服务名称(serviceName)。可以很自然的想到,cs和cr、sr和ss的机器名称是相同的,为了简单起见,cs和cr的服务名称可以一样,sr和ss的服务名称可以一样。

Annotation数据主要用于用户点击一个Span节点时展示具体的Span信息。BinaryAnnotation数据:我们并不满足在跟踪树上只展示调用链的时间信息,如果需要绑定一些业务数据(日志)的话,可以将数据写入BinaryAnnotation中,它的结构和Annotation数据一模一样,在Span中也是一个列表,这里就不再阐述,但BinaryAnnotation中不宜放太多数据,不然将导致性能和体验的下降。

现在我们已经了解了一个Span的内部结构,但这是Span的最终形态,也就是说这是Zipkin在收集完数据并展现给用户锁看到的最终形态。Span的产生是“不完整”的,Zipkin服务端需要将搜集的有同一个traceId和spanId的Span组装成最终完整的Span,也就是上面说到的Span。可能这样说不太直观,我们沿用下图来举例说明:

\

zipkin数据收集(图1)

 

上图在我的第一篇Zipkin博文中已经用到过,这里不再详细阐述,我们直接看该图对应的内部Span细节图:

\

span数据流转(图2)

注意,上图并没有显示Span的所有细节(比如name和binaryAnnotation等),但这并不影响我们分析问题。上图的①和⑥是一次完整的RPC调用,它发生在服务器0和服务器1之间,显而易见的是,用于描述该RPC调用的Span的spanId是1000,所以,这是同一个Span的,只是它的数据来源于两台不同的服务器(应用):服务器0和服务器1。往低层说,该Span由两条跟踪日志表示,一条在服务器0上被采集,另一条在服务器1上被采集,他们的Span的traceId、spanId和parentSpanId都是一样的!而且该Span将成为跟踪树中的顶节点,因为他们的parentSpanId为null。对于步骤①来说,服务器1上的sr减去服务器0上的cs的时间就是约等于网络耗时(这里忽略不同服务器时钟的差异),同理,对于其他步骤,sr-cs和cr-ss得到的都是网络耗时。我们接着看请求步骤②和④,从跟踪树的层次来说他们属于①下的子调用,所以它们的parentSpanId就是①的1000。步骤②和④都会分别产生一个spanId(上面的1001和1002),所以如上图,看似一次简单的RPC过程,其实共产生了6条Span日志,它们将在Zipkin服务端组装成3个Span。

那么,问题来了,此次调用在服务器1上出现了3个spanId:1000、1001和1002,如果我想记录服务器1上和此次调用的业务数据(通过BinaryAnnotation来记录),是将这些数据绑定到哪个Span上呢?如果让我们选择,我们肯定选择1000,因为服务器1上此次请求中调用下游的服务是不确定的(虽然图中只画了服务器2和服务器3),有可能它会调用下游的十几个服务,产生十几个spanId,相对而将业务数据言绑定到这些Span的父Span(1000)上似乎更合理。并且在产生业务日志时,有可能还没开始进行下游调用,所以也只能绑定在1000上。

我们先来看看图2中的Span在Zipkin的跟踪树中大概会显示成什么样子,如下图:

\

Zipkin跟踪树(图3)

当然,部分数据会和图2中的不一样(比如timestamp和duration),但并不影响我们分析问题。可以看出,在Zipkin中最小的时间单位是微秒(千分之一毫秒),所以图3中展现的此次RPC总耗时为96.2ms,有人刚开始看肯定会疑问,为啥经历过四个服务器的RPC调用在图中的跟踪树中只有三个节点?因为在跟踪树中, 一个Span(准确的说是一个spanId)只会展现成一个树节点,比如树节点Service1表示了Gateway(服务器0)调用Service1(服务器1)的过程,树节点Service2表示Service1(服务器1)调用Service2(服务器2)的过程。有人肯定会问,对于树节点Service1,我们记录了cs、sr、ss和cr四个时间,但时间条的显示只用到了cs和cr(耗时duration=cr-cs),那么sr和ss去哪了(别忘了我们可以通过sr-cs和cr-ss计算网络耗时)?我们可以单击Serice1节点,于是打开了Span的详细信息(Span的annotation和binaryAnnotation数据),如下图:

\

Span详细信息(图4)

Relative Time 是相对时间,表示此事件(cs、sr、ss、cr)已经发生了多久(相对起始时间点),因为Service1是顶级节点,所以第一行的RelativeTime是空的,于是乎,该请求的网络耗时(Gateway请求Service1)为10ms,应答的网络耗时(Service1应答Gateway)为96.3-94.3=2ms,所以,从Zipkin目前的页面设计来看,网络耗时只能通过点树节点的详细信息页面来看,而且还需要做简单的计算,并不直观。淘宝的鹰眼系统通过在时间条上分为两种颜色来显示,使用了cs、sr、ss和cr四个时间戳,更加直观。

可能大多数人觉得对于跨四个系统的RPC调用却只显示三个节点,有些别扭。对于图1的调用,我们更希望是Gateway的节点下挂着一个Service1节点,表示Gateway调用了Service1,而Service1节点下挂着Service2和Service3两个节点,表示Service1调用了Service2和Service3,这样更容易理解。于是我们想到了在RPC链路中经过某个节点(服务器应用),那么这个节点就产生几个spanId,这样的话,在图中RPC经过Gateway、Service1、Service2和Service3各一次,所以一共将产生4个spanId(Zipkin在图2中只产生3个spanId),这样就变成了spanId和节点个数一致(前提是RPC链路中只经过每个节点各一次,也就是节点之间没有相互依赖)。这样设计Span数据的流转如下图:

\

修改过的Span数据流转(图5)

图5中可以很明显的看出,还是6条Span的日志,每个服务器节点上会产生一个spanId(1000、1001、1002和1003),而不是像原有图2一样只有3个spanId。这样还有一个好处,就是RPC调用时只需要传递traceId和spanId,而不是像Zipkin的设计那样,需要传递traceId、spanId还有parentSpanId。但立马我们就发现了问题,在图5的服务器1的节点上,1001的spanId记录了两套cs和cr,则也导致了无法区分哪个对应的是调用服务器2,哪个对应的是调用服务器3,所以,这种设计方案直接被否决了。

于是我们换一种思路,不采用spanId和parentSpanId,换成spanId和childSpanId,childSpanId由父亲节点生成并传递给子节点,如下图:

\

新的Span数据流转(图6)

从图6可以看到明显的变化,不再有parentSpanId,而使用了childSpanId,这样RPC之间传递的就是traceId和childSpanId,这也直接解决了图5中所遇到的问题。虽然图5和图6的设计违背了一次RPC调用由一个spanId的数据来进行维护的设计理念,但确实在跟踪树的界面展示上更容易让人接受和理解(树节点和服务器节点对应),而且还减少了RCP间的数据传送,何乐而不为?

来源: https://www.2cto.com/kf/201701/582724.html

最详细的国画兰花画法教程

一般画上见到的兰花是蕙而不是兰,兰与蕙的区别主要在于一茎一花与一茎多花。

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

第四部分 兰草构图

 

一、长方形构图

 

兰草题材以长方形、方形等构图较为常见。在处理大面积兰叶交叉组织时,叶子的走势不怕重复,小变化过多画面易乱。从传统意义上讲,平衡、均衡对立统一是构图的基本规律,或是“S”形构图、“之”字形构图、“C”形构图等等,这是构图最基本的要素,但构图也随时代而发展。因此,也要用新的观念来研究绘画的构图问题。

27

二、方形构图

绘画的构图如文章的起、承、转、合。合就是变化,画面切忌平,一幅画的构图,图像处理也要讲究变化,大小、轻重、对比、虚实、冷与暖等等。画贵空灵,好的作品应耐人寻味。百观而不厌。

28

三、扇面构图

扇面构图运用中,兰草多为主体,背景一般很少甚至不要背景。主要看画兰之用线的水平,与画法近之。

29

第五部分 兰石搭配

 

一、以奔放的笔画山石的造型。

30

二、兰叶的走势与山石的走势要统一。

31

三、兰叶用笔要有虚实变化。

 

四、注意姿态变化及其与叶子的呼应关系,做到虚中有实、实中有虚。

32

 

五、 题款印章应该注意与画面构图的关系。

33

兰花作品欣赏:

 

34

35

36

版权说明:文章源于网络,素材无从查证作者,原创作者可联系我们予以删除处理!

dubbo分布式系统链路追踪_zipkin

基础知识储备

分布式跟踪的目标

一个分布式系统由若干分布式服务构成,每一个请求会经过多个业务系统并留下足迹,但是这些分散的数据对于问题排查,或是流程优化都很有限,要能做到追踪每个请求的完整链路调用,收集链路调用上每个服务的性能数据,计算性能数据和比对性能指标(SLA),甚至能够再反馈到服务治理中,那么这就是分布式跟踪的目标。

分布式跟踪的目的

zipkin分布式跟踪系统的目的:

  • zipkin为分布式链路调用监控系统,聚合各业务系统调用延迟数据,达到链路调用监控跟踪;
  • zipkin通过采集跟踪数据可以帮助开发者深入了解在分布式系统中某一个特定的请求时如何执行的;
  • 假如我们现在有一个用户请求超时,我们就可以将这个超时的请求调用链展示在UI当中;我们可以很快度的定位到导致响应很慢的服务究竟是什么。如果对这个服务细节也很很清晰,那么我们还可以定位是服务中的哪个问题导致超时;
  • zipkin系统让开发者可通过一个Web前端轻松的收集和分析数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。

ZipKin介绍

  • Zipkin是一个致力于收集分布式服务的时间数据的分布式跟踪系统。
  • Zipkin 主要涉及四个组件:collector(数据采集),storage(数据存储),search(数据查询),UI(数据展示)。
  • github源码地址:https://github.com/openzipkin/zipkin。
  • Zipkin提供了可插拔数据存储方式:In-Memory,MySql, Cassandra, Elasticsearch

brave 介绍

Brave 是用来装备 Java 程序的类库,提供了面向标准Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等接口的装备能力,可以通过编写简单的配置和代码,让基于这些框架构建的应用可以向 Zipkin 报告数据。同时 Brave 也提供了非常简单且标准化的接口,在以上封装无法满足要求的时候可以方便扩展与定制。

本文主要介绍springmvc+dubbo下的brave使用。

dubbo项目下快速搭建zipkin、brave追踪系统

1、zipkin安装使用

此处主要介绍linux下的安装使用,zipkin官网地址 http://zipkin.io/pages/quickstart.html

 wget  -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'

说明:zipkin是springboot项目,该jar包可直接通过java -jar zipkin.jar启动。启动完成后可访问http://ip:9411查看。

2、zipkin存储与启动

详情参考官网: https://github.com/openzipkin/zipkin/tree/master/zipkin-server

(1)In-Memory方式

 nohup java -jar zipkin.jar  &

注意:内存存储,zipkin重启后数据会丢失,建议测试环境使用

(2)MySql方式

目前只与MySQL的5.6-7。它的设计是易于理解,使用简单。但是,当数据量大时,查询很慢。性能不是很好。

  • 创建数据库zipkin
  • 建表
CREATE TABLE IF NOT EXISTS zipkin_spans (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  `trace_id` BIGINT NOT NULL,
  `id` BIGINT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `parent_id` BIGINT,
  `debug` BIT(1),
  `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
  `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';

CREATE TABLE IF NOT EXISTS zipkin_annotations (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
  `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
  `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
  `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
  `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
  `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
  `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
  `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
  `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
  `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
  `day` DATE NOT NULL,
  `parent` VARCHAR(255) NOT NULL,
  `child` VARCHAR(255) NOT NULL,
  `call_count` BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
  • 启动zipkin命令

$ STORAGE_TYPE=mysql MYSQL_HOST=IP MYSQL_TCP_PORT=3306 MYSQL_DB=zipkinMYSQL_USER=username MYSQL_PASS=password nohup java -jar zipkin.jar &

(3)Elasticsearch方式

本文建议使用此方法。

3、dubbo项目快速接入

(1)、项目pom中添加brave-dubbo.jar的依赖,brave-dubbo简化dubbo项目接入zipkin的步骤。

     <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-dubbo</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

(2)、在spring-application.xml中配置brave

        <bean id="brave" class="com.github.kristofa.brave.dubbo.BraveFactoryBean" p:serviceName="serviceName" p:zipkinHost="http://zipkin-server-ip:9411/" p:rate="1.0" />

说明:

  • zipkin-server-ip 是zipkin服务器ip地址。
  • p:serviceName 项目名称。
  • 只要是dubbo项目,无论是普通服务,还是web项目,都需要添加此包,并配置brave Bean。

4、大功告成

此时,你可以看到如下效果。

Paste_Image.png

各个服务之间的调用关系及响应时间

转载自:http://www.jianshu.com/p/85f23233345e

补充一些相关的项目地址

https://github.com/jgchen/brave-dubbo

https://github.com/sdcuike/dubbo-brave

https://github.com/sosozhuang/dubbox-brave-filter

https://github.com/andye2004/brave-instrumentation-jms

上述例子供参考