JavaScript架构搭建前端监控如何采集异常数据

目录
  • 前言
  • 什么是异常数据?
  • 接口异常
    • 拦截器中捕获异常
  • 前端异常
    • 为啥不用 window.onerror ?
  • 异常处理函数
    • 处理接口异常
    • 处理前端异常
  • 获取环境数据
    • 在 Vue 中
    • 在 React 中
  • 总结

前言

前两篇,我们介绍了为什么前端应该有监控系统,以及搭建前端监控的总体步骤,前端监控的 Why 和 What 想必你已经明白了。接下来我们解决 How 如何实现的问题。

如果不了解前端监控,建议先看前两篇:

  • 为什么前端不能没有监控系统?
  • 前端监控的总体搭建步骤

本篇我们介绍,前端如何采集数据,先从收集异常数据开始。

什么是异常数据?

异常数据,是指前端在操作页面的过程中,触发的执行异常或加载异常,此时浏览器会抛出来报错信息。

比如说你的前端代码用了个未声明的变量,此时控制台会打印出红色错误,告诉你报错原因。或者是接口请求出错了,在网络面板内也能查到异常情况,是请求发送的异常,还是接口响应的异常。

在我们实际的开发场景中,前端捕获的异常主要是分两个大类,接口异常 和 前端异常,我们分别看下这两大类异常怎么捕获。

接口异常

接口异常一定是在请求的时候触发。前端目前大部分的请求是用 axios 发起的,所以只要获取 axios 可能发生的异常即可。

如果你用 Promise 的写法,则用 .catch 捕获:

axios
  .post('/test')
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    // err 就是捕获到的错误对象
    handleError(err);
  });

如果你用 async/await 的写法,则用 try..catch.. 捕获:

async () => {
  try {
    let res = await axios.post('/test');
    console.log(res);
  } catch (err) {
    // err 就是捕获到的错误对象
    handleError(err);
  }
};

当捕获到异常之后,统一交给 handleError 函数处理,这个函数会将接收到的异常进行处理,并调用 上报接口 将异常数据传到服务器,从而完成采集。

上面我们写的异常捕获,逻辑上是没问题的,实操起来就会发现第一道坎:页面这么多,难道每个请求都要包一层 catch 吗?

是啊,如果我们是新开发一个项目,在开始的时候就规定每个请求要包一层 catch 也无可厚非,但是如果是在一个已有的规模还不小的项目中接入前端监控,这时候在每个页面或每个请求 catch 显然是不现实的。

所以,为了最大程度的降低接入成本,减少侵入性,我们是用第二种方案:在 axios 拦截器中捕获异常

前端项目,为了统一处理请求,比如 401 的跳转,或者全局错误提示,都会在全局写一个 axios 实例,为这个实例添加拦截器,然后在其他页面中直接倒入这个实例使用,比如:

// 全局请求:src/request/axios.js
const instance = axios.create({
  baseURL: 'https://api.test.com'
  timeout: 15000,
  headers: {
    'Content-Type': 'application/json',
  },
})
export default instance

然后在具体的页面中这样发起请求:

// a 页面:src/page/a.jsx
import http from '@/src/request/axios.js';
async () => {
  let res = await http.post('/test');
  console.log(res);
};

这样的话,我们发现每个页面的请求都会走全局 axios 实例,所以我们只需要在全局请求的位置捕获异常即可,就不需要在每个页面捕获了,这样接入成本会大大降低。

按照这个方案,结下来我们在 src/request/axios.js 这个文件中动手实施。

拦截器中捕获异常

首先我们为 axios 添加响应拦截器:

// 响应拦截器
instance.interceptors.response.use(
  (response) => {
    return response.data;
  },
  (error) => {
    // 发生异常会走到这里
    if (error.response) {
      let response = error.response;
      if (response.status >= 400) {
        handleError(response);
      }
    } else {
      handleError(null);
    }
    return Promise.reject(error);
  },
);

响应拦截器的第二个参数是在发生错误时执行的函数,参数就是异常。我们首先要判断是否存在 error.response,存在就说明接口有响应,也就是接口通了,但是返回错误;不存在则说明接口没通,请求一直挂起,多数是接口崩溃了。

如果有响应,首先获取状态码,根据状态码来判断什么时候需要收集异常。上面的判断方式简单粗暴,只要状态码大于 400 就视为一个异常,拿到响应数据,并执行上报逻辑。

如果没有响应,可以看作是接口超时异常,调用异常处理函数时传一个 null 即可。

前端异常

上面我们介绍了在 axios 拦截器中如何捕获接口异常,这部分我们再介绍如何捕获前端异常。

前端代码捕获异常,最常用的方式就是用 try..catch.. 了,任意同步代码块都可以放到 try 块中,只要发生异常就会执行 catch:

try {
  // 任意同步代码
} catch (err) {
  console.log(err);
}

上面说“任意同步代码”而不是“任意代码”,主要是普通的 Promise 写法 try..catch.. 是捕获不到的,只能用 .catch() 捕获,如:

try {
  Promise.reject(new Error('出错了')).catch((err) => console.log('1:', err));
} catch (err) {
  console.log('2:', err);
}

把这段代码丢进浏览器,打印结果是:

1: Error: 出错了

很明显只是 .catch 捕获到了异常。不过与上面接口异常的逻辑一样,这种方式处理当前页面异常没什么问题,但从整个应用来看,这样捕获异常侵入性强,接入成本高,所以我们的思路依然是全局捕获。

全局捕获 js 的异常也比较简单,用 window.addEventLinstener('error') 即可:

// js 错误捕获
window.addEventListener('error', (error) => {
  // error 就是js的异常
});

为啥不用 window.onerror ?

这里很多小伙伴有疑问,为什么不用 window.onerror 全局监听呢?window.addEventLinstener('error') 和 window.onerror 有什么区别呢?

首先这两个函数功能基本一致,都可以全局捕获 js 异常。但是有一类异常叫做 资源加载异常,就是在代码中引用了不存在的图片,js,css 等静态资源导致的异常,比如:

const loadCss = ()=> {
  let link = document.createElement('link')
  link.type = 'text/css'
  link.rel = 'stylesheet'
  link.href = 'https://baidu.com/15.css'
  document.getElementsByTagName('head')[10].append(link)
}
render() {
  return <div>
    <img src='./bbb.png'/>
    <button onClick={loadCss}>加载样式<button/>
  </div>
}

上述代码中的 baidu.com/15.cssbbb.png 是不存在的,JS 执行到这里肯定会报一个资源找不到的错误。但是默认情况下,上面两种 window 对象上的全局监听函数都监听不到这类异常。

因为资源加载的异常只会在当前元素触发,异常不会冒泡到 window,因此监听 window 上的异常是捕捉不到的。那怎么办呢?

如果你熟悉 DOM 事件你就会明白,既然冒泡阶段监听不到,那么在捕获阶段一定能监听到。

方法就是给 window.addEventListene 函数指定第三个参数,很简单就是 true,表示该监听函数会在捕获阶段执行,这样就能监听到资源加载异常了。

// 捕获阶段全局监听
window.addEventListene(
  'error',
  (error) => {
    if (error.target != window) {
      console.log(error.target.tagName, error.target.src);
    }
    handleError(error);
  },
  true,
);

上述方式可以很轻松的监听到图片加载异常,这就是为什么更推荐 window.addEventListene 的原因。不过要记得,第三个参数设为 true,监听事件捕获,就可以全局捕获到 JS 异常和资源加载异常。

需要特别注意,window.addEventListene 同样不能捕获 Promise 异常。不管是 Promise.then() 写法还是 async/await 写法,发生异常时都不能捕获。

因此,我们还需要全局监听一个 unhandledrejection 函数来捕获未处理的 Promise 异常。

// promise 错误捕获
window.addEventListener('unhandledrejection', (error) => {
  // 打印异常原因
  console.log(error.reason);
  handleError(error);
  // 阻止控制台打印
  error.preventDefault();
});

unhandledrejection 事件会在 Promise 发生异常并且没有指定 catch 的时候触发,相当于一个全局的 Promise 异常兜底方案。这个函数会捕捉到运行时意外发生的 Promise 异常,这对我们排错非常有用。

默认情况下,Promise 发生异常且未被 catch 时,会在控制台打印异常。如果我们想阻止异常打印,可以用上面的 error.preventDefault() 方法。

异常处理函数

前面我们在捕获到异常时调用了一个异常处理函数 handleError,所有的异常和上报逻辑统一在这个函数内处理,接下来我们实现这个函数。

const handleError = (error: any, type: 1 | 2) {
  if(type == 1) {
    // 处理接口异常
  }
  if(type == 2) {
    // 处理前端异常
  }
}

为了区分异常类型,函数新加了第二个参数 type 表示当前异常属于前端还是接口。在不同的场景中使用如下:

  • 处理前端异常:handleError(error, 1)
  • 处理接口异常:handleError(error, 2)

处理接口异常

处理接口异常,我们需要将拿到的 error 参数解析,然后取到需要的数据。接口异常一般需要的数据字段如下:

  • code:http 状态码
  • url:接口请求地址
  • method:接口请求方法
  • params:接口请求参数
  • error:接口报错信息

这些字段都可以在 error 参数中获取,方法如下:

const handleError = (error: any, type: 1 | 2) {
  if(type == 1) {
    // 此时的 error 响应,它的 config 字段中包含请求信息
    let { url, method, params, data } = error.config
    let err_data = {
       url, method,
       params: { query: params, body: data },
       error: error.data?.message || JSON.stringify(error.data),
    })
  }
}

config 对象中的 params 表示 GET 请求的 query 参数,data 表示 POST 请求的 body 参数,所以我在处理参数的时候,将这两个参数合并为一个,用一个属性 params 来表示。

params: { query: params, body: data }

还有一个 error 属性表示错误信息,这个获取方式要根据你的接口返回格式来拿。要避免获取到接口可能返回的超长错误信息,多半是接口没处理,这样可能会导致写入数据失败,要提前与后台规定好。

处理前端异常

前端异常异常大多数就是 js 异常,异常对应到 js 的 Error 对象,在处理之前,我们先看 Error 有哪几种类型:

  • ReferenceError:引用错误
  • RangeError:超出有效范围
  • TypeError:类型错误
  • URIError:URI 解析错误

这几类异常的引用对象都是 Error,因此可以这样获取:

const handleError = (error: any, type: 1 | 2) {
  if(type == 2) {
    let err_data = null
    // 监测 error 是否是标准类型
    if(error instanceof Error) {
      let { name, message } = error
      err_data = {
        type: name,
        error: message
      }
    } else {
      err_data = {
        type: 'other',
        error: JSON.strigify(error)
      }
    }
  }
}

上述判断中,首先判断异常是否是 Error 的实例。事实上绝大部分的代码异常都是标准的 JS Error,但我们这里还是判断一下,如果是的话直接获取异常类型和异常信息,不是的话将异常类型设置为 other 即可。

我们随便写一个异常代码,看一下捕获的结果:

function test() {
  console.aaa('ccc');
}
test();

然后捕获到的异常是这样的:

const handleError = (error: any) => {
  if (error instanceof Error) {
    let { name, message } = error;
    console.log(name, message);
    // 打印结果:TypeError console.aaa is not a function
  }
};

获取环境数据

获取环境数据的意思是,不管是接口异常还是前端异常,除了异常本身的数据之外,我们还需要一些其他信息来帮助我们更快更准的定位到哪里出错了。

这类数据我们称之为 “环境数据”,就是触发异常时所在的环境。比如是谁在哪个页面的哪个地方触发的错误,有了这些,我们就能马上找到错误来源,再根据异常信息解决错误。

环境数据至少包括下面这些:

  • app:应用的名称/标识
  • env:应用环境,一般是开发,测试,生产
  • version:应用的版本号
  • user_id:触发异常的用户 ID
  • user_name:触发异常的用户名
  • page_route:异常的页面路由
  • page_title:异常的页面名称

appversion 都是应用配置,可以判断异常出现在哪个应用的哪个版本。这两个字段我建议直接获取 package.json 下的 nameversion 属性,在应用升级的时候,及时修改 version 版本号即可。

其余的字段,需要根据框架的配置获取,下面我分别介绍在 Vue 和 React 中如何获取。

在 Vue 中

在 Vue 中获取用户信息一般都是直接从 Vuex 里面拿,如果你的用户信息没有存到 Vuex 里,从 localStorage 里获取也是一样的。

如果在 Vuex 里,可以这样实现:

import store from '@/store'; // vuex 导出目录
let user_info = store.state;
let user_id = user_info.id;
let user_name = user_info.name;

用户信息存在状态管理中,页面路由信息一般是在 vue-router 中定义。前端的路由地址可以直接从 vue-router 中获取,页面名称可以配置在 meta 中,如:

{
  path: '/test',
  name: 'test',
  meta: {
    title: '测试页面'
  },
  component: () => import('@/views/test/Index.vue')
},

这样配置之后,获取当前页面路由和页面名称就简单了:

window.vm = new Vue({...})
let route = vm.$route
let page_route = route.path
let page_title = route.meta.title

最后一步,我们再获取当前环境。当前环境用一个环境变量 VUE_APP_ENV 表示,有三个值:

  • dev:开发环境
  • test:测试环境
  • pro:生产环境

然后在根目录下新建三个环境文件,写入环境变量:

  • .env.development:VUE_APP_ENV=dev
  • .env.staging:VUE_APP_ENV=test
  • .env.production:VUE_APP_ENV=pro

现在获取 env 环境时就可以这么获取:

{
  env: process.env.VUE_APP_ENV;
}

最后一步,执行打包时,传入模式以匹配对应的环境文件:

# 测试环境打包
$ num run build --mode staging
# 生产环境打包
$ num run build --mode production

获取到环境数据,再拼上异常数据,我们就准备好了数据等待上报了。

在 React 中

和 Vue 一样,用户信息可以直接从状态管理里拿。因为 React 中没有全局获取当前旅游的快捷方式,所以页面信息我也会放在状态管理里面。我用的状态管理是 Mobx,获取方式如下:

import { TestStore } from '@/stores'; // mobx 导出目录
let { user_info, cur_path, cur_page_title } = TestStore;
// 用户信息:user_info
// 页面信息:cur_path,cur_page_title

这样的话,就需要在每次切换页面时,更新 mobx 里的路由信息,怎么做呢?

其实在根路由页(一般是首页)的 useEffect 中监听即可:

import { useLocation } from 'react-router';
import { observer, useLocalObservable } from 'mobx-react';
import { TestStore } from '@/stores';
export default observer(() => {
  const { pathname, search } = useLocation();
  const test_inst = useLocalObservable(() => TestStore);
  useEffect(() => {
    test_inst.setCurPath(pathname, search);
  }, [pathname]);
});

获取到用户信息和页面信息,接下来就是当前环境了。和 Vue 一样通过 --mode 来指定模式,并加载相应的环境变量,只不过设置方法略有不同。大多数的 React 项目可能都是用 create-react-app 创建的,我们以此为例介绍怎么修改。

首先,打开 scripts/start.js 文件,这是执行 npm run start 时执行的文件,我们在开头部分第 6 行加代码:

process.env.REACT_APP_ENV = 'dev';

没错,我们指定的环境变量就是 REACT_APP_ENV,因为只有 REACT_ 开头的环境变量可被读取。

然后再修改 scripts/build.js 文件的第 48 行,修改后如下:

if (argv.length >= 2 && argv[0] == '--mode') {
  switch (argv[1]) {
    case 'staging':
      process.env.REACT_APP_ENV = 'test';
      break;
    case 'production':
      process.env.REACT_APP_ENV = 'pro';
      break;
    default:
  }
}

此时获取 env 环境时就可以这么获取:

{
  env: process.env.REACT_APP_ENV;
}

总结

经过前面一系列操作,我们已经比较全面的获取到了异常数据,以及发生异常时到环境数据,接下来就是调用上报接口,将这些数据传给后台存起来,我们以后查找和追踪就很方便了。

如果你也需要前端监控,不妨花上半个小时,按照文中介绍的方法收集一下异常数据,相信对你很有帮助。

以上就是JavaScript架构搭建前端监控如何采集异常数据的详细内容,更多关于JavaScript前端监控采集异常数据的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavaScript高级之自定义异常

    目录 1.概念 1.1什么是错误与异常 1.2异常的分类 2.异常处理 2.1try...catch语句 2.2finally语句 2.3throw语句 3.Error对象 3.1自定义异常类型 前言: 在我们的实际编程中,抛出异常(代码报错)是最正常不过的,但是怎么处理异常这就要因人而异的,有的人遇到异常,通常会通过某种办法解决这个异常或者将其隐藏:但是在JavaScript中提供了一套完整的处理异常的机制,使程序遇到异常还可以正确的执行.所以说异常处理的在实际应用的重要性是毋庸置疑的,一个完

  • JavaScript代码异常监控实现过程详解

    这篇文章主要介绍了JavaScript代码异常监控实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 JavaScript异常一般有两方面:语法错误和运行时错误.两种错误的捕获和处理方式不同,从而影响具体的方案选型.通常来说,处理JS异常的方案有两种:try...catch捕获 和 window.onerror捕获.以下就两种方案分别分析各自的优劣. 虽然语法错误本应该在开发构建阶段使用测试工具避免,但难免会有马失前蹄部署到线上的时候.

  • @validated注解异常返回JSON值方式

    目录 @validated注解异常返回JSON值 使用@Valid注解,对其参数错误异常的统一处理 @validated注解异常返回JSON值 @ControllerAdvice public class ValidParamExceptionHandler { @ExceptionHandler(value = Exception.class) @ResponseBody public Map<String, Object> allExceptionHandler(Exception e){

  • JavaScript架构前端监控搭建过程步骤

    目录 前言 采集阶段:要采集哪些数据? 前端异常 接口异常 行为数据 API 阶段:搭建上报数据的 API 接口 数据存储阶段:接口对接数据库 查询统计阶段:数据查询和统计分析 可视化阶段:最终的数据图表展现 报警阶段:发现异常马上报警通知 部署阶段:万事俱备只等上线 总结 前言 上一篇介绍了,前端为什么要有监控系统?前端监控系统的意义何在?有小伙伴看完后留言想听些详细的实现.那么本篇我们就开始介绍前端监控如何实现. 如果还不明白为什么,搞监控有什么用,建议先看上篇文章:为什么前端不能没有监控系

  • nestjs中异常过滤器Exceptionfilter的具体使用

    说起Nestjs的异常过滤器,不能不提.Net的全局过滤器Filter,功能那是相当的强悍,用理论话说叫AOP 面向切面编程,可谓方便了太多需要异常处理的场景.说回Nestjs的异常过滤器,实现类似的功能,采用相似的处理方式,只不过一个面向C#,一个面向Nodejs,很荣幸的我,在两个框架都找到了类似的东西. 面向切面编程AOP,是一种类似于编程规范的东东,同门师兄弟有叫面向接口编程.SOLID原则等等. Nestjs的异常处理 默认异常处理 Nestjs内置了默认的全局异常过滤器,处理能够转换

  • javascript异常处理实现原理详解

    这篇文章主要介绍了javascript异常处理实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.什么是例外处理 当 JavaScript程序在运行中发生了诸如数组索引越界.类型不匹配或者语法错误时,JavaScript解释器就会引发例外处理. ECMAScript定义了六种类型的错误,除此之外,我们可以使用Error对象和throw语句来创建并引发自定义的例外处理信息. 通过运用例外处理技术,我们可以实现用结构化的方式来响应错误事

  • JavaScript架构搭建前端监控如何采集异常数据

    目录 前言 什么是异常数据? 接口异常 拦截器中捕获异常 前端异常 为啥不用 window.onerror ? 异常处理函数 处理接口异常 处理前端异常 获取环境数据 在 Vue 中 在 React 中 总结 前言 前两篇,我们介绍了为什么前端应该有监控系统,以及搭建前端监控的总体步骤,前端监控的 Why 和 What 想必你已经明白了.接下来我们解决 How 如何实现的问题. 如果不了解前端监控,建议先看前两篇: 为什么前端不能没有监控系统? 前端监控的总体搭建步骤 本篇我们介绍,前端如何采集

  • JS前端监控采集用户行为的N种姿势

    目录 引言 通用数据 获取用户信息 获取页面信息 设置时间 特定数据 手动埋点上报 全局自动上报 组件上报 总结 引言 上一篇我们详细介绍了前端如何采集异常数据.采集异常数据是为了随时监测线上项目的运行情况,发现问题及时修复.在很多场景下,除了异常监控有用,收集用户的行为数据同样有意义. 怎么定义行为数据?顾名思义,就是用户在使用产品过程中产生的行为轨迹.比如去过哪几个页面,点过哪几个按钮,甚至在某个页面停留了多长时间,某个按钮点击了多少次,如果有需求都可以记录下来. 但是记录行为数据是一个和业

  • JavaScript架构前端不能没有监控系统原因

    目录 监控系统 前端监控具体能解决什么问题? 异常报错问题 性能检测问题 运营反馈工具 为什么要选择自研? 自研前端监控的技术栈 监控系统 提到监控系统,大部分同学首先想到的是后端监控.很明显,比如检测服务器性能,数据库性能,API 的访问流量,以及各种服务的运行情况等等,都与后端息息相关.而前端更多承担的是 UI 展现的角色,主要关注页面怎么排版设计,好像没什么需要监测的地方,因此一直以来都没有涉及到监控的概念. 于是呢大家就一致认为:只要后端稳定可控,应用就是稳定可控的,可实际情况真的是这样

  • 新手学习微服务SpringCloud项目架构搭建方法

    这篇文章主要介绍了新手学习微服务SpringCloud项目架构搭建方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring的微服务框架SpringCloud受到众多公司欢迎,给大家带来一篇框架搭建入门.本次采用的版本是Spring Cloud版本为Finchley.RELEASE. 一.SpringCloud项目简介 spring cloud: 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.

  • Java 用Prometheus搭建实时监控系统过程详解

    上帝之火 本系列讲述的是开源实时监控告警解决方案Prometheus,这个单词很牛逼.每次我都能联想到带来上帝之火的希腊之神,普罗米修斯.而这个开源的logo也是火,个人挺喜欢这个logo的设计. 本系列着重介绍Prometheus以及如何用它和其周边的生态来搭建一套属于自己的实时监控告警平台. 本系列受众对象为初次接触Prometheus的用户,大神勿喷,偏重于操作和实战,但是重要的概念也会精炼出提及下.系列主要分为以下几块 Prometheus各个概念介绍和搭建,如何抓取数据(本次分享内容)

  • kubernetes集群搭建Zabbix监控平台的详细过程

    目录 一.zabbix介绍 1.zabbix简介 2.zabbix特点 3.zabbix的主要功能 4.zabbix架构图 二.检查本地k8s环境 1.检查系统pod运行状态 2.检查node节点状态 三.配置nfs共享存储 1.安装nfs 2.创建共享目录 3.配置共享目录 4.启动相关服务 5.使配置生效 6.查看nfs 7.其他节点检查nfs共享 四.安装zabbix-mysql 1.编写zabbix-mysql的yaml文件 2.创建命名空间 3.创建zabbix数据库 4.检查pod状

  • 搭建zabbix监控以及邮件报警的超级详细教学

    目录 一.zabbix 1.1 简介 1.2 zabbix主要功能 1.3 zabbix的主要特点 1.4 zabbix组件 1.5 zabbix原理 二.基于LNMP环境搭建zabbix监控 2.1 安装nginx 2.2 安装mariadb 2.3 安装php 2.4 修改php-fpm配置文件 2.5 修改nginx配置文件支持PHP并修改php配置文件 2.6 测试网页 2.7 测试连接数据库 2.8 建立数据库及用户并且授权 2.9 测试连接zabbix数据库 2.10 zabbix

  • python监控网站运行异常并发送邮件的方法

    本文实例讲述了python监控网站运行异常并发送邮件的方法.分享给大家供大家参考.具体如下: 这是一个简单的python开发的监控程序,当指定网页状态不正常是通过smtp发送通知邮件 复制代码 代码如下: #!/usr/bin/env python # -*- coding: UTF-8 -*- #author  libertyspy import socket import smtplib import urllib mail_options = {     'server':'smtp.qq

  • mysql 5.7 docker 主从复制架构搭建教程

    分享mysql 5.7 docker 主从复制架构搭建教程,供大家参考,具体内容如下 环境版本: MySQL :  5.7.13 Docker : 1.11.2 CentOS : 7.1 1.先在两个物理机上分别安装两个MySQL.命令如下 复制代码 代码如下: docker pull mysql:5.7.13  docker run --name anuo-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=qaz.00JK -d mysql:5.7.13 2.

随机推荐