MySQL主从配置-Docker

MySQL单机启动mysql
docker run --name mysqlserver -e MYSQL_ROOT_PASSWORD=123456 -d -i -p 3306:3306  mysql:5.7

进入终端:

docker exec -it  2a7a85124400  /bin/bash
mysql -h 127.0.0.1 -u root -p

宿主机目录结构:

image.png

主从配置:

Master和Slaver 配置文件

 

Master: my.cnf   

[mysqld]
 
server_id = 1
 
log-bin= mysql-bin
 
read-only=0
 
binlog-do-db=order_demo

 
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
 
character-set-server=utf8
 
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

 

 Slaver: my.cnf

[mysqld]
 
server_id = 2
 
log-bin= mysql-bin
 
read-only=1
 
replicate-do-db=order_demo
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
 
character-set-server=utf8
 
 
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

说明: log-bin :需要启用二进制日志 server_id : 用于标识不同的数据库服务器,而且唯一

binlog-do-db : 需要记录到二进制日志的数据库 binlog-ignore-db : 忽略记录二进制日志的数据库 auto-increment-offset :该服务器自增列的初始值 auto-increment-increment :该服务器自增列增量

replicate-do-db :指定复制的数据库 replicate-ignore-db :不复制的数据库 relay_log :从库的中继日志,主库日志写到中继日志,中继日志再重做到从库 log-slave-updates :该从库是否写入二进制日志,如果需要成为多主则可启用。只读可以不需要

如果为多主的话注意设置 auto-increment-offset 和 auto-increment-increment 如上面为双主的设置: 服务器 152 自增列显示为:1,3,5,7,……(offset=1,increment=2) 服务器 153 自增列显示为:2,4,6,8,……(offset=2,increment=2)

 

1)read_only=1只读模式,不会影响slave同步复制的功能,所以在MySQL slave库中设定了read_only=1后,通过 show slave status\G ,命令查看salve状态,可以看到salve仍然会读取master上的日志,并且在slave库中应用日志,保证主从数据库同步一致;

  2)read_only=1只读模式,可以限定普通用户进行数据修改的操作,但不会限定具有super权限的用户的数据修改操作;在MySQL中设置read_only=1后,普通的应用用户进行insert、update、delete等会产生数据变化的DML操作时,都会报出数据库处于只读模式不能发生数据变化的错误,但具有super权限的用户,例如在本地或远程通过root用户登录到数据库,还是可以进行数据变化的DML操作;

 

2、启动创建主从容器

//创建并启动主从容器;

 

//master

docker run –name mastermysql -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 -v /opt/docker/mysql/master/data:/var/lib/mysql -v /opt/docker/mysql/master/conf/my.cnf:/etc/mysql/my.cnf  mysql:5.7

 //slave

docker run –name slavermysql -d -p 3308:3306 -e MYSQL_ROOT_PASSWORD=123456 -v /opt/docker/mysql/slaver/data:/var/lib/mysql -v /opt/docker/mysql/slaver/conf/my.cnf:/etc/mysql/my.cnf  mysql:5.7

这里为了方便查看数据,把Docker的端口都与本机进行了映射,对应的本地master/data文件夹和slaver/data文件夹下也能看到同步的数据库文件

 

 

Master和Slaver设置;

//进入master容器

docker exec -it mastermysql bash

//启动mysql命令,刚在创建窗口时我们把密码设置为:anech

mysql -u root -p

//创建一个用户来同步数据

GRANT REPLICATION SLAVE ON *.* to 'backup'@'%' identified by '123456';

//这里表示创建一个slaver同步账号backup,允许访问的IP地址为%,%表示通配符

//例如:192.168.0.%表示192.168.0.0-192.168.0.255的slaver都可以用backup用户登陆到master上

//查看状态,记住File、Position的值,在Slaver中将用到

show master status;

 

 

slaver容器:

//进入slaver容器

docker exec -it slavermysql bash

//启动mysql命令,刚在创建窗口时我们把密码设置为:anech

mysql -u root -p

 

//设置主库链接

change master to master_host='172.17.0.2',master_user='backup',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=0,master_port=3306;

//启动从库同步

start slave;

 

//查看状态

show slave status\G;

 image.png

表示配置成功;

说明:

master_host:主库地址

master_user:主库创建的同步账号

master_password:主库创建的同步密码

master_log_file:主库产生的日志

master_log_pos:主库日志记录偏移量

master_port:主库使用的端口,默认为3306

 

测试主从是否成功,是否同步!

在master创建数据内容,看slave 是否同步过去,

create database order_demo;

use order_demo;

create table userinfo(username varchar(50),age int);

insert into userinfo values('Tom',18);

select * from userinfo;

 

 

 

 

java  读写分离操作:

 

https://www.cnblogs.com/fengwenzhee/p/7193218.html?utm_source=itdadao&utm_medium=referral

 


 

https://www.cnblogs.com/xiaoit/p/4599914.html

 

 

log4j2配置文件log4j2.xml详解

配置实例:

<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
 <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
 <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
 <configuration status="WARN" monitorInterval="30">
 <!—配置参数 -->    
<Properties>
         <Property name="log_dir">logs</Property>
         <Property name="PATTERN_LAYOUT">%d{yyyy-MM-dd HH:mm:ss} %-5level %class{36} %L %M - %msg%xEx%n</Property>
     </Properties>
 
     <Appenders>
 
         <!-- Console日志: 线上删除console, 把此段日志配置删除即可-->
         <Console name="STDOUT">
<!--输出日志的格式-->
             <PatternLayout pattern="${PATTERN_LAYOUT}"/>
         </Console>
 
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
        <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
 
         <!-- INFO 级别日志 -->
 
          <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
 <RollingFile name="INFO" fileName="${log_dir}/info/info.log"
                      filePattern="${log_dir}/info/info-%d{yyyyMMdd}-%i.log.gz">
             <PatternLayout pattern="${PATTERN_LAYOUT}"/>
             <Filters>
                 <!--如果是error级别拒绝-->
                 <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
                 <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                 <!--如果是 debug\info 输出-->
             </Filters>
             <Policies>
                 <TimeBasedTriggeringPolicy/>
                 <!--单个文件大小-->
                 <SizeBasedTriggeringPolicy size="500MB"/>
             </Policies>
             <!--保存日志文件个数-->
             <DefaultRolloverStrategy max="10"/>
         </RollingFile>
 
         <!--error级别日志输出-->
         <RollingFile name="ERROR" fileName="${log_dir}/info/error.log"
                      filePattern="${log_dir}/info/error-%d{yyyyMMdd}-%i.log.gz">
             <PatternLayout pattern="${PATTERN_LAYOUT}"/>
             <Filters>
                 <!--如果是 error 输出-->
                 <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
             </Filters>
             <Policies>
                 <TimeBasedTriggeringPolicy/>
                 <SizeBasedTriggeringPolicy size="500MB"/>
             </Policies>
             <DefaultRolloverStrategy max="10"/>
         </RollingFile>
 
     </Appenders>
 
     <Loggers>
 
         <!-- Console日志: 线上删除console, 把此段日志配置删除即可-->
         <Root level="debug">
             <AppenderRef ref="STDOUT"/>
         </Root>
 
         <AsyncLogger name="jws.event.rec" level="info"   additivity="false">
             <AppenderRef ref="eventRecRolling"/>
         </AsyncLogger>
 
 
     </Loggers>
 </Configuration>

(1).根节点Configuration有两个属性:status和monitorinterval,有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger).

       status 属性,这个属性表示log4j2本身的日志信息打印级别。如果把status改为TRACE再执行测试代码,可以看到控制台中打印了一些log4j加载插件、组装logger等调试信息。

       monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.

日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出。对于Loggers中level的定义同样适用。

上面配置了两种日志打印的方式,打印的等级是info。

 

(2).Appenders节点,常见的有三种子节点:Console、RollingFile、File.

 

Console节点用来定义输出到控制台的Appender.

        name:指定Appender的名字.

        target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.

        PatternLayout:输出格式,不设置默认为:%m%n.

 

File节点用来定义输出到指定位置的文件的Appender.

        name:指定Appender的名字.

        fileName:指定输出日志的目的文件带全路径的文件名.

        PatternLayout:输出格式,不设置默认为:%m%n.

 

 RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender.

        name:指定Appender的名字.

        fileName:指定输出日志的目的文件带全路径的文件名.

        PatternLayout:输出格式,不设置默认为:%m%n.

        filePattern:指定新建日志文件的名称格式.

        Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.

        TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am…而不是7am.

        SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.

        DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。

 

 (3).Loggers节点,常见的有两种:Root和Logger.

       Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出

         level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

         AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender.

       Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。

         level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

         name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.

         AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。

 

(4).关于日志level.

      共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.

      All:最低等级的,用于打开所有日志记录.

      Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出.

      Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.

      Info:消息在粗粒度级别上突出强调应用程序的运行过程.

      Warn:输出警告及warn以下级别的日志.

      Error:输出错误信息日志.

      Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.

      OFF:最高等级的,用于关闭所有日志记录.

      程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少

java 日志框架详解-干货

一、 日志的重要性

对于我们开发人员来说,日志记录往往不被重视。在生产环境中,日志是查找问题来源的重要依据。日志可记录程序运行时产生的错误信息、状态信息、调试信息和执行时间信息等多种多样的信息。可以在程序运行出现错误时,快速地定位潜在的问题源。目前常用的日志系统有java.util.logging、commons logging、slf4j、log4j1.x、logback、log4j2.x 等若干种。

二、 Java常用日志框架历史

 1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即log4j。后来log4j成为Apache基金会项目中的一员。

 期间log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入log4j到java的标准库中,但Sun拒绝了。

 2002年Java1.4发布,Sun推出了自己的日志库jul(java util logging),其实现基本模仿了log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。

 接着,Apache推出了jakarta commons logging,jcl只是定义了一套日志接口(其内部也提供一个simple log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你的应用代码里,只需调用commons logging的接口,底层实现可以是log4j,也可以是java util logging。

 后来(2006年),Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于commons logging)和logback(slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。

 现今,Java日志领域被划分为两大阵营:commons logging阵营和slf4j阵营。

 commons logging在Apache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出slf4j的发展趋势更好。如下图1所示。

 

图1

image.png

Apache眼看有被logback反超的势头,于2012年7月重写了log4j 1.x,成立了新的项目log4j2.x。log4j2在各个方面都与logback非常相似。

Java的logger世界

Commons logging
Apache的commons项目,一个很薄的logging抽象层,制定了使用log的相关接口和规范,可以由不同的logging implementations,[
http://commons.apache.org/proper/commons-logging/]

SLF4J
Simple Logging Facade for Java, 也是一个logging抽象层,底层logging框架可以是(e.g. java.util.logging, logback, log4j),是Commons logging的替代物, [
http://www.slf4j.org/]

jcl-over-slf4j
提供Commons-logging向slf4j迁移用的bridge, 可参考 [
http://www.slf4j.org/legacy.html]

slf4j-log4j
前面说了,SLF4J是个facade,log4j是其实现的一种框架,抽象成接口,具体的绿叶可以是Log4j/Log4j2/LockBack,当使用log4j时,需要此Jar包作为桥接,可参考 [
http://www.slf4j.org/legacy.html]

log4j
这个不用多说,大家使用最多的是1.x版本, [
http://logging.apache.org/log4j/1.2/], 不过好像2.x也出来了,据说采用了异步机制,性能有很大提升 [http://logging.apache.org/log4j/2.x/]

logback
原生实现了SLF4J API,所以不需要中间的bridge,号称是log4j终结者,下一代logging框架,性能比log4j有很大提升,推荐使用,况且现在我们已经在使用SLF4j,所以切换过去应该是很方便的事。[
http://logback.qos.ch/]

总的来说:slf4j与commons-logging只是一个日志门面,实际还是要依赖真正的日志库log4j,虽然slf4j和commons-loggins自带了日志库,但是毕竟log4j才是最强大的。

 

至于Logback是由log4j创始人设计的另一个开源日志组件,是用来取代log4j,

取代的理由自行百度;

https://blog.csdn.net/zbajie001/article/details/79596109

jul日志

 这个是SUN公司自带的日志输出框架,本来Log4j有建议过加入SUN JDK框架,但是SUN不要人家,后台就出了这个框架,但是比较XX,所以很少人使用;

@Test
 public void test() throws IOException {
     Logger logger = Logger.getLogger("");
     logger.info("Hola JDK!");
 }

这就是jul日志。

commons-logging日志

common logging本身不是log,你可以把它看做是一个日志的接口而log4j就是日志的实现,它自身只是实现了简单的日志实现类.

使用很简单:

commons-logging的使用非常简单。首先,需要在pom.xml文件中添加依赖:

<dependency>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
     <version>1.2</version>
 </dependency>

声明测试代码:

public class commons_loggingDemo {
    Log log= LogFactory.getLog(commons_loggingDemo.class);
    @Test
    public void test() throws IOException {
        log.debug("Debug info.");
        log.info("Info info");
        log.warn("Warn info");
        log.error("Error info");
        log.fatal("Fatal info");
    }
}

接下来,在classpath下定义配置文件:commons-logging.properties:

#指定日志对象:
 org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger
 #指定日志工厂:
 org.apache.commons.logging.LogFactory = org.apache.commons.logging.impl.LogFactoryImpl

如果只单纯的依赖了commons-logging,那么默认使用的日志对象就是Jdk14Logger,默认使用的日志工厂就是LogFactoryImpl

commons-logging + Log4j使用:

去掉commons-logging.properties 配置文件:

因为commons-logging

1) 首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则使用其中定义的Log实现类; 

2) 如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类; 

3) 否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类; 

4) 否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类); 
5)
否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog; 

需要在pom.xml文件中添加依赖:

<dependency>
    <
groupId>log4j</groupId>
    <
artifactId>log4j</artifactId>
    <
version>1.2.17</version>
</
dependency>

接下来,在classpath下定义配置文件:log4j.properties

# Logger root
 # \u6ce8\u610f\uff1a\u7ebf\u4e0a\u7cfb\u7edf\uff0c\u9700\u628aconsole\u5220\u9664
 log4j.rootLogger=INFO ,console
 
 log4j.logger.org.springframework=info,console
 
 # \u6253\u5370\u5230Console\u7684\u65e5\u5fd7\uff0c\u6ce8\u610f\uff1a\u7ebf\u4e0a\u7cfb\u7edf\u9700\u8981\u5c06\u8be5\u6bb5\u65e5\u5fd7\u914d\u7f6e\u5220\u9664
 #### First appender writes to console
 log4j.appender.console=org.apache.log4j.ConsoleAppender
 log4j.appender.console.layout=org.apache.log4j.PatternLayout
 log4j.appender.console.layout.ConversionPattern=%-4p,%t,%d{MM-dd HH:mm:ss.SSS},%c{2}.%M:%L - %m%n

Log4j日志框架

 log4j是Apache的一个开放源代码的项目,通过使用log4j,我们可以控制日志信息输送的目的地, 日志的输出格式, 日志信息的级别,可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

   如果在我们系统中单独使用log4j的话,我们只需要引入log4j的核心包就可以了

   <dependency>
    <
groupId>log4j</groupId>
    <
artifactId>log4j</artifactId>
    <
version>1.2.17</version>
</
dependency>
public class Log4jTest {
            private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Log4jTest.class);
           
            public static void main(String[] args) {
                       logger.info("hello word");
            }
}

在系统的src目录下添加依赖的配置文件:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

Log4j 的代码结构:

image.png

  Log4j启动过程:

1.找到对应的配置文件,虽然log4j支持多种配置文件,在平时使用中多数使用log4j.xml格式,该文件位置应该放在classpath下面,在log4j启动时候会去clasPath下面找寻log4j.xml。
2.解析xml,根据配置生成对应名字的logger以及绑定该logger应用的appender。

log4j日志输出关键类图

 image.png 

logger组件:logger的父类category完成基本所有的log功能,其中实现的接口appenderAttachable用于存储与该logger绑定的appender。logger主要用于管理日志level,确定是否需要打出日志,即调用appender。其中每个category中都会有自己父亲的引用,当additivity参数为true的时候(默认为true),则会在自己appender输出信息后,调用父category去输出日志。

appender组件:appender是具体message输出的组件,管理信息输出的位置和格式。
Log4j获取特定名字logger

org.apache.log4j.Logger log4j = or.apache.log4h.Logger.getLogger(Name.class)

logger内部的存储是由Hierarchy来完成,在启动过程中会初始化所有log4j.xml中定义的logger。如果logger中不存在该名字的logger则会新生成一个新的logger,由于该名字的logger不存在配置文件中,所以会根据名字规则寻找其父亲logger,如果找不到则会以rootLogger为父亲。所以如果没有配置该类名对应的logger则会调用其父亲logger来输出日志。

Log4j 的详细配置:

#Access log

log4j.appender.A=org.apache.log4j.DailyRollingFileAppender

log4j.additivity.A = false 

log4j.appender.A.File=${catalina.base}/logs/access.log

log4j.appender.A.layout=org.apache.log4j.PatternLayout

log4j.appender.A.layout.ConversionPattern=%-4p,%t,%d{MM-dd   HH:mm:ss.SSS},%c{2}.%M:%L – %m%n

log4j.appender.A.DatePattern='_' yyyy-MM-dd

log4j.appender.A.append=true

log4j.appender.A.ImmediateFlush=true

log4j.appender.A.Threshold = INFO

https://blog.csdn.net/earthchinagl/article/details/70256527

 

高级配置:

https://www.cnblogs.com/dengjiahai/p/4608946.html

https://www.cnblogs.com/leefreeman/p/3610459.html

 

使用坑:

  在高并发时候会有性能问题:

http://zl378837964.iteye.com/blog/2373591

解决的方式:

1.规避:

1)排查代码是否写入了大量日志,删除非必要日志

 2)简化日志序列,尽量不出现日志嵌套(日志B打印调用了日志D)

2.解决:

1)使用log4j异步写AsyncAppender

2)升级到log4j 2 –> 建议

3)使用logback替换log4j –> 建议

4)补丁:使用可重入锁替换synchronized 

slf4j日志

 一个新的日志框架横空出世了:slf4j , 这个框架由log4j的作者开发并且后台出了一个性能更加好的框架logback;

 

 哪有人会问?为什么还需要slf4j了???

 

   https://www.oschina.net/translate/why-use-sl4j-over-log4j-for-logging

  slf4j是一个日志统一的框架,主要是为了接入不同日志系统建立的统一包装层的框架。其他具体实现日志的系统只需要实现slf4j的一些特定规则则可以接入slf4j使用。

 image.png

上图中可以看出,slf4j对外提供了统一的api,这由slf4j-api.jar包提供。另外如果需要接入log4j,则需要在api和具体框架中加入一个适配层,实现slf4j和log4j接口的适配,这个由包slf4j-log4j12实现。

有几个包需要区别一下:

log4j-slf4j-impl 是 slf4j 和 logj4 2 的 Binding,而 slf4j-log4j12 是 slf4j 和 log4j 1.2 的 Binding,jcl-over-slf4j 是common-logggin 适配 slf4j。

log4j-over-slf4j 是把Log4j适配到slf4j,比如有些系统使用logback+slef4j,有一个第三方jar依赖于log4j打印日志,就需要这个包了

log4j-slf4j-impl 是用于log4j2与slf4j 的桥接用的;

slf4j + Log4j 使用过程:

private org.slf4j.Logger log= LoggerFactory.getLogger(SlfloggingDemo.class);

@Test
public void test() throws IOException {
   
log.debug("Debug info.");
   
log.info("Info info");
   
log.warn("Warn info");
   
log.error("Error info");
}
<dependency>
    <
groupId>log4j</groupId>
    <
artifactId>log4j</artifactId>
    <
version>1.2.17</version>
</
dependency>

<
dependency>
    <
groupId>org.slf4j</groupId>
    <
artifactId>slf4j-log4j12</artifactId>
    <
version>1.7.21</version>
</
dependency>

slf4j 底层实现方式:

1.slf4j的loggerfactory在getlogger()的过程中会检查slf4j-LoggerFactory是否初始化没有,其中performInitialization调用会去适配具体的日志框架。(在slf4j-api层)

 image.png 

2.slf4j-LoggerFactory未初始化,则会调用当前classloader去寻找“org/slf4j/impl/StaticLoggerBinder.class”类,并创建该类。所以需要适配slf4j的日志框架都需要实现该类。(在slf4j-api层)

 image.png

3. 调用对应的staticLoggerBinder会初始化log4j。并生成新的log4j12-log4jloggerFactory。该类在返回logger的时候会将log4j返回的logger在装饰一层即log4jLoggerAdapter,用于适配slf4j的logger接口。(在slf4j-log4j12)

image.png

注意:当有多个日志框架的时候,在找寻“org/slf4j/impl/StaticLoggerBinder.class”会出现多个,这时候加载具体那个StaticLoggerBinder.class则会由jvm来决定,从而加载了对应的日志框架。如果不想出现这个问题,则应该保证classPath下只有一个该类。

有些系统你残留了一些另外的日志框架比如apache commons-logging ( 简称 jcl)的接口, 则当引入这个二方包的时候,由于原本自己并不支持jcl接口,或者想将jcl接口最后输入的日志系统为log4j。则需要引入jcl的桥接工具 jcl-over-slf4j,该包功能代码很少,核心为将jcl的接口调用适配成slf4j接口的调用。这样即可让jcl接口的二方库和自己共用一个日志框架。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.13</version>
    <scope>runtime</scope>
</dependency>

总结:在使用日志系统中,通常在工作的系统上,主要即为log4j作为具体的日志系统的实现,然后将slf4j作为日志系统的抽象层,这样使得应用和具体的日志系统解耦。

LogBack日志

  LogBack和Log4j都是开源日记工具库,LogBack是Log4j的改良版本,比Log4j拥有更多的特性,同时也带来很大性能提升。LogBack官方建议配合Slf4j使用,这样可以灵活地替换底层日志框架。 为了优化log4j,以及更大性能的提升,Apache基金会已经着手开发了log4j 2.0

LogBack被分为3个组件,logback-core, logback-classic 和 logback-access。 
logback-core
:提供了LogBack的核心功能,是另外两个组件的基础。 
logback-classic
:实现了Slf4j的API,所以当想配合Slf4j使用时,需要将logback-classic加入classpath。 
logback-access
:是为了集成Servlet环境而准备的,可提供HTTP-access的日志接口。

需要在pom.xml文件中添加依赖:

<dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
  </dependency>
  <dependency>
        <groupId>org.logback-extensions</groupId>
        <artifactId>logback-ext-spring</artifactId>
        <version>0.1.2</version>
  </dependency>

接下来,在classpath下定义配置文件:logback.xml

<?xml version="1.0" encoding="UTF-8"?>
 
<!-- logback日志系统基础配置 -->
<!-- 1.此处debug="true"与logger level无关,只与配置的状态信息有关(如配置文件是否规范,某些标签元素属性是否赋值) -->
<!-- 3.此处scan="true"设置后,可以扫描本日志配置文件变动并重加载配置,可设置扫描间隔时间,默认为1分钟扫描一次,单位milliseconds, seconds, minutes 或 hours,如scanPeriod="30 seconds" -->
<!-- 4.此处packagingData="true"可以在日志后看到依赖jar包名和版本,很费性能,不建议开启 -->
 
<configuration debug="false" scan="true" scanPeriod="30 seconds" packagingData="false">
 
 
 
   
<!-- 设置 logger context 名称,一旦设置不可改变,默认为default -->
   
<contextName>sharding-jdbc-demo</contextName>
 
    <
property name="logDir" value="D:/eclipse-workspace/logs" />
 
 
   
<!--用于对控制台进行日志输出-->
   
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
       
<!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
       
<encoder>
            <
pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </
encoder>
    </
appender>
 
    <
appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <
file>${logDir}/info.log</file>
       
<!-- 过滤日志 -->
        <!-- 过滤掉非INFO级别 -->
       
<filter class="ch.qos.logback.classic.filter.LevelFilter">
            <
level>INFO</level>
            <
onMatch>ACCEPT</onMatch>  <!-- 如果命中就禁止这条日志 -->
           
<onMismatch>DENY</onMismatch> <!-- 如果没有命中就使用这条规则 -->
       
</filter>
 
        <
rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
           
<!-- rollover daily -->
           
<fileNamePattern>${logDir}/info-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
            <
maxHistory>5</maxHistory>
            <
timeBasedFileNamingAndTriggeringPolicy
                   
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
               
<!-- or whenever the file size reaches 100MB -->
               
<maxFileSize>10MB</maxFileSize>
            </
timeBasedFileNamingAndTriggeringPolicy>
        </
rollingPolicy>
        <
encoder>
            <
charset>UTF-8</charset>
            <
pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n</pattern>
        </
encoder>
    </
appender> 
 
    <
appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <
file>${logDir}/error.log</file>
       
<!-- 过滤日志 -->
       
<filter class="ch.qos.logback.classic.filter.LevelFilter">
           
<!-- 过滤掉非IERROR级别 -->
           
<level>ERROR</level>
            <
onMatch>ACCEPT</onMatch>  <!-- 如果命中就禁止这条日志 -->
           
<onMismatch>DENY</onMismatch> <!-- 如果没有命中就使用这条规则 -->
       
</filter>
 
        <
rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
           
<!-- rollover daily -->
           
<fileNamePattern>${logDir}/error-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
            <
maxHistory>5</maxHistory>
            <
timeBasedFileNamingAndTriggeringPolicy
                   
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
               
<!-- or whenever the file size reaches 100MB -->
               
<maxFileSize>10MB</maxFileSize>
            </
timeBasedFileNamingAndTriggeringPolicy>
        </
rollingPolicy>
        <
encoder>
            <
charset>UTF-8</charset>
            <
pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n</pattern>
        </
encoder>
    </
appender> 
 
 
    <
appender name="ASYNC_FILEINFO_LOG" class="ch.qos.logback.classic.AsyncAppender">
       
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
       
<discardingThreshold>0</discardingThreshold>
       
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
       
<queueSize>2048</queueSize>
       
<!-- 添加附加的appender,最多只能添加一个 -->
       
<appender-ref ref="fileInfoLog" />
    </
appender>
 
 
   
<!-- 日志级别若没显示定义,则继承最近的父logger(该logger需显示定义level,直到rootLogger)的日志级别-->
    <!-- logger的appender默认具有累加性(默认日志输出到当前logger的appender和所有祖先logger的appender中),可通过配置 “additivity”属性修改默认行为-->
   
<logger name="com.fulihui.sharding.jdbc" level="INFO" additivity="false">
        <
appender-ref ref="ASYNC_FILEINFO_LOG" />
    </
logger>
 
   
<!-- 至多只能配置一个root -->
   
<root level="INFO">
        <
appender-ref ref="STDOUT" />
       
<!--<appender-ref ref="fileInfoLog" />-->
       
<appender-ref ref="fileErrorLog" />
        <
appender-ref ref="ASYNC_FILEINFO_LOG" />
    </
root>
 
  </
configuration>

 

 

https://blog.csdn.net/zzzgd_666/article/details/80458444

 

logback 使用的坑:

 1.packagingData="false" 当此属性设置为true时,logback可以包含它输出的堆栈跟踪行的每一行的打包数据,很影响性能,建议线上不能开启

 2.配置中有个<discardingThreshold>0</discardingThreshold>这个很表示不丢日志,并且底层是通过

//class :  ch.qos.logback.core.AsyncAppenderBase 的 append方法
  BlockingQueue<E> blockingQueue;
  @Override
  protected void append(E eventObject) {
    if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) {
      return;
    }
    preprocess(eventObject);
    put(eventObject);
  }
  private void put(E eventObject) {
    try {
      blockingQueue.put(eventObject);
    } catch (InterruptedException e) {
    }
  }

从代码中可以看出来虽然是异步的,但是把日志塞进队列中用的是put方法.是会block的,直到队列空出位置来,所以在配置上可以把队列配置大一掉;建议还是升级采用异步方式

采用log4j2 所以就出现 log4j2 这个异步框架了。

Log4j 与 logback的性能对比:

https://my.oschina.net/OutOfMemory/blog/789267

Log4j2.0日志

  Log4j2.0基于LMAX Disruptor的异步日志在多线程环境下性能会远远优于Log4j 1.x和logback(官方数据是10倍以上)。我想日后logback 也会优化成异步模式的,具体看官方公告。。。。

 

  https://www.jianshu.com/p/570b406bddcd

 

  具体搭建:

  需要在pom.xml文件中添加依赖:

 

<!--log4j-2模式-->
 
<dependency>
    <
groupId>org.slf4j</groupId>
    <
artifactId>slf4j-api</artifactId>
    <
version>1.7.25</version>
  </
dependency>
 
 
<!--核心log4j2jar包-->
 
<dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-api</artifactId>
    <
version>2.11.1</version>
  </
dependency>
 
  <
dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-core</artifactId>
    <
version>2.11.1</version>
  </
dependency>
 
 
<!--用于与slf4j保持桥接-->
 
<dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-slf4j-impl</artifactId>
    <
version>2.11.1</version>
  </
dependency>
 
 
<!--需要使用log4j2的AsyncLogger需要包含disruptor-->
 
<dependency>
    <
groupId>com.lmax</groupId>
    <
artifactId>disruptor</artifactId>
    <
version>3.4.2</version>
  </
dependency>
 
 
<!--web工程需要包含log4j-web,非web工程不需要-->
 
<dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-web</artifactId>
    <
version>2.4.1</version>
    <
scope>runtime</scope>
  </
dependency>

 

 

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
    <Properties>
        <Property name="baseDir">D:\eclipse-workspace\logs</Property>
        <Property name="filename">D:\eclipse-workspace\logs/info.log</Property>
        <Property name="filenameError">D:\eclipse-workspace\logs/error.log</Property>
    </Properties>

    <Appenders>
        <Console name="STDOUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/>
        </Console>

        <RollingFile name="RollingFile" fileName="${filename}"
                     filePattern="${baseDir}/${date:yyyy-MM}/info-%d{yyyy-MM-dd-HH-mm}.log.gz">
            <PatternLayout pattern="%d %-5level [%t]%l - %msg%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="200 MB"/>
                <TimeBasedTriggeringPolicy interval="10" modulate="true"/>
            </Policies>

            <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>

            <!--自动删除超过120天的日志压缩文件-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}" maxDepth="2">
                    <IfFileName glob="*/info-*.log.gz"/>
                    <IfLastModified age="20d"/>
                </Delete>
            </DefaultRolloverStrategy>

        </RollingFile>

        <!--错误日志入文件-->
        <RollingFile name="RollingFileError" fileName="${filenameError}"
                     filePattern="${baseDir}/${date:yyyy-MM}/error-%d{yyyy-MM-dd-HH}.log">
            <PatternLayout pattern="%d %-5level [%t]%l - %msg%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="200 MB"/>
                <TimeBasedTriggeringPolicy interval="24" modulate="true"/>
            </Policies>

            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>

            <!--自动删除超过120天的日志压缩文件-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}" maxDepth="2">
                    <IfFileName glob="*/error-*.log"/>
                    <IfLastModified age="30d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>


    </Appenders>

    <Loggers>

        <!--采用异步输出日志-->
        <AsyncLogger name="com.fulihui.sharding.jdbc" level="debug"   additivity="false">
            <!--写入info级别-->
            <AppenderRef ref="RollingFile" />
            <!--写入error级别-->
            <AppenderRef ref="RollingFileError" level="error"/>

            <AppenderRef ref="STDOUT"/>

        </AsyncLogger>

        <!--采用异步输出日志-->
        <AsyncRoot level="debug">
            <AppenderRef ref="STDOUT"/>
        </AsyncRoot>

    </Loggers>
</Configuration>


配置详情:

    https://blog.csdn.net/u013269532/article/details/53186526

https://blog.csdn.net/scherrer/article/details/73744392


logback log4j log4j2 性能实测

 https://blog.souche.com/logback-log4j-log4j2shi-ce/ 

快捷键 Eclipse VS Idea

分类

功能点

Eclipse快捷键

IDEA快捷键

搜索

搜索文本

Ctrl + F

Ctrl + F

Ctrl + R 查找替换

Alt + P/A 逐个/全部替换

Alt + F3 查找当前选中词

继续搜索

Ctrl + K 向前

Ctrl + Shift + K 向后

F3

Shift + F3

搜索方法

Ctrl + O

Ctrl + F12

显示类的所有防范 Alt + 7 

搜索类

Ctrl + Shift + T

Ctrl + N

搜索所有文件 Ctrl + Shift + R

搜索文件

Ctrl + Shift + T

Ctrl + Shift + N

这两个都支持简单的正则表达式,还支持直接按大写字母的缩略,例如:

查找JsonTranscoder,只需要输入JT

搜索所有引用处

Ctrl + Alt + H

Alt + F7

搜索所有文本出现的位置

Ctrl + H

Ctrl + Shift + F

编辑

自动代码补全

Alt + /

Ctrl + J

自动代码生成

Alt + Insert

快速修复错误

Ctrl + 1

Alt + Enter

删除当前行

Ctrl + D

Ctrl + X

复制到下一行

Ctrl + D

注释/取消注释

Ctrl + /

Ctrl + /

选中当前字

Ctrl + W

 

补全当前行

Ctrl + Shift + Enter

神器,补全当前行,最常用的场景时补全当前行后的;号,并将光标定位到下一行

调出最近复制的N份内容

Ctrl + Shift + V

查看最近编辑的文件

Ctrl + E

对比最近修改

Alt + Shift + C

格式化代码

Ctrl + Shift + F

Ctrl + Alt + L

整理import

Ctrl + Shift + O

Ctrl + Alt + O

跳转

显示方法层次

Ctrl + Shift + H

显示类、方法说明

F2

Ctrl + Q

跳到方法定义处

Ctrl + B

跳到方法实现处

Ctrl + Alt + B

跳到上/下一方法

Alt + Up/Down

上/下一查看处

Alt + <-

Alt + ->

Ctrl + Alt + Up/Down

跳到指定行

Ctrl + L

Ctrl + G

重构

改名

Alt + Shift + R

Shift + F6

其他常用

Ctrl + F6 修改方法签名

Ctrl + Shift + F6 修改参数的类型

Ctrl + Shift + V引入局部变量

Ctrl + Shift + P 引入参数

Ctrl + Shift + F 引入类变量

Ctrl + Shift + M 引入方法

Ctrl + Shift + C 引入常量

运行

启动调试

Alt + Shift + F9

启动运行

Alt + Shift + F10

单步进入

F5

F7

单步跳过

F6

F8

跳过

F8

F9

执行选中语句

Alt + F8

窗口

调出界面

Ctrl + Alt + S调出Settings界面

Ctrl + Alt + Shift + S调出项目Setting界面

关闭界面

Ctrl + F4 或 ESC

打开窗口

Alt + 窗口编号(例如项目窗口编号是1)

最大化窗口

Ctrl + M

Ctrl + Shift + F12

隐藏窗口

Shift + ESC

关闭当前文件

Ctrl + F4

垂直分屏

Ctrl + | (自定义的)

调整窗口位置

Ctrl + M 将当前光标处显示到屏幕中央

切换窗口

Ctrl + Tab

使用Nginx+Lua实现Web项目的灰度发布

需求:

领导对时间要求紧迫、研发对现有系统摸不透、做到数据的兼容性,基于这样的要求就必须做到系统上线采用灰度的方式,指定忠实用户进行线上测试、选取有特征的群体进行线上测试和基于流量切换的方式进行线上测试等。

常规的部署做法:

常规的部署方式是采用Nginx的 upstream  配置来简单的实现新旧机器的切换, 在开发过程中,开发完成,完成测试阶段,修复bug后都要重启后台服务,测试又在测试,每次重启都要一两分钟,平凡的重启,测试不干了;所以想到就是部署两台服务器;用nginx upstream 模块实现 无感知部署,发现一个bug,修复;直接部署不会打断测试;常用的是一台部署完毕之后部署另外一台机器; 

灰度发布概述:

灰度发布,简单来说,就是根据各种条件,让一部分用户使用旧版本,另一部分用户使用新版本。

灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面 来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

灰度部署还可以根据设定的规则将请求路由到我们的灰度版本(灰度机器)上来。比如对于API来说,一般有如下几个需求:特定用户(比如测试帐号)、 特定的App(比如测试app或者合作App)、特定的模块、接口(只有某些接口需要灰度,这种一般是API Container的修改,拿一些不是很重要的API做灰度测试)、特定的机器(某些请求IP转发到灰度机)等。

本章只是简单的简述灰度部署的实现思路:

这里我们所做的灰度发布稍作改变:用1-2台机器作为B,B测试成功再部署A。用于WEB系统新代码的测试发布,让一部分(IP)用户访问新版本,一部分用户仍然访问正常版本,原理如图:

 image.png


执行过程:
1
、当用户请求到达前端web(代理)服务器Openresty,内嵌的lua模块解析Nginx配置文件中的lua脚本代码;
2
、Lua获取客户端IP地址,去查询Redis中是否有该键值,如果有返回值执行@clien2,否则执行@client1。
3
、Location @client2把请求转发给预发布服务器,location @client1把请求转发给生产服务器,服务器返回结果,整个过程完成。

Lua 的好处并不至于这个,可以使用LUA语言是实现一些业务上的负载,比如热点分离; 热点数据的自动降级机制;

  Lua教程: https://www.runoob.com/lua/lua-tutorial.html


案例实现:

1.       安装部署OpenResty:

OpenResty由Nginx核心加很多第三方模块组成,默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。
     借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序。
     而且OpenResty提供了大量组件如Mysql、Redis、Memcached等等,使在Nginx上开发Web应用更方便更简单。

1、部署第一个nginx,作为应用层nginx(192.168.1.104那个机器上)

1、  创建目录/usr/servers,以后我们把所有软件安装在此目录

mkdir -p /usr/servers 

cd /usr/servers/

2、  安装依赖(我的环境是centos,可以使用如下命令安装,其他的可以参考openresty安装步骤)

   yum install -y readline-devel pcre-devel openssl-devel gcc

 3、  下载ngx_openresty-xxx.tar.gz并解压(ngx_openresty-xxx/bundle目录里存nginx核心和很多第三方模块,比如有我们需要的Lua和LuaJIT。)

wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz

tar xvf ngx_openresty-1.9.7.1.tar.gz

 cd ngx_openresty-1.9.7.1

2. 安装LuaJIT

cd bundle/LuaJIT-2.1-20151219/

make clean && make && make install

 ln -sf luajit-2.1.0-alpha /usr/local/bin/luajit

 下载ngx_cache_purge模块,该模块用于清理nginx缓存

root@user:/usr/servers/ngx_openresty-1.9.7.1/bundle# wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz
root@user:/usr/servers/ngx_openresty-1.9.7.1/bundle# tar -xvf 2.3.tar.gz

下载nginx_upstream_check_module模块,该模块用于ustream健康检查

             root@user:/usr/servers/ngx_openresty-1.9.7.1/bundle# 

                   wget https://github.com/yaoweibin/nginx_upstream_check_module/archive/v0.3.0.tar.gz

root@user:/usr/servers/ngx_openresty-1.9.7.1/bundle# tar -xvf v0.3.0.tar.gz


  安装ngx_openresty

root@user:/usr/servers/ngx_openresty-1.9.7.1/bundle# cd .. 
root@user:/usr/servers/ngx_openresty-1.9.7.1# ./configure –prefix=/usr/servers –with-http_realip_module –with-pcre –with-luajit –add-module=./bundle/ngx_cache_purge-2.3/ –add-module=./bundle/nginx_upstream_check_module-0.3.0/ -j2 
root@user:/usr/servers/ngx_openresty-1.9.7.1# make && make install

参数说明:
–with***
安装一些内置/集成的模块
–with-http_realip_module
取用户真实ip模块
-with-pcre Perl
兼容的达式模块
–with-luajit
集成luajit模块
–add-module
添加自定义的第三方模块,如此次的ngx_che_purge

  到/usr/servers目录下用ll命令查看,会发现多出来了如下目录,说明安装成功

root@user:/usr/servers/ngx_openresty-1.9.7.1# cd /usr/servers/ 
root@user:/usr/servers# ll

image.png

说明:
/usr/servers/luajit
:luajit环境,luajit类似于java的jit,即即时编译,lua是一种解释语言,通过luajit可以即时编译lua代码到机器代码,得到很好的性能;
/usr/servers/lualib
:要使用的lua库,里边提供了一些默认的lua库,如redis,json库等,也可以把一些自己开发的或第三方的放在这;
/usr/servers/nginx
:安装的nginx,通过/usr/servers/nginx/sbin/nginx -V 查看nginx版本和安装的模块

启动nginx

root@user:/usr/servers# /usr/servers/nginx/sbin/nginx
检测配置是否正确(需要先切换到root用户):
root@user:/usr/servers# /usr/servers/nginx/sbin/nginx -t
重启nginx:
root@user:/usr/servers# /usr/servers/nginx/sbin/nginx -s reload


LUA环境测试:

1、              为了方便开发我们在/usr/servers/nginx/conf目录下创建一个lua.conf 
root@user:/home/user# cd /usr/servers/nginx/conf
root@user:/usr/servers/nginx/conf# vim lua.conf

server  {

       listen 80;

       server_name _;

       #HelloWorld

       location /lua {

              default_type 'text/html';

              content_by_lua 'ngx.say("hello world")';

       }

}

编辑nginx.conf配置文件 

vim /usr/servers/nginx/conf/nginx.conf 
在http部分添加如下配置 
lua_package_path "/usr/servers/lualib/?.lua;;"; #lua
模块 
lua_package_cpath "/usr/servers/lualib/?.so;;"; #c
模块 
include lua.conf; #
单独lua配置

重启nginx

  /usr/servers/nginx/sbin/nginx -s reload

 访问如http://192.168.1.104/lua(自己的机器根据实际情况换ip),可以看到如下内容 

hello world
说明配置成功。

灰度部署测试:

采用redis 方式;比如 192.168.0.101这个IP采用的是访问服务器的项目资源,其他IP是访问旧项目的资源;进行测试完毕并且完成之后,可以切换正式环境;

image.png

默认情况下lua_code_cache  是开启的,即缓存lua代码,即每次lua代码变更必须reload nginx才生效,如果在开发阶段可以通过lua_code_cache  off;关闭缓存,这样调试时每次修改lua代码不需要reload nginx;但是正式环境一定记得开启缓存

开启后reload nginx会看到如下报警
nginx: [alert] lua_code_cache is off; this will hurt performance ******;


配置文件:

Nginx.conf:

#user  nobody;
worker_processes  1;
 
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
 
#pid        logs/nginx.pid;
 
 
events {
    worker_connections  1024;
}
 
 
http {
    include       mime.types;
    default_type  application/octet-stream;
       
       lua_package_path "/usr/servers/lualib/?.lua;;"; #lua 模块 
       lua_package_cpath "/usr/servers/lualib/?.so;;"; #c模块 
       
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  logs/access.log  main;
 
    sendfile        on;
    #tcp_nopush     on;
 
    #keepalive_timeout  0;
    keepalive_timeout  65;
 
    #gzip  on;
       
       proxy_buffering             off;
    proxy_set_header            Host $host;
    proxy_set_header            X-real-ip $remote_addr;
    proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
       
       
       upstream productServer {
         server 127.0.0.1:8080 weight=1 max_fails=3 fail_timeout=100s; #模拟生产服务器
       }
 
       upstream preServer {
         server 127.0.0.1:8081 weight=1 max_fails=3 fail_timeout=100s;  #模拟预发布服务器
       }
 
 
    server {
        listen       80;
        server_name  localhost;
 
        #charset koi8-r;
 
        #access_log  logs/host.access.log  main;
 
        location / {
                default_type 'text/html';  
          lua_code_cache off;  
          content_by_lua_file /usr/servers/nginx/conf/huidu.lua;
        }
 
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
 
              location @productServer{
                proxy_pass http://productServer;
        }
              
        location @preServer{
                proxy_pass http://preServer;
        }
    
    }
 
 
 
}

huidu.lua:

local redis = require "resty.redis" 
local cache = redis.new() 
cache:set_timeout(60000)
 
local ok, err = cache.connect(cache, '127.0.0.1', 6379) 
if not ok then 
    ngx.say("failed to connect:", err) 
    return 
end 
 
--local red, err = cache:auth("foobared")
--if not red then
    --ngx.say("failed to authenticate: ", err)
    --return
--end
 
local local_ip = ngx.req.get_headers()["X-Real-IP"]
if local_ip == nil then
    local_ip = ngx.req.get_headers()["x_forwarded_for"]
end
 
if local_ip == nil then
    local_ip = ngx.var.remote_addr
end
--ngx.say("local_ip is : ", local_ip)
 
local intercept = cache:get(local_ip) 
 
 
if intercept == local_ip then
    ngx.exec("@preServer")
    return
end
 
ngx.exec("@productServer")
 
local ok, err = cache:close() 
 
if not ok then 
    ngx.say("failed to close:", err) 
    return 
end

上面的代码是简单的IP相等,可以采用IP段形式;


上面的例子测试:

image.png

image.png

image.png

额外参考资料:

https://my.oschina.net/izhangll/blog/884713

Redis Sentinel 容灾演练

本文主要介绍基于redis高可用的集群搭建,并做相应的高可用的容灾演练过程,熟悉一下redis高可用的方案配置;

简单的redis 系列教程可以查看之前的文章 http://www.dczou.com/viemall/tag/redis

首先的知道redis Sentinel 主从原理机制:

image.pngimage.png

image.pngimage.png


安装搭建Sentinel 集群:

分别有3个Sentinel节点,1个主节点,1个从节点组成一个简单的高可用方案;

role

IP

port

master

192.168.1.104

6379

slave

192.168.1.105

6379

Sentinel1

192.168.1.104

5000

Sentinel2

192.168.1.105

5000

Sentinel3

192.168.1.106

5000

安装redis 和部署redis 的主从方式,本章不阐述

配置sentinel.conf

哨兵默认用26379端口,默认不能跟其他机器在指定端口连通,只能在本地访问

192.168.1.104机器实例:

将sentinel.conf 复制 /usr/local/redis/etc

cp /opt/program/tools/redis-4.0.10/sentinel.conf  /usr/local/redis/etc

配置如下:

port 5000

bind 192.168.1.104

dir /tmp

sentinel monitor mymaster 192.168.1.104 6379 2

sentinel down-after-milliseconds mymaster 30000

sentinel failover-timeout mymaster 60000

sentinel parallel-syncs mymaster 1

daemonize yes

logfile /usr/local/redis/logs/sentinel_5000_log.log

192.168.1.105机器实例:

port 5000
bind 192.168.1.105
dir /tmp
sentinel monitor mymaster 192.168.1.104 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
daemonize yes
logfile /usr/local/redis/logs/sentinel_5000_log.log

192.168.1.106机器实例:

port 5000
bind 192.168.1.106
dir /tmp
sentinel monitor mymaster 192.168.1.104 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
daemonize yes
logfile /usr/local/redis/logs/sentinel_5000_log.log

Sentinel 配置文件解析:

http://www.mamicode.com/info-detail-1898706.html

检查设置:

启动哨兵进程

在192.168.1.104、192.168.1.105、192.168.1.106三台机器上,分别启动三个哨兵进程,组成一个集群,观察一下日志的输出

启动哨兵:

   方式一:redis-sentinel /path/to/sentinel.conf(推荐,这种方式启动和redis实例没有任何关系)

  方式二:redis-server /path/to/sentinel.conf –sentinel

  image.png

  日志里会显示出来,每个哨兵都能去监控到对应的redis master,并能够自动发现对应的slave

  哨兵之间,互相会自动进行发现,用的就是之前说的pub/sub,消息发布和订阅channel消息系统和机制


检查哨兵状态

redis-cli -h 192.168.1.104 -p 5000

sentinel master mymaster

SENTINEL slaves mymaster

SENTINEL sentinels mymaster

SENTINEL get-master-addr-by-name mymaster


检查各个redis状态查看

104、105主从状态查看命令

image.png

image.png


演练步骤:

哨兵节点的增加和删除:

    增加sentinal,会自动发现

   删除sentinal的步骤:

    (1)停止sentinal进程

    (2)SENTINEL RESET *,在所有sentinal上执行,清理所有的master状态

    (3)SENTINEL MASTER mymaster,在所有sentinal上执行,查看所有sentinal对数量是否达成了一致


slave的永久下线

   让master摘除某个已经下线的slave:SENTINEL RESET mastername,在所有的哨兵上面执行

    slave-priority,值越小优先级越高

容灾演练:

  master redis主节点宕机,看是否slave会选举成新的master节点;

  通过哨兵看一下当前的master:SENTINEL get-master-addr-by-name mymaster

  image.png

  image.png

   已经切换成功了;

    可以看到log信息

    image.png

    查看sentinal的日志,是否出现+sdown字样,识别出了master的宕机问题; 然后出现+odown字样,就是指定的quorum哨兵数量,都认为master宕机了

故障恢复

再将旧的master重新启动,查看是否被哨兵自动切换成slave节点

image.png

经过以上步骤,基本的sentinel下的高可用一主一从redis高可用就配置完成了。

java测试代码:

public static void main(String[] args) {

        Set<String> sentinels = new HashSet<String>();

        sentinels.add(new HostAndPort("192.168.1.104", 500).toString());

        sentinels.add(new HostAndPort("192.168.1.105", 500).toString());

        sentinels.add(new HostAndPort("192.168.1.106", 500).toString());

        JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels);

        System.out.println("Current master: " + sentinelPool.getCurrentHostMaster().toString());

    }


构建java环境的自动化构建和部署系列四

 所有系列文章:

  <<构建java环境的自动化构建和部署系列一>>

  <<构建java环境的自动化构建和部署系列二>>

 <<构建java环境的自动化构建和部署系列三>>

 <<构建java环境的自动化构建和部署系列四>>

前面都介绍了整套的自动化部署环境的搭建,本章是介绍怎么基于docker快速启动一个jenkins容器,实现docker 的环境部署;

本章将基于 https://gitee.com/gz-tony/viemall-dubbo.git 开源代码来演练自动化部署的步骤;

通过docker file构建 jenkins镜像

#cd  /opt/program/tools/docker-file/jenkins

# docker build -t www.viemall.cn/viemall/jenkins:v1 .

这里构建镜像成功后,把镜像文件push公司的harbor镜像仓库中;

       #docker  push  www.viemall.cn/viemall/jenkins:v1

    docker-fil文件:

        https://gitee.com/gz-tony/viemall-dubbo/tree/master/viemall-docekr/dockerfile

启动jenkins镜像;

docker run -d –name jenkins -p 8080:8080 –restart=always –privileged=true \

-v /var/jenkins_home/:/var/jenkins_home  \

-v /usr/java/jdk1.8.0_131/:/usr/local/jdk \

-v /usr/local/apache-maven-3.3.9/:/usr/local/maven \

-v /var/run/docker.sock:/var/run/docker.sock \

-v $(which docker):/usr/bin/docker \

-v ~/.ssh:/root/.ssh \

-e JAVA_OPTS=-Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai

www.viemall.cn/viemall/jenkins:v1

启动成功,访问:

 配置跟 <<构建java环境的自动化构建和部署系列三>> 基本一致;

唯一注意的配置的路径是配置您的docker mavean 路径

image.png
这里的mavean 就是配置您的docker mavean 路径

2. 构建Job任务:

 构建viemall-web服务:

 新建JOB

image.png

image.png

image.png

   image.png

   image.png

Source files(源目录文件)

viemall-service-web/target/viemall-service-web-0.0.1-SNAPSHOT.war

Remove prefix(删除的目录文件)

viemall-service-web/target

Remote directory(远程目录)

/opt/program/servers

 

Exec command(远程命令)

#!/bin/sh

echo '================开始本机执行 docker 部署任务================'

#相当于在构建完成后自动执行此脚本,这个脚本实现自动化发布工作

mv /opt/program/servers/viemall-service-web-0.0.1-SNAPSHOT.war /opt/program/servers/viemall-service-web.war

docker rm -f viemall-service-web-tomcat || true

docker run -d -p 8081:8080 –restart=always -v /opt/program/servers/viemall-service-web.war:/usr/local/tomcat/webapps/ROOT.war –name viemall-service-web-tomcat tomcat8:v1

echo '================结束 docker 部署任务================'

构建java环境的自动化构建和部署系列三

 所有系列文章:

  <<构建java环境的自动化构建和部署系列一>>

  <<构建java环境的自动化构建和部署系列二>>

 <<构建java环境的自动化构建和部署系列三>>

 <<构建java环境的自动化构建和部署系列四>>

前面已经讲述了SVN、mavean 、nexus的安装;本章将继续介绍jenkins安装与使用

一、环境 2

二、准备工作 2

三、Jenkins环境部署: 2

四、Jenkins系统配置 3

五、构建邮件配置 3

六. 配置SSH服务器 7

七、 Jenkins Job 9

为了方便运维人员的部署,采用Jenkins实现多机远程自动化部署方案;

自动化部署原理图示:

image.png

 Jenkins 是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变得可能。现在软件开发追求的是效率以及质量,Jenkins使得自动化成为可能!

亮点

  采用shell自定义脚本,控制集成部署环境更加方便灵活

  精简war包中的lib包,常驻tomcat里,减少war包传输时间

  细粒度的用户权限管理,

  构建失败发邮件通知相关人员解决

  自动按天备份war包,Jenkins配置备份以及版本控制化

  可扩展性的插件管理

一、环境

  Linux(centos 7)、JDK8

二、准备工作

  SVN 版本控制服务器

  Tomcat发布服务器

  Jenkins服务器(提前安装好Maven,SVN,Jdk)

  Nexus 服务器

实验时可以在同一台机器配置,但是生产不建议,一台机器挂了,所有服务器都挂了。

机器

IP

Jenkins

192.168.0.144

SVN

192.168.1.110

Tomcat

192.168.0.146

Nexus

http://192.168.0.110:6161/nexus/

三、Jenkins环境部署:

1、wget https://pkg.jenkins.io/redhat-stable/jenkins-2.32.2-1.1.noarch.rpm

       rpm -ivh jenkins-2.32.2-1.1.noarch.rpm

安装完成之后: 

    /usr/lib/jenkins/jenkins.war    WAR包 

    /etc/sysconfig/jenkins       配置文件 

    /var/lib/jenkins/        默认的JENKINS_HOME目录 

    /var/log/jenkins/jenkins.log    Jenkins日志文件 

Jenkins 命令:

   service jenkins start –启动

   service jenkins restart –重启

   service jenkins stop –停止

2、配置Jenkins 的目录空间及端口

    vi /etc/sysconfig/jenkins

    JENKINS_HOME="/data/jenkins"

    JENKINS_PORT="6161"

3、访问链接: http://192.168.0.144:6161/

    第一次访问会初始化安装;

    在配置的时候需要注意用户权限问题:

    https://www.cnblogs.com/melody-emma/p/6026622.html


4、Jenkins 插件安装:

    Jenkins以其拥有大量的插件而著称,接下来需要安装常用的插件,本文需要确保已经安装了如下插件:

    Email Extension Plugin (邮件通知)

   Deploy to container Plugin (部署到Java容器中的插件)

   Publish Over SSH (远程Shell)

   Maven Integration plugin (Maven集成,否则新建的时候没有Maven Project)

   Monitoring (监控Jenkins所消耗的系统资源,如内存使用等)

    Docker Commons (运行docker 命令)

    MultiJob plugin(多项目构建的插件)


四、Jenkins系统配置

  1、配置JDK、Git、Maven、Ant:系统管理–Global Tool Configuration

image.png

 image.png

五、构建邮件配置

     1. 配置邮件服务器及邮件模板

     首先保证已经成功安装了Email Extension Plugin插件,然后依次点击左边的“Manage Jenkins” – “Configure System”,首先在Jenkins Location项目下面设置系统管理员的邮件地  址,这里填写的邮箱地址必须跟下面设置的发件人邮箱一致,否则不能成功发送邮件。

image.png

接下来在 Extended E-mail Notification 项下面设置SMTP服务器和邮件模板,这里推荐使用QQ邮箱:

image.png

image.png

Default Recipients填写默认的接收人邮箱地址,多个用英文逗号(,)进行分割,邮件模板可以使用下面参考资料里面提供的模板。

Default Content Type内容:

image.png

构建通知:$PROJECT_NAME – Build # $BUILD_NUMBER – $BUILD_STATUS!

Default Subject内容:

资料参考:

   http://blog.csdn.net/fullbug/article/details/53024562

   http://blog.csdn.net/songjiaping/article/details/51496977


. 配置SSH服务器

首先保证已经安装了Publish Over SSH插件,然后依次点击左边的“Manage Jenkins” – “Configure System”,在Publish over SSH项下面设置登录到远程服务器的配置信息,可以使用用户名/密码的方式,也可以使用证书的方式,这里建议使用后者。

 

1、首先配置免SSH免密码登录:

  确认用户

  确认当前用户是你需要的用户!

 $ whoami

  root   #root用户,根据你自己的需求选择用户。我这儿用root用户演示。不建议使用root

 生成key

 $ cd ~ #回到用户目录,不回去也没有关系

 $ ssh-keygen    

  #可以使用-t选项选择加密方式,包括 RSA 和 DSA 两种密钥

  #例如:$ssh-keygen -t dsa 或者ssh-keygen -t rsa

  #加密方式不同,key的名称不同,其他没有区别

  #如果没有指定密钥,默认RSA

 Generating public/private rsa key pair.

 Enter file in which to save the key (/home/froad/.ssh/id_rsa): #私钥存放的位置,默认会存放在用户目录的.ssh文件夹,直接回车

 Enter passphrase (empty for no passphrase): #默认,回车

 Enter same passphrase again: #默认,回车

 Your identification has been saved in /home/froad/.ssh/id_rsa.#私钥路径

 Your public key has been saved in /home/froad/.ssh/id_rsa.pub.#公钥路径

 The key fingerprint is:

  e8:b6:e6:xxxxxxxxxxxxxxxxx:ec:b5:d8 root@192.168.0.144;

 将本地公钥上传到远程主机:

 scp ~/.ssh/id_rsa.pub tomcatuser1@192.168.0.146:.ssh/id_rsa.pub 在远程主机(tomcat所在主机,如192.168.0.146):1,生成密钥文件:   touch /home/tomcatuser1/.ssh/authorized_keys

6.再转到第二台机器的.ssh目录下,会发现刚刚传输过来的文件-authorized_keys,然后执行命令,将第二台计算机的公钥也加进来,如:cat id-     rsa.pub >> authorized_keys.

2,修改权限:  chmod 600 ~/.ssh/authorized_keys

3,将控制主机上传的公钥复制:  cat /home/tomcatuser1/.ssh/id_rsa.pub  >> /home/tomcatuser1/.ssh/authorized_keys   将authorized_keys文件拷贝到需要被管理的电脑上。注意:放在用户目录下.ssh文件夹中。Linux用户会限制你的访问权限

4.验证

  ssh tomcatuser1@192.168.0.146

5、其他

   如果添加指纹的时候提示添加失败,是因为你以前添加过了这个ip的指纹。

   解决办法:将.ssh目录的known_hosts文件删除掉(好粗暴啊( ⊙ o ⊙ )啊!),也可以打开这个文件把对于ip的那条记录删除(这个就精细多了O(∩_∩)O哈哈~)

   如果操作步骤都正确,但是依然要求输入密码。一般是因为权限的问题。命令如下

   chmod 755 ~/.ssh/

   chmod 600 ~/.ssh/id_rsa  ~/.ssh/id_rsa.pub

   chmod 644 ~/.ssh/authorized_keys

二、 接下来就可以在Publish over SSH项设置Passphrase和Key,分别是私钥的密码和id_rsa文件的内容,点击下面的Add按钮增加SSH Servers,如果不使用上面公共的密钥设置或者使用用户名密码可以将 Use password authenticatio, or use a different key 选项勾选。

image.png

      设置完成之后可以点击下面的Test Configuration按钮,如果无误的话会出现Success的提示。

      PS:如果对远程服务器进行了安全设置,需要将 /etc/ssh/sshd_config 文件中的 PermitRootLogin 项目设置为 without-password,这样root账号不能用密码直接远程登录,但可以使用密钥对进行登录。

      构建完上述的配置后,就可以构建JOB服务了,提前说明一下,Publish over SSH是基于插件的形式进行远程登录,您可以不用这种方式,可以就简单的采用SHELL方式进行免登陆。

3. 构建完成之后使用SSH发布到演示服务器上

  在项目的配置项中的Post Steps项中选中 Send files or execute commands over SSH:

   image.png

接下来就可以设置执行的任务内容了:

   image.png

七、Jenkins Job

   方式一:基于SSH插件

直接点击Jenkins首页的新建按钮,创建一个Maven项目,配置根据大家需要,实际配置,供参考:

   image.png

接下来就可以设置执行的任务内容了:

  image.png

八、自动化部署docker的脚本

#!/bin/bash
#Time
app_name="viemall-web"
deploy_war="$app_name.war"
deploy_path="/opt/program/servers/$app_name"
deploy_path_war="$deploy_path/$deploy_war"
if [ ! -f $deploy_path_war ]; then
    echo "!WARN: No build jar file found in $deploy_path_war."
    echo "!WARN: Deployment is skipped."
    exit 0
fi
#备份log日志
echo "*************备份log日志****************"
if [ ! -d "$deploy_path/logs" ];then
mkdir -p $deploy_path/logs
else
   mv $deploy_path/logs $deploy_path/logs-$(date '+%m-%d')
   rm -rf $deploy_path/logs/*
fi
echo "************* 启动docker镜像 ****************"
docker rm -f $app_name || true
docker run -d -p 8080:8080 --restart=always -e TZ=Asia/Shanghai  -v $deploy_path_war:/usr/local/tomcat/webapps/ROOT.war -v $deploy_path/logs:/usr/local/tomcat/logs --name $app_name harbor.dczou.com/benniu/tomcat8-session-redis-test:v1
echo "************* 启动docker镜像成功 ****************"

构建java环境的自动化构建和部署系列二

 所有系列文章:

  <<构建java环境的自动化构建和部署系列一>>

  <<构建java环境的自动化构建和部署系列二>>

 <<构建java环境的自动化构建和部署系列三>>

 <<构建java环境的自动化构建和部署系列四>>

  

 前段为了方便公司管理项目的版本控制,和方便开发人员快速的部署项目(公司么有运维人员),搭建了搭建敏捷高效的持续集成管理平台,并且规范了安装部署的规范统一通过Shell来实现一键安装环境.(不过也要淘汰了,BOSS想通过Docker 来实现系统环境的部署)

持续集成管理平台的组成

    持续集成管理平台不只是CI服务器,是一系列软件开发管理工具的组合

1、源码版本管理:Subversion、 Git

2、项目构建工具:Maven、 Ant

3、代码质量管理:Sonar(Checkstyle、 PMD、 FindBugs……)

4、持续集成引擎: Jenkins、

5、应用持续部署:操作系统、 JDK、 Tomcat、 JBoss…

实施持续集成过程中要用到的其他各种工具、各种插件、和Shell脚本…

本系列的大概章目: 

   (1) Subversion源码版本控制系统的安装

    Subversion + Apache + jsvnadmin

    公司是使用Windows来搭建SVN的,这比Linux方便多了,偷懒了

     http://www.dczou.com/viemall/522.html

  (2) Maven私有库和本地库的安装与配置  Nexus + Maven

  (3) Sonar代码质量管控平台的安装与使用配置,

      这个比较坑,在安装的时候插件都不兼容;需要一个个找来组装

   (4) Hudson持续集成引擎的安装与配置

   (5) 自动化部署的实现

   (6) 自动化测试的实现

Maven私有库和本地库的安装与配置—-> Maven

    java开发环境(JDK)/ Eclipse IDE

    Maven3.0以上版本 下载地址:

       http://maven.apache.org/index.html

  1.本机机子环境:

      jdk1.7.0_55、apache-maven-3.3.9-bin.zip、myeclise10

 2.配置Mavean环境

       MAVEN_HOME: D:\web-server\apache-maven-3.2.3;

       path: ;%MAVEN_HOME%\bin;

 3.修改本地仓库位置

      Maven会将下载的类库(jar包)放置到本地的一个目录下,如果想重新定义这个目录的位置就需要修改Maven本地仓库的配置:

     D:\web-server\apache-maven-3.2.3\conf\settings.xml

 image.png

   依据该配置,Maven就会将下载的类库保存到固定文件中。

    通过mvn -V 检查mavean是否安装成功;

二、Maven基本介绍:

      Maven 是一个项目管理和构建自动化工具。但是对于我们程序员来说,我们最关心的是它的项目构建功能。所以这里我们介绍的就是怎样用 maven 来满足我们项目的日常需要。

      Maven 使用惯例优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的结构:

 image.png

 Maven 常用命令:

mvn archetype:create 创建Maven项目

mvn compile 编译源代码

mvn deploy 发布项目

mvn test-compile 编译测试源代码

mvn test 运行应用程序中的单元测试

mvn site 生成项目相关信息的网站

mvn clean 清除项目目录中的生成结果

mvn package 根据项目生成的jar

mvn install 在本地Repository中安装jar

mvn eclipse:eclipse 生成eclipse项目文件;

清除eclipse的一些系统设置:mvn eclipse:clean 

mvnjetty:run 启动jetty服务

mvn tomcat:run 启动tomcat服务

mvn clean package -Dmaven.test.skip=true:清除以前的包后重新打包,跳过测试类

我常用的命令是:clean install package -Dmaven.test.skip -PDEV

使用-P参数显示的激活一个profile

    Maven简介(三)——profile介绍

如何将Jar包添加到本地maven仓库:

mvn install:install-file 

 -DgroupId=net.sf.json-lib

 -DartifactId=json-lib

 -Dversion=2.1

 -Dpackaging=jar

 -Dfile=json-lib-2.1-jdk15.jar

 

mvn deploy:deploy-file -DgroupId=org.springframework 

-DartifactId=org.springframework.web.servlet 

-Dversion=3.1.1.RELEASE 

-Dpackaging=jar -Dfile=org.springframework.web.servlet-3.1.1.RELEASE.jar

-Durl=http://192.168.0.66:8081/nexus/content/repositories/releases/    

-DrepositoryId=user-releases


mavean的生命周期及常用的插件介绍:

http://www.cnblogs.com/davidwang456/p/3915031.html


Mavean版本管理:

image.png

  http://hualom.iteye.com/blog/1387708


maven pom.xml文件教程详解:

http://www.zuidaima.com/share/1781583829978112.htm

http://yanguz123.iteye.com/blog/2211224


maven 多项目管理:

https://my.oschina.net/xuqiang/blog/99854

http://www.cnblogs.com/1995hxt/p/5254448.html 


基于mavean配置多个源目录环境(用于环境区分):

  http://casheen.iteye.com/blog/540385

   https://my.oschina.net/vernon/blog/271970

 资料参考:http://blog.csdn.net/liujiahan629629/article/details/39272321

Maven私有库和本地库的安装与配置—-> Nexus

        简介 :前边简单介绍了Maven,而Maven默认提供的中央仓库是在远程网络服务Appache提供的,这对于我们开发时不合理的。如果我们没网了或者什么情况,我们怎么办?也就是说我们队中央仓库的依赖性太大。而Nexus私服则可以解决我们这个问题。先看下这张图应该大家就非才明白了:

image.pngimage.png

      这样就相当于在我们本地的局域网搭建了一个类似中央仓库的服务器,我们开始将中央仓库的一些资料下载到私服务器上,然后平时我们的maven项目就是直接访问局域网内的私服即可,既节省了网络带宽也会加速项目搭建的进程,这样对我们开发来说,对公司来说都是非常好的选择。下边简单看一下Nexus私服的简单使用:

环境配置:

        本地环境: nexus-2.14.2-01-bundle.zip 

 1、Nexus下载

       下载地址:http://www.sonatype.org/nexus/go 

 2、Nexus启动

        我下载的是zip包,解压后进入\ nexus-2.14.2-01-bundle \nexus-2.14.2-01\bin\jsw\,根据操作系统类型选择文件夹,我选的是windows-x86-64文件夹,进入后可看到如下所示bat文件。

 8081为默认的端口号,要修改端口号可进入nexus-2.14.2-01-bundle\nexus-2.14.2-01\conf\打开nexus.properties文件,修改application-port属性值就可以了。

image.png

       双击console-nexus.bat运行。游览器中输入http://127.0.0.1:8081/nexus/,出现如下图所示就代表nexus已经启动成功。

image.png 

Nexus 的仓库分为这么几类:

   hosted 宿主仓库:主要用于部署无法从公共仓库获取的构件(如 oracle 的 JDBC 驱动)以及自己或第三方的项目构件;

   proxy 代理仓库:代理公共的远程仓库;

   virtual 虚拟仓库:用于适配 Maven 1;

   group 仓库组:Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。

PublicRepositories:  仓库组:

     3rd party: 无法从公共仓库获得的第三方发布版本的构件仓库

     Apache Snapshots: 用了代理ApacheMaven仓库快照版本的构件仓库

     Central: 用来代理maven中央仓库中发布版本构件的仓库

    Central M1 shadow: 用于提供中央仓库中M1格式的发布版本的构件镜像仓库

     Codehaus Snapshots: 用来代理CodehausMaven 仓库的快照版本构件的仓库

     Releases: 用来部署管理内部的发布版本构件的宿主类型仓库

     Snapshots:用来部署管理内部的快照版本构件的宿主类型仓库

image.png

配置Maven使用Nexus私服

     让maven项目使用nexus作为远程仓库有两种方式,第一种是在项目的pom.xml中进行更改,让单个项目使用nexus仓库;另一种是通过修改maven的配置文件settings.xml进行更改,让所有项目都使用nexus仓库。

    pom配置:

<!-- 设定除中央仓库 (repo1.maven.org/maven2/)外的其他仓库,按设定顺序进行查询,
       如有Nexus私服, 取消注释并指向正确的服务器地址 -->
	<repositories>
		<repository>
			<id>nexus</id>
			<name>Team Nexus Repository</name>
			<url>http://192.168.0.110:6161/nexus/content/groups/public</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
	</repositories>

	<!-- 如有Nexus私服, 取消注释并指向正确的服务器地址 -->
	<pluginRepositories>
		<pluginRepository>
			<id>nexus</id>
			<name>Team Nexus Repository</name>
			<url>http://192.168.0.110:6161/nexus/content/groups/public</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>

	<!-- 将项目发布到私服 -->
	<distributionManagement>
		<snapshotRepository>
			<uniqueVersion>false</uniqueVersion>
			<id>nexus-snapshots</id>  <!-- settings.xml id 对应 -->
			<name>User Porject Snapshot</name>
			<url>http://192.168.0.110:6161/nexus/content/repositories/snapshots/</url>
		</snapshotRepository>
		<repository>
			<id>nexus-releases</id>
			<name>User Porject Release</name>
			<url>http://192.168.0.110:6161/nexus/content/repositories/releases/</url>
		</repository>
	</distributionManagement>

基于Mavean将项目发布私服:

     1 . 修改私服中仓库的部署策略

        Release版本的项目应该发布到Releases仓库中,对应的,Snapshot版本应该发布到Snapshots仓库中。Maven根据pom.xml文件中版本号<version>节点的属性是否包含-SNAPSHOT,来判断该项目是否是snapshot版本。如果是snapshot版本,在执行mvn deploy部署命令时,maven会自动将项目发布到Snapshots仓库。要发布项目,

         首先需要将Releases仓库和Snapshots仓库的“Deployment Policy”设置为“Allow Redeploy”:

    奇葩问题:在mvn deploy jar是:Snapshot(快照)形式的项目打包后会自动增加时间戳的部分;

    问题:http://jira.codehaus.org/browse/MNG-4452

     image.png

  2 . 配置项目的部署仓库

      在pom.xml中分别对Release版本和Snapshot版本配置部署仓库,其中id唯一,url分别对应私服中Releases和Snapshots仓库的Repository Path:

     image.png

 配置如下:

    <!– 将项目发布到私服 –>        

 <distributionManagement>

              <snapshotRepository>

                     <uniqueVersion>false</uniqueVersion>

                     <id>nexus-snapshots</id>  <!– settings.xml id 对应 –>

                     <name>User Porject Snapshot</name>

                     <url>http://192.168.0.110:6161/nexus/content/repositories/snapshots/</url>

              </snapshotRepository>

              <repository>

                     <id>nexus-releases</id>

                     <name>User Porject Release</name>

                     <url>http://192.168.0.110:6161/nexus/content/repositories/releases/</url>

              </repository>

       </distributionManagement>


 3.在settings.xml中分别为上面配置的部署仓库配置server,其中id需要分别对应上面的部署仓库id:

  image.png

4 . 发布项目

   右键pom.xml – Run As – 2 Maven build…     clean deploy;


构建java环境的自动化构建和部署系列一

 所有系列文章:

  <<构建java环境的自动化构建和部署系列一>>

  <<构建java环境的自动化构建和部署系列二>>

 <<构建java环境的自动化构建和部署系列三>>

 <<构建java环境的自动化构建和部署系列四>>

        前段为了方便公司管理项目的版本控制,和方便开发人员快速的部署项目(公司么有运维人员),搭建了搭建敏捷高效的持续集成管理平台,并且规范了安装部署的规范统一通过Shell来实现一键安装环境.(不过也要淘汰了,BOSS想通过Docker 来实现系统环境的部署)

持续集成管理平台的组成

    持续集成管理平台不只是CI服务器,是一系列软件开发管理工具的组合

1、源码版本管理:Subversion、 Git

2、项目构建工具:Maven、 Ant

3、代码质量管理:Sonar(Checkstyle、 PMD、 FindBugs……)

4、持续集成引擎: Jenkins、

5、应用持续部署:操作系统、 JDK、 Tomcat、 JBoss…

实施持续集成过程中要用到的其他各种工具、各种插件、和Shell脚本…

    总体的平台流程图:下图网上找到,公司的部署架构跟这个类似,所以就没有自己去画了

image.png

本系列的大概章目: 

   (1) Subversion源码版本控制系统的安装

    Subversion + Apache + jsvnadmin

    公司是使用Windows来搭建SVN的,这比Linux方便多了,偷懒了

  (2) Maven私有库和本地库的安装与配置  Nexus + Maven

  (3) Sonar代码质量管控平台的安装与使用配置,

      这个比较坑,在安装的时候插件都不兼容;需要一个个找来组装

   (4) Hudson持续集成引擎的安装与配置

   (5) 自动化部署的实现

  (6) 自动化测试的实现

所有的前提准备:

    JDK 的环境安装(我是1.7的版本)

    Mysql 的安装.

系列一:Subversion + Apache + jsvnadmin

      公司是通过Window来搭建的SVN环境的,关于Windows的搭建,本章不去细说,本章通过Linux环境来搭建源码的控制。建议使用Windows来管理SVN,使用起来比较便捷方便,个人喜欢点;

安装步骤:

   #yum install -y httpd

  安装 apache

    # yum install httpd httpd-devel

    # service httpd start

    # chkconfig httpd on

我的机器有nginx 所以为了防止80端口冲突,改成了81。没有需要的可以不更改,也试过Nginx来实现,但是遇到一些奇葩问题,就采用Apatch了.

   # vi /etc/httpd/conf/httpd.conf

找到 ServerName 并修改成

    Listen 81

    ServerName localhost:81

防火墙中打开 81 端口:

    # vi /etc/sysconfig/iptables

    -A INPUT -m state –state NEW -m tcp -p tcp –dport 81 -j ACCEPT

    # service iptables restart

 访问:http://192.168.1.105:81/ 就可以知道有么有安装成功

 

安装 SVN 服务

   # yum install mod_dav_svn subversion

  必须安装 mod_dav_svn 模块

   安装完 svn 后要重启 apache

   # service httpd restart

查看测试是否安装 svn 模块

# ls /etc/httpd/modules/ | grep svn

# svn –version

创建 svn 库主目录(多库模式,一份配置文件管理多个库)

    # mkdir /svn/

    # vi /etc/httpd/conf.d/subversion.conf 添加以下内容

LoadModule dav_svn_module     modules/mod_dav_svn.so
LoadModule authz_svn_module   modules/mod_authz_svn.so
<Location /svn/>
         DAV svn
         SVNListParentPath on
         SVNParentPath /svn
         AuthType Basic
         AuthName "Subversion repositories"
         AuthUserFile /svn/passwd.http
         AuthzSVNAccessFile /svn/authz
         Require valid-user
</Location>
RedirectMatch ^(/svn)$ $1/

创建/svn/passwd.http 和/svn/authz

   # touch /svn/passwd.http

   # touch /svn/authz

重启 apache

    # service httpd restart

 

安装 jsvnadmin

   svnadmin 介绍

(在 Google Code 上,需要 FQ 才能下载。我们也会把最新版的 jsvnadmin 放到群共享中)

     https://code.google.com/p/jsvnadmin/

      https://jsvnadmin.googlecode.com/files/svnadmin-3.0.5.zip

 

将下载的jsvnadmin部署在tomcat;

修改svnadmin/WEB-INF 的jdbc.properties

内容改为如下

db=MySQL
#MySQL
MySQL.jdbc.driver=com.mysql.jdbc.Driver
MySQL.jdbc.url=jdbc:mysql://127.0.0.1:3306/svnadmin?characterEncoding=utf-8
MySQL.jdbc.username=root
MySQL.jdbc.password=123456

创建 svnadmin 数据库并导入相应数据( UTF-8 编码)

执行 db/mysql5.sql 和 db/lang/en.sql

 

启动tomcat:

浏览器中打开: http://192.168.1.105:9000/svnadmin/

第一次访问需要设置登录用户和密码,登录后的管理界面:

 image.png

创建库

 image.png

此时:

   /svn/目录下会创建一个viemall 的 SVN 库目录。通过下面就会有SVN项目的用户管理文件.

配置库目录权限

# cd /svn

# chown -R apache.apache viemall

# chmod -R 777 viemall

(如创建新库,需要对新建的库目录执行以上两步授权操作)

  否则在客户端提交代码的时候,会出现“SVN提交时:could not begin a transaction”;

关闭 SELinux( Linux 的访问控制)

修改/etc/selinux/config 文件

# vi /etc/selinux/config

将 SELINUX=enforcing 改为SELINUX=disabled

重启机器即可

# reboot

 

SVN 版本管理平台( Subversion+Apache+Jsvnadmin)的使用:

先安装 SVN 管理客户端 TortoiseSVN,方便对 SVN 库的操作和管理

    http://tortoisesvn.net/downloads.html 这个小海龟不多说都懂。

 

接下来可以对 wusc_edu 库进行相应的操作

(1) 用户组

(2) 用户

(3) 授权

(4) 导入项目

 image.png

     http://auskangaroo.blog.51cto.com/740826/1410549