SpringCloud


一. Eureka(服务注册与发现)

1. 概念

什么是Eureka?

        Eureka是Netflix开源的服务注册与发现组件。

Eureka的架构?

        Eureka是CS架构,分为Eureka Server和Eureka Client,Server端是服务注册中心的角色,Client可以向Server端注册自己的信息,或从Server端拉取其他服务列表。

        Client周期性向Server端发送心跳,表明服务实例仍处于健康状态。

Eureka的工作原理?

  • 微服务启动时,会向 Eureka Server 注册自己的信息(例如 IP 地址、端口号、服务名等)
  • Eureka Server 会存储并维护这些注册信息
  • 其他微服务通过 Eureka Server 可以获取到需要调用的服务的位置信息,从而进行服务间的通信

2. 单机Eureka搭建步骤

2.1 搭建Eureka Server

(1)创建一个Maven模块

(2)引入eureka-server的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

(3)启动类上加@EnableEurekaServer

(4)编写application.yml配置文件,添加 服务名、地址、端口

server:
  port: 10086
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url: 
      defaultZone: http://127.0.0.1:10086/eureka

(5)启动服务,并访问 http://127.0.0.1:10086 测试是否搭建成功

2.2 服务注册

(1)在user服务中引入eureka-client的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

(2)修改配置文件,添加服务名称和Eureka Server端地址

spring:
  application:
    name: userservice
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

2.3 服务发现

(1)在order服务中引入eureka-client的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

(2)修改配置文件,添加服务名称和Eureka Server端地址

spring:
  application:
    name: userservice
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

(3)服务拉取和负载均衡

二. Zookeeper(服务注册与发现)

根据黑马文档安装Zookeeper

1. 初识Zookeeper

Zookeeper是Apache下一个分布式协调服务,用于解决分布式系统中的一致性和协作问题。

Zookeeper提供的功能主要有:

  • 注册中心
  • 配置中心
  • 分布式锁

Zookeeper 数据模型

  • Zookeeper是一个树形目录服务,每个节点都被称作ZNode,都可以保存自己的数据和节点信息
  • 节点可以有子节点,同时也允许少量(1MB)数据存储在该节点下
  • 节点可以分为四大类

2. Zookeeper 命令操作

2.1 服务端常用命令

2.2 客户端常用命令

3. Zookeeper JavaAPI操作

Curator是Apache Zookeeper的JavaAPI客户端库

官网:http://curator.apache.org/

3.1 建立连接和节点增删改查

package com.itheima.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class CuratorTest {


    private CuratorFramework client;

    /**
     * 建立连接
     */
    @Before
    public void testConnect() {

        /*
         *
         * @param connectString       连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"
         * @param sessionTimeoutMs    会话超时时间 单位ms
         * @param connectionTimeoutMs 连接超时时间 单位ms
         * @param retryPolicy         重试策略
         */
       /* //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
        //1.第一种方式
        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",
                60 * 1000, 15 * 1000, retryPolicy);*/
        //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
        //2.第二种方式
        //CuratorFrameworkFactory.builder();
        client = CuratorFrameworkFactory.builder()
                .connectString("192.168.149.135:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy)
                .namespace("itheima")
                .build();

        //开启连接
        client.start();

    }
//==============================create=============================================================================

    /**
     * 创建节点:create 持久 临时 顺序 数据
     * 1. 基本创建 :create().forPath("")
     * 2. 创建节点 带有数据:create().forPath("",data)
     * 3. 设置节点的类型:create().withMode().forPath("",data)
     * 4. 创建多级节点  /app1/p1 :create().creatingParentsIfNeeded().forPath("",data)
     */
    @Test
    public void testCreate() throws Exception {
        //2. 创建节点 带有数据
        //如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储
        String path = client.create().forPath("/app2", "hehe".getBytes());
        System.out.println(path);

    }

    @Test
    public void testCreate2() throws Exception {
        //1. 基本创建
        //如果创建节点,没有指定数据,则默认将当前客户端的ip作为数据存储
        String path = client.create().forPath("/app1");
        System.out.println(path);

    }

    @Test
    public void testCreate3() throws Exception {
        //3. 设置节点的类型
        //默认类型:持久化
        String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
        System.out.println(path);


    }

    @Test
    public void testCreate4() throws Exception {
        //4. 创建多级节点  /app1/p1
        //creatingParentsIfNeeded():如果父节点不存在,则创建父节点
        String path = client.create().creatingParentsIfNeeded().forPath("/app4/p1");
        System.out.println(path);
    }
//===========================get================================================================================

    /**
     * 查询节点:
     * 1. 查询数据:get: getData().forPath()
     * 2. 查询子节点: ls: getChildren().forPath()
     * 3. 查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath()
     */

    @Test
    public void testGet1() throws Exception {
        //1. 查询数据:get
        byte[] data = client.getData().forPath("/app1");
        System.out.println(new String(data));
    }

    @Test
    public void testGet2() throws Exception {
        // 2. 查询子节点: ls
        List<String> path = client.getChildren().forPath("/");

        System.out.println(path);
    }

    @Test
    public void testGet3() throws Exception {


        Stat status = new Stat();
        System.out.println(status);
        //3. 查询节点状态信息:ls -s
        client.getData().storingStatIn(status).forPath("/app1");

        System.out.println(status);

    }

    //===========================set================================================================================

    /**
     * 修改数据
     * 1. 基本修改数据:setData().forPath()
     * 2. 根据版本修改: setData().withVersion().forPath()
     * * version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。
     *
     * @throws Exception
     */
    @Test
    public void testSet() throws Exception {
        client.setData().forPath("/app1", "itcast".getBytes());
    }


    @Test
    public void testSetForVersion() throws Exception {

        Stat status = new Stat();
        //3. 查询节点状态信息:ls -s
        client.getData().storingStatIn(status).forPath("/app1");


        int version = status.getVersion();//查询出来的 3
        System.out.println(version);
        client.setData().withVersion(version).forPath("/app1", "hehe".getBytes());
    }

    //===========================delete================================================================================

    /**
     * 删除节点: delete deleteall
     * 1. 删除单个节点:delete().forPath("/app1");
     * 2. 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath("/app1");
     * 3. 必须成功的删除:为了防止网络抖动。本质就是重试。  client.delete().guaranteed().forPath("/app2");
     * 4. 回调:inBackground
     * @throws Exception
     */


    @Test
    public void testDelete() throws Exception {
        // 1. 删除单个节点
        client.delete().forPath("/app1");
    }

    @Test
    public void testDelete2() throws Exception {
        //2. 删除带有子节点的节点
        client.delete().deletingChildrenIfNeeded().forPath("/app4");
    }
    @Test
    public void testDelete3() throws Exception {
        //3. 必须成功的删除
        client.delete().guaranteed().forPath("/app2");
    }

    @Test
    public void testDelete4() throws Exception {
        //4. 回调
        client.delete().guaranteed().inBackground(new BackgroundCallback(){

            @Override
            public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
                System.out.println("我被删除了~");
                System.out.println(event);
            }
        }).forPath("/app1");
    }

    @After
    public void close() {
        if (client != null) {
            client.close();
        }
    }


}

3.2 Watch事件监听

package com.itheima.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class CuratorWatcherTest {


    private CuratorFramework client;

    /**
     * 建立连接
     */
    @Before
    public void testConnect() {

        /*
         *
         * @param connectString       连接字符串。zk server 地址和端口 "192.168.149.135:2181,192.168.149.136:2181"
         * @param sessionTimeoutMs    会话超时时间 单位ms
         * @param connectionTimeoutMs 连接超时时间 单位ms
         * @param retryPolicy         重试策略
         */
       /* //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
        //1.第一种方式
        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.149.135:2181",
                60 * 1000, 15 * 1000, retryPolicy);*/
        //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
        //2.第二种方式
        //CuratorFrameworkFactory.builder();
        client = CuratorFrameworkFactory.builder()
                .connectString("192.168.149.135:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy)
                .namespace("itheima")
                .build();

        //开启连接
        client.start();

    }

    @After
    public void close() {
        if (client != null) {
            client.close();
        }
    }

    /**
     * 演示 NodeCache:给指定一个节点注册监听器
     */

    @Test
    public void testNodeCache() throws Exception {
        //1. 创建NodeCache对象
        final NodeCache nodeCache = new NodeCache(client,"/app1");
        //2. 注册监听
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("节点变化了~");

                //获取修改节点后的数据
                byte[] data = nodeCache.getCurrentData().getData();
                System.out.println(new String(data));
            }
        });

        //3. 开启监听.如果设置为true,则开启监听是,加载缓冲数据
        nodeCache.start(true);


        while (true){

        }
    }



    /**
     * 演示 PathChildrenCache:监听某个节点的所有子节点们
     */

    @Test
    public void testPathChildrenCache() throws Exception {
        //1.创建监听对象
        PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);

        //2. 绑定监听器
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                System.out.println("子节点变化了~");
                System.out.println(event);
                //监听子节点的数据变更,并且拿到变更后的数据
                //1.获取类型
                PathChildrenCacheEvent.Type type = event.getType();
                //2.判断类型是否是update
                if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
                    System.out.println("数据变了!!!");
                    byte[] data = event.getData().getData();
                    System.out.println(new String(data));

                }
            }
        });
        //3. 开启
        pathChildrenCache.start();

        while (true){

        }
    }





    /**
     * 演示 TreeCache:监听某个节点自己和所有子节点们
     */

    @Test
    public void testTreeCache() throws Exception {
        //1. 创建监听器
        TreeCache treeCache = new TreeCache(client,"/app2");

        //2. 注册监听
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                System.out.println("节点变化了");
                System.out.println(event);
            }
        });

        //3. 开启
        treeCache.start();

        while (true){

        }
    }

}

3.3 分布式锁实现

  • “临时”是因为 如果是永久节点获取锁的机器宕机后,节点无法删除、锁无法释放;但是临时节点会话结束后会自动删除
  • “顺序”是为了找最小节点,所以排了个顺序

火车票案例:

public class Ticket12306 implements Runnable{

    private int tickets = 10;//数据库的票数

    private InterProcessMutex lock ;


    public Ticket12306(){
        //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
        //2.第二种方式
        //CuratorFrameworkFactory.builder();
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.149.135:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy)
                .build();

        //开启连接
        client.start();

        lock = new InterProcessMutex(client,"/lock");
    }

    @Override
    public void run() {

        while(true){
            //获取锁
            try {
                lock.acquire(3, TimeUnit.SECONDS);
                if(tickets > 0){

                    System.out.println(Thread.currentThread()+":"+tickets);
                    Thread.sleep(100);
                    tickets--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                //释放锁
                try {
                    lock.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }

    }
}
public class LockTest {

    public static void main(String[] args) {
        Ticket12306 ticket12306 = new Ticket12306();

        //创建客户端
        Thread t1 = new Thread(ticket12306,"携程");
        Thread t2 = new Thread(ticket12306,"飞猪");

        t1.start();
        t2.start();
    }

}

4. Zookeeper 集群

集群搭建详见黑马文档

三.  Feign(服务调用)

1. Feign代替RestTemplate

Feign是什么?

        Feign是一个声明式的Http客户端,作用是 优雅的发送http请求。

        (声明式指的是 以接口或注解的方式来定义对外部服务的访问)

Feign和OpenFeign的区别?

        OpenFeign是对Feign的增强,使其能更好的支持SpringBoot和SpringCloud,比如SpringMvc、Ribbon、Hystrix。

2. Feign快速入门

(1)引入OpenFeign依赖

(2)在主启动类上添加 @EnableFeignClients注解

(3)编写Feign客户端

package cn.itcast.order.client;

import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

// 在order-service中新建一个接口,内容如下

@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

(4)测试

3. Feign使用优化

Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:

  • URLConnection:默认实现,不支持连接池
  • Apache HttpClient :支持连接池
  • OKHttp:支持连接池

因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。

使用步骤:

(1)引入feign-httpClient依赖

(2)配置文件开启HttpClient功能,配置连接池参数

feign:
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

四. Ribbon(负载均衡)

1. 负载均衡原理

@LoadBalance的作用是:标记通过RestTemplate发送的请求要被Ribbon处理

Ribbon底层原理总结(@LoadBalanced)

        Ribbon的底层是使用了一个拦截器,拦截了所有RestTemplate发起的请求,使用请求中的服务名到注册中心拉取服务列表并存储,再通过负载均衡算法选取一台服务器,最后修改请求地址。

2. 负载均衡策略

负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类

五. Gateway(网关)

1. 基础概念

Zuul和Gateway的区别?

        Zuul是Netflix开发的旧版网关服务,目前已经处于维护状态,不再更新新功能;

        Gateway是Spring Cloud生态系统中的新一代API网关,在性能、灵活性和功能方面提供了更多的优势

为什么需要网关?

        Gateway网关是我们服务的守门神,所有微服务的统一入口。

        网关的核心功能特性

                请求路由

                权限控制

                限流

        (主要是路由鉴权)

2. Gateway快速入门

(1)创建项目,引入nacos服务发现和gateway依赖

(2)配置application.yml,包括服务基本信息、nacos地址、路由

(3)测试

3. 断言工厂

        快速入门中用的Path=/user/** 就是是Path断言工厂,是由PathRoutePredicateFactory 来处理的。类似的断言工厂还有十几个,我们只需要掌握path断言工厂就可以了。

4. 过滤器工厂

4.1 过滤器的作用

GatewayFilter的作用是:对路由的请求或响应做加工处理,比如 加请求头

4.2 过滤器的种类

请求头过滤器示例:

默认过滤器示例:

4.3 全局过滤器

全局过滤器的作用?

        全局过滤器的作用也是处理一切进入网关的请求和微服务响应。GatewayFilter功能是固定的,全局过滤器可以自己写代码实现。

可以用全局过滤器实现:

  • 登录状态判断

  • 权限校验

  • 请求限流等

全局过滤器使用方法:实现GlobalFilter接口

public interface GlobalFilter {
    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器 
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

全局过滤器示例:

需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有authorization,

  • authorization参数值是否为admin

如果同时满足则放行,否则拦截

package cn.itcast.gateway.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // 2.获取authorization参数
        String auth = params.getFirst("authorization");
        // 3.校验
        if ("admin".equals(auth)) {
            // 放行
            return chain.filter(exchange);
        }
        // 4.拦截
        // 4.1.禁止访问,设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // 4.2.结束处理
        return exchange.getResponse().setComplete();
    }
}

六. Hystrix(服务容错)

七. XXL-JOB(分布式调度)

八. RabbitMQ(消息)

SpringCloud并没有绑定特定的消息中间件,但是它支持多种消息中间件,如RabbitMQ、Kafka。

SpringAMQP底层就包括对RabbitMQ的支持

1. 初识MQ

根据黑马文档安装RabbitMQ

RabbitMQ基本模型

RabbitMQ中的一些角色:

  • publisher:生产者

  • consumer:消费者

  • exchange个:交换机,负责消息路由

  • queue:队列,存储消息

  • virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔离

RabbitMQ消息模型

  • 基本消息队列(BasicQueue)
  • 工作消息队列(WorkQueue)
  • 发布订阅(Publish、Subscribe),又根据交换机类型分为三种:

        Fanout Exchange:广播

        Direct Exchange:路由

        Topic Exchange:主题

2. SpringAMQP

SpringAMQP是基于RabbitMQ封装的

Topic模式使用

(1)引入AMQP依赖

<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

(2)配置MQ地址

spring:
  rabbitmq:
    host: 192.168.150.101 # 主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: itcast # 用户名
    password: 123321 # 密码

(3)消息发送

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSimpleQueue() {
        // 交换机名称
        String exchangeName = "itcast.topic";
        // 消息
        String message = "喜报!孙悟空大战哥斯拉,胜!";
        // 发送消息
        rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
    }
}

(4)消息接收

@Component
public class SpringRabbitListener {

    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "topic.queue1"),
        exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
        key = "china.#"
    ))
    public void listenTopicQueue1(String msg){
        System.out.println("消费者接收到topic.queue1的消息:【" + msg + "】");
    }

    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "topic.queue2"),
        exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
        key = "#.news"
    ))
    public void listenTopicQueue2(String msg){
        System.out.println("消费者接收到topic.queue2的消息:【" + msg + "】");
    }
}

九. Kafka(消息)

十. Docker(服务部署)

1. 初识Docker

(1)Docker是什么?

        docker是一个容器化平台,可以创建、部署、和运行应用程序

(2)Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?

  • Docker允许开发中将应用程序、依赖、函数库、配置一起打包,形成可移植镜像

  • Docker应用运行在容器中,使用沙箱机制,相互隔离

(3)Docker如何解决开发、测试、生产环境有差异的问题?

  • Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行

(4)docker和虚拟机的区别

  • 虚拟机模拟了计算机,包含整个操作系统;docker是一个系统进程,仅仅封装了一些函数库
  • 虚拟机体积大,启动速度慢,性能一般;docker体积小,启动快,性能好

(5)Docker架构

         Docker采用了C/S架构,客户端和守护进程是关键组件

  •     客户端(client)

                通过命令行或API向docker服务端发送指令

                客户端安装命令是:yum install -y docker-ce(ce指社区免费版本)详情参考文档

  •     服务端(server)

                Docker守护进程,负责处理Docker指令,管理镜像、容器
                相关命令
                    systemctl start docker  # 启动docker服务
                    systemctl stop docker  # 停止docker服务
                    systemctl restart docker  # 重启docker服务

(6)Docker核心组件
    镜像(Image)
        Docker把应用程序、函数库、依赖、配置等打包成一个模版,称为镜像
    容器(Container)
        镜像中的容器启动后形成的进程就是容器,只是docker会给容器做隔离,对外不可见
    仓库(Registry)
        用于存储Docker镜像

2. Docker的基础操作

2.1 镜像

    名称
        分成两部分 [repository]:[tag] tag代表镜像的版本
        可以到DockerHub搜索镜像
        
    命令
        拉取 docker pull nginx
        查看 docker images
        保存 docker save
        加载 docker load
        删除 docker rmi
        查看帮助 --help (如 docker save --help)

2.2 容器

命令
    - docker run:创建并运行一个容器,处于运行状态
        --name 指定容器名称
        -p 指定端口
        -d 后台运行
    - docker pause:让一个运行的容器暂停
    - docker unpause:让一个容器从暂停状态恢复运行
    - docker stop:停止一个运行的容器
    - docker start:让一个停止的容器再次运行
    - docker rm:删除一个容器
    - docker logs:查看日志
        -f 持续查看日志
    - docker ps:查看容器状态
        -a 查看所有容器

2.3 数据卷

遇到的问题
    容器和数据耦合在一起,导致想修改容器中的数据很麻烦、容器中的数据无法复用、升级容器中数据也跟着丢失了,为了解决这个问题,引入数据卷的概念,把容器和数据分隔开

什么是数据卷?
    数据卷是一个虚拟目录,指向宿主机文件系统中的某个目录

操作命令
    基础语法
        docker volume [COMMAND]
    创建数据卷
        docker volume create xxx
    查看所有数据卷
        docker volume ls
    查看某个数据卷详细信息
        docker volume inspect xxx
    删除指定数据卷
        docker volume rm
    挂载数据卷
        创建容器的时候使用 -v参数挂载

docker run \
  --name mn \
  -v html:/root/html \
  -p 8080:80
  nginx \


 -v 代表把html这个数据卷 挂载到 /root/html 目录下
 \ 代表换行
 html这个数据卷可以不用事先创建,会默认创建

数据卷操作小结
     docker run的命令中通过 -v 参数挂载文件或目录到容器中:

  •  -v volume名称:容器内目录
  •  -v 宿主机文件:容器内文件
  •  -v 宿主机目录:容器内目录

3. DockerFile自定义镜像

3.1 初识DockerFile

为什么需要自定义镜像?

        常见的镜像在DockerHub就能找到,但是我们自己写的项目就必须自己构建镜像了

镜像的结构?

如何自定义镜像?

  • 镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。
  • 我们要构建镜像,其实就是实现上述打包的过程
  • 使用dockerfile来完成打包的过程

3.2 DockerFile操作

DockerFile是什么?
    Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer

语法

构建java项目的镜像

  • 基于Ubuntu构建


        

  • 基于java8构建

3.3 DockerFile小结

  • Dockerfile的本质是一个文件,通过指令描述镜像的构建过程
  • Dockerfile的第一行必须是FROM,从一个基础镜像来构建
  • 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,例如:java:8-alpine

4. Docker-Compose

4.1 是什么

docker-compose是一个定义和运行多容器docker应用程序的工具

4.2 安装

主要命令是:

curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

详见黑马docker文档

4.3 使用

(1)通过 docker-compose.yml 文件,可以指定各个服务之间的依赖关系、网络设置、挂载的卷等,从而更容易地定义、配置和管理整个应用的部署

(2)启动应用程序:docker-compose up

5. 镜像仓库

可以通过命令搭建私有镜像仓库,详见黑马docker文档

推送、拉取镜像

十一. Kubernetes(容器编排)

十二. SpringCloud Sleuth(链路追踪)

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值