石沉溪洞

Every step leaves its print


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

毕业设计相关

发表于 2016-12-13   |   分类于 负载均衡   |     |   阅读次数

基于情景感知的nginx负载均衡算法调度模型的研究
解决:传统的负载均衡策略,无论是利用软件实现负载均衡还是昂贵的硬件实现负载均衡,都只是一次配置负载均衡算法,永久使用。nginx负载均衡也不例外。为了能使根据实际情况动态调整。如带宽,用户访问量,机器运行状态(内存,cpu)

    基于web的应用的需求越来越大,用户的日常生活越来越依赖这些应用,如电子商务,在线银行,股票交易,预订和产品销售等。因此,企业服务提供商越来越积极地将应用程序移动到基于Web的基础设施上。这些变化实质上是将web应用从通信和浏览基础设施转变为一种商业的服务。这种基于服务的web应用对Web服务器系统的性能要求越来越高。 Web服务器可能在非常短的时间内接收大量的客户端连接,这将导致网络拥塞,响应时间增加,甚至将Web服务器的操作变为不稳定的情况。响应延迟很长时间将影响用户对与web服务使用的兴趣,并且他们可能放弃浏览网站。对于电子商务网站,这种降级尤其有害,因为它可能导致收入的显着损失和客户访问的大量流失。因此,用户对更快的基于Web的应用程序的需求需要现在的我们特别留意。任何单个服务器都可以轻易地成为瓶颈和单点故障。此外,单个服务器机器只能处理有限数量的请求,并且无法按需扩展。处理对Web服务器不断增长的处理需求的最佳方法是添加更多的硬件资源,而不是用更快的一个服务器完全替换一个服务器。当客户数量呈指数增长时,集群Web服务器是构建强大网站的领先架构。集群web服务器由称为web交换机的前端节点(其负责分派请求)和用于处理请求的几个web服务器组成。由于关于集群web服务器的管理的各种技术和控制问题,负载均衡算法和准入控制(其在web交换机中实现)对于提高集群web服务器的性能特别重要。在本文中,我们提出了一个新的负载平衡算法与几个贡献如下:我们首先基于他们的服务分类请求。

    基于web的应用的需求越来越大,用户的日常生活越来越依赖这些应用,但是随着使用人数的增加,对这些服务器的性能要求越来越高,任何单个服务器都可以轻易地成为瓶颈和单点故障。此外,单个服务器机器只能处理有限数量的请求,并且无法按需扩展。处理对Web服务器不断增长的处理需求的最佳方法是添加更多的硬件资源,而不是用更快的一个服务器完全替换一个服务器。当客户数量呈指数增长时,集群Web服务器是构建强大网站的领先架构。集群web服务器由称为web交换机的前端节点(其负责分派请求)和用于处理请求的几个web服务器组成。由于关于集群web服务器的管理的各种技术和控制问题,负载均衡算法和准入控制(其在web交换机中实现)对于提高集群web服务器的性能特别重要。
 目前的负载均衡策略有很多,有硬件方案和软件方案两大类,硬件主要指的是像f5这种负载均衡器,性能比较好,但是价格昂贵。软件的策略有LVS和nginx,他们都各有优劣,但是他们共同的缺点就是负载均衡的策略都只能在服务启动前提前设置好,所以很难应对一些突发情况,只能够由特定的管理员去手工维护这些策略。
因此我想基于nginx,利用情景感知,监控服务器的一些指标,如带宽,用户访问量,机器运行状态(内存,cpu),通过算法去动态让nginx去使用不同的负载均衡算法的调度。现在nginx负载均衡算法用轮训,ip hash,指定权重这三种。我在图书馆搜索了有关情景感知和负载均衡的相关论文,中文的基本没有,英文的与之相关的也很少,关于负载均衡的很多,但是结合情景感知的很少。后续工作主要是看看已经找到的相关论文,国内外现状和初步实现方案。



访问内容的大小 ip地址 缓存 吞吐量 响应时间 cpu利用率 网络带宽 内存 访问时间

衡量标准:响应时间

1.轮训
2.weight
3.ip_hash
4.fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
5.url_hash(第三方)
访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

比如在cpu利用率都很大,请求队列等一样的时候选择轮训算法,在访问量很小的情况下使用fair算法,访问量小并且有缓存的情况下使用urlhash,同一ip访问量特别大的时候不用ip_hash

相关论文

Deciding Handover Points Based on Context-Aware Load Balancing in a WiFi-WiMAX Heterogeneous Network Environment
基于分布式情景感知的分层式网络路由方法 CN 103595657 A
上下文感知系统中的规则生成与匹配算法
Workload-aware load balancing for clustered Web servers
Load Balancing for Future Internet: An Approach Based on Game Theory

1.特征的选择
1.1 通过相关论文确定备选特征
2.数据采集和标注
2.1 确定采集系统实现方案(服务器如何配置,采集日志的格式)
2.2 确定如何模拟用户访问行为
2.3 在不同场景下分析采用哪个算法最好,给数据标签
2.3 根据所选特征采集数据
3.数据的清理和预处理
4.特征的确定
3.1 根据采集的数据通过神经网络或者特征选择算法去进行指标的评估
3.2 确定参加模型训练的指标
5.模型的选择
4.1 采用不同模型对数据集进行训练,找出准确率最高的
6.模型优化
7.模型评估
8.实际效果展示

在线课堂开发环境搭建

发表于 2016-11-17   |   分类于 Work   |     |   阅读次数

Maven私服的搭建

1. 下载,通过root用户进去Ubuntu server  
1
2
$cd /opt
$wget http://download.sonatype.com/nexus/oss/nexus-2.10.0-02-bundle.tar.gz
2. 启动

  环境准备,启动nexus,必须先完成JDK环境的配置。

1
2
3
4
5
6
7
  $  cd /opt/
  $  tar -zxvf nexus-2.10.0-02-bundle.tar.gz
  $  cd /opt/nexus-2.10.0-02/bin
  $  vi nexus

  在启动文件nexus中增加nexus的启动用户,否则没权限启动,将报错如下:

  WARNING - NOT RECOMMENDED TO RUN AS ROOT

  If you insist running as root, then set the environment variable RUN_AS_USER=root before running this script

  所以要解决此问题,需要在启动文件nexus,加上RUN_AS_USER=root
    
  保存退出之后,即可启动nexus,命令如下:

1
2
3
4
  $  ./nexus start  
  Starting Nexus OSS...
  Started Nexus OSS.

  表示启动成功,访问地址:http://ip:8081/nexus

3. 配置nexus

  登录nexus,右上角Log In,默认登录用户名密码:admin/admin123

  但是需要将其他几个代理的库配置映射到Public中,分别将Apache Snapshots、Central、Codehaus Sanpshots下Download Remote Indexes选项选择【true】,保存即可,默认是false,如下图

 然后分别将Apache Snapshots、Central、Codehaus Sanpshots更新Index,在每一个库上面右键操作Repair Index  

4.开启开机启动

Mysql搭建

1.安装mysql
1
2
3
4
  1. sudo apt-get install mysql-server
  2. sudo apt-get isntall mysql-client
  3. sudo apt-get install libmysqlclient-dev
  4. mysql -u root -p
2.新建用户开启远程链接
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
  1. create user 'xinshen'@'%' identified by 'xinshen_mysql';
  2. create database xinshen;
  3. grant all privileges on xinshen.* to 'xinshen'@'%' identified by 'xinshen_mysql' with grant option;
  4. flush privileges;
  5. service mysql stop
  6. service mysql start
```
##### 3.开启开机启动
### tomcat搭建
apt-get install python-software-properties
apt-get install software-properties-common
1.添加ppa
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
2.安装oracle-java-installer
sudo apt-get install oracle-java7-installer
sudo apt-get install oracle-java8-installer
### redis搭建
http://blog.csdn.net/u010286751/article/details/48924635
### FastDFS搭建
官方论坛: http://www.csource.org
下载地址: http://sourceforge.net/projects/fastdfs/files/
github软件仓库
libfastcommon — FastDFS和FastDHT的通用函数库,地址: https://github.com/happyfish100/libfastcommon.git
FastDFS — FastDFS主代码,地址: https://github.com/happyfish100/fastdfs.git
fastdfs-nginx-module — FastDFS的nginx模块,地址:https://github.com/happyfish100/fastdfs-nginx-module.git
Ubuntu 14.04下部署FastDFS 5.08+Nginx 1.9.14
最新的版本可以在这里获取,本文下载的版本是5.08,FastDFS是为互联网应用量身定做的一套分布式文件存储系统,非常适合用来存储用户图片、视频、文档等文件。对于互联网应用,和其他分布式文件系统相比,优势非常明显。具体情况大家可以看相关的介绍文档,包括FastDFS介绍PPT等等。
出于简洁考虑,FastDFS没有对文件做分块存储,因此不太适合分布式计算场景。
下载好后,server端分为两个部分,一个是tracker,一个是storage。顾名思义,前者调度管理,负载均衡,后者则是实际的存储节点。两个都能做成集群,以防止单点故障。以前的4.x版本依赖libevent,现在不需要了,只需要libfastcommon。安装方法如下:
1. 安装libfastcommon
```bash
git clone https://github.com/happyfish100/libfastcommon.git
cd libfastcommon/
./make.sh
./make.sh install
  1. 这时候需要设置环境变量或者创建软链接

    1
    2
    export LD_LIBRARY_PATH=/usr/lib64/
    ln -s /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
  2. 解压执行

    1
    2
    3
    4
    tar xzf FastDFS.tar.gz
    cd FastDFS/
    ./make.sh
    /make.sh install

确认make没有错误后,执行安装,默认会安装到/usr/bin中,并在/etc/fdfs中添加三个配置文件。

  1. 修改配置文件
    首先将三个文件的名字去掉sample,暂时只修改以下几点,先让fastdfs跑起来,其余参数调优的时候再考虑。

4.1 tracker.conf 中修改:
base_path=/home/fastdfs #用于存放日志。
http.server_port=8090 ////TODO 这个http.server_port=8888 指的是在tracker服务器上启动http服务进程,如:apache或者nginx 启动时所监听的端口

4.2 storage.conf中修改:
disabled=false #启用配置文件
group_name=group1 #组名,根据实际情况修改
port=23000 #设置 storage 的端口号
base_path=/home/fastdfs #设置 storage 的日志目录(需预先创建)
store_path_count=1 #存储路径个数,需要和 store_path 个数匹配
store_path0=/home/fastdfs/storage #存储路径
tracker_server=120.77.41.162:22122 #tracker 服务器的 IP 地址和端口号
http.server_port=8888 #设置storage上启动的http服务的端口号,如安装的nginx的端口号
[备注]:store_path_count如果有多个,那么需要设置多个存储路径,如:
store_path_count=2
store_path0=/home/fastdfs/storage
store_path1=/home/fastdfs/storage1

4.3 client.conf 中修改:
base_path=/home/fastdfs #用于存放日志。 tracker_server=120.77.41.162:22122 #指定tracker服务器地址。
http.tracker_server_port=8888

#include http.conf
其它保持默认,注意上面那个是1个#,默认是2个#,去掉1个就行
付:
1) tracker.conf说明 http://www.tuicool.com/articles/RjQf22f
2) storage.conf说明 http://www.tuicool.com/articles/fuMJjyf

  1. 拷贝配置文件

    1
    2
    3
    4
    cp -rv /home/yjy/fastdfs/conf/httpd.conf /etc/fdfs/ (如果要用httpserver 或者使用nginx作为文件获取服务器必须要这个文件)
    cp -rv /home/yjy/fastdfs/conf/mime.types /etc/fdfs/
    cp FastDFS(源码解压目录)/conf/anti-steal.jpg /etc/fdfs/
    cp fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/
  2. 开启防火墙:

    1
    2
    3
    4
    firewall-cmd --zone=public --add-port=23000/tcp --permanent
    firewall-cmd --permanent --zone=public --add-service=http
    firewall-cmd --zone=public --add-port=22122/tcp --permanent
    firewall-cmd --reload
  3. 启动tracker和storage

/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf
netstat –lnp –tcp 参看端口是否起来,默认如果显示22122和8090,23000,80说明服务正常起来

  1. 上传删除测试
    root@ubuntu:~# fdfs_test /etc/fdfs/client.conf upload /home/steven/01.jpg

nginx搭建

  1. zlib 安装
    wget http://zlib.net/zlib-1.2.8.tar.gz
    tar xzf zlib-1.2.8.tar.gz
    cd zlib-1.2.8
    ./configure –prefix=/usr/local/zlib-1.2.8 && make &&make install
  2. pcre 安装
    wget http://exim.mirror.fr/pcre/pcre-8.38.tar.gz
    tar xzf pcre-8.38.tar.gz
    cd pcre-8.38
    ./configure –prefix=/usr/local/pcre-8.33 –libdir=/usr/local/lib/pcre –includedir=/usr/local/include/pcre && make && make install
  3. openssl
    wget http://www.openssl.org/source/openssl-1.0.1e.tar.gz
    tar xzf openssl-1.0.1e.tar.gz
    cd openssl-1.0.1e
    ./config –prefix=/usr/local/openssl-1.0.1e && make && make install

  4. 添加软连接
    ln -sv /usr/include/fastcommon /usr/local/include/fastcommon
    ln -sv /usr/include/fastdfs /usr/local/include/fastdfs
    ln -sv /usr/lib64/libfastcommon.so /usr/local/lib/libfastcommon.so
    为了nginx自启动还需要
    ln -s /usr/lib64/libfdfsclient.so /usr/lib/libfdfsclient.so

  5. 下载安装fastdfs-nginx-module
    wget http://jaist.dl.sourceforge.net/project/fastdfs/FastDFS%20Nginx%20Module%20Source%20Code/fastdfs-nginx-module_v1.16.tar.gz
    tar xzf fastdfs-nginx-module_v1.16.tar.gz

  6. 下载安装nginx
    wget http://nginx.org/download/nginx-1.9.15.tar.gz
    tar xzf nginx-1.9.15.tar.gz
    cd nginx-1.9.15
    ./configure –prefix=/usr/local/nginx –with-pcre=/home/yjy/pcre-8.38 –with-zlib=/home/yjy/zlib-1.2.8 –add-module=/home/yjy/fastdfs-nginx-module/src –with-http_ssl_module –with-openssl=/home/yjy/openssl-1.0.1e –sbin-path=/usr/local/nginx/sbin/nginx && make && make install

  7. 修改nginx扩展模块的配置文件,注意修改的是复制后的文件
      cd ../fastdfs-nginx-module/src
    cp mod_fastdfs.conf /etc/fdfs/
    vi /etc/fdfs/mod_fastdfs.conf
    base_path=/data/fastdfs/storage #保存日志目录
    tracker_server=120.77.41.162:22122 #tracker 服务器的 IP 地址以及端口号
    storage_server_port=23000 #storage服务器的端口号
    group_name=group1 #当前服务器的group名
    url_have_group_name = true #文件url中是否有group 名
    store_path_count=1 #存储路径个数,需要和store_path 个数匹配
    store_path0=/home/fastdfs/storage #存储路径
    group_count = 1 #设置组的个数
    #然后在末尾添加分组信息,目前只有一个分组,就只写一个
    [group1]
    group_name=group1
    storage_server_port=23000
    store_path_count=1
    store_path0=/home/fastdfs/storage

  8. 做M00的链接
    ln -s /home/fastdfs/storage/data /home/fastdfs/storage/data/M00

  9. 修改nginx的配置文件
    备注:如果nginx有其他服务器在用,在nginx.conf原有的基础上追加一个下面server的配置即可
    vi /usr/local/nginx/conf/nginx.conf
    server {
      listen 8888;/注意端口和trackerServer一样
      location / {
        root /home/fastdfs/storage/data;
        index index.html index.htm;
       }
      location /group1/M00 {
         root /home/fastdfs/storage/data;
        ngx_fastdfs_module;
      }
    }

    1. 重启相关服务,验证整合是否成功
      先重启storage服务:
      /etc/init.d/fdfs_storaged start
      然后再启动nginx,
      注意顺序,否则会报端口占用的错误
      ./usr/local/nginx/sbin/nginx

查看端口使用情况:
netstat -lnp –tcp
如何让server进程退出运行?
直接kill即可让server进程正常退出,可以使用killall命令,例如:
killall fdfs_trackerd
killall fdfs_storaged
也可以使用如下命令:
/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf stop
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf stop
千万不要使用-9参数强杀,否则可能会导致binlog数据丢失的问题。

如何重启server进程?
直接使用:
/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf restart
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf restart

  1. nginx 服务器重启命令,关闭
    nginx -s reload :修改配置后重新加载生效
    nginx -s reopen :重新打开日志文件
    nginx -t -c /path/to/nginx.conf 测试nginx配置文件是否正确

关闭nginx:
nginx -s stop :快速停止nginx
quit :完整有序的停止nginx

其他的停止nginx 方式:

ps -ef | grep nginx

kill -QUIT 主进程号 :从容停止Nginx
kill -TERM 主进程号 :快速停止Nginx
pkill -9 nginx :强制停止Nginx

启动nginx:
nginx -c /path/to/nginx.conf

平滑重启nginx:
kill -HUP 主进程号

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;

Hello World

发表于 2016-09-29   |     |   阅读次数

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

查询接口文档

发表于 2016-09-22   |   分类于 工作   |     |   阅读次数

查询模块接口设计文档

1. NoteController

1.1 api/query/{plat}/notes
  • 描述:新建一个Note
  • 请求方式:POST
  • 参数:

    name:name
    content:content(非必须)
    description:description
    
  • 返回:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "code": 201,
    "msg": "新建成功",
    "data": {
    "id": 22,
    "userId": 100,
    "name": "name",
    "content": "content",
    "description": "description",
    "createTime": 1474452386000,
    "category": 20
    }
    }
1.2 api/query/{plat}/notes/{noteId}
  • 描述:更新一个Note的信息,要更新哪个参数就传哪个参数
  • 请求方式:PUT
  • 参数:
    name:newname(非必须)
    description:newdescription(非必须)
    content:content (非必须)
    
  • 返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "code": 200,
    "msg": "更新成功",
    "data": {
    "id": 22,
    "userId": 100,
    "name": "newname",
    "content": "content",
    "description": "newdescription",
    "createTime": 1474453076000,
    "category": 20
    }
    }
1.3 api/query/{plat}/notes/{noteId}
  • 描述:删除一个Note
  • 请求方式:DELETE
  • 参数:
  • 返回:
    1
    2
    3
    4
    5
    {
    "code": 204,
    "msg": "删除成功",
    "data": null
    }
1.4 api/query/{plat}/notes
  • 描述:获取用户所有的Note
  • 请求方式:GET
  • 参数:
  • 返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    {
    "code": 200,
    "msg": "获取成功",
    "data": [
    {
    "id": 19,
    "userId": 100,
    "name": "newname",
    "content": "content",
    "description": "newdescription",
    "createTime": 1474455086000,
    "category": 20
    },
    {
    "id": 23,
    "userId": 100,
    "name": "newname",
    "content": "content",
    "description": "newdescription",
    "createTime": 1474455039000,
    "category": 20
    }
    ]
    }

2.NotePropertiesController

2.1 api/query/{plat}/note/{noteId}/noteProperties
  • 描述:获取note的设置
  • 请求方式:get
  • 参数:
  • 返回:
    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
    {
    "code": 200,
    "msg": "获取成功",
    "data": [
    {
    "id": 29,
    "noteId": 19,
    "propertyId": 21,
    "propertyValue": "jdbc:hive://hhhhhh",
    "isValid": 1,
    "createTime": 1474447899000,
    "propertyName": null
    },
    {
    "id": 30,
    "noteId": 19,
    "propertyId": 24,
    "propertyValue": "yuan",
    "isValid": 1,
    "createTime": 1474450331000,
    "propertyName": null
    },
    {
    "id": 32,
    "noteId": 19,
    "propertyId": 23,
    "propertyValue": "1000",
    "isValid": 1,
    "createTime": 1474450294000,
    "propertyName": null
    }
    ]
    }
2.2 api/query/{plat}/note/{noteId}/noteProperties
  • 描述:更新一个NoteProperties
  • 请求方式:PUT
  • 参数:
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [
    { "id":29,
    "propertyId":21,
    "propertyValue": "jdbc:hive://hhhhhh"
    },
    {
    "id":30,
    "propertyId":22,
    "propertyValue": "yuan"
    },
    {
    "id":0,
    "propertyId":23,
    "propertyValue": 1000
    }
    ]
  • 返回:

    1
    2
    3
    4
    5
    {
    "code": 200,
    "msg": "更新成功",
    "data": null
    }

3.NoteContentController

3.1 /api/query/{plat}/noteContents
  • 描述:收藏NoteContent
  • 请求方式:POST
  • 参数:

    name:newname
    description:newdescription
    content:select * from table
    
  • 返回:

    1
    2
    3
    4
    5
    {
    "code": 200,
    "msg": "保存成功",
    "data": 1
    }
3.2 /api/query/{plat}/noteContents/{noteContentId}
  • 描述:更新一个NoteContent
  • 请求方式:PUT
  • 参数:
    name:newname1
    description:newdescription1
    content:select name  from table
    
  • 返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {
    {
    "code": 200,
    "msg": "更新成功",
    "data": {
    "id": 1,
    "name": "newname1",
    "description": "newdescription1",
    "content": "select name from table",
    "resultSetId": 0,
    "userId": 100,
    "createTime": 1474439364000,
    "category": 0,
    "resultSet": null
    }
    }
3.3 /api/query/{plat}/noteContents/{noteContentId}
  • 描述:删除NoteContent
  • 请求方式:DELETE
  • 参数:
  • 返回:
    1
    2
    3
    4
    5
    {
    "code": 204,
    "msg": "删除成功",
    "data": "1"
    }
3.4 /api/query/{plat}/noteContents
  • 描述:获取用户该category下所有收藏的NoteContent
  • 请求方式:GET
  • 参数:无
  • 返回:

4.HiveQueryController

4.1 api/query/{plat}/user/note/run
  • 描述:执行语句
  • 参数:noteId content
  • 返回:
4.2 api/query/{plat}/user/note/cancel
  • 描述:取消语句的执行
  • 参数:userId noteId
  • 返回:
4.3 api/query/{plat}/note/timedRun
  • 描述:定时执行
  • 参数:noteId content time
  • 返回:

5.UdfController

5.1 /api/query/{plat}/udfs
  • 描述:新建自定义函数
  • 请求类型:POST
  • 参数:
    name:name
    description:description
    hdfsUrl:hdfsUrl
    
  • 返回:
    1
    2
    3
    4
    5
    {
    "code": 201,
    "msg": "新建成功",
    "data": 1
    }
5.2 /api/query/{plat}/udfs
  • 描述:获取自定义函数
  • 请求类型:GET
  • 参数:
  • 返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "code": 200,
    "msg": "获取成功",
    "data": [
    {
    "id": 1,
    "name": "name",
    "description": "description",
    "hdfsUrl": "hdfsUrl",
    "userId": 100,
    "category": 20,
    "createTime": 1474456371000
    }
    ]
    }
5.3 /api/query/{plat}/udfs/{id}
  • 描述:删除自定义函数
  • 请求类型:DELETE
  • 参数:
  • 返回:
    1
    2
    3
    4
    5
    {
    "code": 204,
    "msg": "删除成功",
    "data": "1"
    }

6.PropertyController(暂时用不着)

6.1 api/query/properties
  • 描述:批量新建参数
  • 请求类型:POST
  • 参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [
    {
    "name": "username",
    "description": "用户名",
    "isValid": 1,
    "category":20,//hive参数
    "defaultValue": "yuanjinyao",
    "initNeed": 1
    },
    {
    "name": "username",
    "description": "用户名",
    "isValid": 1,
    "category":30,//spark参数
    "defaultValue": "yuanjinyao",
    "initNeed": 1
    }
    ]
  • 返回:

    1
    2
    3
    4
    5
    {
    "code": 200,
    "msg": "新建成功",
    "data": 2
    }
6.2 api/query/properties/{propertiesId}
  • 描述:更新参数设置
  • 请求类型:PUT
  • 参数:
       name:newname
    description:newdescription
    defaultValue:defaultValue
    category:20
    initNeed:0
    isValid:1
    
  • 返回:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "code": 200,
    "msg": "更新成功",
    "data": {
    "id": 24,
    "name": "newname",
    "defaultValue": "defaultValue",
    "description": "newdescription",
    "category": 20,
    "initNeed": 0,
    "isValid": 1,
    "createTime": 1474376071000
    }
    }

Java动态代理和CGLib代理

发表于 2016-08-24   |   分类于 Java   |     |   阅读次数

Java动态代理

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
/**
* HelloWorld.java
*/
public interface HelloWorld {
public int say(String words);
}
/**
* HelloWorldImplements.java
*/
public class HelloWorldImplements implements HelloWorld {
public int say(String words) {
System.out.println("I am saying:"+words);
return 1;
}
}
/**
* HelloWorldHandler.java
*/
public class HelloWorldHandler implements InvocationHandler {
Object target;
public HelloWorldHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result=method.invoke(target,args);
System.out.println("after");
return result;
}
}
/**
* Main.java
*/
public class Main {
public static void main(String[] args) {
HelleWorld helleWorld = new HelloWorldImplements();
HelloWorldHandler helloWorldHandler = new HelloWorldHandler(helleWorld);
//HelleWorld proxy =(HelleWorld) Proxy.newProxyInstance(HelleWorld.class.getClassLoader(), HelloWorldImplements.class.getInterfaces(), helloWorldHandler);
HelleWorld proxy = (HelleWorld) Proxy.newProxyInstance(helleWorld.getClass().getClassLoader(), new Class[]{HelleWorld.class}, helloWorldHandler);
proxy.say("fuck the world");
}

JDK动态代理和CGLib动态代理的区别

  • jdk动态代理的对象需要实现一个接口,不能对类直接进行代理
  • CGLIB可以对类进行代理,基于继承去生成一个子类,在子类里对父类的方法进行覆盖,所以最好不要用final修饰方法
  • CGLIB的性能和JDK有所区别

笔试常见问题

发表于 2016-08-14   |   分类于 Java   |     |   阅读次数

scron表达式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

Seconds Minutes Hours DayofMonth Month DayofWeek Year或
Seconds Minutes Hours DayofMonth Month DayofWeek

每一个域可出现的字符如下:
Seconds:可出现”, - /“四个字符,有效范围为0-59的整数
Minutes:可出现”, -
/“四个字符,有效范围为0-59的整数
Hours:可出现”, - /“四个字符,有效范围为0-23的整数
DayofMonth:可出现”, -
/ ? L W C”八个字符,有效范围为0-31的整数
Month:可出现”, - /“四个字符,有效范围为1-12的整数或JAN-DEc
DayofWeek:可出现”, -
/ ? L C #”四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推
Year:可出现”, - * /“四个字符,有效范围为1970-2099年

/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着从第5分钟开始每20分钟触发一次

AtomicInteger的底层实现

发表于 2016-07-29   |   分类于 Java   |     |   阅读次数

悲观锁与乐观锁

我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换。切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来。在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻塞状态。等到资源(比如一个共享数据)可用了,那么就将线程唤醒,让他进入runnable状态等待cpu调度。这就是典型的悲观锁的实现。独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

但是,由于在进程挂起和恢复执行过程中存在着很大的开销。当一个线程正在等待锁时,它不能做任何事,所以悲观锁有很大的缺点。举个例子,如果一个线程需要某个资源,但是这个资源的占用时间很短,当线程第一次抢占这个资源时,可能这个资源被占用,如果此时挂起这个线程,可能立刻就发现资源可用,然后又需要花费很长的时间重新抢占锁,时间代价就会非常的高。

所以就有了乐观锁的概念,他的核心思路就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。在上面的例子中,某个线程可以不让出cpu,而是一直while循环,如果失败就重试,直到成功为止。所以,当数据争用不严重时,乐观锁效果更好。比如CAS就是一种乐观锁思想的应用。

java中CAS的实现

CAS就是Compare and Swap的意思,比较并操作。很多的cpu直接支持CAS指令。CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操作,并且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令。在java.util.concurrent.atomic包下面的所有的原子变量类型中,比如AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作。

在CAS操作中,会出现ABA问题。就是如果V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并需要重新执行算法中的步骤。有简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即使这个值由A变为B,然后为变为A,版本号也是不同的。AtomicStampedReference和AtomicMarkableReference支持在两个变量上执行原子的条件更新。AtomicStampedReference更新一个“对象-引用”二元组,通过在引用上加上“版本号”,从而避免ABA问题,AtomicMarkableReference将更新一个“对象引用-布尔值”的二元组。

AtomicInteger的实现

AtomicInteger 是一个支持原子操作的 Integer 类,就是保证对AtomicInteger类型变量的增加和减少操作是原子性的,不会出现多个线程下的数据不一致问题。如果不使用 AtomicInteger,要实现一个按顺序获取的 ID,就必须在每次获取时进行加锁操作,以避免出现并发时获取到同样的 ID 的现象。

接下来通过源代码来看AtomicInteger具体是如何实现的原子操作。

首先看incrementAndGet() 方法,下面是具体的代码。

1
2
3
4
5
6
7
8
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}

通过源码,可以知道,这个方法的做法为先获取到当前的 value 属性值,然后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,但是内部有一个死循环,不断去做compareAndSet操作,直到成功为止,也就是修改的根本在compareAndSet方法里面,compareAndSet()方法的代码如下:

1
2
3
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

compareAndSet()方法调用的compareAndSwapInt()方法的声明如下,是一个native方法。

1
publicfinal native boolean compareAndSwapInt(Object var1, long var2, int var4, intvar5);

compareAndSet 传入的为执行方法时获取到的 value 属性值,next 为加 1 后的值, compareAndSet所做的为调用 Sun 的 UnSafe 的 compareAndSwapInt 方法来完成,此方法为 native 方法,compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。所以基于 CAS 的操作可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。并且由于 CAS 操作是 CPU 原语,所以性能比较好。

类似的,还有decrementAndGet()方法。它和incrementAndGet()的区别是将 value 减 1,赋值给next 变量。

AtomicInteger中还有getAndIncrement() 和getAndDecrement() 方法,他们的实现原理和上面的两个方法完全相同,区别是返回值不同,前两个方法返回的是改变之后的值,即next。而这两个方法返回的是改变之前的值,即current。还有很多的其他方法,就不列举了。

Github学习

发表于 2016-07-21   |   分类于 编程工具   |     |   阅读次数
  1. 当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。 事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库。

  2. 忽略文件

    一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。 来看一个实际的例子:
    
    1
    2
    3
    $ cat .gitignore
    *.[oa]
    *~

    第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。 此外,你可能还需要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。 要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。

  3. 从远程仓库中抓取与拉取

    就如刚才所见,从远程仓库中获得数据,可以执行:

    1
    $ git fetch [remote-name] 和 git merge [remote-name]/ [local-name]

    这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。

    如果你使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch 命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。

  4. 下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支 origin/serverfix,指向服务器的 serverfix 分支的引用:

    1
    2
    3
    4
    5
    6
    7
    $ git fetch origin
    remote: Counting objects: 7, done.
    remote: Compressing objects: 100% (2/2), done.
    remote: Total 3 (delta 0), reused 3 (delta 0)
    Unpacking objects: 100% (3/3), done.
    From https://github.com/schacon/simplegit
    *[new branch] serverfix -> origin/serverfix

    要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的 serverfix 分支 - 只有一个不可以修改的 origin/serverfix 指针。

    可以运行 git merge origin/serverfix 将这些工作合并到当前所在的分支。 如果想要在自己的 serverfix 分支上工作,可以将其建立在远程跟踪分支之上:

    1
    2
    3
    $ git checkout -b serverfix origin/serverfix
    Branch serverfix set up to track remote branch serverfix from origin.
    Switched to a new branch 'serverfix'

    这会给你一个用于工作的本地分支,并且起点位于 origin/serverfix。

  5. 跟踪分支
    从一个远程跟踪分支检出一个本地分支会自动创建一个叫做 “跟踪分支”(有时候也叫做 “上游分支”)。 跟踪分支是与远程分支有直接关系的本地分支。 如果在一个跟踪分支上输入 git pull,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支。

    当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支。 然而,如果你愿意的话可以设置其他的跟踪分支 - 其他远程仓库上的跟踪分支,或者不跟踪 master 分支。 最简单的就是之前看到的例子,运行 git checkout -b [branch] [remotename]/[branch]。 这是一个十分常用的操作所以 Git 提供了 –track 快捷方式:

    1
    2
    3
    $ git checkout --track origin/serverfix
    Branch serverfix set up to track remote branch serverfix from origin.
    Switched to a new branch 'serverfix'

    如果想要将本地分支与远程分支设置为不同名字,你可以轻松地增加一个不同名字的本地分支的上一个命令:

    1
    2
    3
    $ git checkout -b sf origin/serverfix
    Branch sf set up to track remote branch serverfix from origin.
    Switched to a new branch 'sf'

    现在,本地分支 sf 会自动从 origin/serverfix 拉取。
    设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 –set-upstream-to 选项运行 git branch 来显式地设置。

    1
    2
    $ git branch -u origin/serverfix
    Branch serverfix set up to track remote branch serverfix from origin.

    常用命令:

    1
    2
    3
    4
    5
    6
    7
    8
    git init
    git add index.txt
    git commit -m
    git remote add origin https//github.com
    git push origin master 跟新到origin的master分支
    git pull origin master 从origin的master从跟新
    git fetch origin
    git checkout --track origin/master

JVM性能调优

发表于 2016-07-18   |   分类于 Java   |     |   阅读次数

转自:http://my.oschina.net/chape/blog/200790
http://www.cnblogs.com/gw811/archive/2012/10/19/2730258.html

1) Heap Size

-Xmx —最大Heap Size,即上图的Total size(包括Eden+form+to,Tenured,不包含Perm,见上图),限制了年轻代和年老代的可分配最大值;

-Xms —初始化分配的Heap Size

生产环境中ms一般设置成跟mx相等,因为若ms不等于mx那么在某些场景下JVM可能需要对Heap Size进行频繁的扩展和收缩,增加处理时间;

2)New/Young Generation Size

-Xmn —最大年轻代大小,即上图中的Eden+S0+S1+Virtual

-XX:NewSize —初始化年轻代大小,即上图中的Eden+S0+S1,在只设置了-Xmn不设置-XX:NewSize的情况下,NewSize等于mn。

生产环境中一般只需设置-Xmn或者设置mn和NewSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;

3)Old Generation Size (Tenured)

-XX:NewRatio — Old Size/New Size,通过年老代和年轻代的比例和Heap Size就可以算出年老代的大小。一般默认为8,若Heap Size为1024m,则 NewSize=HeapSize/(NewRatio+1)=114m,OldSize=HeapSize-NewSize=910m;

注意:-Xmn的优先级比-XX:NewRatio高,若-Xmn已指定,则OldSize=HeapSize-NewSize,无需再按比例计算。生产环境中一般只需指定-Xmn就足够了。

4)Eden和S0、S1

-XX:SurvivorRatio — Eden/S0,即 Eden区和S0的比例,默认为8,若NewSize为114m,则S0=NewSize/(SurvivorRatio+2)=11.4m;

S0==S1,S0、S1的职能是一模一样的,又叫做From space和To space,在每一次minor gc后角色会交换。

注意:-XX类型的选项在不同的JDK版本或实现中定义可能有所区别,在近日的实践中发现,

在Linux jdk_1_5_0_10_x86版本中,SurvivorRatio=(YoungSize/S0),而Linux jdk_1_5_0_20_x64版本中,SurvivorRatio=(Eden/S0)

所以,我们在实际的工程实践中还是应该用jmap -heap输出的jvm内存结构信息为准,不要想当然。

5)Permanent Generation Size

-XX:MaxPermSize —最大持久代大小,默认为64m;

-XX:PermSize —初始化持久代大小,默认为16m;

生产环境中一般设置MaxPermSize和PermSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;

当应用引用的类比较多或者应用了一些动态类生产技术时应该加大该区的值,一般256m对服务器程序都很足够了。

6)Thread Stack Size

-Xss —线程堆栈大小,一般用于存放方法入口参数和返回值,以及原子类型的本地变量(即方法内部变量);

一般可设置为128k.

7)Direct Memory Size

-XX:MaxDirectMemorySize —direct byte buffer用到的本地内存,在本blog的一篇《xsocket内存泄漏》文章中介绍过该参数的作用。默认跟mx相等,所以生产环境中一般不设置mx大于物理内存的一半。

GC过程

在讲述GC过程前我先解释一下JVM的两个控制参数:

-XX:TargetSurvivorRatio — Survivor Space最大使用率,若存放对象的总大小超过该值,将引起对象向Old区迁移;

-XX:MaxTenuringThreshold — Young区对象的最大任期阀值,即可经历minor gc的次数,超过该次数的对象直接迁移到Old区;

实际的TenuringThreshold由JVM通过Survivor Space的占用率和TargetSurvivorRatio动态计算,详情请查看参考资料。

引用计数算法

  很多教科书判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的。笔者面试过很多的应届生和一些有多年工作经验的开发人员,他们对于这个问题给予的都是这个答案。

  客观地说,引用计数算法(Reference Counting)的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软的COM(Component Object Model)技术、使用ActionScript 3的FlashPlayer、Python语言以及在游戏脚本领域中被广泛应用的Squirrel中都使用了引用计数算法进行内存管理。但是,Java语言中没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用的问题。   

根搜索算法

  在主流的商用程序语言中(Java和C#,甚至包括前面提到的古老的Lisp),都是使用根搜索算法(GC Roots Tracing)判定对象是否存活的。这个算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如图3-1所示,对象object 5、object 6、object7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。

  在Java语言里,可作为GC Roots的对象包括下面几种:

    虚拟机栈(栈帧中的本地变量表)中的引用的对象。

    方法区中的类静态属性引用的对象。

    方法区中的常量引用的对象。

    本地方法栈中JNI(即一般说的Native方法)的引用的对象。     

引用

  无论是通过引用计数算法判断对象的引用数量,还是通过根搜索算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。在JDK 1.2之前,Java中的引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

  在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(WeakReference)、虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。

  强引用就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

  软引用用来描述一些还有用,但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。

  弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。

  虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。   

Java面试常见问题总结

发表于 2016-07-03   |   分类于 Java   |     |   阅读次数

  1. 自我介绍
    让对方在最短的时间里了解你的教育背景,实习经历,工作经验,兴趣爱好,职业规划,性格特征,以及你的优势等等
  2. 项目
    项目简要介绍,你的主要工作,主要技术,难点,解决方案,心得体会
  3. 运行时多态的解释:a.运行时多态是指程序中定义的引用变量所指向的具体类型和b.通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Father{
    void say(){
    System.out.println("father");
    }
    }
    class Son extends Father{
    void say(){
    System.out.println("son");
    }
    void run(){
    ...;
    }
    }
    Father son=new Son();
    son.run();//编译出错
























  1. TCP的三次握手和四次挥手的补充
    三次握手

    * 第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。
    * 第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。
    * 第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。
    

    完成三次握手,客户端与服务器开始传送数据。

    确认号:其数值等于发送方的发送序号 +1(即接收方期望接收的下一个序列号)。
    

    四次挥手

    * 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。 
    * 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。 
    * 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。 
    * 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。 
    

    CLOSE_WAIT

    发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。

    TIME_WAIT

    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。

  2. 为什么需要TIME_WAIT?

    TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。

  3. 主动发起关闭连接的操作的一方将达到TIME_WAIT状态,而且这个状态要保持Maximum Segment Lifetime的两倍时间。为什么要这样做而不是直接进入CLOSED状态?

    原因有二:

    一、保证TCP协议的全双工连接能够可靠关闭
    二、保证这次连接的重复数据段从网络中消失

    先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。

    再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。
    参考文献:TCP的三次握手和四次挥手



  1. java类加载过程:
    首先是加载:

    这一块虚拟机要完成3件事:

    通过一个类的全限定名来获取定义此类的二进制字节流。
    
    将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    
    在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。 
    

    然后是链接(验证,准备,解释),初始化
    检验的目的:确保class文件的字节流信息符合jvm的口味,不会让jvm感到不舒服。假如class文件是由纯粹的java代码编译过来的,自然不会出现类似于数组越界、跳转到不存在的代码块等不健康的问题,因为一旦出现这种现象,编译器就会拒绝编译了。但是,跟之前说的一样,Class文件流不一定是从java源码编译过来的,也可能是从网络或者其他地方过来的,甚至你可以自己用16进制写,假如jvm不对这些数据进行校验的话,可能一些有害的字节流会让jvm完全崩溃。

检验主要经历几个步骤:文件格式验证->元数据验证->字节码验证->符号引用验证
用Class.forName(String className);来加载类的时候,也会执行初始化动作。注意:ClassLoader的loadClass(String className);方法只会加载并编译某类,并不会对其执行初始化。

  1. Java序列化和transient

拦截器是基于java的反射机制的,而过滤器是基于函数回调。  
拦截器不依赖与servlet容器,过滤器依赖与servlet容器。  
拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。 
拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

有关return和finally

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
package jinyuanyao.com;
/**
* Hello world!
*/
public class App {
static String result = "";
public static void main(String[] args) {
System.out.println("Hello World!");
System.out.println(method(1));
// the output is 2
method(0);
System.out.print(result);
// the output is 2334
}
public static String method(int i) {
try {
if (i == 1) {
throw new Exception();
}
} catch (Exception e) {
result += "2";
return result;
} finally {
result += "3";
}
result += "4";
return "";
}
}

总结:
1、不管有木有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

123
darrenyjy

darrenyjy

Every step leaves its print

25 日志
9 分类
19 标签
© 2016 darrenyjy
由 Hexo 强力驱动
主题 - NexT.Pisces