Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 11805 articles
Browse latest View live

Java多线程(二)同步

$
0
0

如果你的java基础较弱,或者不大了解java多线程请先看这篇文章 java多线程(一)线程定义、状态和属性

同步一直是java多线程的难点,在我们做android开发时也很少应用,但这并不是我们不熟悉同步的理由。希望这篇文章能使更多的人能够了解并且应用java的同步。
在多线程的应用中,两个或者两个以上的线程需要共享对同一个数据的存取。如果两个线程存取相同的对象,并且每一个线程都调用了修改该对象的方法,这种情况通常成为竞争条件。
竞争条件最容易理解的例子就是:比如火车卖票,火车票是一定的,但卖火车票的窗口到处都有,每个窗口就相当于一个线程,这么多的线程共用所有的火车票这个资源。并且无法保证其原子性,如果在一个时间点上,两个线程同时使用这个资源,那他们取出的火车票是一样的(座位号一样),这样就会给乘客造成麻烦。解决方法为,当一个线程要使用火车票这个资源时,我们就交给它一把锁,等它把事情做完后在把锁给另一个要用这个资源的线程。这样就不会出现上述情况。

1. 锁对象
synchronized关键字自动提供了锁以及相关的条件,大多数需要显式锁的情况使用synchronized非常的方便,但是等我们了解ReentrantLock类和条件对象时,我们能更好的理解synchronized关键字。ReentrantLock是JAVA SE 5.0引入的, 用ReentrantLock保护代码块的结构如下:

mLock.lock();
try{
...
}
finally{
mLock.unlock();
}

这一结构确保任何时刻只有一个线程进入临界区,一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,它们则被阻塞直到第一个线程释放锁对象。把解锁的操作放在finally中是十分必要的,如果在临界区发生了异常,锁是必须要释放的,否则其他线程将会永远阻塞。

2. 条件对象
进入临界区时,却发现在某一个条件满足之后,它才能执行。要使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程,条件对象又称作条件变量。
我们来看看下面的例子来看看为何需要条件对象

假设一个场景我们需要用银行转账,我们首先写了银行的类,它的构造函数需要传入账户数量和账户金额

public class Bank {
private double[] accounts;
    private Lock bankLock;
    public Bank(int n,double initialBalance){
        accounts=new double[n];
        bankLock=new ReentrantLock();
        for (int i=0;i<accounts.length;i++){
            accounts[i]=initialBalance;
        }
    }
    }

接下来我们要提款,写一个提款的方法,from是转账方,to是接收方,amount转账金额,结果我们发现转账方余额不足,如果有其他线程给这个转账方再存足够的钱就可以转账成功了,但是这个线程已经获取了锁,它具有排他性,别的线程也无法获取锁来进行存款操作,这就是我们需要引入条件对象的原因。

   public void transfer(int from,int to,int amount){
        bankLock.lock();
        try{
            while (accounts[from]<amount){
                //wait
            }
        }finally {
            bankLock.unlock();
        }
    }

一个锁对象拥有多个相关的条件对象,可以用newCondition方法获得一个条件对象,我们得到条件对象后调用await方法,当前线程就被阻塞了并放弃了锁

public class Bank {
private double[] accounts;
    private Lock bankLock;
    private Condition condition;
    public Bank(int n,double initialBalance){
        accounts=new double[n];
        bankLock=new ReentrantLock();
        //得到条件对象
        condition=bankLock.newCondition();
        for (int i=0;i<accounts.length;i++){
            accounts[i]=initialBalance;
        }
    }
    public void transfer(int from,int to,int amount) throws InterruptedException {
        bankLock.lock();
        try{
            while (accounts[from]<amount){
                //阻塞当前线程,并放弃锁
                condition.await();
            }
        }finally {
            bankLock.unlock();
        }
    }
}

等待获得锁的线程和调用await方法的线程本质上是不同的,一旦一个线程调用的await方法,他就会进入该条件的等待集。当锁可用时,该线程不能马上解锁,相反他处于阻塞状态,直到另一个线程调用了同一个条件上的signalAll方法时为止。当另一个线程准备转账给我们此前的转账方时,只要调用condition.signalAll();该调用会重新激活因为这一条件而等待的所有线程。
当一个线程调用了await方法他没法重新激活自身,并寄希望于其他线程来调用signalAll方法来激活自身,如果没有其他线程来激活等待的线程,那么就会产生死锁现象,如果所有的其他线程都被阻塞,最后一个活动线程在解除其他线程阻塞状态前调用await,那么它也被阻塞,就没有任何线程可以解除其他线程的阻塞,程序就被挂起了。
那何时调用signalAll呢?正常来说应该是有利于等待线程的方向改变时来调用signalAll。在这个例子里就是,当一个账户余额发生变化时,等待的线程应该有机会检查余额。

 public void transfer(int from,int to,int amount) throws InterruptedException {
        bankLock.lock();
        try{
            while (accounts[from]<amount){
                //阻塞当前线程,并放弃锁
                condition.await();
            }
            //转账的操作
            ...
            condition.signalAll();
        }finally {
            bankLock.unlock();
        }
    }

当调用signalAll方法时并不是立即激活一个等待线程,它仅仅解除了等待线程的阻塞,以便这些线程能够在当前线程退出同步方法后,通过竞争实现对对象的访问。还有一个方法是signal,它则是随机解除某个线程的阻塞,如果该线程仍然不能运行,那么则再次被阻塞,如果没有其他线程再次调用signal,那么系统就死锁了。

3. Synchronized关键字
Lock和Condition接口为程序设计人员提供了高度的锁定控制,然而大多数情况下,并不需要那样的控制,并且可以使用一种嵌入到java语言内部的机制。从Java1.0版开始,Java中的每一个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。
换句话说,

public synchronized void method(){

}

等价于

public void method(){
this.lock.lock();
try{

}finally{
this.lock.unlock();
}

上面银行的例子,我们可以将Bank类的transfer方法声明为synchronized,而不是使用一个显示的锁。
内部对象锁只有一个相关条件,wait放大添加到一个线程到等待集中,notifyAll或者notify方法解除等待线程的阻塞状态。也就是说wait相当于调用condition.await(),notifyAll等价于condition.signalAll();

我们上面的例子transfer方法也可以这样写:

    public synchronized void transfer(int from,int to,int amount)throws InterruptedException{
        while (accounts[from]<amount) {
            wait();
        }
        //转账的操作
        ...
        notifyAll();   
        }

可以看到使用synchronized关键字来编写代码要简洁很多,当然要理解这一代码,你必须要了解每一个对象有一个内部锁,并且该锁有一个内部条件。由锁来管理那些试图进入synchronized方法的线程,由条件来管理那些调用wait的线程。

4. 同步阻塞
上面我们说过,每一个Java对象都有一个锁,线程可以调用同步方法来获得锁,还有另一种机制可以获得锁,通过进入一个同步阻塞,当线程进入如下形式的阻塞:

synchronized(obj){

}

于是他获得了obj的锁。再来看看Bank类

public class Bank {
private double[] accounts;
private Object lock=new Object();
   public Bank(int n,double initialBalance){
        accounts=new double[n];
        for (int i=0;i<accounts.length;i++){
            accounts[i]=initialBalance;
        }
    }
    public void transfer(int from,int to,int amount){
        synchronized(lock){
          //转账的操作
            ...
        }
    }
}

在此,lock对象创建仅仅是用来使用每个Java对象持有的锁。有时开发人员使用一个对象的锁来实现额外的原子操作,称为客户端锁定。例如Vector类,它的方法是同步的。现在假设在Vector中存储银行余额

  public void transfer(Vector<Double>accounts,int from,int to,int amount){
  accounts.set(from,accounts.get(from)-amount);
  accounts.set(to,accounts.get(to)+amount;
}

Vecror类的get和set方法是同步的,但是这并未对我们有所帮助。在第一次对get调用完成以后,一个线程完全可能在transfer方法中被被剥夺运行权,于是另一个线程可能在相同的存储位置存入了不同的值,但是,我们可以截获这个锁

  public void transfer(Vector<Double>accounts,int from,int to,int amount){
  synchronized(accounts){
  accounts.set(from,accounts.get(from)-amount);
  accounts.set(to,accounts.get(to)+amount;
  }
}

客户端锁定(同步代码块)是非常脆弱的,通常不推荐使用,一般实现同步最好用java.util.concurrent包下提供的类,比如阻塞队列。如果同步方法适合你的程序,那么请尽量的使用同步方法,他可以减少编写代码的数量,减少出错的几率,如果特别需要使用Lock/Condition结构提供的独有特性时,才使用Lock/Condition。

作者:itachi85 发表于2015/12/15 14:04:20 原文链接
阅读:102 评论:0 查看评论

电力企业信息化系统主要报表展示

$
0
0

报送内容

年报

装机规模及参数

装机规模指的是一个发电厂或者区域电网的发电能力。本表是由电力公司的调通中心负责填写,因此表中所体现的装机规模,即本公司向其它的发电厂或者电网调度的电量总量数据,以及电量的类型,接入电压等级等具体参数。通过对这些数据进行不同维度进行排列组合,使用户能够一目了然的掌握本公司整个年度的电量调度情况,从中发现是否存在诸如由于本单位的相关电厂产能不足,而必须从外网调入大量的电力,或者电量的调度多发生于省调以及省调以下的单位,说明本年度网内总的电力缺口不大等问题。

输电规模及参数

输电顾名思义就是指电能的传输。电能的传输有不同的电压等级,其中220-750kv的是超高压电网,构成大区电网的骨架和大区电网的联络线。110-220kv是输电网,构成复杂的输电网络,在各地区间传输电能。35-110kv是供电网,由枢纽变电站送电到靠近负荷区的本地变电站。10kv是配电网,本地变电站送电到居民区的杆上变压器或者配电室。本表统计的是超高压电网的输电规模及参数,即本年度进行调度的电量分别使用了多少条输电线路,以及线路传输长度。电力传输的具体线路参数。

变电规模及参数

变电指的是电压由高降低或者由低到高的变化,高压电网传输过来的电力必须经过各种变电站降压后才能作为居民用电传送到居民家中。因此对于调度的电量涉及的变电相关数据进行统计。通过本表了解本年度的变电情况,是否需要酌情增加变电站/变压器数量等。

设备检修计划

年度设备检修计划,由调通中心和生产技术部联合填报。调通中心负责制定直调机组的检修计划,生产技术部负责制定输电线路和变电设备的检修计划。主要通过计划的数据发现对辖区内电网运行方式的影响。

设备投产退役计划

各种设备年度投产退役计划,包括发电设备和输电设备,由基建部制定,同样是通过计划的数据来掌握设备的情况对电网运行的影响。

设备投产退役情况

和设备投产退役计划相对的设备投产退役实际数据,上一年度的设备投产退役实际情况,由生产技术部负责。

月报

电力电量平衡计划

内容包括预计最高负荷,跨地区联络线送受电力,辖区内机组铭牌出力,最高负荷时的检修容量、受阻容量及原因、最大可调出力、备用容量;预计用电量、跨地区联络线送受电量,辖区内机组发电量;制定电力电量平衡计划有两个作用,一是可以帮助用户确定在何时何地建立何种类型的发电厂,二是在满足负荷需求并达到各种经济指标的前提下,使规划期内电力系统能够安全运行且投资经济合理。

电网阻塞预计

输电网是电能交易环节的重要组成部分。当支路传输容量不能满足交易需求,或者当系统中发电机组、线路或变压器等元件故障时,将可能导致部分输电元件过载,即产生输电阻塞。输电阻塞会影响电网运行安全。本表是调度中心对电网阻塞情况的预计,了解原因,帮助网方采取措施合理分配电力电量,避免产生阻塞。

电力电量平衡情况

与电力电量平衡计划相对应的,事后报送的上年度实际电力电量平衡情况。

发电设备利用情况

每台机组平均负荷率、最高负荷日负荷率、最低负荷日负荷率,全网平均运行备用容量,最大运行备用容量,最小运行备用容量;“发电设备利用小时数”,指发电机组在一年内平均(满负荷)运行时间;在每年的8760个小时中,发电小时数所占的比重。电力行业是资产密集型产业,扣除设备检修等必要的停机时间,发电小时数越高,设备所创造的经济价值越高。因此,发电设备的利用情况对投资者的经济效益影响巨大,也是衡量电力行业效率的重要指标。

设备检修变更情况

辖区内发电设备检修计划、输变电设备检修计划及对电网运行方式的影响;

断面线路主变超限情况

电网输电线路和断面的超稳定限额运行情况、变压器超控制限额运行情况;

可再生能源受阻情况

可再生能源电厂受阻电量及原因;受化石能源资源有限性和气候变化等环境问题的影响,加快开发利用可再生能源是一个趋势,但是由于可再生能源具有能量密度低、存在间歇不连续的问题,必须采取技术措施解决可再生能源发电的不连续性与用电需求连续性之间的问题,本表主要采集的就是可再生能源电厂受阻的电量数据,以及原因,方便做出调控决策。

设备故障情况

掌握设备故障次数,故障时间,便于了解设备的故障情况对电网运行的影响;

主要输电线路潮流
主要输电线路最大潮流、平均潮流。通过相关数据,对系统的正常运行有一个全面的了解,一方面在发生事故时能够做到更加准确的判断,另一方面在系统不正常运行状态时能够及时采取有效的措施,包括避免某条线路过负荷等。

作者:yuanziok 发表于2015/12/15 15:59:08 原文链接
阅读:187 评论:0 查看评论

时间序列分段算法 [Time series Breakout Detection]

$
0
0

在时间序列分析中,断点检测(breakout detection)是一个很基本的问题。

通过捕捉时序数据中的断点(breakout),来发现时序数据所表示的系统在过去是否发生了某种事件(event),进而为系统诊断提供必要的数据支持。

 

为了实现对时序断点的检测,我们首先需要对时序的整体时序做拟合。

这里我们通过一条直线来拟合一段时序,如果时序的趋势发生了变化,则用多条直线来拟合整条时序数据。

如下是对一条波动规律明显的时序做拟合之后的结果。

每个红色线条的转折点,就是我们找到的断点。

 

以上数据是我们在实验环境下,为了检测算法效果而人工构造的一条时序。

那么,该算法在实际情况下表现如何?

一下是一条实际的股票价格时序数据。我们通过该算法进行断点检测,并将断点红红色线条连起来的效果:

 

 

 

算法介绍:

算法所使用的关键即使:

1. 单变量线性回归,用来拟合某一段时序

2. 动态规划算法,  用来全局最大化断点检测效果。

 

算法核心代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "lsp.h"

static double loss(double * s, int n){
    int i;
    double t;
    double g0   = 0.0, g1   = 0.0; 
    double h00  = 0.0, h01  = 0.0, h10  = 0.0, h11  = 0.0;
    double hv00 = 0.0, hv01 = 0.0, hv10 = 0.0, hv11 = 0.0;
    double l0, l1;

    // grad and hessian matrix
    for (i = 0; i < n; i++){
        t    = s[i];
        g0  += t;
        g1  += t * (1.0 + i);
        h00 += 1.0;
        h01 += 1.0 + i;
        h11 += (1.0 + i) * (1.0 + i);
    }
    h10 = h01;

    // inverse of hessian
    t = h00 * h11 - h01 * h10;
    hv00 = h11 / t;
    hv01 = hv10 = -h01 / t;
    hv11 = h00 /t;

    // the theta
    l0 = hv00 * g0 + hv01 * g1;
    l1 = hv10 * g0 + hv11 * g1;

    // sqare loss
    t = 0.0;
    for (i = 0; i < n; i++){
        t += (l0 + l1 * (i + 1) - s[i]) * (l0 + l1 * (i + 1) - s[i]);
    }
    return t;
}

int * lsp(double * ts, int n, int min_size, double beta, int *ol){

    if (!ts || min_size < 2 || n < 2 * min_size || !ol){
        return NULL;
    }

    // prev breakout point
    int * prev = (int*)malloc(sizeof(int) * (n + 1));
    memset(prev, 0, sizeof(int) * (n + 1));

    // number of breakout point
    int * num  = (int*)malloc(sizeof(int) * (n + 1));
    memset(num, 0, sizeof(int) * (n + 1));

    // F scores
    double * F = (double*)malloc(sizeof(double) * (n + 1));
    memset(F, 0, sizeof(double) * (n + 1));

    // loss 
    double * lossv = (double*)malloc(sizeof(double) * (n + 1));
    memset(lossv, 0, sizeof(double) * (n + 1));

    for (int s = 2 * min_size; s < n + 1; ++s){
        for (int t = min_size; t < s - min_size + 1; ++t){
            //double ls = loss(ts + prev[t], t - prev[t]);
            double ls = lossv[t];
            double rs = loss(ts + t, s - t);
            double as = loss(ts + prev[t], s - prev[t]);
            double score = (as - ls - rs) * (t - prev[t]) * (s - t) /    \
                           ((s - prev[t]) * (s - prev[t])) - num[t] * beta;
            score += F[t];
            if (score > F[s]){
                num[s] = num[t] + 1;
                F[s] = score;
                prev[s] = t;
                lossv[s] = rs;
            }
        }
    }

    int k = num[n];
    *ol = k;
    int * re = (int*)malloc(sizeof(int) * k);
    memset(re, 0, sizeof(int) * k);
    int i = n;
    while(i > 0){
        if (prev[i])
            re[--k] = prev[i];
        i = prev[i];
    }

    free(prev);  prev  = NULL;
    free(num);   num   = NULL;
    free(F);     F     = NULL;
    free(lossv); lossv = NULL;
    return re;
}

 

算法复杂度上限为:O(n * n * n), 如果时序中的断点明显,切规律则算法复杂度接近 O(n * n)。

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Android中WebView页面交互

$
0
0

代码

在android内打开一个网页的时候,有时我们会要求与网页有一些交互。而这些交互是在基于javaScript的基础上。那么我们来学习一下android如何与网页进行JS交互。完整代码如下:

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.JavascriptInterface;
import android.webkit.URLUtil;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import org.json.JSONObject;

/**
 * 软件内通用打开网页的容器页面
 *
 * @author ZRP
 */
public class CommonWebActivity extends BaseActivity {

    protected CustomFrameLayout customFrameLayout;
    protected TextView errorTxt;
    protected View refresh;// 刷新按钮
    protected WebView webView;

    protected String url = "";// 网址url
    protected String param = "";// 交互参数,如json字符串

    protected WebChromeClient chromeClient = new WebChromeClient() {
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                // 判断有无网络
                if (!NetUtil.isAvaliable()) {
                    customFrameLayout.show(R.id.common_net_error);
                    refresh.setVisibility(View.VISIBLE);
                    refresh.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            webView.loadUrl(url);
                        }
                    });
                } else {
                    // 判断网络请求网址是否有效
                    if (!URLUtil.isValidUrl(url)) {
                        customFrameLayout.show(R.id.common_net_error);
                        errorTxt.setText("无效网址");
                    } else {
                        customFrameLayout.show(R.id.common_web);
                    }
                }
            }
        }

        // 获取到url打开页面的标题
        public void onReceivedTitle(WebView view, String title) {
            setBackView(R.id.back_view, title);
        }

        // js交互提示
        public boolean onJsAlert(WebView view, String url, String message, android.webkit.JsResult result){
            return super.onJsAlert(view, url, message, result);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        url = getIntent().getStringExtra("url");
        param = getIntent().getStringExtra("param");

        setContentView(R.layout.common_web_activity);
        initView();
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void initView() {
        setBackView(R.id.back);

        customFrameLayout = (CustomFrameLayout) findViewById(R.id.web_fram);
        customFrameLayout.setList(new int[]{R.id.common_web, R.id.common_net_error, R.id.common_loading});
        customFrameLayout.show(R.id.common_loading);
        refresh = findViewById(R.id.error_btn);
        errorTxt = (TextView) findViewById(R.id.error_txt);
        webView = (WebView) findViewById(R.id.common_web);

        webView.getSettings().setDefaultTextEncodingName("utf-8");
        webView.getSettings().setJavaScriptEnabled(true);
        synCookies();//格式化写入cookie,需写在setJavaScriptEnabled之后
        webView.setWebChromeClient(chromeClient);
        webView.setWebViewClient(new WebViewClient() {// 让webView内的链接在当前页打开,不调用系统浏览器
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        webView.addJavascriptInterface(new JavaScriptInterface(), "zrp");
        webView.loadUrl(url);
    }

    /**
     * CookieManager会将这个Cookie存入该应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中
     * 需要在当前用户退出登录的时候进行清除
     */
    private void synCookies() {
    String[] split = App.cookie.split(";");
    for (int i = 0; i < split.length; i++) {
        CookieManager.getInstance().setCookie(url, split[i]);
    }
   }

    /**
     * 回调网页中的脚本接口。
     *
     * @param notify 传给网页的通知内容。
     */
    public void onNotifyListener(String notify) {
        if (webView != null) {
            webView.loadUrl("javascript:onCommandListener('" + notify + "')");
        }
    }

    /**
     * android js交互实现
     * String ret = zrp.command("");
     * <p/>
     * webView.loadUrl("javascript:onCommandListener('param')");
     */
    public class JavaScriptInterface {

        @JavascriptInterface
        public void command(String jsonString) {
            if (TextUtils.isEmpty(jsonString)) {
                return ;
            }

            //根据网页交互回传的json串进行操作

        }
    }
}

其中CustomFrameLayout为界面切换控件,分别在无网络,网址错误等情况下进行提示:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

/**
 * 用于状态切换的布局,只显示1个状态
 */
public class CustomFrameLayout extends FrameLayout {
    private int[] list;

    public CustomFrameLayout(Context context) {
        super(context);
        initView();
    }

    public CustomFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public CustomFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    /**
     * 设置子面板id数组
     *
     * @param list
     */
    public void setList(int[] list) {
        this.list = list;
        show(0);
    }

    /**
     * 显示某个面板
     *
     * @param id
     */
    public void show(int id) {
        if (list == null) {
            for (int i = 0; i < getChildCount(); ++i) {
                View view = getChildAt(i);

                if (id == view.getId()) {
                    view.setVisibility(View.VISIBLE);
                } else {
                    view.setVisibility(View.GONE);
                }
            }

            return;
        }

        for (int aList : list) {
            View item = findViewById(aList);
            if (item == null) {
                continue;
            }
            if (aList == id) {
                item.setVisibility(View.VISIBLE);
            } else {
                item.setVisibility(View.GONE);
            }
        }
    }

    /**
     * 隐藏所有面板
     */
    public void GoneAll() {
        if (list == null) {
            for (int i = 0; i < getChildCount(); ++i) {
                View view = getChildAt(i);

                view.setVisibility(View.GONE);
            }

            return;
        }

        for (int aList : list) {
            View item = findViewById(aList);
            if (item == null) {
                continue;
            }
            item.setVisibility(View.GONE);
        }
    }

    /**
     * 切换。
     * @param index
     */
    public void showOfIndex(int index) {
        for (int i = 0; i < getChildCount(); ++i) {
            View view = getChildAt(i);

            if (index == i) {
                view.setVisibility(View.VISIBLE);
            } else {
                view.setVisibility(View.GONE);
            }
        }
    }

    public void initView() {

    }
}

界面布局为:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" ><include layout="@layout/common_back" /><com.zrp.test.CustomFrameLayout
        android:id="@+id/web_fram"
        android:layout_width="match_parent"
        android:layout_height="match_parent" ><WebView
            android:id="@+id/common_web"
            android:layout_width="match_parent"
            android:layout_height="match_parent" /><include layout="@layout/common_net_error" /><include layout="@layout/common_loading" /></com.zrp.test.CustomFrameLayout></LinearLayout>

测试分析

好了,这时候我们来添加一些数据进行测试:

用上面的方法为web页面中的param赋值,在asset文件夹中放入要进行测试的html文件,url赋值为: url = "file:///android_asset/html.html";
如上html.html文件源码为:

<!DOCTYPE html><html><head><title>测试容器调用</title></head><body><div class="main"><button onclick="test2()">调用容器方法</button></div><script type="text/javascript">
   function test(){
       alert("容器调用web方法");
   }
    function test2(){
        alert("test2() begin");
        zrp.command("{'type':1,'text':'hello boy'}");
        alert("test2() end");
    }</script></body></html>

java调用js:可通过在java代码中异步执行 webView.loadUrl("javascript:test()");来调用网页代码中的test()方法。

js调用java:在网页代码中可以看到button的点击事件添加了 zrp.command("{'type':1,'text':'hello boy'}");来回传一个json串给java页面,即js通过以上的方法调用了java的方法。

如果webView展示页面的时候需要给网页传递cookie,即各种登录信息,则需要在调用webView.loadUrl(url)之前一句调用如下方法: 注意!多个cookie值必须多次进行setCookie!

设置cookie方法一:

/**
 * CookieManager会将这个Cookie存入该应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中
 * 需要在当前用户退出登录的时候进行清除
 */
private void synCookies() {
    CookieSyncManager.createInstance(this);
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.setAcceptCookie(true);
    cookieManager.removeSessionCookie();

    String[] split = App.cookie.split(";");
    for (int i = 0; i < split.length; i++) {
        cookieManager.setCookie(url, split[i]);
    }
    CookieSyncManager.getInstance().sync();
}

在当前用户退出登录的时候清除掉保存的cookie信息。

/**
 * 清除当前用户存储到cookies表中的所有数据
 */
private void removeCookie() {
    CookieSyncManager.createInstance(this);
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.removeAllCookie();
    CookieSyncManager.getInstance().sync();
}

注意: 在调用设置Cookie之后不能再设置如下的这类属性,否则设置cookie无效。

webView.getSettings().setBuiltInZoomControls(true);  
webView.getSettings().setJavaScriptEnabled(true);  

但是!在如上设置cookie之后,每次设置cookie的时候都有延时,要是点击频率较快的时候,会出现cookie没有被设置进去的问题!

设置方法二:
经过一番查找,找到如下两个解决办法:

  1. http://blog.csdn.net/swust_chenpeng/article/details/37699841,这个是开了一个异步任务来进行等待,然后再次设置cookie,感觉没有从根源上解决这个问题啊。。。但是思路值得参考

  2. http://stackoverflow.com/questions/22637409/android-how-to-pass-cookie-to-load-url-with-webview,有用!设置之后立竿见影。

/**
 * CookieManager会将这个Cookie存入该应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中
 * 需要在当前用户退出登录的时候进行清除
 */
private void synCookies() {
    String[] split = App.cookie.split(";");
    for (int i = 0; i < split.length; i++) {
        CookieManager.getInstance().setCookie(url, split[i]);
    }
}

同时,在退出登录的时候清除cookie, CookieManager.getInstance().removeAllCookie();,清除当前用户存储到cookies表中的所有数据

REDIS与MYSQL实现标签的对比

$
0
0
这里来演示下REDIS和MYSQL之间的数据转换问题,REDIS 是典型的KEY -VALUE型NOSQL数据库,并且提供了额外丰富的数据类型。这里简单列举了标签类型的应用问题。


比如在MySQL里面,对内容的标签有以下简单的几张表,我这里只列出来拆分过后的表结构


第一,MySQL部分,


内容表:
CREATE TABLE `content` (
  `id` int(10) unsigned NOT NULL, -- 内容ID,唯一。
  `name` varchar(60) DEFAULT NULL, -- 内容的名字
  `created_timestamp` timestamp NULL DEFAULT NULL, -- 内容的加入时间
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1


标签表:
CREATE TABLE `tag` (
  `tag_name` varchar(60) NOT NULL, -- 标签名字,唯一
  `visit_count` int(10) unsigned NOT NULL DEFAULT '0', -- 标签的访问次数
  PRIMARY KEY (`tag_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1


内容与标签的关系,多对多。


CREATE TABLE `content_tag_relation` (
  `content_id` int(10) unsigned NOT NULL, -- 内容ID
  `tag_name` varchar(60) NOT NULL -- 标签名字
) ENGINE=InnoDB DEFAULT CHARSET=latin1



假设我们有以下的需求:
1. 得到标签对应的文章名字,
SELECT a.name FROM content AS a,content_tag_relation AS b
WHERE a.id = b.content_id AND b.tag_name = 'mysql'




2. 按照访问量显示前三的标签,


SELECT tag_name FROM tag WHERE 1 ORDER BY visit_count DESC LIMIT 3;

下来我们在REDIS里面存储这部分数据。
第二,redis部分,
1. a,内容,我们用STRING类型来做,值用JSON来存储,

	t_girl:6379> set string:content_id:4  '{"name":"test48601","created_timestamp":"2012-01-01 05:41:01"}'
	OK
	t_girl:6379> get string:content_id:4"{\"name\":\"test48601\",\"created_timestamp\":\"2012-01-01 05:41:01\"}"


但是如果想得到内容对应的名字和创建时间,REDIS方面获取困难,就得交给程序来做了。


 b,或者也可以用HASH类型来存储,
	t_girl:6379> hset 'hset:content_id:4' name 'test48601'
	(integer) 1
	t_girl:6379> hset 'hset:content_id:4' created_timestamp '2012-01-01 05:41:01'
	(integer) 1




那这时想获取对应的名字以及时间非常容易
	t_girl:6379> hget hset:content_id:4 name"test48601"
	t_girl:6379> hget hset:content_id:4 created_timestamp"2012-01-01 05:41:01"
	t_girl:6379> 




2. 标签,我们用有序集合来做,这么做的好处是可以用REDIS对应的有序来做访问量的排序。

	t_girl:6379> zadd zset:tag 680 database 469 db2
	(integer) 2



比如我们想要得到访问前三的标签名字?
	t_girl:6379> zrevrangebyscore zset:tag +inf 0 limit 0 3
	1) "mongodb"
	2) "sql"
	3) "postgresql"




3. 标签与内容的关系,我们用集合来做,
	t_girl:6379> sadd set:content_id:4 role mongodb role database 
	(integer) 3



那么也很容易得到指定内容对应的标签
	t_girl:6379> smembers set:content_id:4
	1) "database"
	2) "role"
	3) "mongodb"




4.  a, 如果用上面的设计我们实现稍微复杂些的需求:比如得到标签对应的文章名字。这样的需求貌似没有可以直接拿来用的方法,比如下面我写的一段PYTHON代码来获取:
    import redis
	    content_id_keys = r.keys('set*')
	    content_id_keys_len = len(content_id_keys)
	    i = 0
	    j = 0
	    content_name_list = []
	    while i < content_id_keys_len:
	        if r.sismember(content_id_keys[i],'mysql') == 1:
	            content_name_list.append(eval(r.get(content_id_keys[i].replace('set','string')))['name'])
	            print('Content name is :' + content_name_list[j])
	            j += 1
	        i += 1  




b.那其实我们可以在REDIS里面做一份冗余的集合来存储,这样就可以直接把信息拿出来。

	t_girl:6379> sadd tag:mysql test123 test133 test144 test155
	(integer) 4
	t_girl:6379> smembers tag:mysql
	1) "test133"
	2) "test155"
	3) "test123"
	4) "test144"




作者:yueliangdao0608 发表于2015/12/16 10:08:04 原文链接
阅读:0 评论:0 查看评论

银联POS术语

$
0
0

一、EMV       

       EMV 是Europay、MasterCard、VISA三大国际银行卡组织共同制定的芯片卡规范,是芯片卡与芯片终端之间的交互对话机制。
       EMV迁移是银行卡由磁条卡向集成电路(IC)卡转移,利用安全性更高的智能IC卡来代替磁卡,以有效防范诸如制作和使用假信用卡、信用卡欺诈、跨国金融zhapian等各种高科技手段的金融犯罪。

      EMV迁移是银行卡从磁条卡向智能IC卡转换的过程。是基于CPU IC卡的金融支付标准,目前已成为公认的框架性标准。其目的是在金融IC卡支付系统中建立卡片和终端接口的统一标准,使得在此体系下所有的卡片和终端能够互通互用,并且该技术的采用将大大提高银行卡支付的安全性,减少欺诈行为。

二、PBOC    

    The People's Bank of China 中国人民银行
    该标准将为我国银行卡芯片化奠定标准基础,确保我国银行卡芯片化实现联网通用和安全,并有效指导实施。
    PBOC 3.0规范已于2013年2月5日正式发布。

三、CUPS    

中国银联银行卡信息交换系统(简称CUPS),实现全国范围内所有跨行银行卡业务的信息转接和资金清算工作。该系统的建设包括交换应用、网络、运行管理、切换过渡及业务持续等项目的建设。

四、Pos术语    

    银行卡 bank card   

商业银行等金融机构及邮政储汇机构向社会发行的,具有消费信用、转账结算、存取现金等全部或部分功能的信用支付工具。    

    磁条卡 magnetic stripe card   

物理特性符合GB/T 14916标准,磁条记录符合GB/T 15120 、GB/T 15694-1、ISO 7812-2、GB/T17552标准的银行卡片。 
    销售点终端 point of sale;POS   

能够接受银行卡信息,具有通讯功能,并接受柜员的指令而完成金融交易信息和有关信息交换的设备。 
     POS 中心 POS center   

接受、处理或转发POS的交易请求信息,并向POS回送交易结果信息的机构。 
     特约商户 merchant   

与收单行签有商户协议,受理银行卡的零售商、个人、公司或其他组织。 
     持卡人 card holder  

卡的合法持有人,即与卡对应的银行账户相联系的客户。
    发卡行 issuer   

发行银行卡,维护与卡关联的账户,并与持卡人在这两方面具有协议关系的机构。 
    收单行 acquirer   

与商户签有协议或为持卡人提供服务,直接或间接凭交易单据(包括电子单据或纸单据)参加交换的清算会员单位。 
    参考号 reference number   

POS中心为交易分配的流水号,在响应报文中下传给POS终端作为对账参考号,并用于事后查证。 
     交易批次号 batch number   

POS从签到起至结算、签退为止的交易为一批次,交易批次号标识一批交易。    POS中心为每个POS的每个批次分配一个批次号,在签到响应报文中下传给POS终端,POS终端对账平或批上送结束后POS中心和POS终端的批次号各自加1。 
     主账号 primary account number   

标识发卡机构和持卡人信息的数字代码。它由发卡机构标识代码、个人账户标识和校验位组成,是银行卡金融交易的主要账号,在银行卡金融交易中等同于卡号。 
     交易处理码 processing code   

标识交易类型的代码。 
    POS 流水号 trace number   

POS为每一笔交易产生的顺序编号。POS每上送一次交易此号码增加1。 POS流水号为6位数字,值从1至999999循环使用。    在自动冲正时,POS中心依据POS流水号作为确定被冲正交易的要素之一。    POS流水号也作为交易凭证号,在进行撤销等交易时,输入原交易凭证号作为确定原交易的要素之一,并且必须上送原交易的凭证号。
    服务点输入方式码 point of service entry mode   

POS输入有刷卡或手工方式,按业务要求输入有关数据。 
    授权码 authorization number   

授权标识应答码,简称“授权码”。是发卡行返回或CUPS代授权时返回的授权序号。 
     响应码 response number   

也称为应答码,是接收方接收到请求或通知后,返回给发送方表示处理结果的代码。 
    个人标识码 personal identification number; PIN   

即个人密码,是在联机交易中识别持卡人身份合法性的数据信息,在计算机和网络系统中任何环节都不允许PIN以明文的方式出现。    在上送报文格式中,如果22域指明有PIN输入,则52域必须出现。客户的个人标识码(PIN)必须加密后存放在52 域中。
   报文鉴别码 messang authentication code;MAC  

MAC是用来完成消息来源正确性鉴别,防止数据被篡改或非法用户窃入的数据。
   安全控制信息 security control related information  

与安全相关的控制信息,用于标识密文的类型。
   密钥加密密钥 key encryption key; KEK  

POS终端工作时对工作密钥进行加密的密钥,由银行人员设置并直接保存在系统硬件中,只能使用,不能读取,该密钥必须与加密算法放在同一加密芯片里。 
   工作密钥 working key;WK  

也称为数据密钥,通常指PIN加密密钥和MAC计算的密钥。工作密钥必须经常更新。在联机更新的报文中对工作密钥必须用密钥加密密钥(KEK)加密,形成密文后进行传输。
   集成电路卡 integrated circuit card  

内部封装一个或多个集成电路用于执行处理和存储功能的卡片。 
   支付系统环境 payment system environment;PSE  

当符合本标准的支付系统应用被选择,或者用于支付系统应用目的的目录定义文件(DDF)被选择后,IC卡中所确立的逻辑条件。 
   应用标识 application identifier;AID  

由注册的应用提供商标识(RID)以及专用应用标识符扩展(PIX)组成。
AID:即唯一标识一个应用,分为两部分,RID(5字节)+PIX(最多11字节)
RID:注册标识符,由ISO组织来分配,标识一个全球唯一的应用提供商,一般是分配给卡组织,比如分配给Master,比如分配给银联,我们遵循的是PBOC规范,分配到的RID=A000000333 
PIX:扩展应用标识符,一般是由应用提供商自己定义,比如我们银联定义的借记应用的PIX=010101,贷记应用的PIX=010102,准贷记应用的PIX=010103,纯电子现金应用的PIX=010106
因此我们遵循PBOC规范,常见的AID为:
A0 00 00 03 33  01 01 01    PBOC借记应用
A0 00 00 03 33  01 01 02    PBOC贷记应用
A0 00 00 03 33  01 01 03    PBOC准贷记应用
A0 00 00 03 33  01 01 06    PBOC纯电子现金应用
   应用标签 application label  

根据ISO/IEC 7816-5标准中与应用标识(AID)相关联的名称,用于应用选择。应用标签在应用数据文件(ADF)的文件控制信息(FCI)中可选(推荐要求),在ADF目录入口中必须存在。
   应用首选名称 application preferred name  

与应用标识(AID)相关联的应用名称。如果应用首选名称存在且终端支持发卡行代码表索引指示的语言,则应用选择过程中显示给持卡人的应用名称应采用应用首选名称,而不是应用标签。
   应用选择指示符 application selection indicator;ASI  

指示应用选择时终端上的应用标识(AID)与卡片中的应用标识(AID)是完全匹配(长度和内容都必须一样)还是部分匹配(卡片AID的前面部分与终端AID相同,长度可以更长)。终端支持的应用列表中的每个应用标识(AID)仅有一个应用选择指示符。
   脚本 script  

发卡行向终端发送的命令或命令序列,目的是向IC卡连续输入命令。
   应用认证密文 application authentication cryptogram;AAC  

在EMV应用交易流程中,由IC卡生成的表示拒绝交易的应用密文。
   授权响应密文 authorization response cryptogram;ARPC  

由发卡行生成并在联机授权报文中返回给终端的应用密文,用于IC卡验证联机授权响应是否来自真正的发卡行。
  授权请求密文 authorization request cryptogram;ARQC 
IC卡为联机处理交易生成的应用密文,发卡行在联机卡片认证过程中通过验证ARQC来认证当前交易中卡片的有效性。
  数据对象列表 data object list;DOL 

在EMV应用交易流程中,终端能够根据卡片的要求建立可变的数据元列表,并通过相关命令发送给卡片。在EMV交易流程使用的DOL包括:通过取处理选项(GET PROCESS OPTIONS)命令使用的PDOL;通过生成应用密文(GENERATE AC)命令使用的CDOL1和CDOL2;用来产生TC哈什值的TDOL和通过内部认证(INTERNAL AUTHENTICATE)命令使用的DDOL。
  fallback 

在某些情况下,IC卡有可能无法在支持芯片卡的终端上使用,比如IC卡本身坏了,或终端上的读写器发生了故障。在这种情况下,支付系统允许使用卡上的磁条来进行交易。
  终端行为代码 terminal action code;TAC 

在EMV应用交易流程中,终端行为代码(缺省、拒绝、联机)反映了收单行根据TVR的内容选择的动作。
  交易证书 transaction certificate;TC 

在EMV应用交易流程中,由IC卡生成表示批准交易的交易证书。
  终端校验结果 terminal verification results;TVR 

在EMV应用交易流程中,用于标识终端执行脱机数据认证、处理限制、持卡人验证、终端风险管理、发卡行认证、发卡行脚本处理等各步骤的结果,终端和卡片将参考TVR的值对当前交易作出脱机拒绝、联机交易或脱机批准的决定。
  交易验证码 transaction authorization crypogram;TAC 

用于验证交易的合法性。
  EP electronic purse 

PBOC 电子钱包缩写。
  PSAM卡 

支付系统安全控制模块,用于PBOC电子钱包卡的安全控制。
  TMS 

终端管理系统,即管理POS终端远程下载应用程序、参数的计算机软件系统。
  刷卡金额 

商品标价。
  持卡人折扣: 
商户优惠给持卡人的消费折扣。
  扣持卡人金额 

持卡人在抵扣掉折扣优惠后的真实支出金额。

五、补充

CUPS(转载)
中国银联银行卡信息交换系统(以下简称CUPS)实现全国范围内所有跨行银行卡业务的信息转接和资金清算工作。在系统性能方面,达到日均处理交易笔数2400万笔、平均每秒处理交易笔数650笔、峰值每天处理交易笔数5200万笔、峰值每秒处理交易笔数 3000笔(实际达到13529笔/秒)、网络可用率(无故障率)99.99%的设计指标,实现了高性能、高可靠性、高可用性、高可管理性和高可扩展性的系统目标,具有国际水平,满足了国内银行卡快速发展的需要。
该系统的建设包括交换应用、网络、运行管理、切换过渡及业务持续等项目的建设。各项目技术情况简述如下:
在交换应用建设方面,交换应用系统总体物理结构设计中,遵循通讯接入、应用处理、数据存储三层设计模式,保证在各个层面的线性扩展能力,便于安全分层控制,从而提高整个系统的可扩展性和安全性。生产系统搭建在多台IBM P系列服务器之上,应用系统支持多机模式,关键服务器之间采用CLUSTER连接,保证系统的可扩展性和高可用性。
核心交换应用系统包括交易转接、文件收发、批处理、安全、代授权、后线(差错、管理、查询报表、门户等)等多个子系统。建设了统一二次清分、风险管理系统等核心交换支持系统,其中统一二次清分平台较为独立,统一实现18个分公司的二次清分,各分公司还可开发个性化模块;风险管理系统包括风险信息共享、外卡实时交易监控、风险报告、外卡联机交易限额控制及涉案卡交易监控等业务处理模块,在一定程度上防范和控制银行卡交易过程中的持卡人及商户风险。
应用系统的主要部分均构建在中间件平台上,根据应用系统的不同特点,分别引入了联机事务处理中间件Tuxedo和J2EE中间件Weblogic。联机交易部分由Tuxedo提供支持,后线管理部分由Weblogic提供支持。
子系统之间的通信统一采用中间件调用方式实现。为提高通信效率,Weblogic和Tuxedo之间使用BEA提供的专用通信连接件WTC(Weblogic Tuxedo Connector)进行通信,采用WTC同时也是为满足应用对Weblogic和Tuxedo之间的双向调用的要求。
在网络建设方面,中国银联跨行交易网络分为核心层、分布层和接入层3个层次。中国银联生产网络采用双中心模式,即在上海和北京分别建立一个网络核心节点,并互为备份,通过高速ATM线路连接。各分公司和大型金融机构均通过两条2M线路分别接入上述网络核心节点,并采用ISDN线路作为拨号备份。银联各分公司根据当地实际情况,提供多种专线接入方式与当地成员机构、商户、第三方机构连接。中国银联网络内采用统一规划的IP地址,实现动态路由、QoS控制和综合网管,划分不同的网络安全区域,并采用先进的网络安全技术确保网络安全。
在运行管理方面,整个生产系统运行和管理采用了统一集成的运行管理平台。运行管理系统分为三层:监控管理层、事件管理层及流程管理层,以事件管理层为中心,整合各资源管理模块,使之成为一个整体。监控管理层采用Patrol及Tivoli工具产品集成;事件管理层由Event Manager和PEM(Patrol Enterprise Manager)组成,收集来自监控层的SNMP格式及文本方式的事件;流程管理层使用流程工具Remedy实施包括事件管理流程、问题管理流程、变更管理流程及配置管理流程。
在业务持续方面,为保证系统7*24*365连续运行的能力,系统搭建了交换系统的灾难备份系统,采用两地三址建立信息处理中心、备份中心、灾备中心,按照“同城同步、异地异步”的技术原则,采用存储复制技术结合容灾应用来进行应用的灾备建设,保证发生灾难时的应用系统切换;建立业务连续计划,保证系统在各种灾难情况下的业务连续运行。
在系统上线及切换过渡方面,中国银联银行卡信息交换系统上线后,中国银联通过精心组织,采用差异测试、对比测试、并行测试;网络改造、物理过渡、前置开发等技术手段,用了不到1年的时间完成了所有18个分公司的无缝透明切换过渡,至2005年11月8日中国银联完成了各分公司交换系统的过渡切换工作,实现了全国银行卡跨行交易的集中处理。
使用开放式平台及技术,建设全国集中式的金融关键应用系统,是一个很大的技术挑战,在系统的建设过程中存在一些需要解决或突破的技术要点,以实现系统规划时的目标,经过对相关技术进行消化、吸收及提炼,通过对成熟产品的集成和功能深度挖掘,并在这些工作的基础上进行技术攻关、完善和创新,解决了系统建设中的问题,形成了具有银联特点的技术方案和方法论,产生了一批具有自主知识产权的专利技术(申请中),这些技术要点包括:CUPS系统架构元模型、自适应流量控制机制、多级动态负载均衡策略、表达式驱动规则引擎、基于内容依赖索引(CDI)的双缓冲内存查找表技术、事件驱动的同步数据推送技术、转接数据库的双活及快速切换机制、超大容量数据库的多维集群和分区技术、跨平台整合的业务连续机制、模式识别异常处理技术、基于交易分流的自动化并行测试技术。目前已有五项专利申请获得初审合格,包括:一种交易结算处理系统及其信息传送与处理方法、一种自动化测试辅助系统以及相应的软件自动测试方法、一种新型银行卡交易处理系统、实现数据升级的计算机系统以及数据升级方法(一)及实现数据升级的计算机系统以及数据升级方法(二);一项专利正在提交申请,即:验证新老系统差异性的验证系统及验证方法;另外还有多项专利申请正在准备提案,包括数据库设计、灾难备份系统设计、加密算法、互联网支付等。
   



 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



ESB总线和能力开放平台

$
0
0


上图是ESB企业服务总线和互联网Open API能力开放平台的一个简单对比。对于在企业内部的服务集成和管控,由于需要面对企业内复杂的业务系统间集成和遗留系统适配,因此使用较多的仍然是ESB企业服务总线。而对于互联网应用,更多考虑的是轻量和高性能,已经开发和接入的效率,当前使用较多的是类似Open API方式下的能力开放平台。

对于能力开放平台,网上有比较明确的定义,具体如下:

Open API即开放API,也称开放平台。 所谓的开放API(OpenAPI)是服务型网站常见的一种应用,网站的服务商将自己的网站服务封装成一系列API(Application Programming Interface,应用编程接口)开放出去,供第三方开发者使用,这种行为就叫做开放网站的API,所开放的API就被称作OpenAPI(开放API)。就现在互联网上Open API的形态来看,主要分成两种:标准REST和类REST(也可以叫做RPC形态)。

对于是否够成一个能力开放平台,有两个重点,其一是服务的提供以htpp rest服务为主,其二是平台本身是提供和暴露服务能力,而不消费服务。因此即使是在企业内部,如果ESB平台本身能够实现为仅暴露和提供服务,同时采用轻量的Rest服务接入,那么也可以看做是能力开放平台。

对于企业内部的服务,往往由于服务接口对需求规范要求严格,往往有会采用SOAP WebService方式来实现完整的服务契约设计。即服务接口本身就对服务的调用规范进行了强约束。而对于开放平台往往采用http协议,传输的对象也是大的json串,对于json串格式的定义往往只有通过文档约定。这两种方式各有优缺点,具体的选择使用关键还是看使用的场景。

企业内部只是提供和暴露服务,供其它业务系统消费,这种场景往往出现在主数据和共享数据服务能力提供,因此对于共享数据往往是适合构建能力开放平台的。而对于企业内部端到端流程引起的横向业务系统接口,这类横向集成接口本身复用度不高,更多是为了业务系统,即使这类服务接入也很难真正高复用和共享。

前面有一篇文章专门谈到过Dubbo服务总线,对于Dubbo是可以用来构建Open API平台的轻量服务总线。互联网能力开放平台这类总线其核心是服务注册和管控,服务的访问安全,服务集群扩展能力等。为了达到高并发和高性能,往往不会在总线层面做详细的服务调用日志审计。即:

服务消费方从总线获取到服务地址后,即转为对服务提供端的直接调用,数据流不经过轻量总线。



对于互联网能力开放平台的构建要注意,能力开放平台不仅仅是一个高性能的轻量服务总线或一个http接口,而是要构建一个完整的互联网PaaS平台生态环境。从最早期的中国移动等电信运营商开放的互联网PaaS能力开放平台可以看到这个特点,即这种能力开放平台提供了完整的自服务,多租户管理,服务申请和开通,安全管控,开发框架和环境,本地SDK开发包,开发样例等完整的支持。即除了开发完成的应用托管能力可以不提供外,其它标准的互联网PaaS平台提供的能力,在能力开放平台都需要完整提供。

 

sql 优化

$
0
0

转:数据库SQL优化大总结之 百万级数据库优化方案

2014-07-18 09:33 雲霏霏 雲霏霏的博客 字号: T |  T
一键收藏,随时查看,分享好友!

网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。

AD:

 

网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。

这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的话推荐给更多的人,让更多的人看到、纠正以及补充。

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

 

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num is null

最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.

备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。

不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

 

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

select id from t where num = 0

 

3.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or Name = 'admin'

可以这样查询:

select id from t where num = 10
union all
select id from t where Name = 'admin'

5.in 和 not in 也要慎用,否则会导致全表扫描,如:

select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

很多时候用 exists 代替 in 是一个好的选择:

select num from a where num in(select num from b)

用下面的语句替换:

select num from a where exists(select 1 from b where num=a.num)

6.下面的查询也将导致全表扫描:

select id from t where name like ‘%abc%’

若要提高效率,可以考虑全文检索。

 

7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

select id from t where num = @num

可以改为强制查询使用索引:

select id from t with(index(索引名)) where num = @num

应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num/2 = 100

应改为:

select id from t where num = 100*2

9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where substring(name,1,3) = ’abc’       -–name以abc开头的id
select id from t where datediff(day,createdate,’2005-11-30′) = 0    -–‘2005-11-30’    --生成的id

应改为:

select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'

 

10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

12.不要写一些没有意义的查询,如需要生成一个空表结构:

select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

create table #t(…)

13.Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志。

 

14.对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差。

 

15.select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。

 

16.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

 

17.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

 

18.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连 接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

 

19.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

 

20.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

 

21.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

 

22. 避免频繁创建和删除临时表,以减少系统表资源的消耗。临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件, 最好使用导出表。

 

23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

 

24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

 

25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

 

26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

 

27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

 

28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

 

29.尽量避免大事务操作,提高系统并发能力。

 

30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

实际案例分析:拆分大的 DELETE 或INSERT 语句,批量提交SQL语句

如果你需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询,你需要非常小心,要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。

Apache 会有很多的子进程或线程。所以,其工作起来相当有效率,而我们的服务器也不希望有太多的子进程,线程和数据库链接,这是极大的占服务器资源的事情,尤其是内存。

如果你把你的表锁上一段时间,比如30秒钟,那么对于一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你的WEB服务崩溃,还可能会让你的整台服务器马上挂了。

所以,如果你有一个大的处理,你一定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)条件是一个好的方法。下面是一个mysql示例:

 

 
while(1){

 //每次只做1000条

 mysql_query(“delete from logs where log_date <= ’2012-11-01’ limit 1000”);

 if(mysql_affected_rows() == 0){      
//删除完成,退出! break; } //每次暂停一段时间,释放表让其他进程/线程访问。 usleep(50000) }
 

好了,到这里就写完了。我知道还有很多没有写到的,还请大家补充。后面有空会介绍一些SQL优化工具给大家。让我们一起学习,一起进步吧!



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




Java 应用发布后,需要关注的7个性能指标

$
0
0

在某个重大发布之后,都需要记录相应的指标,本文介绍了最重要的几个 Java 性能指标,包括响应时间和平均负载等。为理解应用程序在生产环境中如何运行,就需要遵循一些 Java 性能指标。

在以前,当软件被发布后,开发者是没有方法去了解它在生产环境中的运行情况;而现在,几乎任一个你可以想到的指标都可以被监测和报告。时下,开发者面临的问题并不是缺乏信息,而是信息过载、过大。因此在数百台服务器同时工作的情景下,跟踪记录信息就变得越来越困难,虽然多数开发者为了深刻理解产品系统仍旧需要利用日志文件,但依然阻挡不了它们逐步被取代的命运。

本文整理了一些重要的指标,使开发者在不借助任何日志文件的情况下,便于理解应用程序在生产环境中运行的具体过程。谈到对 Java 性能的影响,除了像用户负载(或者 AWS 云服务器停机)这样的外部因素,新功能发布可能是最常见的诱因。所以在那些新功能发布之后的敏感时段,遵循相应准则变得更为关键。

数字至上

在逐个讨论指标之前,先来强调一个重要问题。有这样一个观点:如果某个观点可以得到数字支持,那么它一定是毋庸置疑的。但是这里存在的问题是,当给你呈现时,很多因素会歪曲你对数据的理解。这么说可能有点抽象,这里可以对比这两个测量用例:首先,在一个简单的时间序列中,观察某一个特定基本指标如何随着时间推移而变化;其次,从不同的角度观察数据,并保存关注的性能百分比,底线就是一定要关心留意的那个指标所产生的影响,并给以完整性检查以便对其评估。

例如,假设我们正在观察中值/50百分点处的事务响应时间,因为该点的响应时间已被广泛用作指示符,很多公司将其作为主要KPI之一。在实际中,若单个页面浏览人数达到几十及以上(一般远远超过40),就意味着该用户有99.999...%的可能会经受比中值更差的结果(数学公式可简单的表示为:1 –(0.5 ^ 40) 。因此什么百分点更有意义呢?即使观察点设在第95的百分点,然后你的单页面浏览人数远大于40,那么大部分的用户仍会经受比之前更糟糕的响应时间。若符合多个页面浏览量,事情会更加复杂。想阅读更多关于百分点的知识,了解其对数据的误导,可 点击此处进入Gil Tene 的博客。

家下来我们来仔细解读指标的选择,看看它们各自代表什么,并学习如何来理解这些指标。

1. 响应时间与吞吐量

响应时间用来衡量应用程序中的事务处理速度,它也可以从 HTTP 请求层和数据库层来观察。有些最慢的查询需要最优化解决,而响应时间可以缩小该查询的范围。吞吐量从另一个角度观察处理过程,并显示应用程序在给定时间域中处理多少请求,通常单位为每分钟(cpm)。

测量响应时间的方法之一就是使用像 New Relic 或者 AppDynamics(就是曾在 以前的博客讨论的)那种 APM(应用性能监控工具),通过这些工具,可以追踪平均响应时间,并可以直接在主报告仪表板上与昨日或者上周的平均响应时间作比较,这些比较有助于查看新的部署如何对应用程序造成了影响。另一种方法是通过测量网页处理的百分位数,来测量 HTTP 请求完成响应所需的时间。

也可以内部监测响应时间,但是需要硬代码,例如通过 Dropwizard 指标发送数据并在 Graphite 上发布。尽管看来将这些数据和其他标准关联时会出现最有用的见解,但更多的见解仍涵盖在接下来的方法中。

要点1:确保所使用的采集方法可以实现从不同角度观测数据,并开始进入百分位层面。

可行工具:

  1. AppDynamics

  2. New Relic

  3. Ruxit

  4. OneAPM

Java应用响应时间和吞吐量

图为 OneAPM上监控到的 Java 应用程序响应时间和吞吐量

2. 平均负载

第二个广泛使用的衡量指标就是服务器的平均负载。平均负载习惯上分成3部分,在最后的1、5和15分钟(从左到右)显示其结果。只要分数低于机器内核的数量,就是无压力状态,一旦超过内核数,就意味着机器处于压力状态。

平均负载除了可以简单测量 CPU 利用率,更着重于考量每个内核目前在队列中有多少进程。某内核利用率达100%,但是却即将结束任务,而另一内核在队列中还有6个进程要处理,这两种情况是截然不同的。CPU 这一概念并没有涵盖其区别,但是平均负载却可以从大局中考虑此问题。

若在 linux 系统上跟踪平均负载情况,一个极好的方式就是通过 Hisham Muhammad利用 htop 完成。丰富的色彩加上生动的视觉化效果,瞬间使得命令行有了 NASA 仪表板的即视感。

要点2:要确定负载,仅靠资源利用率是不够的,还需要格外注意以便充分了解队列中的进程。

可行工具:

  1. htop

htop

图为在一台服务器上运行 htop 以检测负载,平均负载显示在界面的右上角。

3. 错误率(及如处理)

错误率观测有多种方法,而多数开发人员都利用高层次标准——在整个应用层考察错误率,比如在所有 HTTP 请求中考察失败的 HTTP 处理总数。但是还有一个经常被忽视的具体点:特定事务的错误,这与应用程序的运行状况有直接的影响。代码中某一特定方法失败、生成日志错误及发生异常的次数占总调用次数的比重,也要予以显示。

错误率

图为 OneAPM对事务中的错误率监控,随时间监控应用错误率情况。

但是这些数据对其本身并没有太大的意义。第一步,从正在处理的事件中优选出最紧急的一件,找到日志错误或异常;第二步,从实际根源处着手,并予以修复。而且基于此问题,已有相应的解决办法。有了 OneAPM,就没有必要根据日志文件去找错误提示,因为关于服务器状态的所有信息都会在同一界面显示,包括堆栈踪迹、实源代码、变量值及每个错误调用的应用实例。

要点3:要解决错误率增长的根本原因,仅靠日志文件是不够的,为了得到大量关于我们所需指标的数据,还需要利用一些错误率监控工具。

4. GC率和中止时间

垃圾回收器行为异常,是导致应用吞吐量和响应时间突然下降的主要原因之一。读者想要了解关于垃圾回收过程的更多知识和相关的标准,可阅读 深入理解Java虚拟机(第2版)

分析 GC 日志文件是理解 GC 中止时间和频率的关键。如果不自行分析,或者使用类似于 jClarity 的工具,这种指标是没有办法直接使用的。所以要确保使用合适的 JVM 参数打开 GC 日志采集,以便分析。

要点4:请记住,分析不同指标的相关数据,要保持开阔的思维,这样容易发现它们之间的互相影响。

可行工具:

  1. jClarity Censum

  2. GCViewer

5. 业务指标

应用的性能不是仅仅依赖于快速响应,也非错误率,另一个方面就是业务指标。其业务责任也不是只由产品/销售人员全权负责。收入、用户数、与应用中特定区域的互动等,这些都对理解软件的运行极为关键。若要从业务角度观察,你所配置的修复方法和新功能是如何影响底线的,以上因素与新部署的时间标记一起作用这一点至关重要。我们固然希望情况向好的方向发展,但假如事态恶化,一旦你把所有数据都存在同一位置,要想知道哪里出了问题需要修复,这就相当容易了。

此外,将业务指标与错误率、延时等数据实时连接起来,这种能力是极有力度的。然后会让人不由自主地深入研讨到底是什么错误或异常造成了这次最主要的问题,接着就可以按照对业务目的影响优先考虑它们。想要搞清楚遍布各处的所有异常及日志错误,就得使用集成开放的监测工具。所以,保持数据开放,使其可以输出到选择服务中,这是极其重要的。

假如你要利用 Graphite 将汇报的业务指标集中化,这就要求你所使用的工具对发送数据开放。例如,我们的工程组就将汇报指标 通过 StatsD 发布出来,因此相应数据就可以指向任一用户选择使用的仪表板上。

要点5:先入先出式数据已是过去式,在使用指标时,也需要让它们和其他来源的数据相关联。

可行工具:

  1. Grafana

  2. The ELK stack

  3. Datadog

  4. Librato

6.正常运行时间和服务运行状况

该指标为整体的工作定下基调。除用作警示媒介外,它还可用于在一段时间内自定义 SLA,以便观察当为用户提供功能完备的服务时所用时间的百分比。

我们通过运行使用 servlet 的 Pingdom 来解决这个问题,它会对所有应用程序事务中参与的服务进行检查,包括数据库和 S3 等。

要点6:正常运行时可能是二进制指标,但是通过聚合多个值的方式在堆栈中定位薄弱点。

可行工具:

  1. Pingdom

monitor

图为用 Pingdom 监测正常运行时和应用运行状况。

7.日志规模

以上讨论到的指标除了 GC 都没有提到日志,但这个仍然不可忽略。日志文件的副作用就是它们一直在增长,如果不留意其大小以及抑制,那么后果不堪设想。当日志不受控制,磁盘驱动器很可能被撑爆,服务器中会充斥着垃圾文件,运行缓慢,因此,一定要密切关注日志规模,否则随时会崩溃。

一个广泛使用的解决办法就是,使用 logstash 等将服务器上的日志分块,再将其送入Splunk、ELK 等其他日志管理工具中存储,或者直接简单地存入 S3。另一种方法就是在某一时间将日志文件翻转再截断,但此法要冒信息丢失的风险。和大部分开发人员一样,目前我们还必须依赖于日志。

要点7:日志会给人带来很大的困扰,尤其是当你用某些外部服务来处理日志时,你会被 GB 指控。这时就要重新考虑一下这个问题,还应该开始降低日志大小。

最后的思考

我们可以看到这一趋势:目前在产品中,应用里的数据采集器正逐渐脱离对日志文件的依赖。软件分析的新世界越来越开放,数据更加智能化,已不再是以前枯燥的数字,而是带有丰富的情境。我们很兴奋地看着世界的改变,并期待和你们一起共建崭新的未来。

原文地址: https://dzone.com/articles/7-java-performance-metrics-to-watch-after-a-major-1

Java 慎用方法级别的synchronized关键字

$
0
0

转自: http://www.jiacheo.org/blog/317

为什么要这么说呢, 因为笔者被这个坑过(其实是自己坑自己)╮(╯_╰)╭

先看一段synchronized 的详解:

synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用.
 
简单来说, synchronized就是为当前的线程声明一个锁, 拥有这个锁的线程可以执行区块里面的指令, 其他的线程只能等待获取锁, 然后才能相同的操作.
 
这个很好用, 但是笔者遇到另一种比较奇葩的情况.
 
1. 在同一类中, 有两个方法是用了synchronized关键字声明
 
2. 在执行完其中一个方法的时候, 需要等待另一个方法(异步线程回调)也执行完, 所以用了一个countDownLatch来做等待
 
3. 代码解构如下:
synchronized void  a(){
  countDownLatch = new CountDownLatch(1);
  // do someing
  countDownLatch.await();
}

synchronized void b(){
     countDownLatch.countDown();
}
 
 
 
其中
a方法由主线程执行, b方法由异步线程执行后回调
 
执行结果是:
主线程执行 a方法后开始卡住, 不再往下做, 任你等多久都没用.
 
这是一个很经典的死锁问题
 
a等待b执行, 其实不要看b是回调的, b也在等待a执行. 为什么呢? synchronized 起了作用.
 
一般来说, 我们要synchronized一段代码块的时候, 我们需要使用一个共享变量来锁住, 比如:
byte[]  mutex = new byte[0];

void a1(){
     synchronized(mutex){
          //dosomething
     }
}

void b1(){

     synchronized(mutex){
          // dosomething
     }

}
 
如果把a方法和b方法的内容分别迁移到 a1和b1 方法的synchronized块里面, 就很好理解了.
 
a1执行完后会间接等待(countDownLatch)b1方法执行
 
然而由于 a1 中的mutex并没有释放, 就开始等待b1了, 这时候, 即使是异步的回调b1方法, 由于需要等待mutex释放锁, 所以b方法并不会执行
于是就引起了死锁
 
而这里的synchronized关键字放在方法前面, 起的作用就是一样的. 只是java语言帮你隐去了mutex的声明和使用而已. 同一个对象中的synchronized 方法用到的mutex是相同的, 所以即使是异步回调, 也会引起死锁, 所以要注意这个问题. 这种级别的错误是属于synchronized关键字使用不当. 不要乱用, 而且要用对.
 
那么这样的 隐形的mutex 对象究竟是 什么呢?
 
很容易想到的就是 实例本身. 因为这样就不用去定义新的对象了做锁了. 为了证明这个设想, 可以写一段程序来证明.
 
思路很简单, 定义一个类, 有两个方法, 一个方法声明为 synchronized, 一个在 方法体里面使用synchronized(this), 然后启动两个线程, 来分别调用这两个方法, 如果两个方法之间发生锁竞争(等待)的话, 就可以说明 方法声明的 synchronized 中的隐形的mutex其实就是 实例本身了.
 
public class MultiThreadSync {

    public synchronized void m1() throws InterruptedException{
         System. out.println("m1 call" );
         Thread. sleep(2000);
         System. out.println("m1 call done" );
    }

    public void m2() throws InterruptedException{
          synchronized (this ) {
             System. out.println("m2 call" );
             Thread. sleep(2000);
             System. out.println("m2 call done" );
         }
    }

    public static void main(String[] args) {
          final MultiThreadSync thisObj  = new MultiThreadSync();

         Thread t1 = new Thread(){
              @Override
              public void run() {
                  try {
                      thisObj.m1();
                 } catch (InterruptedException e) {
                      e.printStackTrace();
                 }
             }
         };

         Thread t2 = new Thread(){
              @Override
              public void run() {
                  try {
                      thisObj.m2();
                 } catch (InterruptedException e) {
                      e.printStackTrace();
                 }
             }
         };

         t1.start();
         t2.start();

    }

}
 
结果输出是:
 
m1 call
m1 call done
m2 call
m2 call done
 
说明方法m2的sync块等待了m1的执行. 这样就可以证实 上面的设想了.
 
另外需要说明的是, 当sync加在 static的方法上的时候, 由于是类级别的方法, 所以锁住的对象是当前类的class实例. 同样也可以写程序进行证明.这里略.
 
所以方法的synchronized 关键字, 在阅读的时候可以自动替换为synchronized(this){}就很好理解了.
                                        void method(){
void synchronized method(){                 synchronized(this){
      // biz code                               // biz code
}                             ------>>>      }
                                        }


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Java GC 调试手记

$
0
0

摘要

本文记录GC调试的一次实验过程和结果。

GC知识要点回顾

问题1:为什么要调试GC参数?
在32核处理器的系统上,10%的GC时间导致75%的吞吐量损失。所以在大型系统上,调试GC是以小博大的不错选择。'small improvements in reducing such a bottleneck can produce large gains in performance.'
 
问题2:怎么样调试GC?
调试GC,有 三个主要的参数
  • 选择合适的GC Collector
  • 整个JVM Heap堆的大小
  • Young Generation的大小(-Xmn?m or -XX:NewRatio=?)
问题3:有哪些不同的GC Collector?
Tony Printezis (JVM大牛)在 Garbage Collection in the Java HotSpot Virtual Machine有图为证,还有一篇更早的 sun开发人员介绍GC调试也是有图为证
 
neo4j总结如下
GC shortname Generation Command line parameter Comment

Copy

Young

-XX:+UseSerialGC

The Copying collector

MarkSweepCompact

Tenured

-XX:+UseSerialGC

The Mark and Sweep Compactor

ConcurrentMarkSweep

Tenured

-XX:+UseConcMarkSweepGC

The Concurrent Mark and Sweep Compactor

ParNew

Young

-XX:+UseParNewGC

The parallel Young Generation Collector — can only be used with the Concurrent mark and sweep compactor.

PS Scavenge

Young

-XX:+UseParallelGC

The parallel object scavenger

PS MarkSweep

Tenured

-XX:+UseParallelGC

The parallel mark and sweep collector

简而言之,Young和Tenured各种三种Collector,分别是
  • Serial 单线程
  • Parallel 多线程并行, GC线程和App线程取一运行,即GC要Stop the (app) world。
  • Concurrent 多线程并发,GC线程和App线程可同时运行。(注: Young generation 没有CMS,取而代之的是可和CMS(Old)一起运行的ParNew)
 
问题4:如何选择Collector?
Serial可以直接排除掉,现在最普通的服务器也有双核64位\8G内存,默认的Collector是PS Scavenge和PS MarkSweep。所以Collector在并行(Parallel)和并发(Concurrent)两者之间选择。
 
问题5:选择的标准(参数指标)是什么?如何得到这些参数值(How to measure it)?
throughput和latency。 garbage-collection-in-java-part-3从GC的耗时给出了吞吐量和响应速度的公式
Total Execution Time = Useful Time + Paused Time
throughput = Useful Time / Total Execution Time
latency = average paused time
 
如何得到Useful time 和 Paused Time?即如何得到JVM的GC时间,有以下几种方式
GC Log
打印GC log,java 启动参数中加入下面的语句(本文为tomcat应用)。GC Log 记录每次GC时间,可根据GC Log计算平均GC时间和累积GC时间。
[plain]  view plaincopy
 
  1. CATALINA_OPTS="$CATALINA_OPTS -verbose:gc -Xloggc:/usr/local/tomcat/gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"  
 
JDK自带工具,java 启动参数中加入下面的语句(本文为tomcat应用),然后在监控端可以远程连接1090端口。在内存一项,有累积GC时间和次数。注意在以min为单位显示时,只显示整数部分,如1min20s显示为1min。
[plain]  view plaincopy
 
  1. CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"  
 
JVM监控工具,未同JDK一起发布,可在 JVisualvm(JDK自带)中以插件的方式使用,本文为独立使用。有累积GC时间和次数,并有曲线图直观显示。
首先在Server端启动jstatd
[plain]  view plaincopy
 
  1. vi jstatd.all.policy  
  2.   
  3. grant codebase "file:${java.home}/../lib/tools.jar" {  
  4.     permission java.security.AllPermission;  
  5. };  
  6.   
  7. jstatd -J-Djava.security.policy=jstatd.all.policy  
然后在监控启动VisualGC,远程连接服务端进程id
visualgc 102592@remote.domain
 
问题5:应用请求的吞吐量和响应是否可以反映JVM的性能?
正是我们调优的目标。本文使用Jmeter来做压力测试,并给出吞吐量和响应 report。

测试

硬件环境
操作系统:  体系结构:  处理器的数目:  分配的虚拟内存: 
Linux 2.6.18-53.el5
amd64
2
2,680,408 Kb
物理内存总量:  可用物理内存:  交换空间总量:  可用交换空间: 
8,175,632 Kb
1,140,520 Kb
8,594,764 Kb
8,594,680 Kb
Test case
  1. 使用用 Jmeter压力测试
  2. 共6个client,每个client启动30个线程发送请求
  3. 每个请求从16种测试样例中随机挑选一个,发送到server端
  4. 测试持续10min
参数值
  1. server使用默认GC(PS Scavenge和PS MarkSweep)
  2. server使用CMS(-XX:+UseConcMarkSweepGC-XX:+UseParNewGC)
  3. server使用CMS(-XX:+UseConcMarkSweepGC -XX:+UseParNewGC),设置Young generation的大小为200m(-Xmn200m)
  4. server使用CMS(-XX:+UseConcMarkSweepGC -XX:+UseParNewGC),设置Young generation的大小为600m(-Xmn600m)
观察值
  1. Jmeter请求的summary report
  2. server端累积GC时间和次数
测试结果
1) CMS和Parallel比较
1.1) 吞吐量和响应
(PS Scavenge和PS MarkSweep)
(ParNew和CMS)
从Jmeter的report中可以看出, 使用CMS后吞吐量(对应总的请求数)下降18%,而最大响应时间(包括最小响应时间)有近30%的提升(变小)。这验证了Tony Printezis在Step-by-Step:Garbage Collection Tuning in the Java HotSpot™ Virtual Machine中说使用CMS应用的吞吐量会相对下降,但有更好的最差响应时间。
  • Expect longer young GC times
    • Due to slower allocations into the old gen
  • Expect better worst-case latencies
    • CMS does its work mostly-concurrently
    • Shorter worst-case pauses
  • Expect lower throughput
    • CMS does more work
在官方的JVM性能调优中给出的建议也是,如果你的应用对峰值处理有要求,而对一两秒的停顿可以接受,则使用(-XX:+UseParallelGC);如果应用对响应有更高的要求,停顿最好小于一秒,则使用(-XX:+UseConcMarkSweepGC)。
 
1.2) GC 累积时间和次数
(PS Scavenge和PS MarkSweep)
(ParNew和CMS)
 
PS累积GC时间(visualgc)为1min25s,其中Eden 189次,共52s;old 13次,共33s。
CMS 累积GC(visualgc)为2min2s,其中Eden 2333次,共1min46s;old 55次,共16s。(Jconsole和GC log却显示没有Full GC,从 understanding cms gc logsjstat显示的full GC次数与CMS周期的关系中我推测visualgc与jstat显示一致,都是统计old的回收次数;而Full GC则是Young和Old一起回收,在其他类型的GC里,Old只有Full GC时才触发)。
 
可以看到PS的GC频率相对低,但每次GC时间长,每次Full在3s左右徘徊,Yong在0.3s左右;CMS则是短频快,频繁快速回收,yong在0.03s(<0.1s)左右,old<0.5s。从JMeter上,使用PS GC,Request Report会有间歇性的停顿,即server没有任何响应;CMS则相对较少,停顿不那么明显。
 
2) CMS下不同Xmn的比较
由于CMS Young太多频繁,又测试了分别调整Xmn为200m和600m之后的结果。200m是仿照 cassandra中100m * cpu #来设置Young gen的大小;600m则是与PS下的Young gen一致。
 
200m
600m
 
随着Young gen的增大(40m -> 200m -> 600m),Young 的回收次数减少,Old的回收次数增加,总体GC累积时间下降,应用吞吐量上升,最差响应时间变慢(即便和PS比较也更差,是我的测试有问题?)。

结论

app停顿3s是不可接受的,因此倾向于使用CMS;CMS的default young gen相当小,于是设置Xmn。对于更加Prefer响应的应用,下面配置是否是黄金标配:
 
JVM_OPTS="$JVM_OPTS -XX:+UseParNewGC"
JVM_OPTS="$JVM_OPTS -XX:+UseConcMarkSweepGC"
JVM_OPTS="$JVM_OPTS -XX:+CMSParallelRemarkEnabled"
JVM_OPTS="$JVM_OPTS -XX:SurvivorRatio=8"
JVM_OPTS="$JVM_OPTS -XX:MaxTenuringThreshold=1"
JVM_OPTS="$JVM_OPTS -XX:CMSInitiatingOccupancyFraction=75"
JVM_OPTS="$JVM_OPTS -XX:+UseCMSInitiatingOccupancyOnly"


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



谷歌发布2015年度搜索排行榜

$
0
0

  继 百度Bing之后,今天谷歌官方也发布了2015年的热搜榜单排行榜,用热词呈现今年人们最关心的热点事件和社会潮流。本年谷歌并没有针对中国地区发布榜单。

  谷歌表示,从新年伊始的那条到底是蓝黑相间还是白金相间的裙子,到11月的巴黎恐怖袭击,再到你期盼已久的《星球大战:原力觉醒》......我们看到人们乐于在互联网上各抒己见,也在灾难时祈福和援助,还时刻关注流行趋势。

  在谷歌2015热搜榜单中,最热搜索是Lamar Odom(拉玛尔·约瑟夫·奥多姆),最热门的消费技术产品是iPhone 6s,最热门的电影是Jurassic World(侏罗纪世界)、最热的新闻事件是Charlie Hebdo(查理周刊)。

  谷歌发布2015年度热搜排行榜视频:

  以下是详细榜单:

   2015年全球热搜排行榜最热搜索:

  Lamar Odom(拉玛尔·约瑟夫·奥多姆)

  Charlie Hebdo(查理周刊)

  Agar.io(细胞吞噬,大型在线多人游戏)

  Jurassic World(侏罗纪世界)

  Paris(巴黎暴恐事件)

  Furious 7(速度与激情7)

  Fallout 4(辐射4)

  Ronda Rousey(女格斗家隆达-罗西)

  Caitlyn Jenner(变性女星登上名利场封面)

  American Sniper(美国狙击手)

   2015年全球热搜排行榜消费技术:

  iPhone 6s

  Samsung Galaxy S6

  Apple Watch

  iPad Pro

  LG G4

  Samsung Galaxy Note 5

  Samsung Galaxy J5

  HTC One M9

  Nexus 6P

  Surface Pro 4

   2015年全球热搜排行榜人物:

  Lamar Odom

  Ronda Rousey

  Caitlyn Jenner

  Adele

  Charlie Sheen

  Ruby Rose

  Donald Trump

  Sia

  Dakota Johnson

  Jeremy Clarkson

   2015年全球热搜排行榜电影:

  Jurassic World

  Furious 7

  American Sniper

  Fifty Shade of Grey

  Minions

  Spectre

  Straight Outta Compton

  Mad Max

  Prem Ratan Dhan Payo

  Birdman

   2015年全球热搜排行榜电视剧:

  Big Brother Brazil

  Jessica Jones

  Bigg Boss

  Fear the Walking Dead

  Better Call Saul

  Game of Thrones

  Daredevil

  One Punch Man

  Verdades Secretas

  Scream Queens

   2015年全球热搜排行榜新闻:

  Charlie Hebdo

  Paris

  Hurricane Patricia

  Isis

  Nepal

  El Chapo

  Greece

  Baltimore Riots

  San Bernardino

  Hurricane Joaquin

   2015年全球热搜排行榜过世人物:

  Bobbi Kristina

  Cristiano Araujo

  Christopher Lee

  Sandra Bland

  Stuart Scott

  Leonard Nimoy

  Zhanna Friske

  Naomi Kawashima

  Scott Weiland

  BB King

  更详细的排行榜,可以访问 google.com/2015来了解更多。   

评论《谷歌发布2015年度搜索排行榜》的内容...

相关文章:


微博: 新浪微博 - 微信公众号:williamlonginfo
月光博客投稿信箱:williamlong.info(at)gmail.com
Created by William Long www.williamlong.info
月光博客

弱引用,软引用及虚引用对GC的影响

$
0
0

在应用程序中使用非强引用会导致一系列的问题,对GC的响应时间及吞吐量都会有所影响。尽管这类引用在某些场景下可以减少 OutOfMemoryError的出现频率,但过度的使用则会严重影响到垃圾回收,从而导致应用程序的性能遭受影响。

应该注意什么?

在使用弱引用时,你最好了解一下它是如何被回收的。一旦垃圾回收器发现有一个对象是弱可达的,也就是说,它只剩下弱引用了,这个对象就会被放到一个相应的ReferenceQueue中,这样它就可以被析构并回收了。随后它可能会被从队列中取出,并进行相关的清理活动。这类清理任务的一个典型例子便是清除缓存中的无效KEY。

这里还有一个小技巧就是在这个对象最终被回收前,你还能再新建一个强引用来指向它,因此垃圾回收器在回收前还必须得进行二次确认。

其实弱引用要比你想象的常见得多。许多缓存方案都是通过弱引用来解决的,所以即便你没有在代码中创建过弱引用对象,但很可能你的应用程序中就大量使用到了它们。

而说到软引用,你需要知道的是它的回收速度要比弱引用慢一些。它具体会何时回收是不确定的,这取决于具体的JVM实现。通常来说在多次回收内存后空间仍然不足的话会触发软引用的回收。也就是说,你可能会面临比预期的更频繁且时间更长的GC暂停。

而对于虚引用,你可能就得手动进行内存管理并将它们标记为允许回收才行了。虚引用非常危险,如果你只是简单地看一眼文档的话,你会觉得使用它是非常安全的:

为了确保可回收对象保持原状,虚引用指向的对象必须无法被获取到:虚引用的get方法应当始终返回null。

但很奇怪的是,许多开发人员都忽略了该文档中接下来的一段说明(加粗部分):

与弱引用和软引用不同,虚引用在加入回收队列后,是无法被垃圾回收器自动清除的。虚引用可达的对象会一直维持原状,直到这类引用都被清除或者它们自身也不可达。

没错,我们必须手动地 clear())虚引用,否则便会让JVM陷入内存耗尽的处境。虚引用存在的意义首先就在于这是确定对象是否已经不可达的唯一途径。与弱引用和软引用不同,虚引用对象是决不可能“复活”的。

看几个例子

我们来看下这个 demo程序,它会创建大量的对象,但在新生代GC中就会被回收掉。但是还有个调整持久代的提升比率阈值的小窍门,让我们用-Xmx24m -XX:NewSize=16m -XX:MaxTenuringThreshold=1这个参数来运行下这个程序,来看看它的GC日志:

2.330: [GC (Allocation Failure)  20933K->8229K(22528K), 0.0033848 secs]
2.335: [GC (Allocation Failure)  20517K->7813K(22528K), 0.0022426 secs]
2.339: [GC (Allocation Failure)  20101K->7429K(22528K), 0.0010920 secs]
2.341: [GC (Allocation Failure)  19717K->9157K(22528K), 0.0056285 secs]
2.348: [GC (Allocation Failure)  21445K->8997K(22528K), 0.0041313 secs]
2.354: [GC (Allocation Failure)  21285K->8581K(22528K), 0.0033737 secs]
2.359: [GC (Allocation Failure)  20869K->8197K(22528K), 0.0023407 secs]
2.362: [GC (Allocation Failure)  20485K->7845K(22528K), 0.0011553 secs]
2.365: [GC (Allocation Failure)  20133K->9501K(22528K), 0.0060705 secs]
2.371: [Full GC (Ergonomics)  9501K->2987K(22528K), 0.0171452 secs]

这种情况下是几乎没有Full GC的。然而,一旦程序开始创建弱引用的话(-Dweak.refs=true),情况将彻底发生变化。这种情况很常见,比如说用作弱哈希表的KEY或者分析对象创建的性能。但不管是什么情况,弱引用的引入都会导致这样的结果:

2.059: [Full GC (Ergonomics)  20365K->19611K(22528K), 0.0654090 secs]
2.125: [Full GC (Ergonomics)  20365K->19711K(22528K), 0.0707499 secs]
2.196: [Full GC (Ergonomics)  20365K->19798K(22528K), 0.0717052 secs]
2.268: [Full GC (Ergonomics)  20365K->19873K(22528K), 0.0686290 secs]
2.337: [Full GC (Ergonomics)  20365K->19939K(22528K), 0.0702009 secs]
2.407: [Full GC (Ergonomics)  20365K->19995K(22528K), 0.0694095 secs]

正如你所看到的,现在出现了大量的Full GC,并且回收的时间也增加了一个数量级。显然这是一次过早提升(premature promotion),不过却有点棘手。当然,罪魁祸首就是弱引用。在没使用它们之前,应用所创建的对象在提升到老生代前就已经被回收掉了。但是使用了弱使用之后,这些对象会多存活一次GC周期才能被正确地回收掉。一个简单的解决方案就是增加新生代的大小,-Xmx64m -XX:NewSize=32m:

2.328: [GC (Allocation Failure)  38940K->13596K(61440K), 0.0012818 secs]
2.332: [GC (Allocation Failure)  38172K->14812K(61440K), 0.0060333 secs]
2.341: [GC (Allocation Failure)  39388K->13948K(61440K), 0.0029427 secs]
2.347: [GC (Allocation Failure)  38524K->15228K(61440K), 0.0101199 secs]
2.361: [GC (Allocation Failure)  39804K->14428K(61440K), 0.0040940 secs]
2.368: [GC (Allocation Failure)  39004K->13532K(61440K), 0.0012451 secs]

于是这些对象便又能在新生代GC中被回收掉了。

下一个DEMO程序中我们用到了软引用,而情况就更为严重了。除非应用面临即将抛出OutOfMemoryError异常的风险,否则软可达的对象是不会被回收的。用软引用替换掉弱引用之后,DEMO程序立即便出现了更多的FULL GC事件:

2.162: [Full GC (Ergonomics)  31561K->12865K(61440K), 0.0181392 secs]
2.184: [GC (Allocation Failure)  37441K->17585K(61440K), 0.0024479 secs]
2.189: [GC (Allocation Failure)  42161K->27033K(61440K), 0.0061485 secs]
2.195: [Full GC (Ergonomics)  27033K->14385K(61440K), 0.0228773 secs]
2.221: [GC (Allocation Failure)  38961K->20633K(61440K), 0.0030729 secs]
2.227: [GC (Allocation Failure)  45209K->31609K(61440K), 0.0069772 secs]
2.234: [Full GC (Ergonomics)  31609K->15905K(61440K), 0.0257689 secs]

第三个DEMO程序中不难看出,虚引用是当之无愧的王者。使用相同的JVM参数运行这个程序,结果和弱引用的非常类似。由于我们在本节前面中所提到的两者在回收阶段的区别,因此事实上这里出现的Full GC暂停要更少一些。

不过,一旦加上停止清除虚引用的标记(-Dno.ref.clearing=true)之后,结果马上就变成了这样:

4.180: [Full GC (Ergonomics)  57343K->57087K(61440K), 0.0879851 secs]
4.269: [Full GC (Ergonomics)  57089K->57088K(61440K), 0.0973912 secs]
4.366: [Full GC (Ergonomics)  57091K->57089K(61440K), 0.0948099 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

虚引用的使用一定要慎之又慎,并且一定要及时清理虚可达对象。否则的话,等着你的就会是OutOfMemoryError了。相信我,不这么做的话你很快就会栽在这里:处理引用队列的那个线程抛出了一个异常,而等着你的只是一个挂掉的应用。

我的JVM会不会受到影响?

通常我们会建议使用-XX:+PrintReferenceGC这个JVM选项来分析不同的引用类型对垃圾回收所带来的影响。如果从弱引用的例子开始就加入这个选项的话,你会看到:

2.173: [Full GC (Ergonomics) 2.234: [SoftReference, 0 refs, 0.0000151 secs]2.234: [WeakReference, 2648 refs, 0.0001714 secs]2.234: [FinalReference, 1 refs, 0.0000037 secs]2.234: [PhantomReference, 0 refs, 0 refs, 0.0000039 secs]2.234: [JNI Weak Reference, 0.0000027 secs][PSYoungGen: 9216K->8676K(10752K)] [ParOldGen: 12115K->12115K(12288K)] 21331K->20792K(23040K), [Metaspace: 3725K->3725K(1056768K)], 0.0766685 secs] [Times: user=0.49 sys=0.01, real=0.08 secs] 
2.250: [Full GC (Ergonomics) 2.307: [SoftReference, 0 refs, 0.0000173 secs]2.307: [WeakReference, 2298 refs, 0.0001535 secs]2.307: [FinalReference, 3 refs, 0.0000043 secs]2.307: [PhantomReference, 0 refs, 0 refs, 0.0000042 secs]2.307: [JNI Weak Reference, 0.0000029 secs][PSYoungGen: 9215K->8747K(10752K)] [ParOldGen: 12115K->12115K(12288K)] 21331K->20863K(23040K), [Metaspace: 3725K->3725K(1056768K)], 0.0734832 secs] [Times: user=0.52 sys=0.01, real=0.07 secs] 
2.323: [Full GC (Ergonomics) 2.383: [SoftReference, 0 refs, 0.0000161 secs]2.383: [WeakReference, 1981 refs, 0.0001292 secs]2.383: [FinalReference, 16 refs, 0.0000049 secs]2.383: [PhantomReference, 0 refs, 0 refs, 0.0000040 secs]2.383: [JNI Weak Reference, 0.0000027 secs][PSYoungGen: 9216K->8809K(10752K)] [ParOldGen: 12115K->12115K(12288K)]

通常来说,只有当你确信自己的应用程序的吞吐量或者响应时延已经受到GC的影响时,才有必要去分析这类信息。只有在这种情况下,你才有必要去检查此类日志。一般情况下,每次GC周期中所回收的引用数都不多,大多数情况下都是0。如果应用程序花费了大量的时间在进行引用的清理或者出现大量引用被回收的情况,那就需要进行进一步的分析了。

解决方案

一旦你确认自己的程序出现了误用、滥用弱引用,软引用或虚引用的情况,这通常就需要调整一下应用的内部实现逻辑了。一般这都得视应用的具体情况而定,很难给出什么通用的建议。不过,应该牢记如下几条原则:

  • 弱引用:如果问题的现象是某个内存池的使用量上升的话,那么通常增加一下池的大小(或话干脆把整个堆调大)就能解决问题。正如上述示例中所见,增加堆的大小或者新生代的大小可以缓解此类问题。
  • 虚引用:一定要确认是否清除了引用。稍不留神便会忽略了某些情况,从而导致清理的线程无法及时地清除引用队列,或者甚至没有清理,从而将负担扔给了垃圾回收器,进而便会导致抛出OutOfMemoryError异常。
  • 软引用:一旦发现软引用是罪魁祸首,那就只能去修改下应用的实现逻辑了。

原创文章转载请注明出处: 弱引用,软引用及虚引用对GC的影响

英文原文链接

银联POS规范总结 (转载)

$
0
0

一、管理功能

1.系统管理

 

开机自检:硬件检测和自动报警、返回工作状态

程序下载:串行口程序下载、联机应用程序下载

参数管理:出厂参数、下发参数、可设定参数、可联机更改参数

终端状态:正常工作状态、已签退状态、锁定状态     

 

2.操作员管理

 

系统管理员:负责管理终端系统、包括软件下载、参数设置,代码99。设置主管操作员的代码和初始密码。

主管操作员:管理POS和其他操作员,撤销和退货交易要经过它的验证和确认,代码00。设置一般操作员的代码和初始密码。

一般操作员:POS签到、交易,密码自己修改。

 

3.应用管理

 

签到:操作员签到、POS签到、收银员积分签到

批结算:借贷记批结算的统计算法

批上送:成功的交易才进行批上送

签退:自动签退或手动签退

回响测试:POS终端与POS中心进行网络连通测试

参数传递:POS终端从POS中心下载参数

POS终端状态上送:硬件状态、下载的参数、通讯统计

TMS参数下载:TMS给POS中心提供相关参数的下载

交易查阅:查询交易明细、记录、余额

锁定功能:POS需要暂停工作

清除POS记录:需输入系统管理员密码

离线类交易和IC卡脱机交易上送:先上送联机交易

磁道数据加密:53域的磁道加密标志应该置1

 

二、交易功能

1.联机交易功能

 

余额查询:账户余额、积分余额

消费:普通消费、分期付款、积分、手机芯片、部分扣款、自动折扣、手机无卡预约、订购

消费撤销:必须是撤销当日当批的消费交易,必须在原POS上进行,撤销金额等于原金额

退货:支持部分退货和多次退货,除了电子现金不支持当日退货外,日期不限,金额小于等于原金额

预授权:不参与清算

预授权撤销:需核对原始预授权签购单据,主管操作员密码

预授权完成(请求):金融类请求交易,必须是同一商户

预授权完成(通知):发送结算报文,通知类交易

预授权完成撤销:原交易当日当批进行,金额相等

基于PBOC电子钱包应用的指定账户圈存交易:提交脱机PIN

基于PBOC电子钱包应用的非指定账户圈存交易:发卡行可以不同行

基于PBOC电子钱包应用的现金充值:联机完成

分期付款交易:只需出首款,剩余款项由发卡方暂时垫付

自动冲正:应答码为“00”“25”“12”表示冲正成功,查询和通知类交易不产生冲正

电子现金指定账户圈存交易:联机进行

电子现金非指定账户圈存交易:只支持传出卡为磁条卡的模式

电子现金现金充值:支持接触式和非接触式,非接触式应用于手机支付场景,走PBOC流程

电子现金现金充值撤销:全额撤销

磁条预付费卡现金充值:要先查询可充值余额

磁条预付费卡账户充值:转账交易,需提交PIN

 

2.离线交易功能

 

电子现金余额查询:直接读取

电子现金明细查询:脱机查询

离线结算(仅适用于外卡):手输卡号,不产生冲正

结算调整(仅适用于外卡):对金额进行调整,追加小费,或覆盖原交易,或生成新的交易

基于PBOC电子钱包消费交易:该交易不能撤销

电子现金消费交易:该交易不能撤销,如果被列入黑名单,应拒绝交易

 

 

三、交易流程

1.读卡处理

 

2.余额查询

 

3.明细查询

 

4.消费

 

5.消费撤销

 

6.退货

 

7.预授权

 

8.预授权撤销

 

9.预授权完成

 

10.预授权完成(请求)撤销

 

11.IC卡圈存交易处理流程

 

12.离线结算

 

13.结算调整

 

14.批结算

 

15.磁条预付费卡充值

 

 

四、消息域

2域:主账号

3域:交易处理码

4域:交易金额

11域:受卡方系统跟踪号

12域:受卡方所在地时间

13域:受卡方所在地日期

14域:卡有效期

15域:清算日期

22域:服务点输入方式码

23域:卡序列号

25域:服务点条件码

26域:服务点PIN服务码

32域:受理方标识码

35域:二磁道数据

36域:三磁道数据

37域:检索参考号

38域:授权标识应答码

39域:应答码

41域:受卡机终端标识码

42域:受卡方的标识码

44域:附加响应数据

48域:附加数据-私有

49域:货币交易代码

52域:个人标识码数据

53域:安全控制信息

54域:余额

55域:IC卡数据域

58域:PBOC电子钱包标准的交易信息

60域:自定义域

60.1域:消息类型码

60.2域:批次号

60.3域:网络管理信息码

60.4域:终端读取能力

60.5域:基于PBOC借/贷记标准的IC卡条件代码

60.6域:支持部分扣款和返回余额标志

60.7域:账户类型

61域:原始信息域

61.1域:原始交易批次号

61.2域:原始交易POS流水号

61.3域:原始交易日期

61.4域:原交易授权方式

61.5域:原交易授权机构代码

62域:自定义域

63域:自定义域

63.1域:自定义域(国际信用卡公司代码/操作员代码)

63.2域:自定义域

63.2.1域:发卡方保留域

63.2.2域:中国银联保留域

63.2.3域:受理机构保留域

63.2.4域:POS终端保留域

64域:报文鉴别码

 

 

五、报文格式

1.消息格式

TPDU

报文头

应用数据

ISO8583 Msg

ID

目的

地址

源地址

应用类别定义

软件

总版本号

终端

状态

处理

要求

软件分版本号

交易数据

60H

NN NN

NN NN

N2

N2

N1

N1

N6

不定长度

应用类别定义:

60:磁条卡金融支付类

61:IC卡金融支付类

62:磁条卡增值业务类支付

63:IC卡增值业务类支付

 

处理要求:

0:无处理要求

1:下传终端磁条卡参数

2:上传终端磁条卡状态信息

3:重新签到

4:通知终端发起更新公钥信息操作

5:下载终端IC卡参数

6:TMS参数下载

7:卡BIN黑名单下载

8:币种汇率下载(仅在境外使用)/助弄取款手续费比率下载(仅在境内使用)

 

2.交易

 

查询

消费

消费冲正

消费撤销

消费撤销冲正

退货

预授权

预授权撤销

预授权撤销冲正

预授权完成(请求)

预授权完成(通知)

预授权完成(请求)冲正

预授权完成撤销

预授权完成撤销冲正

基于PBOC借/贷记标准IC卡脚本处理结果通知(含小额支付应用)

基于PBOC电子钱包/存折标准的IC卡指定账户圈存

基于PBOC电子钱包/存折标准的IC卡指定账户圈存冲正

基于PBOC电子钱包/存折标准的IC卡现金充值

基于PBOC电子钱包/存折标准的IC卡现金充值冲正

基于PBOC电子钱包/存折标准的IC卡非指定账户转账圈存

基于PBOC电子钱包/存折标准的IC卡非指定账户转账圈存冲正

基于PBOC借/贷记标准的IC卡电子现金指定账户圈存

基于PBOC借/贷记标准的IC卡电子现金指定账户圈存冲正

基于PBOC借/贷记标准的IC卡电子现金现金充值

基于PBOC借/贷记标准的IC卡电子现金现金充值冲正

基于PBOC借/贷记标准的IC卡电子现金非指定账户转账圈存

基于PBOC借/贷记标准的IC卡电子现金非指定账户转账圈存冲正

磁条卡现金充值账户验证

磁条卡现金充值

磁条卡现金充值确认

磁条卡账户充值(转账)

基于PBOC借/贷记标准的IC卡电子现金现金充值撤销

基于PBOC借/贷记标准的IC卡电子现金现金充值撤销冲正

 

3.离线类和脱机类

 

离线结算

结算调整

基于PBOC电子钱包/存折标准的IC卡离线交易

基于PBOC借/贷记标准的IC卡离线交易

 

4.管理类

 

签到

收银员积分签到

签退

批结算

批上送金融交易/批上送结束

批上送通知交易

基于PBOC借/贷记IC卡批上送通知交易

POS参数传递

回响测试

POS状态上送

IC卡公钥/参数/TMS参数/卡BIN黑名单下载结束

基于PBOC电子钱包/存折标准的IC卡圈存批上送通知

 

 

六、凭证要素

名称

中文标题

英文标题

要求

格式

来源

特约商户名称

商户名称

MERCHANT NAME

必须

 

POS终端

特约商户编号

商户编号

MERCHANT NO.

必须

 

42域

终端机编号

终端机编号

TERMINAL NAME

必须

 

41域

收单行标识码

收单机构

ACQ INSTITUTE

必须

 

44域

发卡行标识码

发卡机构

ISS .

必须

 

44域

POS中心标识码

 

 

可选

 

32域

卡号

卡号

CARD NO.

必须

 

2域

卡片序列号

卡片序列号

CARD SEQUENCE NUMBER

可选(基于PBOC借/贷记应用的小额支付必选)

 

23域

操作员号

操作员号

OPERATOR NO.

必须

 

POS终端

交易类型

交易类型

TRANS TYPE

必须

消费(SALE)

0域, 注 1

卡有效期

有效期

EXP DATE

可选

YYYYMM

14域

交易批次号

批次号

BATCH NO.

必须

 

60域

交易凭证号

凭证号

VOUCHER NO.

必选

 

11域

交易日期和时间

日期/时间

DATE/TIME

必选

YYYY/MM/DD  hh:mm:ss

12,13域

授权码

授权码

AUTH NO.

可选

 

38域

参考号

参考号

REFER NO.

必选

 

37域

交易金额

金额

AMOUNT

必选

¥1234.56或RMB1234.56

4域-48域

小费金额

小费

TIP

可选

¥1234.56或RMB1234.56

48域

累计金额

累计

 

 

¥1234.56或RMB1234.56

54域

总金额

总计

TOTAL

可选

¥1234.56或RMB1234.56

4域

国际信用卡公司代码

卡组织

 

外卡必选

MCC(万事顺)

63域

不可预知数

不可预知数

Unpredictable Number

可选(基于PBOC借/贷记应用的小额支付脱机消费交易必选)

 

55域 Tag9F37

应用交互特征(AIP)

应用交互特征

Application Interchange Profile

可选(基于PBOC借/贷记应用的小额支付脱机消费交易必选)

 

55域

Tag9F82

备注

备注

REFERENCE

可选

 

注 2

持卡人签名栏

持卡人签名栏

CARDHOLDER SIGNATURE

必选

 

注 3

IC卡交易证书

IC卡交易证书

TXN CERT.

可选(EMV/PBOC标准借/贷记应用必选,基于PBOC借/贷记应用的小额支付脱机消费交易必选)

 

EMV/PBOC借/贷记卡

终端验证结果(TVR)

终端验证结果

TVR

可选(EMV/PBOC标准借/贷记应用、基于PBOC借/贷记应用的小额支付脱机消费交易必选)

 

EMV/PBOC借/贷记卡

交易状态信息(TSI)

交易状态信息

TSI

(EMV/PBOC标准借/贷记应用、基于PBOC借/贷记应用的小额支付脱机消费交易必选)

 

EMV/PBOC借/贷记卡

应用标识(AID)

应用标识

AID

可选(EMV/PBOC标准借/贷记应用、指定账户圈存交易必选)

 

EMV/PBOC借/贷记卡

应用交易计数器(ATC)

应用交易计数器

ATC

可选(EMV/PBOC标准借/贷记应用、基于PBOC借/贷记应用的小额支付、IC卡指定帐户圈存、非指定帐户圈存、现金充值必选)

 

EMV/PBOC借/贷记卡

发卡行应用数据

发卡应用数据

IAD

可选(基于PBOC借/贷记应用的小额支付脱机消费交易必选)

 

PBOC借贷记卡

应用标签

应用标签

Appl Label

可选(EMV/PBOC标准借/贷记应用必选)

 

EMV/PBOC借/贷记卡

应用首选名称

应用标签

Appl Name

可选(EMV/PBOC标准借/贷记应用必选)

 

EMV/PBOC借/贷记卡

交易验证码(TAC)

交易验证码

TAC

可选(基于PBOC电子钱包标准的IC卡必选)

 

基于PBOC电子钱包标准的IC卡交易

充值后卡片余额

充值后卡片余额

ECA

可选(基于PBOC借/贷记应用的小额支付IC卡指定帐户圈存、非指定帐户圈存、现金充值必选)

 

基于PBOC借/贷记应用的小额支付IC卡交易

授权请求密文(ARQC)

授权请求密文

ARQC

可选(快速PBOC借/贷记应用的联机交易必选)

 

快速PBOC借/贷记应用联机交易

扣持卡人金额

扣持卡人金额

 

可选(折扣消费交易必选)

 

折扣消费交易

分期付款期数

分期付款期数

Installment Period

可选(分期付款消费交易必选)

n2

分期付款消费交易请求

分期付款持卡人首期金额

分期付款首期金额

Initial Installment Payment

可选(分期付款消费交易必选)

n12

分期付款消费交易应答

分期付款持卡人还款币种

分期付款还款币种

 

可选(分期付款消费交易必选)

N3

分期付款消费交易应答

分期付款持卡人手续费

持卡人手续费

Installment Charge

可选(分期付款消费交易必选)

n12

分期付款消费交易应答

商品代码

商品代码

 

可选(积分消费交易中,发卡行与商户约定的情况下可选)

ans30

积分消费交易请求

兑换积分

兑换积分数

Exchange Points

可选(积分消费交易必选)

n10

积分消费交易应答

积分余额

积分余额数

Points Banlance

可选(积分消费交易必选)

n10

积分消费交易应答

自付金额

自付金额

Outstanding Amount

可选(积分消费交易必选)

n12

积分消费交易应答

注 1:交易类型

说明:根据8583 中MESSAGE TYPE 和PROCESSING CODE 唯一确认交易类型, 如果终端可以打印中英文,应选择如下内容:

消费(SALE)、消费撤销(VOID)、退货(REFUND)、预授权(AUTH)、预授权撤销(CANCEL)、预授权完成(通知)(AUTH SETTLEMENT)、预授权完成(请求)(AUTH COMPLETE)、预授权完成撤销(COMPLETE VOID)、电子钱包消费(EP SALE)、分期付款消费(INSTALLMENT)、分期付款消费撤销(VOID)、积分消费(BONUS)、积分消费撤销(VOID)、离线结算(OFFLINE)、结算调整(ADJUST)、电子现金消费(EC SALE)、电子现金退货(EC REFUND)、预约消费(RESERVATION SALE)、预约消费撤销(VOID)、订购消费(MOTO SALE)、订购消费撤销(VOID)、订购退货(REFUND)、电子现金指定账户圈存(EC LOAD)、电子现金非指定账户圈存(EC LOAD)、电子现金现金充值(EC LOAD)、电子现金充值撤销(EC LOAD VOID)、磁条卡现金充值(ACCOUNT LOAD)、磁条卡账户充值(ACCOUNT LOAD)。

如果终端只能打印英文,可以选择上述内容中英文。

对于刷卡的交易,应在交易类型之后打印“(S)”;对于芯片卡接触式插入交易,应在交易类型之后打印“(I)”;对于芯片卡非接触式挥卡交易,应在交易类型之后打印“(C)”;对于手输卡号交易,应在交易类型之后打印“(M)”;对于无卡交易,应在交易类型之后打印“(N)”。

 

注 2: 备注栏

用于打印一些参考信息,这些信息是否需要打印一般与交易类型相关。可能出现的备注信息有:

1、      原交易凭证信息:撤销交易,打印原交易凭证号;退货交易,打印原交易参考号和原交易日期。

2、      预授权码:预授权撤销、预授权完成(请求)、预授权完成(请求)撤销时输入的原预授权码。

3、      重打印标志:重打印功能印出的凭证,必须在备注栏打印该标志。

4、      收单方需要持卡人了解的信息(域63.2.3)。

5、      PBOC标准借/贷记IC卡消费和预授权交易,应打印应用标识(AID)、IC卡交易证书(TC)。

6、      快速PBOC借/贷记联机应用,应打印应用标识(AID)、IC卡交易证书(TC)、打印授权请求密文(ARQC)。

7、      基于PBOC借/贷记应用的小额支付,应打印卡片序列号、应用标识(AID)、IC卡交易证书(TC)、终端验证结果(TVR)、交易状态信息(TSI)、应用交易计数器(ATC)、不可预知数、应用交互特征(AIP)、卡片余额和发卡行应用数据(包括卡片验证结果(CVR))。

8、      基于PBOC借/贷记应用的小额支付指定帐户圈存、非指定帐户圈存、现金充值交易,应打印应用标识(AID)、充值后卡片余额、应用交易计数器(ATC),其中非指定账户圈存需要打印转出卡卡号(应隐去转出(磁条)卡卡号校验位前4位数字)。

9、      对于PBOC电子钱包的IC卡消费交易,还应打印发卡方标识、IC卡脱机交易序号(CTC)、密钥版本号、密钥索引号、PSAM卡的终端机编号、PSAM卡的终端交易序号(TTC)和TAC.

10、   对于PBOC电子钱包的IC卡圈存交易,还应打印发卡方标识、IC卡联机交易序号(CTC)、密钥版本号、密钥索引号和TAC。

11、   对于折扣消费还应打印实扣持卡人金额。

12、   对于部分扣款应在交易金额处打印承兑金额。

13、   对于分期付款消费交易和分期付款消费撤销交易,还应打印分期付款期数、分期付款首期金额、分期付款还款币种(156为人民币)和持卡人手续费。当为一次性支付手续费时,不用打印个性化信息中的“首期手续费”和“每期手续费”;当为分期支付手续费时,不用打印“持卡人手续费”。

14、   对于积分消费交易,还应打印商品代码、兑换积分数、积分余额(以应答报文的54域为准)和自付金金额;对于积分消费撤销交易,还应打印兑换积分数和自付金金额。

15、   对于磁条预付费卡的消费、消费撤销、预授权、预授权撤销、预授权完成、预授权完成撤销交易和联盟积分卡的消费、消费撤销交易,需打印可用余额(域54)

16、   对于小额支付脱机消费,签购单打印张数可设为1张。

17、   备注栏的打印要确保汉字不断行,如果出现只剩下一个字节的空间,移到下一行开始打印。

18、   对于预约消费交易,根据请求报文信息打印手机号码(手机号码屏蔽,除前三位和后三位以外全部显示星号)

其中:

1、     原交易凭证号:“原凭证号/VOUCHER:”+原交易凭证号(6位数字)

2、     预授权码:“预授权码/AUTH NO:”+预授权码(数字或字母)

3、     重打印标志:“重打印凭证/DUPLICATED”

4、     发卡行回传信息:原样打印。

5、     POS中心回传信息:原样打印。

6、     收单方回传信息:原样打印。

  注:以上存在多个组合的情况下,分多行打印。

 样例:            预授权码/AUTH NO:453521

                            重打印凭证/DUPLICATED

 

注 3:持卡人签名栏

说明:在持卡人签名栏的适当位置,一般有以下说明:

         “本人确认以上交易,同意将其记入本卡账户/ I ACKNOWLEDGE

           SATISFATORY RECEIPT OF RELATIVE GOODS/SERVICES”。

      在签名档的最后一行,允许打印一行(8个汉字或16个英文字符)与交易无关的信息,套打可以不支持。

 

 

字母

交易类型

S

消费

T

分期付款消费

B

积分消费电子钱包/存折

E

电子现金(钱包)消费

R

退货

P

预授权完成(请求)

C

预授权完成(通知)

L

离线结算

Q

圈存类、充值类交易

 

 

七、通信方式

1.双工连接

建立连接过程:

建立连接时序:

2.每个端口只对应一个客户端

3.监控报文的格式

报文长度位(2Byte)

TPDU(5Byte)

LRI首标志位(5Byte)

ANI主叫号码(8Byte 右BCD)

DNIS被叫号码(8Byte右BCD)

LRI尾标志位(12Byte)

报文头+报文数据

 

 

八、安全加密

1.MAC的算法

MTI到63域的信息构成MAB,将MAB按每8个字节做异或,最后如果不满8位则添加0X00.

比如:MAB= M1 M2 M3 M4

M1=MS11 MS12 MS13 MS14 MS15 MS16 MS17 MS18

M2=MS21 MS22 MS23 MS24 MS25 MS26 MS27 MS28

M3=MS31 MS32 MS33 MS34 MS35 MS36 MS37 MS38

M4=MS41 MS42 MS43 MS44 MS45 MS46 MS47 MS48

 

然后,M1与M2异或,得到TM1,再与M3异或,得到TM2,再与M4异或,得到TM3。

将TM3扩展成16个字节,得到TM3=TM31 TM32

将TM31用MAK加密得到EN1,将EN1与TM32异或,得到TE1

将TE1用MAK加密得到EN2,将EN2扩展成16个字节,得到EN2=EN21 EN22

则MAC=EN21

 

2.磁道信息加密算法

对于磁道数据,从结束符向左第二个字节开始,再向左取8个字节,作为参与加密的TDB。

比如:

二磁道数据(37):1234567890123456789=0508201781999168302

表示为:0x12 0x34 0x56 0x78 0x90 0x12 0x34 0x56 0x78 0x9D 0x05 0x08 0x20 0x17 0x81 0x99 0x91 0x68 0x30 0x20

则TDB2表示为:0x08 0x20 0x17 0x81 0x99 0x91 0x68 0x30

采用TDK对TDB2进行TDES加密,加密后的磁道信息为:

ENC BLOCK1 = eTDK(0x08 0x20 0x17 0x81 0x99 0x91 0x68 0x30)

 

3.PIN的加解密

PIN的长度为6位,可扩展到12位。

加密算法示例:

例如:明文PIN为: 123456,

假设: 磁卡上的PAN:1234 5678 9012 3456 78

   截取下的PAN:6789 0123 4567

则用于PIN加密的PAN为:0x00 0x00 0x67 0x89 0x01 0x23 0x45 0x67

     PIN BLOCK为:0x06 0x12 0x34 0x56 0xFF 0xFF 0xFF 0xFF

                      异或:0x00 0x00 0x67 0x89 0x01 0x23 0x45 0x67

                 结果为:0x06 0x12 0x53 0xDF 0xFE 0xDC 0xBA 0x98

解密算法,步骤倒置即可。

 

 

九、配置参数

参数大类

参数名称

参数格式、取值

备注

终端参数

商户编号

N15

 

终端编号

AN8

 

安全密码

N6

控制商户编号与终端编号修改的权限

商户名称

Ans40

 

当前年份

N4,YYNN

下拉框实现

流水号

N6

 

批次号

N6

 

最大退货金额

 

 

结算打印明细设置

是/否

缺省为否

英文设置

是/否

热敏时出现,缺省为是

签购单设置

新/旧

针打时出现,缺省为新

默认交易设置

消费/预授权

默认消费

消费撤销是否刷卡

是/否

默认否

消费撤销是否输密

是/否

默认否

预授权完成撤销是否刷卡

是/否

默认否

预授权完成撤销是否输密

是/否

默认是

预授权撤销是否输密

是/否

默认是

预授权完成(请求)是否输密

是/否

默认是

通讯参数

拨号、串口、GPRS、CDMA、以太网

可选

 

通讯参数-共有参数

TPDU

 

默认60

是否预拨号

是/否

默认是

交易超时时间

 

默认60S

交易重拔次数

 

默认3次

通讯参数-拨号

外线号码

 

 

中心交易号码1

 

 

中心交易号码2

 

 

中心交易号码3

 

 

通讯参数-GPRS

接入号码

 

 

APN1名称

 

 

主机1 IP地址

 

 

主机1 端口

 

 

主机2 IP地址

 

 

主机2 端口

 

 

用户名设置

是/否

默认为否

 

用户名

 

 

密码

 

 

通讯参数-CDMA

接入号码

 

 

主机1 IP地址

 

 

主机1 端口

 

 

主机2 IP地址

 

 

主机2 端口

 

 

用户名

 

 

密码

 

 

通讯参数-以太网

本机IP地址

 

 

子网掩码

 

 

网关

 

 

主机1 IP地址

 

 

主机1 端口

 

 

主机2 IP地址

 

 

主机2 端口

 

 

交易功能设置

屏蔽设置

 

面对所有支持的交易

交易功能设置-屏蔽支持设置

消费撤销

是/否

默认是

查询

是/否

默认是

预授权

是/否

默认是

离线结算

是/否

默认是

结算调整

是/否

默认是

退货

是/否

默认是

预授权撤销

是/否

默认是

预授权完成请求

是/否

默认是

预授权完成撤销

是/否

默认是

圈存类

是/否

默认是

预授权完成通知

是/否

默认是

电子现金消费

是/否

默认是

电子钱包消费

是/否

默认是

分期付款消费

是/否

默认是

积分消费

是/否

默认是

手机芯片消费

是/否

默认是,UpCard应用

预约消费

是/否

默认是

订购消费

是/否

默认是

订购授权类

是/否

默认是

磁条卡现金充值

是/否

默认是

磁条卡账户充值

是/否

默认是

电子现金退货

是/否

默认是

交易功能设置-签退设置

结算后自动签退

是/否

默认是

交易功能设置-小费比例

可设置

数值

 

交易功能设置-消息重发次数

重发次数

N1

默认3

交易功能设置-打印张数

打印张数

N1

默认为2

交易功能设置-最大交易笔数

最大交易笔数

N3

默认为500

其他功能

终端密钥索引

N2

(00,99)

修改管理员密码

 

 

修改安全密码

 

 

清除交易流水

是/否

默认为是

参数打印

 

 

快捷键设置

 

弹出二级界面

显示可以通过快捷键配置的交易类型

原地址: http://www.cnblogs.com/miyosan/archive/2012/08/28/2660268.html



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



如何规避抓取别家数据的潜在风险?

$
0
0

我对星球大战的喜爱始于高中,那阵恰逢周五晚电影频道佳片有约安排播出。我看后便再也无法忘却天行者阿纳·金在塔图因的血色残阳下疾驰的身影。他悲剧性的黑化过程与对力量的迫切渴望成了我叛逆青春的注脚。

然而回到现实,一路创业跌跌撞撞走来,遇到的苦难问题不计其数,纯粹的「力量」似乎并不能解决一切问题,你必须提升「智力」,你的团队必须学会「敏捷」,尤其是在我朝这片土地上。

何况尘世间的事并不都像科幻电影里那样,能清晰地界定出原力的黑暗面与光明面,你从中选择一方站队。很多时候,你必须在灰色地带游走,怀揣光明之心,动用黑暗能量。这过程中,稍有不慎脚下踏空,或将万劫不复永堕业火,但也总有高人得以竹杖芒鞋凌波微步,火中取粟之后全身而退。

《增长黑客》里提到一些数据抓取解决冷启动的案例。其实在互联网行业里,依靠抓取来获取数据的做法,并不罕见(如果你觉得很新鲜,那只能说道行太浅)。但我敢于将其写出来,这当中自然会遭到一些质疑和非议,参加的大大小小活动也总有人提出如何规避风险的问题。

在此我就「如何规避抓取别家数据的潜在风险?」的问题在此简单阐述我的观点:

第一,技术是中立的,本身不带有任何倾向性。如何你觉得从别的平台「右键另存为」来获得素材运用到自己的产品里不构成任何问题,那么写脚本批量抓取没有改变这件事的性质,它只是帮你将原本需要人工操作三天的事情简化到三个小时内自动完成。

第二,平台各有自己的政策,不同平台的抓取策略不同。有的平台明确在自己的用户协议里声明,「本平台只承担数据存储的作用,内容版权隶属原作者所有」,这时候,你完全可以征得原作者同意之后,以你觉得方便的姿势去获取。至于如何快速大量获得原作者同意?通过写脚本批量发私信给目标群体就可以,这个行为的性质参见第一条。

第三,分清学习目的与商用目的。我专门查询过版权法等相关法律法规,其中对「出于学习目的」是有专门的分类讨论的。如果将抓取来的数据用于产品上线前内部的测试、参考、决策依据,我认为是属于这一范围的(当然还是得具体问题具体分析)。至于正式上线成为一款商业产品,那么还是小心为妙。

最后,你有没有越过界其实你自己心里清楚。


如何通过电子邮件了解你的客户

$
0
0

对电子商务行业而言,相较于社交网络或者全渠道复杂多样的营销方式,邮件营销则看起来较为简单:我们只是将信息发送给客户,并且希望客户喜欢这些内容。

显然,在真实的邮件营销层面远非如此简易。我们会解读电子邮件的相关数据,对比不同电子邮件营销活动的反馈。只有经过深入的数据分析,邮件营销才能真正有效。Webpower邮件营销平台就可以为企业提供全方位数据统计监测报告、个性化活动绩效分析报告、以及方便查询绩效转化点,并由专业咨询师针对性提供优化意见。帮助客户一目了然的了解营销效果,并为持续优化做铺垫。

A / B测试是非常基本的电子邮件营销技能,即我们发送电子邮件的一个版本(称之为A)给10%的客户,与此同时发送电子邮件的另一个版本(称之为B)给10%的客户。然后通过比较这两部分数据来决定向剩余的80%的客户发送哪封最佳邮件版本。

邮件营销A / B测试技巧是培育优秀邮件内容的前提。Webpower首席数据分析师Dr.Maurits Kaptein认为其实这其中也包含了未被充分验证的部分,我们可能并没有通过A / B测试真正筛选出最佳邮件版本,实际上,我们只是对20%的客户进行了测试,其余80%客户其实是割裂状态。如果我们能相对充分地覆盖客户,才真正较为客观地进行了消费者洞察。

动态A / B测试

这里有一个基本事实:在A / B测试期间,有一半概率接收到电子邮件“A”,另一半概率接收到“B”。试验结束后,如果“A”的转化率为最高,“A”与“B”的概率分别变​​为1和0。然而随着时间的推移,这一概率比例会被平顺地改变。当缺乏数据支撑时,概率演变为1/2和1/2。但是在小范围测试中,若“A”有更好的表现,概率值变为2/3和1/3。也就是说,做动态A / B测试,在测算品质上是完胜静态A / B测试的。

动态A / B测试的好处不仅在于它在准确度上优于静态的,它还允许添加新的测试选项,并覆盖更多的时间。因此,在面对一封自动生成的“购物车挽回”的电子邮件时,你可以随时增加新的邮件版本测试最佳呈现效果。你不该只是做“是”或“否”的单一测试,而是要适时地持续优化。

不断学习

使用动态A / B测试,为邮件与客户互动空间开辟了更多的空间。比如,你如何通过推广邮件确定一个新的在线服务产品的最佳价格?如果定价太高,可能没有人会购买该产品;如果定价太低,又可能无法盈利。

所以该如何取舍平衡呢?不妨试试摒弃付费的市场研究机构所做的潜在客户成本分析,激发邮件功能,有效利用相关数据探索最优价格。在邮件中,尝试新的定价给客户,观察他们的反馈,计算你的收益,并不断调整实时更新价格策略。可称之为结合邮件反馈的动态定价。

个性化发送

如果将电子邮件营销活动延伸开来看,可以与客户个体有连续的互动学习。比如你每周的newsletter是针对同一组收件人,基于客户newsletter的反馈你可以展开积极地调研,从而发现是什么让用户买单。

经过反复的邮件试探,我们可以了解客户的产品偏好,他喜欢什么电影、音乐。或者他喜欢公路自行车运动还是攀岩?我们可以应用电子邮件对这些问题展开积极测试,并建立客户的个人资料。

然而,我们也可以超越简单的产品偏好层面,进一步了解客户更高级的需求,比如他是否喜欢折扣,他是否热衷追逐潮流?电子邮件之于我们,不单单是依次去了解客户的手段。我们更可以借此建立个人电子身份档案,更好地描摹出个人的心理轮廓。对此,Webpower可以提供无论是电子邮件还是网页端的数据分析。

电子邮件也可以很酷

电子邮件渠道并不是“批量”和“推送”的概念,而是一个互动渠道。电子邮件可以与社交网络一样让你生动地了解客户。你可以由电子邮件渠道触发,结合其他渠道,全面洞察消费者,并将其运用于商务决策层面。

著名的密码恢复工具Hashcat password cracker开源

$
0
0

Hashcat工具的开发者Jens ‘Atom’ Steube宣布将Hashcat的源代码进行开放。Hashcat和oclHashcat是广大侵入测试员及安全专家常用的高级密码恢复和破解工具,该工具基于CPU和GPU,可跨平台使用。Hashcat及其变种版oclHashcat的代码现在都可以从GitHub上获得。开源声明发布后,在GitHub上的相关讨论区内也引发了激烈的讨论。

Atom在一篇论坛文章中介绍道,之前对是否开放源代码进行过一些讨论,但那时仍有一些问题有待解决。Hashcat现在采用了MIT许可证,可以与许多Linux发行版(如Ubuntu)集成,用于Kali Linux的软件包也正在开发中,预计在今后发布。

Atom写道:“Hashcat开源后,集成外部库变得十分简便。之前由于许可证问题,几乎不可能集成外部库。一些crypto库需要非常严格的许可证,而且不允许在二进制文件中集成其代码。即便可以集成,所需的前提条件也非常严苛。针对这一点,Hashcat及oclHashcat不需要任何外部库,但有时仅仅解析自身的hash就十分复杂,甚至比GPU内核本身更具挑战性。GPG就是一个很好的例子,如果Hashcat和oclHashcat的源代码开放,就能够很容易地添加GPG。”

由于苹果公司对内核代码的离线编译进行了限制,因此OS X操作系统不支持Hashcat。但随着Hashcat的开源,使用Mac的开发者可以用苹果协议编译内核,因此,支持oclHashcat的OS X系统现在也成为了可能。

Atom等开发者计划把Hashcat和 oclHashcat最终合并为一个项目,统一称为“Hashcat”。项目合并的基本思路是:将当前的oclHashcat作为基础merge分支(目前开发者的大部分精力都用在了oclHashcat项目上),接下来将Hashcat归入oclHashcat,最后将项目名称更改为“Hashcat”。这项合并工作意义重大,其难度也不容忽视,短期内难以完成。不过在代码开源之后,开发社区的朋友们众人拾柴火焰高,也可能会使合并工作完成得更快。

华为计划进军美国智能手机市场

$
0
0
华为计划进军美国智能手机市场。这家中国公司的电信网络设备由于安全担忧仍被美国拒之门外。华为是全球第三大智能手机生产商,次于三星和苹果。该公司计划明年首次在美国市场推出其高端旗舰智能手机Mate 8。华为今年和Google合作生产Nexus 6P手机,这是华为意欲在美国扩大品牌知名度的一个证明。据参与Nexus手机生产的知情人士称,为了开发这款手机,华为的设计师和工程师访问了美国,并和Google同行展开了密切合作。到目前为止,在美国市场最成功的中国智能手机品牌是中兴。






tomcat自动化部署脚本

$
0
0

   一个简单的tomcat自动化部署脚本 ,实现功能:

 

   (1) 检查tomcat进程是否存在,如果存在则kill掉

   (2) 备份现有war包到tomcat/backup目录

    (3) 复制当前目录新war包到tomcat/webapps目录

  (4) 启动tomcat

 

#!/bin/bash
now=`date +%Y%m%d%H%M%S`
tomcatPath=/home/tomcat/software/tomcat6
backupPath=/home/tomcat/software/tomcat6/backup
war=$1

if [ -e "$war.war" ]; then
  echo -e "\033[34m war archive: $war.war \033[0m"
else 
  echo -e "\033[31m war archive '$war.war' not exists \033[0m"
  exit -1
fi
# change color
echo -e "\033[34m"
#create backup dir
if [ ! -d "$backupPath" ]; then
  mkdir "$backupPath"
fi
echo "tomcat home: $tomcatPath"
echo "backup path: $backupPath"
echo 'try to stop tomcat...'

pid=`ps aux|grep "java"|grep "$tomcatPath"|awk '{printf $2}'`
if [ -n $pid ]; then
  echo "tomcat pid: $pid";
  kill -9 $pid;
fi

echo 'stop tomcat finished...'
echo 'backup old archive...'
if [ -f "$tomcatPath/webapps/$war.war" ]; then
  mv -v "$tomcatPath/webapps/$war.war" "$backupPath/$1_$now.war";
fi

rm -rf $tomcatPath/webapps/$war*
echo "copy $war.war archive to webapps.."
cp -v "$war.war" "$tomcatPath/webapps/"

echo -e "\033[32m"
echo 'startup tomcat...'

sh $tomcatPath/bin/startup.sh

tail -10f $tomcatPath/logs/catalina.out

   

 

   使用时,需要先修改tomcatPath的值为实际tomcat路径。

 

    保存该文件到autodeploy.sh, 执行命令: 

./autodeploy.sh  abc

   abc为待发布的war包名称,即当前目录存在两个文件:

   autodeploy.sh和abc.war



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Webmagic 一个网络爬虫工具包

$
0
0

webmagic是一个开源的Java垂直爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发。webmagic的核心非常简单,但是覆盖爬虫的整个流程,也是很好的学习爬虫开发的材料。作者曾经进行过一年的垂直爬虫的开发,webmagic就是为了解决爬虫开发的一些重复劳动而产生的框架。

web爬虫是一种技术,webmagic致力于将这种技术的实现成本降低,但是出于对资源提供者的尊重,webmagic不会做反封锁的事情,包括:验证码破解、代理切换、自动登录、抓取静态资源等。

webmagic的架构和设计参考了以下两个项目,感谢以下两个项目的作者:

python爬虫 scrapy  https://github.com/scrapy/scrapy

Java爬虫 Spiderman  https://gitcafe.com/laiweiwei/Spiderman

webmagic遵循 Apache 2.0协议,你可以自由进行使用和修改。有使用不便或者问题,欢迎在github 提交issue,或者在 oschina讨论模块提问。

 

快速开始

使用maven

webmagic使用maven管理依赖,你可以直接下载webmagic源码进行编译:

git clone https://github.com/code4craft/webmagic.git
mvn clean install

安装后,在项目中添加对应的依赖即可使用webmagic:

<dependency><groupId>us.codecraft</groupId><artifactId>webmagic-core</artifactId><version>0.2.0</version></dependency><dependency><groupId>us.codecraft</groupId><artifactId>webmagic-extension</artifactId><version>0.2.0</version></dependency>

项目结构

webmagic主要包括两个包:

  • webmagic-core

    webmagic核心部分,只包含爬虫基本模块和基本抽取器。webmagic-core的目标是成为网页爬虫的一个教科书般的实现。

  • webmagic-extension

    webmagic的扩展模块,提供一些更方便的编写爬虫的工具。包括注解格式定义爬虫、JSON、分布式等支持。

webmagic还包含两个可用的扩展包,因为这两个包都依赖了比较重量级的工具,所以从主要包中抽离出来:

  • webmagic-saxon

    webmagic与Saxon结合的模块。Saxon是一个XPath、XSLT的解析工具,webmagic依赖Saxon来进行XPath2.0语法解析支持。

  • webmagic-selenium

    webmagic与Selenium结合的模块。Selenium是一个模拟浏览器进行页面渲染的工具,webmagic依赖Selenium进行动态页面的抓取。

在项目中,你可以根据需要依赖不同的包。

不使用maven

<<<<<<< HEAD

不使用maven的用户,可以下载这个二进制打包版本(感谢 oschina):

=======

不使用maven的用户,可以下载附带二进制jar包的版本(感谢 oschina):

>>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

git clone http://git.oschina.net/flashsword20/webmagic-bin.git

在**bin/lib**目录下,有项目依赖的所有jar包,直接在IDE里import即可。

第一个爬虫

定制PageProcessor

PageProcessor是webmagic-core的一部分,定制一个PageProcessor即可实现自己的爬虫逻辑。以下是抓取osc博客的一段代码:

public class OschinaBlogPageProcesser implements PageProcessor {

    private Site site = Site.me().setDomain("my.oschina.net")
       .addStartUrl("http://my.oschina.net/flashsword/blog");

    @Override
    public void process(Page page) {
        List<String> links = page.getHtml().links().regex("http://my\\.oschina\\.net/flashsword/blog/\\d+").all();
        page.addTargetRequests(links);
        page.putField("title", page.getHtml().xpath("//div[@class='BlogEntity']/div[@class='BlogTitle']/h1").toString());
        page.putField("content", page.getHtml().$("div.content").toString());
        page.putField("tags",page.getHtml().xpath("//div[@class='BlogTags']/a/text()").all());
    }

    @Override
    public Site getSite() {
        return site;

    }

    public static void main(String[] args) {
        Spider.create(new OschinaBlogPageProcesser())
             .pipeline(new ConsolePipeline()).run();
    }
}

这里通过page.addTargetRequests()方法来增加要抓取的URL,并通过page.putField()来保存抽取结果。page.getHtml().xpath()则是按照某个规则对结果进行抽取,这里抽取支持链式调用。调用结束后,toString()表示转化为单个String,all()则转化为一个String列表。

Spider是爬虫的入口类。Pipeline是结果输出和持久化的接口,这里ConsolePipeline表示结果输出到控制台。

执行这个main方法,即可在控制台看到抓取结果。webmagic默认有3秒抓取间隔,请耐心等待。

使用注解

webmagic-extension包括了注解方式编写爬虫的方法,只需基于一个POJO增加注解即可完成一个爬虫。以下仍然是抓取oschina博客的一段代码,功能与OschinaBlogPageProcesser完全相同:

@TargetUrl("http://my.oschina.net/flashsword/blog/\\d+")
public class OschinaBlog {

    @ExtractBy("//title")
    private String title;

    @ExtractBy(value = "div.BlogContent",type = ExtractBy.Type.Css)
    private String content;

    @ExtractBy(value = "//div[@class='BlogTags']/a/text()", multi = true)
    private List<String> tags;

    public static void main(String[] args) {
        OOSpider.create(
            Site.me().addStartUrl("http://my.oschina.net/flashsword/blog"),
            new ConsolePageModelPipeline(), OschinaBlog.class).run();
    }
}

这个例子定义了一个Model类,Model类的字段'title'、'content'、'tags'均为要抽取的属性。这个类在Pipeline里是可以复用的。

注解的详细使用方式见后文中得webmagic-extension注解模块。

 

webmagic-core

webmagic-core是爬虫的核心框架,只包括一个爬虫各功能模块的核心功能。webmagic-core的目标是成为网页爬虫的一个教科书般的实现。

此节部分内容摘自作者的博文  webmagic的设计机制及原理-如何开发一个Java爬虫

webmagic-core的模块划分

webmagic-core参考了scrapy的模块划分,分为Spider(整个爬虫的调度框架)、Downloader(页面下载)、PageProcessor(链接提取和页面分析)、Scheduler(URL管理)、Pipeline(离线分析和持久化)几部分。只不过scrapy通过middleware实现扩展,而webmagic则通过定义这几个接口,并将其不同的实现注入主框架类Spider来实现扩展。

image

 

Spider类(核心调度)

Spider是爬虫的入口类,Spider的接口调用采用了链式的API设计,其他功能全部通过接口注入Spider实现,下面是启动一个比较复杂的Spider的例子。

Spider.create(sinaBlogProcessor)
.scheduler(new FileCacheQueueScheduler("/data/temp/webmagic/cache/"))
.pipeline(new FilePipeline())
.thread(10).run();  

Spider的核心处理流程非常简单,代码如下:

<!-- lang: java -->
private void processRequest(Request request) {
    Page page = downloader.download(request, this);
    if (page == null) {
        sleep(site.getSleepTime());
        return;
    }
    pageProcessor.process(page);
    addRequest(page);
    for (Pipeline pipeline : pipelines) {
        pipeline.process(page, this);
    }
    sleep(site.getSleepTime());
}

<<<<<<< HEAD =======

Spider还包括一个方法test(String url),该方法只抓取一个单独的页面,用于测试抽取效果。

>>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

PageProcessor(页面分析及链接抽取)

页面分析是垂直爬虫中需要定制的部分。在webmagic-core里,通过实现**PageProcessor**接口来实现定制爬虫。PageProcessor有两个核心方法:public void process(Page page)和public Site getSite() 。

  • public void process(Page page)

    通过对**Page**对象的操作,实现爬虫逻辑。Page对象包括两个最重要的方法:addTargetRequests()可以添加URL到待抓取队列,put()可以将结果保存供后续处理。 Page的数据可以通过Page.getHtml()和Page.getUrl()获取。

  • public Site getSite()

    Site对象定义了爬虫的域名、起始地址、抓取间隔、编码等信息。

Selector是webmagic为了简化页面抽取开发的独立模块,是webmagic-core的主要着力点。这里整合了CSS Selector、XPath和正则表达式,并可以进行链式的抽取。

<!-- lang: java -->
//content是用别的爬虫工具抽取到的正文
List<String> links = page.getHtml()
.$("div.title")  //css 选择,Java里虽然很少有$符号出现,不过貌似$作为方法名是合法的
.xpath("//@href")  //提取链接
.regex(".*blog.*") //正则匹配过滤
.all(); //转换为string列表

webmagic包括一个对于页面正文的自动抽取的类**SmartContentSelector**。相信用过Evernote Clearly都会对其自动抽取正文的技术印象深刻。这个技术又叫**Readability**。当然webmagic对Readability的实现还比较粗略,但是仍有一些学习价值。

基于Saxon,webmagic提供了XPath2.0语法的支持。XPath2.0语法支持内部函数、逻辑控制等,是一门完整的语言,如果你熟悉XPath2.0语法,倒是不妨一试(需要引入**webmagic-saxon**包)。

webmagic-samples包里有一些为某个站点定制的PageProcessor,供学习之用。

Downloader(页面下载)

Downloader是webmagic中下载页面的接口,主要方法:

  • public Page download(Request request, Task task)

    Request对象封装了待抓取的URL及其他信息,而Page则包含了页面下载后的Html及其他信息。Task是一个包装了任务对应的Site信息的抽象接口。

  • public void setThread(int thread)

    因为Downloader一般会涉及连接池等功能,而这些功能与多线程密切相关,所以定义了此方法。

目前有几个Downloader的实现:

  • HttpClientDownloader

    集成了**Apache HttpClient**的Downloader。Apache HttpClient(4.0后整合到HttpCompenent项目中)是强大的Java http下载器,它支持自定义HTTP头(对于爬虫比较有用的就是User-agent、cookie等)、自动redirect、连接复用、cookie保留、设置代理等诸多强大的功能。

  • SeleniumDownloader

    对于一些Javascript动态加载的网页,仅仅使用http模拟下载工具,并不能取到页面的内容。这方面的思路有两种:一种是抽丝剥茧,分析js的逻辑,再用爬虫去重现它;另一种就是:内置一个浏览器,直接获取最后加载完的页面。**webmagic-selenium**包中整合了Selenium到SeleniumDownloader,可以直接进行动态加载页面的抓取。使用selenium需要安装一些native的工具,具体步骤可以参考作者的博文 使用Selenium来抓取动态加载的页面

Scheduler(URL管理)

Scheduler是webmagic的管理模块,通过实现Scheduler可以定制自己的URL管理器。Scheduler包括两个主要方法:

  • public void push(Request request,Task task)

    将待抓取URL加入Scheduler。Request对象是对URL的一个封装,还包括优先级、以及一个供存储数据的Map。Task仍然用于区分不同任务,在多个任务公用一个Scheduler时可以此进行区分。

  • public Request poll(Task task)

    从Scheduler里取出一条请求,并进行后续执行。

webmagic目前有三个Scheduler的实现:

  • QueueScheduler

    一个简单的内存队列,速度较快,并且是线程安全的。

  • FileCacheQueueScheduler

    使用文件保存队列,它可以用于耗时较长的下载任务,在任务中途停止后(手动停止或者程序崩溃),下次执行仍然从中止的URL开始继续爬取。

  • RedisScheduler

    使用redis存储URL队列。通过使用同一台redis服务器存储URL,webmagic可以很容易的在多机部署,从而达到分布式爬虫的效果。

Pipeline(后续处理和持久化)

Pipeline是最终抽取结果进行输出和持久化的接口。它只包括一个方法:

  • public void process(ResultItems resultItems,Task task)

    ResultItems是集成了抽取结果的对象。通过ResultItems.get(key)可以获取抽取结果。Task同样是用于区分不同任务的对象。

webmagic包括以下几个Pipeline的实现:

  • ConsolePipeline

    直接输出结果到控制台,测试时使用。

  • FilePipeline

    输出结果到文件,每个URL单独保存到一个页面,以URL的MD5结果作为文件名。通过构造函数 public FilePipeline(String path)定义存储路径,**以下使用文件持久化的类,多数都使用此方法指定路径**。

  • JsonFilePipeline

    以JSON输出结果到文件(.json后缀),其他与FilePipeline相同。

webmagic目前不支持持久化到数据库,但是结合其他工具,持久化到数据库也是很容易的。这里不妨看一下 webmagic结合JFinal持久化到数据库的一段代码。因为JFinal目前还不支持maven,所以这段代码并没有放到webmagic-samples里来。

 

webmagic-extension

webmagic-extension是为了开发爬虫更方便而实现的一些功能模块。这些功能完全基于webmagic-core的框架,包括注解形式编写爬虫、分页、分布式等功能。

注解模块

webmagic-extension包括注解模块。为什么会有注解方式?

因为PageProcessor的方式灵活、强大,但是没有解决两个问题:

  • 对于一个站点,如果想抓取多种格式的URL,那么必须在PageProcesser中写判断逻辑,代码难以管理。
  • 抓取结果没有对应Model,并不符合Java程序开发习惯,与一些框架也无法很好整合。

注解的核心是Model类,本身是一个POJO,这个Model类用于传递、保存页面最终抓取结果数据。注解方式直接将抽取与数据绑定,以便于编写和维护。

注解方式其实也是通过一个PageProcessor的实现--ModelPageProcessor完成,因此对webmagic-core代码没有任何影响。仍然以抓取OschinaBlog的程序为例:

@TargetUrl("http://my.oschina.net/flashsword/blog/\\d+")
public class OschinaBlog {

    @ExtractBy("//title")
    private String title;

    @ExtractBy(value = "div.BlogContent",type = ExtractBy.Type.Css)
    private String content;

    @ExtractBy(value = "//div[@class='BlogTags']/a/text()", multi = true)
    private List<String> tags;

    public static void main(String[] args) {
        OOSpider.create(
            Site.me().addStartUrl("http://my.oschina.net/flashsword/blog"),
            new ConsolePageModelPipeline(), OschinaBlog.class).run();
    }
}

注解部分包括以下内容:

  • TargetUrl

    "TargetUrl"表示这个Model对应要抓取的URL,它包含两层意思:符合这个条件的URL会被加入抓取队列;符合这个条件的URL会被这个Model抓取。TargetUrl可以**sourceRegion**指定提取URL的区域(仅支持XPath)。

    TargetUrl使用了正则表达式,匹配 " http://my.oschina.net/flashsword/blog/150039" 格式的URL。webmagic对正则表达式进行了修改,"."仅表示字符"."而不代表任意字符,而"*"则代表了".*",例如"http://*.oschina.net/*"代表了oschina所有的二级域名下的URL。

    与TargetUrl相似的还有**HelpUrl**,HelpUrl表示:仅仅抓取该URL用作链接提取,并不对它进行内容抽取。例如博客正文页对应TargetUrl,而列表页则对应HelpUrl。

  • ExtractBy

    • 用于字段

      "ExtractBy"可用于类以及字段。用于字段时,定义了字段抽取的规则。抽取的规则默认使用 XPath,也可以选择使用CSS Selector、正则表达式(通过设置type)。

      ExtractBy还有几个扩展属性。**multi**表示是否抽取列表,当然,设置为multi时,你需要一个List字段去容纳它。**notnull**则表示,此字段不允许为null,若为null则放弃整个对象。

    • 用于类

      "ExtractBy"用于类时,则限定了字段抽取的区域。用于类时仍支持multi,multi则表示一个页面可以抽取到多个对象。

    • ExtractByRaw & ExtractByUrl

      在类使用"ExtractBy"修饰后,字段的"ExtractBy"使用的是其抽取的结果,如果仍然想要抽取原HTML,可以使用"ExtractByRaw"。与此类似的还有"ExtractByUrl",表示从URL中抽取信息。ExtractByUrl只支持正则表达式。

    • ExtractBy2 ExtractBy3

      "ExtractBy"、"ExtractByRaw"支持链式抽取,通过增加注解"ExtractBy2"、"ExtractBy3"实现。

  • AfterExtractor

    AfterExtractor接口是对注解方式抽取能力不足的补充。实现AfterExtractor接口后,会在**使用注解方式填充完字段后**调用**afterProcess()**方法,在这个方法中可以直接访问已抽取的字段、补充需要抽取的字段,甚至做一些简单的输出和持久化操作(并不是很建议这么做)。这部分可以参考 webmagic结合JFinal持久化到数据库的一段代码

  • OOSpider

    OOSpider是注解式爬虫的入口,这里调用**create()**方法将OschinaBlog这个类加入到爬虫的抽取中,这里是可以传入多个类的,例如:

    OOSpider.create(
        Site.me().addStartUrl("http://www.oschina.net"),
        new ConsolePageModelPipeline(),
        OschinaBlog.clas,OschinaAnswer.class).run();

    OOSpider会根据TargetUrl调用不同的Model进行解析。

  • PageModelPipeline

    <<<<<<< HEAD

    可以通过定义PageModelPipeline来选择结果输出方式。这里new ConsolePageModelPipeline()是PageModelPipeline的一个实现,会将结果输出到控制台。 PageModelPipeline还有一个实现**JsonFilePageModelPipeline**,可以将对象持久化以JSON格式输出,并持久化到文件。JsonFilePageModelPipeline默认使用对象的MD5值作为文件名,你可以在Model中实现HasKey接口,指定输出的文件名。

    =======

    可以通过定义PageModelPipeline来选择结果输出方式。这里new ConsolePageModelPipeline()是PageModelPipeline的一个实现,会将结果输出到控制台。

    >>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637
  • 分页

    处理单项数据分页(例如单条新闻多个页面)是爬虫一个比较头疼的问题。webmagic目前对于分页的解决方案是:在注解模式下,Model通过实现**PagedModel**接口,并引入PagedPipeline作为第一个Pipeline来实现。具体可以参考webmagic-samples中抓取网易新闻的代码:**us.codecraft.webmagic.model.samples.News163**。

    关于分页,这里有一篇对于webmagic分页实现的详细说明的文章 关于爬虫实现分页的一些思考。 <<<<<<< HEAD 目前分页功能还没有分布式实现,如果使用RedisScheduler进行分布式爬取,请不要使用分页功能。

    ======= 目前分页功能还没有分布式实现,如果实现RedisScheduler进行分布式爬取,请不要使用分页功能。

     

    >>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637

分布式

webmagic-extension中,通过redis来管理URL,达到分布式的效果。但是对于分布式爬虫,仅仅程序能够分布式运行,还满足不了大规模抓取的需要,webmagic可能后期会加入一些任务管理和监控的功能,也欢迎各位用户为webmagic提交代码,做出贡献。

<<<<<<< HEAD

更进一步

如果这篇文档仍然满足不了你的需要,你可以阅读webmagic的 Javadoc,或者直接阅读源码。

======= >>>>>>> 3f4368cf780251a8d9fdd9da8cfff73e1190a637



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Viewing all 11805 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>