玩儿转MVC之大屌牌儿MVC框架(一)

2023-10-07 16:40
文章标签 框架 mvc 牌儿 玩儿 之大

本文主要是介绍玩儿转MVC之大屌牌儿MVC框架(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

这篇文章标题的确够屌,那么接下来阿海就以这个标题为入口,那么,就请诸位看官们且听我如何吹牛!
在阿海开始吹之前呢,也就是三天前吧,在某群看到过一篇博客,那界面,真心爽!而且阿海最近也在抽时间看关于前段的一些资料。虽然在前段上没什么造诣,但是写个HelloWorld什么的还是可以的,最起码我觉得我的审美观还是比较正常的。

你问我为什么扯到前端上了?哦,是这个样子,前面说到我的审美观比较不错,所以当我看到一个博客的清新大气简约时尚的页面时,又想想自己那个傻不溜秋的博客(blog.321aiyi.com),就萌生了一个完全属于自己的博客的想法!

于是就搭建了一个基于Spring的工程,当然数据库用的还是之前我曾经说过的基于Mybatis封装的神速开发框架。但是吧,尽管Spring很轻量级,但是我觉得写个博客程序什么的有点儿大材小用,直接用Servlet吧,又觉得有点儿麻烦。

于是乎,我决定封装一套用于MVC层的一个小框架,指不定以后什么时候突然兴趣来临写个小东西什么的就能派上用场了呢!

效果演示

哦了,上面的部分纯属乱吹,可以直接无视,在介绍这个MVC之前,我喜欢先让大家们看一下他的运行效果图:

先创建一个Controller:

@MVCController("controller")
public class MyController{//创建完毕~待会儿下面演示的Java代码都是写在这里的
}

测试对象响应体

代码:

    /*** 测试对象返回* @return*/@MVCURL("test")@MVCJsonpublic Object test(){Map<String, Object> result = new HashMap<>();result.put("testString", "Hello World!");result.put("testInteger", 110);result.put("testBoolean", true);String[] liStrings = {"哈哈哈", "嘿嘿嘿", "呵呵呵"};result.put("testList", liStrings);return result;}

启动并访问localhost:8080/Blog/controller/test.e
大屌牌儿MVC框架测试对象返回图片

测试JSP模板

JAVA代码

    /*** 测试JSP模板* @param request* @param response* @return*/@MVCURL("test2")public String toJsp(HttpServletRequest request, HttpServletResponse response){request.setAttribute("va", "如果你看到这句话,说明JSP模板成功了!");return "test";}

JSP代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${va }
</body>
</html>

运行效果:
大屌牌儿MVC框架测试JSP模板截图

测试自定义参数接收

JAVA代码:

    /*** 测试自定义参数接收* @param name* @param sex* @return*/@MVCURL("test4")@MVCJsonpublic Map<String, Object> test4(String name, String sex, int age){Map<String, Object> result = new HashMap<>();result.put("姓名", name);result.put("性别", sex);result.put("年龄", age);return result;}

运行效果
测试MVC的自定义参数列表结果截图

测试Model的封装

Java代码:

    /*** 测试Model的封装* @param name* @param model* @return*/@MVCURL("test5")public String test5(String name, Model model){model.set("name", name);return "testJsp";}

JSP代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title>
</head>
<body>你传入的姓名是:${name }
</body>
</html>

结果截图:
测试MVC框架的Model封装截图

到此,演示完毕,毕竟这个框架才写了三天,并且都是业余时间写的,所以也就只有这些比较常用的功能了,好在可以直接用HttpServletRequest和HttpServletResponse来处理所以一般情况下的小程序用这个做MVC感觉已经差不多了。
不是哥们儿我吹啊,想当年,哥们儿我小学的时候就就会黑别人QQ了(手动滑稽)~


简介

接下来我把这个封装的东西暂且叫他MVC框架,或者mvc框架。如果你觉得不够亲切,可以叫他《大屌牌儿MVC框架》
说说它的整体结构吧,他的整体结构其实就是一个单利的基于Hash表的ControllerBean全局管理控制容器和一个RequestMethod全局管理控制容器。(说白了就是俩静态Map)

下面是这个容器类的代码,如果被我之前说的一大堆没用的给整蒙了甚至找不到北的话,你看了下面这个代码或许会想“TMD这么简单的东西说那么复杂干毛!”:

package com.aiyi.mvc.reflection;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;import com.aiyi.mvc.annotations.RequesMethod;/*** 对象容器* @author 郭胜凯* @time 2016年11月8日 下午2:47:18* @addr blog.321aiyi.com* @email 719348277@qq.com*/
public class BeanContainer {/*** 对象容器集合*/public static Map<String, Object> ObjectContainer = new HashMap<>();/*** 方法容器集合*/public static Map<String, Method> MethodContainer = new HashMap<>();/*** 向容器中添加一个Bean* @param string* @param filterUrl* @param class1*/public static void setBean(String type, String filterUrl, Object bean) {// TODO Auto-generated method stubObjectContainer.put("<t>" + type + "</t><u>" + filterUrl + "</u>", bean);}/*** 向容器中添加一个Url映射* @param requesMethod* @param url* @param method*/public static void setMethod(RequesMethod requesMethod, String url, Method method) {// TODO Auto-generated method stubMethodContainer.put("<m>" + requesMethod.name().toUpperCase() + "</m><u>" + url + "</u>", method);}public static Method getMethod(String method, String uri) {// TODO Auto-generated method stubreturn MethodContainer.get("<m>" + method.toUpperCase() + "</m><u>" + uri + "</u>");}public static Object getBean(String type, String uri) {// TODO Auto-generated method stubreturn ObjectContainer.get("<t>" + type + "</t><u>" + type + "</u>");}}

哈哈,就是这么简单,就是俩Map而已!然后就是定义了一些通过客户端请求URI获取Controller和Method的一些方法。仅此而已!别问我我为什么要卖这么个关子,现在很多程序员都把很简单的东西说的那么复杂,还满嘴的名词儿一个一个的往外蹦,其实说的都是HelloWorld级别的东西,还自我认为很良好的样子。不只是程序员,连一些其他的职位也是这个样子,感觉会几个词儿技术就会变厉害似的。

下面看一个有趣的对话:

甲:你会MQ嘛?
我:消息队列就消息队列呗,还M什么Q呀 ,别问我我不会!
甲:…教教我

其实我当时真不会消息队列,但是我认为这类的问题,我可以花一个小时的时间阅读一下资料就能解决的问题。要知道一个开源的Java,他的东西那么那么多,你没有学完的时候,所以对于Java工程师而言,我觉得做的最多的还是现学现用!活学活用!随用而学!
要知道没有什么问题是看一遍文档解决不了的,如果有,那就看两遍!


好了一不小心又给跑题了,接下来回到正题,说一下这个MVC框架的核心:

另外,他的核心呢,就是一个Servlet!(这点儿和Spring MVC挺像)
当然,他也有属于自己的Filter,而且我已经定义好了Filter注解了,但是我还没有给他加上任何功能。
所以我想到这里大家应该知道怎么配这个框架了吧!

先不说怎么配,把这个核心的Servlet代码给大家亮一下:

package com.aiyi.mvc;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Set;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import com.aiyi.mvc.annotations.MVCController;
import com.aiyi.mvc.annotations.MVCFilter;
import com.aiyi.mvc.annotations.MVCJson;
import com.aiyi.mvc.annotations.MVCURL;
import com.aiyi.mvc.reflection.BeanContainer;
import com.aiyi.mvc.reflection.BeanUtil;
import com.aiyi.mvc.reflection.Model;
import com.aiyi.mvc.util.PropertiesUtil;
import com.aiyi.mvc.util.StringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;/*** MVC核心控制器* @author 郭胜凯* @time 2016年11月9日 上午11:23:38* @addr blog.321aiyi.com* @email 719348277@qq.com*/
public class CoreServlet extends HttpServlet{/*** */private static final long serialVersionUID = 5441344237169464974L;private boolean isChinaseUincode = false;private String contentCharset = "utf-8";private Set<Class<?>> classes;private String templateFrist = null;private String templateSuffix = ".jsp";private String contentPath = "";@Overridepublic void init() throws ServletException {// TODO Auto-generated method stubProperties properites = new PropertiesUtil().getProperites("mvc/config.properties");/*** 中文Unicode编码*/isChinaseUincode = properites.getProperty("ChinaseUnicode", "false").equalsIgnoreCase("true");/*** 字符集*/contentCharset = properites.getProperty("ContentCharset", "UTF-8");/*** JSP模板前缀*/templateFrist = properites.getProperty("TemplateFrist", "/");/*** JSP模板后缀*/templateSuffix = properites.getProperty("TemplateSuffix", ".jsp");classes = BeanUtil.getClasses(properites.getProperty("ClassPacketName"));for (Class<?> c : classes) {//实例化过滤器MVCFilter filterAnm = c.getAnnotation(MVCFilter.class);if (null != filterAnm) {try {String value = filterAnm.value();if (null == value || "".equals(value)) {value = "/*";}Object bean = c.newInstance();BeanContainer.setBean("filter", value, bean);} catch (InstantiationException | IllegalAccessException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}}super.init();}@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IllegalArgumentException {// TODO Auto-generated method stub//获取客户端请求路径和请求MethodcontentPath = request.getContextPath() + "/";String requestURI = request.getRequestURI().replace(contentPath, "");String requestMehod = request.getMethod();//根据请求路径,从Bean容器中获取对应处理方法Method method = BeanContainer.getMethod(requestMehod, requestURI);if (null == method) {//容器中没有对应方法的实例,扫描注解并加载实例到容器中for (Class<?> c : classes) {MVCController controllerAnm = c.getAnnotation(MVCController.class);if (null != controllerAnm) {String controllerUri = controllerAnm.value();if (requestURI.indexOf(controllerUri) != -1) {Method[] methods = c.getMethods();for (Method meth : methods) {MVCURL urlAnm = meth.getAnnotation(MVCURL.class);if (null != urlAnm) {String reqUri = urlAnm.value();if (controllerUri.charAt(0) == '/') {controllerUri = controllerUri.substring(1);}//整理完整请求URIif (controllerUri.charAt(controllerUri.length() - 1) == '/') {if (reqUri.charAt(0) == '/') {reqUri = controllerUri + reqUri.substring(1);}else{reqUri = controllerUri + reqUri;}}else{if (requestURI.charAt(0) == '/') {reqUri = controllerUri + reqUri;}else{reqUri = controllerUri + "/" + reqUri;}}if (requestURI.charAt(requestURI.length() - 1) != '/' && requestURI.indexOf(".") != -1) {requestURI = requestURI.substring(0, requestURI.lastIndexOf("."));}if (requestURI.equals(reqUri) && requestMehod.equalsIgnoreCase(urlAnm.method().name())) {//找到对应方法,实例化到Bean容器中BeanContainer.setMethod(urlAnm.method(), reqUri, meth);Log.info("实例化方法 -" + urlAnm.method() + ":" + reqUri + "[" + meth + "]");//如果该方法的类没有被实例化则实例化类到Bean容器中Object bean = BeanContainer.getBean("controller", controllerUri);if (null == bean) {try {bean = c.newInstance();BeanContainer.setBean("controller", controllerUri, bean);Log.info("实例化控制器 " + ":" + controllerUri + "[" + c.getName() + "]");} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}try {goToMethod(request, response, meth, bean);return;} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}}}}else{for (Class<?> c : classes) {MVCController controllerAnm = c.getAnnotation(MVCController.class);if (null != controllerAnm) {String controllerUri = controllerAnm.value();Object bean = BeanContainer.getBean("controller", controllerUri);if (null == bean) {try {bean = c.newInstance();BeanContainer.setBean("controller", controllerUri, bean);Log.info("实例化控制器 " + ":" + controllerUri + "[" + c.getName() + "]");} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}try {goToMethod(request, response, method, bean);return;} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}response.sendError(404);
//      super.service(request, response);}/*** 反射执行控制器的URL处理方法* @param request* @param response* @param method* @param controller* @throws InvocationTargetException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws IOException * @throws ServletException */private void goToMethod(HttpServletRequest request, HttpServletResponse response, Method method, Object controller) throws IllegalArgumentException, InvocationTargetException, IOException, ServletException, IllegalAccessException{response.setCharacterEncoding(contentCharset);Class<?>[] parameterTypes = method.getParameterTypes();//当该方法的参数列表是空的时候,直接调用if (parameterTypes.length == 0) {Object result = method.invoke(controller);if (null != result) {MVCJson jsonAnm = method.getAnnotation(MVCJson.class);//标有Json注解并具有返回值的方法将返回值序列成Json并输出if (null != jsonAnm) {response.setContentType("Content-Type: application/json;charset" + contentCharset);String jsonResult = null;if (isChinaseUincode) {jsonResult = StringUtil.chinaToUnicode(new ObjectMapper().writeValueAsString(result));}else{jsonResult = new ObjectMapper().writeValueAsString(result);}response.getWriter().print(jsonResult);return;}else if (result instanceof String) {//否则返回String文本,该文本对应一个Jsp模板路径String templatePath = String.valueOf(result);if (templatePath.charAt(0) == '/') {if (!templateFrist.equals("/")) {if (templateFrist.charAt(templateFrist.length() - 1) == '/') {templatePath = templateFrist + templatePath.substring(1);}else{templatePath = templateFrist + templatePath;}}}else{if (templateFrist.charAt(templateFrist.length() - 1) == '/') {templatePath = templateFrist + templatePath;}else{templatePath = templateFrist + "/" + templatePath;}}templatePath += templateSuffix;if (templatePath.charAt(0) != '/') {templatePath = "/" + templatePath;}request.getRequestDispatcher(templatePath).forward(request, response);return;}else{//以上两种都不是,直接404response.sendError(404);return;}}}else{//Controller中带有参数Object[] params = new Object[parameterTypes.length];String[] methodParamNames = MethodUtil.getMethodParameterNamesByAsm4(controller.getClass(), method);Map<String, String[]> parameterMap = request.getParameterMap();for (int i = 0; i < parameterTypes.length; i++) {if (parameterTypes[i].equals(HttpServletRequest.class)) {params[i] = request;methodParamNames[i] = null;continue;}if (parameterTypes[i].equals(HttpServletResponse.class)) {params[i] = response;methodParamNames[i] = null;continue;}if (parameterTypes[i].equals(HttpSession.class)) {params[i] = request.getSession();methodParamNames[i] = null;continue;}if (parameterTypes[i].equals(Model.class)) {params[i] = new Model(request);methodParamNames[i] = null;continue;}//除了标准的一些Http处理参数外,还有一些额外的参数,有戏了,匹配用户请求参数名if (null != methodParamNames[i]) {String[] strings = parameterMap.get(methodParamNames[i]);if (null != strings) {if (strings.length == 1) {if (parameterTypes[i].equals(Integer.class) || parameterTypes[i].equals(Integer.TYPE)) {try {params[i] = Integer.valueOf(strings[0]);} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");}continue;}if (parameterTypes[i].equals(Long.class) || parameterTypes[i].equals(Long.TYPE)) {try {params[i] = Long.valueOf(strings[0]);} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");}continue;}if (parameterTypes[i].equals(Double.class) || parameterTypes[i].equals(Double.TYPE)) {try {params[i] = Double.valueOf(strings[0]);} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");}continue;}if (parameterTypes[i].equals(Float.class) || parameterTypes[i].equals(Float.TYPE)) {try {params[i] = Float.valueOf(strings[0]);} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");}continue;}if (parameterTypes[i].equals(Short.class) || parameterTypes[i].equals(Short.TYPE)) {try {params[i] = Short.valueOf(strings[0]);} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");}continue;}if (parameterTypes[i].equals(Boolean.class) || parameterTypes[i].equals(Boolean.TYPE)) {try {params[i] = Boolean.valueOf(strings[0]);} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("客户端提交数据类型与服务端接收类型不相符(参数:" + methodParamNames[i] + ")");}continue;}//上面的都没戏了,直接贴数据params[i] = strings[0];continue;}//有数组!例如复选框之类的表单!//!!!待完善else{}}}}try {Object result = method.invoke(controller, params);if (null != result) {MVCJson jsonAnm = method.getAnnotation(MVCJson.class);//标有Json注解并具有返回值的方法将返回值序列成Json并输出if (null != jsonAnm) {response.setContentType("Content-Type: application/json;charset" + contentCharset);String jsonResult = null;if (isChinaseUincode) {jsonResult = StringUtil.chinaToUnicode(new ObjectMapper().writeValueAsString(result));}else{jsonResult = new ObjectMapper().writeValueAsString(result);}response.getWriter().print(jsonResult);return;}else if (result instanceof String) {//否则返回String文本,该文本对应一个Jsp模板路径String templatePath = String.valueOf(result);if (templatePath.charAt(0) == '/') {if (!templateFrist.equals("/")) {if (templateFrist.charAt(templateFrist.length() - 1) == '/') {templatePath = templateFrist + templatePath.substring(1);}else{templatePath = templateFrist + templatePath;}}}else{if (templateFrist.charAt(templateFrist.length() - 1) == '/') {templatePath = templateFrist + templatePath;}else{templatePath = templateFrist + "/" + templatePath;}}templatePath += templateSuffix;if (templatePath.charAt(0) != '/') {templatePath = "/" + templatePath;}request.getRequestDispatcher(templatePath).forward(request, response);return;}else{//以上两种都不是,直接404response.sendError(404);return;}}} catch (Exception e) {// TODO: handle exceptionthrow new IllegalArgumentException("请求参数与服务器接收参数不匹配");}}}
}

写道这里,时间又不早了,那么就先到这儿,我就不吊大家胃口了,如果对这个MVC感兴趣,的话我分享到GItHub了,有感兴趣的朋友可以直接在这里down到源码:https://git.coding.net/shengkai/MVC.git

如果有兴趣的朋友可以期待我的下一篇博客《玩儿转MVC之大屌牌儿MVC框架(二)》

这篇关于玩儿转MVC之大屌牌儿MVC框架(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/159068

相关文章

BD错误集锦8——在集成Spring MVC + MyBtis编写mapper文件时需要注意格式 You have an error in your SQL syntax

报错的文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuan.dao.YuanUserDao"><!

BD错误集锦7——在集成Spring MVC + MyBtis时使用c3p0作为数据库时报错Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatem

异常信息如下: Type Exception ReportMessage Handler dispatch failed; nested exception is java.lang.AbstractMethodError: Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatement.isClosed()Z is abstractDescr

[分布式网络通讯框架]----Zookeeper客户端基本操作----ls、get、create、set、delete

Zookeeper数据结构 zk客户端常用命令 进入客户端 在bin目录下输入./zkCli.sh 查看根目录下数据ls / 注意:要查看哪一个节点,必须把路径写全 查看节点数据信息 get /第一行代码数据,没有的话表示没有数据 创建节点create /sl 20 /sl为节点的路径,20为节点的数据 注意,不能跨越创建,也就是说,创建sl2的时候,必须确保sl

【服务器08】之【游戏框架】之【加载主角】

首先简单了解一下帧率 FixedUpdate( )   >   Update( )   >   LateUpdate( ) 首先FixedUpdate的设置值 默认一秒运行50次 虽然默认是0.02秒,但FiexedUpdate并不是真的0.02秒调用一次,因为在脚本的生命周期内,FixedUpdate有一个小循环,这个循环也是通过物理时间累计看是不是大于0.02了,然后调用一次。有

Java中的集合框架使用技巧

Java中的集合框架使用技巧 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨Java中集合框架的使用技巧,这些技巧能够帮助我们更高效地处理数据和优化程序性能。 Java集合框架概述 Java集合框架提供了一组实现了各种集合接口的类和接口,用于存储和操作数据。它包括列表、集合、队列和映射等数据结构,能够满足不

[分布式网络通讯框架]----ZooKeeper下载以及Linux环境下安装与单机模式部署(附带每一步截图)

首先进入apache官网 点击中间的see all Projects->Project List菜单项进入页面 找到zookeeper,进入 在Zookeeper主页的顶部点击菜单Project->Releases,进入Zookeeper发布版本信息页面,如下图: 找到需要下载的版本 进行下载既可,这里我已经下载过3.4.10,所以以下使用3.4.10进行演示其他的步骤。

C# 日志框架Serilog使用

1、框架和说明        C#日志框架Serilog支持多种场景输出,简单验证了一下,比较方便        包的安装,推荐直接使用“推荐NuGet包管理器”安装Serilog.AspNetCore,常见的组件都已经集成在一个包中,使用比较方便 2、配置文件        Serilog可以由配置文件来定义行为,而且配置文件的修改即时生效。参考配置文件如下: {"Serilog":

Pytest和Unitest框架对比

在学到自动化的时候,很多同学都遇到了Pytest和Unitest框架,有的人是两个都学,但是学的不精只是知道分别怎么用.不了解两个区别是什么.有的是犹豫到底要学习那个框架.其实要做好自动化测试,是有必要了解不同框架之间的差异化的. Pytest 特点: Pytest采用了更简洁、更灵活的语法风格,使用“assert”语句来进行断言,Pytest可以自动发现并执行以“test_”开头的函数

mysql-mmm框架2

简介 MMM(Master-Master replication manager for MySQL)是一套支持双主故障切换和双主日常管理的脚本程序。MMM使用Perl语言开发,主要用来监控和管理MySQL Master-Master(双主)复制,虽然叫做双主复制,但是业务上同一时刻只允许对一个主进行写入,另一台备选主上提供部分读服务,以加速在主主切换时刻备选主的预热,可以说MMM这套脚本程

MySQL-MMM框架

一、环境简述 1、工作逻辑图 2、MySQL-MMM优缺点 优点:高可用性,扩展性好,出现故障自动切换,对于主主同步,在同一时间只提供一台数据库写操作,保证的数据的一致性。 缺点:Monitor节点是单点,可以结合Keepalived实现高可用。 3、MySQL-MMM工作原理 MMM(Master-Master replication managerfor Mys