WEB应用中的SESSION详解

作为一名WEB开发程序员,对session的理解是*基础的,但是现状是WEB程序员遍地都是,随便一划拉一大把,不过估计能把session能透彻理解的人应该不是很多,起码我之前对此是知之甚少,偶然看到的一个关于session的文章,经过适当整理,特拿来与大家分享,因为时间太久,文章出处已然记不清楚,无法附上,请原作理解,若有必要我会删除,谢谢!

目 录
一、Session
二、Cookies
三、Cookies机制
四、Session机制
五、Cookies机制与Session机制的区别和联系
六、常见问题
七、Session的用法

Session是WEB上有效的信息交互手段,因其使用方便、稳定、安全、可靠而被众多WEB开发者所认知。尤其在互联网身份验证、网上电子购物等方面的应用更为广泛。下面就着重来介绍下Session。

一、Session
Session,在汉语中表示通话、会话、对话(期)、话路[对谈时间]的意思,其本来的含义一个终端用户与交互系统进行通信的时间(间隔),通常是指从注册(进入系统)到注销(退出系统)之间所经过的时间。比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个Session。有时候我们可以看到这样的话“在一个浏览器会话期间,…”,这里的会话一词用的就是这个意思,是指从一个浏览器窗口打开到关闭这个期间。Session在我们的网络应用中就是一种客户端与服务器端保持状态的解决方案,有时候Session也用来指这种解决方案的存储结构,
Session对象,就是客户端浏览器与服务器之间建立的互动信息状态。每一个不同的用户连接将得到不同的Session,也就是说Session与用户之间是一种一对一的关系。Session在用户进入网站时由服务器自动产生,并在用户正常离开站点时释放。使用Session的好处就在于,可以将很多与用户相关的信息,例如用户的帐号、昵称等保存到Session中;利用Session,可以跟踪用户在网站上的活动。例如:当你上网进入一个网站时,如果你没有登陆,无论你访问哪几个页面都会跳转回登陆页。还有就是你在购物时,不可能把你的东西放到别人的购物车里去,这就得用一个信息变量来判断!
如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的Session机制则是又一种在客户端与服务器之间保持状态的解决方案。

二、Cookies 
Cookie是WEB上*常用的跟踪用户会话方式,当  Cookie被禁止后,一般都用URL重写来跟踪会话。Cookie是一种由服务器发送给客户的片段信息,存储在客户环境中,并在客户所有的对服务器的请求中都要发回它。就好比我们在用IE登陆某个电子购物商城时,IE在得到商品列表页面的 同时还收到Set-Cookie应答头信息,我们打开一个Cookie文件,我们所看到的格式一般都是:
Cookie:NAME=VALUE;Comment=COMMENT;Domain=DOMAINNMAM;Max-age=SECONDS;Path=PATH;secure;Version=1*DIGIT
其中NAME值对(值对间用分号分隔)是必须的,其余都是可选的。*重要的信息当然也在所必须的值对里了,VALUE是NAME的值,也是这个Cookie的标识,Max-age定义了Cookie的*长生存时间,其它几个可选值对可参阅http://www.faqs.org/rfcs/rfc2109.html。当我们选购了某种商品,向服务器发送选购清单时,会自动在你的请求信息头里加上NAME值对,如果Cookie被禁止,则用URL重写方式在URL请求地址上附加NAME值对。当Web服务器收到这个请求后,会检查该Cookie是否存在,然后相应的跟踪会话。从以上分析不难理解,其实Web服务器跟踪会话就靠Set-Cookie头信息,跟踪NAME值对进行身份验证。假如我们用非Web终端接收Web服务器的响应信息,从中解析出Cookie头信息,当再次向Web服务器发送请求时附加上解析出的Cookie信息,Web服务器据此不就可以进行身份认证了吗?
Cookies中文是饼干的意思,对于为何引用Cookies,从网上查找了一些资料:
在浏览器与WEB服务器之间是使用HTTP协议进行通信的,当某个用户发出页面请求时,WEB服务器只是简单的进行响应,然后就关闭与该用户的连接。因此当一个请求发送到WEB服务器时,无论其是否是*次来访,服务器都会把它当作*次来对待,这样的不好之处可想而知。为了弥补这个缺陷,Netscape开发出了cookie这个有效的工具来保存某个用户的识别信息,因此人们昵称为“小甜饼”。cookies是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段:Netscape Navigator使用一个名为cookies.txt本地文件保存从所有站点接收的Cookie信息;而IE浏览器把Cookie信息保存在类似于c:\Internet 临时文件\的目录下。当用户再次访问某个站点时,服务端将要求浏览器查找并返回先前发送的Cookie信息,来识别这个用户。Cookies给网站和用户带来的好处:
(1)、Cookie能使站点跟踪特定访问者的访问次数、*后访问时间和访问者进入站点的路径
(2)、Cookie能告诉在线广告商广告被点击的次数,从而可以更精确的投放广告
(3)、Cookie有效期限未到时,Cookie能使用户在不键入密码和用户名的情况下进入曾经浏览过的一些站点
(4)、Cookie能帮助站点统计用户个人资料以实现各种各样的个性化服务,其实,cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力.

三.Cookie机制 
Cookie机制采用的是在客户端保持状态的方案。
Cookie机制,就是当服务器对访问它的用户生成了一个Session的同时服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie,保存在客户端,里面记录着用户当前的信息,当用户再次访问服务器时,浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置也就是对应的Cookie文件。 若存在,则把该cookie附在请求资源的HTTP请求头上发送给服务器,例如:当我们登陆了一个网站,并且填写了有关资料,以本站会员的名义登陆上了有关网页,这时你把浏览器关闭,再重启进入该网站的某一个页面时是以你登陆过的会员进去的,当然,不是所有网站都是这样,我们知道,cookie的保存有临时性的和持久性的,大多都是临时性的,也就是cookie只保存在客户端的内存中,而没有保存在硬盘上,当关闭浏览器,cookie也就销毁。以下是有关cookie机制的一些具体说明:
cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。
存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于微软的IE浏览器,在一个打开的窗口上按Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于火狐狸firefox浏览器,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用Session机制的web应用程序开发者造成很大的困扰。

四、Session机制 
Session机制采用的是在服务器端保持状态的方案。
当用户访问到一个服务器,服务器就要为该用户创建一个SESSION,在创建这个SESSION的时候,服务器首先检查这个用户发来的请求里是否包含了一个SESSIONID,如果包含了一个SESSIONID则说明之前该用户已经登陆过并为此用户创建过SESSION,那服务器就按照这个SESSIONID把这个SESSION在服务器的内存中查找出来(如果查找不到,就有可能为他新创建一个),如果客户端请求里不包含有SESSIONID,则为该客户端创建一个SESSION并生成一个与此SESSION相关的SESSIONID。这个SESSIONID是唯一的、不重复的、不容易找到规律的字符串,这个SESSIONID将被在本次响应中返回到客户端保存,而保存这个SESSIONID的正是COOKIE,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。
我们知道在IE中,我们可以在工具的internet选项中把COOKIE禁止,那么会不会出现把客户端的COOKIE禁止了,SESSIONID就无法再用了呢?找了一些资料说明,可以有其他机制在COOKIE被禁止时仍然能够把Session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把Session id直接附加在URL路径的后面一种是作为URL路径的附加信息,表现形式为:
http://…./xxx;jSession=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764;
另一种是作为查询字符串附加在URL后面,表现形式为:
http://…../xxx?jSession=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
还有一种就是表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把Session id传递回服务器。这里就不介绍了。
我们常说的在一个IE被打开是创建一个Session,当关闭IE时Session也就被删除,事实上,除非程序通知服务器删除Session,否则Session会被服务器一直保留,直到Session的失效时间到了自动删除。服务器不知道IE被关闭,IE不会主动在其关闭之前通知服务器它将要关闭。程序一般都是在用户做注销时删除Session。我们产生这种错觉的原因是:一般Session机制都使用cookie来保存Session id,而一旦关闭IE浏览器,Session id就不存在了,再连接服务器时找不到原来的Session了.如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP请求头,把原来的Session id发送给服务器,则再次打开浏览器仍然能够找到原来的Session。恰恰是由于关闭浏览器不会导致Session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用Session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把Session删除以节省存储空间。
一般情况下,Session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的Session也会被清空,如果设置了Session的持久化特性,服务器就会把Session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用。

五、cookie机制与Session机制的区别和联系
具体来说cookie机制采用的是在客户端保持状态的方案,而Session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于在服务器端保持状态的方案在客户端也需要保存一个标识,所以Session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上还有其他选择。例如,我们经常用到的会员卡,也就相当于这种情况。消费到了一定程度就有*,就如下面例子说明:
1.发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
2、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。
以下是一些关于两者的区别与联系:
具体来说cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制,他需要用户打开客户端的cookie支持。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力.而Session机制采用的是一种在客户端与服务器之间保持状态的解决方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以Session机制可能需要借助于cookie机制来达到保存标识的目的。而Session提供了方便管理全局变量的方式。
Session是针对每一个用户的,变量的值保存在服务器上,用一个Session来区分是哪个用户Session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。
就安全性来说:当你访问一个使用Session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。
正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。
从网络服务器观点看所有HTTP请求都独立于先前请求。就是说每一个HTTP响应完全依赖于相应请求中包含的信息.
状态管理机制克服了HTTP的一些限制并允许网络客户端及服务器端维护请求间的关系。在这种关系维持的期间叫做会话(Session)。
Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies 。
来看一下Tomcat是如何实现web应用程序之间session的隔离的,从Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

六、常见问题 
1、Session在何时被创建
Session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page Session=”false”%> 关闭Session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession Session = HttpServletRequest.getSession(true);这也是JSP中隐含的Session对象的来历。由于Session会消耗内存资源,因此,如果不打算使用Session,应该在所有的JSP中关闭它。
2、Session何时被删除
综合前面的讨论,Session在下列情况下被删除:
a.程序调用HttpSession.invalidate();
b.距离上一次收到客户端发送的Session id时间间隔超过了Session的超时设置;
c.服务器进程被停止(非持久Session)。
3、如何做到在浏览器关闭时删除Session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除Session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。
4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控Session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是Session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。
5、存放在Session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了Session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把Session交换出内存。在Weblogic Server的Session中放置一个不可序列化的对象在控制台上会收到一个警告。
6、开两个浏览器窗口访问应用程序会使用同一个Session还是不同的Session
对Session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。
7、如何防止用户打开两个浏览器窗口操作导致的Session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在Session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在Session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。
8、为什么Session不见了
排除Session正常失效的因素之外,服务器本身的可能性应该是微乎其微的;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。出现这一问题的大部分原因都是程序的错误,*常见的就是在一个应用程序中去访问另外一个应用程序。
9、服务器关掉后,当前Session会丢掉吗
这个取决于你使用什么样的web服务器以及web服务器是如何配置的。tomcat在shutdown前默认会自动将Session保存到指定的目录中,重新启动是重新加载,因此tomcat重新启动后,Session是可以继续使用的。此外,你还何以将Session保存到数据库中,这个要在server.xml中配置。
10、Cookie的过期和Session的超时有什么区别
会话的超时由服务器来维护,它不同于Cookie的失效日期。首先,会话一般基于驻留内存的cookie不是持续性的cookie,因而也就没有截至日期。即使截取到JSESSION cookie,并为它设定一个失效日期发送出去。浏览器会话和服务器会话也会截然不同。

七、Session的用法 
Session 对象
可以使用 Session 对象存储特定用户会话所需的信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
Session 对象*常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在 Session 对象中。
注意:会话状态仅在支持 cookie 的浏览器中保留。
语法
Session.collection|property|method
集合
Contents 包含已用脚本命令添加到会话中的项目。
StaticObjects 包含通过 <OBJECT> 标记创建的并给定了会话作用域的对象。
属性
CodePage 将用于符号映射的代码页。
LCID 现场标识。
SessionID 返回用户的会话验证。
Timeout 应用程序会话状态的超时时限,以分钟为单位。
方法
Abandon 该方法破坏 Session 对象并释放其资源。
事件
asa 文件中声明下列事件的脚本。
Session_OnEnd
Session_OnStart
注释

您可以在 Session 对象中存储值。存储在 Session 对象中的信息在会话及会话作用域内有效。

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
  • 建表
  1. CREATE TABLE IF NOT EXISTS zipkin_spans (
  2. `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  3. `trace_id` BIGINT NOT NULL,
  4. `id` BIGINT NOT NULL,
  5. `name` VARCHAR(255) NOT NULL,
  6. `parent_id` BIGINT,
  7. `debug` BIT(1),
  8. `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
  9. `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
  10. ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
  11. ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
  12. ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
  13. ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
  14. ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
  15. ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
  16. CREATE TABLE IF NOT EXISTS zipkin_annotations (
  17. `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
  18. `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
  19. `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
  20. `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
  21. `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
  22. `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
  23. `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
  24. `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
  25. `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
  26. `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
  27. `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
  28. ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
  29. ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
  30. ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
  31. ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
  32. ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
  33. ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
  34. ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
  35. ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
  36. CREATE TABLE IF NOT EXISTS zipkin_dependencies (
  37. `day` DATE NOT NULL,
  38. `parent` VARCHAR(255) NOT NULL,
  39. `child` VARCHAR(255) NOT NULL,
  40. `call_count` BIGINT
  41. ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
  42. ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
  • 启动zipkin命令

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

(3)Elasticsearch方式

本文建议使用此方法。

3、dubbo项目快速接入

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

  1. <dependency>
  2. <groupId>io.zipkin.brave</groupId>
  3. <artifactId>brave-dubbo</artifactId>
  4. <version>1.0.0-SNAPSHOT</version>
  5. </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、大功告成

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

%title插图%num

Paste_Image.png

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

%title插图%num

MySQL中的alter table命令的基本使用方法及提速优化

首先,分析以下alter一个超大表低效率的原因:
1. mysql中alter表的机制是:创建一个符合alter目标的表,然后把数据全部插入到新表中,然后删除老表。
2. 在数据迁移时,每插入一行,需要对索引进行一次更新。效率低下且会产生索引碎片
3. 当没有足够的内存,或者表上的索引非常多时,效率十分的低下。

避免方式:

1. 使用正确的alter语句
如露alter的目标是更改某列的default值。mysql有特定的语句只对frm文件进行操作,而不更新index和data。
错误的方式:alter table tbl_name modify column col1 int not null default 5;
正确的方式:alter table tbl_name alter column col1 set default 5;

2. 手动的执行alter的机制
首先,create tbl_new like tbl_old;
然后,alter table modify column …
然后,alter tbl_new disable keys;
—load data—
然后,alter tbl_new enable keys;
*后,rename table tbl_old to tbl_new,tbl_new to tbl_old;

3. 人工修改frm表(危险!!!)
在5.1.36的myisam下,对这种方法进行了测试,并没有成功,会报index corrupted的错误。

首先,create tbl_new like tbl_old;
然后,alter table modify column …
然后,flush tables with read lock;
接着在shell中将两个表的frm文件互换。

*后,unlock tables;

Solr术语介绍:SolrCloud,单机Solr,Collection,Shard,Replica,Core之间的关系

Solr有一堆让人发晕的术语如:collections,shards,replicas,cores,config sets.

在了解这些术语之前需要先做做如下功课:

1)什么是倒排索引?

2)搜索引擎是怎么工作的?

Solr术语介绍:

Collections:SolrCloud集群中的一个完整的逻辑上的倒排索引(什么是倒排索引?),和一个独立的config set相关联,由一个或者多个shard组成,shard可以在不同的服务器上,shard对搜索接口的调用者来说是隐形的,搜索者不用考虑在搜索时如何指定shard,只需要传入Collection名即可。

Config Set:包含两个*根本的配置文件:solrconfig.xml和schema.xml,视这两个文件的内容而定是否需要包含其他文件。SolrCloud的config set目录会上传到zookeeper中,而传统单机Solr的config set是保存在本地文件夹中。

Core:一个Solr Core是一个包含索引和配置文件的运行实例,以前Solr Core是单例模式的,后来重构成了多实例的,(什么是SolrCores?)。一个Replica对应一个Core实例,同一个Shard对应的Replica的Core的配置和索引数据是一样的,但是是不同实例。

Replica:Shard的一个副本。一个Shard会在不同的服务器上保留Repicas(副本),通过选举机制(和zookeeper的leader选举机制类似)在Replicas(副本)中选出一个leader来对外提供服务。leader连不上了就重新选其他副本作为leader,这样能保证至多(副本数-1)台服务器挂掉后仍然能正常工作。

Shard:Collection的一个逻辑分片。每个Shard对应一个Core,并且包含一个索引(Collection)的文档(Documents)的不相交子集,一个Shard由至少一个Replica组成,当有多个Replicas时,选举机制选出作为leader的Replica。单机Solr中,Shard指的是Solr cores.

Zookeeper:分布式集群的基本组件,MapReduce、HDFS、Hive等分布式系统都基于它,Leader选举也要靠它。Solr有自己的内嵌Zookeeper,但是一般不会用内嵌的。部署Zookeeper至少需要3台主机(出于节约成本,可以和Solr实例部署在相同的服务器上,目前很多Solr users都是这么用的)。

 

各个术语之间对应关系图

%title插图%num

阿里云OSS术语

英文 中文
Bucket 存储空间
Object 对象或者文件
Endpoint OSS 访问域名
Region 地域或者数据中心
AccessKey AccessKeyId 和 AccessKeySecret 的统称,访问密钥
Put Object 简单上传
Post Object 表单上传
Multipart Upload 分片上传
Append Object 追加上传
Get Object 简单下载
Callback 回调
Object Meta 文件元信息。用来描述文件信息,例如长度,类型等
Data 文件数据
Key 文件名
ACL (Access Control List) 存储空间或者文件的权限

mybatis中的#和$的区别

#相当于对数据 加上 双引号,$相当于直接显示数据

 

1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”.

2. $将传入的数据直接显示生成在sql中。如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id,  如果传入的值是id,则解析成的sql为order by id.

3. #方式能够很大程度防止sql注入。

4.$方式无法防止Sql注入。

5.$方式一般用于传入数据库对象,例如传入表名.

6.一般能用#的就别用$.

MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

字符串替换
默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDER BY,你可以这样来使用:
ORDER BY ${columnName}
这里MyBatis不会修改或转义字符串。

重要:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。

Hadoop 新 MapReduce 框架 Yarn 详解

原 Hadoop MapReduce 框架的问题

对于业界的大数据存储及分布式处理系统来说,Hadoop 是耳熟能详的卓越开源分布式文件存储及处理框架,对于 Hadoop 框架的介绍在此不再累述,读者可参考 Hadoop 官方简介。使用和学习过老 Hadoop 框架(0.20.0 及之前版本)的同仁应该很熟悉如下的原 MapReduce 框架图:

图 1.Hadoop 原 MapReduce 架构

图 1.Hadoop 原 MapReduce 架构

从上图中可以清楚的看出原 MapReduce 程序的流程及设计思路:

  1. 首先用户程序 (JobClient) 提交了一个 job,job 的信息会发送到 Job Tracker 中,Job Tracker 是 Map-reduce 框架的中心,他需要与集群中的机器定时通信 (heartbeat), 需要管理哪些程序应该跑在哪些机器上,需要管理所有 job 失败、重启等操作。
  2. TaskTracker 是 Map-reduce 集群中每台机器都有的一个部分,他做的事情主要是监视自己所在机器的资源情况。
  3. TaskTracker 同时监视当前机器的 tasks 运行状况。TaskTracker 需要把这些信息通过 heartbeat 发送给 JobTracker,JobTracker 会搜集这些信息以给新提交的 job 分配运行在哪些机器上。上图虚线箭头就是表示消息的发送 – 接收的过程。

可以看得出原来的 map-reduce 架构是简单明了的,在*初推出的几年,也得到了众多的成功案例,获得业界广泛的支持和肯定,但随着分布式系统集群的规模和其工作负荷的增长,原框架的问题逐渐浮出水面,主要的问题集中如下:

  1. JobTracker 是 Map-reduce 的集中处理点,存在单点故障。
  2. JobTracker 完成了太多的任务,造成了过多的资源消耗,当 map-reduce job 非常多的时候,会造成很大的内存开销,潜在来说,也增加了 JobTracker fail 的风险,这也是业界普遍总结出老 Hadoop 的 Map-Reduce 只能支持 4000 节点主机的上限。
  3. 在 TaskTracker 端,以 map/reduce task 的数目作为资源的表示过于简单,没有考虑到 cpu/ 内存的占用情况,如果两个大内存消耗的 task 被调度到了一块,很容易出现 OOM。
  4. 在 TaskTracker 端,把资源强制划分为 map task slot 和 reduce task slot, 如果当系统中只有 map task 或者只有 reduce task 的时候,会造成资源的浪费,也就是前面提过的集群资源利用的问题。
  5. 源代码层面分析的时候,会发现代码非常的难读,常常因为一个 class 做了太多的事情,代码量达 3000 多行,,造成 class 的任务不清晰,增加 bug 修复和版本维护的难度。
  6. 从操作的角度来看,现在的 Hadoop MapReduce 框架在有任何重要的或者不重要的变化 ( 例如 bug 修复,性能提升和特性化 ) 时,都会强制进行系统级别的升级更新。更糟的是,它不管用户的喜好,强制让分布式集群系统的每一个用户端同时更新。这些更新会让用户为了验证他们之前的应用程序是不是适用新的 Hadoop 版本而浪费大量时间。

新 Hadoop Yarn 框架原理及运作机制

从业界使用分布式系统的变化趋势和 hadoop 框架的长远发展来看,MapReduce 的 JobTracker/TaskTracker 机制需要大规模的调整来修复它在可扩展性,内存消耗,线程模型,可靠性和性能上的缺陷。在过去的几年中,hadoop 开发团队做了一些 bug 的修复,但是*近这些修复的成本越来越高,这表明对原框架做出改变的难度越来越大。

为从根本上解决旧 MapReduce 框架的性能瓶颈,促进 Hadoop 框架的更长远发展,从 0.23.0 版本开始,Hadoop 的 MapReduce 框架完全重构,发生了根本的变化。新的 Hadoop MapReduce 框架命名为 MapReduceV2 或者叫 Yarn,其架构图如下图所示:

图 2. 新的 Hadoop MapReduce 框架(Yarn)架构

图 2. 新的 Hadoop MapReduce 框架(Yarn)架构

重构根本的思想是将 JobTracker 两个主要的功能分离成单独的组件,这两个功能是资源管理和任务调度 / 监控。新的资源管理器全局管理所有应用程序计算资源的分配,每一个应用的 ApplicationMaster 负责相应的调度和协调。一个应用程序无非是一个单独的传统的 MapReduce 任务或者是一个 DAG( 有向无环图 ) 任务。ResourceManager 和每一台机器的节点管理服务器能够管理用户在那台机器上的进程并能对计算进行组织。

事实上,每一个应用的 ApplicationMaster 是一个详细的框架库,它结合从 ResourceManager 获得的资源和 NodeManager 协同工作来运行和监控任务。

上图中 ResourceManager 支持分层级的应用队列,这些队列享有集群一定比例的资源。从某种意义上讲它就是一个纯粹的调度器,它在执行过程中不对应用进行监控和状态跟踪。同样,它也不能重启因应用失败或者硬件错误而运行失败的任务。

ResourceManager 是基于应用程序对资源的需求进行调度的 ; 每一个应用程序需要不同类型的资源因此就需要不同的容器。资源包括:内存,CPU,磁盘,网络等等。可以看出,这同现 Mapreduce 固定类型的资源使用模型有显著区别,它给集群的使用带来负面的影响。资源管理器提供一个调度策略的插件,它负责将集群资源分配给多个队列和应用程序。调度插件可以基于现有的能力调度和公平调度模型。

上图中 NodeManager 是每一台机器框架的代理,是执行应用程序的容器,监控应用程序的资源使用情况 (CPU,内存,硬盘,网络 ) 并且向调度器汇报。

每一个应用的 ApplicationMaster 的职责有:向调度器索要适当的资源容器,运行任务,跟踪应用程序的状态和监控它们的进程,处理任务的失败原因

新旧 Hadoop MapReduce 框架比对

让我们来对新旧 MapReduce 框架做详细的分析和对比,可以看到有以下几点显著变化:

首先客户端不变,其调用 API 及接口大部分保持兼容,这也是为了对开发使用者透明化,使其不必对原有代码做大的改变 ( 详见 2.3 Demo 代码开发及详解),但是原框架中核心的 JobTracker 和 TaskTracker 不见了,取而代之的是 ResourceManager, ApplicationMaster 与 NodeManager 三个部分。

我们来详细解释这三个部分,首先 ResourceManager 是一个中心的服务,它做的事情是调度、启动每一个 Job 所属的 ApplicationMaster、另外监控 ApplicationMaster 的存在情况。细心的读者会发现:Job 里面所在的 task 的监控、重启等等内容不见了。这就是 AppMst 存在的原因。ResourceManager 负责作业与资源的调度。接收 JobSubmitter 提交的作业,按照作业的上下文 (Context) 信息,以及从 NodeManager 收集来的状态信息,启动调度过程,分配一个 Container 作为 App Mstr

NodeManager 功能比较专一,就是负责 Container 状态的维护,并向 RM 保持心跳。

ApplicationMaster 负责一个 Job 生命周期内的所有工作,类似老的框架中 JobTracker。但注意每一个 Job(不是每一种)都有一个 ApplicationMaster,它可以运行在 ResourceManager 以外的机器上。

Yarn 框架相对于老的 MapReduce 框架什么优势呢?我们可以看到:

  1. 这个设计大大减小了 JobTracker(也就是现在的 ResourceManager)的资源消耗,并且让监测每一个 Job 子任务 (tasks) 状态的程序分布式化了,更安全、更优美。
  2. 在新的 Yarn 中,ApplicationMaster 是一个可变更的部分,用户可以对不同的编程模型写自己的 AppMst,让更多类型的编程模型能够跑在 Hadoop 集群中,可以参考 hadoop Yarn 官方配置模板中的 mapred-site.xml 配置。
  3. 对于资源的表示以内存为单位 ( 在目前版本的 Yarn 中,没有考虑 cpu 的占用 ),比之前以剩余 slot 数目更合理。
  4. 老的框架中,JobTracker 一个很大的负担就是监控 job 下的 tasks 的运行状况,现在,这个部分就扔给 ApplicationMaster 做了,而 ResourceManager 中有一个模块叫做 ApplicationsMasters( 注意不是 ApplicationMaster),它是监测 ApplicationMaster 的运行状况,如果出问题,会将其在其他机器上重启。
  5. Container 是 Yarn 为了将来作资源隔离而提出的一个框架。这一点应该借鉴了 Mesos 的工作,目前是一个框架,仅仅提供 java 虚拟机内存的隔离 ,hadoop 团队的设计思路应该后续能支持更多的资源调度和控制 , 既然资源表示成内存量,那就没有了之前的 map slot/reduce slot 分开造成集群资源闲置的尴尬情况。

新的 Yarn 框架相对旧 MapRduce 框架而言,其配置文件 , 启停脚本及全局变量等也发生了一些变化,主要的改变如下:

表 1. 新旧 Hadoop 脚本 / 变量 / 位置变化表

由于新的 Yarn 框架与原 Hadoop MapReduce 框架相比变化较大,核心的配置文件中很多项在新框架中已经废弃,而新框架中新增了很多其他配置项,看下表所示会更加清晰:

表 2. 新旧 Hadoop 框架配置项变化表

Hadoop Yarn 框架 Demo 示例

Demo 场景介绍:Weblogic 应用服务器日志分析

了解了 hadoop 新的 Yarn 框架的架构和思路后,我们用一个 Demo 示例来检验新 Yarn 框架下 Map-Reduce 程序的开发部署。

我们考虑如下应用场景:用户的生产系统由多台 Weblogic 应用服务器组成,每天需要每台对应用服务器的日志内容进行检查,统计其日志级别和日志模块的总数。

WebLogic 的日志范例如下图所示:

图 3.Weblogic 日志示例

图 3.Weblogic 日志示例

如上图所示,<Info> 为 weblogic 的日志级别,<Security>,<Management> 为 Weblogic 的日志模块,我们主要分析 loglevel 和 logmodule 这两个维度分别在 WebLogic 日志中出现的次数,每天需要统计出 loglevel 和 logmodule 分别出现的次数总数。

Demo 测试环境 Yarn 框架搭建

由于 Weblogic 应用服务器分布于不同的主机,且日志数据量巨大,我们采用 hadoop 框架将 WebLogic 各个应用服务器主机上建立分布式目录,每天将 WebLogic 日志装载进 hadoop 分布式文件系统,并且编写基于 Yarn 框架的 MapReduce 程序对日志进行处理,分别统计出 LogLevel 和 Logmodule 在日志中出现的次数并计算总量,然后输出到分布式文件系统中,输出目录命名精确到小时为后缀以便区分每次 Demo 程序运行的处理结果。

我们搭建一个 Demo 测试环境以验证 Yarn 框架下分布式程序处理该案例的功能,以两台虚拟机作为该 Demo 的运行平台,两机均为 Linux 操作系统,机器 hostname 为 OEL 和 Stephen,OEL 作为 NameNode 和 ResouceManager 节点主机,64 位,Stephen 作为 DataNode 和 NodeManager 节点主机,32 位(Hadoop 支持异构性), 具体如下:

表 3.Demo 测试环境表

我们把 hadoop 安装在两台测试机的 /hadoop 文件系统目录下,安装后的 hadoop 根目录为:/hadoop/hadoop-0.23.0,规划分布式文件系统存放于 /hadoop/dfs 的本地目录,对应分布式系统中的目录为 /user/oracle/dfs

我们根据 Yarn 框架要求,分别在 core-site.xml 中配置分布式文件系统的 URL,详细如下:

清单 1.core-site.xml 配置
1
2
3
4
5
6
< configuration >
   < property >
   < name >fs.defaultFS</ name >
   < value >hdfs://192.168.137.8:9100</ value >
   </ property >
</ configuration >

在 hdfs-site.xml 中配置 nameNode,dataNode 的本地目录信息,详细如下:

清单 2.hdfs-site.xml 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
< configuration >
< property >
  < name >dfs.namenode.name.dir</ name >
  < value >/hadoop/dfs/name</ value >
  < description >  </ description >
</ property >
< property >
  < name >dfs.datanode.data.dir</ name >
  < value >/hadoop/dfs/data</ value >
  < description > </ description >
</ property >
< property >
   < name >dfs.replication</ name >
   < value >2</ value >
</ property >
</ configuration >

在 mapred-site.xml 中配置其使用 Yarn 框架执行 map-reduce 处理程序,详细如下:

清单 3.mapred-site.xml 配置
1
2
3
4
5
6
< configuration >
  < property >
  < name >mapreduce.framework.name</ name >
  < value >Yarn</ value >
  </ property >
</ configuration >

*后在 Yarn-site.xml 中配置 ResourceManager,NodeManager 的通信端口,web 监控端口等,详细如下:

清单 4.Yarn-site.xml 配置
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
<? xml version = "1.0" ?>
< configuration >
<!-- Site specific YARN configuration properties -->
  < property >
  < name >Yarn.nodemanager.aux-services</ name >
  < value >mapreduce.shuffle</ value >
  </ property >
  < property >
  < description >The address of the applications manager interface in the RM.</ description >
  < name >Yarn.resourcemanager.address</ name >
  < value >192.168.137.8:18040</ value >
  </ property >
  < property >
  < description >The address of the scheduler interface.</ description >
  < name >Yarn.resourcemanager.scheduler.address</ name >
  < value >192.168.137.8:18030</ value >
  </ property >
  < property >
  < description >The address of the RM web application.</ description >
  < name >Yarn.resourcemanager.webapp.address</ name >
  < value >192.168.137.8:18088</ value >
  </ property >
 
  < property >
  < description >The address of the resource tracker interface.</ description >
  < name >Yarn.resourcemanager.resource-tracker.address</ name >
  < value >192.168.137.8:8025</ value >
  </ property >
</ configuration >

具体配置项的含义,在 hadoop 官方网站有详细的说明,读者可以参见 hadoop 0.23.0 官方配置模板

Demo 代码开发及详解

以下我们详细介绍一下新的 Yarn 框架下针对该应用场景的 Demo 代码的开发, 在 Demo 程序的每个类都有详细的注释和说明,Yarn 开发为了兼容老版本,API 变化不大,可以参考 官方 Hadoop Yarn 框架 API

在 Map 程序中,我们以行号为 key,行文本为 value 读取每一行 WebLogic 日志输入,将 loglevel 和 logmodule 的值读出作为 Map 处理后的新的 key 值,由于一行中 loglevel 和 logmodule 的出现次数应该唯一,所以经 Map 程序处理后的新的 record 记录的 value 应该都为 1:

清单 5. Map 业务逻辑
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
public static class MapClass extends Mapper< Object , Text, Text, IntWritable> 
  {
  private Text record = new Text(); 
  private static final IntWritable recbytes = new IntWritable(1); 
  public void map(Object key, Text value,Context context) 
    throws IOException,InterruptedException {  
  String line = value.toString(); 
// 没有配置 RecordReader,所以默认采用 line 的实现,
//key 就是行号,value 就是行内容,
// 按行 key-value 存放每行 loglevel 和 logmodule 内容
  if (line == null || line.equals("")) 
    return;
  String[] words = line.split("> <"); 
  if (words == null || words.length < 2) 
    return;
  String logLevel = words[1]; 
  String moduleName = words[2]; 
 
  record.clear();
  record.set(new StringBuffer("logLevel::").append(logLevel).toString()); 
  context.write(record, recbytes); 
  // 输出日志级别统计结果,通过 logLevel:: 作为前缀来标示。
 
  record.clear();
   record.set(new StringBuffer("moduleName::").append(moduleName).toString()); 
  context.write(record, recbytes); 
   // 输出模块名的统计结果,通过 moduleName:: 作为前缀来标示
 
  }

由于有 loglevel 和 logmodule 两部分的分析工作,我们设定两个 Reduce 来分别处理这两部分,loglevel 的交给 reduce1,logmodule 交给 reduce2。因此我们编写 Patitioner 类,根据 Map 传过来的 Key 中包含的 logLevel 和 moduleName 的前缀,来分配到不同的 Reduce:

清单 6.Partition 业务逻辑

public static class PartitionerClass extends Partitioner<Text, IntWritable>
{
public int getPartition(Text key, IntWritable value, int numPartitions)
{
if (numPartitions >= 2)//Reduce 个数,判断 loglevel 还是 logmodule 的统计,分配到不同的 Reduce
if (key.toString().startsWith(“logLevel::”))
return 0;
else if(key.toString().startsWith(“moduleName::”))
return 1;
else return 0;
else
return 0;
}

}

在 Reduce 程序中,累加并合并 loglevel 和 logmodule 的出现次数

清单 7. Reduce 业务逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class ReduceClass extends  Reducer< Text , IntWritable,Text, IntWritable> 
        {
            private IntWritable result = new IntWritable(); 
            public void reduce(Text key, Iterable< IntWritable > values, 
                    Context context)throws IOException, 
                                                     InterruptedException { 
               
                int tmp = 0; 
                for (IntWritable val : values) { 
                    tmp = tmp + val.get();                                     
                }
                result.set(tmp);
                context.write(key, result);// 输出*后的汇总结果
            }   
        }

以上完成了 MapReduce 的主要处理逻辑,对于程序入口,我们使用 Hadoop 提供的 Tools 工具包方便的进行 May-Reduce 程序的启动和 Map/Reduce 对应处理 class 的配置。

清单 8. Main 执行类
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import java.io.File; 
import java.io.IOException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.Iterator; 
import org.apache.hadoop.conf.Configuration; 
import org.apache.hadoop.conf.Configured; 
import org.apache.hadoop.fs.Path; 
import org.apache.hadoop.io.IntWritable; 
import org.apache.hadoop.io.Text; 
import org.apache.hadoop.mapreduce.Job; 
import org.apache.hadoop.mapreduce.Reducer; 
import org.apache.hadoop.mapreduce.Mapper; 
import org.apache.hadoop.mapreduce.Partitioner; 
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; 
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; 
import org.apache.hadoop.util.Tool; 
import org.apache.hadoop.util.ToolRunner; 
public class LogAnalysiser extends Configured implements Tool { 
   public static void main(String[] args) 
  {
    try
  {
  int res; 
  res = ToolRunner.run(new Configuration(),new LogAnalysiser(), args); 
  System.exit(res);
  } catch (Exception e) 
  {
  e.printStackTrace();
  }
  }
  public int run(String[] args) throws Exception 
  {
  if (args == null || args.length < 2 )
  {
  System.out.println("need inputpath and outputpath"); 
  return 1; 
  }
  String inputpath args [0];
  String outputpath args [1];
  String shortin args [0];
  String shortout args [1];
  if (shortin.indexOf(File.separator) >= 0) 
  shortin = shortin.substring(shortin.lastIndexOf(File.separator)); 
  if (shortout.indexOf(File.separator) >= 0) 
  shortout = shortout.substring(shortout.lastIndexOf(File.separator)); 
  SimpleDateFormat formater = new SimpleDateFormat("yyyy.MM.dd.HH.mm"); 
  shortout = new StringBuffer(shortout).append("-") 
  .append(formater.format(new Date())).toString(); 
 
 
  if (!shortin.startsWith("/")) 
  shortin = "/" + shortin; 
  if (!shortout.startsWith("/")) 
  shortout = "/" + shortout; 
  shortin = "/user/oracle/dfs/" + shortin; 
  shortout = "/user/oracle/dfs/" + shortout;   
  File inputdir = new File(inputpath); 
  File outputdir = new File(outputpath); 
 
  if (!inputdir.exists() || !inputdir.isDirectory()) 
  {
  System.out.println("inputpath not exist or isn't dir!"); 
  return 0; 
  }
  if (!outputdir.exists()) 
  {
  new File(outputpath).mkdirs(); 
  }
// 以下注释的是 hadoop 0.20.X 老版本的 Job 代码,在 hadoop0.23.X 新框架中已经大大简化
//   Configuration conf = getConf(); 
//   JobConf job = new JobConf(conf, LogAnalysiser.class);   
//    JobConf conf = new JobConf(getConf(),LogAnalysiser.class);// 构建 Config 
//    conf.setJarByClass(MapClass.class); 
//    conf.setJarByClass(ReduceClass.class); 
//    conf.setJarByClass(PartitionerClass.class); 
//    conf.setJar("hadoopTest.jar"); 
//    job.setJar("hadoopTest.jar"); 
// 以下是新的 hadoop 0.23.X Yarn 的 Job 代码
job job = new Job(new Configuration()); 
    job.setJarByClass(LogAnalysiser.class);
    job.setJobName("analysisjob");
    job.setOutputKeyClass(Text.class);// 输出的 key 类型,在 OutputFormat 会检查
    job.setOutputValueClass(IntWritable.class); // 输出的 value 类型,在 OutputFormat 会检查
    job.setJarByClass(LogAnalysiser.class);
    job.setMapperClass(MapClass.class);
    job.setCombinerClass(ReduceClass.class);
    job.setReducerClass(ReduceClass.class);
    job.setPartitionerClass(PartitionerClass.class);
    job.setNumReduceTasks(2);// 强制需要有两个 Reduce 来分别处理流量和次数的统计
    FileInputFormat.setInputPaths(job, new Path(shortin));//hdfs 中的输入路径
    FileOutputFormat.setOutputPath(job,new Path(shortout));//hdfs 中输出路径
   
    Date startTime = new Date(); 
    System.out.println("Job started: " + startTime); 
    job.waitForCompletion(true);   
    Date end_time = new Date(); 
    System.out.println("Job ended: " + end_time); 
    System.out.println("The job took " + 
    (end_time.getTime() - startTime.getTime()) /1000 + " seconds."); 
    // 删除输入和输出的临时文件
//    fileSys.copyToLocalFile(new Path(shortout),new Path(outputpath)); 
//    fileSys.delete(new Path(shortin),true); 
//    fileSys.delete(new Path(shortout),true); 
    return 0; 
  }
}

Demo 部署及运行

Demo 输入输出的控制

本 demo 中我们将从 Weblogic 日志目录中拷贝原始待处理日志文件作为 Yarn 程序的输入,使用 hadoop dfs 命令将其放入分布式目录的 input 目录,处理完后将生成以时间戳为文件目录后缀的输出目录

Weblogic 日志存放的原始目录位于:/u01/app/Oracle/Middleware/user_projects/domains/test_domain/AdminServer/logs

分布式文件系统中的输入目录:/user/oracle/dfs/input

分布式文件系统中的输出目录:/user/oracle/dfs/output_%YYYY-MM-DD-hh-mm%

Demo 打包和部署

可以使用 JDeveloper 或者 Eclipse 等 IDE 工具将开发的 Hadoop Demo 代码打包为 jar,并指定 Main 类为 LoyAnalyze,本文中我们采用 JDeveloper 打包 Demo 代码,如下图示例:

图 4.Yarn Demo 程序打包示例

图 4.Yarn Demo 程序打包示例

Demo 执行与跟踪

我们在 OEL 主机(NameNode&ResourceManager 主机,192.168.137.8)上启动 dfs 分布式文件系统:

图 5. 启动 Demo dfs 文件系统

图 5. 启动 Demo dfs 文件系统

从上图可以看出 dfs 分布式文件系统已经在 OEL 和 Stephen 主机上成功启动,我们通过默认的分布式文件系统 Web 监控 端口 http://192.168.137.8:50070(也可以在上文中 core-site.xml 中配置 dfs.namenode.http-address 项指定其他端口 ) 来验证其文件系统情况:

图 6.hadoop 文件系统 web 监控页面

图 6.hadoop 文件系统 web 监控页面

从上图中我们可以看到 /user/oracle/dfs 分布式文件系统已成功建立。

接下来我们在 NameNode 主机(OEL,192.168.137.8)上启动 Yarn 框架:

图 7. 启动 Demo Yarn 框架

图 7. 启动 Demo Yarn 框架

从上图我们可以看到 ResouceManager 在 OEL 主机上成功启动,NodeManager 进程在 Stephen 节点主机上也已经启动,至此整个新的 Hadoop Yarn 框架已经成功启动。

我们将打好的 testHadoop.jar 包上传至 NameNode 主机(OEL)的 /hadoop/hadoop-0.23.0/ 根目录下,我们使用 Hadoop 自带的 hadoop 命令行工具执行 Demo 的 jar 包,具体步骤为,先使用 hadoop dfs 命令将输入文件(weblogic 原始日志)拷贝至 dfs 分布式目录的 input 输入目录,清理 dfs 分布式目录下的 output 输出子目录。然后使用 hadoop jar 命令执行 testHadoop 的 jar 包。

执行 Demo 的 shell 脚本示例如下:

1
2
3
4
5
6
./bin/hadoop dfs -rmr /user/oracle/dfs/output* 
./bin/hadoop dfs -rmr /user/oracle/dfs/input 
./bin/hadoop dfs -mkdir /user/oracle/dfs/input 
./bin/hadoop dfs -copyFromLocal ./input/*.log /user/oracle/dfs/input/ 
./bin/hadoop jar ./hadoopTest.jar /hadoop/hadoop-0.23.0/input 
                 /hadoop/hadoop-0.23.0/output

清单 9.Demo 执行脚本

然后我们使用上文中的脚本启动 demo 并执行:

图 8.Demo 程序运行

图 8.Demo 程序运行

查看大图

从上图的 console 输出中我们可以看到 Demo 程序的结果和各项统计信息输出,下面我们通过 Web 监控界面详细中观察程序执行的执行流程和步骤细节。

Job 启动后我们可以通过 ResourceManager 的 Web 端口(在上文中 Yarn-site.xml 配置文件中 Yarn.resourcemanager.webapp.address 配置项) http://192.168.137.8:18088 来监控其 job 的资源调度。

图 9. 接收请求和生成 job application

图 9. 接收请求和生成 job application

查看大图

上图中我们可以看到 Yarn 框架接受到客户端请求 , 如上图所示 ID 为 application_1346564668712_0003 的 job 已经是 accepted 状态

我们点击该 ID 的链接进入到该 application 的 Map-Reduce 处理监控页面,该界面中有动态分配的 ApplicationMaster 的 Web 跟踪端口可以监视 MapReduce 程序的步骤细节

图 10.hadoop MapReduce Application Web 监控页面 (1)

图 10.hadoop MapReduce Application Web 监控页面 (1)

点击上图中 ApplicationMaster 的 URL 可以进入该 ApplicationMaster 负责管理的 Job 的具体 Map-Reduce 运行状态:

图 11.hadoop MasterApplication Web 监控页面(2)

图 11.hadoop MasterApplication Web 监控页面(2)

上图中我们可以看到 ID 为 application_1346564668712_0003 的 Job 正在执行,有 2 个 Map 进程,已经处理完毕,有 2 个 Reduce 正在处理,这跟我们程序设计预期的是一样的。

当状态变为 successful 后,进入 dfs 文件系统可以看到,输出的 dfs 文件系统已经生成,位置位于 /user/oracle/dfs 下,目录名为 output-2012.09.02.13.52,可以看到格式和命名方式与 Demo 设计是一致的,如下图所示:

图 12.Demo 输出目录(1)

图 12.Demo 输出目录(1)

我们进入具体的输出目录,可以清楚的看到程序处理的输出结果,正如我们 Demo 中设计的,两个 Reduce 分别生成了两个输出文件,分别是 part-r-00000 和 part-r-00001,对应 Module 和 Log Level 的处理输出信息:

图 13.Demo 输出目录(2)

图 13.Demo 输出目录(2)

点击 part-r-00000 的输出文件链接,可以看到程序处理后的 log level 的统计信息:

图 14.Demo 输出结果(1)

图 14.Demo 输出结果(1)

点击 part-r-00001 的输出文件链接,可以看到程序处理后 Module 的统计信息:

图 15.Demo 输出结果(2)

图 15.Demo 输出结果(2)

至此我们基于新的 Yarn 框架的 Demo 完全成功运行,实现功能与预期设计完全一致,运行状态和 NameNode/DataNode 部署,Job/MapReduece 程序的调度均和设计一致。读者可参考该 Demo 的配置及代码进行修改,做为实际生产环境部署和实施的基础。

转载自:https://www.ibm.com/developerworks/cn/opensource/os-cn-hadoop-yarn/

DNS域名服务器介绍

dns服务—domain name server
一、DNS服务简介

DNS(Domain Name System,域名系统)
因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,*终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)

二、DNS 使用协议和功能

功能: 负责将主机名称,转化为 IP 地址的服务。
使用协议: TCP / UDP
监听端口: 53 953
三、DNS 解析工具 nslookup

yum -y install bind-utils

[root@deng-120 ~]# nslookup www.baidu.com

Server: 202.96.128.166
Address: 202.96.128.166#53

Non-authoritative answer:
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 14.215.177.39
Name: www.a.shifen.com
Address: 14.215.177.38

四、域名级别

%title插图%num

1、根域—–>顶级域名—–>二级域名—->子域—->主机名

2、根域:全球有13台,一台为主,12台为辅,由美国ICANM机构维护

3、顶级域名(com[工商]、net、org、edu[教育]、gov[政府机构])

4、二级域名(cn、hk、jp、tw)

5、域名机构:http://www.blog.cn.com.

http://主机host.子域.二级域名.顶级域名.根域(隐藏)

五、DNS 服务器的类型

纯缓存的 DNS 服务器 ( 根域转发服务器 ): 例如: 202.96.128.166
没有解析记录,收到请求之后,直接转发给 “根域”。 解析之后,会将记录缓存一段时间

转发DNS服务器:没有解析记录,收到请求之后,发给另外一台DNS服务器。 全局转发 , 单域转发

主DNS服务器:自己有解析记录文件,对于某些特定的域进行解析,是某个区域的权威DNS服务器

辅助/从 DNS服务器:它自己本来没有解析记录,解析记录来自于主DNS服务器,定时向主DNS服务器请求解析记录同步

六、 DNS 解析过程

%title插图%num

(1)客户机发出请求解析域名 www.baidu.com 的报文;

(2)本地域名服务器收到请求后,查询本地缓存,假如没有缓存记录,本地服务器 dns.company.com 则会向根域名服务器 NS.INTER.NET 发起请求解析域名 www.baidu.com ;

(3)根域名服务器NS.INTER.NET收到请求后查询本地记录得到如下结果:linejet.com NS dns.baidu.com(表示baidu.com域中的域名服务器为:dns.baidu.com ), 同时给出dns.baidu.com的地址,并将结果返回给域名服务器dns.company.com;

(4)域名服务器 dns.company.com 收到回应后,再发出请求解析域名www.linejet.com的报文.

(5)域名服务器dns.baidu.com收到请求后,开始查询本地的记录,找到如下一条记录: www.baidu.com A 14.215.177.38(表示 baidu.com 域中域名服务器 dns.baidu.com 的 IP 地址为:14.215.177.38),并将结果返回给客户本地域名服务器 dns.company.com;

(6)客户本地域名服务器将返回的结果保存到本地缓存,同时将结果返回给客户机;
这样就完成了一次域名解析过程

什么是AD域控制器

域”的真正含义指的是服务器控制网络上的计算机能否加入的计算机组合。一提到组合,势必需要严格的控制。所以实

行严格的管理对网络安全是非常必要的。在对等网模式下,任何一台电脑只要接入网络,其他机器就都可以访问共享资

源,如共享上网等。尽管对等网络上的共享文件可以加访问密码,但是非常容易被破解。在由Windows 9x构成的对等

网中,数据的传输是非常不安全的。

不过在“域”模式下,至少有一台服务器负责每一台联入网络的电脑和用户的验证工作,相当于一个单位的门卫一样,称

为“域控制器(Domain Controller,简写为DC)”。

域控制器中包含了由这个域的账户、密码、属于这个域的计算机等信息构成的数据库。当电脑联入网络时,域控制器首

先要鉴别这台电脑是否是属于这个域的,用户使用的登录账号是否存在、密码是否正确。如果以上信息有一样不正确,

那么域控制器就会拒*这个用户从这台电脑登录。不能登录,用户就不能访问服务器上有权限保护的资源,他只能以对

等网用户的方式访问Windows共享出来的资源,这样就在一定程度上保护了网络上的资源。

要把一台电脑加入域,仅仅使它和服务器在网上邻居中能够相互“看”到是远远不够的,必须要由网络管理员进行相应的

设置,把这台电脑加入到域中。这样才能实现文件的共享。

1. 服务器端设置

以系统管理员身份在已经设置好Active Directory(活动目录)的Windows 2000 Server上登录,选择“开始”菜单中“程序

”选项中的“管理工具”,然后再选择“Active Directory用户和计算机”,之后在程序界面中右击“Computers”,在弹出的菜

单中单击“新建”,然后选择“计算机”,之后填入想要加入域的计算机名即可。要加入域的计算机名*好为英文,中文计

算机名可能会引起一些问题。

2. 客户端设置

首先要确认计算机名称是否正确,然后在桌面“网上邻居”上右击鼠标,点击“属性”出现网络属性设置窗口,确认“主网络

登录”为“Microsoft网络用户”。选中窗口上方的“Microsoft网络用户”(如果没有此项,说明没有安装,点击“添加”安装

“Microsoft网络用户”选项)。点击“属性”按钮,出现“Microsoft网络用户属性”对话框,选中“登录到Windows NT域”复选

框,在“Windows NT域”中输入要登录的域名即可。这时,如果是Windows 98操作系统的话,系统会提示需要重新启动

计算机,重新启动计算机之后,会出现一个登录对话框。在输入正确的域用户账号、密码以及登录域之后,就可以使用

Windows 2000 Server域中的资源了。请注意,这里的域用户账号和密码,必须是网络管理员为用户建的那个账号和密

码,而不是由本机用户自己创建的账号和密码。如果没有将计算机加入到域中,或者登录的域名、用户名、密码有一项

不正确,都会出现错误信息。

AD域、Windows AD域管理功能大全

Windows AD域管理功能大全,再也不用东拼西凑了!

功能一、AD域管理

1、AD域管理
通过批量创建和编辑用户帐户,指派管理权限等,简化Windows AD域的管理。
2、批量管理域用户
使用CSV文件批量导入用户、编辑用户属性、重置密码、批量迁移用户和用户对象。
3、批量创建域用户
通过导入CSV文件,批量创建用户,包含Exchange邮箱、终端服务等所有属性,指派到组。
4、批量编辑域用户
批量重置密码、解锁用户、迁移用户、删除/启用/禁用用户、添加到组或从组中移除、修改用户属性等。
5、管理不活跃/禁用的用户帐户
通过生成不活域/禁用的用户帐户列表,轻松删除、启用或迁移帐户,达到清理AD域的目的。
6、AD域密码管理
重置多个用户帐户的密码、配置密码策略、启用/禁用密码过期的用户。
7、手机App
通过手机App执行重置密码、启用、禁用、解锁以及删除用户帐户等操作。
8、AD域计算机管理
批量创建计算机、启用、禁用和迁移计算机,修改计算机属性和所属组。
9、终端服务管理
批量编辑终端服务主文件夹、路径、启动程序、会话时间和远程设置。

功能二、AD域报表

1、AD域报表
默认提供150多种报表,全面分析Windows AD域架构。
2、AD域用户报表
全面的用户分析报表,包括不活跃的用户、被锁定的用户、*后登录的用户以及被禁用的用户等。
3、用户登录报表
生成用户登录行为报表,包括登录时间、时长等属性。
4、密码报表
包括密码状态、安全权限、密码过期、无效登录尝试和密码变更报表。
5、*后登录报表
了解某组织单元、组或整个AD域里,用户*后一次登录时间,包括帐户状态、帐户创建时间等信息。
6、计算机报表
全面的计算机对象、域控制器、工作站、计算机帐户状态以及按照操作系统分类的报表。
7、组报表
所有类型的安全和分发组,包括其中的组成员报表以及分发列表及成员报表。
8、组织单元报表
详细的组织单元报表,包括*近创建的组织单元、*近修改的组织单元、GPO链接的组织单元、GPO阻止继承的组织单元以及空的组织单元。
9、计划报表
设置报表计划,自动生成并发送报表,报表可导出为CSV、PDF和HTML格式文件。

功能三、权限指派/工作流

1、帮助台管理
将管理权限指派给帮助台技术员或HR,降低AD管理员的工作负荷。
2、管理权限指派
设置工作流程,安全地将管理权限指派给其他用户。
3、划分不同权限
设置不同的角色,配置不同的管理权限,确保AD域的安全。
4、基于组织单元的权限指派
为一个组织单元配置一个管理员,执行相关的管理操作。
5、安全指派
按照一定的规则,设置管理权限,确保信息安全。

功能四、Exchange管理

1、Exchange服务器管理
在一个平台全面管理Exchange服务器和AD。
2、邮箱创建
批量创建邮箱,支持Exchange Server 2003, 2007/2010/2013版本和Office 365。
3、设置邮箱权限
可通过模板轻松批量设置邮箱权限,支持Exchange Servers 2003和2007/2010/2013。
4、禁用/删除Exchange邮箱
批量禁用和删除邮箱。
5、分发列表报表
全面分析分发列表,提供分发组、分发列表成员和非分发列表成员报表。
6、Exchange服务器邮箱迁移
批量将邮箱迁移到指定的Exchange服务器。
7、Exchange策略管理
应用多个Exchange策略,例如共享策略、角色指派策略、保留策略、UM策略和ActiveSync策略。

此内容由ManageEngien原创编辑整理,转载请标明出处。