练习
使数组和能被 P 整除
给你一个正整数数组 nums,请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
子数组 定义为原数组中连续的一组元素。
示例 1:
输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6 。
示例 2:
输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9 。
示例 3:
输入:nums = [1,2,3], p = 3
输出:0
解释:和恰好为 6 ,已经能被 3 整除了。所以我们不需要移除任何元素。
示例 4:
输入:nums = [1,2,3], p = 7
输出:-1
解释:没有任何方案使得移除子数组后剩余元素的和被 7 整除。
示例 5:
输入:nums = [1000000000,1000000000,1000000000], p = 3
输出:0
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
1 <= p <= 109
class Solution {
public int minSubarray(int[] nums, int p) {
int sum = 0;
for (int i : nums) {
sum = (sum + i) % p;
if(sum==0){
return 0;
}
}
Map<Integer, Integer> last = new HashMap<>();
//last[0] = -1,如果 target = 0,那么 j = -1,说明我们可以删掉 [0,..i] 这整一段前缀数组
last.put(0, -1);
int n = nums.length;
int ans = n;
int cur = 0;//为了记录i前缀和
for (int i = 0; i < n; ++i) {
cur = (cur + nums[i]) % p;//取模
//想要找到一个位置j 让该位置之前的和与p取模为0
//sum-cur 是截至当前i的位置之后的数和的模
//所以 是找到一个位置j在【0-i】里 算出【0-j】的和的模 去除【j+1,i】
//target 对应([0,j])% p,sum 是([0,n])%p, ,cur是([0, i])%p,
//怎么算 就是[0-j]的模与[i+1-n]的模的和取模为0
//即 (target + sum -cur)取模p为0
int target = (cur - sum + p) % p;
//判断last是否存在target键值
if (last.containsKey(target)) {
//
ans = Math.min(ans, i - last.get(target));
}
last.put(cur, i);
}
return ans == n ? -1 : ans;
}
}
//可以把nums看成3段([0,j],[j+1, i], [i+1, n])都是闭区间,
//target 对应([0,j])% p,sum 是([0,n])%p, ,cur是([0, i])%p,
//target来确定删除子数组的起点位置,前缀和确定子数组的末尾位置
原谅我脑仁小,到现在也是似懂非懂
好吧,就是没看懂,不理解int target = (cur - sum + p) % p;
就算我写了这么多的解释,也是看着别人给出来的解答思考分析最后写上去的
就跟数学上,我知道是这个做,但是我不理解,听完课后我能写出解题思路,但是我还是不会做
希望以后回来看看能明白 因为我已经看了快俩小时了 头疼
八股
深拷贝和浅拷贝区别是什么?
浅拷贝是共用一个内存地址,即引用一个对象地址,一方改动,另一方也会跟着变动
深拷贝是将对象和值都复制过来,一方改动,另一方不跟着变动
jsp 和 servlet 有什么区别?
servlet就是编译后的jsp 因为jvm处理不了jsp 需要web容器去将jsp代码编译成jvm课识别的java类
jsp更适合页面显示,servlet更适合逻辑控制
servlet没有内置对象 jsp是servlet的简化版,有内置对象都需要通过httpservletrequest对象
httpservletresponse对象和httpservlet 对象得到
jsp 有哪些内置对象?作用分别是什么?
request封装客户端请求 即get post请求参数
response 封装服务端对客户端的想要
pagecontext 通过该对象获取其他对象
session 封装用户会话对象
application 封装服务器运行环境对象
out 输出服务器输出流对象
config web配置
page jsp页面本身
exception 页面抛出异常
说一下 jsp 的 4 种作用域?
page表示一个页面对象和属性
request代表与客户端发出的请求对象和属性 请求体
session代表用户与服务端建立一次会话的对象和属性,也就是连接,跟某个用户相关的数据应该放在用户自己的session中。
application全局作用域 实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。
基于阳哥第二季的SpringCloud(第二天)
工程重构-对实体类的优化
昨天发现 消费者和支付模块有重复的实体
需要修改
新建一个cloud-api-commons
里面的pom新增了这个
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.1.0</version>
</dependency>
之后把实体类复制过来
再在8001 80端口下pom引入自己的api
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
注意 怕以后我还会犯这个错误
文件复制粘贴了,但是包名没改 那肯定是一直红
不是maven没引用过去, 而是包名不对 需要改了
Eureka
基础理解
什么是服务治理
Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
什么是服务注册与发现
Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
![](https://i-blog.csdnimg.cn/blog_migrate/0b26f3c2823baa1709f8d6373e626073.png)
Eureka包含两个组件:Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
单机Eureka构建
区别
以前的老版本(当前使用2018)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
现在新版本(当前使用2020.2)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
新增
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
pom
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
yml
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动器
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
支付微服务入住EurekaServer 成为服务提供者 provider
支付pom里面
新增
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml里面
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
启动类新增
@EnableEurekaClient
![](https://i-blog.csdnimg.cn/blog_migrate/bd74ef99f85d7a302ddaea71907b4473.png)
![](https://i-blog.csdnimg.cn/blog_migrate/17e05b044b9d83e5a76d8e53f28b7a0f.png)
消费微服务入住EurekaServer 成为服务消费者 consumer
消费pom里面
新增
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yml里面
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
启动类新增
@EnableEurekaClient
http://localhost/consumer/payment/get/31
register-with-eureka: true 当改为false
就不显示入住情况了
![](https://i-blog.csdnimg.cn/blog_migrate/be3060a20ff1388c7a0989cbe12d827b.png)
Eureka集群
原理
![](https://i-blog.csdnimg.cn/blog_migrate/56b27a86189d5e3b7729e9f9f846f97c.png)
问题:微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用,所以
解决办法:搭建Eureka注册中心集群 ,实现负载均衡+故障容错
复制一台一模一样的server7002
写代码之前 请在自己的电脑上 host添加
![](https://i-blog.csdnimg.cn/blog_migrate/b5b10b33f1a25d68bb50f06270c97976.png)
7001
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
7002
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
完事后去试一下
![](https://i-blog.csdnimg.cn/blog_migrate/c2e0044f1f8b8cfd10a3cd482720519b.png)
可以看出来 出现相互守望的地址了
7001支付服务yml 80消费服务
7001支付服务
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
80消费服务
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
http://localhost/consumer/payment/get/31
支付微服务集群配置
参考cloud-provider-payment8001
一个字 粘
除了控制器
因对外暴露的名字都是一样的
所以需要端口(负载均衡)
8001
@RestController
@Slf4j
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
8002
@RestController
@Slf4j
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
测试 只能进入8001
负载均衡
在80消费者端口将写死的8001改成使用的支付服务的名字
//public static final String PAYMENT_SRV = "http://localhost:8001";
// 通过在eureka上注册过的微服务名称调用
public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";
使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
80端口下的config
@Configuration
public class ApplicationContextConfig
{
@Bean
@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate restTemplate()
{
return new RestTemplate();
}
}
Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了。
actuator微服务信息完善
![](https://i-blog.csdnimg.cn/blog_migrate/6daac7dab8f866e96bf41392ed58e5d9.png)
主机名称:服务名称修改
当前问题 含有主机名称
修改cloud-provider-payment8001 8002通用 包括80消费者
# 单机版
instance:
instance-id: payment8001
修改之后
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机
defaultZone: http://localhost:7001/eureka
# 集群
# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
# 单机版
instance:
instance-id: payment8001
访问信息有IP信息提示
当前问题 没有IP提示
修改cloud-provider-payment8001
# 单机版
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示IP地址
修改之后
`eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机
defaultZone: http://localhost:7001/eureka
# 集群
# defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
# 单机版
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示IP地址
服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
修改cloud-provider-payment8001的Controller
@Resource
private DiscoveryClient discoveryClient;
....
....
....
....
@GetMapping(value = "/payment/discovery")
public Object discovery()
{
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
8001主启动类
@EnableDiscoveryClient
@EnableDiscoveryClient
自测
先要启动EurekaServer
再启动8001主启动类,需要稍等一会儿
http://localhost:8001/payment/discovery
Eureka自我保护
Eureka自我保护
![](https://i-blog.csdnimg.cn/blog_migrate/517ffd508f4e2c954b87e20dc36c250b.png)
红色就是自动保护的显示
故障现象
Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据
也就是不会注销任何微服务。
导致原因
一句话:某时刻某一个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存
属于CAP里面的AP分支
在自我保护模式中,Eureka Server会保护服务注册表中的信息,
不再注销任何服务实例。
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,
EurekaServer不会立刻将EurekaClient服务剔除
什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,
EurekaServer将会注销该实例(默认90秒)。
但是当网络分区故障发生(延时、卡顿、拥挤)时,
微服务与EurekaServer之间无法正常通信,
以上行为可能变得非常危险了——因为微服务本身其实是健康的,
此时本不应该注销这个微服务。
Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),
那么这个节点就会进入自我保护模式。
怎么禁止自我保护
注册中心eureakeServer端7001
出厂默认,自我保护机制是开启的
eureka.server.enable-self-preservation=true
server:
#关闭自我保护机制,保证不可用服务被及时踢除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: true
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://eureka7002.com:7002/eureka/
server:
#关闭自我保护机制,保证不可用服务被及时踢除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
使用eureka.server.enable-self-preservation = false 可以禁用自我保护模式
关闭效果
在eurekaServer端7001处设置关闭自我保护机制
![](https://i-blog.csdnimg.cn/blog_migrate/3dc5c30e669c0c0412b6d16e470b47c8.png)
生产者客户端eureakeClient端8001
默认
eureka.instance.lease-renewal-interval-in-seconds=30
单位为秒(默认是30秒)
eureka.instance.lease-expiration-duration-in-seconds=90
单位为秒(默认是90秒)
配置
#心跳检测与续约时间
#开发时设置小些,保证服务关闭后注册中心能即使剔除服务
instance:
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
测试
7001和8001都配置完成
先启动7001再启动8001
先关闭8001
马上被删除了
Failed to bind properties under 'eureka.client.service-url' to java.util.Map<java.lang.String, java.lang.String>问题
![](https://i-blog.csdnimg.cn/blog_migrate/b350f92078cf52c28a984303149ee57b.png)
总结下
今天把eureka学完了 虽然在2020都过期了,但是还是学了,收获不小
希望明天会更好