Spring Cloud 快速上手
maven版本依赖
简介:
单体拆分成微服务:
面向服务拆分
SOA它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协议联系起来。
领域模型拆分
请领域专家来专门设计领域模型
1、后端会拆分成N个服务器,N个服务器为客户端提供服务。
2、客户端所在的视角看到服务器是一个cloud,只需要调用一个服务器(Ip+port)网关,由网关路由到不同的服务器中进行访问。
3、后端服务器内部,需要进行数据交互。进行数据交互必须要知道对方的
IP+Port才能访问。
4、在数据交互过程中可能出现复杂的应用,这时候我们需要简化、方便维护,这我们就叫做:服务治理—>拆分成多个服务器,服务器之间的交互、维护、管理会变得非常的复杂,这时候我们就需要进行服务治理
1、服务发现
2、集中配置
3、断熔(服务降级一起使用)
4、负载均衡(其实本质是冗余)
5、网关(提供一个统一的访问入口)
6、协调机制
7、分布式事务
8、链路追踪
三、服务器之间的调用进行数据传输
1、协议:TPC/IP http
2、方式:同步和 异步
openFeign–>http协议同步调用
rabbitMQ---->tcp/ip协议,异步调用
1、feign—>调用远程就像调用本地一样
服务器调用服务器
openFeign
被调用方啥都不用变化
调用方要引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
启动类加上
@EnableFeignClients //把feign引入
调用方调用被调用的方法
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(name = "movie",url = "http://127.0.0.1:8080/")
public interface IMovieServiceRemote {
@RequestMapping("/move")
public String test();
}
@RequestMapping("/move")
public String test();
是与被调用的一致
url = "http://127.0.0.1:8080/ 被调用方的地址
name 可以随意
服务注册与发现
spring cloud nacos
细节问题:
MySQL数据库版本必须高于或者等于5.7
提供的数据库少了一张日志表,自己手动创建
nacos 默认就是一个集群,单机模式启动命令 startup.sh -m standalone
被调用方加入nacos注册中心
pom
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
启动类
server:
port: 8000
spring:
application:
name: movie
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
那么必须要在nacos就是通过找到ip 端口号
调用端通过 spring.application.name 找到
@FeignClient(name = "movie")
public interface IMovieServiceRemote {
@GetMapping("test")
public String test();
@GetMapping("movieNameInfo/{movieName}")
public MovieVo movieNameInfo(@PathVariable("movieName")String movieName);
@PostMapping("saveMovie")
public boolean saveMovie(@RequestBody MovieVo vo);
}
调用端也需要注册到nacos
openfeign远程调用不需要IP加端口号,使用springboot项目启动名
1、服务发现 用nacos来实现
2、负载均衡 用netflix Ribbon 来实现---->算法是轮询
负载均衡常用的算法:轮询 (session不同步) hash一致算法 iphash(session同步)
nacos 自带 netflix Ribbon
只需要创建一个同一个被调用的工程
注意 spring.application.name 名字一样
请求就能轮询分发
使用 2.1.0版本,特别注意排除 ribbon依赖,不然loadbalancer 无效
<dependency>
<groupid>com.alibaba.cloud</groupid>
<artifactid>spring-cloud-starter-alibaba-nacos-discovery</artifactid>
<exclusions>
<exclusion>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-netflix-ribbon</artifactid>
</exclusion>
</exclusions>
</dependency>
加入 loadbalancer pom坐标
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-loadbalancer</artifactid>
</dependency>
消息队列MQ
消息推送与消费
多个消息消费者消费同一个消息存在并发问题,
如何解决 加锁
锁
乐观锁
在查询数据的时候不会加锁,在更新数据之前如果有人更新数据,就不会更新数据
实现
取出记录时,获取当前version
更新时,带上这个version 执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败
悲观锁
每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起
实现
传统的关系型数据库使用这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
Java 里面的同步 synchronized 关键字的实现
分类:
-
共享锁【shared locks】又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
-
排他锁【exclusive locks】又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改。
TCP/IP协议传输的是二进制,所以对象需要序列化
Java操作RabbitMQ
pom 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置
rabbitmq:
host: ip地址
username: guest
password: guest
port: 5672
如果新配置号的rabbitmq 用户名和密码 默认:guest
将rabbit mq 交割spring 管理
@Configuration
public class MQConfig {
//注入工厂(已经预加载)
@Autowired
CachingConnectionFactory connectionFactory;
//rabbitmq模板
@Bean(value = "rabbitTemplate")
public RabbitTemplate rabbitTemplate(){
return new RabbitTemplate(connectionFactory);
}
//创建了一个队列
@Bean
public Queue movieQueue(){
return new Queue("movieQueue");
}
//创建了一个交换机
@Bean
public DirectExchange movieExchange(){
return new DirectExchange("movieExchange");
}
//把队列与交换机进行绑定
@Bean
public Binding directExchangeToMovieQueue(DirectExchange movieExchange,Queue movieQueue){
return BindingBuilder.bind(movieQueue).to(movieExchange).with("movie.basic");
}
}
向某一个队列推送消息,是通过交换机向队列推送消息,队列与交换机通过key
使用默认交换机
推送
@Autowired
RabbitTemplate rabbitTemplate;
@GetMapping("sendInfo/{info}")
public boolean sendInfo(@PathVariable("info")String info){
try{
rabbitTemplate.convertAndSend("movieExchange","movie.basic",info);
}catch (Exception e){
return false;
}
return true;
}
监听消息,只需要监听队列
@RabbitListener(queues = "movieQueue")
public void movieInfo(String info){
System.out.println("服务器推送给我的消息:"+info);
}
推送对象
对象需要序列化
序列化与反序列化需要包名、类名、属性一致
@GetMapping("sendVO")
public boolean sendVO(){
try{
MovieVo vo=new MovieVo();
vo.setMovieName("沙丘");
vo.setMovieDate("2021-11-17");
rabbitTemplate.convertAndSend("movieExchange","movie.basic",vo);
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
监听对象
@RabbitListener(queues = "movieQueue")
public void MovieVO(MovieVo vo){
System.out.println(vo);
}
topic 交换机 模糊匹配 a.b a.c a.f (a.#)—>格式 *(1) ,#(0-N)
其它不变,只是交换机绑定队列那个key用了模糊匹配的格式,在推送消息的时候,就可以用复合模糊匹配的路由
fanout 交换机 订阅/发布
这个交换机不需要key,但需要用null占位