当前位置: 首页 > news >正文

文件上传网站源码wordpress自适应手机端

文件上传网站源码,wordpress自适应手机端,网站设计结构图用什么做,个人网站的制作教程作者丨 caiyfc 来自神州数码钛合金战队 神州数码钛合金战队是一支致力于为企业提供分布式数据库 TiDB 整体解决方案的专业技术团队。团队成员拥有丰富的数据库从业背景#xff0c;全部拥有 TiDB 高级资格证书#xff0c;并活跃于 TiDB 开源社区#xff0c;是官方认证合作伙…作者丨 caiyfc 来自神州数码钛合金战队 神州数码钛合金战队是一支致力于为企业提供分布式数据库 TiDB 整体解决方案的专业技术团队。团队成员拥有丰富的数据库从业背景全部拥有 TiDB 高级资格证书并活跃于 TiDB 开源社区是官方认证合作伙伴。目前已为 10 客户提供了专业的 TiDB 交付服务涵盖金融、证券、物流、电力、政府、零售等重点行业。 背景 笔者最近在驻场发现这里的 tidb 集群是真的多有将近 150 套集群。而且集群少则 6 个节点起步多则有 200 多个节点。在这么庞大的集群体量下巡检就变得非常的繁琐了。 那么有没有什么办法能够代替手动巡检并且能够快速准确的获取到集群相关信息的方法呢答案是有但不完全有。其实可以利用 tidb 的 Prometheus 来获取集群相关的各项数据比如告警就是一个很好的例子。可惜了告警只是获取了当前数据进行告警判断而巡检需要使用一段时间的数据来作为判断的依据。而且告警是已经达到临界值了巡检却是要排查集群的隐患提前开始规划避免出现异常。 那直接用 Prometheus 获取一段时间的数据并且把告警值改低不就行了 认识 PromQL 要使用 Prometheus 那必须要先了解什么是 PromQL 。 PromQL 查询语言和日常使用的数据库 SQL 查询语言SELECT * FROM ...是不同的PromQL 是一 种 嵌套的函数式语言 就是我们要把需 要查找的数据描述成一组嵌套的表达式每个表达式都会评估为一个中间值每个中间值都会被用作它上层表达式中的参数而查询的最外层表达式表示你可以在表格、图形中看到的最终返回值。比如下面的查询语句 histogram_quantile( # 查询的根最终结果表示一个近似分位数。0.9, # histogram_quantile() 的第一个参数分位数的目标值# histogram_quantile() 的第二个参数聚合的直方图sum by(le, method, path) (# sum() 的参数直方图过去5分钟每秒增量。rate(# rate() 的参数过去5分钟的原始直方图序列demo_api_request_duration_seconds_bucket{jobdemo}[5m])) ) 然后还需要认识一下告警的 PromQL 中经常出现的一些函数 rate 用于计算变化率的最常见 函数是 rate()  rate() 函数用于计算在指定时间范围内计数器平均每秒的增加量。因为是计算一个时间范围内的平均值所以我们需要在序列选择器之后添加一个范围选择器。 irate 由于使用 rate 或者 increase 函数去计算样本的平均增长速率容易陷入长尾问题当中其无法反应在时间窗口内样本数据的突发变化。 例如对于主机而言在 2 分钟的时间窗口内可能在某一个由于访问量或者其它问题导致 CPU 占用 100%的情况但是通过计算在时间窗口内的平均增长率却无法反应出该问题。 为了解决该问题PromQL 提供了另外一个灵敏度更高 的函数 irate(v range-vector) 。 irate 同样用于计算区间向量的计算率但是其 反应出的是瞬时增长率。 histogram_quantile 获取数据的分位数。histogram_quantile(φ scalar, b instant-vector) 函数用于计算历史数据指标一段时间内的分位数。该函数将目标分位数 (0 ≤ φ ≤ 1) 和直方图指标作为输入就是大家平时讲的 pxxp50 就是中位数参数 b 一定是包含 le 这个标签的瞬时向量不包含就无从计算分位数了但是计算的分位数是一个预估值并不完全准确因为这个函数是假定每个区间内的样本分布是线性分布来计算结果值的预估的准确度取决于 bucket 区间划分的粒度粒度越大准确度越低。 该部分引用 Prometheus 基础相关--PromQL 基础(2) ( Prometheus基础相关--PromQL 基础(2) - 知乎 ) 想学习的同学可以去看看原文 修改 PromQL 要让巡检使用 PromQL 就必须要修改告警中的 PromQL。这里需要介绍一个函数max_over_time(range-vector)它是获取区间向量内每个指标的最大值。其实还有其他这类时间聚合函数比如 avg_over_time、min_over_time、sum_over_time 等等但是我们只需要获取到最大值来提醒 dba 就行了。 Prometheus 是支持子查询的它允许我们首先以指定的步长在一段时间内执行内部查询然后根据子查询的结果计算外部查询。子查询的表示方式类似于区间向量的持续时间但需要冒号后添加了一个额外的步长参数 [:]。 举个例子 # 原版 sum(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance) ​ # 修改 max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance)[24h:1m]) 这是获取 TiKV raftstore 线程池 CPU 使用率的告警项。原版是直接将 1 分钟内所有线程的变化率相加而笔者的修改版是将 1 分钟内所有线程的使用率取平均值并且从此刻向后倒 24 小时内每一分钟执行一次获取平均线程使用率的查询再取最大值。 也就是说从 24 小时前到现在每分钟执行一次步长为 1 分钟 avg(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance) 并获取其中最大的一次值。这样就满足了我们需要使用一段时间的数据来判断集群是否有风险的依据了。 然后我们可以选取合适的 PromQL 来加上时间聚合函数和查询时间及步长信息 # TiKV 1 TiDB.tikv.TiKV_server_is_down: {pql: probe_success{grouptikv,instance~.*} 0,pql_max: ,note: TiKV 服务不可用 }, TiDB.tikv.TiKV_node_restart: {pql: changes(process_start_time_seconds{jobtikv,instance~.*}[24h]) 0,pql_max: max(changes(process_start_time_seconds{jobtikv,instance~.*}[24h])),note: TiKV 服务5分钟内出现重启 }, TiDB.tikv.TiKV_GC_can_not_work: {pql_max: ,pql: sum(increase(tikv_gcworker_gc_tasks_vec{taskgc, instance~.*}[2d])) by (instance) 1 and (sum(increase(tikv_gc_compaction_filter_perform{instance~.*}[2d])) by (instance) 1 and sum(increase(tikv_engine_event_total{cfwrite,dbkv,typecompaction,instance~.*}[2d])) by (instance) 1),note: TiKV 服务GC无法工作 }, # TiKV 2 TiDB.tikv.TiKV_raftstore_thread_cpu_seconds_total: {pql_max: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance)[24h:1m]),pql: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance)[24h:1m]) 0.8,note: TiKV raftstore 线程池 CPU 使用率过高 }, TiDB.tikv.TiKV_approximate_region_size: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance~.*}[1m])) by (le,instance))[24h:1m]),pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance~.*}[1m])) by (le,instance))[24h:1m]) 1073741824,note: TiKV split checker 扫描到的最大的 Region approximate size 大于 1 GB }, TiDB.tikv.TiKV_async_request_write_duration_seconds: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{typewrite, instance~.*}[1m])) by (le, instance, type))[24h:1m]),pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{typewrite, instance~.*}[1m])) by (le, instance, type))[24h:1m]) 1,note: TiKV 中Raft写入响应时间过长 }, TiDB.tikv.TiKV_scheduler_command_duration_seconds: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[20m])) by (le, instance, type) / 1000)[24h:20m]) ,pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[20m])) by (le, instance, type) / 1000)[24h:20m]) 20 ,note: TiKV 调度器请求响应时间过长 }, TiDB.tikv.TiKV_scheduler_latch_wait_duration_seconds: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[20m])) by (le, instance, type))[24h:20m]) ,pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[20m])) by (le, instance, type))[24h:20m]) 20,note: TiKV 调度器锁等待响应时间过长 }, TiDB.tikv.TiKV_write_stall: {pql_max: max_over_time(delta(tikv_engine_write_stall{instance~.*}[10m])[24h:10m]),pql: max_over_time(delta(tikv_engine_write_stall{instance~.*}[10m])[24h:10m]) 10,note: TiKV 中存在写入积压 }, ​ # TiKV 3 TiDB.tikv.TiKV_server_report_failure_msg_total: {pql_max: max_over_time(sum(rate(tikv_server_report_failure_msg_total{typeunreachable}[10m])) BY (instance)[24h:10m]),pql: max_over_time(sum(rate(tikv_server_report_failure_msg_total{typeunreachable}[10m])) BY (instance)[24h:10m]) 10,note: TiKV 节点报告失败次数过多 }, TiDB.tikv.TiKV_channel_full_total: {pql_max: max_over_time(sum(rate(tikv_channel_full_total{instance~.*}[10m])) BY (type, instance)[24h:10m]),pql: max_over_time(sum(rate(tikv_channel_full_total{instance~.*}[10m])) BY (type, instance)[24h:10m]) 0,note: TIKV 通道已占满 tikv 过忙 }, TiDB.tikv.TiKV_raft_log_lag: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance~.*}[1m])) by (le,instance))[24h:10m]),pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance~.*}[1m])) by (le, instance))[24h:10m]) 5000,note: TiKV 中 raft 日志同步相差过大 }, TiDB.tikv.TiKV_thread_unified_readpool_cpu_seconds: {pql_max: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~unified_read_po*, instance~.*}[1m])) by (instance)[24h:1m]),pql: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~unified_read_po*, instance~.*}[1m])) by (instance)[24h:1m]) 0.7,note: unifiled read 线程池使用率大于70% }, TiDB.tikv.TiKV_low_space: {pql_max: sum(tikv_store_size_bytes{typeavailable}) by (instance) / sum(tikv_store_size_bytes{typecapacity}) by (instance),pql: sum(tikv_store_size_bytes{typeavailable}) by (instance) / sum(tikv_store_size_bytes{typecapacity}) by (instance) 0.3,note: TiKV 当前存储可用空间小于阈值 }, 由于有的告警项是获取了 5 分钟或者 10 分钟的数据在写步长的时候也要同步修改为 5 分钟或者 10 分钟保持一致可以保证检查能覆盖选定的全部时间段并且不会重复计算造成资源浪费。 顺带一提如果不加 max_over_time 可以获取到带有时间戳的全部数据而不是只获取到最大的一个数据。这个带时间戳的全部数据可以方便画图像 grafana 那样展示数据趋势。 巡检脚本 了解了以上所有知识我们就可以开始编写巡检脚本了。 这是笔者和同事共同编写的一部分巡检脚本最重要的是 tasks 中的 PromQL 在脚本执行之前要写好 PromQL其他部分可以随意更改。如果一次性巡检天数太多比如一次巡检一个月的时间Prometheus 可能会因检查数据太多而报错的所以使用的时候要注意报错信息避免漏掉一些巡检项。 # -*- coding: utf-8 -*- import subprocess import re import datetime import requests import sys import pandas as pd ​ days None ​ ​ def get_cluster_name():try:command tiup cluster listresult subprocess.Popen(command, shellTrue, stdoutsubprocess.PIPE, stderrsubprocess.PIPE)output, error result.communicate()cluster_name_match re.search(r([a-zA-Z0-9_-])\stidb\sv, output.decode(utf-8))if cluster_name_match:return cluster_name_match.group(1)else:return Noneexcept Exception as e:print(An error occurred:, e)return None ​ ​ def display_cluster_info(cluster_name):if not cluster_name:print(Cluster name not found.)return ​try:command tiup cluster display {0}.format(cluster_name)result subprocess.Popen(command, shellTrue, stdoutsubprocess.PIPE, stderrsubprocess.PIPE)output, error result.communicate()return output.decode(utf-8)except Exception as e:print(An error occurred:, e) ​ ​ def extract_id_role(output):id_role_dict {}lines output.strip().split(\n)for line in lines:print(line)parts line.split()if is_valid_ip_port(parts[0]):node_id, role parts[0], parts[1]id_role_dict[node_id] rolereturn id_role_dict ​ ​ def is_valid_ip_port(input_str):pattern re.compile(r^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$)return bool(pattern.match(input_str)) ​ ​ def get_prometheus_ip(data_dict):prometheus_ip Nonefor key, value in data_dict.items():if value prometheus:prometheus_ip keybreakreturn prometheus_ip ​ ​ def get_tasks():global daystasks {# TiKV 1TiDB.tikv.TiKV_server_is_down: {pql: probe_success{grouptikv,instance~.*} 0,pql_max: ,note: TiKV 服务不可用},TiDB.tikv.TiKV_node_restart: {pql: changes(process_start_time_seconds{jobtikv,instance~.*}[24h]) 0,pql_max: max(changes(process_start_time_seconds{jobtikv,instance~.*}[24h])),note: TiKV 服务5分钟内出现重启},TiDB.tikv.TiKV_GC_can_not_work: {pql_max: ,pql: sum(increase(tikv_gcworker_gc_tasks_vec{taskgc, instance~.*}[2d])) by (instance) 1 and (sum(increase(tikv_gc_compaction_filter_perform{instance~.*}[2d])) by (instance) 1 and sum(increase(tikv_engine_event_total{cfwrite,dbkv,typecompaction,instance~.*}[2d])) by (instance) 1),note: TiKV 服务GC无法工作},# TiKV 2TiDB.tikv.TiKV_raftstore_thread_cpu_seconds_total: {pql_max: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance)[24h:1m]),pql: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~(raftstore|rs)_.*}[1m])) by (instance)[24h:1m]) 0.8,note: TiKV raftstore 线程池 CPU 使用率过高},TiDB.tikv.TiKV_approximate_region_size: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance~.*}[1m])) by (le,instance))[24h:1m]),pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance~.*}[1m])) by (le,instance))[24h:1m]) 1073741824,note: TiKV split checker 扫描到的最大的 Region approximate size 大于 1 GB},TiDB.tikv.TiKV_async_request_write_duration_seconds: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{typewrite, instance~.*}[1m])) by (le, instance, type))[24h:1m]),pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket{typewrite, instance~.*}[1m])) by (le, instance, type))[24h:1m]) 1,note: TiKV 中Raft写入响应时间过长},TiDB.tikv.TiKV_write_stall: {pql_max: max_over_time(delta(tikv_engine_write_stall{instance~.*}[10m])[24h:10m]),pql: max_over_time(delta(tikv_engine_write_stall{instance~.*}[10m])[24h:10m]) 10,note: TiKV 中存在写入积压}, ​# TiKV 3TiDB.tikv.TiKV_server_report_failure_msg_total: {pql_max: max_over_time(sum(rate(tikv_server_report_failure_msg_total{typeunreachable}[10m])) BY (instance)[24h:10m]),pql: max_over_time(sum(rate(tikv_server_report_failure_msg_total{typeunreachable}[10m])) BY (instance)[24h:10m]) 10,note: TiKV 节点报告失败次数过多},TiDB.tikv.TiKV_channel_full_total: {pql_max: max_over_time(sum(rate(tikv_channel_full_total{instance~.*}[10m])) BY (type, instance)[24h:10m]),pql: max_over_time(sum(rate(tikv_channel_full_total{instance~.*}[10m])) BY (type, instance)[24h:10m]) 0,note: TIKV 通道已占满 tikv 过忙},TiDB.tikv.TiKV_raft_log_lag: {pql_max: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance~.*}[1m])) by (le,instance))[24h:10m]),pql: max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance~.*}[1m])) by (le, instance))[24h:10m]) 5000,note: TiKV 中 raft 日志同步相差过大},TiDB.tikv.TiKV_thread_unified_readpool_cpu_seconds: {pql_max: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~unified_read_po*, instance~.*}[1m])) by (instance)[24h:1m]),pql: max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name~unified_read_po*, instance~.*}[1m])) by (instance)[24h:1m]) 0.7,note: unifiled read 线程池使用率大于70%},TiDB.tikv.TiKV_low_space: {pql_max: sum(tikv_store_size_bytes{typeavailable}) by (instance) / sum(tikv_store_size_bytes{typecapacity}) by (instance),pql: sum(tikv_store_size_bytes{typeavailable}) by (instance) / sum(tikv_store_size_bytes{typecapacity}) by (instance) 0.3,note: TiKV 当前存储可用空间小于阈值},}for key, value in tasks.items():for inner_key, inner_value in value.items():if isinstance(inner_value, str) and pql in inner_key:value[inner_key] inner_value.replace(24h:, f{24 * days}h:).replace([24h], f[{24 * days}h])return tasks ​ ​ def request_prome(prometheus_address, query):try:response requests.get(http://%s/api/v1/query % prometheus_address, params{query: query})return responseexcept:return None ​ ​ def has_response(prometheus_address, query):response request_prome(prometheus_address, query)if not response:return Falsetry:if response.json()[data][result]:return Trueelse:return Falseexcept:return False ​ ​ def check_prome_alive(prometheus_address):# dummy query is used to judge if prometheus is alivedummy_query probe_success{}return has_response(prometheus_address, dummy_query) ​ ​ def find_alive_prome(prometheus_addresses):if check_prome_alive(prometheus_addresses):return prometheus_addressesreturn None ​ ​ # ip:port - ip_port def decode_instance(instance):return instance.replace(:, _) ​ ​ def check_metric(alert_name, prometheus_address, pql, is_value, pql_max):record []try:is_warning 异常response request_prome(prometheus_address, pql)alert_name alert_name.split(.)result response.json()[data][result] ​# 判断是否出现异常if len(result) 0:is_warning 正常if pql_max :result [{metric: {}, value: [0, 0]}]else:response request_prome(prometheus_address, pql_max)result response.json()[data][result] ​for i in result:# 判断是否按节点显示if instance in i[metric]:instance i[metric][instance]node decode_instance(instance)else:node 集群# 判断是否有typeif type in i[metric]:type i[metric][type]else:type 无类型value i[value][1] ​if value NaN:value 0else:value round(float(value), 3)message %s,%s,%s,%s,%s,%s,%s,%s % (datetime.datetime.now(), node, alert_name[1], alert_name[2], type, is_warning, is_value, value)print(message)record.append(message)except Exception as e:print(alert_name[2] ----An error occurred check_metric:, e)returnreturn record ​ ​ def csv_report(record):data pd.DataFrame([line.split(,) for line in record],columns[timestamp, ip_address, service, event_type, type, status, description,value])grouped data.groupby(service)writer pd.ExcelWriter(inspection_report.xlsx, enginexlsxwriter)for name, group in grouped:group.to_excel(writer, sheet_namename, indexFalse)worksheet writer.sheets[name]for i, col in enumerate(group.columns):column_len max(group[col].astype(str).str.len().max(), len(col)) 2worksheet.set_column(i, i, column_len)writer.save() ​ ​ def run_tasks(role_metrics, prometheus_address):record []for alert in role_metrics:pql role_metrics[alert][pql]is_value role_metrics[alert][note]pql_max role_metrics[alert][pql_max]message check_metric(alert, prometheus_address, pql, is_value, pql_max)for data in message:record.append(data)csv_report(record) ​ ​ def run_script(prometheus_addresses):active_prometheus_address find_alive_prome(prometheus_addresses) ​# check if all prometheus are downif not active_prometheus_address:sys.exit()tasks get_tasks()run_tasks(tasks, active_prometheus_address) ​ ​ def get_user_input():global daystry:user_input int(input(请输入需要巡检的天数: ))days user_inputexcept ValueError:print(输入无效请输入一个有效的数字。) ​ ​ if __name__ __main__:# 输入巡检天数get_user_input() ​prometheus_ip 10.3.65.136:9091# prometheus_ip Noneif prometheus_ip is None:cluster_name get_cluster_name()cluster_info display_cluster_info(cluster_name)id_role_dict extract_id_role(cluster_info)print(id_role_dict)prometheus_ip get_prometheus_ip(id_role_dict)print(prometheus_ip)run_script(prometheus_ip) 总结 一个完善的巡检脚本的编写是一个长期的工作。因为时间有限笔者只编写了基于 Prometheus 的一部分巡检项有兴趣的同学可以继续编写更多巡检项。 目前巡检脚本都是基于 Prometheus 的数据来作判断但是在真实的巡检当中dba 还会查看一些 Prometheus 没有的数据比如表的健康度、一段时间内的慢 SQL、热力图、日志信息等等这些信息在后面一些时间可能会慢慢入到巡检脚本中。 现在该脚本已在 Gitee 上开源欢迎大家使用 https://gitee.com/mystery-cyf/prometheus--for-inspection/tree/master
http://www.zqtcl.cn/news/98151/

相关文章:

  • 企业网站建设模板多少钱在哪里建网站免费
  • 盐城网站建设咨询优秀vi设计
  • 网站里面内外链接如何做北京pk10网站建设
  • 网站布局策划的流程百度知道入口
  • 个人网站设计毕业设计论文上海百度seo优化
  • 展台设计网站都有哪些拓者设计吧手机版
  • 河南省级建设主管部门网站免费推广平台哪个好
  • wordpress禁止自动升级seo实战密码怎么样
  • 福永网站建设公司如何利用个人nas做网站
  • 北京网站seo外包wordpress心情
  • 租用服务器一般是谁帮助维护网站安全网站如何看是哪家公司做的
  • 戴尔网站建设的特点开创者wordpress素材
  • 网站假设公司排名不用囤货
  • 有关网站建设合同织梦珠宝网站模板
  • 月牙河做网站公司电商网站开发成本
  • iis7建立网站注册公司地址虚拟地址怎么申请
  • 响应式网站开发的想要去国外网站买东西怎么做
  • 网站建设开发有什么好处百度网盘0基础网站开发教程
  • 桂林整站优化青岛网站制作哪里有
  • 织梦cms手机网站源码天天想你视频免费观看西瓜
  • 怎么做网站弄网盟邯郸超速云_网站建设
  • 桂阳做网站的软件定制开发外包wordpress电子商务插件
  • 10有免费建网站那些公司做网站比较厉害
  • 网站关键词优化推广旅游类网站开发开题报告范文
  • 官方网站营销拟在建项目信息网官网
  • 沈阳做微信和网站的公司湛江网站建设公司哪家好
  • 网站 开发逻辑电话销售电销系统
  • 有哪些做兼职的设计网站有哪些工作可以用asp做哪些网站
  • 装修网站推广方案东莞网站建设0086
  • 知名营销网站开发高端网站建设如何收费