订单导出的预发和线上的自动化对比工具

问题与背景

订单导出需要将交易数据通过报表的形式导出并提供下载給商家,供商家发货、对账等。由于交易的场景非常多,承接多个业务(微商城、零售单店、零售连锁版、餐饮),订单类型很多,新老报表的字段覆盖交易、支付、会员、优惠、发货、退款、特定业务等,合计多达120个。每次代码变更(尤其是比较大的改动),如果想要手工验证指定时间段内的绝大多数场景下绝大多数订单类型的所有字段都没有问题,在前端页面点击下载报表,然后手工对比,将是非常大的工作量。因此,迫切需要一个自动化的对比工具,对比变更分支与线上分支的导出报表,找出和分析差异,修复问题。

为什么选择要在预发而不在QA进行呢? 因为订单导出的准确性不仅包含导出和下载功能(20%),更重要的是数据的准确性(80%)。而QA的数据不一定准确,且涵盖面不广,不准确的数据会导致错误的对比结果,对变更的影响造成很大的干扰,延误时间。 因此,这里直接选择用线上的数据来做对比,有时也会意外发现线上数据的一点问题。

整体思路

先做出一个假定:如果master分支的线上逻辑是没有问题的,那么预发的branch分支导出的结果,应该跟线上保持一致; 如果线上的逻辑有问题,那么预发 branch 分支导出的结果,应该有部分跟线上不一致,且不一致的地方根据推断应该仅跟改动部分有关。 分两种情况:

  • 系统代码优化与重构:逻辑没有改动,那么预发和线上的导出结果应该完全一致。如果有不一致的情况发生,那么需要分析不一致的原因,决定是否可以接受和取舍。
  • 业务逻辑优化:比如在某个场景下,“订单类型”字段原来输出“分销买家订单”,现在需要输出“分销买家订单/拼团订单”,那么导出结果的不一致应该限于“订单类型”。当然,如果有其他报表字段的输出也依赖于“订单类型”字段,那么可能其他字段也会不一致,这时候需要进一步分析。

整体思路如下:

  • 使用 Python 来完成该任务,因为 Python 非常简洁实用 ,适合做质量要求不是非常高的接口测试工具;
  • 分别往预发和线上发送相同的请求,然后通过导出ID拿到预发请求的文件和线上请求的文件,然后读取并逐字段对比,打印出差异;
  • 将对比结果保存在 /tmp/cmp_export.txt , 发送邮件保存。
  • 不同店铺的不同业务配置的导出测试用例通过一个单独的配置文件来给出,测试用例配置与请求测试功能分离。

这里使用了闭包的技术来配置化地构造大量测试用例。参阅:Python使用闭包结合配置自动生成函数

源代码

test.py : 主测试程序。 只要运行 python test.py 即可。然后看看是否有 diff 。如果没有 diff ,那就说明预发和线上导出结果一致; 如果有 diff ,就需要仔细分析 diff ,找出原因并解决。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# Name:        test.py
# Purpose:     test if exports from pre is consistent with exports from production
# USAGE:       python test.py
# When:        before deploy to production
#                  STEP1: login in bc-pre-order-export0 and vim test.py, cases.py in your directory ,
#                             enter :set paste ,  copy this script and save ;
#                  STEP2: run python test.py
#
# Author:      qin.shuq
#
# Created:     12/22/2017
# Copyright:   (c) qin.shuq 2017
# Licence:     <your licence>
#-------------------------------------------------------------------------------
import requests
import os
import json
import time
import math
import urllib2
import traceback

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

from cases import *

import sys
import codecs
import locale
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)

preUrl = 'http://pre-host:7001/api/general/export'
prodUrl = 'http://prod-host:7001/api/general/export'
queryUrl = 'http://prod-host:7001/api/general/queryRecords'

filedir = './files/'
resultfile = '/tmp/export_cmp.txt'

def extractFields(fieldsStr):
    return map(lambda x: x.strip(), fieldsStr.split(','))

templateIdFieldsMap = {     "1": extractFields("OrderNo,ExpressCompany,ExpressNo,OrderState,BuyerName,BuyerSex,BuyerProvince,BuyerCity,IsFans,ShouldPay,Postage,TotalPay,RealPay,BuyWay,SKU,SKUCode,GoodsCode,FeedBackInfo,ReceiverName,ReceiverProvince,ReceiverCity,ReceiverCounty,ReceiverDetailAddress,PostCode,ExpressWay,SelfFetchAddress,SelfFetcher,SelfFetchPhone,SelfFetchTime,Phone,BookTime,PayTime,Verificator,VerificateTime,Title,GoodsPrice,OrderRemark,GoodsNum,ShopId,TeamName,GoodsMsg,OrderMsg,OuterTransactionNo,InnerTransactionNo,Star,Remark,FenxiaoOrderNo,FenxiaoPay,GroupNo,StoreId,StoreName,ItemRealPay,ItemRefundPay,ItemExpressTime, DeliveryTime,SuccessTime,PeriodInfo,OrderType") ,     "2": extractFields("TeamName,OrderNo,OrderType,OrderState,OrderMsg,OrderRemark,AllGoodsTotalNum,AllGoodsOriginTotalPrice, AllGoodsPromotionTotalPrice,FoodBoxFee,DeliveryFee,Coupon,ManLiMinus,TotalPay,RealPay,OrderRefundFee,BuyWay,BookTime,PayTime,SuccessTime"),     "3": extractFields("TeamName,OrderNo,OrderType,OrderState,OrderMsg,OrderRemark,ReceiverName,Phone,ReceiverDetailAddress,AllGoodsTotalNum,AllGoodsOriginTotalPrice, AllGoodsPromotionTotalPrice,FoodBoxFee,DeliveryFee,Coupon,ManLiMinus,TotalPay,RealPay,OrderRefundFee,BuyWay,BookTime,PayTime,SuccessTime"),     "4": extractFields("OrderNo, Saleway, OrderType, OrderState, OrderSource, BookTime, PayTime, SuccessTime, BuyWay, InnerTransactionNo, OrderTotalPrice, OrderPostage, TotalPromotion, ShouldPay, RealPay, CashReturn, PromotionDetail, AllGoodsTitle, AllGoodsKinds, DeliveryType, DeliveryTime, ReceiverProvince, ReceiverCity, ReceiverCounty, ReceiverDetail, Receiver, ReceiverTel, OrderMsg, OrderStar, FansName, IsMember, FansTel, StoreName, Cashier, OrderRefundState, OrderRefundFee, OrderRemark, PeriodInfo, IDCard"),     "5": extractFields("OrderId, SimpleOrderState, GoodsTitle, GoodsType, GoodsKind, GoodsSaleway, GoodsSku, GoodsSkuCode, GoodsBizCode, GoodsOriginPrice, GoodsPromotionDetail, GoodsActivityPrice, GoodsTotalNum, GoodsUnit, GoodsTotalPrice, GoodsSharedShopPromotion, GoodsActualDealPay, GoodsPointsPrice, GoodsRemark, GoodsExpressState, GoodsExpressWay, GoodsExpressCorp, GoodsExpressNo, GoodsExpressPerson, GoodsExpressTime, GoodsRefundState, GoodsRefundFee"),     "7": extractFields("OrderID, OrderType, OrderState, OrderSource, BookTime, PayTime, SuccessTime, BuyWay, OuterTransactionNo, InnerTransactionNo, OrderTotalPrice, OrderPostage, TotalPromotion, ShouldPay, RealPay, CashReturn, PromotionDetail, AllGoodsTitle, AllGoodsKinds, DeliveryType, AppointmentTime, Receiver, ReceiverTel, ReceiverProvince, ReceiverCity, ReceiverCounty, ReceiverDetail, OrderMsg, OrderStar, FansName, IsMember, FansTel, BookStoreName, OrderRefundState, OrderRefundFee, OrderRemark, PeriodInfo, IDCard"),     "8": extractFields("OrderId, OuterTransactionNo, SimpleOrderState, SuccessTime, GoodsTitle, GoodsType, GoodsCategory, GoodsSku, GoodsSkuCode, GoodsBizCode, GoodsOriginPrice, GoodsPromotionDetail, GoodsActivityPrice, GoodsTotalNum, GoodsTotalPrice, GoodsSharedShopPromotion, GoodsActualDealPay, GoodsPointsPrice, GoodsRemark, Receiver, ReceiverTel, ReceiverProvince, ReceiverCity, ReceiverCounty, ReceiverDetail, OrderMsg, GoodsExpressState, GoodsExpressWay, GoodsExpressCorp, GoodsExpressNo, WscGoodsExpressPerson, WscGoodsExpressTime, GoodsRefundState, GoodsRefundFee")     }

def mkdir(filedir):
    isExists=os.path.exists(filedir)
    if not isExists:
        os.makedirs(filedir)
        return True
    else:
        return False

def sendRequest(url, query):
    r = requests.post(url, data=query, headers={"Content-type":"application/json"})
    return r.json()

def getData(url, query):
    try:
        resp = sendRequest(url, query)
        if resp['result'] and resp['data']['success']:
            return resp['data']['data']
        return None
    except:
        return None

def download(url, tag, exportId):
    f = urllib2.urlopen(url)
    data = f.read()
    filename = filedir + tag + "_" + str(exportId) + ".csv"
    with open(filename, "w") as csvFile:
        csvFile.write(data)
    return filename

def getFileLines(filename):
    with open(filename, 'r') as f:
        lines = f.readlines()
    return (filename,lines)

def getExportId(url, query):
    exportId = getData(url,query)
    if not exportId:
        return 0
    return int(exportId)

def getExportRecord(exportId):
    rec = {}
    while len(rec) == 0:
        time.sleep(5)
        queryReq = '{"export_id": %d, "page": 1, "size": 1, "export_state": 10}' % (exportId)
        resp = getData(queryUrl, queryReq)
        if resp['total'] > 0:
            rec = resp['list'][0]
    return rec

def getExportedFile(url, query, tag):
    exportId = getExportId(url, query)
    return getByExportId(exportId, tag)

def getExportedFileWithExportId(url, query, tag, exportId):
    getExportId(url, query)
    return getByExportId(exportId, tag)

def getByExportId(exportId, tag):
    exportRecord = getExportRecord(exportId)
    print 'tag=%s, exportId=%s, url=%s' % (tag, exportId, exportRecord['url'])
    csvFile = download(exportRecord['url'], tag, exportId)
    return csvFile

def cmpByOldExportReq(oldExportReq):
    preOldExportUrl = 'http://pre-host:7001/api/order/export'
    prodOldExportUrl = 'http://prod-host:7001/api/order/export'
    query = oldExportReq
    exportId = int(json.loads(oldExportReq)['export_id'])
    updateRecord(exportId)
    preFile = getExportedFileWithExportId(preOldExportUrl, query, 'pre', exportId)
    updateRecord(exportId)
    prodFile = getExportedFileWithExportId(prodOldExportUrl, query, 'prod', exportId)
    cmpExportFile(preFile, prodFile, query, "1")

def updateRecord(exportId):
    updateParam = '{"source":"AVeryComplexSecretTestHacker", "export_id": %s, "url":"", "export_state": 1, "token": "0"}' % (exportId)
    updateUrl = 'http://pre-host:7001/api/general/update'
    updateResult = getData(updateUrl, updateParam)
    if updateResult:
        print 'update record to init success !'

def cmplines(prodLines, preLines, fields, keyIndex=0):
    print 'length: online=%d, pre=%d' % (len(prodLines), len(preLines))
    try:
        for i in range(len(prodLines)):
            online = prodLines[i].strip().split(',')
            preline = preLines[i].strip().split(',')
            for t in range(len(online)):
                try:
                    if online[t] != preline[t]:
                        print 'diff: field=%s, online=%s, pre=%s, orderNo=%s' % (fields[t], online[t].decode('gb18030'), preline[t].decode('gb18030'), online[keyIndex])
                except:
                    print 'compare failed. field=%s orderNo=%s %s' % (fields[t], online[keyIndex], traceback.format_exc())
        print 'passed.'
    except:
        print 'compare failed. %s' % traceback.format_exc()

def cmpExport(exportReq):
    preFile = getExportedFile(preUrl, exportReq, 'pre')
    prodFile = getExportedFile(prodUrl, exportReq, 'prod')
    templateId = json.loads(exportReq)['template_id']
    cmpExportFile(preFile, prodFile, exportReq, templateId)

def cmpExportFile(preFile, prodFile, exportReq, templateId="1"):
    fields = templateIdFieldsMap[templateId]
    keyIndex = 0
    if templateId == "2" or templateId == "3":  #餐饮的报表,订单号在第二列
        keyIndex = 1

    # 按照订单号排序,因为下单时间可能相同,对比时有不必要的不一致
    preSortedFile = getSortedFile(preFile, keyIndex)
    prodSortedFile = getSortedFile(prodFile, keyIndex)
    preSorted = getFileLines(preSortedFile)[1]
    prodSorted = getFileLines(prodSortedFile)[1]
    print 'exportReq=[ %s ], prodFile=%s, preFile=%s' % (exportReq, prodSortedFile, preSortedFile)
    cmplines(prodSorted, preSorted, fields, keyIndex)

def getSortedFile(originFile, index):

    filename = originFile.rsplit('.',1)[0]
    sortedfilename = filename + "_sorted.csv"
    cmd = 'sort -k %d %s > %s' % (index+1, originFile, sortedfilename)
    os.system(cmd)
    return sortedfilename

def sendmail(text):
    sender = '[email protected]'
    receivers = ['[email protected]']

    message = MIMEMultipart()
    message['From'] = Header("导出对比工具", 'utf-8')
    message['To'] =  Header("导出对比工具", 'utf-8')
    subject = '导出对比结果'
    message['Subject'] = Header(subject, 'utf-8')

    message.attach(MIMEText('导出对比结果如附件所示', 'plain', 'utf-8'))

    att1 = MIMEText(open(resultfile, 'rb').read(), 'base64', 'utf-8')
    att1["Content-Type"] = 'application/octet-stream'
    att1["Content-Disposition"] = 'attachment; filename="export_cmp_result.txt"'
    message.attach(att1)

    try:
        smtpObj = smtplib.SMTP('smtp.exmail.qq.com', 25)
        smtpObj.login('[email protected]', 'YxtkETvis7Ck3UYZ')
        smtpObj.sendmail(sender, receivers, message.as_string())
        print "Email Send success!"
    except smtplib.SMTPException:
        print "Email Send failed!"

if __name__ == '__main__':

    savedStdout = sys.stdout

    mkdir(filedir)
    f_result = open(resultfile, 'w')
    sys.stdout = f_result

    allreqs = []
    for func in caseGenerateFuncs:
        allreqs.extend(func(startTimeParam, endTimeParam))
    for req in allreqs:
        cmpExport(req)
        print '\n'

    allOldReqs = []
    for func in oldExportInstGenerateFuncs:
        allOldReqs.extend(func(startTimeParam, endTimeParam))
    for req in allOldReqs:
        #cmpByOldExportReq(req)
        print '\n'

    print 'success done !'

    sendmail('export cmp result')

    f_result.close()

    sys.stdout = savedStdout

cases.py : 导出对比的测试用例配置

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# Name:        cases.py
# Purpose:     Provides cases of exports
#
# Author:      qin.shuq
#
# Created:     12/22/2017
# Copyright:   (c) qin.shuq 2017
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import time
import math
import json

kdtId = 63077
parts = 2

endTime = math.floor(time.time()) - 300
startTime = endTime - 600

baseExportReqStr = '{"biz_type":"order", "export_type":"default", "request_id": "123456", "source":"testsource", "order_by":"book_time", "order":"desc", "order_search_param": {"kdt_id":%d, "start_time":%d, "end_time":%d, "must_not": {"is_visible":0}}, "template_id":1}' % (kdtId, startTime, endTime)

def divideNParts(total, N):
    '''
       divide [0, total) into N parts:
        return [(0, total/N), (total/N, 2*total/N), ((N-1)*total/N, total)]
    '''

    each = total / N
    parts = []
    for index in range(N):
        begin = index*each
        if index == N-1:
            end = total
        else:
            end = begin + each
        parts.append((begin, end))
    return parts

def commonGenerateReqByTime(startTime, endTime, kdtId=xxx, templateId=1):
    def generateReqByTimeInner(startTime, endTime):
        totalInterval = endTime-startTime
        timeparts = divideNParts(totalInterval, parts)
        timeparts = map(lambda t: (t[0]+startTime, t[1]+startTime), timeparts)
        reqs = []
        for time in timeparts:
            baseReq = buildReq(baseExportReqStr, time[0], time[1], kdtId, templateId)
            reqs.append(json.dumps(baseReq))
        return reqs
    return generateReqByTimeInner

def commonGenerator(startTime, endTime, kdtId=xxx, templateId=1, field='', values=[]):
    def generateReqInner(startTime, endTime):
        reqs = []
        for val in values:
            baseReq = buildReq(baseExportReqStr, startTime, endTime, kdtId, templateId, field, val)
            reqs.append(json.dumps(baseReq))
        return reqs
    return generateReqInner

def buildReq(baseExportReqTemplate, startTime, endTime, kdtId=xxx, templateId=1, field=None, value=None):
    requestId = str(startTime) + "_" + str(endTime) + "_" + str(kdtId) + "_" + str(templateId)
    baseReq = json.loads(baseExportReqTemplate)
    baseReq['order_search_param']['start_time'] = startTime
    baseReq['order_search_param']['end_time'] = endTime
    if field and value:
        baseReq['order_search_param'][field] = value
    baseReq['order_search_param']['kdt_id'] = kdtId
    #baseReq['order_search_param']['order_nos'] = ['E2017**********00001', 'E2017**********0887']
    baseReq['request_id'] = requestId
    baseReq['template_id'] = templateId
    return baseReq

def generateGenerators(startTime, endTime, configs):
    gvars = globals()
    for (templateId,kdtId) in kdtIdTemplateIdMap.iteritems():
        if len(configs) == 0:
            funcName = 'generateReqByTime_' + str(kdtId) + "_" + str(templateId)
            gvars[funcName] = commonGenerateReqByTime(startTime, endTime, kdtId, templateId)
        else:
            for (field, values) in configs.iteritems():
                funcName = 'generateReqBy_' + str(kdtId) + "_" + str(templateId) + "_" + field
                gvars[funcName] = commonGenerator(startTime, endTime, kdtId, templateId, field, values)

#templateId=1,7,8 wsc export ;  =2,3 canyin ; =4,5 retail
kdtIdTemplateIdMap = {"1": xxx, "2":yyy, "3":yyy, "4":zzz, "5":zzz, "7": xxx, "8": xxx}
#kdtIdTemplateIdMap = {"1": xxx}

#如果改动了搜索相关,则需要测试订单搜索,使用该配置
searchconfigs = {"order_state": [[],["TOPAY"], ["CONFIRM"], ["PAID"], ["SENT"], ["SUCCESS"], ["CLOSE"]],                  "order_type": [[],["NORMAL"], ["GROUP"], ["HOTEL"]],                  "express_type": [[],["EXPRESS"], ["SELF_FETCH"], ["LOCAL_DELIVERY"]],                  "feedback": [[],["SAFE_NEW"], ["SAFE_FINISHED"]],                  "buy_way":[[],["WXPAY", "WXPAY_DAIXIAO", "WXPAY_SHOULI", "WX_APPPAY", "WX_WAPPAY"], ["ALIWAP", "ALIPAY"], ["UMPAY", "UNIONPAY", "UNIONWAP", "BAIDUWAP", "YZPAY"]]
                 }

# 如果只改动了详情,不需要测试订单搜索,只需要按照时间段来导出预发线上数据进行比较即可。
detailconfigs = {}

def buildOldExportInst(kdtId,startTime,endTime):
    def buildOldExportInstInner(startTime, endTime):
        totalInterval = endTime-startTime
        timeparts = divideNParts(totalInterval, parts)
        timeparts = map(lambda t: (t[0]+startTime, t[1]+startTime), timeparts)
        reqs = []
        for time in timeparts:
            req = {}
            req['kdt_id']=kdtId
            req['start_time']=time[0]
            req['end_time']=time[1]
            req['export_type']='default';
            req['biz_type']='order';
            req['export_id']= 'xxxxxx';   # 借壳生蛋
            req['param_hash']= '7295**********************e3c1';
            reqs.append(json.dumps(req))
        return reqs
    return buildOldExportInstInner

vipKdtIdConfigs = [xxx]

def buildAllOldExportInstGenerator(startTime, endTime, vipKdtIdConfigs):
    gvars = globals()
    for config in vipKdtIdConfigs:
        funcName = 'buildOldExportInstInner_' + str(config)
        gvars[funcName] = buildOldExportInst(config, startTime, endTime)

def getGenerateFuncs():
    gvars = globals()
    caseGenerators = [ gvars[var] for var in gvars if var.startswith('generateReq')  ]
    print 'case generators: ', [ var for var in gvars if var.startswith('generateReq') ]
    return caseGenerators

def getOldExportGenerateFuncs():
    gvars = globals()
    oldExportCaseGenerators = [ gvars[var] for var in gvars if var.startswith('buildOldExportInstInner')  ]
    print 'old export case generators: ', [ var for var in gvars if var.startswith('buildOldExportInstInner') ]

    return oldExportCaseGenerators

generateGenerators(startTime, endTime, detailconfigs)
caseGenerateFuncs = getGenerateFuncs()

buildAllOldExportInstGenerator(startTime, endTime, vipKdtIdConfigs)
oldExportInstGenerateFuncs = getOldExportGenerateFuncs()

# 5.14 - 5.22 has dirty data, should be excluded
startTimeParam = 1506787200
endTimeParam = 1530288000

小结

无论大改还是小改,通过运行这个预发和线上对比工具,很大程度上增强了成功发布的信心。可见,预发和线上的自动化对比工具,确实是发布前的最后一道防线。

原文地址:https://www.cnblogs.com/lovesqcc/p/9277071.html

时间: 2024-09-30 06:19:53

订单导出的预发和线上的自动化对比工具的相关文章

线上多服务管理工具实例剖析

公司线上对nginx.tomcat和jar包的java应用的服务管理脚本之前都是单独分离开的,这样子就造成了运维人员在跳板机上进行服务管理的时非常的不方便.特别是把这些服务管理框架纳入到自动化管理平台时,没有一个统一的服务管理接口去使用. 因此,在空的时候,我就将多个服务的管理脚本融合在了一起,将他做成一个工具,这样子就减少了服务管理的杂乱. 废话不说了,下面就是脚本内容,文章结尾我会简单介绍该脚本的思路. #!/bin/bash # # 本脚本用来对系统上的多服务进行管理操作 # 目前仅支持n

【原创】MySQL Replay线上流量压测工具

一. 背景 去年做过一次mysql trace 重放的测试,由于performance schema本身采集样本的长度等限制,实际回放的成功率比较低. 最近找到一款开源的工具,基于TCPCopy实现了线上流量的仿真测试,这款开源工具是网易的王斌开发,后面很多公司的模拟在线测试都是基于TCPCopy实现. https://github.com/session-replay-tools/mysql-replay-module 1.实现原理 生产服务器上部署TCPCopy, 包捕获是在数据链路层增加一

轻松排查线上Node内存泄漏问题

I. 三种比较典型的内存泄漏 一. 闭包引用导致的泄漏 这段代码已经在很多讲解内存泄漏的地方引用了,非常经典,所以拿出来作为第一个例子,以下是泄漏代码: 'use strict'; const express = require('express'); const app = express(); //以下是产生泄漏的代码 let theThing = null; let replaceThing = function () { let leak = theThing; let unused =

关于线上故障的思考

周末早上,一个哥们突然@我,问是否有线上故障处理和定级的规范或者模板,虽然手头有既有文档,但内容显的太具象了,跟我们的业务有很强的关联性,并不是那么好直接复制到他的团队中.因此,个人对过去的线上故障处理进行了回顾和思考,并进行了简要的归纳,望帮助到需要的同学.文本将按事中处理.事后总结和事前预防的顺序进行介绍,不足之处望大家不吝赐教. 换个角度来说,其实故障处理的过程,和小朋友发高烧的处理过程类似.首先mama会带孩子上医院,如果温度高医生会要求打退烧针,类似发布回滚,之后再通常吃对症的药物慢慢

关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程

某线上应用在进行查询结果导出Excel时,大概率出现持续的FullGC.解决这个问题时,记录了一下整个的流程,也可以作为一般性的FullGC问题排查指导. 1. 生成dump文件 为了定位FullGC的原因,首先需要获取heap dump文件,看下发生FullGC时堆内存的分配情况,定位可能出现问题的地方. 1. 1 通过JVM参数自动生成 可以在JVM参数中设置-XX:+ HeapDumpBeforeFullGC参数. 建议动态增加这个参数,直接在线上镜像中增加一方面是要重新打包发布,另一方面

线上性能问题初步排查方法

文章出处http://ifeve.com/find-bug-online/ 有时候有很多问题只有在线上或者预发环境才能发现,而线上又不能Debug,所以线上问题定位就只能看日志,系统状态和Dump线程,本文只是简单的介绍一些常用的工具,帮助定位线上问题. 问题定位 1: 首先使用TOP命令查看每个进程的情况,显示如下: top - 22:27:25 up 463 days, 12:46, 1 user, load average: 11.80, 12.19, 11.79 Tasks: 113 t

点批量移动到线上[1] Toolbox解决方法

点批量移动到线上,如下:左图效果变为右图效果: Analysis Tools工具箱下的Near方法: 点与线做Near后,如下会在point属性表中增加两个字段,分别是NEAR_X和NEAR_Y: 执行后,打开point属性表: 导出dbf表: 添加XY: 结果如下:

线上数据导入测试环境的一些注意点

1 线上导入测试环境或开发环境时,需要把线上一些推送消息的表删除,避免测试时出现把消息推送到 真实用户 2 开发有一个需求,生产数据导入开发环境中时,保留开发环境的一张运营管理的表 先备份开发环境的运营管理表,导出为opm.sql文件 把生产环境数据导入到开发环境 把opm.sql导入到开发环境

ecshop 导出订单 导出excel订单

ecshop 导出订单 导出excel订单 很多时候,我们每月或者每年都需要做一个订单销售总结,这时要从ecshop订单管理里面拿订单详情,所以需要给ecshop订单管理加一个“导出订单”功能! 思路分析:ecshop后台的“订单管理”里面“打印订单”就是我们要的内容,只需要把内容用PHPExcel导出到一个excel表里面即可. 最终效果:所有信息版,为了能看全所有信息,我把列缩小了 1,admin\templates\order_list.htm 加入“导出订单”按钮 <input name