20 2015

Java中的UUID唯一标识符

最近在公司写Kafka的消息中间件,发现需要一个消息的唯一标识,用到了uuid。确实是一个很好的解决消息唯一性的思路。

GUID是一个128位长的数字,一般用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID。从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义上)3240年不重复。

UUID是1.5中新增的一个类,在java.util下,用它可以产生一个号称全球唯一的ID

import java.util.UUID;
  public class Test {
    public static void main(String[] args) {
      UUID uuid = UUID.randomUUID();
      System.out.println (uuid);
  }
}

编译运行输出:
07ca3dec-b674-41d0-af9e-9c37583b08bb
UUID的Java参考文档请参照:

http://www.cuku.net/api/java/util/UUID.html

另外一个说明:
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成UUID的API。UUID按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同),时钟序列,全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得),UUID的唯一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最普遍的是微软的GUID(Globals Unique Identifiers)。

18 2015

GC日志的查看格式

可以通过在java命令种加入参数来指定对应的gc类型,打印gc日志信息并输出至文件等策略。

GC的日志是以替换的方式(>)写入的,而不是追加(>>),如果下次写入到同一个文件中的话,以前的GC内容会被清空。

对应的参数列表

-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径

这里使用如下的参数来进行日志的打印:

-XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:./gclogs

对于新生代回收的一行日志,其基本内容如下:
2014-07-18T16:02:17.606+0800: 611.633: [GC 611.633: [DefNew: 843458K->2K(948864K), 0.0059180 secs] 2186589K->1343132K(3057292K), 0.0059490 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

其含义大概如下:
2014-07-18T16:02:17.606+0800(当前时间戳): 611.633(时间戳): [GC(表示Young GC) 611.633: [DefNew(单线程Serial年轻代GC): 843458K(年轻代垃圾回收前的大小)->2K(年轻代回收后的大小)(948864K(年轻代总大小)), 0.0059180 secs(本次回收的时间)] 2186589K(整个堆回收前的大小)->1343132K(整个堆回收后的大小)(3057292K(堆总大小)), 0.0059490 secs(回收时间)] [Times: user=0.00(用户耗时) sys=0.00(系统耗时), real=0.00 secs(实际耗时)]
老年代回收的日志如下:

2014-07-18T16:19:16.794+0800: 1630.821: [GC 1630.821: [DefNew: 1005567K->111679K(1005568K), 0.9152360 secs]1631.736: [Tenured:
2573912K->1340650K(2574068K), 1.8511050 secs] 3122548K->1340650K(3579636K), [Perm : 17882K->17882K(21248K)], 2.7854350 secs] [Times: user=2.57 sys=0.22, real=2.79 secs]

gc日志中的最后貌似是系统运行完成前的快照:

Heap
def new generation total 1005568K, used 111158K [0x00000006fae00000, 0x000000073f110000, 0×0000000750350000)
eden space 893888K, 12% used [0x00000006fae00000, 0x0000000701710e90, 0x00000007316f0000)
from space 111680K, 3% used [0×0000000738400000, 0x000000073877c9b0, 0x000000073f110000)
to space 111680K, 0% used [0x00000007316f0000, 0x00000007316f0000, 0×0000000738400000)
tenured generation total 2234420K, used 1347671K [0×0000000750350000, 0x00000007d895d000, 0x00000007fae00000)
the space 2234420K, 60% used [0×0000000750350000, 0x00000007a2765cb8, 0x00000007a2765e00, 0x00000007d895d000)
compacting perm gen total 21248K, used 17994K [0x00000007fae00000, 0x00000007fc2c0000, 0×0000000800000000)
the space 21248K, 84% used [0x00000007fae00000, 0x00000007fbf92a50, 0x00000007fbf92c00, 0x00000007fc2c0000)
No shared spaces configured.

13 2015

笔试题目(一)

河边 N 块石头排成一排,一只青蛙蹲在第 1 块石头上,打算跳到第 N 块石头上。
青蛙每次可以向前跳过一块石头或者两块石头,即从第 K 块石头可以跳到第 K+1
或者 K+2 块石头。问青蛙从第 1 块石头跳到第 N 块石头,共有多少种不同的方式?
请编程实现,已知 1 <= N <= 40。


public class FrogJump {

/**

*             10

*             / \

*            9   8

*           /\   /\

*          8  7  7 6

* 可以通过倒退的方式来计算青蛙跳的不同方式。假设青蛙站在N=10的石头上,那么有可能从N=9跳过来,也有可能从N=8跳过来。

* 要知道N=10的不同跳的方式,其实就是(N=9的不同的方式)+(N=8的不同方式)。依次类推

* @param n 有N块石头

*/

  public int jumpToN(int n) {

    if (n == 0 || n == 1) {

      return 1;

    } else if (n > 1) {

      return jumpToN(n - 1) + jumpToN(n - 2);

    }

      return -1;

  }

public static void main(String[] args) {

  int n = 40;

  FrogJump frogJump = new FrogJump();

  int result = frogJump.jumpToN(n);

  System.out.println("result : " + result);

}

}

已知一个结构体包含两个字段:一个整形值,一个指向下一结构体的指针。多个此结
构体可以连接成一个列表,以空指针结束。给定指向该列表第一个元素的指针,
写一程序返回一个列表,此列表中元素值的序列与原列表中值的序列顺序相反。原
列表可以被破坏。如果编程语言不支持结构体或指针,可以使用类或引用代替。

一个棋盘有 8*8=64 个格子,编号 (1,1), (1,2), …… (8,8)。 “马”按照“日”字走法
从指定起点跳到指定终点,写一程序求最短路径需要几步。例如,从(1,1)点跳到
(4,4)点至少要两步,一种方式为 (1,1) -> (2,3) -> (4,4)。程序接受4个 1-8 之间整
数,表示起点和终点位置,计算出最短路径需要几步。

13 2015

秒杀系统设计

商城秒杀系统设计

一.目标

a)     解决超卖的问题

b)     解决页面白板的问题

c)     秒杀用户体验(公平性)与后端性能的平衡

二.秒杀场景

a)     总体请求数:1亿+ request/second

b)     应用服务器请求数:5w+ request/second

c)     商品库存数:10w

三.考虑要点

  1. 用户在秒杀时候的不断刷新,网络带宽问题

假设一个页面要100K的数据,那么5w的请求数,带宽消耗=100K*50000=5G

  1. 高并发的全站雪崩
  2. 秒杀开始的时间同步问题
  3. 防刷机制,过滤频繁的点击
  4. 高并发下的超卖问题

四.技术方案
Read More >>

11 2015

Zookeeper介绍以及原理

更精辟的文章:http://cailin.iteye.com/blog/2014486
  
在Zookeeper的官 网上有这么一句话:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
  这大概描述了Zookeeper主要可以干哪些事情:配置管理,名字服务,提供分布式同步以及集群管理。那这些服务又到底是什么呢?我们为什么需要这样的服务?我们又为什么要使用Zookeeper来实现呢,使用Zookeeper有什么优势?接下来我会挨个介绍这些到底是什么,以及有哪些开源系统中使用了。
  配置管理
  在我们的应用中除了代码外,还有一些就是各种配置。比如数据库连接等。一般我们都是使用配置文件的方式,在代码中引入这些配置文件。但是当我们只有一种配置,只有一台服务器,并且不经常修改的时候,使用配置文件是一个很好的做法,但是如果我们配置非常多,有很多服务器都需要这个配置,而且还可能是动态的话使用配置文件就不是个好主意了。这个时候往往需要寻找一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。比如我们可以把配置放在数据库里,然后所有需要配置的服务都去这个数据库读取配置。但是,因为很多服务的正常运行都非常依赖这个配置,所以需要这个集中提供配置服务的服务具备很高的可靠性。一般我们可以用一个集群来提供这个配置服务,但是用集群提升可靠性,那如何保证配置在集群中的一致性呢? 这个时候就需要使用一种实现了一致性协议的服务了。Zookeeper就是这种服务,它使用Zab这种一致性协议来提供一致性。现在有很多开源项目使用Zookeeper来维护配置,比如在HBase中,客户端就是连接一个Zookeeper,获得必要的HBase集群的配置信息,然后才可以进一步操作。还有在开源的消息队列Kafka中,也使用Zookeeper来维护broker的信息。在Alibaba开源的SOA框架Dubbo中也广泛的使用Zookeeper管理一些配置来实现服务治理。
Read More >>

15 2014

Java的ClassLoader类加载机制

之前对于Java的类加载流程不是了解的很透彻,今天好好静下心来,研究了一番,发现并没有想象中的那么神秘。要理解java的类加载,其实就是要理解JVM中三个默认classloader的职责。
Bootstrap ClassLoader:负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
Extension ClassLoader:负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class
App ClassLoader:负责加载当前java应用的classpath中的所有类。

其中Bootstrap ClassLoader是JVM级别的,由C++撰写;Extension ClassLoader、App ClassLoader都是java类,都继承自URLClassLoader超类。
Bootstrap ClassLoader由JVM启动,然后初始化sun.misc.Launcher,sun.misc.Launcher初始化Extension ClassLoader、App ClassLoader。
Read More >>

14 2014

LRU(近期最少使用)算法

LRU原理

LRU(Least recently used,最近最少使用)被广泛使用于缓存机制中,算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。在缓存机制中,系统将常用的数据保存在本地内存,用以提高系统的响应,但是内存毕竟是有限的,系统不可能把所有的数据都缓存下来,在有限的内存中,当内存满的时候,系统需要淘汰一些数据,而进行淘汰的规则,则是使用LRU算法。

LRU实现

LRU通过链表的方式来实现,最近使用过的元素,会被放到链表的头,而不常使用的元素,会被放到链表的尾部,如果链表满的时候,有新的元素要进来,那么就直接删除链表尾部的元素。
1.一个数据进入到列表,直接添加到链表的头部。
2.如果该元素已经存在链表中,那么将该元素移动到链表的头部。
3.当链表满的时候,有新的元素进来,这将链表的底部元素删除。
Read More >>