学而后知不足


  • 首页

  • 分类

  • 归档

  • 标签

idea内嵌git不可用问题解决

发表于 2016-06-12   |   分类于 工具

idea中使用内嵌git插件

以前使用idea中的git比较习惯,也用过source tree、小乌龟一些插件。麻烦,总要切出去切回来。也不想用命令行,对比代码revert不方便。

个人比较懒就喜欢用idea自带的git插件和一些简单git的命令。

重装系统之后,导入项目。idea中的git插件确一直不可用
git插件不可用的状态没有git:master的显示

idea git 提示

解决方式:正确的导入maven项目
1.选择一个项目目录,mkdir directory
2.cd directory, git init初始化一个git目录
3.在这个git目录拉取maven项目
4.重启idea
5.idea会提示unregistered vcs roots detected如图,选择add roots

idea git 提示

Linux File System 总体介绍

发表于 2016-05-18   |   分类于 Linux

On a UNIX system, everything is a file; if something is not a file, it is a process

文件的种类

  • 目录文件:存放其他文件的文件
  • 特殊文件:用于输入输出机制,大多数特殊文件在/dev中
  • 链接:系统使文件在系统的file tree中多处可见
  • socket:特殊的文件类型,类似tcp socket,通过文件系统提供受保护的进程网络连接
  • 命名管道:不使用网络套接字,进程间通信的一种方式

分区

在发生故障的情况下,硬盘被分区可以保证更高的数据安全性。通过硬盘分区,可以将数据分组分离,当事故发生,只有被命中分区的数据被破坏,其他分区的数据被保存下来。

例如:若整个系统为一个分区,应用程序将分区硬盘写满了,程序不能再执行,整个系统也将停止。如果数据单独存放到一个数据分区上,受影响的也仅仅是数据分区,系统分区和其他的分区正常使用。

两种分区:

1. 数据分区,包含启动和执行系统的根目录
2. swap分区,扩展计算的物理内存,在硬盘上的额外内存    
数据分区

大多数系统包含一个根分区,一个或者多个数据分区,一个或多个交换分区。并且Linux系统使用fdisk安装分区的类型。

标准根分区约为100-500 MB,包含系统的配置文件,基本的命令,服务器程序,系统函数库,一些临时空间和管理用户的主目录

内核是一个单独的分区,他是系统中最重要的文件。你可以查看你的/boot,用来保持内核执行,和相应的文件。

硬盘的其他数据都被分在数据分区,尽管有可能仅仅是一个分区。在Linux中

  • /usr 用户的程序
  • /home 用户的个人数据
  • /var 存储临时文件和mail队列
  • /opt 三方或者额外的软件
swap分区

交换空间仅适用于系统本身的访问,隐藏于正常的操作,是工作的保障。在Linux上从未看过内存已满的异常也是因为swap的功劳。
一般Linux在使用swap的时候,硬盘和内存的转换约为2:1。就是说一个系统如果需要512M的swap内存空间。三种方案:

  • 一个1g的分区
  • 2个512m的分区
  • 2个硬盘分别512m的分区

最后的适用于大量的io操作。

一旦分区创建成功,你只能添加。虽然可以改变已有的分区大小,但是并不建议
服务器上的往往是用户数据和系统数据分离。提供服务的程序和程序的数据分离

挂载点

所有的分区通过一个挂载点连接到系统,挂载点定义在文件系统中的特定数据组的位置,通常所有的分区通过根分区链接,在这个分区中用/根目录表示。这些空目录件是链接到这些分区的起始点

例如我们想将分区链接到/opt/media的目录中,首先系统中/opt/media这个目录是必须存在的,并且最好是一个空的目录,然后使用mount命令,将分区添加到系统中。当你看以前的空的目录/opt/media,会包含一些媒介的文件或者目录。

在一个正在运行的系统中

  • df -h 命令显示有关分区和挂载点的信息
  • df -h . 查看自己当前目录所在的分区

文件系统

每个分区都有自己的文件系统,通过想象这些文件系统连接在一起,可以看到整个系统的树状结构。
在一个文件系统中,一个文件由一个inode表示,一个包含文件具体信息的序列号,文件所属,文件在硬盘上的位置

每个分区都有自己的索引节点,在同一个系统中的多个文件系统中,可以有相同的inode编号
每个inode都是描述文件在硬盘上的数据结构,存储文件的属性。包含文件数据的物理位置,当一个硬盘初始化并接受数据存储,通常在初始化安装系统或者添加额外的硬盘时候,指定固定的inode的数量,这个值限制文件的最大数量,一个inode大约2到8kb存储

在创建一个文件时候,得到一个新的空闲的inode。包含信息

  • 文件的所有者
  • 文件类型
  • 权限
  • 创建、修改日期
  • 连接号 连接到这个文件的
  • 文件大小
  • 定位到硬盘的实际物理地址

有些数据不包含在inode中,文件名和目录。这些都被存储在目录文件中,通过对比文件名和inode编号,可以构成一个树状系统。用户可以使用ls -i显示inode编号,inode在硬盘上有独立的空间存储。

参考文献:

http://www.tldp.org/LDP/intro-linux/html/sect_03_01.html

guarded suspension-要等我准备好呦

发表于 2016-05-14   |   分类于 设计模式

多线程 guarded suspension 模式

在java多线程设计模式这本书中,提及到guarded suspension模式。
这种模式应用在”你要等我准备好哦”这种情况下。表明某个状态正在被保护着,不能对其进行操作。

举个🌰,模拟一个http服务器的交互
1.客户端提交request到服务端
2.服务端接受到request放在requestQueue中
3.服务端处理request

首先需要一个RequestQueue,用来存放request对象,这个队列同时被客户端和服务端操作。

public class RequestQueue {

    private final LinkedList<Request> queue = new LinkedList();

    public synchronized Request getRequest(){
        while(queue.size() <= 0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return queue.removeFirst();
    }

    public synchronized void putRequest(Request request){
        queue.addLast(request);
        notifyAll();
    }

}

然后是ClientThread向队列中put request对象

public class ClientThread extends Thread {

    private Random random;
    private final RequestQueue requestQueue;

    public ClientThread(RequestQueue requestQueue, String name, long seed) {
        super(name);
        this.random = new Random(seed);
        this.requestQueue = requestQueue;
    }

    @Override
    public void run() {

        int requestNum = 10000;
        for (int i = 0; i < requestNum; i++){
            Request request = new Request("No." + i);
            log(Thread.currentThread().getName() + " requests " + request);
            requestQueue.putRequest(request);
            doSomeThing();
        }

    }

    private void log(String log){
        System.out.println(log);
    }

    private void doSomeThing(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ServerThread从队列中拿出request进行处理

public class ServerThread extends Thread {

    private Random random;
    private final RequestQueue requestQueue;

    public ServerThread(RequestQueue requestQueue, String name, long seed) {
        super(name);
        this.random = new Random(seed);
        this.requestQueue = requestQueue;
    }

    @Override
    public void run() {

        int requestNum = 10000;
        for (int i = 0; i < requestNum; i++) {
            Request request = requestQueue.getRequest();
            log(Thread.currentThread().getName() + " handles " + request);
            doSomeThing();
        }

    }

    private void log(String log) {
        System.out.println(log);
    }

    private void doSomeThing() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Main 中执行完整的运行

public class Main {

    public static void main(String[] args){
        RequestQueue requestQueue = new RequestQueue();
        new ClientThread(requestQueue,"client thread",2323423l).start();
        new ServerThread(requestQueue,"server thread-demo1",9292822l).start();
        new ServerThread(requestQueue,"server thread-2",9292822l).start();
        new ServerThread(requestQueue,"server thread-3",9292822l).start();
        new ServerThread(requestQueue,"server thread-4",9292822l).start();
        new ServerThread(requestQueue,"server thread-5",9292822l).start();
    }
}

RequestQueue里封装了一个LinkedList对象放出request对象
getRequest方法,从queue队列头中获取一个request对象,getRequest方法必须在queue中值时才能正确的执行。

一般这种情况都是有两种模式轮训和事件通知,很明显的是基于轮训的方式无端的消耗着cpu时间片。所以使用后者,guarded suspension 模式也是基于此实现的。

目的:从queue中取得request
条件:queue.size() > 0

使用synchronized,wait方式,首先若是不满足条件进入while,调用wait(),线程进入到对象的wait set中,等待其他线程对该对象notify

其他线程在什么情况下对其notify呢?看条件变量queue.size() > 0,queue的size大于0时,也就是putRequest的时候

现在已经知道什么时候去wait,什么时候去notify了。
可为什么要加synchronized同步呢,这个以后再进行详细说明。简单介绍下
在线程调用object.wait方法时候,会把当前线程防到wait set中(虚拟概念),并且该线程进入Wait状态
在线程调用object.notify方法的时候,会把object的 wait set中的一个线程唤醒
多个线程同时处理共享的变量域,所以需要加synchronized同步。

现在回来,当其他线程调用putRequest的时候,并且同步锁释放了,之前调用因getRequest方法处于Wait状态的线程获取到了锁,进行后续操作,好有问题!!!看代码

public synchronized Request getRequest(){
    while(queue.size() <= 0){
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return queue.removeFirst();
}

synchronized方法并没有执行完,不应该释放同步锁,为什么putRequest方法可以获取到锁,并且执行?

stackoverflow原文

“Invoking wait inside a synchronized method is a simple way to acquire the intrinsic lock”

This sentence is false, it is an error in documentation.

Thread acquires the intrinsic lock when it enters a synchronized method. Thread inside the synchronized method is set as the owner of the lock and is in RUNNABLE state. Any thread that attempts to enter the locked method becomes BLOCKED.

When thread calls wait it releases the current object lock (it keeps all locks from other objects) and than goes to WAITING state.

When some other thread calls notify or notifyAll on that same object the first thread changes state from WAITING to BLOCKED, Notified thread does NOT automatically reacquire the lock or become RUNNABLE, in fact it must fight for the lock with all other blocked threads.

WAITING and BLOCKED states both prevent thread from running, but they are very different.

WAITING threads must be explicitly transformed to BLOCKED threads by a notify from some other thread.

WAITING never goes directly to RUNNABLE.

When RUNNABLE thread releases the lock (by leaving monitor or by waiting) one of BLOCKED threads automatically takes its place.

So to summarize, thread acquires the lock when it enters synchronized method or when it reenters the synchronized method after the wait.

public synchronized guardedJoy() {
    // must get lock before entering here
    while(!joy) {
        try {
            wait(); // releases lock here
            // must regain the lock to reentering here
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

也就是说进入guardedJoy方法时候需要获取到锁
当调用wait方法时会释放锁,线程从Runnable进入Wait
当其他线程调用notify
线程从Wait进入Blocking
这时候需要获取到同步锁才能进行Runnable
明白了

整理一下guarded suspension 模式

1.一个具有状态的对象,这个对象被多个线程同时访问,这个对象只有在自己的状态合适的时候,才会让线程进行某些处理
2.使用警戒条件来表示对象的状态,并且在目的处理前,测试满足条件不满足则进行wait,满足则执行处理方法
3.用notify方法通知wait中的线程表明状态已经满足了,wait中的线程进入blocking获得锁后执行操作

参考代码:

https://github.com/whx4J8/Thread.git

参考文献:

java多线程设计模式第3章
stackoverflow : http://stackoverflow.com/questions/13249835/java-does-wait-release-lock-from-synchronized-block

Hadoop安装-准备环境

发表于 2016-04-16   |   分类于 分布式计算

环境准备

首先准备3台机器,这里和小伙伴一起买了三台阿里云

1.为每台机器添加hadoop用户组,添加hadoop用户

sudo groupadd hadoop
sudo useradd -g hadoop
sudo passwd hadoop    

2.为每台机器的hadoop配置sshkey

sudo su - hadoop
cd ~/.ssh                没有.ssh目录使用ssh-keygen生成
将三台机器的id_rsa.pub公钥拷贝到.ssh/authorized_keys中,若没有该文件touch一个
务必保证每台机器使用hadoop用户可以ssh登录到其他机器上
这里使用注意的是authorized_keys文件必须要有600权限

3.配置host

***.***.***.*** master
***.***.***.*** slave01
***.***.***.*** slave02

3.下载hadoop,配置环境变量

cd ~
wget https://archive.apache.org/dist/hadoop/core/hadoop-2.2.0/hadoop-2.2.0.tar.gz

解压后设置环境变量

vim ~/.bashrc

export HADOOP_PREFIX=$HOME/local/opt/hadoop-2.2.0
export HADOOP_COMMON_HOME=$HADOOP_PREFIX
export HADOOP_HDFS_HOME=$HADOOP_PREFIX
export HADOOP_MAPRED_HOME=$HADOOP_PREFIX
export HADOOP_YARN_HOME=$HADOOP_PREFIX
export HADOOP_CONF_DIR=$HADOOP_PREFIX/etc/hadoop
export PATH=$PATH:$HADOOP_PREFIX/bin:$HADOOP_PREFIX/sbin

srouce ~/.bash_profile 使之生效,这样的好处不会污染其他用户的环境变量

echo $HADOOP_PREFIX
/home/hadoop/local/opt/hadoop-2.2.0

effective java 读书笔记-类和接口

发表于 2016-04-09   |   分类于 设计模式

复用建议:组合优先于继承

如果需要复用代码,我们第一想到的是使用继承,但是这只是面向对象的一种复用方式- 白盒复用。
子类继承父类,父类中的实现对子类可见,若是父类的实现代码改变,子类代码也就需要跟着改变。
另一种方式,对象组合-黑盒复用。对象组合要求对象之间定义好接口,相互之间使用接口引用。对象内部的实现是不可见的。

因为继承父类,父类的实现对子类完全可见甚至可改变,所以父类中的任何一个改变都有可能影响到子类,这样破坏了封装性,代码非常脆弱。
而使用组合的方式,对象之间使用接口的方式进行调用。接口的实现改变了,但并不影响接口外部的调用,这样的代码非常稳固,复用性高

使用原则:大多数情况下,组合方式优先于继承,使用这种方式的代码更容易维护。

dapper 的学习

发表于 2016-04-09   |   分类于 分布式服务

dapper 大规模分布式跟踪系统

在分布式系统中,一次请求会跨越几个甚至几十个子系统所以我们很难统计出每个系统之间的耗时问题。
所以google研发出dapper , twitter基于dapper研发出zipkin这些大规模耗时跟踪系统。

dapper 如何跟踪系统之间的耗时

下图是分布式系统中最普遍的一次请求,用户发起request请求,A服务接收到,使用rpc调用下面的子系统。
最终形成一颗调用树,所以在需要每次做rpc调用的时候进行时间统计。

dapper的trace

trace

一次完整的跟踪叫做trace,从请求到服务器开始,服务器返回response结束,跟踪每次rpc调用的耗时。

span

在调用rpc服务中,产生一个span用来记录rpc接受到请求的时间,rpc返回请求的时间。
span中下面几个参数

trace_id             用于标示一次完整的请求id
span_id            当前这次调用span_id
parent_id            上层服务的调用span_id        最上层服务parent_id为null
annotations        用于标记的时间戳

span在rpc收到请求创建,在rpc返回响应被收集并且销毁。
在rpc调用时,调用方需要将自己的span信息封装到请求中,被调用方接受到请求,从请求中拿到调用方的trace_id,span_id,生成自己的span。

这样我们可以从收集的时间日志中找到所有的属于trace_id的所有span数据,根据span的parent_id和span_id就能找到自己的完整的调用链,再根据调用链中的annotaion计算出每个服务所消耗的时间

annotation

时间注释,一个span中会有多个annotation注释描述。
在dapper的论文中没有详细描述所需的annotation,在zipkin中,对其的描述是这样。
一次rpc调用中会有四个核心的annotation

server receive      服务器接收         sc
client send         客户端发送        cs
client receive      客户端接收        cr
server send         服务器发送        ss

这个四个核心的annotation记录下一次调用耗时

      调用方rpc                    被调用方rpc

    ------->
                              --------->

                              <---------
    <-------          

      sc                                


      cs                                sr


      cr                                ss


      ss    

调用方会产生这个四个时间戳,而被调用方因其没有调用其他rpc,所以只有sr和ss两个事件
span会在sc这个事件中被创建,并且存活于threadlocal中,在ss这个事件中被收集并且被销毁于threadlocal中        

dapper 收集记录

如下图,dapper纪录日志放入log file中,dapper collectors 定时请求拉取日志,存放到bigtable中。
尽管这样,日志量还是太大,dapper采用采样率的方式进行日志采集,采样率为1/1024。
zipkin 对收集工作做的更为复杂,实现了scribe方式,解决任何节点的故障的数据丢失问题。

dapper的trace

关于rpc的改造

dapper没有开源,zipkin的对其dapper的设计进行实现。并且提供java的客户端brave
我们的线上服务使用的dubbo框架,所以只要将brave的api嵌入的dubbo中,并且在rpc调用中将当前的span信息加入到请求中。就可以完成对请求时间日志收集的工作,后端工作zipkin已经实现。

后记

paper传送门:

原文地址:http://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/36356.pdf
译文地址:http://bigbully.github.io/Dapper-translation/

todo:

阅读dubbo源码
阅读brave的demo源码
改造dubbo

effective java读书笔记-静态工厂

发表于 2016-04-05   |   分类于 设计模式

静态工厂方法 static method factory

静态工厂是effective java 中的第一节,本文大部分内容是从上面摘抄的。
static method factory 译为静态工厂模式,指的是类可以提供一个静态工厂方法,一个返回类的实例的静态方法。

public static final Boolean FALSE = new Boolean(false);
public static final Boolean TRUE = new Boolean(true);

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

public Boolean(boolean value) {
    this.value = value;
}

在java8 中前者为静态工厂创建对象,后者为构造方法创建对象。

那么静态工厂的优点

1.静态方法在方法命名上更具备可读性
用构造方法创建对象的时候,传递不同的参数生成不同类型的对象。但是这并不明显,若不去查看api我们不能看出构造方法创建的到底是哪些对象。而且静态工厂可以在方法上作特征对象的命名。

2.静态方法参数的灵活性
与上述的优点相同,使用构造方法创建不同类型对象时会出现这样一个问题。当我们需要相同类型的参数,创建不同类型的对象,这个问题导致构造方法极其难使用。如果使用静态方法我们完全可以在方法名上进行区别。

3.不必每次调用的时候都创建新的对象
我们可以利用静态工厂对常用的单例的对象进行缓存。
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
每次调用valueOf()的时候并不会创建新的对象而是使用缓存中的Boolean对象,这样对类的实例有很好的控制权利,但是要保证类是线程安全的并且单例的。

4.静态工厂可以返回类的任何子类型
这优点实在是太好啦,api可以返回对象,却不需要将类的构造公有化(公有化子类就可以),以这种隐藏构造子类代码的方式可以使api对外部变的非常简洁,特别适用于服务提供者框架中进行解耦。

private static final Map<String,Provider> providers = new ConcurrentHashMap<String, Provider>();
static{
    providers.put("xml",new XMLProvider());
    providers.put("json",new JSONProvider());
}

private ServiceFactory(){

}

public Service getService(String type){
    return providers.get(type).newService();
}    

在这个factory中,外界只需要给出需要的类型,完全不需要关注其service是如何创建的,创建何种类型,甚至可以做到外部只关注获得Service接口,外部面向接口进行编程。从而降低代码的耦合程度。

python 无法安装setuptools问题

发表于 2016-04-02

Python无法安装setuptools解决

centos6.5阿里云的新机器,更新python版本之后安装setuptools,但是一直报错。说不能解析https,也ping不同这个其中报错的https网址

查看之后发现是python内置的urllib模块不支持https协议,是因为安装python之前没有编译openssl 这样的ssl库,以至于python不支持ssl

yum install openssl-devel

找到python的安装目录,重新编译python

./configure
make
make install

测试

import urllib
urllib.urlopen('https://www.baidu.com')

tcp详解学习笔记-tcp建立终止

发表于 2016-03-27   |   分类于 网络

tcp建立连接三次握手

初始化连接、数据传输、关闭连接

tcp建立连接三次握手

一般是由一端打开一个套接字,然后监听另一方的连接,服务器被动打开,客户端主动打开链接

1.客户端发送一个syn来创建主动打开,指明客户端需要创建链接的端口和时间序列号报文包
2.服务端回复客户端初始的syn和ack,ack为客户端传说的syn包的时间序列号+1
3.最后客户端再发一个ack包,服务端接收到链接进入已创建状态

tcp关闭连接四次握手

tcp连接全双工的
1.由任意一端发送fin请求关闭连接,另一方受到后执行被动关闭,然后返回一个fin的ack
2.再由被动关闭的一端发送fin请求,另一端收到返回给一个fin的ack
3.连接进入关闭状态

tcp的半关闭

也是因为tcp时全双工的,当一方请求关闭,接收到关闭的一方还能send数据,完成后进行关闭。

tcp状态变迁图

全组合形式

呼入请求队列(链接请求从内核到应用层的过程)

tcp如何处理呼入的链接请求?
1.正在等待连接请求的一端又一个固定的链接队列,队列中的链接已经被tcp接受,但还没有被应用层接收
2.应用层指明队列的长度,叫做积压值(0-5之间)
3.syn到达时,tcp使用一个算法,根据俄当前链接队列中的连接数来确定是否接受这个链接。积压值说明的是tcp监听的端点已被tcp接受而等待应用层接受的最大链接数
4.对于一个新的链接请求,改tcp监听的端点的链接队列中还有空间,tcp将对syn进行确认并完成链接的建立,但是应用层只有在三次握手中的第三个报文段收到后才会知道这个新链接
5.如果对于新的链接请求,链接队列中已经没有空间,tcp将不会理会收到的syn,也不会发任何报文段。如果饮用层不能及时接受已经被tcp接受的链接,这些链接可能占满整个队列,客户端的主动打开最终将超时

tcp链接端口的理解

发表于 2016-03-16   |   分类于 网络

tcp端口

tcp中的端口,一直以为tcp的端口是个类似的实体的东西,类似于存储数据的数据包的队列那种,所以一个server的端口只能处理一个链接,当server开启listen端口后,服务器接受到链接请求,server会分配一个随机的没有用到的端口给tcp链接。但是这是错的!

tcp使用本地地址和远端地址组成的4元组(目的ip,目的端口,本地ip,本地端口)来处理多个链接请求。这个端口在tcp内核中紧紧是协议中的一部分,内核中仅仅通过目的端口无法确定是哪个进程接受到请求,所以需要server端的端口才能确定和客户端的链接。另外只有处于listen状态中的进程才能接受新的链接请求。当有新的请求到达并且被接受,内核会创建一个处于estableished状态的链接,而且端口号不变与listen的端口号一致。

看个🌰

我们起一个服务,我这里起了个hexo服务,使用4000端口号
使用命令,查看链接

netstat -a -n -f inet

可以看到一个处于listen的4000端口,显示本地地址*.4000, 远端地址是*.*,这表明可以接受任意主机的请求

tcp4       0      0  *.4000                 *.*                    LISTEN     

这时候我们telnet 4000这个端口,可以看到有两个新的请求,一个本地端口4000 远端端口60352,这是server上的链接,另一个本地60352,远端4000,这是telnet客户端的链接

tcp4       0      0  *.4000                 *.*                    LISTEN     
tcp4       0      0  127.0.0.1.4000         127.0.0.1.60352        ESTABLISHED
tcp4       0      0  127.0.0.1.60352        127.0.0.1.4000         ESTABLISHED

关于tcp的更多知识,tcp详解 18章,unix网络编程,都有对tcp进行详细的描述。

123
whx4J8

whx4J8

github.com/whx4J8

24 日志
12 分类
14 标签
© 2017 whx4J8
由 Hexo 强力驱动
主题 - NexT.Mist