首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

Java学习(17)--正则表达式

正则表达式(正确的规则表达式) (1)就是符合一定规则的字符串 (2)常见规则 A:字符 x 字符 x。举例:'a'表示字符 a \\ 反斜线字符。 \n 新行(换行)符 ('\u000A') \r 回车符 ('\u000D') B:字符类 [abc] a、b或c(简单类) [^abc] 任何字符,除了 a、b或c(否定) [a-zA-Z] a 到 z或 A 到 Z,两头的字母包括在内(范围) [0-9] 0到 9 的字符都包括 C:预定义字符类 . 任何字符。我就是 .字符本身,怎么表示呢 ?\. \d 数字: [0-9] 在Java中的正则表达式里,用\\表示 \w 单词字符: [a-zA-Z_0-9]在正则表达式里面组成单词的东西必须由a-zA-Z 和 0-9组成 D:边界匹配器 ^行的开头(必须以**开头) $行的结尾(必须以**结尾) \b 单词边界 :不是单词字符的地方 E:Greedy数量词 X? X,一次或一次也没有 X* X,零次或多次 X+ X,一次或多次 X{n} X,恰好 n次 X{n,} X,至少 n次 X{n,m} X,至少 n 次,但是不超过 m次 (3)常见功能: A:判断功能 String 类的 public boolean matches(String regex) B:分割功能 String 类的 public String[] split(String regex) C:替换功能 String 类的 public String replaceAll(String regex,String replacement) D:获取功能 Pattern 和 Matcher Pattern p = Pattern.compile("a*b"); Matcher m = p.matcher("aaaaab"); find(): 查找存不存在group():获取刚才查找过的数据 (4)案例 A:判断QQ号码规范 普通方法 添加循环 正则表达式方法

优秀的个人博客,低调大师

大数据学习~Hadoop初识三Yarn模式

我们都知道在如今的Hadoop中主要有三个重要的执行管理器。一个HDFS,一个MapReduce,还有就是我们今天要看的 YARN。 2.0以前的Hadoop 在2.0以前的hadoop中是没有Yarn这个模式管理的。大部分都是独自作战。Hbase做自己的,Spark也是做自己的,等等。这样的话就会造成资源的浪费,不能充分的把资源给利用上。特别是在1.x的版本上容易出现单点故障,不容易扩展的情况。 在这里Client的请求都会通过1个JobTracker来分发任务,如果我们的这个JobTracker出现异常。整个集群就没法参与正常工作。 在JobTracker 过多的TaskScheduler 集中过来,容易造成内存,cpu不够用的情况。增加了任务执行失败的风险。 因为这些情况,随着发展,Hadoop需要更新的一代管理引擎来帮助我们管理集群-YARN引擎 在2.0的YARN 在新的业务驱动下,发展起来的YARN替代原先的模式。将原先浪费的资源进行合并,共同管理建立在一个模式管理下 新的YARN模式如下 从图中我们可以看到 原先的JobTracker 被拆分成 资源管理和任务调度监控。 我们来看下如今的架构 ResourceManager : 在集群中提供资源的统一管理和调度。并且接收来自客户端的请求。同时不停的接收来自 DataNode上的心跳信息。并且对集群进行管理。 NodeManager : 在整个集群中会有多个该节点。主要用来维护自己节点上资源的管理和使用。 定时向ResourceManager 汇报自己资源的使用情况。并且 接收来自ResourceManager 各种命令 启动我们在图中看到的ApplicationMaster. ApplicationMaster : 该ApplicationMaster 对应我们提交的程序,该程序可以来自Spark,Hbase , MapReduce.该master向管理器YARN申请资源。然后供应用程序使用。 分配任务给接下来的Container 。包含启动,停止任务。 Container 封装了CPU ,Memory 等资源的容器。 Client 通过client来提交任务,进行任务的开始与结束。并且查询任务的执行进度等情况。 了解了这几个功能名称的作用,我们来看下整个任务执行流程是怎么样的。 从Client端发送一个 请求到我们的ResourceManager 上。其中内容应该包含ApplicationMaster,ApplicationMaster的启动命令。本身应用程序的内容。 ResourceManager 分配任务到NodeManager上 NodeManager根据配置信息进行处理启动ApplicationMaster 。 注册到ResourceManager,并且申请到资源返回到我们的ApplicationMaster 上。 根据申请到的资源注册到NodeManger上。 NodeManager 启动对应的Container上。在这之间会通过心跳进行任务汇报。然后任务汇报后。进行任务管理。 总结 整个yarn的流程和新的结构大概就是如此。新模式解决了原先的单点问题。并且挺高了高可用性和扩展性。一套集群环境就能供多个应用程序使用。YARN模式帮助我们解决掉了资源管理的问题,程序员关注业务开发即可。 原文发布时间为:2018-06-22本文作者:mengrui LuckQI本文来自云栖社区合作伙伴“LuckQI”,了解相关信息可以关注“LuckQI”。

优秀的个人博客,低调大师

Java 学习(07)--面向对象常见问题

1.有5家衣服专卖店,每家最多购买3件。用户可以选择离开,可以买衣服。最后打印总共买了几件衣服。 代码: //有5家衣服专卖店,每家最多购买3件。用户可以选择离开,可以买衣服。最后打印总共买了几件衣服。 import java.util.Scanner; public class BuyClothes{ private static int number = 0;//存储总购买量 public void setNumber(int a){ number = number+a; } public int GetNumber(){ return number; } public static void main(String[] args){ int name;//输出商店序号 shop s=new shop(); for(name = 1 ; name<6 ; name++){ System.out.println("欢迎光临第" + name + "家专卖店"); s.ask(); } System.out.println("总共买了" + number + "件衣服");//输出总购买量 } }//建立专卖店类 class shop{ BuyClothes b = new BuyClothes(); private int n;//存储当前商店购买量 public void ask(){ n = 0; for(String a = "n" ; a.equals("n") ;){ System.out.print("要离开吗(y/n)? "); Scanner sc = new Scanner(System.in); a = sc.nextLine();//判断是否离开 if(a.equals("n") && n<3){ System.out.println("买了一件衣服"); n = n+1; b.setNumber(1); }else if(a.equals("y")){ System.out.println("离店结账"); System.out.println(); }else{ String t;//判断输入是否有误 for(t=a ; (n<3 && !a.equals("n") && !a.equals("y")) || (!a.equals("y") && n>=3) ;){ System.out.print("输入有误或已达到本店购买上限,请重新输入(y/n):"); a = sc.nextLine(); } } } } } 运行: 2.完成购买商品结算功能: 1、循环输入商品编号和购物数量,计算消费金额并累加到总金额 2、输入“n”开始结账(假设享8折) 3、输入实付金额,计算找零 //完成购买商品结算功能: import java.util.Scanner; public class Shop{ public static void main(String[] args){ Scanner sc=new Scanner(System.in); String[] a=new String[]{"T恤","网球鞋","网球拍"}; float[] b=new float[]{245,570,100}; System.out.println("购物管理系统 > 购物结算"); System.out.println(); System.out.println(); //输出45个星 for(int i=0;i<45;i++){ System.out.print("*"); } System.out.println(); System.out.println("请选择购买的商品编号:"); System.out.println(); //遍历数组列出商品 for(int i=0;i<3;i++){ System.out.print((i+1) + "." + a[i] + " "); } System.out.println(); //输出45个星 for(int i=0;i<45;i++){ System.out.print("*"); } System.out.println(); System.out.println(); //开始购物 float cost=(float)0; float realCost=(float)0; for (String d="y" ; d.equals("y") ; ){ Scanner sc2=new Scanner(System.in); System.out.print("请输入商品编号: "); int x=sc2.nextInt()-1; System.out.println(); System.out.print("请输入购买数量: "); int y=sc2.nextInt(); System.out.println(); System.out.println(a[x] + " " + "¥" + b[x] + " " + "¥" + (b[x]*(float)y)); System.out.println(); cost=cost + (b[x]*(float)y); System.out.print("是否继续(y/n):"); d=sc.nextLine(); System.out.println(); } System.out.println("折扣:0.8"); System.out.println("金额总计 ¥" + cost*(float)0.8); System.out.print("实际交费 ¥"); //判断交钱数目是否正确 for (realCost=(float)sc.nextInt() ; realCost < cost*(float)0.8;){ System.out.println("钱数不对"); realCost=(float)sc.nextInt(); } System.out.println(""); System.out.println("找钱 ¥" + (realCost-(cost*(float)0.8))); } } 运行: 3.万年历(实现从键盘输入1900年之后的任意的某年、某月,输出该月的日历) 代码: //万年历(实现从键盘输入1900年之后的任意的某年、某月,输出该月的日历) import java.util.Scanner; public class Week{ public static void main(String [] args){ int year;//保存输入的年 int month;//保存输入的月 int days=0;//保存月份的天数 int totalDays=0;//存放总的天数差 boolean isRn;//判断输入的年是否是闰年,是的话true,不是false Scanner input=new Scanner(System.in); System.out.println("***************万年历**************"); System.out.println("请输入年:"); year=input.nextInt(); System.out.println("请输入月:"); month=input.nextInt(); if(year%400==0||(year%4==0&&year%100!=0)){ isRn=true;//代表闰年 }else{ isRn=false;//代表平年 } if(isRn){ System.out.println(year+"是闰年"); }else{ System.out.println(year+"是平年"); } //计算年的总天数 for(int i=1900;i<year;i++){ if(i%400==0||(i%4==0&&i%100!=0)){ totalDays+=366; }else{ totalDays+=365; } } //计算你输入月份的天数 for(int i=1;i<=month;i++){ switch(i){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: days=31; break; case 2: if(isRn){ days=29; }else{ days=28; } break; case 4: case 6: case 9: case 11: days=30; break; default: System.out.println("输入的月份有误"); break; } //累加求和,输入月份的天数保存到days变量中,但不累加进来 if(i!=month){ totalDays+=days; } } //求这个月的第一天是星期几.也就是输出的\t的个数 int beforeDays=0; beforeDays=1+totalDays%7; if(beforeDays==7){ beforeDays=0; } //打印输出 System.out.println("日\t一\t二\t三\t四\t五\t六"); //先打印输出\t的个数 for(int i=0;i<beforeDays;i++){ System.out.print("\t"); } for(int i=1;i<=days;i++){ System.out.print(i+"\t"); if((beforeDays+i)%7==0){ System.out.println(); } } } } 运行: 4.编写一个类Student,代表学员,要求: 1) 具有属性:姓名、年龄、性别、专业 2) 具有方法:自我介绍,负责输出该学员的姓名、年龄、性别以及专业 代码: //编写一个类Student,代表学员,要求: public class School{ public static void main(String[] args){ Student wjh=new Student(); wjh.introduce(); } } class Student{ private String name="小明"; private int age=18; private String sex="男"; private String collegeMajor="电子信息工程"; public void introduce(){ System.out.println("我叫" + name + "\t"); System.out.println(sex + " " + "今年" + age + "岁\t"); System.out.println("我的专业是" + collegeMajor + "\t"); } } 运行:

优秀的个人博客,低调大师

day17_Listener与Filter学习笔记

一、Listener监听器(了解) Javaweb开发中的监听器,是用于监听web常见对象的。 例如web的常见对象有:ServletContext、HttpServletRequest、HttpSession 监听它们的创建与销毁、属性变化、session绑定javaBean。 1、监听机制 事件:就是一个事情。 事件源:产生这个事情的源头。 监听器:用于监听指定的事件的对象。(关联事件和事件源) 注册监听:要想让监听器可以监听到事件产生,必须对其进行注册。 2、Javaweb开发中常见监听器 2.1、监听域对象的创建与销毁 若要监听ServletContext对象的创建与销毁 则需要写一个监听器类实现 ServletContextListener 接口 若要监听HttpSession对象的创建与销毁 则需要写一个监听器类实现 HttpSessionListener 接口 若要监听HttpServletRequest对象的创建与销毁 则需要写一个监听器类实现 ServletRequestListener 接口 2.2、监听域对象的属性变化 若要监听ServletContext对象的属性变化 则需要写一个监听器类实现 ServletContextAttributeListener 接口 若要监听HttpSession对象的属性变化 则需要写一个监听器类实现 HttpSessionAttributeListener 接口 若要监听HttpServletRequest对象的属性变化 则需要写一个监听器类实现 ServletRequestAttributeListener 接口 2.3、监听session绑定javaBean 若要监听javaBean对象是否绑定到了session域 则需要写一个javaBean实现 HttpSessionBindingListener 接口 若要监听javaBean对象的活化与钝化 则需要写一个javaBean实现 HttpSessionActivationListener 接口 3、监听器的快速入门 关于创建一个监听器的步骤: 1. 创建一个监听器类,实现指定的监听器接口。 2. 重写接口中的方法。 3. 在web.xml文件中对监听器进行注册。 3.1、关于域对象的创建与销毁的演示 1. ServletContext对象的创建与销毁 ServletContext对象是在服务器启动时创建的,在服务器关闭时销毁的。 package com.itheima.listener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象被创建了"); // 服务器一启动,ServletContext对象就被创建了 }@Overridepublic void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); // 服务器一关闭,ServletContext对象就被销毁了 }} 2. HttpSession对象的创建与销毁 HttpSession session = request.getSession(); Session的销毁方式 1. 默认超时30分钟后销毁 2. 关闭服务器时销毁 3. 调用invalidate()方法 4. setMaxInactiveInterval(int interval) 可以设置超时时间 问题:直接访问一个jsp页面时,是否会创建Session? 答:会创建,因为我们默认情况下是在jsp页面中直接使用Session内置对象的。 package com.itheima.listener;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;public class MyHttpSessionListener implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent se) { System.out.println("HttpSession对象被创建了"); }@Overridepublic void sessionDestroyed(HttpSessionEvent se) { System.out.println("HttpSession对象被销毁了"); }} index.jsp <body><% session.invalidate(); // 手动使session销毁 %></body> 3. HttpServletRequest对象的创建与销毁 Request对象是发送请求时服务器就会去创建它,当响应产生时,request对象就会被销毁。 package com.itheima.listener;import javax.servlet.ServletRequestEvent;import javax.servlet.ServletRequestListener;public class MyServletRequestListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent sre) { System.out.println("ServletRequest被销毁了"); }@Overridepublic void requestInitialized(ServletRequestEvent sre) { System.out.println("ServletRequest被创建 了"); }} 3.2、演示Request域对象中的属性变化 在java的监听机制中,它的监听器中的方法都是有参数的,参数就是事件对象,而我们可以通过事件对象直接获取事件源。 package com.itheima.attributelistener;import javax.servlet.ServletRequestAttributeEvent;import javax.servlet.ServletRequestAttributeListener;public class MyServletRequestListener implements ServletRequestAttributeListener {@Overridepublic void attributeAdded(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest添加属性了"); }@Overridepublic void attributeRemoved(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest移除属性了"); }@Overridepublic void attributeReplaced(ServletRequestAttributeEvent srae) { // 参数代表事件对象 System.out.println("ServletRequest替换属性了");// srae.getServletRequest(); // 通过事件对象直接获取事件源// System.out.println(srae.getName() + "\t" + srae.getValue()); // org.apache.catalina.ASYNC_SUPPORTED true }} index.jsp <body> <%// session.invalidate(); // 手动使session销毁 request.setAttribute("name", "tom"); request.setAttribute("name", "luna"); request.removeAttribute("name"); %> </body> 3.3、演示session绑定javaBean 1.javaBean对象自动感知被绑定到session中 HttpSessionBindingListener 这个接口是由javaBean实现的,并且不需要在web.xml文件中进行注册。 package com.itheima.domain;import javax.servlet.http.HttpSessionBindingEvent;import javax.servlet.http.HttpSessionBindingListener;public class User implements HttpSessionBindingListener {private String name;private int age;public String getName() {return name; }public void setName(String name) {this.name = name; }public int getAge() {return age; }public void setAge(int age) {this.age = age; }@Overridepublic void valueBound(HttpSessionBindingEvent event) { System.out.println("向session中绑定了一个user对象"); }@Overridepublic void valueUnbound(HttpSessionBindingEvent event) { System.out.println("从session中将user对象移除"); }} index.jsp <body><% // session.invalidate(); // 手动使session销毁 // request.setAttribute("name", "tom"); // request.setAttribute("name", "luna"); // request.removeAttribute("name");User user = new User();user.setName("luna");user.setAge(25); // 绑定到sessionsession.setAttribute("u", user); // 解除绑定session.removeAttribute("u"); %></body> 2.javabean对象可以活化或钝化到session中 HttpSessionActivationListener,如果javaBean实现了这个接口,那么当我们正常关闭服务器时,session中的javaBean对象就会被钝化到我们指定的文件中。 当下一次再启动服务器,因为我们已经将对象写入到文件中,这时就会自动将javaBean对象活化到session中。 package com.itheima.domain;import java.io.Serializable;import javax.servlet.http.HttpSessionActivationListener;import javax.servlet.http.HttpSessionBindingEvent;import javax.servlet.http.HttpSessionBindingListener;import javax.servlet.http.HttpSessionEvent;public class User implements Serializable, HttpSessionBindingListener, HttpSessionActivationListener {private String name;private int age;public String getName() {return name; }public void setName(String name) {this.name = name; }public int getAge() {return age; }public void setAge(int age) {this.age = age; }@Overridepublic void valueBound(HttpSessionBindingEvent event) { System.out.println("向session中绑定了一个user对象"); }@Overridepublic void valueUnbound(HttpSessionBindingEvent event) { System.out.println("从session中将user对象移除"); }@Overridepublic void sessionDidActivate(HttpSessionEvent se) { System.out.println("钝化"); }@Overridepublic void sessionWillPassivate(HttpSessionEvent se) { System.out.println("活化"); }} 我们还需要个context.xml文件来配置钝化时存储的文件,在META-INF目录下创建一个context.xml文件,输入以下代码: <Context><Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"><Store className="org.apache.catalina.session.FileStore" directory="it315"/></Manager></Context> 4、监听器的案例 4.1、案例_定时销毁session 怎样可以将每一个创建的session全都保存起来呢?答:我们可以做一个HttpSessionListener,当session对象创建时,就将这个session对象装入到一个集合中,然后将集合List<HttpSession>保存到ServletContext域中。 怎样判断session是否过期了呢?答:在HttpSession中有一个方法public long getLastAccessedTime(),它可以得到session对象最后使用的时间。然后可以使用invalidate方法销毁。 我们上面的操作需要使用任务调度功能。 在java中有一个Timer定时器类,定时器测试代码: TestTimer.java package com.itheima.timer;import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class TestTimer {public static void main(String[] args) {// 创建一个计时器对象 Timer t = new Timer();// 调度任务功能 t.schedule(new TimerTask() { // 要执行的任务@Overridepublic void run() { System.out.println(new Date().toLocaleString()); } }, 5000, 1000); // 5秒后开始,每隔1秒执行一次 }} 监听ServletContext代码: package com.itheima.test;import java.util.ArrayList;import java.util.Collections;import java.util.Iterator;import java.util.List;import java.util.Timer;import java.util.TimerTask;import javax.servlet.ServletContext;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.http.HttpSession;public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {// 通过事件对象得到事件源(ServletContext) ServletContext application = sce.getServletContext();// 创建一个集合用于存储所有的session对象(需要考虑并发问题,因为我们在web中,它一定是一个多线程的)final List<HttpSession> list = Collections.synchronizedList(new ArrayList<HttpSession>());// 把集合放到application域中 application.setAttribute("sessions", list);// 创建一个计时器对象 Timer t = new Timer();// 调度任务 t.schedule(new TimerTask() {@Overridepublic void run() { System.out.println("开始扫描了...");for (Iterator iterator = list.iterator(); iterator.hasNext();) { HttpSession session = (HttpSession) iterator.next();long l = System.currentTimeMillis() - session.getLastAccessedTime();if (l > 5000) { // 如果时间大于5秒,就把session销毁 System.out.println("session移除了" + session.getId()); session.invalidate(); // 把session销毁 iterator.remove(); } }/* for (HttpSession session : list) { long l = System.currentTimeMillis() - session.getLastAccessedTime(); if (l > 5000) { // 如果时间大于5秒,就把session销毁 session.invalidate(); // 把session销毁 list.remove(session); // 从集合中移除 } } */ } }, 2000, 5000); // 延迟2秒后执行,每间隔5秒执行一次 }@Overridepublic void contextDestroyed(ServletContextEvent sce) { }} 监听Session代码: package com.itheima.test;import java.util.List;import javax.servlet.ServletContext;import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;public class MySessionListener implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent se) {// 通过事件源得到HttpSession对象 HttpSession session = se.getSession();// 得到application对象(ServletContext对象) ServletContext application = session.getServletContext();// 得到session对象,并放入到list集合中 List<HttpSession> list = (List<HttpSession>) application.getAttribute("sessions"); list.add(session); System.out.println("添加了" + session.getId()); }@Overridepublic void sessionDestroyed(HttpSessionEvent se) { }} 配置注册监听代码: <listener><listener-class>com.itheima.test.MyServletContextListener</listener-class></listener><listener><listener-class>com.itheima.test.MySessionListener</listener-class></listener> 二、Filter过滤器(重要) Javaweb中的过滤器可以拦截所有访问web资源的请求或响应操作。 1、Filter快速入门 步骤: 1. 创建一个类实现Filter接口。 2. 重写接口中方法,其中doFilter方法是真正过滤用的。 3. 在web.xml文件中进行配置。 注意:在实现Filter接口的类中重写的doFilter方法内如果没有执行这句代码chain.doFilter(request, response);那么浏览器访问服务器的资源是不会被访问到的。 示例代码如下: package com.itheima.filter;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class MyFilter1 implements Filter{@Overridepublic void init(FilterConfig filterConfig) throws ServletException { }@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("MyFilter1拦截开始了"); // 对浏览器发送的请求进行过滤// 放行 chain.doFilter(request, response); System.out.println("MyFilter1拦截结束了"); // 对服务器返回的响应进行过滤 }@Overridepublic void destroy() { }} 在web.xml文件中进行配置。 <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"><display-name>day17_01_filter</display-name><filter><filter-name>MyFilter1</filter-name><filter-class>com.itheima.filter.MyFilter1</filter-class></filter><filter-mapping><filter-name>MyFilter1</filter-name><url-pattern>/*</url-pattern></filter-mapping><servlet><servlet-name>ServletDemo1</servlet-name><servlet-class>com.itheima.servlet.ServletDemo1</servlet-class></servlet><servlet-mapping><servlet-name>ServletDemo1</servlet-name><url-pattern>/servlet/demo1</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app> 测试代码 测试代码一:http://localhost:8080/day17_01_filter/index.jsp 测试代码二:http://localhost:8080/day17_01_filter/servlet/demo1 package com.itheima.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class ServletDemo1 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("ServletDemo1"); }public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }} 2、FilterChain过滤器链 FilterChain 是 servlet 容器(即服务器)为开发人员提供的对象,它提供了对某一资源的已过滤请求调用链的视图。 过滤器使用 FilterChain 调用链中的下一个过滤器(由我们配置的web.xml的先后顺序决定),如果调用的过滤器是链中的最后一个过滤器,则调用链末尾的资源。 问题:怎样可以形成一个Filter链? 答:只要多个Filter对同一个资源进行拦截就可以形成Filter链。 问题:怎样确定Filter的执行顺序? 答:由web.xml中的<filter-mapping>的先后顺序来确定。 示例代码如下图所示: 3、Filter的生命周期 Servlet的生命周期: 实例化 --> 初始化 --> 服务 --> 销毁 当第一次访问服务器资源的时候,Servlet就进行实例化和初始化,只调用一次。(二者不同的地方) 只要应用在(即应用没有卸载),service服务就一直在。 只要应用卸载了或者服务器停止了,Servlet就销毁了。 Filter的生命周期: 当服务器启动时,会创建Filter对象(即调用构造方法进行实例化),并调用init方法,只调用一次。(二者不同的地方) 当浏览器访问服务器资源时,路径与Filter的拦截路径匹配后,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法。 当服务器关闭或应用卸载时,会调用Filter中的destroy方法来进行销毁操作。 4、FilterConfig过滤器参数 在Filter的init方法上有一个参数,类型就是FilterConfig。 FilterConfig它是Filter的配置对象,它可以完成下列功能: 1. 获取Filter名称。 2. 获取Filter初始化参数。 3. 获取ServletContext对象。 如下图所示: 问题:怎样在Filter中获取一个FIlterConfig对象呢? 答:如下图所示: 5、Filter的配置文件书写格式 基本配置 <filter><filter-name>Filter名称</filter-name><filter-class>Filter类的包名.类名</filter-class></filter><filter-mapping><filter-name>Filter名称</filter-name><url-pattern>路径</url-pattern></filter-mapping> 关于其它配置 1. <url-pattern> 完全匹配 以"/demo1"开始,不包含通配符*。 目录匹配 以”/”开始 ,以*结束。 扩展名匹配 *.xxx 不能写成 /*.xxx。</url-pattern> 2. <servlet-name> 它是对指定的servlet名称的servlet进行拦截的。</servlet-name> 3. <dispatcher> 可以取的值有 REQUEST FORWARD ERROR INCLUDE 它的作用是:当以什么方式去访问web资源时,进行拦截操作。 1.REQUEST 当从浏览器直接访问资源,或是重定向到某个资源时进行拦截方式配置,它也是默认值。 2.FORWARD 它描述的是请求转发的拦截方式配置。 3.ERROR 如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。(了解即可) 4.INCLUDE 如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。跟FORWORD差不多。(了解即可)</dispatcher> 三、自动登陆 1、创建数据库与数据表 CREATE DATABASE day17;USE day17;CREATE TABLE USER(id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(100),PASSWORD VARCHAR(100))INSERT INTO USER VALUES(NULL, "tom", "123"); 2、自动登陆功能实现步骤 当用户登陆成功后,判断是否勾选了自动登陆,如果勾选了,就将用户名与密码持久化存储到cookie中。 做一个Filter,对需要自动登陆的资源进行拦截。 自动登录思路图: 四、MD5加密 在mysql中可以对数据进行md5加密,是不可逆(单向)加密。 Md5(字段) UPDATE USER SET PASSWORD=MD5(PASSWORD) WHERE id=1; 在java中也提供了md5加密。md5工具类(MD5Utils类)示例代码如下: package com.itheima.util;import java.math.BigInteger;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public class MD5Utils {/** * 使用md5的算法给某数据进行加密 */public static String md5(String plainText) {byte[] secretBytes = null;try { secretBytes = MessageDigest.getInstance("md5").digest( plainText.getBytes()); } catch (NoSuchAlgorithmException e) {throw new RuntimeException("没有md5这个算法!"); } String md5code = new BigInteger(1, secretBytes).toString(16);for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; }return md5code; }} 五、全局的编码过滤器 分析: 我们之前做的过滤操作,只能对post请求是ok的,get请求就不行了。 怎样可以做成一个通用的,可以处理post、get所有的请求的? 在java中怎样可以对一个方法进行功能增强? 法1. 继承该方法所在的类 该方法不好,因为不想要的东西我也得继承。 法2. 装饰设计模式 1). 创建一个装饰类让它与被装饰类实现同一个接口或继承同一个父类 2). 在装饰类中有一个被装饰类的引用(即:定义一个与被包装类相同对象的引用(字段)) 3). 定义一个构造方法,把被包装的对象传过来 3). 重写要增强的方法(即:对于不需要改写方法,直接调用;对于需要改写方法,写自己的方法体) 问题:我们获取请求参数有以下方法: 1. getParameter 2. getPrameterValues 3. getParameterMap 这三个方法都可以获取请求参数。 分析后,我们知道 getParameter 与 getParameterValues 方法可以依赖于 getParamterMap 方法来实现。 login.jsp <form action="${pageContext.request.contextPath }/servlet/loginServlet" method="post"> username:<input type="text" name="username"><br/> username1:<input type="text" name="username1"><br/><input type="submit" value="登录"></form> LoginServlet.java package com.itheima.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class LoginServlet extends HttpServlet {@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// request.setCharacterEncoding("UTF-8"); // 每一个servlet都写这句代码,不好 String name = request.getParameterValues("username")[0]; String name1 = request.getParameter("username1"); System.out.println(name); System.out.println(name1); }@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { doGet(request, response); }} web.xml <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"><display-name>day17_03_Globalfilter</display-name><filter><filter-name>MyFilter</filter-name><filter-class>com.itheima.filter.MyFilter</filter-class></filter><filter-mapping><filter-name>MyFilter</filter-name><url-pattern>/*</url-pattern> </filter-mapping><servlet><servlet-name>LoginServlet</servlet-name><servlet-class>com.itheima.servlet.LoginServlet</servlet-class></servlet><servlet-mapping><servlet-name>LoginServlet</servlet-name><url-pattern>/servlet/loginServlet</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file><welcome-file>default.html</welcome-file><welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file></welcome-file-list></web-app> MyFilter.java package com.itheima.filter;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.Map;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;// import javax.servlet.http.HttpServletResponse;public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException { }@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request;// HttpServletResponse resp = (HttpServletResponse) response;// req.setCharacterEncoding("UTF-8"); // 只能解决post的提交方式// resp.setContentType("text/html; charset=utf-8"); req = new MyRequest(req); // 装饰一把(把处理乱码的动作放在装饰类代码里) chain.doFilter(req, response); }@Overridepublic void destroy() { }}// 装饰设计模式 + 适配器// 实现与被包装对象相同的接口// 定义一个与被包装类相同对象的引用(字段)// 定义一个构造方法,把被包装的对象传过来// 对于不需要改写方法,直接调用// 对于需要改写方法,写自己的方法体class MyRequest extends HttpServletRequestWrapper { HttpServletRequest request;public MyRequest(HttpServletRequest request) {super(request); // 是因为父类没有无参数构造this.request = request; }// 一个一个的乱码处理,不好// @Override public String getParameter(String name) { // name = request.getParameter(name); // 可能会出现乱码 // try { // return new String(name.getBytes("iso-8859-1"), "UTF-8"); // 处理乱码// } catch (UnsupportedEncodingException e) {// e.printStackTrace(); // } // return null; // // }@Overridepublic String getParameter(String name) { Map<String, String[]> map = getParameterMap();return map.get(name)[0]; }@Overridepublic String[] getParameterValues(String name) { Map<String, String[]> map = getParameterMap();return map.get(name); }private boolean flag = true;@Overridepublic Map<String, String[]> getParameterMap() {// 得到原始的map集合 Map<String, String[]> map = request.getParameterMap(); // 可能会出现乱码if (flag) { // 处理乱码// 将map集合中的String[]得到,解决每一个元素的乱码问题for (Map.Entry<String, String[]> m : map.entrySet()) { String[] values = m.getValue();for (int i = 0; i < values.length; i++) {try { values[i] = new String(values[i].getBytes("iso-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } flag = false; }return map; }} 我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

我的深度学习论文阅读之旅(一)

0. 参考资料 Xception: Deep Learning with Depthwise Separable Convolutions. François Chollet, 2017. 如何评价谷歌的xception网络? MobileNet V2 论文初读 纵览轻量化卷积神经网络:SqueezeNet、MobileNet、ShuffleNet、Xception 无需数学背景,读懂 ResNet、Inception 和 Xception 三大变革性架构 王井东详解ICCV 2017入选论文:通用卷积神经网络交错组卷积 变形卷积核、可分离卷积?卷积神经网络中十大拍案叫绝的操作 CNN中千奇百怪的卷积方式大汇总 对深度可分离卷积、分组卷积、扩张卷积、转置卷积(反卷积)的理解 hellozting/InterleavedGroupConvolutions 可变形卷积网络:计算机新“视”界 对 CNN 的研究,目前集中在三个维度:channel, filter,和 residual。 目标是把模型做小、做强、做到移动端,精度差点,没关系,可以加数据,离线多跑几轮。 1. Channel 无论 Xception,还是 ResNeXt,还有面向移动端的 ShuffleNet 和 MobileNet。都是基于 Group Convolution思想在 channel 维度进行“网络工程”,搭建新的模型。 Xception 的核心思想是:Depth-wise Separable Convolution 1.1 Group Convolution 推荐阅读: A Tutorial on Filter Groups (Grouped Convolution) Groups & Group Convolutions Mathematicians call these abstract patterns groups. There is an entire field of math dedicated to them. Connections between a group and an object like the square are called group actions. 下图是一个变换群: group 是相对于上一层的 channel 来说的。 假如 \(group_{size} = N\), 上一层的 \(\frac{Channel}{FeatureMap \times Filter}\) 的数目为 \(M\) 简单的讲就是把 channel 做 \(N\) 等分,然后每一份(一个 group)分别与上一层的输出的\(\frac{M}{N}\) 个 channel 独立连接,之后将每个 group 的输出叠在一起(concatenate),作为这一层的输出 channel。 Group Convolution 是指将 channels 细分成多个 group,然后再分组进行 Convolution。这种思想始于 2012 年 AlexNet 的双 GPU 架构设计,相当于把 channels 均分到两个 GPU,分组卷积: 如果对每个通道进行卷积,就是 Depthwise Convolution。 1.2 Separable Convolution 在卷积层中间插入 \(1 \times 1\) 卷积,即 pointwise convolution。举个例子,对经典的卷积操作,如果 OD 是 \(256\),ID 是 \(128\),卷积核大小 \(3\times3\),需要的参数为 \(128 \times 3 \times 3 \times 256=294912\) 个参数,而 Spearable 卷积方法,假如 \(DM=4\),这样中间层的 channel 数为\(128 \times 4=512\),再经过 \(1 \times 1\) 卷积降维到 \(256\) 个 channel,需要的总参数为:\(128 \times 3 \times 3 \times 4 + 128 \times 4 \times 1 \times 1 \times 256=135680\),参数量相当于普通卷积的 \(46\%\),还增加了通道数(\(128 \times 4=512\))增强了特征表达能力。 所以说,理想的卷积 Block 应该是先用 \(1 \times 1\) 卷积核降 channel,然后再进行 \(3 \times 3\) 卷积提取特征,最后再用 \(1 \times 1\) 卷积核降 channel。 1.3 Xception 利用上述结构重新设计 Inception model block,就是 Xception;重新设计 Resnet,就是 ResNeXt 架构。以达到在减少参数量的情况下增加模型的层数,既减少了存储空间,还增强了模型的表达能力。 通常来讲,如果对 channel 进行分组卷积,各小组就分头行动,互相没有交流,这样显然没有充分利用 channel 的信息。ShuffleNet 在分组之前,先将 channel 随记打乱,这样对信息的利用更充分,因此可以通过设计降低模型参数量而不影响模型的表达能力。 2. Filter 2.1 Wavenet 经典 CNN 的 Filter 是在邻域内采样卷积,如 \(3 \times 3\) 卷积核是在 \(8\) 邻域采样。 但是,Filter 可以跨点采样进行卷积,从而可以利用更大范围内的信息,即 Dilated CNN,最早应用于图像语义分割,去年谷歌提出的 Wavenet 模型将 CNN 拓展到语音识别和语音合成。 2.2 Dilated Resnet 将 Dilated CNN 的思想应用到 Resnet 架构中,就是 CVPR 2017 的 “Dilated Residual Networks” 3. Connection ResNet 的 Connection 方式是将输入和输出相加,形成一个残差 Block。DenseNet 则更进一步,在每个 Block 内,将输入和输出直接拼接,而且在每一层都和之前所有层的输出拼接,这样可以减少中间层的通道数。 而最新的 Dual Path Networks 模型,则是融合了 ResNet 和 DenseNet 的优点:特征重利用和特征重提取。采用了双通道架构: 可以预想,接下来,将会有模型融合以上三点的集大成者? 启发与思考 现在越来越多的 CNN 模型从巨型网络到轻量化网络一步步演变,模型准确率也越来越高。现在工业界追求的重点已经不是准确率的提升(因为都已经很高了),都聚焦于速度与准确率的 trade off,都希望模型又快又准。因此从原来 AlexNet、VGGnet,到体积小一点的 Inception、Resnet 系列,到目前能移植到移动端的 mobilenet、ShuffleNet(体积能降低到 \(0.5\) mb!),我们可以看到这样一些趋势: 卷积核方面: 大卷积核用多个小卷积核代替; 单一尺寸卷积核用多尺寸卷积核代替; 固定形状卷积核趋于使用可变形卷积核; 使用 \(1 \times 1\) 卷积核(bottleneck 结构)。 卷积层通道方面: 标准卷积用 depthwise 卷积代替; 使用分组卷积; 分组卷积前使用 channel shuffle; 通道加权计算。 卷积层连接方面: 使用 skip connection,让模型更深; densely connection,使每一层都融合上其它层的特征输出(DenseNet) 启发 类比到通道加权操作,卷积层跨层连接能否也进行加权处理?bottleneck + Group conv + channel shuffle + depthwise 的结合会不会成为以后降低参数量的标准配置? 探寻有趣之事!

优秀的个人博客,低调大师

Docker 构建自己的容器(学习笔记二)

Dockerfile --------------------------------------------------------------------------------------------------------------------- 首先需要的包和dockerfile放在一起 [root@localhost docker-file]# ls Dockerfile nginx-1.9.3.tar.gz pcre-8.37.tar.gz [root@localhost docker-file]# pwd /opt/docker-file [root@localhost docker-file]# --------------------------------------------------------------------------------------------------------------------- 创建dockerfile [root@localhost docker-file]# vim Dockerfile #base images,name or id 可以是镜像名字或者镜像ID FROM 3bee3060bfc8 #maintainer 作者 MAINTAINER wangzz #add 把包添加到容器的指定目录,如果是tar包会自动解压 ADD pcre-8.37.tar.gz /usr/local/src ADD nginx-1.9.3.tar.gz /usr/local/src #run 在容器里运行命令安装nginx需要软件 RUN yum install -y wget gcc gcc-c++ make openssl-devel #run 在容器里运行命令创建用户 RUN useradd -s /sbin/nologin -M www #workdir 相当于cd到这个目录 WORKDIR /usr/local/src/nginx-1.9.3 #run 在容器里运行命令进行编译安装 RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-http_stub_status_module --with-pcre=/usr/local/src/pcre-8.37 && make && make install #run 在容器里运行命令更改nginx配置文件,使其前台运行 RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf #env 将nginx启动命令加到环境变量里 ENV PATH /usr/local/nginx/sbin:$PATH #映射80端口 EXPOSE 80 执行nginx命令 CMD ["nginx"] --------------------------------------------------------------------------------------------------------------------- 执行dockerfile [root@localhost docker-file]# docker build -t my-nginx-1:v1 /opt/docker-file/ 当成功后会出现这个,说明执行成功 Successfully built 21273b8e163a --------------------------------------------------------------------------------------------------------------------- 查看镜像 [root@localhost docker-file]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-nginx-1 v1 21273b8e163a 14 minutes ago 429.1 MB --------------------------------------------------------------------------------------------------------------------- 使用创建好的镜像启动容器 [root@localhost docker-file]# docker run -dit --name mynginx 21273b8e163a --------------------------------------------------------------------------------------------------------------------- 查看运行的容器 [root@localhost docker-file]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES aa30accbfc06 21273b8e163a "nginx" 3 seconds ago Up 2 seconds 80/tcp mynginx --------------------------------------------------------------------------------------------------------------------- 访问这个容器的80端口,看nginx是否安装启动成功 [root@localhost docker-file]# curl 172.17.0.3 Welcome to nginx! body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } Welcome to nginx! If you see this page, the nginx web server is successfully installed and working. Further configuration is required. For online documentation and support please refer to nginx.org. Commercial support is available at nginx.com. Thank you for using nginx.

优秀的个人博客,低调大师

Mysql 主从复制实战(学习笔记十三)

https://www.cnblogs.com/along21/p/8011596.html https://blog.csdn.net/qq3401247010/article/details/78754671 一主一从,一主多从,双主 ① iptables -F && setenforce 清空防火墙策略,关闭selinux ② 拿两台服务器都使用yum 方式安装Mysql 服务,要求版本一致 ③ 分别启动两台服务器mysql 2、在主master 主服务器上 ① vim /etc/my.cnf 修改mysql主配置文件,对master进行配置,包括打开二进制日志,指定唯一的servr ID server-id=1#配置server-id,让主服务器有唯一ID号 log-bin=mysql-bin #打开Mysql日志,日志格式为二进制 skip-name-resolve #关闭名称解析,(非必须) ②创建并授权slave mysql 用的复制帐号 GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO slave@'192.168.%.%'IDENTIFIED BY'along'; 分析:在Master的数据库中建立一个备份帐户:每个slave使用标准的MySQL用户名和密码连接master。进行复制操作的用户会授予REPLICATION SLAVE权限。 ③ 查看主服务器状态 在Master的数据库执行show master status,查看主服务器二进制日志状态,位置号 3、在从slave mysql1上 ① 修改主配置文件 vim /etc/my.cnf 打开中继日志,指定唯一的servr ID,设置只读权限 server-id=2#配置server-id,让从服务器有唯一ID号 relay_log = mysql-relay-bin #打开Mysql日志,日志格式为二进制 read_only =1 #设置只读权限 log_bin = mysql-bin #开启从服务器二进制日志,(非必须) log_slave_updates =1#使得更新的数据写进二进制日志中 systemctl start mariadb 开启服务 ②启动从服务器复制线程,让slave连接master,并开始重做master二进制日志中的事件。 MariaDB [(none)]> change master to master_host='192.168.30.107', -> master_user='slave', -> master_password='along', -> master_log_file='mysql-bin.000001', -> master_log_pos=245; MariaDB [(none)]> start slave; # 启动复制线程,就是打开I/O线程和SQL线程;实现拉主的bin-log到从的relay-log上;再从relay-log写到数据库内存里 ③ 查看从服务器状态 可使用SHOW SLAVE STATUS\G查看从服务器状态,如下所示,也可用show processlist \G查看当前复制状态: Slave_IO_Running: Yes #IO线程正常运行 Slave_SQL_Running: Yes #SQL线程正常运行 4、测试 ① 在主上创建一个along库 ② 从上自动生成along数据库 5、若要继续添加新salve,实现一主多从 假如master已经运行很久了,想对新安装的slave 进行数据同步,甚至它没有master 的数据。 (1)在主master-mysql 上 ① 进行完全备份 mysqldump --all-databases > /backup/mysql-all-backup-`date+%F-%T`.sql 把备份生成的文件发给salve-mysql2机器上scp/backup/mysql-all-backup-2017-11-20-22\:04\:06.sql @192.168.30.2: ② 查看现在的二进制文件状态 MariaDB [(none)]> show master status; (2)在从slave-mysql2上 ① vim /etc/my.cnf 修改主配置文件,设为从 ② 进行master的完全备份恢复 mysql -uroot -p < mysql-all-backup-2017-11-20-22\:04\:06.sql systemctl start mariadb 开启服务 恢复完后,数据直接与主完全一致 ③启动从服务器复制线程 MariaDB [(none)]> change master to master_host='192.168.30.107', -> master_user='slave', -> master_password='along', -> master_log_file='mysql-bin.000003', -> master_log_pos=500; 7、解除主从关系,恢复独立的服务器 ① MariaDB [(none)]> stop slave; 关闭两个线程 ② vim /etc/my.cnf 删除3行 relay-log =mysql-relay-log read-only =1log_slave_updates =1 ③ systemctl restart mariadb 重启服务 主从+复制过滤 1、环境准备 机器名称IP配置服务角色备注 master-mysql192.168.30.107主数据库二进制日志 slave-mysql1192.168.30.7从数据库中继日志 slave-mysql2192.168.30.2从数据库中继日志 2、在主master 主服务器上 ① vim /etc/my.cnf 修改mysql主配置文件,对master进行配置,打开二进制日志,指定唯一的servr ID,设置复制过滤 server-id=1#配置server-id,让主服务器有唯一ID号 log-bin=mysql-bin #打开Mysql日志,日志格式为二进制 skip-name-resolve #关闭名称解析,(非必须) binlog_ignore_db=home #除了home数据库,其他都允许从复制主的二进制文件 #binlog_do_db=along #仅允许从复制along数据库的二进制文件 systemctl start mariadb 开启服务 ②创建并授权slave mysql 用的复制帐号 GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO slave@'192.168.%.%'IDENTIFIED BY'along'; 分析:在Master的数据库中建立一个备份帐户:每个slave使用标准的MySQL用户名和密码连接master。进行复制操作的用户会授予REPLICATION SLAVE权限。 ③ 查看主服务器状态 在Master的数据库执行show master status,查看主服务器二进制日志状态,位置号 3、在从slave mysql1上 ① 修改主配置文件 vim /etc/my.cnf 打开中继日志,指定唯一的servr ID,设置只读权限 server-id=2#配置server-id,让从服务器有唯一ID号 relay_log = mysql-relay-bin #打开Mysql日志,日志格式为二进制 read_only =1 #设置只读权限 log_bin = mysql-bin #开启从服务器二进制日志,(必须) log_slave_updates =1#使得更新的数据写进二进制日志中 systemctl start mariadb 开启服务 ②启动从服务器复制线程,让slave连接master,并开始重做master二进制日志中的事件。 MariaDB [(none)]> change master to master_host='192.168.30.107', -> master_user='slave', -> master_password='along', -> master_log_file='mysql-bin.000001', -> master_log_pos=245; MariaDB [(none)]> start slave; # 启动复制线程,就是打开I/O线程和SQL线程;实现拉主的bin-log到从的relay-log上;再从relay-log写到数据库内存里 ③ 查看从服务器状态 可使用SHOW SLAVE STATUS\G查看从服务器状态,如下所示,也可用show processlist \G查看当前复制状态: Slave_IO_Running: Yes #IO线程正常运行 Slave_SQL_Running: Yes #SQL线程正常运行 4、测试主从和复制过滤 (1)测试主从关系 在主上创建一个along、home库;从上自动生成along、home数据库 (2)测试复制过滤 ① 在主上:在along库中创建一个classes的表;从上自动生成 MariaDB [home]> create table classes (id int not null,name varchar(20)); ② 在主上:在home库中创建一个classes的表;从上没有生成 MariaDB [home]> create table classes (id int not null,name varchar(20)); slave-mysql1 上,过滤成功 5、设置slave-mysql2 为slave-mysql1 的从,且在mysql2 设置复制过滤 (1)在slave-mysql1上,不用怎么设置 因为上边主配置文件已经开启了自己的二进制文件;且slave-mysql1 是从开始就同步master的,所以授权命令也同步过了 MariaDB [home]> select user,host from mysql.user; 可以查看自己授权过的用户 (2)slave-mysql2 上,可以像上实验一样,先给主的完全备份在本机恢复一下 ① 在主上完备 mysqldump --all-databases > /backup/mysql-all-backup-`date+%F-%T`.sqlscp/backup/mysql-all-backup-2017-11-21-11:14:59.sql @192.168.30.2: ② 进行master的完全备份恢复 mysql -uroot -p < mysql-all-backup-2017-11-20-22\:04\:06.sql ③ 在slave-mysql2 上 vim /etc/my.cnf 修改主配置文件,设为从;且设置过滤 server-id=3relay-log =mysql-relay-log read-only =1log-bin = mysql-bin log_slave_updates =1replicate_do_dB=along #只复制它的主的along数据库 systemctl start mariadb 开启服务 ④ mysql 打开数据库,查看数据恢复成功; 启动从服务器复制线程,让slave连接master,并开始重做master二进制日志中的事件。 MariaDB [(none)]> change master to master_host='192.168.30.107', -> master_user='slave', -> master_password='along', -> master_log_file='mysql-bin.000008', -> master_log_pos=773; MariaDB [(none)]> start slave; ⑤ MariaDB [(none)]> show slave status \G; 查看,两个进程打开,且只复制主的along数据库 6、测试主从从和slave-mysql2的复制过滤 (1)在主上删除job数据库,master 和slave-mysql1 都删除成功 MariaDB [home]> drop database job; 删除job库 因为slave-mysql2 只同步slave-mysql1 的along库,所以没有删除 (2)在主上的along数据库,创建一个grade 表,master 和slave-mysql1 都删除成功 MariaDB [along]> create table grade (id int not null,name varchar(20)); 在slave-mysql2 上也自动生成成功 双主 应用:只适合小型公司,小并发访问量,毕竟同时写入易出错 1、环境准备 机器名称IP配置服务角色备注 mysql1192.168.30.107数据库中继日志、二进制日志 mysql2192.168.30.7数据库中继日志、二进制日志 2、配置总配置文件,除了ID号和起始数,两边都是一样的 vim /etc/my/cnf server-id=1 #mysql1的配置ID为1,mysql2的ID为2 relay-log =mysql-relay-log log-bin = mysql-bin log_slave_updates =1auto_increment_increment=2 #表示自增长字段每次递增的量,步长 auto_increment_offset=1#表示自增长字段从那个数开始,mysql1从1开始;mysql2从2开始 systemctl start mariadb 3、互相设为对方的从 (1)授权远程登录的用户 mysql1、2 上 GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO slave@'192.168.%.%'IDENTIFIED BY'along'; (2)开启复制线程 ① mysql1 上 MariaDB [(none)]> change master to master_host='192.168.30.7', -> master_user='slave', -> master_password='along', -> master_log_file='mysql-bin.000002', -> master_log_pos=245; MariaDB [(none)]> start slave; # 启动复制线程 ② mysql2 上 MariaDB [(none)]> change master to master_host='192.168.30.107', -> master_user='slave', -> master_password='along', -> master_log_file='mysql-bin.000002', -> master_log_pos=245; MariaDB [(none)]> start slave; # 启动复制线程 4、测试双方互为主从的关系 在mysql1上,删除test数据库;mysql2 上也自动删除 在mysql2上,创建xiaohei数据库;mysql2 上也自动生成 5、设置自增长ID的表 MariaDB [along]> create table home (id int PRIMARY KEY AUTO_INCREMENT,name varchar(20)); ① 在mysql1上向表中插入数据 MariaDB [along]> insert into home(name) value('mayun'),('mahuateng'),('wangjianlin'); ② 在mysql2上向表中插入数据 MariaDB [along]> insert into home(name) value('dinglei'),('liyanhong'),('leijun'); 场景 公司规模已经形成,用户数据已成为公司的核心命脉,一次老王一不小心把数据库文件删除,通过mysqldump备份策略恢复用了两个小时,在这两小时中,公司业务中断,损失100万,老王做出深刻反省,公司也因此对于数据库的性能和可靠性提出更高要求。要求对数据库进行改造,使其承载力进行提升,故障修复时间减少,有没有能实现的方案呢? 数据库常遇到的问题 一、性能问题 1、向上拓展 scale up:针对单台服务器,提高服务器的硬件性能,比如:内存,cpu等,个体本身 容易达到极限 2、向外拓展 scale out:多台服务器形成集群,共同完成一件事情 二、可用性问题 1、数据库服务中断 2、误操作数据损坏 3、硬件故障 4、数据库升级测试遭遇bug 5、黑客攻击 数据库高可用技术说明 高可用架构对于互联网服务基本是标配,无论是应用服务还是数据库服务都需要做到高可用。虽然互联网服务号称7*24小时不间断服务,但多多少少有一些时候服务不可用,比如某些时候网页打不开,百度不能搜索或者无法发微博,发微信等。一般而言,衡量高可用做到什么程度可以通过一年内服务不可用时间作为参考,要做到3个9的可用性,一年内只能累计有8个小时不可服务,而如果要做到5个9的可用性,则一年内只能累计5分钟服务中断。所以虽说每个公司都说自己的服务是7*24不间断的,但实际上能做到5个9的屈指可数,甚至根本做不到,国内互联网巨头BAT(百度,阿里巴巴,腾讯)都有因为故障导致的停服问题。对于一个系统而言,可能包含很多模块,比如前端应用,缓存,数据库,搜索,消息队列等,每个模块都需要做到高可用,才能保证整个系统的高可用。对于数据库服务而言,高可用可能更复杂,对用户的服务可用,不仅仅是能访问,还需要有正确性保证,因此,对于实现数据库高可用,对互联网公司来说极其重要! 企业级数据库高可用架构图 Mysql主从架构技术说明 Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机(Master)的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。 主从复制架构图 数据库复制特性 Mysql复制解决的问题 MySQL复制技术有以下一些特点: (1) 数据分布 (Data distribution ) (2) 负载平衡(load balancing) (3) 备份(Backups) (4) 高可用性和容错性 High availabilityand failover Mysql复制如何工作 Mysql的复制功能主要有3个步骤: (1) 主服务器(master)将改变记录到二进制日志(binarylog)中(这些记录叫做二进制日志事件,binary log events) (2) 从服务器(slave)将主服务器master的binary logevents拷贝到它的中继日志(relay log) (3) slave重做中继日志中的事件,将改变反映它自己的数据。 1、该过程的第一部分就是master记录二进制日志。在每个事务更新数据完成之前,master在二进制日志记录这些改变。MySQL将事务串行的写入二进制日志,在事件写入二进制日志完成后,master通知存储引擎提交事务。此后可接收slave的请求 2、下一步就是slave将master的binary log拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程。I/O线程在master上打开一个普通的连接,然后开始在主节点上binlog dump process(二进制转存线程)。Binlog dump process从master的二进制日志中读取事件,如果已经跟上master,它会睡眠并等待master产生新的事件。I/O线程将这些事件写入中继日志。 3、 SQL slave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新slave的数据,使其与master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。 I/O线程:将master数据库二进制日志拉到slave数据库上,并将二进制日志写到中继日志,写完之后,他会睡眠并等待master数据库二进制日志更新,一旦更新,就会写入slave数据库的中继日志中 SQL线程:读取中继日志的事件,并在数据库中执行,写入到内存中,使slave数据库的数据与master数据库中的数据一致 Mysql实现企业级数据库主从复制架构实战 注意:slave数据库只能是可读的,不能是可写的,如果改变了slave数据库的数据,master不能从slave数据库上同步数据,导致主从数据库数据不一致。 实战演练 一、环境准备 centos系统服务器2台、一台用户做Mysql主服务器,一台用于做Mysql从服务器,都在同一个网段中,配置好yum源、防火墙关闭、各节点时钟服务同步、各节点之间可以通过主机名互相通信 二、准备步骤: 1、iptables -F && setenforce 清空防火墙策略,关闭selinux 2、拿两台服务器都使用yum方式安装Mysql服务,要求版本一致 3、分别启动两台服务器mysql服务,确保服务正常 三、实现步骤: 1、配置master主服务器 对master进行配置,包括打开二进制日志,指定唯一的servr ID。例如,在配置文件加入如下值 vim /etc/my.cnf server-id=1#配置server-id,让主服务器有唯一ID号(让从服务器知道他的主服务器是谁) log-bin=mysql-bin#打开Mysql日志,日志格式为二进制 skip-name-resolve#关闭名称解析,(非必须) 然后重启数据库服务 systemctl restart mariadb 2.创建复制帐号 在Master的数据库中建立一个备份帐户:每个slave使用标准的MySQL用户名和密码连接master 。进行复制操作的用户会授予REPLICATION SLAVE权限。(给从服务器授权,让他能从主服务器拷贝二进制日志) mysql GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO slave@'192.168.10.%' IDENTIFIED BY 'magedu'; 3.查看主服务器状态 在Master的数据库执行show master status,查看主服务器二进制日志状态 4、配置slave从服务器 对slave进行配置,打开中继日志,指定唯一的servr ID,设置只读权限。在配置文件加入如下值 vim /etc/my.cnf server-id=2#配置server-id,让从服务器有唯一ID号 relay_log = mysql-relay-bin#打开Mysql日志,日志格式为二进制 read_only = 1#设置只读权限 log_bin = mysql-bin#开启从服务器二进制日志 log_slave_updates = 1#使得更新的数据写进二进制日志中 然后重启数据库服务 systemctl restart mariadb 5.启动从服务器复制线程 让slave连接master,并开始重做master二进制日志中的事件。 mysql CHANGE MASTER TO MASTER_HOST='192.168.10.190', MASTER_USER='slave', MASTER_PASSWORD='magedu', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=278; 执行start slave;# 启动复制线程。 6、查看从服务器状态 可使用SHOW SLAVE STATUS\G查看从服务器状态,如下所示,也可用show processlist \G查看前复制状态: mysql SHOW SLAVE STATUS\G Slave_IO_Running: Yes #IO线程正常运行 Slave_SQL_Running: Yes #SQL线程正常运行 7.测试 理想的结果是在主服务器上添加的数据,在从服务器上也会同步 在主服务器上 在从服务器上 四、添加新slave服务器 假如master已经运行很久了,想对新安装的slave进行数据同步,甚至它没有master的数据。 此时,有几种方法可以使slave从另一个服务开始,例如,从master拷贝数据,从另一个slave克隆,从最近的备份开始一个slave。为了加快Slave与master同步,可用以下方式先进行数据同步: (1)master的某个时刻的数据快照; (2)数据库的备份数据 (3)master的二进制日志文件。 实现主从从架构 也可以搭建主从从架构,让从服务器之间进行复制 就是在从服务器也开启二进制日志,然后从的从I/O线程再将从的二进制日志给拷贝过来写入到自己的relay log中,然后sql线程再读取relay log中的事件,在数据库中执行,写入到内存中。 Mysql复制过滤器 复制过滤器: 仅复制有限一个或几个数据库相关的数据,而非所有;由复制过滤器进行; 有两种实现思路: (1) 主服务器 主服务器仅向二进制日志中记录有关特定数据库相关的写操作; binlog_do_db= binlog_ignore_db= (2) 从服务器 从服务器的SQL THREAD仅重放关注的数据库或表相关的事件,并将其应用于本地; Replicate_Do_DB= Replicate_Ignore_DB= 企业常见数据库架构 一、单一master和多slave 在实际应用场景中,MySQL复制90%以上都是一个Master复制到一个或者多个Slave的架构模式,主要用于读压力比较大的应用的数据库端廉价扩展解决方案。因为只要Master和Slave的压力不是太大(尤其是Slave端压力)的话,异步复制的延时一般都很少很少。尤其是自从Slave端的复制方式改成两个线程处理之后,更是减小了Slave端的延时问题。而带来的效益是,对于数据实时性要求不是特别高的应用,只需要通过廉价的pcserver来扩展Slave的数量,将读压力分散到多台Slave的机器上面,即可通过分散单台数据库服务器的读压力来解决数据库端的读性能瓶颈,毕竟在大多数数据库应用系统中的读压力还是要比写压力大很多。这在很大程度上解决了目前很多中小型网站的数据库压力瓶颈问题,甚至有些大型网站也在使用类似方案解决数据库瓶颈。 单一master和多slave架构图 (1) 不同的slave扮演不同的作用(例如使用不同的索引,或者不同的存储引擎); (2) 用一个slave作为备用master,只进行复制;#主服务器挂了之后,可在从服务器执行 1> 在备机上执行STOP SLAVE 和RESET MASTER 2> 查看show slave status \G; 3> 然后修改应用的连接地址。 (3) 用一个远程的slave,用于灾难恢复; 二、互为主从Master-Master(Master-Master in Active-Active Mode) Master-Master复制的两台服务器,既是master,又是另一台服务器的slave。这样,任何一方所做的变更,都会通过复制应用到另外一方的数据库中。 即:在两台服务器上既执行master的操作又执行slave的操作(注意:两台数据库都必须是可写的) 互为主从复制过程 互为主从:两个节点各自都要开启binlog和relay log; 1、数据不一致; 2、自动增长id; 什么是自增长ID? 对于某些唯一性的字段,可以通过设置自增长ID来实现,自增长ID的数据,代表这个表中存在一条唯一的记录;而自增长id是肯定不会重复的; 创建表,设置ID为自增长 create table userInfo (id int PRIMARY KEY AUTO_INCREMENT,name varchar(50) NOT NULL); 两边插入数据看数据增长 insert into userInfo(name) value('xiao'),('da'),('lao'); 定义一个节点使用奇数id auto_increment_increment=2#表示自增长字段每次递增的量 auto_increment_offset=1#表示自增长字段从那个数开始 另一个节点使用偶数id auto_increment_increment=2 auto_increment_offset=2 配置: 1、server_id必须要使用不同值; 2、均启用binlog和relay log; read only = 0(因为互为主从,所以必须是可写的) 3、存在自动增长id的表,为了使得id不相冲突,需要定义其自动增长方式; 服务启动后执行如下两步: 4、都授权有复制权限的用户账号; 5、各把对方指定为主节点; 实验:数据库互为主从复制步骤 1.修改mysql配置文件 一台数据库服务器上 vim /etc/my.cnf server-id = 1 log_bin = mysql_bin relay_log = relay-log skip-name-resolve = on log_slave_updates = 1 auto_increment_increment=2 auto_increment_offset=1 另一台数据库服务器上 vim /etc/my.cnf server-id = 2 relay_log = relay-log log_bin = mysql-log skip-name-resolve = on log_slave_updates = 1 auto_increment_increment=2 auto_increment_offset=2 修改完配置文件后,重启数据库服务 systemctl restart mariadb 2.创建复制帐号 分别在两台数据库服务器上创建复制账号 mysql GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO slave@'192.168.10.%' IDENTIFIED BY 'magedu'; 3.启动从服务器复制线程 让slave连接master,并开始重做master二进制日志中的事件。 mysql CHANGE MASTER TO MASTER_HOST='192.168.10.190', MASTER_USER='slave', MASTER_PASSWORD='magedu', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=278; 执行start slave;# 启动复制线程。 另一台数据库服务器也是如此 4、查看从服务器状态 可使用SHOW SLAVE STATUS\G查看从服务器状态,如下所示,也可用show processlist \G查看前复制态: mysql SHOW SLAVE STATUS\G Slave_IO_Running: Yes #IO线程正常运行 Slave_SQL_Running: Yes #SQL线程正常运行 两台数据库服务器都显示如上结果就ok。 5.创建表,设置ID为自增长,两边插入数据看数据增长 在一台数据库服务器上 mysql create database dnf; use dnf; create table userinfo (id int PRIMARY KEY AUTO_INCREMENT,name varchar(20) NOT NULL); insert into userinfo (name) values('ni'),('wo'),('ta'); 然后查看表,因为是自增长id,从1开始,步长为2,所以添加的数据id为1,3,5 然后在另一台数据库服务器插入数据,因为是自增长id,从2开始,步长为2,所以新添加的数据id为6,8,10 排错:当配置文件中配置中继日志格式不小心配置错了,或者让slave连接master,执行sql语句不小心写错了,都有可能导致start slave;报错,此时可以show slave status\G;会出现一大串信息,里面会提示错误。找到错误以后,重置slave,reset slave;重新设置,然后再start slave; 注意:mysql的错误日志非常重要,可以提供错误信息,从而找到错误原因。 互为主从容易导致数据不一致,此时我们可以用两个实例来互为主从 三种复制方式 异步复制(Asynchronous replication) MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果crash掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整 全同步复制(Fully synchronous replication) 指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。需要有超时时间。 半同步复制(Semisynchronous replication) 介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。 半同步复制 支持多种插件:/usr/lib64/mysql/plugins/ 需要安装方可使用: mysql> INSTALL PLUGIN plugin_name SONAME 'shared_library_name'; 半同步复制: semisync_master.so semisync_slave.so 主节点: INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; MariaDB [mydb]> SHOW GLOBAL VARIABLES LIKE 'rpl_semi%'; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_enabled | OFF | | rpl_semi_sync_master_timeout | 10000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_no_slave | ON | +------------------------------------+-------+ MariaDB [mydb]> SET GLOBAL rpl_semi_sync_master_enabled=ON/1; stop slave; start slave; 从节点: INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; MariaDB [mydb]> SHOW GLOBAL VARIABLES LIKE 'rpl_semi%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_semi_sync_slave_enabled | OFF | | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+ MariaDB [mydb]> STOP SLAVE IO_THREAD; MariaDB [mydb]> SET GLOBAL rpl_semi_sync_slave_enabled = ON ; MariaDB [mydb]> SHOW GLOBAL VARIABLES LIKE 'rpl_semi%'; MariaDB [mydb]> START SLAVE IO_THREAD; stop slave; start slave; 可查看从库错误日志观察是否生效 master错误日志 slave错误日志 mysql优化: 1.可以用数据缓存,常见的memcache 2.数据库本身有很多缓存机制,可使用对应的缓存策略 3.对数据来说,竟可能使用索引 4.对请求而言,可以实现读写分离,对读请求负载均衡 5.对大数据库或者表,可根据业务逻辑进行分库分表 6.多有的优化,尽可能网内存中存放 分库分表

优秀的个人博客,低调大师

JavaScript DOM学习笔记--创建追加元素

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>动态添加城市</title> <script> window.onload = function(){ document.getElementById("btn").onclick = function(){ //1.获取ul元素节点 var ulEle = document.getElementById("ul1"); //2.创建城市文本节点 var textNode = document.createTextNode("深圳");//深圳 //3.创建li元素节点 var liEle = document.createElement("li");//<li></li> //4.将城市文本节点添加到li元素节点中去 liEle.appendChild(textNode);//<li>深圳</li> //5.将li添加到ul中去 ulEle.appendChild(liEle); } } </script> </head> <body> <input type="button" value="添加新城市" id="btn"/> <ul id="ul1"> <li>北京</li> <li>上海</li> <li>广州</li> </ul> </body> </html>

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册