Spring Cloud:概述,服务注册和服务发现,多机部署和负载均衡
什么是微服务
就是将一个大型的应用程序拆分成多而小的独立的服务模块,每个服务模块围绕某个业务功能建立,具有独立的数据库,服务栈,并通过轻量级的通信协议进行交互。
单体架构
就是将所有的业务和功能都打包在一个jar包中,这就是单体架构。
单体架构的优点:构建简单,部署简单,一个项目包括了多个功能,省去了不同项目交互调用的麻烦,
单体架构的缺点:由于网站用户越来越多,需求也越来越多,功能需求也越来越多,导致后端的压力越来越大,负载越来越高,一个很小的问题,就会引起很大的连锁反应,冗余性太高。
集群和分布式
集群:是将一个系统部署到多个服务器上,每个服务器都能提供系统的所有服务,多个服务器通过负载均衡调度完成工作,每个服务器称为集群的节点。
分布式:将一个系统拆分成多个子系统,多个子系统部署到多个服务器上,多个服务器上的子系统协同完成工作。
集群是多个计算机在做相同的事,分布式是多个计算机在不同的事情。
微服务架构
就是每个服务只负责一种特定的业务功能,每个服务独立构建,测试部署,互不干扰,可用不同的技术栈。
Spring-Cloud
Spring-Cloud就是微服务的一种解决方案
单项依赖
微服务之间需要做到单项依赖,严禁循环依赖,双向依赖。
DependencyManagement和Dependencies
dependencies
将所依赖的jar直接加入到项目中,子项目也会继承该依赖。
DependencyManagement
只是申明依赖,并没有jar包的引入,如果子项目需要用到相关的依赖,需要自己引入
<properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><java.version>17</java.version><mybatis.version>2.2.0</mybatis.version> <!-- 使用已知的兼容版本 --><mysql.version>8.0.33</mysql.version><spring-cloud.version>2022.0.3</spring-cloud.version></properties>
<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version> <!-- 采用最新兼容版本 --></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.version}</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>${mybatis.version}</version><scope>test</scope></dependency></dependencies></dependencyManagement>
在创建子项目的时候,注意pom文件申明项目的依赖和构建插件
简单创建好父子工程
来进行远程调用
根据订单查询订单信息时,根据订单里面的产品Id,获取产品的详细信息
可以采用RestTemplate
RestTemplate
是 Spring 提供的用于 同步 HTTP 请求的客户端工具类,它可以方便地发送 HTTP 请求并接收响应,支持 GET、POST、PUT、DELETE 等多种请求方法
定义RestTemplate
@Configurationpublic class BeanConfig {@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}}
@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public OrderInfo selectOrderById(Integer orderId){OrderInfo orderInfo = orderMapper.selectOrderById(orderId);String url = "http://127.0.0.1:9090/product/"+orderInfo.getProductId();ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}
测试一下
但是rul是写死的,如果要是修改IP,就得修改代码
注册中心
CPA理论
一致性:CPA理论中的一致性,指的是强一致性,所有节点在同一时间既有相同的数据。
可用性:保证每个请求都有响应。
分区容错性:当出现网络分区后,系统仍然能对外提供服务。
这三个基本需求,不能一次性全部满足,最多只能满足两个,CA或者CP架构。
Eureka
Eureka是Netflix开发的基于REST的服务发现框架,主要⽤于服务注册,管理,负载均衡和服务故障 转移,是常见的注册中心。
EurekaServer的搭建
EurekaServer是一个独立的微服务
注意:要引入eureka-server依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
构建插件
build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>org.example.eurekaserver.EurekaServerApplication</mainClass></configuration></plugin></plugins></build>
注意在启动类上添加@EnableEurekaServer注解,用来开启eureka注册中心
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class,args);}
}
配置文件
server:port: 10010
spring:application:name: eureka-server
eureka:instance:hostname: localhostclient:fetch-registry: false # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为falseregister-with-eureka: false # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false.service-url:# 设置Eureka Server的地址,查询服务和注册服务都需要依赖这个地址defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
logging:pattern:console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'
启动服务
在远程调⽤时,从eureka-server拉取product-service的服务信息,url则不会被写死
@Slf4j
@Service
public class OrderService { @Autowiredprivate DiscoveryClient discoveryClient;public OrderInfo selectOrderById(Integer orderId){OrderInfo orderInfo = orderMapper.selectOrderById(orderId);//从Eureka中获取服务列表List<ServiceInstance> instances = discoveryClient.getInstances("product-service");String uri = instances.get(0).getUri().toString();String url = uri+"/product/"+orderInfo.getProductId();log.info("远程调用url:{}", url);ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
负载均衡
这行代码是获得了第一个服务实例,但是如果有多个服务实例呢?
EurekaServiceInstance instance = (EurekaServiceInstance) instances.get(0);
在创建几个实例后我们发现访问的都是同一台机器,其它的并没有进行访问。
修改代码,使其轮流访问
int index = atomicInteger.getAndIncrement() % instances.size();
public OrderDetail selectOrderById(Integer orderId){OrderDetail orderDetail = orderMapper.selectOrderById(orderId);
// String url="http://localhost:9090/product/"+orderDetail.getProductId();
// if (instances == null || instances.isEmpty()) {
// log.warn("No available instances for product-service");
// throw new IllegalStateException("No available instances for product-service");
// }List<ServiceInstance> instances= discoveryClient.getInstances("product-service");int index=count.getAndIncrement() % instances.size();URI uri = instances.get(index).getUri();String url=uri+"/product/"+orderDetail.getProductId();log.info("url:{}",url);ProductDetail productDetail = restTemplate.getForObject(url, ProductDetail.class);orderDetail.setProductDetail(productDetail);return orderDetail;}
请求被均衡的分配在了不同的实例上,这就是负载均衡
负载均衡:当服务流量增⼤时,通常会采⽤增加机器的⽅式进⾏扩容,负载均衡就是⽤来在多个机器或者其他资源 中,按照⼀定的规则合理分配负载
SpringCloudLoadBalancer
SpringCloudLoadBalancer这个组件是用来实现客户端的负载均衡
添加 @LoadBalanced 注解
@LoadBalancerClient(name = "product-service",configuration = CustomLoadBalancerConfiguration.class)
@Configuration
public class BeanConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}
}
修改IP端⼝号为服务名称
public OrderDetail selectOrderById(Integer orderId){OrderDetail orderDetail = orderMapper.selectOrderById(orderId);String url="http://product-service/product/"+orderDetail.getProductId();log.info("url:{}",url);ProductDetail productDetail = restTemplate.getForObject(url, ProductDetail.class);orderDetail.setProductDetail(productDetail);return orderDetail;}
负载均衡策略
负载均衡策略是一种思想,有轮询和随机选择两种,就是字面意思。
⾃定义负载均衡策略
//负载均衡策略
public class CustomLoadBalancerConfiguration {@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);}
}
使⽤@LoadBalancerClient或@LoadBalancerClients注解,可以对不同的服务提供⽅配置不同的客⼾端负载均衡算法策略
name:对哪个服务生效
configuration:在这写负载均衡策略,用哪个负载均衡策略实现
希望能对大家有所帮助!!!!