jenkins使用

jenkins使用

说明:jenkins是一个能实现项目自动化部署管理的工具

准备工作

下载软件:https://jenkins.io/download/ 下载war包就可以了,在不同的平台上都可以通用

安装git、maven工具

git1.7.1在和Jenkins使用的时候会出现问题,这里建议是用1.7.1以上的 由于centos6自带的git就是1.7.1的,所以我是编译安装的,安装步骤如下:

1)安装依赖工具

<code>yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel</code>

2)下载源代码,我用的是2.7.4 https://mirrors.edge.kernel.org/pub/software/scm/git/

<code>cd git-2.7.4
make prefix=/usr/local/git all
make prefix=/usr/local/git install
vim /etc/profile
// 在末尾新开一行填写下面的代码
export PATH=$PATH:/usr/local/git/bin
// :wq保存退出,然后执行下面的命令,让其生效
source /etc/profile
// 查看git版本
git --version</code>

maven安装 http://maven.apache.org/download.cgi 我下载最新版3.5.3 将文件放在/usr/local/下 配置环境变量:

<code>MAVEN_HOME=/usr/local/apache-maven-3.5.3
export MAVEN_HOME
export PATH=${MAVEN_HOME}/bin</code>

运行jenkins并创建项目

启动:在服务器上运行: nohup java -jar jenkins.war &
控制台会打印出初始化的用户名和密码 至此就在浏览器打开页面,输入账号就可以登录到管理界面,jenkins会自动扫描服务器上的安装的maven、git服务

新建一个项目:
newjenkins
选择项目源码位置,这里使用gitlab来管理源码,下面是登陆到gitlab的账号
newjenkins1

因为我使用的是maven项目,填写maven构建的执行命令,我这里是打包成一个war包
newjenkins2

添加构建后的操作,比如上传到war包到服务器,然后备份原来的项目,重启tomcat,这里面需要配合shell脚本来完成
newjenkins3

shell脚本,如下:基本的逻辑是先将原来的项目压缩,移动到备份目录,然后将修改后的项目上传到服务器对应的目录,并重启tomcat

<code>WORK_DIR=`pwd`
TOMCAT_DIR=$WORK_DIR/tomcat-dynamic-api
NOW_TIME=`date +%Y%m%d%H%M%S`
tar -jcvf $TOMCAT_DIR/webapps/dynamicAPI.tar.$NOW_TIME $TOMCAT_DIR/webapps/dynamicAPI
mv $TOMCAT_DIR/webapps/dynamicAPI.tar.$NOW_TIME $TOMCAT_DIR/backup/
mv $TOMCAT_DIR/backup/dynamicAPI.war $TOMCAT_DIR/webapps/
rm -rf $TOMCAT_DIR/webapps/dynamicAPI
kill -9 `ps aux | grep tomcat-dynamic-api | grep -v grep | awk '{print $2}'` &amp;&amp; $TOMCAT_DIR/bin/startup.sh</code>

maven配置

   
    <profiles>
        <profile>
            <id>local</id>
            <properties>
                <env>local</env>
            </properties>
        </profile>
        <profile>
            <id>product156</id>
            <!--
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
             -->
            <properties>
                <env>product_156</env>
            </properties>
        </profile>
        <profile>
            <id>product169</id>
            <properties>
                <env>product_169</env>
            </properties>
        </profile>
        <profile>
            <id>backup148</id>
            <properties>
                <env>backup_148</env>
            </properties>
            <build>
                <resources></resources>
            </build>
        </profile>

        <profile>
            <id>test157</id>
            <properties>
                <env>test_157</env>
            </properties>
        </profile>
        <profile>
            <id>test169</id>
            <properties>
                <env>test_169</env>
            </properties>
        </profile>

    </profiles>
    <!-- 配置文件 -->

    <build>
        <finalName>dynamicAPI</finalName>
        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-ssh</artifactId>
                <version>2.8</version>
            </extension>
        </extensions>
        <resources>
            <resource>
                <directory>src/main/resources/${env}</directory>
            </resource>
        </resources>
        <testSourceDirectory>src/test/java</testSourceDirectory>
        <testResources>
            <testResource>
                <directory>src/test/resources</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <webResources>
                        <resource>
                            <directory>WebContent</directory>
                        </resource>
                        <resource>
                            <directory>libs/</directory>
                            <targetPath>WEB-INF/lib</targetPath>
                            <includes>
                                <include>**/*.jar</include>
                            </includes>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.16</version>
                <configuration>
                    <skipTests>true</skipTests>
                    <junitArtifactName>junit:junit</junitArtifactName>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <excludes>
                        <exclude>product_156/**</exclude>
                        <exclude>product_169/**</exclude>
                        <exclude>test_157/**</exclude>
                        <exclude>*.properties</exclude>
                        <exclude>*.xml</exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>versions-maven-plugin</artifactId>
                <version>2.1</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.8</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

kafka和zookeeper集群安装

kafka和zookeeper集群安装

上期讲了kafka的作用及应用场景,今天我们来自己搭建一套kafka集群,由于kafka目前的安装包已经自带了zookeeper,所以在搭建zookeeper集群直接使用它内置的即可

准备工作:3台服务器,当然你可以使用自己的虚拟机尝试,如果安装虚拟机,可以查看我的这篇博客
我的三台服务器的IP分别是:192.168.101.116、192.168.101.115、192.168.102.215

安装步骤

下载安装包:

地址:https://www.apache.org/dyn/closer.cgi?path=/kafka/1.0.0/kafka_2.11-1.0.0.tgz

tar -xzf kafka_2.11-1.0.0.tgz
cd kafka_2.11-1.0.0

配置zookeeper

配置kafka的路径、心跳检测时间 初始化连接时follower和leader最长连接时间 follower和leader数据最长同步时间

 dataDir=/data/kafka/zookeeper
 tickTime=2000
 initLimit=5
 syncLimit=2
 server.0=192.168.101.116:2888:3888
 server.1=192.168.101.115:2888:3888
 server.2=192.168.102.215:2888:3888

在每个服务器上注册zookeeper

/data/kafka/zookeeper目录下面touch myid文件
192.168.101.116上执行
echo "0" > /data/kafka/zookeeper/myid
192.168.101.115上执行
echo "1" > /data/kafka/zookeeper/myid
192.168.102.215上执行
echo "2" > /data/kafka/zookeeper/myid

配置kafka

 192.168.101.116上配置
 advertised.listeners=PLAINTEXT://zc1:9092
 broker.id=0

 192.168.101.115上配置
 advertised.listeners=PLAINTEXT://zc2:9092
 broker.id=1

 192.168.102.215上配置
 advertised.listeners=PLAINTEXT://zc3:9092
 broker.id=2

 通用配置
 log.dirs=/data/kafka/kafka-logs
 zookeeper.connect=192.168.101.116:2181,192.168.101.115:2181,192.168.102.215:2181

配置hosts

在每个服务器/etc/hosts中添加如下配置

192.168.101.116 zc1
192.168.101.115 zc2
192.168.102.215 zc3

开放防火墙端口

由于zookeeper和kafka监听了2181、9092、2888、3888,所有我们还要把这些端口添加到防火墙中,编辑/etc/sysconfig/iptables,添加:

-A INPUT -m state --state NEW -m tcp -p tcp --dport 2181 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 2888 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3888 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 9092 -j ACCEPT

启动zookeeper和kafka

先启动zookeeper

bin/zookeeper-server-start.sh config/zookeeper.properties &

再启动kafka

bin/kafka-server-start.sh config/server.properties &

测试数据写入和读取

使用命令测试

生产者
[root@zc1 kafka_2.11-1.0.0]# bin/kafka-console-producer.sh –broker-list 192.168.101.115:9092,192.168.101.116:9092,192.168.102.215:9092 –topic firsttopic

>hello
>word

消费者
[root@zc2 kafka_2.11-1.0.0]# bin/kafka-console-consumer.sh –bootstrap-server 192.168.101.115:9092,192.168.101.116:9092,192.168.102.215:9092 –topic firsttopic

hello
word

代码测试

生产者

public class ProducerTest {

    private final static String TOPIC_NAME = "firsttopic";
    private static Producer&lt;String, String&gt; producer;
    private final static Logger logger = LoggerFactory.getLogger(ProducerTest.class);

    public ProducerTest() {
        /** 生产者*/
        InputStream in_proc = this.getClass().getClassLoader().getResourceAsStream("kafka_test_producer.properties");
        Properties prop_proc = new Properties();
        try {
            prop_proc.load(in_proc);
        } catch (IOException e) {
            logger.error("加载kafkacore_test_producer配置文件失败", e);
        }
        producer = new KafkaProducer&lt;String, String&gt;(prop_proc);
    }

    public void execute() {
        while(true) {
            try {
                String key = "CA1234";
                String message = System.currentTimeMillis()+" CA1234,PEK,SHA,2018-02-01";
                this.sendToTestServer(key, message);
                Thread.sleep(500);
            } catch (InterruptedException e) {
                logger.error("任务执行异常",e);
            }

        }
    }

    private void sendToTestServer(String key, String message) {
        logger.info("发送消息:"+message);
        ProducerRecord&lt;String, String&gt; producerRecord = new ProducerRecord&lt;String, String&gt;(TOPIC_NAME, key, message);
        producer.send(producerRecord);
    }

    public static void main(String[] args) {
        new ProducerTest().execute();
    }

}

kafka_test_producer.properties

kafka.topic=firsttopic
group.id=chuanzh
bootstrap.servers=192.168.101.115:9092,192.168.101.116:9092,192.168.102.215:9092
retries=5
request.timeout.ms=10000
key.serializer=org.apache.kafka.common.serialization.StringSerializer
value.serializer=org.apache.kafka.common.serialization.StringSerializer

消费者

public class ConsumerTest {

    private final static String TOPIC_NAME = "firsttopic";
    private static Consumer&lt;String, String&gt; consumer;
    private final static Logger logger = LoggerFactory.getLogger(DynamicPushTestTask.class);

    public ConsumerTest() {
        /** 消费者*/
        InputStream ksin = this.getClass().getClassLoader().getResourceAsStream("kafka_test_consumer.properties");
        Properties props = new Properties();
        try {
            props.load(ksin);
        } catch (IOException e) {
            logger.error("加载kafkacore_test_consumer配置文件失败", e);
        }
        consumer = new KafkaConsumer&lt;String, String&gt;(props);
        consumer.subscribe(Arrays.asList(TOPIC_NAME));
    }

    public void execute() {
        while(true) {
            try {
                ConsumerRecords&lt;String, String&gt; records = consumer.poll(2);
                logger.info("读取kafka,取到消息数量:" + records.count());
                for (ConsumerRecord&lt;String, String&gt; record : records) {
                    logger.info("value:{}", record.value());
                    logger.info("offset:{}", record.offset());
                }

                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.error("任务执行异常",e);
            }

        }
    }

    public static void main(String[] args) {
        new ConsumerTest().execute();
    }

}

kafka_test_consumer.properties

kafka.topic=flightcore
group.id=flightPacketPush
bootstrap.servers=43.241.208.208:9092,43.241.228.39:9092,43.241.234.89:9092
metadata.max.age.ms=60000
max.poll.records=50
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer

 

 

启用免费HTTPS

最近看了酷壳网耗子哥写的博客,把我的网站的http域名也改为https了

为何要使用https,而不是http?

https可以理解为一种安全的http通信协议,比如你通过浏览器访问一个网站,中间会通过网络运营商,由于http是明文传输,中间报文就可能被其他人劫持,获取你的信息,而https就可以保证传输的加密性。

我的服务器是centos 6和nginx,打开网址(https://certbot.eff.org)选择nginx和centos6,照着步骤一步步做就行了。

因为原先使用的是apache,这里顺便说下如何使用nginx替换apache

安装nginx

yum install nginx
service nginx start

nginx的网页目录默认为:/usr/share/nginx/html/

可以通过vi /etc/nginx/conf.d/default.conf来修改默认目录,我修改为原先的Apache的目录是/var/www/html

root         /var/www/html;

由于nginx只负责转发请求,并不能解析php脚本,所以我们还需要安装php解析器:php-fpm,nginx是通过把请交给php-fpm来解析php生成html页面,所以安装它:

yum install php-fpm

接着配置ngxin的域名目录并关联php-fpm

server {
        listen 80;
        root /var/www/html/blog;
        server_name www.ihnbc.cn ihnbc.cn;
        index index.shtml index.html index.htm index.php;
        charset utf-8;
        access_log /home/www.ihnbc.cn.access.log main;

        location ~.*\.(php|php5)?$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi.conf;
       }
}

php结尾的域名全部交给127.0.0.1:9000来执行,而php-fpm正好监听这个端口,启动php-fpm,重启nginx

/etc/init.d/php-fpm start
service nginx restart

配置https证书,下载certauto

wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto

然后运行:

sudo ./path/to/certbot-auto --nginx

certbot会扫描你Nginx里面的域名,选择需要配置的域名,多个用空格隔开,其他选项安装步骤执行就可以了,运行完后,certbot会在你的Nginx加上如下配置:

    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/ihnbc.cn/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/ihnbc.cn/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

注意 listen 443 ssl后面http2是我添加上去的,http2开启后更有助于提高https的性能。

重启nginx,由于Let’s Encrypt的证书90天过期,所以你还需要配置一个任务定时去更新证书,使用cron,如下:

0 1 * * * /home/zc/certbot-auto renew

最后还有一件事要做,因为原先WordPress网站中使用的链接都是http的,所以你还需要更新网页中的http链接,不然你的链接上不会显示“安全锁”标识,使用WordPress的插件search-regex就可以批量替换(手动下载search-regex,放到wordpress的wp-content/plugins就可以了)

 

Hystrix服务隔离及熔断

上次说了使用RxJava并行提升API效率,但是如何避免项目中一个服务拖慢整体的性能?hystrix优雅的解决了这个问题。

先贴个图,网上找的,我觉得这个图就可以说明问题了

hystrix-1

当依赖服务A挂了,直接就会影响到整个服务的性能,如果连接一直被A暂用,最终将导致服务发生雪崩

所以我们要保证核心服务的稳定,其他非核心的服务可以不那么重要,即使出现了错误数据可以丢弃或不用保证实时准确,再以SOA项目中的getDynamicInfo为例,如下:

87194035-3D85-4860-AB44-35EA71697A44

getFlight为核心的业务,必须要获取到数据,获取不到那就整个服务不可用了

getEntryCard、getPlane、getRefund、getWeather、getPortaitUser等方法为非核心业务,我们允许其数据丢失的情况,要保证整个服务的可用

首先我们需要把这些服务使用hystrix分离出来,先对这几个服务做先分组:

pydyn:getEntryCard、getPlane、getRefund、getWeather

portrait:getPortaitUser

Maven配置

	<dependency>
	  <groupId>com.netflix.hystrix</groupId>
	  <artifactId>hystrix-core</artifactId>
	  <version>1.5.8</version>
	</dependency>

以getEntryCard服务为例,继承HystrixCommand<T>,重写run方法

public class GetEntryCardCommand extends HystrixCommand<String>{

	private String arrCode;
        private GetEntryCardService service;

	public GetEntryCardCommand(String arrCode) {
		super(setter());
		this.arrCode = arrCode;
	}

	private static Setter setter() {
		return PySetter.setter().andCommandKey(HystrixCommandKey.Factory.asKey("getEntryCard"));
	}

	@Override
	protected String run() throws Exception {
		// TODO Auto-generated method stub
		return service.getEntryCardCity(arrCode);
	}

	@Override
	protected String getFallback() {
		return service.getFromCache(arrCode);
	}

}

run方法要执行的业务逻辑,如果出现异常则会调用getFallback方法。

public class PySetter {

	public static Setter setter() {
		// 服务分组
		HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("pydyn");
		// 服务标识
		//HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("getEntryCard");
		// 线程池名称
		HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("pydyn-pool");
		// 线程配置
		HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter()
				.withCoreSize(10)
				.withKeepAliveTimeMinutes(5)
				.withMaxQueueSize(Integer.MAX_VALUE)
				.withQueueSizeRejectionThreshold(10000);

		//命令属性配置
		HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
				.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                                .withExecutionIsolationThreadInterruptOnTimeout(true).withExecutionTimeoutInMilliseconds(3000) //设置超时3秒自动熔断
				.withCircuitBreakerErrorThresholdPercentage(30);//失败率达到30%自动熔断

		return HystrixCommand.Setter
				.withGroupKey(groupKey)
				//.andCommandKey(commandKey)
				.andThreadPoolKey(threadPoolKey)
				.andThreadPoolPropertiesDefaults(threadPoolProperties)
				.andCommandPropertiesDefaults(commandProperties);
	}

}

以上是对服务的线程池熔断配置

HystrixCommandGroupKey:服务分组,以上pydyn分组就包括4个服务,必填选项

HystrixCommandKey:服务的名称,唯一标识,如果不配置,则默认是类名

HystrixThreadPoolKey:线程池的名称,相同线程池名称的线程池是同一个,如果不配置,默认为分组名

HystrixThreadPoolProperties:线程池的配置,coreSize配置核心线程池的大小,maxQueueSize线程池队列的最大大小,queueSizeRejectionThreshold,限制当前队列的大小,实际队列大小由这个参数决定,即到达队列里面条数到达10000,则都会被拒绝。

HystrixCommandProperties:配置命令的一些参数,如executionIsolationStrategy,配置执行隔离策略,默认是使用线程隔离,THREAD即为线程池隔离,ExecutionIsolationThreadInterruptOnTimeout和ExecutionTimeoutInMilliseconds配置了启用超时和最大执行时间,这里为3s,circuitBreakerErrorThresholdPercentage失败率配置,默认为50%,这里配置的为30%,即失败率到达30%触发熔断

接下来就是调用GetEntryCardCommand获取数据

String result = new GetEntryCardCommand(request.getArrCode()).execute();

我使用的是阻塞的方式获取,当然也可以使用异步的方法,得到Future对象,使用

Future<String> = new GetEntryCardCommand(request.getArrCode()).queue();

其他getPlane、getRefund、getWeather方法同GetEntryCardCommand类,

getPortaitUser也一样,只需要更改下自己的分组为portait,线程池名称,并配置自己的线程池参数

好了,这样就实现了多个服务之间的隔离和熔断,不同的服务分组使用不同的线程池,即使getDynamicInfo并发量突然剧增,也不会对getPlane、getRefund、getWeather等产生影响

下面来说说对服务的监控,hystrix已经帮我们实现了数据的记录,只需要安装他们的管理后台就可以查看数据,他们家有2个监控后台:hystrix-dashboard和Turbine,第一个是监控单个项目的日志,第二个可以把多个项目的日志聚合在一起

hystrix-dashboard监控:

maven配置:

	 <dependency>  
	     <groupId>com.netflix.hystrix</groupId>  
	     <artifactId>hystrix-metrics-event-stream</artifactId>  
	     <version>1.1.2</version>  
 	</dependency>

web.xml配置

    <servlet>  
	    <display-name>HystrixMetricsStreamServlet</display-name>  
	    <servlet-name>HystrixMetricsStreamServlet</servlet-name>  
	    <servlet-class>com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet</servlet-class>  
	</servlet>  
	<servlet-mapping>  
	    <servlet-name>HystrixMetricsStreamServlet</servlet-name>  
	    <url-pattern>/hystrix.stream</url-pattern>  
	</servlet-mapping>

访问http://ip:port/projectName/hystrix.stream查看是否有监控数据

hystrix-dashboard:先安装hystrix-dashboard.war,下载后放入web容器(Tomcat或Jetty)中,启动容器,访问http://ip:port/hystrix-dashboard,如下的界面:

371D6055-27B8-41ED-ADF3-544E92ECC934

最后点击Monitor Stream就可以看到数据了

EFD10FC9-9BAB-43DE-B5EB-09082222744B

可以看到每个服务及他们的线程池使用情况

CirCuit主要监控成功的请求数,请求超时数,失败率,平均耗时、90%,99%,99.5%的耗时(将鼠标移动到对于数字位置可以看到描述)

Thread Pools主要监控线程池的配置数,线程队列的配置数,最大活跃线程

 

Turbine监控(将多个实例的数据聚合起来)

1)下载turbine-web-1.0.0.war,并将war放入web容器中。

2)在容器下路径为turbine-web-1.0.0/WEB-INF/classes下新建config.properties文件。

InstanceDiscovery.impl=com.netflix.turbine.discovery.ConfigPropertyBasedDiscovery
#cluster
turbine.aggregator.clusterConfig=dynamicAPI
turbine.instanceUrlSuffix=:8080/projectName/hystrix.stream
turbine.ConfigPropertyBasedDiscovery.dynamicAPI.instances=127.0.0.1,127.0.0.2

这里我配置了2个实例127.0.0.1和127.0.0.2,他会同时调用http://127.0.0.1:8080/projectName/hystrix.stream和http://127.0.0.2:8080/projectName/hystrix.stream,然后根据分组名,服务名,线程池名将两个实例数据合并

3)调用http://ip:port/turbine-web-1.0.0/turbine.stream?cluster=dynamicAPI,即可查看到聚合的数据

4)打开hystrix-dashboard配置turbine

766892D0-ADE9-4BC4-A991-7844965A1557

点击Monitor Stream即可查看到两个实例监控的数据

5EC0BFFE-3C41-4F14-980D-39614A231A16

 

参考资料:

http://tech.lede.com/2017/06/15/rd/server/hystrix/

http://www.cnblogs.com/java-zhao/archive/2016/09/01/5831002.html

 

JAVA内存分析

JAVA通过自带的垃圾回收机制来管理内存,但对于项目的内存分析也要了解,比如项目的内存使用情况,何时回收?或出现内存泄露如何排查?

首先看看JVM内存分配图:

671D4492-75BB-4046-8B2E-199ABDE70734

如上图,jvm包含了堆、本地方法栈,

堆上存放新建(New)的对象,它被划为为新生代,老年代两个区域,新生代又分为伊甸园、幸存者两个区域,不同的区域存放的对象生命周期不同

说说JAVA中几种内存溢出的情况:

1,JVM堆溢出(java.lang.OutOfMemoryError: java heap space)

当生成一个新对象,JVM内存申请如下流程:

1),jvm先尝试在eden分配新对象所需的内存,若内存足够,则将对象放入eden返回

2),若内存不够,jam启动youngGC,试图将eden不活跃的对象释放掉,若释放后仍不足以分配内存,则将Eden活跃的对象放入survivor中。

3),survivor作为Eden和old的中间交换区域,若old空间足够,survivor去对象会被移动到old区,否则留在survivor区

4),当old区不够时,jvm会在old区进行fullGC,若fullGC后,survivor和old仍然无法存放从Eden复制过来的对象,则会出现“outOfMemoryError: java heap space”

解决方法:加大堆内存的大小,通过设置-Xms(java heap初始化大小,默认是物理内存1/64) -Xmx(java heap的最大值) -Xmn(新生代heap的大小,一般为Xmx3或4分之一,注:增加新生代后会减少老年代的大小)

2,方法区内存溢出(java.lang.OutMemoryError: permGen space)

方法区主要是用来存放类信息,常量、静态变量等,所以程序中类加载过多(引入第三方包),或者过多使用反射、cglib这种动态代理,就可能导致该区域内存溢出

解决方法:通过设置-XX:PermSize(内存永久区初始值)和-XX:MaxPerSize(内存永久区最大值)的大小

3,线程栈溢出(java.lang.StackOverflowError)

线程栈是线程独有的一块内存区域,所以线程栈溢出必定是线程运行是出现错误,一般是递归太深,或者方法层级调用太深引起的

解决方法:设置栈区的大小,通常栈的大小是1-2M,可通过-Xss设置线程的栈的大小,jdK5以后每个栈默认大小为1M。

下面针对线上的某个项目,查看它是否出现内存溢出的情况

1,如何查看项目的内存使用情况?

jmap -heap pid

Attaching to process ID 64909, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b14

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 44564480 (42.5MB)
   MaxNewSize               = 715653120 (682.5MB)
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 31457280 (30.0MB)
   used     = 12660000 (12.073516845703125MB)
   free     = 18797280 (17.926483154296875MB)
   40.24505615234375% used
From Space:
   capacity = 1572864 (1.5MB)
   used     = 1179648 (1.125MB)
   free     = 393216 (0.375MB)
   75.0% used
To Space:
   capacity = 9437184 (9.0MB)
   used     = 0 (0.0MB)
   free     = 9437184 (9.0MB)
   0.0% used
PS Old Generation
   capacity = 40894464 (39.0MB)
   used     = 4514768 (4.3056182861328125MB)
   free     = 36379696 (34.69438171386719MB)
   11.040046887520033% used

查看Java内存的分配情况及新生代、老年代内存的使用情况,确定是否内存分配过小?

2,判断是否出现内存泄露

jstat -gcutil pid 2000

D09AEF2B-2D3E-4D50-B74C-E603875EAD70

上面命令表示每隔2秒打印出GC的使用回收情况,若FGC很多很可能出现了内存泄露

3,查看占用内存最多的对象

jmap -histo:live pid | more

0B137198-E38B-4466-A554-E68FEC36EC61

按使用大小进行了排序,重点查看排在前面的对象,看是否程序写的有问题。

另外可以将jvm的堆内存导出来分析,使用

jmap -dump:format=b,file=dynamicapi.hprof pid

使用java vistual工具分析,jdk自带了jvisualvm就可以进行内存分析,执行命令:

进入jkd的bin目录,执行jvisualvm

点击文件->装入,选择堆内存文件,即可看到堆中类的使用情况

3E5ECD10-58E2-450B-A8C4-31B276689EC6

 

MAC下虚拟机安装

MAC下虚拟机安装

mac下可使用virtualbox虚拟机和VMware虚拟机,但VMware是收费的,这里我使用的是virtualbox

软件及镜像文件

  • 虚拟机:virtualbox 下载地址:https://www.virtualbox.org/wiki/Downloads
  • 镜像文件:centos6.5(final) 下载地址:http://mirror.nsc.liu.se/centos-store/6.5/isos/x86_64/

虚拟机安装

略(mac上按照软件步骤)

系统安装

  • 为系统分配空间和内存
    1-6
  • 选择镜像文件
    6-20

网络连接

常用的有4种模式 :

  • Host-Only 仅主机,此时在本地可以连上虚拟机IP
  • 内部网络,虚拟机内部之间可以互通
  • 网络地址转换(NAT) 虚拟机之中可以访问外网
  • 桥接网卡 虚拟使用和本地一样获取网络的方式

第4种包括可以实现前面3个的所有功能,所以这里就选择桥接网卡
wifi

配置虚拟机网卡,基本不用改,把onboot改为yes即可:

DEVICE=eth0
HWADDR=08:00:27:3A:A1:D8
TYPE=Ethernet
UUID=216900a4-4929-49a0-a4d6-7bbafba232e5
ONBOOT=yes
NM_CONTROLLED=yes
BOOTPROTO=dhcp

 

 

LINUX用户及用户组管理

1, 管理用户命令

1)创建用户,默认会创建一个相同名称的用户组,创建用户后,会在/home目录下生成一个用户名称的目录

用法:

-c comment 指定一段注释性描述。

-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。

-g 用户组 指定用户所属的用户组。

-G 用户组,用户组 指定用户所属的附加组。

-s Shell文件 指定用户的登录Shell

-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。

  #useradd zc
  #groups zc
  zc:zc

2)为zc账号创建密码

  #passwd zc

3)删除用户

用法:

-r 用户号,把用户的主目录一起删除

  #userdel zc

 

2, 一个关于用户权限的例子

说明:现在有两个用户zczc2zc属于zc用户组,zc2属于zc2用户组,两个用户同时向一个目录写入文件。

zczc2的目录下分别放有tomcat服务,分别用zczc2,启动各自的tomcat服务。有一个接口http://xxx.xxx.xxx.xxx:8081/hscheck/check是向服务器/data/zc下写文件。zc目录对所有用户及用户组都具有读写权限,即其权限为drwxrwxrwx

1)首先调用zc的接口http://xxx.xxx.xxx.xxx:8080/hscheck/check,向/data/zc目录下写入文件

FC]RSQ$]0$W77GT5O5__4}C

2)再调用zc2的接口http://xxx.xxx.xxx.xxx:8081/hscheck/check,向/data/zc目录下写入文件,此时zc2用户是没有写入权限的,因为/2015/08/目录为zc用户创建,zc2对此目录没有写权限

  解决方法:

zczc2分配到同一组,使用usermod命令

起初我使用:usermod -a -G zc zc2命令,将zc2用户放入zc用户组中,但仍报权限错误,这里要指出一点,使用-a -G参数是在zc用户组中添加zc2用户,但zc2也属于zc2用户组,zc22个用户组zc2zc,其中zc2zc2用户的主要用户组

 

所以应该使用usermod -g zc zc2命令,将zc2的主要用户组改为zc,这样再次调用zc2的接口,此时zc2能在zc创建的目录写入数据。

ZR1RRAX$3$3{M{(]3A99TR9

Linux下SVN服务搭建

1. SVN服务安装

yum install subversion

在我安装的时候出现了错误:

File “/usr/lib/python2.4/threading.py”, line 416, in start
_start_new_thread(self.__bootstrap, ())
thread.error: can’t start new thread

解决办法是:

编辑/etc/yum/pluginconf.d/fastestmirror.conf

将enabled=1改为enabled=0,禁用该功能

2. SVN配置

创建svn目录

mkdir /data/svn

svnadmin create /data/svn/repo

修改配置文件

进入/data/svn/repo/conf下

编辑svnserve.conf

将anon-access = read 注释去掉,并将read改为none
将authz-db=authz , password-db=passwd 前面注释去掉

1) 设置密码

编辑passwd和authz 文件

passwd格式:

user1=passwd1
user2=passwd2
例如:
zhangchuan=zhangchuan123
kongming=kongming123

2)分配权限

编辑authz文件

authz格式:

2.1). 添加用户组

[groups]
group1=user1,user2,…
group2=user3,user4,…

例如:
[groups]
g1_users=zhangchuan,kongming,guanyu
g2_users=zhangchuan,kongming

2.2)设置用户组的权限

@group1=只读
@group2=读写

例如:
[repo:/]
@g1_users=r
@g2_users=rw

启动svn服务

svnserve -d -r /data/svn/

可以通过netstat -atln查看3690端口是否被使用

到时svn搭建完毕,svn地址为:

svn://服务器IP/repo

 

MySQL用户管理

1. 用户添加及分配权限

1)登录mysql

#mysql  -uroot –p

 2)添加用户

 mysql> user mysql   //使用mysql数据库

mysql> grant all on *.* to zhangchuan@”%” identified by “123″ ;

 (执行完会在mysql.user表插入一条记录,all表示所有权限(包括增 删 改 查

等权限); *.* 表示所有数据库,zhangchuan为添加的用户名,123为密码,

%为匹配的所有主机,上面的信息都可以指定如grant select,update on db.* to

zhangchuan @localhost identified by ’123″;)

mysql> flush privileges;  //刷新系统权限表

3)修改用户

mysql> update user set user=’mysqladminqwe’ where user=’root’;

mysql> flush privileges;

4)删除用户

mysql> delete from mysql.user where user =’zhangchuan’ ;

mysql> flush privileges;

注:执行完sql语句后必须执行 flush privileges才可生效

2. 密码修改:

1)使用mysqladmin命令

# mysqladmin -u root password oldpass “newpass”

2)登录mysql客户端操作mysql.user

#mysql –u root

mysql> user mysql

mysql> UPDATE user SET Password = PASSWORD(‘newpass’) WHERE user = ‘root’;

mysql> flush privileges;

3. 忘记密码

1) 停止mysql服务

# /etc/init.d/mysqld stop

或: service mysqld stop

2)进入mysql的bin目录执行以下操作

# cd /usr/local/mysql/bin

# ./mysqld_safe –user=mysql –skip-grant-tables –skip-networking &

3)进入mysql客户端修改密码

# mysql -u root mysql

mysql> UPDATE user SET Password=PASSWORD(‘newpassword’)

where USER=’root’;

mysql> FLUSH PRIVILEGES;

mysql> quit

4)启动mysql服务登录

# /etc/init.d/mysql restart

# mysql -uroot -p

Enter password: <输入新设的密码newpassword>

4. 开放外网权限

上面已经说过,可以使用grant命令,不过我遇到使用grant开放权限后,使用本地客户端却连接不上。

解决方法如下:

1)检查3306端口是否开放给指定IP

使用nestat命令查看3306端口状态:

~# netstat -an | grep 3306

tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN

若出现以上结果表示3306端口只是在IP 127.0.0.1上监听,所以拒绝了其他IP的访问。

 2)修改/etc/mysql/my.cnf文件。打开文件,找到下面内容:

# Instead of skip-networking the default is now to listen only on

# localhost which is more compatible and is not less secure.

bind-address = 127.0.0.1

把上面这一行注释掉或者把127.0.0.1换成合适的IP,建议注释掉。

重新启动后,重新使用netstat检测:

~# netstat -an | grep 3306

tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN

PHP实现导入大SQL文件

对于mysql的大量数据导入导出,直接在服务器上使用命令,会很方便快捷,但是有时候我们不能总结直接调用命令来实现,此时我们可以使用一个脚本语言作为调用这个命令的中介,这里我是用的是php里面的system函数。

system使用说明:
string system ( string $command [, int &$return_var ] )

command: 执行的命令
return_var: 如果return_var存在,则为执行命令完后返回的结果
函数返回的结果为命令输出的最后一行。

举例:


所以如果需要通过php导入大数据,可以使用:


Dbhost 改为您的数据库服务器地址
Dbuser 改为您的数据库用户名
Password 改为您的数据库用户密码
Dbname 改为您的数据库名
backup.sql 导入的sql文件

3.在浏览器里面访问mysql.php即可完成导入,同理如果需要导出sql语句也可以使用system命令来完成,其执行的时间相对比较快。