阿里P8重磅总结:看完别说不会了哦,SpringBoot「完结篇

2022-05-31  本文已影响0人  程序员阿远

一、 缓存

1)JSR107

Java Caching定义了5个核心接口,分别是

定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。

定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。

是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。

是一个存储在Cache中的key-value对。

每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

image.png

2)Spring缓存抽象

Spring从3.1开始定义了
org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术,并支持使用JCache(JSR-107)注解简化我们开发。

  1. 确定方法需要被缓存以及他们的缓存策略
  2. 从缓存中读取之前缓存存储的数据
image.png

3)几个重要缓存注解

image.png image.png image.png

4)缓存使用

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
复制代码
@MapperScan("cbuc.life.cache.mapper")
@SpringBootApplication
@EnableCaching
public class Springboot01CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot01CacheApplication.class, args);
    }
}
复制代码
  1. @Cacheable
  2. @CacheEvict
  3. @CachePut

将数据保存在ConcurrentMap<Object, Object>中
开发中使用缓存中间件:redis、memcached、ehcache

5)几大属性

  1. cacheNames/value: 指定缓存组件的名字,将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存
  2. key: 缓存数据使用的key,可以用它来指定。默认是使用方法参数的值
  3. keyGenerator: key的生成器,可以自己指定key的生成器的组件id
  4. cacheManager:指定缓存管理器,或者 cacheResolver 指定获取解析器
  5. condition:指定符合条件的情况下才缓存
  6. unless:否定缓存,当 unless 指定的条件为true,方法的返回值就不会被缓存
  7. sync:是否使用异步模式
image.png image.png

2)

image.png

3)

image.png

4)

image.png

6)整合redis

  1. 引入spring-boot-starter-data-redis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
复制代码

2.application.yml配置 redis 连接地址

spring.redis.host=118.24.44.169
复制代码

3.使用ReditTemplate操作redis

image.png

4.配置缓存

image.png

5.自定义redisTemplate

image.png

6.将自定义的redisTemplate注册进RedisCacheManager

image.png

二、消息

*大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力 *

消息服务中两个重要概念:

当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。

消息队列主要有两种形式的目的地

点对点式

发布订阅式

JMS(Java Message Service)JAVA消息服务

AMQP(Advanced Message Queuing Protocol)

image.png

Spring支持

Spring Boot自动配置

RabbitMQ

简介
RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。
核心概念

image.png

运行机制

AMQP 中消息的路由过程和 Java 开发者熟悉的 JMS 存在一些差别,AMQP 中增加了 Exchange 和Binding 的角色。生产者把消息发布到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器的消息应该发送到哪个队列。

image.png

Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers 。headers 匹配 AMQP 消息的* header 而不是路由键*, headers 交换器和 direct 交换器完全一致,但性能差很多,目前几乎用不到了,所以直接看另外三种类型:

  1. direct
image.png

消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog”,则只转发 routing key 标记为“dog”的消息,不会转发“dog.puppy”,也不会转发“dog.guard”等等。它是完全匹配、单播的模式。

2.fanout

image.png

每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。

3.topic

image.png

topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“”。#匹配0个或多个单词,匹配一个单词。

整合RabbitMQ

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
复制代码
spring.rabbitmq.host=118.24.44.169
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
复制代码
image.png

示例

@Configuration
public class MyAMQPConfig {
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}
复制代码
/**
  * 注入我们需要的两个bean
  */
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
AmqpAdmin amqpAdmin;
复制代码
image.png

Service 中接收消息示例
注意要在启动类中开启基于注解的RabbitMQ模式 :@EnableRabbit

image.png

三、检索

我们的应用经常需要添加检索功能,开源的 ElasticSearch是目前全文搜索引擎的首选。他可以快速的存储、搜索和分析海量数据。Spring Boot通过整合Spring Data ElasticSearch为我们提供了非常便捷的检索功能支持;

Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard(分片)的方式保证数据安全,并且提供自动resharding的功能,github等大型的站点也是采用了ElasticSearch作为其搜索服务

概念

以 员工文档 的形式存储为例:一个文档代表一个员工数据。存储数据到 ElasticSearch 的行为叫做索引,但在索引一个文档之前,需要确定将文档存储在哪里。
一个 ElasticSearch 集群可以 包含多个 索引,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。

类似关系:

image.png

整合ElasticSearch

SpringBoot 默认支持两种技术来和 ES 交互

导入jest的工具包

<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
    <version>5.3.3</version>
</dependency>
复制代码
  1. 版本适配问题升级SpringBoot版本安装对应版本的ES
  2. 导入工具包
<!--SpringBoot默认使用SpringData ElasticSearch模块进行操作-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
复制代码
  1. 安装Spring Data 对应版本的ElasticSearch
  2. application.properties配置
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=118.24.44.169:9301
复制代码
  1. 测试
@Document(indexName = "cbuc",type = "book")
public class Book {
    private Integer id;
    private String bookName;
    private String author;
    //省略 get/set方法
}

public class Article {
    @JestId
    private Integer id;
    private String author;
    private String title;
    private String content;
    //省略 get/set 方法
}
复制代码
public interface BookRepository extends ElasticsearchRepository<Book,Integer> {
    //参照 https://docs.spring.io/spring-data/elasticsearch/docs/3.0.6.RELEASE/reference/html/
   public List<Book> findByBookNameLike(String bookName);
}
复制代码
@Autowired
JestClient jestClient;
@Autowired
BookRepository bookRepository;
复制代码

法1:
存入index:

Book book = new Book();
book.setId(1);
book.setBookName("西游记");
book.setAuthor("吴承恩");
bookRepository.index(book);
复制代码

取:

for (Book book : bookRepository.findByBookNameLike("游")) {
    System.out.println(book);
}
复制代码

法2:

//给Es中索引(保存)一个文档:
Article article = new Article();
article.setId(1);
article.setTitle("测试");
article.setAuthor("cbuc");
article.setContent("测试检索");
//构建一个索引功能
Index index = new Index.Builder(article).index("cbuc").type("news").build();
//执行
jestClient.execute(index);
复制代码

测试搜索:

//查询表达式
@Test
public void search(){
    //查询表达式
    String json ="{\n" +
            "    \"query\" : {\n" +
            "        \"match\" : {\n" +
            "            \"content\" : \"hello\"\n" +
            "        }\n" +
            "    }\n" +
            "}";
    //更多操作: https://github.com/searchbox-io/Jest/tree/master/jest
    //构建搜索功能
    Search search = new Search.Builder(json).addIndex("cbuc").addType("news").build();
    //执行
    try {
        SearchResult result = jestClient.execute(search);
        System.out.println(result.getJsonString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
复制代码

四、任务

异步任务

在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的。但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题。
两个注解:

使用:

  1. 启动类开启异步注解功能
@EnableAsync  //开启异步注解功能
@SpringBootApplication
public class Springboot04TaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot04TaskApplication.class, args);
    }
}
复制代码

2.Service:

@Service
public class AsyncService {
    //告诉Spring这是一个异步方法
    @Async
    public void test(){
        try {
            //当前线程睡眠 3 秒页面也能及时响应
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("处理数据中...");
    }
}
复制代码

3.Controller:

@RestController
public class AsyncController {
    @Autowired
    AsyncService asyncService;
    @GetMapping("/hello")
    public String hello(){
        asyncService.test();
        return "success";
    }
}
复制代码

定时任务

项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息。Spring为我们提供了异步执行任务调度的方式,提供TaskExecutor、TaskScheduler接口。
两个注解:

cron表达式:

image.png image.png

使用:

  1. 启动类开启基于注解的定时任务:
@EnableScheduling //开启基于注解的定时任务
@SpringBootApplication
public class Springboot04TaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot04TaskApplication.class, args);
    }
}
复制代码

2.Service:

image.png

邮件任务

使用

  1. 邮件发送需要引入spring-boot-starter-mail
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
复制代码
  1. 定义MailProperties内容,配置在application.properties中
spring.mail.username=(发送人qq账号)
spring.mail.password=(发送人qq密码)
spring.mail.host=smtp.qq.com
spring.mail.properties.mail.smtp.ssl.enable=true
复制代码

3.自动装配JavaMailSender

image.png

五、分布式系统

单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

Zookeeper和Dubbo

ZooKeeper:
是一个分布式的,开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
Dubbo:
是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。

image.png

使用:

引入dubbo和zkclient相关依赖

<dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>0.1.0</version>
</dependency>

<!--引入zookeeper的客户端工具-->
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
      <groupId>com.github.sgroschupf</groupId>
      <artifactId>zkclient</artifactId>
      <version>0.1</version>
</dependency>
复制代码

配置dubbo的扫描包和注册中心地址

  dubbo.application.name=provider-ticket
  dubbo.registry.address=zookeeper://118.24.44.169:2181
  dubbo.scan.base-packages=cbuc.life.ticket.service
复制代码

使用@Service发布服务

  @Component
  @Service //将服务发布出去  注意注解 Service是dubbo包下的
  public class TicketServiceImpl implements TicketService {
      @Override
      public String getTicket() {
          return "《我和我的祖国》";
      }
  }
复制代码

引入dubbo和zkclient相关依赖

  <dependency>
      <groupId>com.alibaba.boot</groupId>
      <artifactId>dubbo-spring-boot-starter</artifactId>
      <version>0.1.0</version>
  </dependency>

  <!--引入zookeeper的客户端工具-->
  <!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
  <dependency>
      <groupId>com.github.sgroschupf</groupId>
      <artifactId>zkclient</artifactId>
      <version>0.1</version>
  </dependency>
复制代码

引用服务

  @Service
  public class UserService{
      @Reference    // 使用 Reference 引入服务
      TicketService ticketService;
      public void hello(){
          String ticket = ticketService.getTicket();
          System.out.println("调用买票服务:"+ticket);
      }
  }
复制代码

Spring Boot和Spring Cloud

Spring Cloud是一个分布式的整体解决方案。Spring Cloud 为开发者提供了在分布式系统(配置管理,服务发现,熔断,路由,微代理,控制总线,一次性token,全局锁,leader选举,分布式session,集群状态)中快速构建的工具,使用Spring Cloud的开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。
SpringCloud分布式开发五大常用组件

使用

<dependencyManagement>
    <dependencies>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Edgware.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

    </dependencies>
</dependencyManagement>
复制代码
server:
  port: 8761
eureka:
  instance:
    hostname: eureka-server  # eureka实例的主机名
  client:
    register-with-eureka: false #不把自己注册到eureka上
    fetch-registry: false #不从eureka上来获取服务的注册信息
    service-url:
      defaultZone: http://localhost:8761/eureka/
复制代码
@EnableEurekaServer    
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
复制代码

引入依赖

image.png

配置application

image.png

启动类

image.png

service

image.png

controller

image.png

引入依赖

image.png

配置application

image.png

启动类

image.png

controller (通过使用RestTemplate调用服务)

image.png
上一篇 下一篇

猜你喜欢

热点阅读