廾匸

廾匸 查看完整档案

上海编辑  |  填写毕业院校  |  填写所在公司/组织 www.baifan123 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

廾匸 赞了文章 · 2020-11-12

Redis的简单介绍

缓存机制

说明:使用缓存可以有效的降低用户访问物理设备的频次,快速从内存中后去数据,之后返回给用户需要保证内存中的数据就是数据库数据。
1.缓存的运行环境应该在内存中(快)。
2.使用C语言开发缓存。
3.缓存的数据结构【K——V结构,一般采用String类型多,hey必须唯一,v:Json格式】。
4.内存环境断电即擦除,所以应该将内存数据持久化(执行写盘操作)。
5.如果没有维护内存的大小,则容易导致内存溢出,所以采用LRU算法优化!
image.png

Redis介绍

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
原子性说明:
Redis的操作是单进程单线程操作,所以没有线程并发性的安全问题,采用队列的方式一个一个操作。
Redis常见用法:
1.Redis可以当做缓存使用。
2.Redis可以当做数据库使用(验证码....)
3.Redis可以当做消息中间件使用(银行转账....)

Redis的安装

1.解压Redis安装包
[root@localhost src]# tar -zxvf redis-5.0.4.tar.gz

image.png

2.安装Redis

说明:在Redis的根目录中执行命令
命令:
1."make"
2.make install
image.png

修改Redis的配置文件

命令set nu //展现行号
image.png
修改位置:
~~~~1.注释IP绑定
image.png
2.关闭保护模式
image.png
3.开启后台启动
image.png

Redis服务器命令

1.启动命令: redis-server redis.conf
2.检索命令: ps -ef | grep redis
3.进入客户端: redis-cli -p 6379(如果默认端口是6379则这里的端口号可以省略)
4.关闭redis: kill -9 PID号 | redis-cli -p 6379 shutdown
image.png

Redis入门案例

引入jar包文件

<!--spring整合redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>

编辑测试API

public class TestRedis {
    /**
 * 1.测试redis的程序连接是否正常
 * 步骤:
 *      1.实例化jedis工具API对象(host:port)
 *      2.根据实例 操作redis 方法就是命令
 *       
* 关于链接不通的说明:
 *       1.检查Linux防火墙
 *       2.检查Redis配置文件修改项
 *            2.1 IP绑定
 *            2.2 保护模式
 *            2.3 后台启动
 *       3.检查redis启动方式 redis-server redis.conf
 *       4.检查IP 端口 及redis是否启动...
 */ @Test
 public void test01(){
        String host="192.168.126.129";
        int port=6379;
        Jedis jedis=new Jedis(host,port);
        jedis.set("2006", "好好学习天天向上");
        System.out.println(jedis.get("2006"));
        //练习是否存在key
 if(jedis.exists("2006")){
            jedis.del("2006");
        }else{
            jedis.set("2006","hdsjiahjds");
            jedis.expire("2006", 100);
        }
    }

setex的使用

/**
 * 需求:
 *      1.向redis中插入数据
 *      2.为key设定超时时间。,60秒后失效
 *      3.线程sleep 3秒
 *      4.获取key的剩余的存活时间
 *
 */// 如果用下面这种方法的话会导致出现异常的话下面的代码不执行,所以要满足原子性要求
@Test
public void test02() throws InterruptedException {
    Jedis jedis=new Jedis("192.168.126.129",6379);
    jedis.set("红楼梦", "贾宝玲");
    jedis.expire("红楼梦", 60);
    Thread.sleep(3000);
    System.out.println(jedis.ttl("红楼梦"));
}
/*如果用redis并且需要添加超时时间时,一般需要满足原子性要求
* 原子性:    操作时要么成功要么失败,但是必须同时完成
* 用setex完成redis的添加,setex保证数据的原子性操作
* */
@Test
public void test03() throws InterruptedException {
    Jedis jedis=new Jedis("192.168.126.129",6379);
    jedis.setex("红楼梦",60, "林黛玉");
    System.out.println(jedis.get("红楼梦"));
}

setnx的使用

/**
 * 判断数据是否已经存在,如果已经存在则不修改,如果不存在则添加
 *
 * 之前用if判断语句来判断数据是否已经存在,
 * 在这里用setnx判断数据是否已经存在
 * */
@Test
public void test04() throws InterruptedException {
    Jedis jedis=new Jedis("192.168.126.129",6379);
    /*if(jedis.exists("aa")){
 System.out.println("key已经存在,不修改");
 }else{ jedis.set("aa", "测试数据");
 }*/ jedis.setnx("aaa", "数据是否存在");
    System.out.println(jedis.get("aaa"));
}

set超时时间原子性操作

/**
 * 需求:
 *      1.要求用户赋值时,如果数据存在则不赋值。 setnx
 *      2.要求在赋值操作时,必须设定超时的时间,并且要求满足原子性 setex
 * * 如果同时要满足这两个要求的话则不能用setnx和setex。因为这两个方法在单独
 * 执行时是可以的,但是放在一块一起执行的话则会出错。
 * 所以这里要用set方法,里面的params则是表示设置一个set对象。
 *    SetParams中的参数:
 *          nx:当数据不存在的时候才会赋值
 *          xx:当数据存在的时候才会赋值
 *          ex:表示失效的时间,单位是秒
 *          px:也表示失效的时间,单位是毫秒
 */
@Test
public void test05(){
    Jedis jedis=new Jedis("192.168.126.129",6379);
    SetParams setParams=new SetParams();
    setParams.nx().ex(60);//加锁的操作
 jedis.set("bbb", "实现业务操作ccc", setParams);
    System.out.println(jedis.get("bbb")+jedis.ttl("bbb"));
    jedis.del("bbb");//解锁的操作
}

List集合的练习

 @Test
    public void testList() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        jedis.lpush("list", "1","2","3");
        System.out.println(jedis.rpop("list"));
    }

redis事务控制

@Test
    public void testTx() throws InterruptedException {
        Jedis jedis = new Jedis("192.168.126.129", 6379);
        //1.开启事务
        Transaction transaction = jedis.multi();
        try {
            transaction.set("aa", "aa");
            //提交事务
            transaction.exec();
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            transaction.discard();
        }
    }

分布式锁机制简单说明

说明:秒杀业务逻辑
如果1元抢购一部价值6988的手机可能会造成多个用户都抢购成功并且支付成功。
问题:
1.tomcat服务器有多台
2.数据库只有一份
3.必然会出现高并发的现象。
如何实现抢购?

常规锁操作

超卖的原因

image.png

同步锁的问题

说明:同步锁只能解决tomcat内部的问题,不能解决对个tomcat并发问题。
image.png

分布式锁机制

思想:
1.锁应该使用第三方操作,应该公用!
2.原则:如果锁正在被使用时,其他的用户不能操作。
3.策略:用户向redis保存一个key,如果redis中有key表示有人正在使用这把锁,其他用户不允许操作。如果redis中没有key,则表示可以使用这把锁。
4.风险:如何解决死锁问题?-->设定超时时间。
image.png

查看原文

赞 2 收藏 1 评论 0

廾匸 赞了文章 · 2020-11-12

SpringBoot整合Redis

编辑配置文件 redis.proper

说明:由于该配置被其他的项目共同使用,则应该写到common中。
image.png

编辑配置类

说明:编辑redis配置类,将Jedis对象交给Spring容器进行管理。
image.png

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {
    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;
    @Bean
 public Jedis jedis(){
        return new Jedis(host,port);
    }
}

对象与JSON转化(ObjectMapper介绍)

简单对象转化

/**
     * 测试简单对象的转化
     */
    @Test
    public void test01() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(100L).setItemDesc("商品详情信息")
                .setCreated(new Date()).setUpdated(new Date());
        //对象转化为json
        String json = objectMapper.writeValueAsString(itemDesc);
        System.out.println(json);

        //json转化为对象
        ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
        System.out.println(itemDesc2.getItemDesc());
    }

集合对象转化

 //测试集合对象的转化
 @Test
 public void testJson01() throws JsonProcessingException {
        ObjectMapper objectMapper=new ObjectMapper();
        ItemDesc itemDesc1=new ItemDesc();
        itemDesc1.setItemId(100L).setItemDesc("商品信息详情1")
                .setCreated(new Date()).setUpdated(new Date());
        ItemDesc itemDesc2=new ItemDesc();
        itemDesc2.setItemId(100L).setItemDesc("商品信息详情2")
                .setCreated(new Date()).setUpdated(new Date());
        List<ItemDesc> list=new ArrayList<>();
        list.add(itemDesc1);
        list.add(itemDesc2);
        //将集合对象转化为json格式
 String json = objectMapper.writeValueAsString(list);
        System.out.println(json);//[{"created":1604828831646,"updated":1604828831646,"itemId":100,"itemDesc":"商品信息详情1"},{"created":1604828831646,"updated":1604828831646,"itemId":100,"itemDesc":"商品信息详情2"}]
 //将json格式串转化为集合对象
 List list1 = objectMapper.readValue(json, list.getClass());
        System.out.println(list1);//[{created=1604828831646, updated=1604828831646, itemId=100, itemDesc=商品信息详情1}, {created=1604828831646, updated=1604828831646, itemId=100, itemDesc=商品信息详情2}]
 }
}

编辑工具API

image.png

public class ObjectMapperUtil {
    /**
 * 1.将用户传递的数据转化为json
 * 2.将用户传递的json转化为对象
 */
 private static final ObjectMapper MAPPER=new ObjectMapper();
    //1.将用户传递的数据转化为json
 public static String toJson(Object object){
        if(object==null){
            throw new RuntimeException("传递的数据不能为空,请检查");
        }
        try {
           return MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            //将检查异常转化为运行时异常
 e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    //2.将用户传递的json转化为对象
 //需求:要求用户传递什么样的类型,就返回什么样的对象(运用泛型的知识)
 public static <T> T toObj(String json,Class<T> target){
        if(StringUtils.isEmpty(json)||target==null)
            throw new RuntimeException("参数不能为空");
        try {
            return MAPPER.readValue(json, target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

商品分类的缓存实现

实现步骤

1.定义Redis中的key。key必须唯一不能重复,设计到存和取。【格式应该是:key="ITEM_CAT_PARENTID::70"】
2.根据key去redis中进行查询数据(有数据|没有数据)。
3.没有数据则查询数据库获取巨鹿并将查询出来的数据保存到redis中,方便后续使用。
4.有数据表示用户不是第一次查询,可以将缓存数据直接返回即可。

编辑ItemCatController

image.png

编辑ItemCatService

/**
 * 1.定义Redis中的key,
 * 这里的key要求唯一还不能重复,涉及到存和取
 *      key="ITEM_CAT_PARENTID::"+parentId;
 * 2.根据key去redis中进行查询   要么有数据 要么没有数据
 * 3.如果没有数据则查询数据库获取记录,之后将数据保存在redis中,方便后续使用
 * 4.如果有数据则表示用户不是第一次查询,可以将缓存数据也直接返回即可
 *
 */@Override
public List<EasyUITree> findItemCatListCache(Long parentId) {
    //定义一个公共的返回值对象
 List<EasyUITree> treeList=new ArrayList<>();
    //1.定义Redis中的key,
 String key="ITEM_CAT_PARENTID::"+parentId;
   //测试redis中的执行时间和查询数据库的时间
 Long startTime= System.currentTimeMillis();
    //检索redis中的key是否存在
 if(jedis.exists(key)){
        //数据存在
 String json=jedis.get(key);
        //需要将json串转化为对象,并将转化后的对象存入treeList
 treeList= ObjectMapperUtil.toObj(json, treeList.getClass());
        Long endTime= System.currentTimeMillis();
        System.out.println("redis执行时间"+(endTime-startTime));
    }else{
        //数据不存在 则调用上面的查询方法在数据库中查询数据
 treeList= findItemCatList(parentId);
        //将数据保存到缓存中,以便后续查询方便
 String json = ObjectMapperUtil.toJson(treeList);
        jedis.set(key,json);
        Long endTime= System.currentTimeMillis();
        System.out.println("数据库查询执行时间"+(endTime-startTime));
    }
    return treeList;
}

使用redis的速度差

image.png

查看原文

赞 1 收藏 0 评论 0

廾匸 赞了文章 · 2020-11-12

Redis持久化、内存配置以及缓存问题的简单介绍

关于Redis配置说明

Redis持久化的说明

redis默认条件下支持数据的持久化操作,当redis中有数据时会定期将数据保存到磁盘中,当redis服务器重启时会根据配置文件读取指定的持久化文件,实现内存数据的恢复。

持久化方式

RDB模式

特点:
1.RDB模式是redis的默认的持久化策略。
2.RDB模式记录的是redis内存数据的快照,最新的快照会覆盖之前的内容,所有RDB持久化文件占用空间更小,持久化的效率更高。
3.RDB模式由于定期持久化,所以可能导致数据的丢失。

命令:

  1. save 要求立即马上持久化 同步的操作 其他的redis操作会陷入阻塞的状态.
  2. bgsave 开启后台运行 异步的操作 由于是异步操作,所以无法保证rdb文件一定是最新的需要等待.

配置:
1.持久化文件名称
image.png
2.持久化文件位置
dir./ 相对路径的写法。
dir/usr/local/src/redis 绝对路径的写法。
image.png
3.RDB模式持久化策略
image.png

AOF模式

特点:
1.AOF模式默认条件下是关闭的,需要手动开启,开启之后需要重启才能生效。
image.png
2.AOF模式是异步的操作,记录的是用户的操作的过程,可以防止用户的数据丢失。
3.由于AOF模式记录的是程序的运行状态,所以持久化文件相对较大,恢复数据的时间长, 需要人为的优化持久化文件。
配置:
image.png
总结:
1.如果不允许数据丢失,使用AOF模式。
2.如果追求效率,允许少量数据丢失,采用RDB模式。
3.如果纪要保证效率,又要保证数据,则应该配置redis的集群,主机使用RDB模式,从机使用AOF模式。

关于Redis内存策略

关于内存策略的说明

Redis数据的存储都在内存中.如果一直向内存中存储数据 必然会导致内存数据的溢出.
解决方式:

  1. 尽可能为保存在redis中的数据添加超时时间.
  2. 利用算法优化旧的数据.

LRU算法

特点: 最好用的内存优化算法.
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的数据置换算法,选择最近最久未使用的数据予以淘汰。该算法赋予每个数据一个访问字段,用来记录一个数据自上次被访问以来所经历的时间 t,当须淘汰一个数据时,选择现有数据中其 t 值最大的,即最近最少使用的数据予以淘汰。
维度: 时间 T

LFU算法

LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 使用次数

RANDOM算法

即随机删除数据。

TTL算法

设定了超时时间的数据,把将要超时的数据提前删除的算法。

Redis内存数据优化

1.volatile-lru 设定了超时时间的数据采用lru算法。
2.allkeys-lru 所有的数据采用LRU算法。
3.volatile-lfu 设定了超时时间的数据采用lfu算法删除。
4.allkeys-lfu 所有数据采用lfu算法删除。
5.volatile-random 设定超时时间的数据采用随机算法。
6.allkeys-random 所有数据的随机算法。
7.volatile-ttl 设定超时时间的数据的TTL算法。
8.noeviction 如果内存溢出了 则报错返回. 不做任何操作. 默认值。
image.png

关于Redis缓存问题

问题描述: 如果海量用户同时请求 而这时redis服务器出现问题 则可能导致整个系统崩溃.
运行速度:

  1. tomcat服务器 150-250 之间 JVM调优 1000/秒
  2. NGINX 3-5万/秒
  3. REDIS 读 11.2万/秒 写 8.6万/秒 平均 10万/秒

缓存穿透

问题描述: 由于用户高并发环境下访问 数据库中不存在的数据时 ,容易导致缓存穿透.
解决方案: 设定IP限流的操作 nginx中 或者微软服务机制 API网关实现.

缓存击穿

问题描述: 由于用户高并发环境下, 由于某个数据之前存在于内存中,但是由于特殊原因(数据超时/数据意外删除)导致redis缓存失效. 而使大量的用户的请求直接访问数据库。
解决方案:
1.设定超时时间时 不要设定相同的时间。
2.设定多级缓存。
image.png

缓存雪崩

问题描述: 由于高并发条件下 有大量的数据失效.导致redis的命中率太低.而使得用户直接访问数据库(服务器)导致崩溃,称之为缓存雪崩.
解决方案:
1.不要设定相同的超时时间 随机数
2.设定多级缓存.
3.提高redis缓存的命中率 调整redis内存优化策略 采用LRU等算法.
image.png

查看原文

赞 1 收藏 0 评论 0

廾匸 赞了文章 · 2020-11-12

Redis分片机制

为什么需要分片机制

如果需要存储海量的内存数据,如果只使用一台redis,则无法保证redis工作的效率。大量时间都浪费到了寻址中,所以需要一种机制能够满足该要求。
采用分片机制实现:
image.png

Redis分片搭建

搭建注意事项

Redis服务的启动需要依赖于redis.conf的配置文件,如果需要准备多台redis则需要多个redis.conf的配置。
准备端口号:
1.6379
2.6380
3.6381

分片实现

image.png
修改端口号:将各自的端口号进行修改
1.进入各自的redis.conf文件中
2.修改各自的端口号
image.png
启动redis服务器
image.png
校验服务器是否正常运行
image.png

关于分片的注意事项

1.问题描述:
当启动多台redis服务器之后,多台redis暂时没有必然的联系,各自都是独立的实体,可以数据的存储。
如图所示:
2.如果将分片通过程序的方式进行操作,要把3太redis当做一个整体,所以与上述的操作完全不同,不会出现一个key同时保存到多个redis的现象。
image.png

分片入门案例

/**
     * 测试Redis分片机制
     * 思考: shards 如何确定应该存储到哪台redis中呢???
     */
    @Test
    public void testShards(){
        List<JedisShardInfo> shards = new ArrayList<>();
        shards.add(new JedisShardInfo("192.168.126.129",6379));
        shards.add(new JedisShardInfo("192.168.126.129",6380));
        shards.add(new JedisShardInfo("192.168.126.129",6381));
        //准备分片对象
        ShardedJedis shardedJedis = new ShardedJedis(shards);
        shardedJedis.set("shards","redis分片测试");
        System.out.println(shardedJedis.get("shards"));
    }

一致性hash算法

一般的hash是8为16禁止数。0---9 A--F 组合总共为2^32。
如果对相同的数据进行hash运算,结果必然相同。
一个数据1M和数据1G的hash运算的速度一致。

一致性hash算法介绍

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题。
image.png

特性

平衡性

概念:平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题。(大致平均)
问题描述: 由于节点都是通过hash方式进行算计.所以可能出现如图中的现象.,导致负载严重不平衡。
image.png
解决方法:引入虚拟节点
image.png

单调性

特点:单调性是指在新增或者删减节点时,不影响系统正常运行。
image.png

分散性

分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据 。

SpringBoot整合Redis分片

编辑配置文件

# 配置redis单台服务器
redis.host=192.168.126.129
redis.port=6379

# 配置redis分片机制
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

编辑配置类

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {

    @Value("${redis.nodes}")
    private String nodes;  //node,node,node.....

    //配置redis分片机制
    @Bean
    public ShardedJedis shardedJedis(){
        nodes = nodes.trim();   //去除两边多余的空格
        List<JedisShardInfo> shards = new ArrayList<>();
        String[] nodeArray = nodes.split(",");
        for (String strNode : nodeArray){   //strNode = host:port
            String host = strNode.split(":")[0];
            int port = Integer.parseInt(strNode.split(":")[1]);
            JedisShardInfo info = new JedisShardInfo(host, port);
            shards.add(info);
        }
        return new ShardedJedis(shards);
    }
   }

修改AOP注入项

image.png

查看原文

赞 1 收藏 0 评论 0

廾匸 赞了文章 · 2020-11-11

关于Redis 缓存面试题

关于Redis 缓存

=============

问题描述:

由于海量的用户请求 如果这时redis服务器出现问题 则可能导致整个系统崩溃.

运行速度:

1.tomcat服务器150-250之间 JVM调优 1000/秒

2.NGINX 3-5万秒

3.REDIS 读11.2万/秒 写8.6万/秒 平均10万/秒

缓存穿透

问题描述:

由于用户高并发环境下访问 数据库中不存在的数据时 ,容易导致缓存穿透.

如何解决:

设定IP限流的操作 nginx中 或者微软服务机制API网关实现.

缓存击穿

问题描述:

由于用户高并发环境下, 由于某个数据之前存在于内存中,但是由于特殊原因(数据超时/数据意外删除)导致redis缓存失效. 而使大量的用户的请求直接访问数据库.

如何解决:

1.设定超时时间时,不要设定一样的时间.

2.设定多级缓存.

image

缓存雪崩

说明:由于高并发条件下有大量数据失效,导致redis的命中率太低,而使得用户直接访问数据库(服务器)导致崩溃,称之为缓存雪崩.

解决方案:

1.不要设定相同的超时时间 随机数

2.设定多级缓存

3.提高redis的命中率 调整redis内存优化策略采用LRU等算法.

image

查看原文

赞 1 收藏 0 评论 0

廾匸 关注了用户 · 2020-11-09

BolunWu @bolunwu

关注 3

廾匸 发布了文章 · 2020-10-30

第五阶段day05

1.hystrix+turbine 集群聚合监控

image.png

hystrix dashboard一次只能监控一个服务实例 使用turbine可以汇集监控信息 将聚合后的信息提供给hystrix dahsboard来集中展示和监控

新建sp10-turbine项目

1.添加 Turbine Eureka Discovery Client两个依赖
2.配置yml文件

spring:
application:

name: turbin

server:
port: 5001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
  

turbine:
app-config: order-service
cluster-name-expression: new String("default")

3.主程序 添加@EnableTurbine和@EnableDiscoveryClient注解

访问测试
image.png

zuul API网关

zuul API网关 为微服务应用提供统一的对外访问接口
zuul还提供过滤器 对所有微服务提供统一的请求校验

新建sp11-zuul项目

1.添加Eureka Discoery Client Zuul sp01-commons项目依赖
2.yml文件 zuul路由配置可以省略 缺省以服务id作为访问路径
3.在主程序添加@EnableZuulProxy和@EnableDiscoveryClient注解
4.启动测试

zuul+ribbon负载均衡
zuul已经集成了ribbon 默认已经实现了负载均衡

zuul+ribbon重试
1.添加spring-retry依赖
2.需要开启重试 默认不开启
image.png

zuul+hystrix降级

创建降级类

getRoute() 方法中指定应用此降级类的服务id,星号或null值可以通配所有服务

package cn.tedu.sp11.fallback;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import cn.tedu.web.util.JsonResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class ItemServiceFallback implements FallbackProvider {

@Override
public String getRoute() {
    //当执行item-service失败,
    //应用当前这个降级类
    return "item-service";
    //星号和null都表示所有微服务失败都应用当前降级类
    //"*"; //null;
}

//该方法返回封装降级响应的对象
//ClientHttpResponse中封装降级响应
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    return response();
}

private ClientHttpResponse response() {
    return new ClientHttpResponse() {
        //下面三个方法都是协议号
        @Override
        public HttpStatus getStatusCode() throws IOException {
            return HttpStatus.OK;
        }
        @Override
        public int getRawStatusCode() throws IOException {
            return HttpStatus.OK.value();
        }
        @Override
        public String getStatusText() throws IOException {
            return HttpStatus.OK.getReasonPhrase();
        }

        @Override
        public void close() {
        }

        @Override
        public InputStream getBody() throws IOException {
            log.info("fallback body");
            String s = JsonResult.err().msg("后台服务错误").toString();
            return new ByteArrayInputStream(s.getBytes("UTF-8"));
        }

        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            return headers;
        }
    };
}

}

image.png

zuul降级类实现 FallbackProvider这个接口

zull+hystrix数据监控

暴露hystrix.stream监控端点

zuul已经包含了actuator的依赖

management:
endpoints:

web:
  exposure:
    include: hystrix.stream
    

启动服务 查看暴露的监控端点
(http://localhost:3001/actuator)(http://localhost:3001/actuator/hystrix.stream)

zuul+turbine聚合监控

修改turbine项目 聚合zuul服务实例

image.png

zuul请求过滤

定义过滤器 继承ZuulFilter

新建过滤器

package cn.tedu.sp11.filter;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import cn.tedu.web.util.JsonResult;

@Component
public class AccessFilter extends ZuulFilter{

@Override
public boolean shouldFilter() {
    //对指定的serviceid过滤,如果要过滤所有服务,直接返回 true
    
    RequestContext ctx = RequestContext.getCurrentContext();
    String serviceId = (String) ctx.get(FilterConstants.SERVICE_ID_KEY);
    if(serviceId.equals("item-service")) {
        return true;
    }
    return false;
}

@Override
public Object run() throws ZuulException {
    RequestContext ctx = RequestContext.getCurrentContext();
    HttpServletRequest req = ctx.getRequest();
    String token = req.getParameter("token");
    if (token == null) {
        //此设置会阻止请求被路由到后台微服务
        ctx.setSendZuulResponse(false);
        //向客户端的响应
        ctx.setResponseStatusCode(200);
        ctx.setResponseBody(JsonResult.err().code(JsonResult.NOT_LOGIN).toString());
    }
    //zuul过滤器返回的数据设计为以后扩展使用,
    //目前该返回值没有被使用
    return null;
}

@Override
public String filterType() {
    return FilterConstants.PRE_TYPE;
}

@Override
public int filterOrder() {
    //该过滤器顺序要 > 5,才能得到 serviceid
    return FilterConstants.PRE_DECORATION_FILTER_ORDER+1;
}

}

查看原文

赞 1 收藏 0 评论 0

廾匸 发布了文章 · 2020-10-28

第五阶段day04

1.feign 声明式客户端接口

微服务应用中 ribbon和hystrix总是同时出现 feign整合了两者 并提供了声明式消费者客户端

用feign代替hystrix+ribbon

image.png

新建项目
添加依赖
编辑yml文件
spring:
application:

name: feign

server:
port: 3001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

主程序添加@EnableDiscoverClient和@EnableFeignClients
注解

Feign远程调用

Feign提供了声明式客户端 只需要定义一个接口 就可以通过
接口做远程调用 具体调用代码通过动态代理添加

// 调用商品服务的远程调用接口
// 通过注解配置3件事:调用哪个服务,调用什么路径,向这个路径提交什么参数

@FeignClient(name="item-service")

public interface ItemFeignClient {

@GetMapping("/{orderId}")

JsonResult<List<Item>> getItems(@PathVariable String orderId);

}

添加Feign声明式客户端
1.OpenFeign依赖
2.启动类@EnableFeignClients
3.定义声明式客户端接口
ItemFeignClient
UserFeignClient
OrderFeignClient

4.添加一个测试用的控制器 使用三个声明式客户端接口调用远程服务

image.png

image.png

image.png

FeignController
////////////////////////
package cn.tedu.sp09.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp09.service.ItemFeignService;
import cn.tedu.sp09.service.OrderFeignService;
import cn.tedu.sp09.service.UserFeignService;
import cn.tedu.web.util.JsonResult;

@RestController
public class FeignController {

@Autowired
private ItemFeignService itemService;
@Autowired
private UserFeignService userService;
@Autowired
private OrderFeignService orderService;

@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
    return itemService.getItems(orderId);
}

@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
    return itemService.decreaseNumber(items);
}

/

@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
    return userService.getUser(userId);
}

@GetMapping("/user-service/{userId}/score") 
public JsonResult addScore(@PathVariable Integer userId, Integer score) {
    return userService.addScore(userId, score);
}

/

@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
    return orderService.getOrder(orderId);
}

@GetMapping("/order-service")
public JsonResult addOrder() {
    return orderService.addOrder();
}

}

调用流程

image.png

feign+ribbon负载均衡和重试

Feign默认启用了Ribbon的负载均衡和重试 无需额外配置

默认重试参数:
MaxAutoRetries=0
MaxAutoRetriesNextServer=1
ReadTimeout=1000

重试参数配置

对所有的服务都有效

ribbon:
MaxAutoRetries: 1

对item-service单独配置 对其他服务无效

item-service:
ribbon:

 MaxAutoRetries: 0
 
 

ribbon.xxx 全局配置
item-service.ribbon.xxx 对特定的服务实例的配置

/////////////////////////////////////////////
feign+hystrix降级

feign启用hystrix

feign默认没有启动hystrix 添加配置 启用hystrix

yml文件添加配置

feign:
hystrix:

enabled: true

启用hystrix后 访问服务

[http://localhost:3001/item-service/35]
默认1秒会快速失败,没有降级方法时,会显示白板页

可以添加配置 暂时减小降级超时时间 以便后续对降级进行测试

feign:
hystrix:

enabled: true

hystrix:
command:

default:
  execution:
    isolation:
      thread:
        timeoutInMilliseconds: 500
        

feign+hystrix降级

feign远程接口中的指定降级类

远程调用失败 会执行降级类中的代码

image.png

image.png

image.png

image.png

feign+hystrix监控和熔断测试

image.png

修改sp09-feign项目

主程序添加@EnableCircuitBreaker

////
配置actuator 暴露hystrix.stream监控端点

1.查看pom.xml 确认已经添加了actuator依赖

2.yml文件配置暴露 hystrix.stream端点

启动服务 查看监控端点

hystrix dashboard

启动hystrix dashboard服务 填入feign监控路径 开启监控
访问http://localhost:4001/hystrix

填入feign监控路径
http://localhost:3001/actuator/hystrix.stream

访问微服务 以产生监控数据

熔断测试
用ab工具 以并发50次 来发送20000个请求
ab -n 20000 -c 50 http://localhost:3001/item-service/35

断路为open状态 所有的请求都会被降级
直接降级执行callback方法

OrderService调用商品库存服务和用户服务

image.png

修改sp04-orderservice项目 添加feign 调用item service和user service

  1. pom.xml
  2. application.yml
  3. 主程序
  4. ItemFeignService
  5. UserFeignService
  6. ItemFeignServiceFB
  7. UserFeignServiceFB
  8. OrderServiceImpl

右击项目编辑起步依赖
actuator
feign
hystrix

image.png

image.png

image.png

image.png

image.png

image.png

package cn.tedu.sp04.order.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.sp01.service.OrderService;
import cn.tedu.sp04.order.feignclient.ItemFeignService;
import cn.tedu.sp04.order.feignclient.UserFeignService;
import cn.tedu.web.util.JsonResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {


@Autowired
private ItemFeignService itemService;
@Autowired
private UserFeignService userService;

@Override
public Order getOrder(String orderId) {
    //调用user-service获取用户信息
    JsonResult<User> user = userService.getUser(7);
    
    //调用item-service获取商品信息
    JsonResult<List<Item>> items = itemService.getItems(orderId);
    
    
    Order order = new Order();
    order.setId(orderId);
    order.setUser(user.getData());
    order.setItems(items.getData());
    return order;
}

@Override
public void addOrder(Order order) {
    //调用item-service减少商品库存
    itemService.decreaseNumber(order.getItems());
    
    //TODO: 调用user-service增加用户积分
    userService.addScore(7, 100);
    
    log.info("保存订单:"+order);
}

}

查看原文

赞 1 收藏 0 评论 0

廾匸 发布了文章 · 2020-10-27

第五阶段day02

Hyxtrix

系统容错工具
降级
熔断

image.png

image.png

微服务宕机时 ribbon无法转发请求

降级: 调用远程服务失败(异常 超时 服务不存在),可以通过执行当前服务中的一段代码来向客户端发回响应

降级响应 : 错误提示 返回缓存数据

快速失败:即使后台服务失败 也要让客户端尽快得到错误提示 而不能让客户端等待

添加降级
1.添加Hystrix依赖
2.启动类添加@EnableCircuitBreaker 注解

启动断路器 断路器的两个核心功能
降级: 超时 出错 不可到达时 对服务降级 返回错误信息或者是缓存数据
熔断: 当服务压力过大时 错误比例过多时 熔断所有请求 所有请求直接降级

3.添加降级代码

在远程调用方法上添加
@HystrixCommand(fallbackMethod="降级方法名")
完成降级方法 返回降级响应

hystrix超时

超时设置

在yml文件中添加

hystrix:
command:

default:
  execution:
    isolation:
      thread:
        timeoutInMilliseconds: 500

默认1秒超时 执行降级
如果配置了ribbon重试 重试还会继续执行 最终重试结果无效

Hystrix超时>=Ribbon总的超时时长

hystrix熔断

整个链路达到一定的阈值,默认情况下,10秒内产生超过20次请求,则符合第一个条件。
满足第一个条件的情况下,如果请求的错误百分比大于阈值,则会打开断路器,默认为50%。
Hystrix的逻辑,先判断是否满足第一个条件,再判断第二个条件,如果两个条件都满足,则会开启断路器

断路器打开 5 秒后,会处于半开状态,会尝试转发请求,如果仍然失败,保持打开状态,如果成功,则关闭断路器

当请求量增大 出现过多错误 hystrix可以和后台服务断开连接(过热保护)
可以避免雪崩效应 故障传播

限流措施

流量过大时造成服务故障 可以断开服务 降低它的流量

在特定条件下会自动触发熔断
1.10秒内20次请求(必须首先满足)
2.50%出错 执行了降级代码

3个数值都可以改变

半开状态下可以自动恢复

断路器打开几秒后 进入半开状态 尝试发送请求
如果请求成功自动关闭断路器恢复正常
如果请求失败 再保持打开状态几秒

Hystrix监控仪表盘 监控Hystrix降级和熔断的错误信息

image.png

hystrix对请求的降级和熔断 可以产生监控信息 hystrix dashboard可以实时的进行监控

1.添加actuactor依赖 并暴露hystrix监控端点

image.png

2.配置yml文件 并暴露hystrix.stream监控端点

management:
endpoints:

web:
  exposure:
    include: hystrix.stream
    

访问路径 http://localhost:3001/actuator 查看监控端点

image.png

创建项目
添加hystrix dashboard依赖
配置yml文件

spring:
application:

name: hystrix-dashboard

server:
port: 4001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

hystrix:
dashboard:

proxy-stream-allow-list: localhost

主程序添加
@EnableHystrixDashboard注解

访问测试
http://localhost:4001/hystrix

填写hystrix的监控端点 开启监控

http://localhost:3001/actuator/hystrix.steram

image.png

image.png

虚拟机的配置

VMware提供了虚拟网络环境 有多块虚拟网卡

它的虚拟网络经常出现问题 可以还原默认设置 删除所有的虚拟网卡重新创建

NAT网络网段
VMware会随机自动分配网段 选择VMware8网卡 将网段改为64

设置NAT网络的ip地址

NetworkManager服务于network.service服务有冲突 会引起网络无法设置ip 网卡丢失的情况

禁用NetworkManager服务

systemctl stop NetworkManager

systemctl disable NetworkManager

自动获取ip

设置ip 要修改一个网卡的配置文件

/etc/sysconfig/network-scripts/
vim /ifcfg-ens33

自动获取ip
./ip-dhcp
ip addr
ifconfig

设置固定ip

./ip-static
ip:192.168.64.8

idconfig

ping www.baidu.com (ctrl+c结束)

Rabbitmq虚拟机

1.克隆centos-7-1908:rabbitmq
2.设置固定ip
./ip-static
ip:192.168.64.140

ifconfig

3.用Mobaxterm连接140服务器

将rabbit-install解压并将目录上传到/root

切换到rabbit-install目录
cd rabbitmq-install
安装
rpm -ivh *.rpm

启动rabbitmq服务器

systemctl enable rabbit-server 设置服务 开机自动启动

systemctl start rabbit-server 启动服务

rabbitmq管理界面

启动管理界面

开启管理界面插件
rabbitmq-plugins enable rabbitmq_management

防火墙打开 15672 管理端口
firewall-cmd --zone=public --add-port=15672/tcp -- permanent
firewall-cmd --reload

重启RabbitMQ服务

systemctl restart rabbitmq-server

访问服务器的15672端口

http://192.168.64.140:15672

添加用户

添加用户
rabbitmqctl add_user admin admin

新用户设置用户为超级管理员
rabbitmqctl set_user_tags admin administrator

image.png

开放客户端连接端口

打开客户端连接端口
firewall-cmd --zone=public --add-port=5672/tcp --permanent
firewall-cmd --reload

主要端口介绍

  • 4369 – erlang发现口
  • 5672 – client端通信口
  • 15672 – 管理界面ui端口
  • 25672 – server间内部通信口
查看原文

赞 1 收藏 0 评论 0

廾匸 发布了文章 · 2020-10-26

第五阶段day01

1.Spring Cloud

Spring Cloud是一个工具集,集成了多种工具,来解决微服务中的各种问题
微服务的整体解决方案 微服务全家桶

微服务治理,服务注册和发现 eureka
负载均衡、请求重试 ribbon
断路器,服务降级、熔断 hystrix
ribbon + hystrix 集成,并提供声明式客户端 feign
hystrix 数据监控 hystrix dashboard 和 turbine
API 网关,提供微服务的统一入口,并提供统一的权限验证 zuul
配置中心 config
消息总线, 配置刷新 bus
链路跟踪 sleuth+zipkin
...

Spring Cloud不是一个解决单一问题的框架!!!

2.开发环境
IDEA或STS
Lombok
Maven

3.Spring Cloud和Dubbo的区别

image.png

Dubbo
1.Dubbo只是一个远程调用(RPC)框架
2.默认基于长连接,支持多种序列化格式

Spring Cloud
1.框架集
2.提供了一整套微服务解决方案(全家桶)
3.基于http调用, Rest API

4.service- 服务

image.png

五.item service商品服务
1.新建项目
2.配置依赖pom.xml 添加commons依赖
3.配置application.yml
image.png
4.配置主程序
5.编写代码

业务实现层
@Slf4j
@Service
public class ItemServiceImpl implements ItemService {

@Override
public List<Item> getItems(String orderId) {
    ArrayList<Item> list = new ArrayList<Item>();
    list.add(new Item(1, "商品 1",1));
    list.add(new Item(2, "商品 2",2));
    list.add(new Item(3, "商品 3",3));
    list.add(new Item(4, "商品 4",4));
    list.add(new Item(5, "商品 5",5));
    return list;
}

@Override
public void decreaseNumbers(List<Item> list) {
    for(Item item : list) {
        log.info("减少库存 - "+item);
    }
}

}
控制层

@Slf4j
@RestController
public class ItemController {

@Autowired
private ItemService itemService;

@Value("${server.port}")
private int port;

@GetMapping("/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
    log.info("server.port="+port+", orderId="+orderId);
    
    List<Item> items = itemService.getItems(orderId);
    return JsonResult.ok(items).msg("port="+port);
}

@PostMapping("/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
    itemService.decreaseNumbers(items);
    return JsonResult.ok();
}

}

Spring MVC接收参数的几个注解

@RequestParam
1.表单参数,键值对参数
2.可以接受get或post请求提交的参数
3.name1=value&name2=value2&name3=value3
@PathVariable
1.请求路径参数
2.http://localhost:8080/123
http://localhost:8080/123/aa/bb
@RequestBody
1.post请求协议体中的数据
2.完整的接受协议中的json数据

六.user service用户服务
1.新建项目
2.配置依赖pom.xml
3.配置yml文件
image.png

4.配置主程序
5.编写代码

业务层

@Slf4j
@Service
public class UserServiceImpl implements UserService {

@Value("${sp.user-service.users}")
private String userJson;

@Override
public User getUser(Integer id) {
    log.info("users json string : "+userJson);
    List<User> list = JsonUtil.from(userJson, new TypeReference<List<User>>() {});
    for (User u : list) {
        if (u.getId().equals(id)) {
            return u;
        }
    }
    
    return new User(id, "name-"+id, "pwd-"+id);
}

@Override
public void addScore(Integer id, Integer score) {
    // 这里增加积分
    log.info("user "+id+" - 增加积分 "+score);
}

}

控制层

@Slf4j
@RestController
public class UserController {


@Autowired
private UserService userService;

@GetMapping("/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
    log.info("get user, userId="+userId);
    User u = userService.getUser(userId);
    return JsonResult.ok(u);
}

@GetMapping("/{userId}/score") 
public JsonResult addScore(
        @PathVariable Integer userId, Integer score) {
    userService.addScore(userId, score);
    return JsonResult.ok();
}

}

七. order service订单服务

  1. 新建项目
  2. 配置依赖 pom.xml
  3. 配置 application.yml
    image.png
  4. 配置主程序
  5. 编写代码

业务层

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

@Override
public Order getOrder(String orderId) {
    //TODO: 调用user-service获取用户信息
    //TODO: 调用item-service获取商品信息
    Order order = new Order();
    order.setId(orderId);
    return order;
}

@Override
public void addOrder(Order order) {
    //TODO: 调用item-service减少商品库存
    //TODO: 调用user-service增加用户积分
    log.info("保存订单:"+order);
}

}

控制层

@Slf4j
@RestController
public class OrderController {

@Autowired
private OrderService orderService;

@GetMapping("/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
    log.info("get order, id="+orderId);
    
    Order order = orderService.getOrder(orderId);
    return JsonResult.ok(order);
}

@GetMapping("/")
public JsonResult addOrder() {
    //模拟post提交的数据
    Order order = new Order();
    order.setId("123abc");
    order.setUser(new User(7,null,null));
    order.setItems(Arrays.asList(new Item[] {
            new Item(1,"aaa",2),
            new Item(2,"bbb",1),
            new Item(3,"ccc",3),
            new Item(4,"ddd",1),
            new Item(5,"eee",5),
    }));
    orderService.addOrder(order);
    return JsonResult.ok();
}

}

八 eureka 注册与发现

image.png

  1. 创建eureka项目
  2. 配置依赖 pom.xml
  3. 配置 application.yml
    spring:
    application:
    name: eureka-server

server:
port: 2001

eureka:
server:

enable-self-preservation: false   关闭保护模式

instance:

hostname: eureka1     eureka集群服务器之间 通过此标签区分

client:

#不同自身注册  不从自身拉取信息
register-with-eureka: false   
fetch-registry: false


  1. 主程序启用 eureka 服务器
  2. 启动,访问测试

eureka注册中心

作用:服务注册和发现

提供者(Provider)
向注册中心注册自己的地址
消费者(Consumer)
从注册中心发现其他服务

eureka的运行机制

注册 一次次反复连接eureka 直到注册成功为止
拉取 每隔30秒拉取一次注册表 更新注册信息
心跳 每30秒发送一次心跳 3次收不到心跳 ureka会删除这个服务
自我保护模式 特殊情况 由于网络不稳定15分钟内85%服务器出现心跳异常
保护所有的注册信息不删除
网络恢复后 可以自动退出保护模式
开发测试期间 可以关闭保护模式

搭建eureka服务

1.eureka server依赖
2.yml文件配置
关闭保护模式
主机名(集群中区分每台服务器)
针对单台服务器,不向自己注册,不从自己拉取
3.启动类
@EnableEurekaServer 触发eureka服务器的自动配置

服务提供者

image.png

2.yml配置eureka连接地址

image.png

主程序添加@EnableEurekaServer注解

修改hosts文件

127.0.0.1 eureka1
127.0.0.1 eureka2

eureka和"服务提供者"的高可用(集群)

image.png

远程调用

RestTemplate

springboot 提供的远程调用工具,类似 HttpClient

RestTemplate 对http Rest API 调用做了高度封装,只需要调用一个方法就可以完成请求、响应、json转换

  • getForObject(url, 转换的类型.class, 提交的参数数据)
  • postForObject(url, 提交的协议体数据, 转换的类型.class)

用 RestTemplate 调用 2,3,4 项目

Ribbon

springcloud 提供的工具,对 RestTemplate 进行了增强封装,提供了负载均衡和重试的功能

item-service 高可用

启动参数 --server.port 可以覆盖yml中的端口配置

配置启动参数
--server.port=8001
复制一份
--server.port=8002

eureka高可用

添加两个服务器的profile配置文件

image.png

配置启动参数和端口
--spring.profiles.active=eureka1 --server.port=2001
--spring.profiles.active=eureka2 --server.port=2002

eureka客户端注册时,向两个服务器注册

image.png

ribbon 服务消费者

image.png

ribbon 提供了负载均衡和重试功能 它底层是使用RestTemplate进行Rest api调用

RestTemplate

RestTemplate是SpringBoot提供的一个Rest远程调用工具

常用方法
getForObject() 执行get请求
postForObject() 执行post请求

image.png

1.新建ribbon项目
2.pom.xml
添加Eureka Discovery Client 和 web 依赖
3.yml文件

spring:
application:

name: ribbon

server:
port: 3001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

4.主程序

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

//创建 RestTemplate 实例,并存入 spring 容器

@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

public static void main(String[] args) {
    SpringApplication.run(Sp06RibbonApplication.class, args);
}

}
5.controller

@RestController
public class RibbonController {

@Autowired
private RestTemplate rt;

@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
    //向指定微服务地址发送 get 请求,并获得该服务的返回结果 
    //{1} 占位符,用 orderId 填充
    return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
}

@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
    //发送 post 请求
    return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
}

/

@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
    return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
}

@GetMapping("/user-service/{userId}/score") 
public JsonResult addScore(
        @PathVariable Integer userId, Integer score) {
    return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score);
}

/

@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
    return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
}

@GetMapping("/order-service")
public JsonResult addOrder() {
    return rt.getForObject("http://localhost:8201/", JsonResult.class);
}

}
6.启动 并访问测试

ribbon负载均衡和测试

负载均衡

  1. 从 eureka 获得地址表
  2. 使用多个地址来回调用
  3. 拿到一个地址,使用 restTemplate 执行远程调用

image.png

image.png

image.png

修改sp06-ribbon项目

1.添加ribbon起步依赖
2.RestTemplate设置@loadBalanced
3.访问路径设置为服务id

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {


@LoadBalanced //负载均衡注解
@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

public static void main(String[] args) {
    SpringApplication.run(Sp06RibbonApplication.class, args);
}

}

访问路径设置问服务id

将控制层的本地地址和端口改为服务的id

例如 "http://item-service/{1}"

image.png

pom.xml文件中添加spring-retry依赖

<dependency>

<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>

</dependency>

yml文件配置ribbon重试

spring:
application:

name: ribbon

server:
port: 3001

eureka:
client:

service-url:
  defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
  

ribbon:
MaxAutoRetriesNextServer: 2 更换实例的次数
MaxAutoRetries: 1 当前实例重试次数 尝试失败会更换下一个实例
OkToRetryOnAllOperations: true 可不写 默认支队GET请求重试 默认为false 当设置为true时 对POST等所有的类型请求进行都重试

主程序设置RestTemplate的请求工厂的超时属性

@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {

@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
    SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
    f.setConnectTimeout(1000);
    f.setReadTimeout(1000);
    return new RestTemplate(f);
    
    //RestTemplate 中默认的 Factory 实例中,两个超时属性默认是 -1,
    //未启用超时,也不会触发重试
    //return new RestTemplate();
}

public static void main(String[] args) {
    SpringApplication.run(Sp06RibbonApplication.class, args);
}

}

item-service 的 ItemController 添加延迟代码,以便测试 ribbon 的重试机制

///--设置随机延迟

    if(Math.random()<0.6) { 
        long t = new Random().nextInt(5000);
        log.info("item-service-"+port+" - 暂停 "+t);
        Thread.sleep(t);
    }
    
    














查看原文

赞 1 收藏 0 评论 0

认证与成就

  • 获得 9 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-08-25
个人主页被 321 人浏览