iyihua

  • 首页

  • 标签

  • 分类

  • 归档

JavaError-The-temporary-upload-location-[/tmp/tomcat.7104877156386249310.8070/work/Tomcat/localhost/ROOT]-is-not-valid

发表于 2018-02-25 | 更新于 2019-05-22 | 分类于 java

SpringBoot内置Tomcat缓存文件目录被意外删除导致异常

在项目中,一般会将文件临时保存到缓存目录

当时使用

1
2
File.createTempFile("tmp", ext,
(File) request.getServletContext().getAttribute(ServletContext.TEMPDIR))

创建临时文件时,项目一直运行正常,然而有一次报异常:

1
2
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: 
The temporary upload location [/tmp/tomcat.7104877156386249310.8070/work/Tomcat/localhost/ROOT] is not valid

检查文件目录,文件确实不在,检查代码,也未发现问题。实在不知道原因,只有重启了服务器,问题也就不再出现。

今天偶然查看官方文档,发现问题所在,也提供了解决方法

1
2
3
4
5
    If you choose to use Tomcat on CentOS be aware that, by default, a temporary directory is
used to store compiled JSPs, file uploads etc. This directory may be deleted by tmpwatch
while your application is running leading to failures. To avoid this, you may want to customize
your tmpwatch configuration so that tomcat.* directories are not deleted, or configure
server.tomcat.basedir so that embedded Tomcat uses a different location

前往目录 /etc/cron.daily/ 中,修改 tmpwatch 文件:

1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/sh
flags=-umc
/usr/sbin/tmpwatch "$flags" -x /tmp/.X11-unix -x /tmp/.XIM-unix \
-x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix \
-X '/tmp/hsperfdata_*' 10d /tmp \
-X '/tmp/tomcat.*' 10d /tmp
/usr/sbin/tmpwatch "$flags" 30d /var/tmp
for d in /var/{cache/man,catman}/{cat?,X11R6/cat?,local/cat?}; do
if [ -d "$d" ]; then
/usr/sbin/tmpwatch "$flags" -f 30d "$d"
fi
done

可以看到添加了一行

1
-X '/tmp/tomcat.*' 10d /tmp

mosquitto使用记录与服务器调试

发表于 2018-02-25 | 更新于 2019-05-22 | 分类于 mqtt

mosquitto使用记录:

mqtt:

启动:

1
mosquitto -c /etc/mosquitto/mosquitto.conf

加-d表示后台运行:

1
mosquitto -c /etc/mosquitto/mosquitto.conf -d

sub一个主题:

1
mosquitto_sub -h localhost -t test -d

pub一个消息到主题:

1
mosquitto_pub -h localhost -m "中文 的mqtt" -t test -d

重启:找到线程,kill

1
2
ps -A | grep mosquitto
kill -9 xxx

linux最大连接数设置

1
2
3
4
5
6
7
8
ulimit -n20000 -s512

ulimit -f unlimited
ulimit -t unlimited
ulimit -v unlimited
ulimit -n 1048576
ulimit -m unlimited
ulimit -u 1048576

Till now I have achieved 74K concurrent connections on a broker. I have configured the ulimit of broker server by editing sysctl.conf and limit.conf file.

  • vi /etc/sysctl.conf
1
2
3
4
5
6
fs.file-max = 10000000 
fs.nr_open = 10000000
net.ipv4.tcp_mem = 786432 1697152 1945728
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
net.ipv4.ip_local_port_range = 1000 65535
  • vi /etc/security/limits.conf
1
2
3
4
* soft nofile 10000000
* hard nofile 10000000
root soft nofile 10000000
root hard nofile 10000000

After this reboot your system.

mqtt启动后,需要开放对应端口的,则处理如下

CentOS 7.0默认使用的是firewall作为防火墙,使用iptables必须重新设置一下

1、直接关闭防火墙

1
2
3
systemctl stop firewalld.service #停止firewall

systemctl disable firewalld.service #禁止firewall开机启动

2、设置 iptables service

1
yum -y install iptables-services

如果要修改防火墙配置,如增加防火墙端口3306

1
vi /etc/sysconfig/iptables

增加规则

1
-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT

保存退出后

1
2
3
systemctl restart iptables.service #重启防火墙使配置生效

systemctl enable iptables.service #设置防火墙开机启动

最后重启系统使设置生效即可。

事务管理-spring事务使用注意点、数据库事务的隔离级别与锁

发表于 2018-02-25 | 更新于 2019-05-22 | 分类于 java

1. spring事务@Transactional的注意点

@Transactional注解可以标注在类和方法上,也可以标注在定义的接口和接口方法上。
如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以,spring建议我们将@Transaction注解在实现类上。
在方法上的@Transactional注解会覆盖掉类上的@Transactional。

注意:

  @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

  虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

  默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

2. 数据库事务中的隔离级别和锁

数据库事务在后端开发中占非常重要的地位,如何确保数据读取的正确性、安全性也是我们需要研究的问题。

ACID

首先总结一下数据库事务正确执行的四个要素(ACID):

原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做,不能只做一部分;
一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏;比如我们做银行转账的相关业务,A转账给B,要求A转的钱B一定要收到。如果A转了钱而B没有收到,那么数据库数据的一致性就得不到保障,在做高并发业务时要注意合理的设计。
隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因各种异常导致数据不一致或丢失。
事务隔离级别
大部分数据库事务操作都是并发执行的,这就可能遇到下面的几种问题:

丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,后果比较严重。一般是由于没加锁的原因造成的。
脏读(Dirty reads):一个事务A读取到了另一个事务B还没有提交的数据,并在此基础上进行操作。如果B事务rollback,那么A事务所读取到的数据就是不正确的,会带来问题。
不可重复读(Non-repeatable reads):在同一事务范围内读取两次相同的数据,所返回的结果不同。比如事务B第一次读数据后,事务A更新数据并commit,那么事务B第二次读取的数据就与第一次是不一样的。
幻读(Phantom reads):一个事务A读取到了另一个事务B新提交的数据。比如,事务A对一个表中所有行的数据按照某规则进行修改(整表操作),同时,事务B向表中插入了一行原始数据,那么后面事务A再对表进行操作时,会发现表中居然还有一行数据没有被修改,就像发生了幻觉,飘飘欲仙一样。
注意:不可重复读和幻读的区别是,不可重复读对应的表的操作是更改(UPDATE),而幻读对应的表的操作是插入(INSERT),两种的应对策略不一样。对于不可重复读,只需要采用行级锁防止该记录被更新即可,而对于幻读必须加个表级锁,防止在表中插入数据。有关锁的问题,下面会讨论。

为了处理这几种问题,SQL定义了下面的4个等级的事务隔离级别:

未提交读(READ UNCOMMITTED ):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
提交读(READ COMMITTED):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现丢失更新、脏读,但可能出现不可重复读、幻读;
可重复读(REPEATABLE READ):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
序列化(SERIALIZABLE):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。
隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。所以一般地,推荐使用REPEATABLE READ级别保证数据的读一致性。对于幻读的问题,可以通过加锁来防止。
MySQL支持这四种事务等级,默认事务隔离级别是REPEATABLE READ。Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别,所以Oracle数据库不支持脏读。Oracle数据库默认的事务隔离级别是READ COMMITTED。

各种锁

下面总结一下MySQL中的锁,有好几种分类。其它RDBMS也差不多是这样。

首先最重要的分类就是乐观锁(Optimistic Lock)和悲观锁(Pessimistic Lock),这实际上是两种锁策略。

乐观锁,顾名思义就是非常乐观,非常相信真善美,每次去读数据都认为其它事务没有在写数据,所以就不上锁,快乐的读取数据,而只在提交数据的时候判断其它事务是否搞过这个数据了,如果搞过就rollback。乐观锁相当于一种检测冲突的手段,可通过为记录添加版本或添加时间戳来实现。

悲观锁,对其它事务抱有保守的态度,每次去读数据都认为其它事务想要作祟,所以每次读数据的时候都会上锁,直到取出数据。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性,但随之而来的是各种开销。悲观锁相当于一种避免冲突的手段。
选择标准:如果并发量不大,或数据冲突的后果不严重,则可以使用乐观锁;而如果并发量大或数据冲突后果比较严重(对用户不友好),那么就使用悲观锁。

从读写角度,分共享锁(S锁,Shared Lock)和排他锁(X锁,Exclusive Lock),也叫读锁(Read Lock)和写锁(Write Lock)。

理解:

持有S锁的事务只读不可写。如果事务A对数据D加上S锁后,其它事务只能对D加上S锁而不能加X锁。
持有X锁的事务可读可写。如果事务A对数据D加上X锁后,其它事务不能再对D加锁,直到A对D的锁解除。
从锁的粒度角度,主要分为表级锁(Table Lock)和行级锁(Row Lock)。
表级锁将整个表加锁,性能开销最小。用户可以同时进行读操作。当一个用户对表进行写操作时,用户可以获得一个写锁,写锁禁止其他的用户读写操作。写锁比读锁的优先级更高,即使有读操作已排在队列中,一个被申请的写锁仍可以排在所队列的前列。
行级锁仅对指定的记录进行加锁,这样其它进程可以对同一个表中的其它记录进行读写操作。行级锁粒度最小,开销大,能够支持高并发,可能会出现死锁。

MySQL的MyISAM引擎使用表级锁,而InnoDB支持表级锁和行级锁,默认是行级锁。

还有BDB引擎使用页级锁,即一次锁定一组记录,并发性介于行级锁和表级锁之间。

三级锁协议

三级加锁协议是为了保证正确的事务并发操作,事务在读、写数据库对象是需要遵循的加锁规则。

一级封锁协议:事务T在修改数据R之前必须对它加X锁,直到事务结束方可释放。而若事务T只是读数据,不进行修改,则不需加锁,因此一级加锁协议下可能会出现脏读和不可重复读。
二级加锁协议:在一级加锁协议的基础上,加上这样一条规则——事务T在读取数据R之前必须对它加S锁,直到读取完毕以后释放。二级加锁协议下可能会出现不可重复读。
三级加锁协议:在一级加锁协议的基础上,加上这样一条规则——事务T在读取数据R之前必须对它加X锁,直到事务结束方可释放。三级加锁协议避免了脏读和不可重复读的问题。

java并发学习(一)

发表于 2017-09-06 | 更新于 2019-05-22 | 分类于 java

1. 进程和线程

进程,是运行在自己地址空间内的自包容程序。

而java并发系统,会共享内存和IO这样的资源,因此需要协调不同线程驱动的任务之间对资源的使用。

线程机制是在执行程序表示的单一进程中创建任务。这样的好处是操作系统的透明性。

java线程机制,抢占式,调度机会周期性地中断程序,将上下文切换到另一个线程。

线程在设计上的好处是,简化了设计,同时具有松散耦合。

2. 基本线程机制

一个线程是进程内的一个单一的顺序控制流,单进程可以同时拥有多个并发任务,其底层是切分CPU时间。

  • 定义任务: 继承Runnable,实现run方法。

  • yield()线程让步。

  • Thread类

  • 使用Executor,可以管理一部任务的执行,而无需显式管理线程生命周期,是启动线程的优选方法。

  • ExecutorService:

CachedThreadPool

FixedThreadPool:定义好有限的线程集,一次性预先执行代价高昂的线程分配,需要线程时从线程池中获取,省去了创建线程的开销。

SingleThreadPool:同时只有一条线程,顺序执行,序列化任务队列,可以省去贡献资源上的同步

  • 返回值,Callable接口,call()方法,Future对象,get()和isDone()方法。

  • 休眠:sleep()

  • 如果必须控制任务执行的顺序,最好的方式就是使用同步控制。

  • 优先级:getPriority(),setPriority(),set优先级是在run的开头部分设置,由于不同系统的有限级别难以一一适配,所以,调整优先级最好是只用这3种:MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY

  • 让步:yield(),

  • 后台线程:
    当所有非后台线程结束时,程序终止,并且杀死所有后台线程。
    setDeamon(true);
    通过编写定制的ThreadFactory可以定制由Executor创建的线程的属性(后台、优先、名称)

一个后台线程创建的任何线程,会自动设置为后台线程。
非后台的Executor通常是一种更好的方式,它控制所有任务可以同时被关闭。

  • 实现Runnable接口和使用Thread类的区别在于,使用接口,你可以继承另一个不同的类。

  • join(),在某个线程上调用,表示加入到某个线程,等待这个线程结束后,才开始执行。
    可使用interrupt()中断。

  • interrupt()时会抛出异常,然后就清除了interrupt的标记,这时调用isInterrupted()检查时就会是false。

  • 捕获异常:
    main主体放到try-catch语句中没有作用。

Thread.UncaughtExceptionHandler接口,允许你在每个Thread对象上都附上一个异常处理器。

Thread.UncaughtExceptionHandler.uncaughtExcetion()会在线程因未捕获的异常而临近死亡时被调用。

使用方法:

1,可以创建一个新的ThreadFactory,在每个新建的Thread对象上附上一个Thread.UncaughtExceptionHandler,然后将这个ThreadFactory传递给Executors创建新的ExecutorService。

2,如果知道要在代码中处处使用相同的异常处理,那么可以简单的在Thread类中设置一个静态域,并将这个处理器设置为默认的未捕获异常处理器。它只有在不存在线程专有的未捕获异常处理器时才被调用。

3. 共享受限资源

  • 本质:基本上所有并发模式在解决线程冲突问题的时候,都是采用序列化访问共享资源的方案。

  • 互斥量。
    对于某个特定对象而言,其所有synchronized方法共享同一个锁,可防止多个任务同时访问被编码为对象内存。

synchronized static方法可以在类的范围内防止对static数据的并发访问。

  • Brian同步规则:如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写线程都必须用相同的监视器锁同步。

  • 重点:如果一个类中有超过一个方法在处理临界数据,那么必须同步所有相关方法。每个访问临界共享资源的方法都必须被同步,否则它们就不会正确工作。

  • 使用显式的Lock对象:ReentrantLock

Lock的好处是可以在finally子句中维护抛出异常后的清理,同时具有更细粒度的控制力。

  • 平时用synchronized,需要特殊情况时使用Lock。

  • 原子性:原子性可以用于除long和double之外的所有基本类型之上的“简单操作”。
    为什么long和double不行?因为jvm可以将64位的读取和写入当做两个分离的32位操作来执行,这就产生了读写两个操作之间发生上下文切换的可能。

  • volitle,用于定义变量,提供原子性。它提供了变量在内存中的可视性,即更改会刷新到内存中,可以让别的线程看到。

  • 一般还是使用synchronized,只有在类中只有一个可变的域时,volitile才是安全的。

  • 原子类:
    Atomic
    可以一定程度的去除一些别的同步方法,但同步锁通常更安全。

  • 如何把一个不是线程安全的类变成线程安全?
    可以创造一个对象的Manager类,Manager类持有该对象,并控制对它的一切访问,唯一的public方法是get()对象的方法,并且是synchronized的。

  • 线程本地存储:ThreadLocal
    根除线程间变量共享,线程隔离。

4. 终结任务

  • cancle(),isCancled()

  • 在阻塞时终结:

进入阻塞几种方式:

1,sleep

2,wait

3,等待某个输入输出完成

4,视图获得对象锁而还没有获取到

  • 中断:interrupt()

  • 安全离开线程run方法的方式:

1,calcled标志,cancle()

2,interrupt()

3,Executor.shotdownNow()

4,Executor,submit(),Future,cancle()

  • 中断时要注意I/O的关闭等底层资源的关闭,因为不能中断正在试图获取synchronized锁或者I/O操作的线程。

  • 一个任务应该能够调用在同一个对象中的其他synchronized方法,而这个任务已经持有锁了。

  • interrupt(),可以中断被互斥锁所阻塞的调用,并且,interrupt()只能在任务处于阻塞时有效。

  • 清除中断状态:如果不清除,那么它可能会提醒你2次。

  • 清理策略:try-finally子句

5. 线程间协作

  • wait(),notifyAll()

  • 关键:wait()会释放锁,而sleep()和yield()则不会

  • 要点:

1,wait(),notifyAll(),notify()是基于类Object的,而不是属于Thread的一部分。而且,只能在同步控制方法或同步控制块里调用wait(),notifyAll(),notify()。

2,必须用一个检查感兴趣的条件的while循环包围wait(),也即是说,不满足我条件,我就继续等的意思。

[microservices]01-Spring boot开发微服务

发表于 2017-03-29 | 更新于 2019-05-22 | 分类于 microservices

Spring boot开发微服务

  • github项目地址: microservices

1. 为什么使用spring boot?

  • 1) spring boot有pivotal和netfix背书,是一套完整的企业级应用的开发方案,天然集成分布式云架构spring-cloud。
  • 2) spring-boot的完全抛弃以往java项目配置文件过多的“陋习”,开启一个项目只需几行代码。
  • 3) Sprinboot允许项目使用内嵌的tomcat像启动普通java程序一样启动一个web项目.由于有了这个特性,项目不再需要打war包部署到tomcat,而是打成jar包,直接使用java -jar命令启动.

2. 开始第一个springboot项目

在一切开始之前,我们首先要知道如何开始一个springboot项目。

2.1. 创建一个空的maven项目,pom.xml中添加maven依赖

1
2
3
4
5
6
7
8
9
10
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2.2. 编写项目启动入口App.java

1
2
3
4
5
6
7
@SpringBootApplication
public class App
{
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
}

ok! done!
这样就已经能直接使用spring boot了.
启动App.java,spring boot就会使用内置的tomcat直接在本机的8080端口开启一个服务。

2.3. 再进一步,为应用引入spring mvc

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class SampleController {


@RequestMapping("/")
@ResponseBody
String home() {
String data = "";
return "Hello World!";
}
}

启动App.java,访问localhost:8080, 即可遇见“Hello World!”

[microservices]06-Jenkins执行自动化的构建

发表于 2017-03-29 | 更新于 2019-05-22 | 分类于 microservices

[microservices]06-Jenkins执行自动化的构建

  • github项目地址: microservices

1. Jenkins安装运行

1.1 docker方式安装运行

docker拉取镜像:

1
docker pull jenkinsci/jenkings

启动容器的方式运行jenkins:

1
docker run -d -u root -m 500m -p 8080:8080 -v ~/jenkins:/var/jenkins_home --name jenkins jenkinsci/jenkins

后台查看jenkins日志:

1
docker logs -f jenkins

会看到一条解锁jenkins的key:

1
unlock jenkins:c1bbf6aaa7aa4bd99cfcc0fb4a56ec83

访问:http://localhost:8080,首次访问,需要输入key
复制unlock jenkins key,输入,完成.

后面就是安装插件,可以根据自己需要安装,也可以安装jenkins建议的插件.

1.2 war包方式部署运行

jenkins官网,下载最新稳定版本jenkins的war包.

放进准备好的tomcat的webapps下,启动tomcat即可运行。

1.3 Centos的yum安装

设置yum源

1
2
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key

安装:

1
yum install jenkins

修改端口和用户(可选,如果有需要修改端口和运行jenkins用户的需要,可以修改):

1
vi /etc/sysconfig/jenkins

启动jenkins:

1
service jenkins start

设置开机启动

1
chkconfig jenkins on

2. jenkins必要设置

基本上安装后第一次运行按照jenkins建议的插件,基本够用。有特殊需要再添加。

要使用git,需要安装git相关插件。

新版本jenkins不再默认设置maven,有需要maven功能的,可以在设置的工具管理页面配置一个maven。

3. Jenkins搭建持续集成系统

这个步骤的目标是实现使用jenkins进行项目构建:从代码仓库gitlab/github/svn等拉源代码,通过maven进行编译打包,最后将构建成功输出的程序包进行归档。

3.1 创建构建任务

创建一个新任务,填任务名称,选择构建什么项目

配置git仓库。如果需要账号密码认证,填上。

添加一个构建步骤:

选择maven targets的选项:

添加一个构建后步骤,将jar包归档

输入框中输入需要存档的文件:

3.2 手工构建

3.3 实现自动执行构建

我们希望代码提交到git后,自动触发构建。

构建触发器,选择“Poll SCM”:

输入“H/10 ”。表示有最新代码提交后,等10分钟就会触发自动构建

4. 使用jenkins实现自动化发布

4.1 自动发布jar包

在输入框中输入shell命令来运行项目:

1
java -jar $WORKSPACE/xxx/target/xxx.jar --server.port=10800

仅仅是这样还不够,需要考虑正在运行中的应用的关闭、后台启动、生产环境如何部署发布等众多细节,继续完善的话,是一条路。另一条路是使用docker。

4.2 自动发布Docker容器

使用docker容器发布项目,我们要达到的目标是:
1)开发人员将代码同步到git仓库,随后触发jenkins自动构建
2)jenkins调用maven进行构建,生成jar包。
3)根据当前构建过程,生成一个docker镜像,将其推送至局域网内的docker registry中,供生产环境随时获取并发布。
4)根据生成的docker镜像,运行一个docker容器(若当前存在该镜像,则先移除该镜像)

前2步在前面的步骤已实现,以下将完成后两步:

在构建后步骤中选择shell脚本,在脚本框中输入具体的docker镜像构建和推送:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
API_NAME="spring-boot-docker"
API_VERSION="1.0.0"
API_PORT=8101
IMAGE_NAME="127.0.0.1:5000/com.iyihua/$API_NAME:$BUILD_NUMBER"
CONTAINER_NAME=$API_NAME-$API_VERSION

cd $WORKSPACE/target
cp classes/Dockerfile .

docker build -t $IMAGE_NAME .

docker push $IMAGE_NAME

cid=$(docker ps | grep "$CONTAINER_NAME" | awk '{print $1}')
if [ "$cid" != "" ]; then
docker rm -f $cid
fi

docker run -d -p $API_PORT:$API_PORT --name $CONTAINER_NAME $IMAGE_NAME

rm -f Dockerfile

[microservices]05-Gitlab作为代码仓库

发表于 2017-03-29 | 更新于 2019-05-22 | 分类于 microservices

[microservices]05-Gitlab作为代码仓库

  • github项目地址: microservices

1. Gitlab的Docker安装

gitlab安装的过程稍微麻烦,但如果使用docker安装的话,就会非常简单。

下载镜像:

1
docker pull gitlab/gitlab-ce

下载镜像,并启动容器:

1
docker run -d -m 512m -h gitlab.iyihua.com -p 22:22 -p 80:80 -v ~/gitlab/etc:/etc/gitlab -v ~/gitlab/log:/var/log/gitlab -v ~/gitlab/opt:/var/opt/gitlab --name gitlab gitlab/gitlab-ce

说明:
(1) -h, 设置gitlab访问域名
(2) -p, 指定映射端口,22表示ssh端口,80表示http端口,
(3) -m, 指定目录映射
(4) -v, 指定分配多少内存来运行容器

启动后,访问首页gitlab.iyihua.com,会定向到修改管理员密码页面,修改完管理员密码后,会重定向到登陆页面。

2. git使用

gitlab安装好后,和github的使用并无二致。

git本地使用需要设置好git全局设置

1
2
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

如果要用ssh拉取和提交代码,需要设置好ssh公钥

1
2
3
4
5
6
ssh-keygen -t rsa -C "admin@example.com"

#可通过以下命令查看ssh key:
cat ~/.ssh/id_rsa.pub

把得到的key进入gitlab ssh密钥管理界面,输入这个ssh key.

3. more

gitlab运行需要的资源比较多,一个800m的虚拟机用docker跑gitlab比较吃力,经常会出现页面502.建议最低内存是1G。

如果仅仅是个人使用,可以直接使用github代替,或者使用GOGS代替.

[microservices]04-Docker作为服务运行容器

发表于 2017-03-29 | 更新于 2019-05-22 | 分类于 microservices

[microservices]04-Docker作为服务运行容器

  • github项目地址: microservices

1. docker运行环境

1.1 操作系统选择

对于linux系统,Docker 需要安装在 64 位的平台,并且内核版本不低于 3.10。 CentOS 7 满足最低内核的要求.
这里选用centos7系统作为docker的运行环境.

在windows系统下,可以使用vm虚拟机虚拟centos7,运行docker.

1.2 linux操作系统配置

1.2.1 防火墙

centos7使用firewall作为防火墙,docker向外提供服务,需要开通必要端口。
firewall开放端口命令:

1
2
3
4
#开放8080端口
firewall-cmd --zone=public --add-port=8080/tcp --permanent
#重启防火墙
firewall-cmd --reload

当然,如果习惯于iptables防火墙,也可以关闭firewall,启用iptables防火墙:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#1、关闭firewall:
systemctl stop firewalld.service #停止firewall
systemctl disable firewalld.service #禁止firewall开机启动
#查看默认防火墙状态(关闭后显示notrunning,开启后显示running)
firewall-cmd --state

#2、iptables防火墙(这里iptables已经安装,下面进行配置)
$ vi /etc/sysconfig/iptables #编辑防火墙配置文件

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT[0:0]
:OUTPUT ACCEPT[0:0]
-A INPUT -m state--state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -jACCEPT
-A INPUT -i lo -jACCEPT
-A INPUT -p tcp -mstate --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -jACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 8080-j ACCEPT
-A INPUT -j REJECT--reject-with icmp-host-prohibited
-A FORWARD -jREJECT --reject-with icmp-host-prohibited
COMMIT
:wq! #保存退出

2. 安装并启动docker

从2017-03-01起,新版的docker分为了CE和EE两个版本,CE是社区版,EE是企业版. 我们这里使用CE版即可.

2.1 安装docker-ce

2.1.1. Set up the repository

Set up the Docker CE repository on CentOS:

1
2
3
4
5
6
7
sudo yum install -y yum-utils

sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

sudo yum makecache fast

2.1.2. Get Docker CE

Install the latest version of Docker CE on CentOS:

1
sudo yum -y install docker-ce

Start Docker:

1
sudo systemctl start docker

docker开机启动:

1
systemctl  enable docker.service

2.1.3. Test your Docker CE installation

Test your installation:

1
sudo docker run hello-world

2.2 添加镜像加速

假如你有阿里云账号,可配置镜像加速

1
vim /etc/docker/daemon.json

添加:

1
2
3
{
"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"]
}

“https://xxxxxxxx.mirror.aliyuncs.com"
是你的专属镜像加速地址,可以在阿里云管理页面找到.

重启docker:

1
systemctl restart docker

3. docker镜像操作和docker容器运行使用

3.1 拉取镜像,并启动容器

1
2
3
4
5
6
# 查看当前有什么镜像
docker images
# 拉取centos系统镜像
docker pull centos
# 启动刚刚拉取的镜像
docker run -it centos /bin/bash

3.2 运行docker容器时的一些常用命令和选项

  • 列出当前运行中的容器

    1
    docker ps
  • 如果要列出所有状态(包括已停止)的容器,添加-a参数

    1
    docker ps -a
  • 进入运行中的容器

    1
    docker attach 容器id
  • 停止容器

    1
    docker stop 容器id
  • 删除容器

    1
    docker rm 容器id
  • 删除镜像

    1
    docker rmi 镜像名称
  • 将宿主机上的磁盘挂载到容器中,也即“目录映射”

    1
    docker run -i -t -v /home/software:/mnt/software centos /bin/bash

“-v /home/software:/mnt/software”表示将容器的/mnt/software目录挂载到宿主机的/home/software目录.

4. 手工制作java镜像

4.1 上传java rpm安装包到/home/software目录

这里使用已下载好的java8 64位安装包:jdk-8u65-linux-x64.rpm

4.2 启动容器

1
docker run -i -t -v /home/software:/mnt/software centos /bin/bash

4.3 运行安装包

/mnt/software映射到宿主机的/home/software,说明容器内的/mnt/software已有jdk-8u65-linux-x64.rpm文件,直接rpm运行安装java8

1
2
cd /mnt/software
rpm -ivh jdk-8u65-linux-x64.rpm

4.4 查看是否安装成功

1
java -version

4.5 提交镜像

再打开一个终端,查看当前运行的容器

1
2
3
$docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3443c1097867 127.0.0.1:5000/com.iyihua/spring-boot-docker:1.0.0 "/bin/sh -c 'java ..." 6 days ago Up 6 days 0.0.0.0:18101->8101/tcp objective_shannon

获取容器id(3443c1097867),提交镜像
docker commit 3443c1097867 iyihua/java

4.6 验证镜像

1
docker run -rm iyihua/java java -version

“-rm”参数表示不想保留容器,运行结束后即删除退出

5. 使用Dockerfile构建镜像

Dockerfile就是把手工构建镜像的过程写成一段自动执行的脚本,最终生成镜像。

5.1 Dockerfile构建java镜像

也就是把之前手工构建的java镜像的步骤放到脚本里,脚本如下:

1
2
3
4
5
FROM centos:latest
MAINTAINER "iyihua"<wanglvyihua@gmail.com>
ADD jdk-8u65-linux-x64.rpm /usr/local
RUN rpm -ivh /usr/local/jdk-8u65-linux-x64.rpm
CMD java -version

这个Dockerfile顺利运行要求Dockerfile所在宿主机目录含有一个准备好的java安装包jdk-8u65-linux-x64.rpm。

  • 如果构建的镜像与之前构建过的镜像的仓库名、标签名相同,之前的镜像的仓库名和标签名就会更新为. 我们可以使用docker tag命令来修改镜像仓库名和标签名。
    1
    docker tag 3443c1097867 iyihua/java:1.0.0

6. 使用Docker Registry管理镜像

我们默认就是从Docker Hub下载公共镜像。官方的Docker Hub也为我们提供了一个私有仓库,可以让内部人员通过这个仓库上传下载内部镜像,不过免费用户只能创建一个私有仓库。

不过,我们可以通过Docker Registry开源项目,在内部搭建一个私有镜像注册中心。

6.1 注册登录Docker Hub

通过浏览器注册登录Docker Hub,手动创建一个私有仓库。

然后我们就可以通过客户端login并push镜像到仓库。

登录:

1
docker login

推送镜像:

1
docker push iyihua/java

6.2 搭建Docker Registry

6.2.1 启动

通过docker本身的镜像,就可以简单的在本地搭建起Docker Registry:

1
2
3
docker run -d -p 5000:5000 --restart=always --name registry \
-v `pwd`/data:/var/lib/registry \
registry:2

这样就会在127.0.0.1:5000的地址启动起Docker Registry服务.

  • 参数说明:
    (1)-d表示后台运行
    (2)-p是宿主机与容器的端口映射
    (3)-v是宿主机与容器的目录映射,也即目录挂载

6.2.2 重命名镜像标签

docker push默认的镜像中心是Docker Hub,没有指明目标地址的镜像,其完整的镜像名称是“docker.io/iyihua/java”.
如果我们打算将iyihua/java推送到本地的Docker Registry,则需要将镜像名称修改为127.0.0.1:5000/iyihua/java.

使用docker tag命令更名:

1
docker tag 3443c1097867 127.0.0.1:5000/iyihua/java

使用docker push命令推送:

1
docker push 127.0.0.1:5000/iyihua/java

7. Spring Boot与Docker整合

Spring Boot与Docker整合的目标是构建spring boot应用程序时可同时生成Docker镜像,并将此镜像推送至Docker Registry,整个构建过程依然使用maven来完成

现在假定已有一个普通spring boot应用spring-boot-docker.

7.1 为spring boot程序添加Dockerfile

在resources目录下添加Dockerfile:

1
2
3
4
5
FROM java
MAINTAINER "iyihua"<wanglvyihua@gmail.com>
ADD spring-boot-docker-1.0.0.jar app.jar
EXPOSE 8101
CMD java -jar app.jar

7.2 使用maven构建Dockerfile

在pom文件中添加docker相关插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.10</version>
<configuration>
<imageName>${docker.registry}/${project.groupId}/${project.artifactId}:${project.version}</imageName>
<dockerDirectory>${project.build.outputDirectory}</dockerDirectory>
<resources>
<resource>
<!-- <targetPath>/</targetPath> -->
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>

需要添加的属性配置

1
2
3
<properties>
<docker.registry>127.0.0.1:5000</docker.registry>
</properties>

7.3 构建并推送

1
mvn docker:build docker:push

7.4 docker容器启动应用

1
docker run -d -p 18101:8101 127.0.0.1:5000/com.iyihua/spring-boot-docker:1.0.0
  • p参数指明宿主机和容器的端口映射
  • d参数指明要后台运行

7.5 调整docker容器内存

查看docker容器运行情况

1
docker stats

运行应用时调整内存限制

1
docker run -d -p 18101:8101 -m 512m 127.0.0.1:5000/com.iyihua/spring-boot-docker:1.0.0

  • m参数指明内存调整为多少

  • demo代码可以在这里获取:
    spring-boot-docker sample项目

或者:
microservices/spring-boot-docker

附:常见问题:

(1)docker iptables failed no chain/target/match by that name

重启docker即可:

1
systemctl restart docker

(2)当docker run centos,出现:centos exec user process caused “permission denied”

需要加一个参数:–privileged

结果命令变为:

1
docker run --privileged -i -t centos /bin/bash

说明:

1
2
3
4
5
大约在0.6版,privileged被引入docker。
使用该参数,container内的root拥有真正的root权限。
否则,container内的root只是外部的一个普通用户权限。
privileged启动的容器,可以看到很多host上的设备,并且可以执行mount。
甚至允许你在docker容器中启动docker容器。

建议:

1
如果总是需要privileged才能正常运行docker,那么可能你安装的docker可能有问题,建议重新安装最新的docker-ce,将不再需要privileged参数.

(3)docker build cannot allocate memory

这个问题的终极解决办法,还是重启docker,或者重启服务器;

[microservices]03-Zookeeper注册发现服务

发表于 2017-03-29 | 更新于 2019-05-22 | 分类于 microservices

[microservices]03-Zookeeper注册发现服务

  • github项目地址: microservices

1. zookeeper原理介绍

  • 分布式服务框架 Zookeeper – 管理分布式环境中的数据

2. 基础微服务简单架构与demo说明

  • api-gateway实例:dispatcher(/microservices/api-gateway/dispatcher)
  • web-client实例:web-client(/microservices/apps/web-client)
  • api-service实例:api-demo(/microservices/services/api-demo)

  • zookeeper:注册中心

  • spring boot应用api-demo,作为一个service微服务实例,依赖一个服务注册组件service-registry(/microservices/services/service-registry),实现服务向zookeeper注册。启动api-demo即可向外提供服务并注册到zookeeper。
  • Nodejs应用dispatcher,作为api网关,它使用一个node-zookeeper-client组件,连接zookeeper,根据client传递过来的service名称,发现服务,并转发请求到目标服务中。
  • web-client,即是前端web客户端,它只向api网关dispatcher发送请求,并且在请求的header中传递Service-Name字段,指明要请求的服务名称.

3. 运行demo

3.1 bin/zkServer.sh start启动zookeeper

没有zookeeper请先下载:http://zookeeper.apache.org/releases.html

3.2 打包运行api-demo

1
2
3
cd ../microservices/services/api-demo
mvn clean package
java -jar api-demo-1.0.0.jar

或者直接导入项目,运行ApiDemoApplication启动.

3.3 运行api网关dispatcher

1
2
cd ../microservices/api-gateway/dispatcher
node app.js

如果想每次更改代码后即刻生效,可使用supervisor模块启动应用:

1
2
npm install supervisor -g
supervisor app.js

如果需要Node应用在发生异常停止时能够重新启动,可使用forever模块启动应用:

1
2
npm install forever -g
forever app.js

forever app.js表示在前台启动,想要后台启动,使用命令:

1
forever start app.js

3.4 运行web-client,访问首页,进行服务请求.

1
2
cd ../microservices/api-gateway/dispatcher
node app.js

访问:http://localhost:9001/

[microservices]02-Nodejs&Express作为微服务api网关

发表于 2017-03-29 | 更新于 2019-05-22 | 分类于 microservices

[microservices]02-Nodejs&Express作为微服务api网关

  • github项目地址: microservices

1. 安装Node、NPM和Express

安装Nodejs后,npm会一起安装,然后npm install express安装Express.

2. 使用Express框架开发web应用

express使用例子:

1
2
3
4
5
6
7
var express = require('express');
var port = 1234;
var app = express();
app.use(express.static('.'));
app.listen(port, function(){
console.log('server is running at %d', port);
});

express进行简易路由:

1
2
3
app.get('/hello', function(req, res){
res.send('Hello');
});

3. 搭建Nodejs集群环境

利用服务器的多核CPU,让每个CPU都运行一个Node.js进程,例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var cluster = require('cluster');  
var express = require('express');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
// Create a worker
cluster.fork();
}
} else {
// Workers share the TCP connection in this server
var app = express();

app.get('/', function (req, res) {
res.send('Hello World!');
});

// All workers use this port
app.listen(8080);
}

4. 使用Node.js实现反向代理,作为统一服务网关

使用Node作为api网关,原理是利用Node的http-proxy模块来启动代理服务器,实现反向代理。
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var http = require('http');
var httpProxy = require('http-proxy');
var PORT = 3000;

var proxy = httpProxy.createProxyServer();
proxy.on('error', function(err, req, res){
res.end();//当代理的请求发生错误时,输出空白的相应数据
});

var app = http.createServer(function(req, res) {
proxy.web(req, res, {
target: 'http://localhost:8080' //代理的目标地址
});
});
app.listen(PORT, function(){
console.log('server is running at %d', port);
});

  • 下一步,就是服务的注册和发现,Nodejs网关将需要增加服务发现的功能
1234…9
Wanglv Yihua

Wanglv Yihua

82 日志
20 分类
206 标签
RSS
© 2019 Wanglv Yihua
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Muse v6.4.1