python--boto3 之 与dynamoDB 的基本交互,表的备份与恢复
最近因工作需要,研究了一下boto3中dynamoDB部分,略有心得,在此总结一下。
首先是boto3的安装,在装有python和pip的机器上,运行
sudo pip install boto3
官网文档里,boto3提供的与dynamoDB交互的接口有以下几种:
batch_get_item()
batch_write_item()
can_paginate()
create_table()
delete_item()
delete_table()
describe_limits()
describe_table()
describe_time_to_live()
generate_presigned_url()
get_item()
get_paginator()
get_waiter()
list_tables()
list_tags_of_resource()
put_item()
query()
scan()
tag_resource()
untag_resource()
update_item()
update_table()
update_time_to_live()
说白了,就是对表和记录的增、删、查、改。本文主要描述我最近使用的那几个接口。
要在python中使用boto3,就得先import boto3。当然,这是废话。为了使用方便,我先写了一个json格式的配置文件,如下:
{
"region_name":"xxx",
"aws_access_key_id":"xxx",
"aws_secret_access_key":"xxx"
}
然后封装了一个专门用于操作dynamoDB的类,目前什么都没有
class dynamodb_operation():
它需要一个读取json文件的方法:
def load_json(self,path):
try:
with open(path) as json_file:
data = json.load(json_file)
except Exception as e:
print 'ERROR: no such file like ' + path
exit(-1)
else:
return data
由于读进来的文件可能不是json格式,我这里就是想让他报个错,然后退出。如果不想让它退出,在except里改改就好了。
然后,我希望这个类有一个私有成员client,在我实例化对象的时候就建立好连接,于是,有了以下初始化方法:
def __init__(self,path):
conf = self.load_json(path)
self.client = boto3.client('dynamodb',region_name=conf['region_name'],aws_access_key_id=conf['aws_access_key_id'], aws_secret_access_key=conf['aws_secret_access_key'])
与之前的配置文件是对应的。
有了这个基础,就可以封装自己想要使用的方法了。各方法的在官网上的说明就不照搬过来了。
1、列出dynamoDB中的所有的表
def list_all_table(self):
page=1
LastEvaluationTableName = ""
while True:
if page == 1:
response = self.client.list_tables()
else:
response = self.client.list_tables(
ExclusiveStartTableName=LastEvaluationTableName
)
TableNames = response['TableNames']
for table in TableNames:
print table
if response.has_key('LastEvaluatedTableName'):
LastEvaluationTableName = response["LastEvaluatedTableName"]
else:
break
page += 1
list_table()方法一次最多只能获取100张表的表名,并且在每次返回的时候,key为"LastEvaluatedTableName"的值为最后一张表的表名,可以做为下次请求的时候的参数。这样循环调用,即可获取所有的表名。如果后面没有表了,response里将不会有LastEvaluatedTableName。此处我只是想把表名打印到终端,如果想保存起来,也是可以的。
2、获取某张表的信息 describe_table()
def get_table_desc_only(self,table):
try:
response = self.client.describe_table(TableName=table)
except Exception as e:
print 'ERROR: no such table like ' + table
exit(-1)
else:
return response["Table"]
此处只是将response["Table"]原原本本地返回,没有做其它处理。
如果我想知道一张表的大小,可以:
def get_table_size(self,table):
response = self.get_table_desc_only(table)
stastic = {}
stastic['TableSizeBytes'] = response['TableSizeBytes']
stastic['ItemCount'] = response['ItemCount']
return stastic
如果想知道其它信息,而且是只想知道那些信息的话,也可以写出对应的方法。
3、创建一张表
def create_table(self,tablename,keySchema,attributeDefinitions,provisionedThroughput):
table = self.client.create_table(
TableName=tablename,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput
) # Wait until the table exists.
self.client.get_waiter('table_exists').wait(TableName=tablename) response = self.client.describe_table(TableName=tablename)
print response
这是在创建一张没有索引的表。创表需要时间,所以使用了get_waiter()方法。
4、插入数据
def put_item(self,tableName,item):
try:
self.client.put_item(
TableName=tableName,
Item=item
)
except Exception as e:
print 'ERROR: put item fail. msg: ' + str(e)
exit(-1)
else:
return
封装的此方法需要传入的是一个格式正确的json,并且key要与表对应。比如:
{'uid':{'N':''},'aid':{'N':''},'sid':{'N':''},'ksid':{'N':''}}
5、删表
def delete_table(self,table):
try:
self.client.delete_table(
TableName=table
)
except Exception as e:
print 'ERROR: delete table ' + table + ' fail. msg: ' + str(e)
else:
print 'delete table ' + table + ' succ'
其它方法不多说了。接下来就是表的备份与恢复。要做到什么程度呢,备份的时候,保存好表的结构,大小,以及所有条目,包括索引,恢复的时候,要能建一张一模一样的表,并把数据灌进去。
首先是备份表的结构。为了方便恢复表,对describe_table()方法的response进行了处理,同时对init方法进行修改:
def __init__(self,path):
conf = self.load_json(path)
self.client = boto3.client('dynamodb',region_name=conf['region_name'],aws_access_key_id=conf['aws_access_key_id'], aws_secret_access_key=conf['aws_secret_access_key'])
self.conf_path = path
self.items = ['TableName','AttributeDefinitions','KeySchema','LocalSecondaryIndexes','GlobalSecondaryIndexes','ProvisionedThroughput','StreamSpecification']
items里为创表时create_table()方法中的所有参数,其中,TableName,AttributeDefinitions,KeySchema,ProvisionedThroughput四项是创表时必传参数,另外三项为选传。同样地,describe_table()方法的response中,这四项也是一定存在的。故:
def get_SecondaryIndexes_desc(self,content):
result = []
for sub_item in content:
sub_content = {}
sub_content['IndexName'] = sub_item['IndexName']
sub_content['KeySchema'] = sub_item['KeySchema']
sub_content['Projection'] = sub_item['Projection']
result.append(sub_content)
return result
LocalSecondaryIndexes与GlobalSecondaryIndexes都是列表,所以默认不存在的项直接赋值一个空列表。
def get_table_desc_for_create_table(self,table):
response = self.get_table_desc_only(table)
result = {}
for item in self.items:
try:
content = response[item]
except Exception as e:
continue
else:
if item == 'TableName':
if content != table:
print 'ERROR: dynamoDB get table desc error'
exit(-1)
result[item] = content elif item == 'LocalSecondaryIndexes' or item == 'GlobalSecondaryIndexes':
result[item] = self.get_SecondaryIndexes_desc(content)
continue elif item == 'ProvisionedThroughput':
continue else:
result[item] = content
continue return json.dumps(result)
由于表的阈值不是固定的,所以不做保存。在创表的时候直接设置成一个固定的值即可。
对应地,恢复表的方法们:
def get_item_desc(self,item,content):
try:
result = content[item]
except Exception as e:
result = []
return result def create_table_from_desc(self,path):
table_desc = self.load_json(path)
provisionedThroughput={
'ReadCapacityUnits': 5,
'WriteCapacityUnits': 5
}
tableName = self.get_item_desc('TableName',table_desc)
attributeDefinitions = self.get_item_desc('AttributeDefinitions',table_desc)
keySchema = self.get_item_desc('KeySchema',table_desc)
localSecondaryIndexes = self.get_item_desc('LocalSecondaryIndexes',table_desc)
globalSecondaryIndexes = self.get_item_desc('GlobalSecondaryIndexes',table_desc)
streamSpecification = self.get_item_desc('StreamSpecification',table_desc) if len(globalSecondaryIndexes):
for item in globalSecondaryIndexes:
item['ProvisionedThroughput'] = provisionedThroughput try:
if len(localSecondaryIndexes):
if len(globalSecondaryIndexes):
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput,
LocalSecondaryIndexes=localSecondaryIndexes,
GlobalSecondaryIndexes=globalSecondaryIndexes
)
else:
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput,
LocalSecondaryIndexes=localSecondaryIndexes
)
else:
if len(globalSecondaryIndexes):
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput,
GlobalSecondaryIndexes=globalSecondaryIndexes
)
else:
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput
) except Exception as e:
print 'ERROR: error desc like file: ' + path + '\tmsg: ' + str(e)
exit(-1) else:
# Wait until the table exists.
self.client.get_waiter('table_exists').wait(TableName=tableName) response = self.client.describe_table(TableName=tableName)
print response
传入的path为之前保存好的表的结构。由于两个索引非必传,所以写了四个创表的方法。之前尝试过,不存在索引的时候传空列表[],None,或者()之类,都会报错,只能用这么一样比较笨的方法。由于StreamSpecification我没有用到,所以只是写在这里。
由于时间关系,dump和灌表工具直接就使用git_hub中的 dynamo-archive了。不然自己实现一个也是极好的。
在考虑备份表的时候,最近是否使用到成为一个判断因素。此处要使用到cloudwatch。
def get_stastic(self,dimension):
conf = self.load_json(self.conf_path)
cw = boto3.client('cloudwatch',region_name=conf['region_name'],aws_access_key_id=conf['aws_access_key_id'], aws_secret_access_key=conf['aws_secret_access_key']) stastic={}
stastic['Write'] = 0
stastic['Read'] = 0 # write
table_stastic = cw.get_metric_statistics(Namespace='AWS/DynamoDB', MetricName='ConsumedWriteCapacityUnits',
Dimensions=dimension,
StartTime=datetime.utcnow()-timedelta(days=9), EndTime=datetime.utcnow(),
Period=900, Statistics=['Sum', 'Maximum'], Unit='Count')['Datapoints']
if len(table_stastic) > 1:
for item in table_stastic:
stastic['Write'] += int(item['Sum']) #read
table_stastic = cw.get_metric_statistics(Namespace='AWS/DynamoDB', MetricName='ConsumedReadCapacityUnits',
Dimensions=dimension,
StartTime=datetime.utcnow()-timedelta(days=9), EndTime=datetime.utcnow(),
Period=900, Statistics=['Sum', 'Maximum'], Unit='Count')['Datapoints'] if len(table_stastic) > 1:
for item in table_stastic:
stastic['Read'] += int(item['Sum']) return stastic def get_table_use(self,table_name):
dimension = [{'Name': 'TableName', 'Value': table_name}]
stastic = self.get_stastic(dimension)
table = self.get_table_desc_only(table_name)
for index in table.get('GlobalSecondaryIndexes', []):
if index['IndexStatus'] != 'ACTIVE':
return
dimension = [{'Name': 'TableName', 'Value': table_name}, {'Name': 'GlobalSecondaryIndexName', 'Value': index['IndexName']}]
tmp = self.get_stastic(dimension)
stastic['Write'] += tmp['Write']
stastic['Read'] += tmp['Read'] return stastic def check_table_is_use(self,stastic):
read = stastic['Write']
write = stastic['Read']
if read == 0 and write == 0:
return False
else:
return True
直接获取了最近9天的总使用量。
当然,如果想知道这张表是不是存在的,也是可以的
def check_table_is_exist(self,table):
try:
response = self.client.describe_table(TableName=table)
except Exception as e:
return 0
else:
return 1
总体写得挺龊的QAQ
参考资料:http://boto3.readthedocs.io/en/latest/reference/services/dynamodb.html
原始文件 dynamodb_operation.py
#!/usr/bin/python
#-*- encoding: utf-8 -*- import boto3
import json
import sys
from datetime import datetime, timedelta class dynamodb_operation():
def __init__(self,path):
conf = self.load_json(path)
self.client = boto3.client('dynamodb',region_name=conf['region_name'],aws_access_key_id=conf['aws_access_key_id'], aws_secret_access_key=conf['aws_secret_access_key'])
self.conf_path = path
self.items = ['TableName','AttributeDefinitions','KeySchema','LocalSecondaryIndexes','GlobalSecondaryIndexes','ProvisionedThroughput','StreamSpecification'] def load_json(self,path):
try:
with open(path) as json_file:
data = json.load(json_file)
except Exception as e:
print 'ERROR: no such file like ' + path
exit(-1)
else:
return data def create_table(self,tablename,keySchema,attributeDefinitions,provisionedThroughput):
table = self.client.create_table(
TableName=tablename,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput
) # Wait until the table exists.
self.client.get_waiter('table_exists').wait(TableName=tablename) response = self.client.describe_table(TableName=tablename)
print response def get_item_desc(self,item,content):
try:
result = content[item]
except Exception as e:
result = []
return result def create_table_from_desc(self,path):
table_desc = self.load_json(path)
provisionedThroughput={
'ReadCapacityUnits': 5,
'WriteCapacityUnits': 5
}
tableName = self.get_item_desc('TableName',table_desc)
attributeDefinitions = self.get_item_desc('AttributeDefinitions',table_desc)
keySchema = self.get_item_desc('KeySchema',table_desc)
localSecondaryIndexes = self.get_item_desc('LocalSecondaryIndexes',table_desc)
globalSecondaryIndexes = self.get_item_desc('GlobalSecondaryIndexes',table_desc)
streamSpecification = self.get_item_desc('StreamSpecification',table_desc) if len(globalSecondaryIndexes):
for item in globalSecondaryIndexes:
item['ProvisionedThroughput'] = provisionedThroughput try:
if len(localSecondaryIndexes):
if len(globalSecondaryIndexes):
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput,
LocalSecondaryIndexes=localSecondaryIndexes,
GlobalSecondaryIndexes=globalSecondaryIndexes
)
else:
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput,
LocalSecondaryIndexes=localSecondaryIndexes
)
else:
if len(globalSecondaryIndexes):
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput,
GlobalSecondaryIndexes=globalSecondaryIndexes
)
else:
table = self.client.create_table(
TableName=tableName,
KeySchema=keySchema,
AttributeDefinitions=attributeDefinitions,
ProvisionedThroughput=provisionedThroughput
) except Exception as e:
print 'ERROR: error desc like file: ' + path + '\tmsg: ' + str(e)
exit(-1) else:
# Wait until the table exists.
self.client.get_waiter('table_exists').wait(TableName=tableName) response = self.client.describe_table(TableName=tableName)
print response def get_table_desc_only(self,table):
try:
response = self.client.describe_table(TableName=table)
except Exception as e:
print 'ERROR: no such table like ' + table
exit(-1)
else:
return response["Table"] def check_table_is_exist(self,table):
try:
response = self.client.describe_table(TableName=table)
except Exception as e:
return 0
else:
return 1 def get_SecondaryIndexes_desc(self,content):
result = []
for sub_item in content:
sub_content = {}
sub_content['IndexName'] = sub_item['IndexName']
sub_content['KeySchema'] = sub_item['KeySchema']
sub_content['Projection'] = sub_item['Projection']
result.append(sub_content)
return result def get_table_desc_for_create_table(self,table):
response = self.get_table_desc_only(table)
result = {}
for item in self.items:
try:
content = response[item]
except Exception as e:
continue
else:
if item == 'TableName':
if content != table:
print 'ERROR: dynamoDB get table desc error'
exit(-1)
result[item] = content elif item == 'LocalSecondaryIndexes' or item == 'GlobalSecondaryIndexes':
result[item] = self.get_SecondaryIndexes_desc(content)
continue elif item == 'ProvisionedThroughput':
continue else:
result[item] = content
continue return json.dumps(result) def get_table_size(self,table):
response = self.get_table_desc_only(table)
stastic = {}
stastic['TableSizeBytes'] = response['TableSizeBytes']
stastic['ItemCount'] = response['ItemCount']
return stastic def list_all_table(self):
page=1
LastEvaluationTableName = ""
while True:
if page == 1:
response = self.client.list_tables()
else:
response = self.client.list_tables(
ExclusiveStartTableName=LastEvaluationTableName
)
TableNames = response['TableNames']
for table in TableNames:
print table
if response.has_key('LastEvaluatedTableName'):
LastEvaluationTableName = response["LastEvaluatedTableName"]
else:
break
page += 1 def get_stastic(self,dimension):
conf = self.load_json(self.conf_path)
cw = boto3.client('cloudwatch',region_name=conf['region_name'],aws_access_key_id=conf['aws_access_key_id'], aws_secret_access_key=conf['aws_secret_access_key']) stastic={}
stastic['Write'] = 0
stastic['Read'] = 0 # write
table_stastic = cw.get_metric_statistics(Namespace='AWS/DynamoDB', MetricName='ConsumedWriteCapacityUnits',
Dimensions=dimension,
StartTime=datetime.utcnow()-timedelta(days=9), EndTime=datetime.utcnow(),
Period=900, Statistics=['Sum', 'Maximum'], Unit='Count')['Datapoints'] if len(table_stastic) > 1:
for item in table_stastic:
stastic['Write'] += int(item['Sum']) #read
table_stastic = cw.get_metric_statistics(Namespace='AWS/DynamoDB', MetricName='ConsumedReadCapacityUnits',
Dimensions=dimension,
StartTime=datetime.utcnow()-timedelta(days=9), EndTime=datetime.utcnow(),
Period=900, Statistics=['Sum', 'Maximum'], Unit='Count')['Datapoints'] if len(table_stastic) > 1:
for item in table_stastic:
stastic['Read'] += int(item['Sum']) return stastic def get_table_use(self,table_name):
dimension = [{'Name': 'TableName', 'Value': table_name}]
stastic = self.get_stastic(dimension)
table = self.get_table_desc_only(table_name)
for index in table.get('GlobalSecondaryIndexes', []):
if index['IndexStatus'] != 'ACTIVE':
return
dimension = [{'Name': 'TableName', 'Value': table_name}, {'Name': 'GlobalSecondaryIndexName', 'Value': index['IndexName']}]
tmp = self.get_stastic(dimension)
stastic['Write'] += tmp['Write']
stastic['Read'] += tmp['Read'] return stastic def check_table_is_use(self,stastic):
read = stastic['Write']
write = stastic['Read']
if read == 0 and write == 0:
return False
else:
return True def delete_table(self,table):
try:
self.client.delete_table(
TableName=table
)
except Exception as e:
print 'ERROR: delete table ' + table + ' fail. msg: ' + str(e)
else:
print 'delete table ' + table + ' succ' def list_dynamodb_conf(self):
conf = self.load_json(self.conf_path)
print 'region_name=' + '"' + conf['region_name'] + '"'
print 'aws_access_key_id=' + '"' + conf['aws_access_key_id'] + '"'
print 'aws_secret_access_key=' + '"' + conf['aws_secret_access_key'] + '"' def put_item(self,tableName,item):
try:
self.client.put_item(
TableName=tableName,
Item=item
)
except Exception as e:
print 'ERROR: put item fail. msg: ' + str(e)
exit(-1)
else:
return def put_items(self,tableName,item_path):
for item in open(item_path):
self.put_item(tableName,eval(item)) if __name__ == "__main__":
if len(sys.argv) < 2:
print "cmd args"
print "list_all_table"
print "list_dynamodb_conf"
print "get_table_desc_for_create_table table"
print "get_table_desc_only table"
print "get_table_size table"
print "create_table_from_desc table_desc_file"
print "check_table_is_exist table"
print "get_table_use table"
print "delete_table table password"
print "put_item table item(json)"
print "put_items table item_file_path"
exit(-1) db = dynamodb_operation('../conf/dynamoDB.conf') cmd = str(sys.argv[1])
if len(sys.argv) == 2:
if cmd == 'list_all_table':
db.list_all_table()
if cmd == 'list_dynamodb_conf':
db.list_dynamodb_conf() if len(sys.argv) == 3:
if cmd == 'get_table_desc_for_create_table':
table = str(sys.argv[2])
print db.get_table_desc_for_create_table(table) if cmd == 'get_table_desc_only':
table = str(sys.argv[2])
print db.get_table_desc_only(table) if cmd == 'check_table_is_exist':
table = str(sys.argv[2])
print db.check_table_is_exist(table) if cmd == 'get_table_size':
table = str(sys.argv[2])
print db.get_table_size(table) if cmd == 'create_table_from_desc':
desc_file_path = str(sys.argv[2])
db.create_table_from_desc(desc_file_path) if cmd == 'get_table_use':
table = str(sys.argv[2])
stastic = db.get_table_use(table)
print stastic
print db.check_table_is_use(stastic) if len(sys.argv) == 4:
if cmd == 'delete_table':
table = str(sys.argv[2])
password = str(sys.argv[3])
if password == 'password':
db.delete_table(table)
else:
print 'ERROR: password error!'
exit(-1) if cmd == 'put_item':
table = str(sys.argv[2])
tmp = str(sys.argv[3])
item = eval(tmp)
db.put_item(table,item) if cmd == 'put_items':
table = str(sys.argv[2])
item_file_path = str(sys.argv[3])
db.put_items(table,item_file_path)
python--boto3 之 与dynamoDB 的基本交互,表的备份与恢复的更多相关文章
- Python搭建Web服务器,与Ajax交互,接收处理Get和Post请求的简易结构
用python搭建web服务器,与ajax交互,接收处理Get和Post请求:简单实用,没有用框架,适用于简单需求,更多功能可进行扩展. python有自带模块BaseHTTPServer.CGIHT ...
- 【python】用 sqlacodegen 将存在的数据库表 转化成model.py
Flask的sqlalchemy对数据库表的模型提供了很多易用的方法.为了使用这些内容,需要将数据库表按照Flask识别的格式创建成Model,但是一般我们都是在已经创建好的数据库环境中开发Pytho ...
- Python 3 利用 subprocess 实现管道( pipe )交互操作读/写通信
这里我们用Windows下的shell来举例: from subprocess import * #因为是举例,就全部导入了 为了方便你理解,我们用一个很简单的一段代码来说明: 可以看见我们利用Pop ...
- python中web应用与mysql数据库交互
7使用数据库 具体使用python的DB-API,这一章里介绍如何编写代码与MYSQL数据库技术交互,这里使用一个通用的数据库API,名为DB-API. 7.1基于数据库的web应用 之前我们把日志数 ...
- Python的subprocess子进程和管道进行交互
在很久以前,我写了一个系列,Python和C和C++的交互,如下 http://blog.csdn.net/marising/archive/2008/08/28/2845339.aspx 目的是解决 ...
- (数据科学学习手札104)Python+Dash快速web应用开发——回调交互篇(上)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- (数据科学学习手札105)Python+Dash快速web应用开发——回调交互篇(中)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- (数据科学学习手札106)Python+Dash快速web应用开发——回调交互篇(下)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- Python用pip安装IPython/Jupyter最佳交互环境
一.Python模块及安装包简介 如果说编程语言是武器,那么Python就是一把双管枪(Python2/Python3),而各种为Python编写的模块和包就是子弹.使用pip来填满我们的武器吧! I ...
随机推荐
- shell中tr的用法
转自http://blog.csdn.net/zhuying_linux/article/details/6825568 tr(translate缩写)主要用于删除文件中的控制字符,或进行字符转换.语 ...
- MkDocs 文档生成逻辑浅析
Markdown 和 MkDocs 简介 Markdown 的语法简洁明了.学习容易,而且功能比纯文本更强,因此有很多人用它写博客.世界上最流行的博客平台 WordPress 和大型 CMS 如 Jo ...
- springmvc初步配置
导包/添加依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spr ...
- Linux安装Elasticsearch-head插件
首先需要下载以下内容: 我试验的对应版本:ES:elasticsearch-6.6.1.tar.gz Node:node-v10.15.3-linux-x64.tar JDK:jdk-8u201- ...
- PostMan --API调试工具
https://blog.csdn.net/fxbin123/article/details/80428216
- 接口自动化之unittest初探
最近几天苦心钻研unittest,终于略有所得,所以想来跟大家分享一下.有关python和unittest的基础知识部分就不在一一细说,相信各位也不是小白了.如果需要我整理基础知识,欢迎留言,我会看情 ...
- MM 后台配置(转)
本文转自:https://www.cnblogs.com/yanglikun/p/4124797.html 一.全局配置 1.一般配置 SPRO->SAP NETWEAVER -> GEN ...
- url传递参数带 + ,解决办法
修改客户端,将客户端带“+”的参数中的“+”全部替换为“%2B”,这样参数传到服务器端时就能得到“+”了.
- WinForm GDI编程:Graphics画布类
命名空间: using System.Drawing;//提供对GDI+基本图形功能的访问 using System.Drawing.Drawing2D;//提供高级的二维和矢量图像功能 using ...
- WCF系列教程之初识WCF
本随笔参考自WCF编程系列(一)初识WCF,纯属读书笔记,加深记忆. 1.简介:Windows Communication Foundation(WCF)是微软为构建面向服务的应用程序所提供的统一编程 ...