Posted in Working Skills, 技术

Java Web 工作技巧总结 16.8

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

四时不谢之兰,百节长青之竹,万古不败之石,千秋不变之人。


1. AOP – LOG
项目中,一个请求过来,一个响应回去。

1_thumb3.png

经常我们需要记录请求过来的对应的 类,方法名,请求参数和IP等一些信息,还有响应出去的,如果是 JSON 结果集的话。比如现在一般暴露给前端(前后端分离),或者其他模块基本走 Restful 接口。那怎么优雅的去记录这些东西呢?

beta 1.0
@Path("user/get")
public ListResult<User> getUser(@BeanParam GetUserParam getUserParam){
    LOGGER.info(getUserParam.toString());
    ListResult<User> userList = userService.getUserList(getUserParam);
    LOGGER.info(userList.toString());

    return userList;
}
同样,硬编码加入了两句打印的代码,其实这个没这么简单。可能日志需要输出到日志平台,可能获取IP或者请求来源的一些信息。那怎么升级会优雅呢?Annotation + AOP可能不错哦!
realease 1.0
@Path("user/get")
@ApiAdvice
public ListResult<User> getUser(@BeanParam GetUserParam getUserParam){
 ListResult<User> userList = userService.getUserList(getUserParam);

 return userList;
}

ApiAvice 的实现原理是基于 Spring AOP 和 SPring 自定义注解完成。具体这篇文章写得差不多:http://www.xdemo.org/springmvc-aop-annotation/

 

2. 出、入参封装

上一点说到了,请求入参和响应出参。
入参提供一个 BaseParam,包含了 IP ,请求来源机器,机型等各种。PagerParam 继承 BaseParam 类,增加了分页相关的参数。比如,用户的参数 UserGetPagerParam..
出参提供一个 BaseResult,包含返回码,返回信息及请求 IP等。PlainResult 单个结果集,ListResult 列表结果集,MapResult… 
同样,两者需要实现序列化。序列化的可以看看这个文章 http://www.infoq.com/cn/articles/serialization-and-deserialization
 
3. LIMIT
在处理线上问题的时候,有时候会有脏数据要进行处理。自然 DDL 操作,需要通过审批什么的。这里就不说了。主要在 UPDATE 操作的时候,注意 SQL 的写法。防止一条 SQL 误操作了其他正确的数据,加个 LIMIT 限制语句。例如
UPDATE user SET name = "BYSocket" WHERE name = "Jeff" LIMIT 1.

http://database.51cto.com/art/201005/200401.htm


4.Guava EventBus
Guava EventBus 是基于事件处理机制,是设计模式中的生产消费模式的实现。使用的场景:多半在异步的场景上。但是基于 JVM 内存上实现的,通俗的说是单机模式。需要评估好,这个异步是否是非常快速可以处理完毕。具体使用见
http://www.cnblogs.com/peida/p/EventBus.html

5.GC常用分析命令
这个总结来自有次 FullGC 特别多,OOM 的场景。然后是这样操作分析的:
jps -lvm // 查看JVM进程中的状态,并得知 JVM 的具体配置
jstat -gcutil {pid} 1000 // 每秒钟打印 GC 信息,可以看下 full GC,各种区的 实时信息
jmap -F -heap {pid} // 查看内存对象信息
主要是
jmap -dump:format=b,file=dump.bin {pid} // 导出内存对象 dump 文件

然后分析工具很多,这里用IBM HeapAnalyzer 具体使用看: http://blog.csdn.net/alexbxp/article/details/6756411

JVM 相关也看看

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in Working Skills, 技术

Java Web dev搭建经验总结

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

回馈分析法使我看到,我对专业技术人员,不管是工程师、会计师还是市场研究人员,都容易从直觉上去理解他们。这令我大感意外。它还使我看到,我其实与那些涉猎广泛的通才没有什么共鸣。

总结下,新Mac电脑的环境搭建及推荐一些老司机的玩具(软件)

一、玩具篇(软件)

1. Google Chrome / Chrome 插件 -Draw.io Desktop 、Postman、FeHelper、Website IP、Isometric Contributions、Octotree、Momentum

Chrome 的 F12 ,调试神器。

Draw.io 我是用来画图的。Postman 测试 Rest API 接口 。FeHelper一个小工具集合体。三个是经常戳戳点击的。

还有 Website IP 显示 IP。Git Hub 两个小工具:Isometric Contributions 纯好看,Octotree 左侧出现代码目录。Momentum 在空的 Tab 加入了赏心悦目的图片。

2. Evernote/为知笔记

印象笔记我喜欢记录生活,记录IDEA,记录。。。。

为知笔记记录代码,记录技术,记录工作相关的。。。

3. Shadowsocks

代理。提供个代理服务器吧

 

server:45.78.48.253
port:4433

password:NWNmNzQ2ZT

4. SwitchHosts

Host修改。很多类似的。

5. IDEA

Java 吃饭的家伙,不多说。

二、环境

1/ zsh 安装

sh -c"$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

2/ JDK 8 安装

http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html下载jdk-xxx.dmg。

然后设置 JAVA_HOME 环境变量,vim 文件’~/.zshrc’或’~/.bash_profile’,加入下面代码

 

export JAVA_HOME=`/usr/libexec/java_home -v 1.8`

export PATH=${JAVA_HOME}/bin:$PATH

java -version 验证下。

3/ Git 安装

https://sourceforge.net/projects/git-osx-installer/下载安装即可。

git –version验证下。

4/ Maven 安装

https://maven.apache.org/download.cgi下载 apache-maven-x.x.x-bin.zip。

设置 Maven 环境变量,和 JDK 环境一样,vim 文件’~/.zshrc’或’~/.bash_profile’,加入下面代码

export M2_HOME={Maven解压目录}/apache-maven-x.x.x

export PATH=$PATH:$M2_HOME/bin

mvn -version 验证下

项目一般都是 maven 项目,启动都基本用 maven 插件集成 jetty 启动。所以不涉及到 tomcat jetty这种安装。

5/ mysql 安装

下载 http://rj.baidu.com/soft/detail/25675.html?ald

# 移动解压后的二进制包到安装目录
sudo mv mysql-5.6.24-osx10.9-x86_64 /usr/local/mysql

# 更改 mysql 安装目录所属用户与用户组
cd /usr/local
sudo chown -R root:wheel mysql

sudo bin/mysqld --initialize --user=mysql

cd /usr/local/mysql

# 启动
sudo support-files/mysql.server start

# 重启
sudo support-files/mysql.server restart

# 停止
sudo support-files/mysql.server stop

# 检查 MySQL 运行状态
sudo support-files/mysql.server status

vim 文件’~/.zshrc’或’~/.bash_profile’,加入下面代码:

# MySQL
export PATH=${PATH}:/usr/local/mysql/bin

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

 

Posted in Java, 技术

代码片段:基于 JDK 8 time包的时间工具类 TimeUtil

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“知识的工作者必须成为自己时间的首席执行官。”

前言

这次泥瓦匠带来的是一个好玩的基于 JDK 8 time包的时间工具类 TimeUtil。本意,用 JDK 8 写个实战的时间工具类,初版泥瓦匠写的很不好,后来雨神分分钟将这个代码优化优化,谢谢雨神。就此分享下这个代码,让更多的人看到~

一、 Java 8 time包

从 Java 1.0 有个 Date 类,想必大家不陌生,后面有了 Calendar 类(被废弃 )。API 确实比较难用,因此 Java 8 引入 java.time API,这次看看是不是很好用。大致引入了这几个对象:

  • Instant – 瞬间类,表示时间线上的一点(与 Date 类似)
  • Duration – 持续时间,表示两个 Instant 之间的时间
  • LocalDateTime – 本地日期时间,表示一个日期和时间。

本小文重点还是在使用 LocalDateTime 及其格式化类 DateTimeFormatter

二、介绍 LocalDateTime & DateTimeFormatter APIs

LocalDateTime 表示一个日期和时间,存储确定时区中的某个时间点。
例如某一次练书法弹琴活动。(ps:泥瓦匠有个小圈子,里面喜欢加入一些文艺的程序员。爱技术,爱生活,爱艺术~ 雨神就爱弹琴,日语思密达*&#@#% )

常用api:

  • now()
    从系统中获取当前时间
  • parse(CharSequence text, DateTimeFormatter formatter)
    从字符串按格式获取 LocalDateTime 实例

DateTimeFormatter 用于格式化时间,提供了公用的方法入口,打印和解析格式化的时间类。

常用api:

  • format(TemporalAccessor temporal)
    按格式格式化时间
  • ofPattern(String pattern)
    按字符串指定的格式,生成时间格式

 

三、TimeUtil 代码详解

泥瓦匠一一道来这个代码的实现。先看代码:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 基于 JDK 8 time包的时间工具类
 * <p/>
 * Created by bysocket on 16/8/23.
 */
public final class TimeUtil {

    /**
     * 获取默认时间格式: yyyy-MM-dd HH:mm:ss
     */
    private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER = TimeFormat.LONG_DATE_PATTERN_LINE.formatter;

    private TimeUtil() {
        // no construct function
    }

    /**
     * String 转时间
     *
     * @param timeStr
     * @return
     */
    public static LocalDateTime parseTime(String timeStr) {
        return LocalDateTime.parse(timeStr, DEFAULT_DATETIME_FORMATTER);
    }

    /**
     * String 转时间
     *
     * @param timeStr
     * @param format  时间格式
     * @return
     */
    public static LocalDateTime parseTime(String timeStr, TimeFormat format) {
        return LocalDateTime.parse(timeStr, format.formatter);
    }

    /**
     * 时间转 String
     *
     * @param time
     * @return
     */
    public static String parseTime(LocalDateTime time) {
        return DEFAULT_DATETIME_FORMATTER.format(time);
    }

    /**
     * 时间转 String
     *
     * @param time
     * @param format 时间格式
     * @return
     */
    public static String parseTime(LocalDateTime time, TimeFormat format) {
        return format.formatter.format(time);
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    public static String getCurrentDatetime() {
        return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now());
    }

    /**
     * 获取当前时间
     *
     * @param format 时间格式
     * @return
     */
    public static String getCurrentDatetime(TimeFormat format) {
        return format.formatter.format(LocalDateTime.now());
    }

    /**
     * 时间格式
     */
    public enum TimeFormat {

        /**
         * 短时间格式
         */
        SHORT_DATE_PATTERN_LINE("yyyy-MM-dd"),
        SHORT_DATE_PATTERN_SLASH("yyyy/MM/dd"),
        SHORT_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd"),
        SHORT_DATE_PATTERN_NONE("yyyyMMdd"),

        /**
         * 长时间格式
         */
        LONG_DATE_PATTERN_LINE("yyyy-MM-dd HH:mm:ss"),
        LONG_DATE_PATTERN_SLASH("yyyy/MM/dd HH:mm:ss"),
        LONG_DATE_PATTERN_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss"),
        LONG_DATE_PATTERN_NONE("yyyyMMdd HH:mm:ss"),

        /**
         * 长时间格式 带毫秒
         */
        LONG_DATE_PATTERN_WITH_MILSEC_LINE("yyyy-MM-dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_SLASH("yyyy/MM/dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_DOUBLE_SLASH("yyyy\\MM\\dd HH:mm:ss.SSS"),
        LONG_DATE_PATTERN_WITH_MILSEC_NONE("yyyyMMdd HH:mm:ss.SSS");

        private transient DateTimeFormatter formatter;

        TimeFormat(String pattern) {
            formatter = DateTimeFormatter.ofPattern(pattern);
        }
    }
}

工具类由 final TimeUtil类 及 其内部枚举类TimeFormat时间格式类 组成。
1/ TimeUtil 具有私有构造函数,表示被保护,无法被外部 new 出实例。

声明了默认的  DateTimeFormatter 时间格式:yyyy-MM-dd HH:mm:ss。其他则是提供了 获取当前时间 和 时间与String互转的方法。
2/ TimeFormat 内部枚举类,首先它是单例的。

transient 关键字目的是确保 DateTimeFormatter 无序列化存储。为啥单例,因为 DateTimeFormmatter 是无状态的,可以线程共享。

具体方法解析如下:
1. 获取当前时间

String now = TimeUtil.getCurrentDatetime();
System.out.println(now);
output:
2016-08-28 16:35:23

2. 获取当前相应格式的当前时间

String now = TimeUtil.getCurrentDatetime(TimeUtil.TimeFormat.LONG_DATE_PATTERN_SLASH);
System.out.println(now);
output:
2016/08/28 16:36:24

3. String 转时间
默认格式:yyyy-MM-dd HH:mm:ss

LocalDateTime expectedDateTime = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
LocalDateTime parsedTime = TimeUtil.parseTime("2014-11-11 10:11:11");
assertEquals(expectedDateTime, parsedTime);

其他格式之一:yyyy-MM-dd HH:mm:ss

LocalDateTime expectedDateTime = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
LocalDateTime parsedTime = TimeUtil.parseTime("2014/11/11 10:11:11", LONG_DATE_PATTERN_SLASH);
assertEquals(expectedDateTime, parsedTime);

4. 时间转 String
默认格式:yyyy-MM-dd HH:mm:ss

LocalDateTime time = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
assertEquals(TimeUtil.parseTime(time), "2014-11-11 10:11:11");

其他格式之一:yyyy-MM-dd HH:mm:ss

LocalDateTime time = LocalDateTime.of(2014, 11, 11, 10, 11, 11);
assertEquals(TimeUtil.parseTime(time, LONG_DATE_PATTERN_DOUBLE_SLASH), "2014\\11\\11 10:11:11");

 

四、与 Old 代码互操作

java.time 类与 Old Date 代码互操作如下图:

time

 

五、小结

实战中的 JDK8 ,Show the code。 在用的项目,完善测试时候用起来才是关键。自然需要 JDK 环境支持,升级吧升级吧。
基于 JDK 8 time包的实践,这次只讲了 LocalDateTime 类,慢慢带来更多。
相关代码分享在 Github 主页

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

 

时间啊 时间啊,来发反鸡汤
72e172b3gw1f5tlp60gryj20je0m8ta0

Posted in Java, 技术

fast-framework – 基于 JDK 8 实现的 Java Web MVC 框架

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

fast-framework


轻量级 Java Web 框架 – https://github.com/JeffLi1993/fast-framework

  • 基于 JDK 8 规范
  • 基于 Servlet 3.0 规范
  • 零配置
  • REST 服务接口 基于 JSON 传输
  • 目前提供 MVC … 解决方案

 

一、作者与学习乐园

源码地址:我的GitHub地址
作者: 李强强 也叫 泥瓦匠
QQ群:编程之美 365234583 立即加入

二、快速入门

1. 必备环境

2. 下载 fast-framework 项目及 sample 案例

git clone https://github.com/JeffLi1993/fast-framework.git

3. Maven 编译 sample 案例

cd fast-framework // cd 到工程根目录

mvn clean install // 编译工程

4. 启动 sample 案例

cd fast-sample // cd 到 sample 案例工程根目录
mvn jetty:run  // 启动 sample 工程

Console 出现如下结果,表示服务器成功启动:

...
[INFO] Started ServerConnector@f1a45f8{HTTP/1.1}{0.0.0.0:8080}
[INFO] Started @5133ms
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 10 seconds.

5. 访问 sample

在浏览器中打开:

http://localhost:8080/hello?name=BYSocket

会得到如下结果:
sample结果图

 

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

 

Posted in Working Skills, 技术

配置Maven

采用maven管理模块工程的编译、发布。通俗的讲,就是利用maven来管理各项目。

一、Maven下载

Maven官网地址:http://maven.apache.org

二、Maven环境搭建

Windows 用户配置如下:右键”计算机” —  “属性” — “高级系统设置” — “环境变量” — “系统变量”

1. 下载 apache-maven-3.1.1.zip

2. 配置环境变量

     M2_HOME  — 配置Maven安装路径

     PATH           — 配置Maven命令文件的位置 (%M2_HOME%\bin;)

三、校验环境是否配置完毕

“win键” — 运行”cmd” ,在控制台中输入 mvn -version。如果出现下面界面,则说明环境配置完成。

 

Maven的settings.xml配置

使用默认即可。

常用指令

mvn

├── clean  清理项目

├── install 安装jar到本地仓库

├── archetype:generate  创建maven项目目录

一般利用 mvn archetype:generate 创建生成骨架工程。还有开发或构建项目,经常使用到clean和install。

Posted in 清文

2016 6月 7月 随笔

首先在6月,写了很多好多好玩的文案

4.pic

3.pic

2.pic

1.pic

…… 等等


2016-06-28 白领

到家了,看到镜子前深夜里自己的她,卸了装后完全看清了自己


2016-06-30 睡衣

只有睡衣穿久了 才真正有你的味道


2016-07-01 野鸭

野鸭,不像家禽那个肥大,也不像家禽那么绚丽。批着防御大自然的颜色,敏捷的钻水捕鱼


2016-07-10 随笔

双胞胎的孩子 在双人娃娃椅上 一个睡着 一个动着


2016-07-20 随笔

往往就是这种不太大的、微不足道的变化,对我们的未来有着深远影响— 蝴蝶效应


2016-08-01 走路

不看路走的人容易踩狗屎

8月开始了。。。

祝自己别踩狗屎

Posted in Java, 技术

Code片段 : .properties属性文件操作工具类 & JSON工具类

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“贵专” — 泥瓦匠

一、java.util.Properties API & 案例

java.util.Properties 是一个属性集合。常见的api有如下:

  • load(InputStream inStream)  从输入流中读取属性
  • getProperty(String key)  根据key,获取属性值
  • getOrDefault(Object key, V defaultValue) 根据key对象,获取属性值需要强转

首先在resources目录下增加/main/resources/fast.properties:

fast.framework.name=fast
fast.framework.author=bysocket
fast.framework.age=1

然后直接上代码PropertyUtil.java:

/**
 * .properties属性文件操作工具类
 *
 * Created by bysocket on 16/7/19.
 */
public class PropertyUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);

    /** .properties属性文件名后缀 */
    public static final String PROPERTY_FILE_SUFFIX	= ".properties";

    /**
     * 根据属性文件名,获取属性
     *
     * @param propsFileName
     * @return
     */
    public static Properties getProperties(String propsFileName) {
        if (StringUtils.isEmpty(propsFileName))
            throw new IllegalArgumentException();

        Properties  properties  = new Properties();
        InputStream inputStream = null;

        try {

            try {
                /** 加入文件名后缀 */
                if (propsFileName.lastIndexOf(PROPERTY_FILE_SUFFIX) == -1) {
                    propsFileName += PROPERTY_FILE_SUFFIX;
                }

                inputStream = Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream(propsFileName);
                if (null != inputStream)
                    properties.load(inputStream);
            } finally {
                if ( null != inputStream) {
                    inputStream.close();
                }
            }

        } catch (IOException e) {
            LOGGER.error("加载属性文件出错!",e);
            throw new RuntimeException(e);
        }

        return properties;
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @return
     */
    public static String getString(Properties properties, String key){
        return properties.getProperty(key);
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @param defaultValue
     * @return
     */
    public static String getStringOrDefault(Properties properties, String key, String defaultValue){
        return properties.getProperty(key,defaultValue);
    }

    /**
     * 根据key,获取属性值
     *
     * @param properties
     * @param key
     * @param defaultValue
     * @param <V>
     * @return
     */
    public static <V> V getOrDefault(Properties properties, String key, V defaultValue){
        return (V) properties.getOrDefault(key,defaultValue);
    }
}

UT如下:

/**
 * {@link PropertyUtil} 测试用例
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class PropertyUtilTest {

    @Test
    public void testGetProperties() {
        Properties properties = PropertyUtil.getProperties("fast");
        String fastFrameworkName = properties.getProperty("fast.framework.name");
        String authorName        = properties.getProperty("fast.framework.author");
        Object age               = properties.getOrDefault("fast.framework.age",10);
        Object defaultVal        = properties.getOrDefault("fast.framework.null",10);
        System.out.println(fastFrameworkName);
        System.out.println(authorName);
        System.out.println(age.toString());
        System.out.println(defaultVal.toString());
    }

    @Test
    public void testGetString() {
        Properties properties = PropertyUtil.getProperties("fast");
        String fastFrameworkName = PropertyUtil.getString(properties,"fast.framework.name");
        String authorName        = PropertyUtil.getString(properties,"fast.framework.author");
        System.out.println(fastFrameworkName);
        System.out.println(authorName);
    }

    @Test
    public void testGetOrDefault() {
        Properties properties = PropertyUtil.getProperties("fast");
        Object age               = PropertyUtil.getOrDefault(properties,"fast.framework.age",10);
        Object defaultVal        = PropertyUtil.getOrDefault(properties,"fast.framework.null",10);
        System.out.println(age.toString());
        System.out.println(defaultVal.toString());
    }
}

Run Console:

1
10
fast
bysocket
1
10
fast
bysocket

相关对应代码分享在 Github 主页


二、JACKSON 案例

首先,加个Maven 依赖:

                


                <!-- Jackson -->
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.13</version>
		</dependency>
		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-jaxrs</artifactId>
			<version>1.9.13</version>
		</dependency>

 

然后直接上代码JSONUtil:

/**
 * JSON 工具类
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class JSONUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(JSONUtil.class);

    /**
     * 默认JSON类
     **/
    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * 将 Java 对象转换为 JSON 字符串
     *
     * @param object
     * @param <T>
     * @return
     */
    public static <T> String toJSONString(T object) {
        String jsonStr;
        try {
            jsonStr = mapper.writeValueAsString(object);
        } catch (Exception e) {
            LOGGER.error("Java Object Can't covert to JSON String!");
            throw new RuntimeException(e);
        }
        return jsonStr;
    }


    /**
     * 将 JSON 字符串转化为 Java 对象
     *
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T toObject(String json, Class<T> clazz) {
        T object;
        try {
            object = mapper.readValue(json, clazz);
        } catch (Exception e) {
            LOGGER.error("JSON String Can't covert to Java Object!");
            throw new RuntimeException(e);
        }
        return object;
    }

}

UT如下:

/**
 * {@link JSONUtil} 测试用例
 * <p/>
 * Created by bysocket on 16/7/19.
 */
public class JSONUtilTest {

    @Test
    public void testToJSONString() {
        JSONObject jsonObject = new JSONObject(1, "bysocket", 33);
        String jsonStr = JSONUtil.toJSONString(jsonObject);
        Assert.assertEquals("{\"age\":1,\"name\":\"bysocket\",\"id\":33}", jsonStr);
    }

    @Test(expected = RuntimeException.class)
    public void testToJSONStringError() {
        JSONUtil.toJSONString(System.out);
    }

    @Test
    public void testToObject() {
        JSONObject jsonObject = new JSONObject(1, "bysocket", 33);
        String jsonStr = JSONUtil.toJSONString(jsonObject);
        JSONObject resultObject = JSONUtil.toObject(jsonStr, JSONObject.class);
        Assert.assertEquals(jsonObject.toString(), resultObject.toString());
    }

    @Test(expected = RuntimeException.class)
    public void testToObjectError() {
        JSONUtil.toObject("{int:1}", JSONObject.class);
    }
}

class JSONObject {
    int age;
    String name;
    Integer id;

    public JSONObject() {
    }

    public JSONObject(int age, String name, Integer id) {
        this.age = age;
        this.name = name;
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "JSONObject{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

Run Console(抛出了异常信息):

16/07/19 23:09:13 ERROR util.JSONUtil: JSON String Can't covert to Java Object!
16/07/19 23:09:13 ERROR util.JSONUtil: Java Object Can't covert to JSON String!

三、小结

相关对应代码分享在 Github 主页

请看到的Java小伙伴多交流多评论改进之。

参考 黄勇 smart

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in 技术, 算法

图解:二叉搜索树算法(BST)

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!
“岁月极美,在于它必然的流逝”
“春花 秋月 夏日 冬雪”
— 三毛

一、树 & 二叉树

是由节点和边构成,储存元素的集合。节点分根节点、父节点和子节点的概念。
如图:树深=4; 5是根节点;同样8与3的关系是父子节点关系。

1
二叉树binary tree,则加了“二叉”(binary),意思是在树中作区分。每个节点至多有两个子(child),left child & right child。二叉树在很多例子中使用,比如二叉树表示算术表达式。
如图:1/8是左节点;2/3是右节点;

2

二、二叉搜索树 BST

顾名思义,二叉树上又加了个搜索的限制。其要求:每个节点比其左子树元素大,比其右子树元素小。
如图:每个节点比它左子树的任意节点大,而且比它右子树的任意节点小

3

三、BST Java实现

直接上代码,对应代码分享在 Github 主页
BinarySearchTree.java

package org.algorithm.tree;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二叉搜索树(BST)实现
 *
 * Created by bysocket on 16/7/7.
 */
public class BinarySearchTree {
    /**
     * 根节点
     */
    public static TreeNode root;

    public BinarySearchTree() {
        this.root = null;
    }

    /**
     * 查找
     *      树深(N) O(lgN)
     *      1. 从root节点开始
     *      2. 比当前节点值小,则找其左节点
     *      3. 比当前节点值大,则找其右节点
     *      4. 与当前节点值相等,查找到返回TRUE
     *      5. 查找完毕未找到,
     * @param key
     * @return
     */
    public TreeNode search (int key) {
        TreeNode current = root;
        while (current != null
                && key != current.value) {
            if (key < current.value )
                current = current.left;
            else
                current = current.right;
        }
        return current;
    }

    /**
     * 插入
     *      1. 从root节点开始
     *      2. 如果root为空,root为插入值
     *      循环:
     *      3. 如果当前节点值大于插入值,找左节点
     *      4. 如果当前节点值小于插入值,找右节点
     * @param key
     * @return
     */
    public TreeNode insert (int key) {
        // 新增节点
        TreeNode newNode = new TreeNode(key);
        // 当前节点
        TreeNode current = root;
        // 上个节点
        TreeNode parent  = null;
        // 如果根节点为空
        if (current == null) {
            root = newNode;
            return newNode;
        }
        while (true) {
            parent = current;
            if (key < current.value) {
                current = current.left;
                if (current == null) {
                    parent.left = newNode;
                    return newNode;
                }
            } else {
                current = current.right;
                if (current == null) {
                    parent.right = newNode;
                    return newNode;
                }
            }
        }
    }

    /**
     * 删除节点
     *      1.找到删除节点
     *      2.如果删除节点左节点为空 , 右节点也为空;
     *      3.如果删除节点只有一个子节点 右节点 或者 左节点
     *      4.如果删除节点左右子节点都不为空
     * @param key
     * @return
     */
    public TreeNode delete (int key) {
        TreeNode parent  = root;
        TreeNode current = root;
        boolean isLeftChild = false;
        // 找到删除节点 及 是否在左子树
        while (current.value != key) {
            parent = current;
            if (current.value > key) {
                isLeftChild = true;
                current = current.left;
            } else {
                isLeftChild = false;
                current = current.right;
            }

            if (current == null) {
                return current;
            }
        }

        // 如果删除节点左节点为空 , 右节点也为空
        if (current.left == null && current.right == null) {
            if (current == root) {
                root = null;
            }
            // 在左子树
            if (isLeftChild == true) {
                parent.left = null;
            } else {
                parent.right = null;
            }
        }
        // 如果删除节点只有一个子节点 右节点 或者 左节点
        else if (current.right == null) {
            if (current == root) {
                root = current.left;
            } else if (isLeftChild) {
                parent.left = current.left;
            } else {
                parent.right = current.left;
            }

        }
        else if (current.left == null) {
            if (current == root) {
                root = current.right;
            } else if (isLeftChild) {
                parent.left = current.right;
            } else {
                parent.right = current.right;
            }
        }
        // 如果删除节点左右子节点都不为空
        else if (current.left != null && current.right != null) {
            // 找到删除节点的后继者
            TreeNode successor = getDeleteSuccessor(current);
            if (current == root) {
                root = successor;
            } else if (isLeftChild) {
                parent.left = successor;
            } else {
                parent.right = successor;
            }
            successor.left = current.left;
        }
        return current;
    }

    /**
     * 获取删除节点的后继者
     *      删除节点的后继者是在其右节点树种最小的节点
     * @param deleteNode
     * @return
     */
    public TreeNode getDeleteSuccessor(TreeNode deleteNode) {
        // 后继者
        TreeNode successor = null;
        TreeNode successorParent = null;
        TreeNode current = deleteNode.right;

        while (current != null) {
            successorParent = successor;
            successor = current;
            current = current.left;
        }

        // 检查后继者(不可能有左节点树)是否有右节点树
        // 如果它有右节点树,则替换后继者位置,加到后继者父亲节点的左节点.
        if (successor != deleteNode.right) {
            successorParent.left = successor.right;
            successor.right = deleteNode.right;
        }

        return successor;
    }

    public void toString(TreeNode root) {
        if (root != null) {
            toString(root.left);
            System.out.print("value = " + root.value + " -> ");
            toString(root.right);
        }
    }
}

/**
 * 节点
 */
class TreeNode {

    /**
     * 节点值
     */
    int value;

    /**
     * 左节点
     */
    TreeNode left;

    /**
     * 右节点
     */
    TreeNode right;

    public TreeNode(int value) {
        this.value = value;
        left  = null;
        right = null;
    }
}

1. 节点数据结构
首先定义了节点的数据接口,节点分左节点和右节点及本身节点值。如图

4

代码如下:

/**
 * 节点
 */
class TreeNode {

    /**
     * 节点值
     */
    int value;

    /**
     * 左节点
     */
    TreeNode left;

    /**
     * 右节点
     */
    TreeNode right;

    public TreeNode(int value) {
        this.value = value;
        left  = null;
        right = null;
    }
}

 

2. 插入
插入,和删除一样会引起二叉搜索树的动态变化。插入相对删处理逻辑相对简单些。如图插入的逻辑:

5
a. 从root节点开始
b.如果root为空,root为插入值
c.循环:
d.如果当前节点值大于插入值,找左节点
e.如果当前节点值小于插入值,找右节点
代码对应:

    /**
     * 插入
     *      1. 从root节点开始
     *      2. 如果root为空,root为插入值
     *      循环:
     *      3. 如果当前节点值大于插入值,找左节点
     *      4. 如果当前节点值小于插入值,找右节点
     * @param key
     * @return
     */
    public TreeNode insert (int key) {
        // 新增节点
        TreeNode newNode = new TreeNode(key);
        // 当前节点
        TreeNode current = root;
        // 上个节点
        TreeNode parent  = null;
        // 如果根节点为空
        if (current == null) {
            root = newNode;
            return newNode;
        }
        while (true) {
            parent = current;
            if (key < current.value) {
                current = current.left;
                if (current == null) {
                    parent.left = newNode;
                    return newNode;
                }
            } else {
                current = current.right;
                if (current == null) {
                    parent.right = newNode;
                    return newNode;
                }
            }
        }
    }

 

3.查找

其算法复杂度 : O(lgN),树深(N)。如图查找逻辑:

6
a.从root节点开始
b.比当前节点值小,则找其左节点
c.比当前节点值大,则找其右节点
d.与当前节点值相等,查找到返回TRUE
e.查找完毕未找到
代码对应:

    /**
     * 查找
     *      树深(N) O(lgN)
     *      1. 从root节点开始
     *      2. 比当前节点值小,则找其左节点
     *      3. 比当前节点值大,则找其右节点
     *      4. 与当前节点值相等,查找到返回TRUE
     *      5. 查找完毕未找到,
     * @param key
     * @return
     */
    public TreeNode search (int key) {
        TreeNode current = root;
        while (current != null
                && key != current.value) {
            if (key < current.value )
                current = current.left;
            else
                current = current.right;
        }
        return current;
    }

4. 删除
首先找到删除节点,其寻找方法:删除节点的后继者是在其右节点树种最小的节点。如图删除对应逻辑:7

结果为:

8
a.找到删除节点
b.如果删除节点左节点为空 , 右节点也为空;
c.如果删除节点只有一个子节点 右节点 或者 左节点
d.如果删除节点左右子节点都不为空
代码对应见上面完整代码。

 

案例测试代码如下,BinarySearchTreeTest.java

package org.algorithm.tree;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 二叉搜索树(BST)测试案例 {@link BinarySearchTree}
 *
 * Created by bysocket on 16/7/10.
 */
public class BinarySearchTreeTest {

    public static void main(String[] args) {
        BinarySearchTree b = new BinarySearchTree();
        b.insert(3);b.insert(8);b.insert(1);b.insert(4);b.insert(6);
        b.insert(2);b.insert(10);b.insert(9);b.insert(20);b.insert(25);

        // 打印二叉树
        b.toString(b.root);
        System.out.println();

        // 是否存在节点值10
        TreeNode node01 = b.search(10);
        System.out.println("是否存在节点值为10 => " + node01.value);
        // 是否存在节点值11
        TreeNode node02 = b.search(11);
        System.out.println("是否存在节点值为11 => " + node02);

        // 删除节点8
        TreeNode node03 = b.delete(8);
        System.out.println("删除节点8 => " + node03.value);
        b.toString(b.root);


    }
}

运行结果如下:

value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 8 -> value = 9 -> value = 10 -> value = 20 -> value = 25 -> 
是否存在节点值为10 => 10
是否存在节点值为11 => null
删除节点8 => 8
value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 9 -> value = 10 -> value = 20 -> value = 25 ->

四、小结

与偶尔吃一碗“老坛酸菜牛肉面”一样的味道,品味一个算法,比如BST,的时候,总是那种说不出的味道。

树,二叉树的概念

BST算法

相关代码分享在 Github 主页

如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

Posted in Working Skills, 技术

ApacheBench压测 那点事儿

摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢!

“我总是希望自己不要过于草率地去批评一个人” -《傲慢与偏见》

写代码,到处是指标和数据的。在写完接口,为了保证代码在一定压力下没问题,我们需要对接口进行压测。比如写完一模块的RestAPI,就需要用Web压测工具进行压测。压测工具有很多,新潮的getling,还有Old版的ApacheBench压测。

一、 什么是ApacheBench(AB)?

ApacheBench (ab) is a single-threaded command line computerprogram for measuring the performance of HTTP web servers.

AB是一个测试HTTP WEB服务器性能的单线程命令程序。实战才是真理,但需要学会怎么使用AB命令。

 

二、 AB命令参数

通过简单的指令 ab -h ,就可以得到AB命令参数列表。如下:

ab -h

Usage: ab [options] [http[s]://]hostname[:port]/path

Options are:

    -n requests     Number of requests to perform

    -c concurrency  Number of multiple requests to make at a time

    -t timelimit    Seconds to max. to spend on benchmarking

                    This implies -n 50000

    -s timeout      Seconds to max. wait for each response

                    Default is 30 seconds

    -b windowsize   Size of TCP send/receive buffer, in bytes

    -B address      Address to bind to when making outgoing connections

    -p postfile     File containing data to POST. Remember also to set -T

    -u putfile      File containing data to PUT. Remember also to set -T

    -T content-type Content-type header to use for POST/PUT data, eg.

                    'application/x-www-form-urlencoded'

                    Default is 'text/plain'

    -v verbosity    How much troubleshooting info to print

    -w              Print out results in HTML tables

    -i              Use HEAD instead of GET

    -x attributes   String to insert as table attributes

    -y attributes   String to insert as tr attributes

    -z attributes   String to insert as td or th attributes

    -C attribute    Add cookie, eg. 'Apache=1234'. (repeatable)

    -H attribute    Add Arbitrary header line, eg. 'Accept-Encoding: gzip'

                    Inserted after all normal header lines. (repeatable)

    -A attribute    Add Basic WWW Authentication, the attributes

                    are a colon separated username and password.

    -P attribute    Add Basic Proxy Authentication, the attributes

                    are a colon separated username and password.

    -X proxy:port   Proxyserver and port number to use

    -V              Print version number and exit

    -k              Use HTTP KeepAlive feature

    -d              Do not show percentiles served table.

    -S              Do not show confidence estimators and warnings.

    -q              Do not show progress when doing more than 150 requests

    -l              Accept variable document length (use this for dynamic pages)

    -g filename     Output collected data to gnuplot format file.

    -e filename     Output CSV file with percentages served

    -r              Don't exit on socket receive errors.

    -m method       Method name

    -h              Display usage information (this message)

    -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)

    -f protocol     Specify SSL/TLS protocol

                    (SSL3, TLS1, TLS1.1, TLS1.2 or ALL)

常用命令参数如下:

-n 测试的请求数量

-c 同时请求数量(并发数)

-t 测试时间

以上一般搭配 -n & -c 或者 -n & -t

-k HTTP保持Keep-Alive方式(长连接)

-p POST方式下发送的数据

-T POST或PUT方式下的Content-type

 

三、简单例子

从常用命令参数中,我们用案例说话:

a. 同时10个请求,连续点击1000次(每次Request完毕自动销毁,重新请求)

ab -n 1000 -c 10 http://www.test.com/index.html

b. 同时10个请求,连续点击1000次,并保持Keep-Alive方式(保持长连接的方式)

ab -n 1000 -c 10 -k http://www.test.com/index.html

c. 同时10个请求,请求测试时间为20s

ab -c 10 -t 20 http://www.test.com/index.html

d. 将请求性能详情输出到CSV档文件

ab -e output.csv -n 1000 -c 10 http://www.test.com/index.html

out.csv: {请求序号 – 请求耗时ms}

e. 包含Post的数据的文件,例如POST发送JSON格式数据

ab -p p.json -T application/json -n 1000 -c 10 http://www.test.com/index.html

p.json:

{"mallId" : 1, "itemIds" : [9102,9101]}

四、返回参数详解

比如 我们拿简单案例第一个测试,得到了返回报告如下:

Server Software:        nginx/1.9.15

Server Hostname:        www.test.com

Server Port:            80

Document Path:          /index.html

Document Length:        161 bytes

Concurrency Level:      10

Time taken for tests:   5.760 seconds

Complete requests:      100

Failed requests:        0

Non-2xx responses:      100

Total transferred:      35200 bytes

HTML transferred:       16100 bytes

Requests per second:    17.36 [#/sec] (mean)

Time per request:       575.955 [ms] (mean)

Time per request:       57.595 [ms] (mean, across all concurrent requests)

Transfer rate:          5.97 [Kbytes/sec] received

Connection Times (ms)

              min  mean[+/-sd] median   max

Connect:      179  197  28.3    187     320

Processing:   179  240 236.8    186    2074

Waiting:      179  231 229.2    186    2073

Total:        360  437 238.4    374    2263

Percentage of the requests served within a certain time (ms)

  50%    374

  66%    381

  75%    418

  80%    427

  90%    513

  95%    567

  98%   1685

  99%   2263

 100%   2263 (longest request)

指标太多,我们要去看关键指标,比如

—-关键指标—-

Failed requests  失败的请求数。如果太多证明Web服务器稳定度不够

Request per second 每秒请求的次数。代表web服务器的承载量,即吞吐量(不考虑带宽限制)

Time per request:855.897 [ms] (mean)用户平均请求等待时间。可以通过1000ms/Time per request算出每秒的事务数 QPS ,mean是平均值

Time per request:85.590 [ms] (mean, across all concurrent requests) 服务器平均处理每一个并发请求的时间。可以1000ms/Time per request算出平均事务响应时间,mean是平均值

详细解释如下:

Server Software:  服务器名称(从http响应数据的头信息获取)

Server Hostname:  服务器Host名称(从http请求数据的头信息获取)

Server Port:      服务器端口

Document Path:    请求的根绝对路径, 默认为 /

Document Length:  响应数据的正文大小, 单位为 Byte

Concurrency Level: 并发用户数(命令中表现为-c后面跟着的参数)

Time taken for tests: 所有请求测试的总耗时(如果是 -t 参数,则为该数值;如果是 -n 请求,一般来说 值越大,耗时越多)

Complete requests: 总请求数(-n 参数值)

Failed requests:   失败的请求数(2XX失败的请求除外)

Write errors:      写入失败的数量

Total transferred: 数据传输量

HTML transferred:  所有请求的响应数据长度总和

Requests per second: 每秒请求的次数。代表web服务器的承载量,即吞吐量(不考虑带宽限制)

Time per request:  用户平均请求等待时间

Time per request:  服务器平均处理每一个并发请求的时间

Transfer rate:     请求在单位时间内从服务器获取的数据长度

五、机器Top命令 & 压测建议

压测中,我们要关注服务器的性能,一般28原则。即

1. 压测要将某个指标压到 80%左右。比如Linux服务器 top命令,实时查看服务器性能:

top - 15:22:08 up 41 days, 23:39,  1 user,  load average: 0.21, 0.05, 0.02

Tasks: 392 total,   1 running, 391 sleeping,   0 stopped,   0 zombie

Cpu0  :  1.0%us,  0.0%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  P COMMAND

24425 root      20   0 13728 1580  944 R  1.0  0.1   0:00.98 0 top

关键指标达到 80% ,得出报告最准确。

load average (0.21, 0.05, 0.02)- 系统负载,即 1分钟、5分钟、15分前的任务队列的平均长度,所以可以看第一个是否达到80%

Cpu0 CPU占用百分比

wa 等待输入输出的CPU时间百分比,即网络IO操作

2. 循序渐进:同时请求数阶梯式加进去。达到上面某个指标的域

3. 服务器压测服务器,不用多说

4. 排除带宽限制、多台服务器测试


如以上文章或链接对你有帮助的话,别忘了在文章结尾处评论哈~ 你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。