Mapper 判断输入数据的文件名

很多时候我们在一个map-reduce任务中,会用到Join,这样整个job的输入可能就是两个以上的文件了(换句话说:mapper要处理两个以上的文件)。

mapper的输入可以是一个文件夹:

    FileInputFormat.setInputPaths(conf, new Path("/tmp/"));

1.在java代码中获取文件名

在java代码中获取文件名,需要得到input split 所在的文件名,需要从map函数中的context参数着手。

    // 获取 input split 所在的文件名
    private String getFileName(MapContext context) { 
        org.apache.hadoop.mapreduce.lib.input.FileSplit inputSplit = (FileSplit) context.getInputSplit();
        return inputSplit.getPath().getName();      
    }

如果需要获得在hdfs上的绝对路径,可以用以下代码实现:

String filepath = ((FileSplit)context.getInputSplit()).getPath().toString();

获取文件名的大致流程为:Context(map函数里) → InputSplit → FileSplit → Path → String(file name)。

2.在streaming中获取文件名

实际中经常用python开发streaming程序,在python代码中可以用如下方式获得文件名:

import os
filepath = os.environ["mapreduce_map_input_file"]

通过上面的代码即可达到获取文件名的目的,通过代码也很容易看出,文件名保存在名为mapreduce_map_input_file的环境变量中。

需要稍微注意的地方有两点: 
1.filepath保存的是文件在hdfs上的完整路径。 
2.新版本的api为mapreduce_map_input_file,老版本的api为map_input_file,在集群上尝试了老版本的api,代码会报错

JQuery Ajax + Json 实现瀑布流

function show() {
    $.ajax({
        type: "get",
        url: "goods",
        dataType: "json",
        success: function(data) {
            console.log(data);
            //var obj = JSON.parse(data);
            
            $.each(data, function(index,obj) {
                //console.log(obj.imgPath);
                
                //创建新的节点:div>img+p  
                var li = document.createElement('li');
                var div = document.createElement('div');
                var img = document.createElement('img');
                var p = document.createElement('p');
                
                img.src = obj.imgPath; //img获取图片地址
                p.innerHTML = obj.goodsTitle; //p获取图片标题  
                
                //添加
                div.appendChild(img);
                div.appendChild(p);
                li.appendChild(div);
                $("ul").append(li); 
            });
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            alert(errorThrown);
        }
    });
}
    
$(function(){
    $(window).scroll(function() {
        var top = $(window).scrollTop();
        var height = $(window).height();
        var domheight= $(document).height(); 
        
        // console.log("top:"+top+"height:"+height+"domheight:"+domheight);
        
        if( 20 + top + height >= domheight ){
            $('#showmore').text('加载中...');
            
            show();
        }
    });
})

RPC(Remote Procedure Call)——远程过程调用

RPC(Remote Procedure Call)——远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议

blob.png


====

1. A B 两个进程之间需要进行数据交换。

2.于是我们想出来在某个内存区域划出一个空间,然后向该空间中写入和读取数据。(共享文件也可以)

(常见的socket就是这一共享内存的抽象,只是现在大多指网络通路)

3.A B 通信完成。

====

4.A B需要完成更复杂的交互

5.于是我们指定一个协议,A B 根据该协议对数据的进行编码解码,根据协议内容做出决策。

====

6.发现协议过于复杂(比如 编号1代表调用 a函数,编号2代表b函数) 

7.试图优化协议,将函数参数和调用的函数名称作为协议的一部分,函数返回值类似

8.RPC达成

=====

9.表现出来的特性就是,object invok(parameter),就代表了,序列化 parameter 对象到中间格式,

利用远程服务器的 invok 函数进行处理 ,同时将返回的数据解码生成 object对象。

======总结=====

RPC 在整个过程中,体现了逐层抽象,将复杂的协议编解码和数据传输封装到了一个函数中。


v2-046e2ee4a881f059c419149262c6fb10_hd.jpg

v2-963e5a46ed9157df5bf74fd03b812f56_hd.jpg


原理:

本地过程调用RPC就是要像调用本地的函数一样去调远程函数。

在研究RPC前,我们先看看本地调用是怎么调的。

假设我们要调用函数Multiply来计算lvalue * rvalue的结果:

1 int Multiply(int l, int r) {
2    int y = l * r;
3    return y;
4 }
5 
6 int lvalue = 10;
7 int rvalue = 20;
8 int l_times_r = Multiply(lvalue, rvalue);

那么在第8行时,我们实际上执行了以下操作:

将 lvalue 和 rvalue 的值压栈进入Multiply函数,

取出栈中的值10 和 20,将其赋予 l 和 r

执行第2行代码,计算 l * r ,并将结果存在 y

将 y 的值压栈,然后从Multiply返回

第8行,从栈中取出返回值 200 ,并赋值给 l_times_r

以上5步就是执行本地调用的过程。

远程过程调用带来的新问题:

在远程调用时,我们需要执行的函数体是在远程的机器上的,也就是说,Multiply是在另一个进程中执行的。

这就带来了几个新问题:

    1、Call ID映射。我们怎么告诉远程机器我们要调用Multiply,而不是Add或者FooBar呢?

在本地调用中,函数体是直接通过函数指针来指定的,我们调用Multiply,编译器就自动帮我们调用它相应的函数指针。

但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。所以,在RPC中,所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。

客户端在做远程过程调用时,必须附上这个ID。然后我们还需要在客户端和服务端分别维护一个 {函数 <–> Call ID} 的对应表。

两者的表不一定需要完全相同,但相同的函数对应的Call ID必须相同。当客户端需要进行远程调用时,它就查一下这个表,找出相应的Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。

    2、序列化和反序列化。客户端怎么把参数值传给远程的函数呢?

在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。

甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端用Java或者Python)。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。

这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。

    3、网络传输。远程调用往往用在网络上,客户端和服务端是通过网络连接的。

所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。

只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。

尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2。Java的Netty也属于这层的东西。

所以,要实现一个RPC框架,其实只需要把以上三点实现了就基本完成了。

Call ID 映射可以直接使用函数字符串,也可以使用整数ID。映射表一般就是一个哈希表。

序列化反序列化可以自己写,也可以使用Protobuf或者FlatBuffers之类的。

网络传输库可以自己写socket,或者用asio,ZeroMQ,Netty之类。

blob.png



安装Hadhoop 时 DataNode 只有一个

描述一下我遇到的问题

 

搭建了全分布模式 start-all 通过jps 检查线程没有问题

但是 hdfs dfsdmin -report 发现只有一台datanode

blob.png

网页上看也只有一台

 blob.png

按照网上说的 删除了 tmplogs 重新格式化启动 还是一样的错误


tail -100 hadoop-root-namenode-localhost.Hadoop01.log  

查看启动日志 出现一个奇怪的IP

 blob.png

进查找发现是 第三台虚拟机的一个IP.可是第三台虚拟机的IP 已经设置为192.168.169.13了的,

 blob.png

并且已经重启了网络,重启了系统,可是通过ipconfig 查看依然是 129

 blob.png

现在两个地址都能ping

 blob.png

问题解决:

还是IP设置的问题:自动生成了DHCP 修改为Static

blob.png

解决:

blob.png

blob.png

倒排索引

倒排索引基本概念

       文档(Document):一般搜索引擎的处理对象是互联网网页,而文档这个概念要更宽泛些,代表以文本形式存在的存储对象,相比网页来说,涵盖更多种形式,比如Word,PDF,html,XML等不同格式的文件都可以称之为文档。再比如一封邮件,一条短信,一条微博也可以称之为文档。在本书后续内容,很多情况下会使用文档来表征文本信息。

       文档集合(Document Collection):由若干文档构成的集合称之为文档集合。比如海量的互联网网页或者说大量的电子邮件都是文档集合的具体例子。

       文档编号(Document ID):在搜索引擎内部,会将文档集合内每个文档赋予一个唯一的内部编号,以此编号来作为这个文档的唯一标识,这样方便内部处理,每个文档的内部编号即称之为“文档编号”,后文有时会用DocID来便捷地代表文档编号。

       单词编号(Word ID):与文档编号类似,搜索引擎内部以唯一的编号来表征某个单词,单词编号可以作为某个单词的唯一表征。

       倒排索引(Inverted Index)倒排索引是实现“单词-文档矩阵”的一种具体存储形式,通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”

       单词词典(Lexicon):搜索引擎的通常索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。

      倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。

      倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即被称之为倒排文件,倒排文件是存储倒排索引的物理文件。

     


                                         

 

                                                                              倒排索引基本概念示意图

blob.png

桥接、NAT以及仅主机模式的详细介绍和区别

当我们安装VMware时,VMware会自动为3种网络连接模式各自创建1个虚拟机网络:VMnet0(桥接模式)、VMnet8(NAT模式)、VMnet1(仅主机模式)。此外,我们也可以根据需要自行创建更多的虚拟网络。

blob.png

VMware 桥接模式 Bridged Networking

VMware桥接模式,也就是将虚拟机的虚拟网络适配器与主机的物理网络适配器进行交接,虚拟机中的虚拟网络适配器可通过主机中的物理网络适配器直接访问到外部网络(例如图中所示的局域网和Internet,下同)。简而言之,这就好像在上图所示的局域网中添加了一台新的、独立的计算机一样。因此,虚拟机也会占用局域网中的一个IP地址,并且可以和其他终端进行相互访问。桥接模式网络连接支持有线和无线主机网络适配器。如果你想把虚拟机当做一台完全独立的计算机看待,并且允许它和其他终端一样的进行网络通信,那么桥接模式通常是虚拟机访问网络的最简单途径。

VMware NAT模式

NAT,是Network Address Translation的缩写,意即网络地址转换。NAT模式也是VMware创建虚拟机的默认网络连接模式。使用NAT模式网络连接时,VMware会在主机上建立单独的专用网络,用以在主机和虚拟机之间相互通信。虚拟机向外部网络发送的请求数据"包裹",都会交由NAT网络适配器加上"特殊标记"并以主机的名义转发出去,外部网络返回的响应数据"包裹",也是先由主机接收,然后交由NAT网络适配器根据"特殊标记"进行识别并转发给对应的虚拟机,因此,虚拟机在外部网络中不必具有自己的IP地址。从外部网络来看,虚拟机和主机在共享一个IP地址,默认情况下,外部网络终端也无法访问到虚拟机。

此外,在一台主机上只允许有一个NAT模式的虚拟网络。因此,同一台主机上的多个采用NAT模式网络连接的虚拟机也是可以相互访问的。

前面我们已经提到,默认情况下,外部网络无法访问到虚拟机,不过我们也可以通过手动修改NAT设置实现端口转发功能,将外部网络发送到主机指定端口的数据转发到指定的虚拟机上。比如,我们在虚拟机的80端口上"建立"了一个站点,只要我们设置端口转发,将主机88端口上的数据转发给虚拟机的80端口,就可以让外部网络通过主机的88端口访问到虚拟机80端口上的站点。

VMware 仅主机模式 Host-Only

仅主机模式,是一种比NAT模式更加封闭的的网络连接模式,它将创建完全包含在主机中的专用网络。仅主机模式的虚拟网络适配器仅对主机可见,并在虚拟机和主机系统之间提供网络连接。相对于NAT模式而言,仅主机模式不具备NAT功能,因此在默认情况下,使用仅主机模式网络连接的虚拟机无法连接到Internet。

在同一台主机上可以创建多个仅主机模式的虚拟网络,如果多个虚拟机处于同一个仅主机模式网络中,那么它们之间是可以相互通信的;

形象的说:
桥接模式的虚拟机,就像一个在路由器"民政局"那里"上过户口"的成年人,有自己单独的居住地址,虽然和主机住在同一个大院里,但好歹是有户口的人,可以大摇大摆地直接和外面通信。

NAT模式的虚拟机,纯粹就是一个没上过户口的黑户,路由器"民政局"根本不知道有这么个人,自然也不会主动和它通信。
即使虚拟机偶尔要向外面发送点的信件,都得交给主机以主机的名义转发出去,主机还专门请了一位叫做NAT的老大爷来专门负责这些虚拟机的发信、收信事宜。

仅主机模式的虚拟机,纯粹是一个彻彻底底的黑奴,不仅没有户口、路由器"民政局"不知道这么号人,还被主机关在小黑屋里,连信件也不准往外发。

虚拟机连接方式.png

JDBC连接MySQL

加载及注册JDBC驱动程序

Class.forName(“com.mysql.jdbc.Driver”);
Class.forName(“com.mysql.jdbc.Driver”).newInstance();

JDBC URL 定义驱动程序与数据源之间的连接

标准语法:

<protocol(主要通讯协议)>:<subprotocol(次要通讯协议,即驱动程序名称)>:<data source identifier(数据源)>

MySQL的JDBC URL格式:

jdbc:mysql//[hostname][:port]/[dbname][?param1=value1][&param2=value2]….


示例:
   jdbc:mysql://localhost:3306/test?user=root&password=&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false

常见参数:

user                      用户名
password                  密码
autoReconnect             联机失败,是否重新联机(true/false)
maxReconnect              尝试重新联机次数
initialTimeout            尝试重新联机间隔
maxRows                   传回最大行数
useUnicode                是否使用Unicode字体编码(true/false)
characterEncoding         何种编码(GB2312/UTF-8/…)
failOverReadOnly          自动重连成功后,连接是否设置为只读(默认true)
relaxAutocommit           是否自动提交(true/false)
capitalizeTypeNames       数据定义的名称以大写表示


建立连接对象
String url=”jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password”;
Connection con = DriverManager.getConnection(url);


建立SQL陈述式对象(Statement Object)

Statement stmt = con.createStatement();


执行SQL语句

// 更新操作
executeUpdate()
String upd=”insert into test (id,name) values(1001,'Tom')”;
int con=stmt.executeUpdate(upd);
execute()

//查询操作
executeQuery()
String query = “select * from test”;
ResultSet rs=stmt.executeQuery(query);

结果集ResultSet

while(rs.next()){
    rs.getString(1);
    rs.getInt(2);
}


JNDI-数据源(Data Source)与连接池(Connection Pool)

设置web.xml

<?xml version=”1.0″ encoding=”ISO-8859-1″?>
<!–<?xml version=”1.0″ encoding=”GB2312″?>–>
 
<web-app 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/web-app_2_4.xsd”
version=”2.4″>
….
<servlet>
    <servlet-name>ServletConfigurator</servlet-name>
    <servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
    <init-param>
        <param-name>propertyFile</param-name>
        <param-value>WEB-INF/classes/Proxool.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

后端统计端口添加下列

<servlet>
    <servlet-name>Admin</servlet-name>
    <servlet-class>org.logicalcobwebs.proxool.admin.servlet.AdminServlet</servlet-class>
</servlet>
 
<servlet-mapping>
    <servlet-name>Admin</servlet-name>
    <url-pattern>/Admin</url-pattern>
</servlet-mapping>
….
</web-app>

 配置Proxool.properties

jdbc-0.proxool.alias=JSPBook
jdbc-0.proxool.driver-class=com.mysql.jdbc.Driver
jdbc-0.proxool.driver-url=jdbc:mysql://localhost:3306/sample_db?user=root&password=browser&useUnicode=true&characterEncoding=UTF-8
jdbc-0.proxool.maximum-connection-count=10
jdbc-0.proxool.prototype-count=4
jdbc-0.proxool.house-keeping-test-sql=select CURRENT_DATE
jdbc-0.proxool.verbose=true
jdbc-0.proxool.statistics=10s,1m,1d    
jdbc-0.proxool.statistics-log-level=DEBUG

使用Proxool连接池
Connection con = DriverManager.getConnection(“proxool.JSPBook”);
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
String query = “SELECT * FROM employee”;
ResultSet rs = stmt.executeQuery(query);

本文地址:

 http://www.52xjava.cn/2017/10/23/jdbc-mysql-jndi-pool/

mysql中case when then 的使用

case when then end  两种写法

1.

case  字段 when 值1 then 结果1

when 值2 then 结果2

else 结果3

end

eg.

case status when ‘1’ then “在会”

when ‘0’ then “未知”

when ‘-1’ then ‘离开’

end

这个sql 可将 status为1 的 记录查询出来

2.另外 case when (表达式1)then 结果

when (表达式1) then 结果

else 结果

end

SQL> select u.id,u.name,u.sex,
  2    (case u.sex
  3      when 1 then '男'
  4      when 2 then '女'
  5      else '空的'
  6      end
  7     )性别
  8  from users u;

java中使用MD5进行加密

在各种应用系统的开发中,经常需要存储用户信息,很多地方都要存储用户密码,而将用户密码直接存储在服务器上显然是不安全的,本文简要介绍工作中常用的 MD5加密算法,希望能抛砖引玉。

对字符串进行加密

/**利用MD5进行加密
     * @param str  待加密的字符串
     * @return  加密后的字符串
     * @throws NoSuchAlgorithmException  没有这种产生消息摘要的算法
     * @throws UnsupportedEncodingException
     */
    public String EncoderByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException{
        //确定计算方法
        MessageDigest md5=MessageDigest.getInstance(“MD5”);
        BASE64Encoder base64en = new BASE64Encoder();
        //加密后的字符串
        String newstr=base64en.encode(md5.digest(str.getBytes(“utf-8”)));
        return newstr;
    }
验证密码是否正确
对输入密码(消息报文)重新计算其摘要,和数据库中存储的摘要进行对比(即数据库中存储的其实为用户密码的摘要),若两个摘要相同,则说明密码正确,不同,则说明密码错误。

    /**判断用户密码是否正确
     * @param newpasswd  用户输入的密码
     * @param oldpasswd  数据库中存储的密码--用户密码的摘要
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public boolean checkpassword(String newpasswd,String oldpasswd) throws NoSuchAlgorithmException, UnsupportedEncodingException{
        if(EncoderByMd5(newpasswd).equals(oldpasswd))
            return true;
        else
            return false;
    }