SpringCloud全面学习笔记之初尝美妙篇
目录
- 前言
- 初识微服务
- 单体架构
- 分布式架构
- 微服务架构
- 初见SpringCloud
- 微服务治理
- 分布式服务架构案例
- 微服务组件及使用
- Eureka注册中心
- 提供者和消费者
- Eureka的结构和作用
- 搭建Eureka服务
- 注册服务
- 服务发现
- Eureka注册服务总结
- Ribbon负载均衡原理
- 负载均衡原理
- 负载均衡策略
- 懒加载
- Nacos注册中心
- 认识安装nacos
- Nacos快速入门
- Nacos服务分级存储模型
- Nacos环境隔离
- Nacos和Eureka的区别
- Nacos管理配置
- 统一配置管理
- 配置热更新
- 配置共享
- 搭建Nacos集群
- Feign远程调用
- Feign替代RestTemplate
- 自定义配置
- Feign的性能优化
- Feign的最佳实践
- Gateway服务网关
- 初识Gateway网关
- Gateway快速入门
- 断言工厂
- 过滤器工厂
- 全局过滤器
- 跨域问题
- 未完待续
前言
本篇是记录黑马的SpringCloud学习过程中的笔记,该篇为实用篇的上篇,详尽记录了微服务架构,Eurake注册中心,Nacos注册及配置管理中心,Ribbon,Feign和Gateway网关;而Docker,MQ,ES等服务组件在实用篇下篇继续记录,最后感谢您的阅览,愿您终有所获
初识微服务
单体项目随身业务的增加,不可避免的就是项目越来越庞大,很不利于后期项目的维护,导致项目结构变的很臃肿,耦合度高,所以,现在的服务架构也从单体项目,演变为分布式和微服务架构
单体架构
单体架构
:将业务的所有功能集中在一个项目中开发,打成一个包部署到服务器上
优点是
- 简单便捷
- 易上手
- 操作难度低
缺点是
- 随着业务的增加结构逐渐臃肿
- 耦合度较高,不易于维护
分布式架构
分布式架构
:根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。
这里引用颜老师的一句话
分布式的核心就一个字:拆。只要是将一个项目拆分成了多个模块,并将这些模块分开部署,那就算是分布式。
分布式的拆分可以分为水平拆分和垂直拆分
水平拆分
字面上理解,水平拆分就是按照三层模型来拆,“三层架构”拆分成 表示层(jsp+servlet)、业务逻辑层(service)和数据访问层(dao),然后再分开部署各个服务器上,之间通过dubbo或RPC进行进行整合
垂直拆分
根据业务进行拆分。例如,可以根据业务逻辑,比如常见的电商系统,可以把用户模块当作一个独立的项目,同理,订单,聊天也是可以拆分为一个独立项目的。==显然这三个拆分后的项目,仍然可以作为独立的项目使用。==像这种拆分的方法,就成为垂直拆分。
分布式架构的优缺点:
优点:
- 降低服务耦合
- 有利于服务升级和拓展
缺点:
- 服务调用关系错综复杂
微服务架构
微服务可以理解为一种非常细粒度的垂直拆分。例如,以上“订单项目”本来就是垂直拆分后的子项目,但实际上“订单项目”还能进一步拆分为“购物项目”、“结算项目”和“售后项目”。
微服务是不能再拆的“微小”服务,类似于“原子性”
微服务的架构四大特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
- 自治:团队独立、技术独立、数据独立,独立部署和交付
- 面向服务:服务提供统一标准的接口,与语言和技术无关
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
微服务的上述特性其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。
因此,可以认为微服务是一种经过良好架构设计的分布式架构方案
①优点:拆分粒度更小、服务更独立、耦合度更低
②缺点:架构非常复杂,运维、监控、部署难度提高
下图就是标准的微服务架构的图解
每个模块的作用与组合结构如下图关系所示
注意微服务不仅仅是SpringCloud
初见SpringCloud
SpringCloud作为Spring大家族之一,也是目前国内使用最广泛的微服务框架,SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
下面是一些常见的组件
SpringCloud底层是依赖于SpringBoot的,并且有版本的兼容关系
微服务治理
在国内最知名的就是SpringCloud和阿里巴巴的Dubbo。后来阿里出的最火热的SpringCloudAlibaba框架,兼容了前两种服务协议(Dubbo,Feign)
微服务架构落地的四种方案
分布式服务架构案例
现在来演示一个小小的服务拆分的demo示例
例如,现在我们把user模块给拆分出来,把order模块给拆分出来;步骤如下
①首先为两个项目建立各自的数据库,导入对应的数据(sql文件)创建表
②先创建boot主项目(如果是b站黑马过来的,直接导入资料文件夹下的demo项目),然后创建其他模块的项目
注意一点,cloud底层是依赖boot的,所以cloud和boot版本需要一一对应,这重要,具体对应关系如下表
例如下面是主项目工程的pom文件
主项目工程中的依赖
<dependencyManagement><dependencies><!-- springCloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
然后创建子模块项目,boot版本高了,创建好后手动降低版本,就是在parent那块,把version手动调整刷新就好了
具体项目结构如下图
然后完善子模块,先写一个基本查询业务,mapper层,service层,controller层,没什么好说的,如果用上mp的api,甚至可以更快。
接下来把user-service 和 cloud-service项目都启动起来,然后浏览器访问http://localhost:8080/order/101,可以看到订单信息的数据已经查询出来了
③实现远程调用
但是如上图其中的user属性是null,这是因为order表中没有user字段,如果是以前的单体项目就是直接来个两表联查,但是对于微服务项目,每个模块负责各自的业务,不允许业务重复,这是就需要远程调用出手了
我们需要在order-service中 向user-service发起一个http的请求,调用http://localhost:8081/user/{userId}这个接口。
大概的步骤是这样的:
- 注册一个RestTemplate的实例到Spring容器
- 修改order-service服务中的OrderService类中的queryOrderById方法,根据Order对象中的userId查询User
- 将查询的User填充到Order对象,一起返回
实现如下
在order-service项目中的启动类里,注册RestTemplate实例
@MapperScan("cn.order.mapper")
@SpringBootApplication
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}
修改order-service服务中的service层下的OrderService类中的queryOrderById方法:
@Service
public class OrderService {@Resourceprivate OrderMapper orderMapper;@Resourceprivate RestTemplate restTemplate;public Order queryOrderById(Long orderId) {// 1.查询订单Order order = orderMapper.findById(orderId);// 2.远程查询user// 2.1 url地址String url = "http://localhost:8081/user" + order.getUserId();// 2.2 发起调用User user = restTemplate.getForObject(url, User.class);// 3. 存入orderorder.setUser(user);// 4.返回return order;}
}
然后再次重启两个服务,再去浏览器访问order服务,会发现user也查询出来了
微服务组件及使用
Eureka注册中心
提供者和消费者
如上面的分布式案例,在需要其他模块数据信息时,是用远程调用对应的模块服务,其中在服务调用关系中,会有两个不同的角色:
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言。
下面一个例子便于理解
如果服务A调用了服务B,而服务B又调用了服务C,服务B的角色是什么?
- 对于A调用B的业务而言:A是服务消费者,B是服务提供者
- 对于B调用C的业务而言:B是服务消费者,C是服务提供者
因此,服务B既可以是服务提供者,也可以是服务消费者。
Eureka的结构和作用
如果服务的提供者部署了多个实例,在进行服务的远程调用时,会发生以下问题:
- order-service在发起远程调用的时候,该如何得知user-service实例的ip地址和端口?
- 有多个user-service实例地址,order-service调用时该如何选择?
- order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
而在这种集群项目结构下,Eureka义不容辞的来了
Eureka是SpringCloud中的注册中心,也是最广为人知的注册中心
结构如下
回答之前的各个问题。
问题1:order-service如何得知user-service实例地址?
获取地址信息的流程如下:
- user-service服务实例启动后,将自己的信息注册到eureka-server(Eureka服务端)。这个叫服务注册
- eureka-server保存服务名称到服务实例地址列表的映射关系
- order-service根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取
问题2:order-service如何从多个user-service实例中选择具体的实例?
- order-service从实例列表中利用负载均衡算法(例如轮询,随机,权重)选中一个实例地址
- 向该实例地址发起远程调用
问题3:order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
- user-service会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳
- 当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除
- order-service拉取服务时,就能将故障实例排除了
注意:一个微服务,既可以是服务提供者,又可以是服务消费者,因此eureka将服务注册、服务发现等功能统一封装到了eureka-client端
搭建Eureka服务
1.引入eureka依赖
注册中心服务端:eureka-server,这必须是一个独立的微服务(独立的子项目,建议创建maven项目,如果你想用boot我也没意见),只需要在它的pom文件中加入eureka的依赖即可,其他的例如版本信息和其他依赖都在父工程中加了,它是继承了父工程的依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.编写启动类
给eureka-server服务编写一个启动类,一定要添加一个@EnableEurekaServer注解,开启eureka的注册中心功能:
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {public static void main(String[] args) {SpringApplication.run(EurekaApplication.class, args);}
}
3.编写配置文件
resource文件夹下编写一个application.yml文件
server:port: 10086
spring:application:name: eureka-server
eureka:client:service-url: defaultZone: http://127.0.0.1:10086/eureka
4.启动服务
配置完毕后,可以启动服务看看是否搭建成功,访问http://localhost:10086/
注册服务
1.引入依赖
不同于上面搭建eureka服务的依赖了,这回注入的是client
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
注:在服务提供者的pom文件中引入该依赖
2.配置文件
在服务提供者user-service中,修改application.yml文件,添加服务名称、eureka服务地址
spring:application:name: userservice
eureka:client:service-url:defaultZone: http://localhost:10086/eureka
启动多个user-service实例(可选)
这里是为了演示一个服务有多个实例的场景时,轮询策略远程调用,我们添加一个SpringBoot的启动配置,再启动一个user-service。
首先,复制原来的user-service启动配置:
然后,在弹出的窗口中,作一下配置:
现在,SpringBoot窗口会出现两个user-service启动配置,第一个是8081端口,第二个是8082端口。
启动新添的user-server实例
现在再访问http://localhost:10086 看看服务是否已经注册成功
服务发现
将order-service的逻辑修改:向eureka-server拉取user-service的信息,实现服务发现。
1.引入依赖
服务发现、服务注册统一都封装在eureka-client依赖,因此这一步与服务注册时一致。
在order-service的pom文件中,引入下面的eureka-client依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.配置文件
服务发现也需要知道eureka地址,因此第二步与服务注册一致,都是配置eureka信息:
在order-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:application:name: orderservice
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
3.服务拉取和负载均衡
最后,我们要去eureka-server中拉取user-service服务的实例列表,并且实现负载均衡。
在服务消费者order-service的OrderApplication中,给RestTemplate这个Bean添加一个@LoadBalanced注解:轮询策略
修改order-service服务中的OrderService类中的queryOrderById方法。修改访问的url路径,用服务名代替ip、端口:
spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡。
4.最后测试结果
成功远程调用了user-service服务查询道理user信息
Eureka注册服务总结
1.搭建EurekaServer
- 引入eureka-server依赖
- 添加@EnableEurekaServer注解·在application.yml中配置eureka地址
2.服务注册
- 引入eureka-client依赖
- 在application.yml中配置eureka地址
3.服务发现
- 引入eureka-client依赖
- 在application.yml中配置eureka地址
- 给RestTemplate添加@LoadBalanced注解·用服务提供者的服务名称远程调用
Ribbon负载均衡原理
Ribbon(谐音:瑞本),我怕自己读不标准,记一下
上面做了Eureka服务注册后,就自动拉取服务并完成了负载均衡。那什么时候自动拉取,什么时候做的负载均衡呢,下面就来探究负载均衡原理
负载均衡原理
1.当服务消费者发起远程调用服务请求
2.其中的LoadBalancerIntercepor会对请求进行拦截
然后做了几件事:
request.getURI()
:获取请求uri,本例中就是 http://user-service/user/8originalUri.getHost()
:获取uri路径的主机名,其实就是服务id,也就是user-service
this.loadBalancer.execute()
:处理服务id,和用户请求。
3.断点下一步继续跟进上面的execute方法(这一步完成了获取eureka中注册的对应的服务,并获取了指定的负载均衡策略)
- getLoadBalancer(serviceId):根据服务id获取ILoadBalancer,而LoadBalancer会拿着服务id去eureka中获取服务列表并保存起来。
- getServer(loadBalancer):利用内置的负载均衡算法,从服务列表中选择一个。本例中,可以看到获取了8082端口的服务
4.负载均衡策略IRule
在上面的代码中,可以看到获取服务使通过一个getServer
方法来做负载均衡:
下面就是源码跟进,不断跟到底,看看到底是谁在帮我们做负载均衡
最后下面是一个负载均衡流程图,这个图会更容易理解从请求远程服务调用负载均衡的流程
基本流程如下:
- 拦截我们的RestTemplate请求http://userservice/user/1
- RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
- DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
- eureka返回列表,localhost:8081、localhost:8082
- IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
- RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求
负载均衡策略
Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:
不同规则的含义如下:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
默认的实现就是ZoneAvoidanceRule,是一种轮询方案
自定义负载均衡策略
通过定义IRule实现可以修改负载均衡规则,有两种方式:
- 代码方式:在order-service中的OrderApplication类(启动类)中,定义一个新的IRule:
@Bean
public IRule randomRule(){return new RandomRule();
}
这个是全局配置,order-service这个服务在调用其他微服务也都是会遵循这个配置的策略
2.配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务ribbon:NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
这个在yml文件中添加的配置只在当前微服务有效,是局部配置
注意,一般用默认的负载均衡规则,不做修改。
懒加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:eager-load:enabled: trueclients: userservice
Nacos注册中心
由于国内公司一般都推崇阿里巴巴的技术,比如注册中心,SpringCloudAlibaba也推出了一个名为Nacos的注册中心,相比Eureka功能更加丰富,使用更广泛
认识安装nacos
nacos1.4.1下载:nacos下载
提取码:olww
下载完毕后在bin目录下,键入cmd启动
输入以下命令,因为它默认是集群启动, 这里设置为单体启动
startup.cmd -m standalone
浏览器跟上这个地址
http://localhost:8848/nacos/index.html
用户名密码都是nacos,登录进去
Nacos快速入门
1.父工程导入SpringCloudAlibaba的依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.6.RELEASE</version><type>pom</type><scope>import</scope>
</dependency>
2.在user-service和order-service中的pom文件中引入nacos-discovery依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
记得注释掉原来的eureka依赖
3.配置nacos地址
在导入nacos依赖的项目的yml文件中加上nacos配置
spring:cloud:nacos:server-addr: localhost:8848
4.启动服务,然后再前面打开的nacos网页中查看
Nacos服务分级存储模型
一般大厂都会做集群来容灾,保证当本地服务宕机后,仍然能够正常运转(访问别处的服务)
比如当它的杭州服务器发生故障导致宕机,那么它就会访问上海的服务,来保证功能的正常运转
有点互为备胎的意思
Nacos就将同一机房内的实例 划分为一个集群。
user-service是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型,——>其实就是分为服务——集群——实例这三层
微服务互相访问时,应该尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群
给服务配置集群
yml配置文件中加上
名称随意
NacosRule负载均衡策略
userservice:ribbon:NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
①优先选择同集群服务实例列表
②本地集群找不到提供者,才去其它集群寻找,并且会报警告
③确定了可用实例列表后,再采用随机负载均衡挑选实例
权重负载均衡
实际部署中会出现这样的场景:
服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
但默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。
但我们需要考虑到能者多劳,所以权重配置就来了,根据不同的权重设置可以控制访问的频率,权重越大访问频率越高
当权重为0时,就不会通过该服务器进行访问了
以前一个服务要版本更新升级,就需要服务重启,但是不可能光天化日之下升级,导致用户都访问失败,往往是在夜深人静时偷偷升级。不是很方便。
而权重策略的作用之一就是,项目更新升级时,把对应的服务器权重调低,放入少量用户测试看看刚上线功能是否通过,做到平滑升级。
举个例子,某种游戏,有时候会发出公告,不停服更新,就是这么来的
实例的权重控制
①Nacos控制台可以设置实例的权重值
②0~1之间同集群内的多个实例,权重越高被访问的频率越高
③权重设置为0则完全不会被访问
Nacos环境隔离
默认情况下,所有service、data、group都在同一个namespace,名为public
Nacos提供了namespace来实现环境隔离功能。
- nacos中可以有多个namespace
- namespace下可以有group、service等
- 不同namespace之间相互隔离,例如不同namespace的服务互相不可见
具体操作如下
Nacos环境隔离
namespace用来做环境隔离,每个namespace都有唯一id,不同namespace下的服务不可见
Nacos和Eureka的区别
Nacos(谐音:哪克四);Eureka(谐音:衣锐咔)我自己英语读的不标准,记录一下,免得下回和别人聊天,只会读拼写。搞得别人一脸懵圈说你和我说的是同一个技术吗?
回归正题
Nacos是主动把注册服务列表推送给服务消费者,如果有服务挂掉了,就立马推送新的服务列表
Eureka是定时从注册中心拉去服务列表,所以它的服务列表更新效率稍逊于Nacos
Nacos的服务实例分为两种类型:
-
临时实例:如果实例宕机超过一定时间(不主动发送心跳信息),会从服务列表剔除,默认的类型。
-
非临时实例:nacos会主动询问实例的心跳信息,如果实例宕机,也不会从服务列表剔除,也可以叫永久实例。
在配置中设置实例类型
最后总结
Nacos与eureka的共同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别
- Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
- Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
Nacos管理配置
统一配置管理
使用场景
当一个集群中微服务过多,成千上百个时,需要更改其中一个微服务的配置信息,那么其他远程调用该服务的成千上百个服务都需要重启,这在生产环境中几乎是不可能的
所以,我们需要一种统一配置管理方案,可以集中管理所有实例的配置
Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新
添加配置信息
注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。
从微服务中拉去服务
微服务要拉取nacos中管理的配置,并且与本地的application.yml配置合并,才能完成项目启动。
而nacos的地址等配置信息在applicationyml中,但如果尚未读取application.yml,又如何得知nacos地址
因此spring引入了一种新的配置文件:bootstrap.yaml文件,会在application.yml之前被读取
1.导入Nacos配置管理依赖
<!--nacos配置管理依赖-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.添加bootstrap.yaml
然后,在resource文件夹下中添加一个bootstrap.yaml文件
spring:application:name: userservice # 服务名称profiles:active: dev #开发环境,这里是dev cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名
这里会根据spring.cloud.nacos.server-addr获取nacos地址,再根据name,active,extension来读取对应配置
3.添加nacos配置并读取
在user-service中的UserController中添加业务逻辑,读取在Nacos中添加的pattern.dateformat配置:
按照我们规定的格式完成日期格式化并返回
表示成功拉去到了Nacos中的配置信息了
配置热更新
Nacos中的配置文件变更后,微服务无需重启就可以感知(就是直接刷新网页就会更新配置)。可以通过下面两种配置方式实现:
方式一
在@Value注入的变量所在类上添加注解@RefreshScope:
方式二
使用@ConfigurationProperties注解代替@Value注解。
在user-service服务中,添加一个类,读取patterrn.dateformat属性:
@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {private String dateformat;
}
在UserController中使用这个类代替@Value:
配置共享
有一些属性在开发,测试等多个环境值都是一样的,为避免修改该配置的值,要取一个一个的修改;就引用了配置共享方法,把相同的配置放在共享配置中,就像类中的public static修饰的变量一样
微服务启动时,会去nacos读取多个配置文件,例如:
-
[spring.application.name]-[spring.profiles.active].yaml
,例如:userservice-dev.yaml -
[spring.application.name].yaml
,例如:userservice.yaml
而[spring.application.name].yaml
不包含环境,因此可以被多个环境共享。
配置优先级
远程专属配置 > 远端共享配置 > 本地配置
搭建Nacos集群
学习阶段,没有那么多机子,只能搞搞简略版,把服务都配在本地一台上
前提:搭建mysql集群,初始化数据库表,条件有限也可以只用一台mysql数据库
①把nacos压缩包解压缩
②进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf:
③然后添加内容:(因为只是把三台配在本地没有三台服务器,所以都是本地ip,端口选没使用的就行)
127.0.0.1:8845
127.0.0.1.8846
127.0.0.1.8847
④修改application.properties文件,添加数据库配置
⑤将nacos文件夹复制三份,然后分别修改三个文件夹中的application.properties,
nacos1:
server.port=8845
nacos2:
server.port=8846
nacos3:
server.port=8847
⑥然后分别启动三个nacos节点、
就是bin目录下的startup.cmd,因为它默认就是集群启动的,双击即可
⑦使用nginx进行反向代理
修改conf/nginx.conf文件,配置如下:
直接复制进去就行了
upstream nacos-cluster {server 127.0.0.1:8845;server 127.0.0.1:8846;server 127.0.0.1:8847;
}server {listen 80;server_name localhost;location /nacos {proxy_pass http://nacos-cluster;}
}
⑧代码中application.yml文件配置如下:
spring:cloud:nacos:server-addr: localhost:80 # Nacos地址
这时候在nacos中创建新的配置会存到数据库里面去,完成持久化了
优化
-
实际部署时,需要给做反向代理的nginx服务器设置一个域名,这样后续如果有服务器迁移nacos的客户端也无需更改配置.
-
Nacos的各个节点应该部署到多个不同服务器,做好容灾和隔离
Feign远程调用
前面都是完成了服务注册,配置中心nacos相关,但是服务拉取的部分是用的RestTemplate
以前利用RestTemplate发起远程调用的代码:
存在下面的问题:
•代码可读性差,编程体验不统一
•参数复杂URL难以维护
Feign是一个声明式的http客户端,其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
Feign替代RestTemplate
①引入Feign依赖
pom文件中引入feign的依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
②添加注解
在启动类上添加注解开启Feign的功能:
③编写Feign的客户端
下面是以以前demo的order-service为例
@FeignClient("userservice")
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。
④在业务方法中替换以前的RestTemplate
就不会像以前那样在业务代码中添加url,可读性很低,代码也不简洁
最后,可以看到最后也完成了远程调用,而且代码更加简洁,同时多试几次,会发现feign不仅实现了服务拉取,而且实现了负载均衡
⑤总结
使用Feign的步骤:
① 引入依赖
② 添加@EnableFeignClients注解
③ 编写FeignClient接口
④ 使用FeignClient中定义的方法代替RestTemplate
自定义配置
Feign可以支持很多的自定义配置,如下表所示:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
配置文件方式
基于配置文件修改feign的日志级别可以针对单个服务:
feign: client:config: userservice: # 针对某个微服务的配置loggerLevel: FULL # 日志级别
也可以针对所有服务:
feign: client:config: default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置loggerLevel: FULL # 日志级别
Java代码方式
也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:
public class DefaultFeignConfiguration {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; // 日志级别为BASIC}
}
如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
如果是局部生效,则把它放到对应的@FeignClient这个注解中:
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class)
而日志的级别分为四种:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
调试错误时可以用FULL,但是平常时候一般还是用NONE 和 BASIC
Feign的性能优化
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
•URLConnection:默认实现,不支持连接池所以性能不是很好,因为连接池可以减少连接的创建和销毁的连接损耗(因为每次连接都需要三次握手和四次挥手)
•Apache HttpClient :支持连接池
•OKHttp:支持连接池
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。
这里用Apache的HttpClient来演示。
①引入依赖
<!--httpClient的依赖 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId>
</dependency>
②配置文件中做相应的配置
feign:client:config:default: # default全局的配置loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息httpclient:enabled: true # 开启feign对HttpClient的支持max-connections: 200 # 最大的连接数max-connections-per-route: 50 # 每个路径的最大连接数
总结,Feign的优化:
1.日志级别尽量用basic
2.使用HttpClient或OKHttp代替URLConnection
-
① 引入feign-httpClient依赖
-
② 配置文件开启httpClient功能,设置连接池参数
Feign的最佳实践
最佳实践就是前辈们不断踩坑后总结的经验,也是Feign最好的一种使用方式
feign客户端:
UserController:
观察可以发现,Feign的客户端与服务提供者的controller代码非常相似,为了简化这种重复的代码编写,下面有两种实现方式
继承方式
一样的代码可以通过继承来共享:
1)定义一个API接口,利用定义方法,并基于SpringMVC注解做声明。
2)Feign客户端和Controller都集成改接口
优点:
- 简单容易
- 实现了代码共享
缺点:
-
服务提供方、服务消费方紧耦合
-
参数列表中的注解映射并不会继承,因此Controller中必须再次声明方法、参数列表、注解
抽取方式
将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
例如,将UserClient、User、Feign的默认配置都抽取到一个feign-api包中,所有微服务引用该依赖包,即可直接使用。
缺点:一些服务不需要的依赖也被统一引入了
总结
Feign的最佳实践:
①让controller和FeignClient继承同一接口
②将FeignClient、POJO、Feign的默认配置都定义到一个项目中,供所有消费者使用
代码实现
下面是对第二种方式——抽取的实现
第一步,创建feign模块做统一api,并把前面demo中order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
第二步,在feign-api中然后引入feign的starter依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第三步,可以把前面order-service中的实体类和feign的Client都删掉了,在其pom文件中导入刚编写的eign-api模块
修改order-service中的所有与上述三个组件有关的导包部分,改成导入feign-api中的包
第四步,注入到Spring容器中
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:
方式一:指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
方式二:指定FeignClient字节码
@EnableFeignClients(clients = {UserClient.class})
一般推荐用第二种,精准打击
Gateway服务网关
初识Gateway网关
Gateway网关是我们服务的门卫,是所有微服务的统一入口。
网关的三个核心的功能如下
①权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
②路由和负载均衡:一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
③限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。
在SpringCloud中网关的实现包括两种:
- gateway
- zuul
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
Gateway快速入门
要实现网关的基本路由功能,基本步骤如下:
- 创建SpringBoot工程gateway,引入网关依赖
- 编写启动类
- 编写基础配置和路由规则
- 启动网关服务进行测试
代码实现
1.创建一个gateway模块作为服务,引入gateway和nacos服务发现依赖
建议创建maven工程,boot工程的话要改版本
<!--网关-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.在gateway模块中编写启动类
@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
3.配置yml文件,给其添加对应配置信息
server:port: 10010 # 网关端口
spring:application:name: gateway # 服务名称cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 网关路由配置- id: user-service # 路由id,自定义,只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称predicates: # 路由断言,也就是判断请求是否符合路由规则的条件- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
将符合Path
规则的一切请求,都代理到 uri
参数指定的地址。
我们将 /user/**
开头的请求,代理到lb://userservice
,lb是负载均衡,根据服务名拉取服务列表,实现负载均衡。
4.启动网关服务,访问网关服务端口,测试结果如下图,可以通过网关然后访问到服务
报错503
新版本的nacos一定要加上 spring-cloud-starter-loadbalancer 依赖,用于替换ribbon
网关路由的流程图
最后,总结流程步骤
网关搭建步骤:
-
创建项目,引入nacos服务发现和gateway依赖
-
创建GatewayApplication启动类
-
配置application.yml,包括服务基本信息、nacos地址、路由
路由配置包括:
-
路由id:路由的唯一标示(一般为服务名但要求不重复)
-
路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
-
路由断言(predicates):判断路由的规则,
-
路由过滤器(filters):对请求或响应做处理
断言工厂
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
例如Path=/user/**是按照路径匹配
断言(assertion):是一种在程序中的一阶逻辑(如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果——当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。
大白话就是判断,返回值是true或者false
断言工厂在SpringCloudGateway还有十几个:
名称 | 说明 | 示例 |
---|---|---|
After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
不需要记,随用随查即可,而且也记不住,一般只需要掌握Path这种路由工程就可以了
总结:
PredicateFactory的作用是什么?
读取用户定义的断言条件,对请求做出判断
Path=/user/**是什么含义?
路径是以/user开头的就认为是符合的
过滤器工厂
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
路由过滤器的种类
Spring提供了31种不同的路由过滤器工厂。下面是几种常见的过滤器:
名称 | 说明 |
---|---|
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求的流量 |
这里以请求投过滤器为例,来写个案例示范
需求:给所有进入userservice的请求添加一个请求头:Hello World
只需要修改gateway服务的application.yml文件,添加路由过滤即可:
spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path=/user/** filters: # 过滤器- AddRequestHeader=Head, Hello World # 添加请求头
测试效果
结果如下
默认过滤器
上面加的过滤器是只针对对应的路由有效,若要像对所有路由都有效,就可以配置默认过滤器
如果要对所有的路由都生效,则可以将过滤器工厂写到default下
spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path=/user/**default-filters: # 默认过滤项- AddRequestHeader=Head, Hello World
总结
过滤器的作用是什么?
① 对路由的请求或响应做加工处理,比如添加请求头
② 配置在路由下的过滤器只对当前路由的请求生效
defaultFilters的作用是什么?
对所有路由都生效的过滤器
全局过滤器
虽然默认过滤器已经实现了全局过滤路由的功能了,但是不能自定义,无法进行定制过滤
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
在filter中编写自定义逻辑,可以实现下列功能:
- 登录状态判断
- 权限校验
- 请求限流等
自定义全局过滤器
范例
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:
-
参数中是否有authorization,
-
authorization参数值是否为admin
如果同时满足则放行,否则拦截
在gateway中定义一个过滤器:
这个@Order(-1)是指定过滤器执行的顺序,比如有很多过滤器时,这个就是指定谁先执行谁后执行,值越小,越先执行
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();// 2.获取authorization参数String auth = params.getFirst("authorization");// 3.校验if ("admin".equals(auth)) {// 放行return chain.filter(exchange);}// 4.拦截// 4.1.禁止访问,设置状态码exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// 4.2.结束处理return exchange.getResponse().setComplete();}
}
结果如下图
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
排序的规则
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
- GlobalFilter通过实现Ordered接口,或者添加**@Order**注解来指定order值,由我们自己指定
- 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
- 当过滤器的order值一样时,会按照defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
跨域问题
跨域:域名不一致就是跨域,主要包括:
-
域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
-
域名相同,端口不同:localhost:8080和localhost8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
CORS详解
解决跨域问题
在gateway服务的application.yml文件中,添加下面的配置:
spring:cloud:gateway:globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:'[/**]':allowedOrigins: # 允许哪些网站的跨域请求 - "http://localhost:8090"allowedMethods: # 允许的跨域ajax的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期
未完待续
受篇幅限制,微服务的剩余组件:Docker,MQ,ES的使用及解析在下篇继续记录。感谢您的阅览
相关文章:
SpringCloud全面学习笔记之初尝美妙篇
目录 前言初识微服务单体架构分布式架构微服务架构初见SpringCloud微服务治理分布式服务架构案例 微服务组件及使用Eureka注册中心提供者和消费者Eureka的结构和作用搭建Eureka服务注册服务服务发现Eureka注册服务总结 Ribbon负载均衡原理负载均衡原理负载均衡策略懒加载 Nacos…...
Spring MVC框架
Spring MVC框架 Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spri…...
Illustrator如何使用图层与蒙版之实例演示?
文章目录 0.引言1.绘制可爱冰淇淋图标2.霓虹渐变立体文字海报3.炫彩花纹背景 0.引言 因科研等多场景需要进行绘图处理,笔者对Illustrator进行了学习,本文通过《Illustrator CC2018基础与实战》及其配套素材结合网上相关资料进行学习笔记总结,…...
Office Tool Plus的使用
是否为安装,卸载,激活Office而烦恼? 下载 地址:Office Tool Plus 官方网站 - 一键部署 Office 安装office 先安装Office,Office_Pro_Plus_2021_LTSCProjectVisio_x64_zh_CN_VL_2022-02 注意,要安装批量…...
射频PCB 设计的六大条技巧
即使是最自信的设计人员,对于射频电路也往往望而却步,因为它会带来巨大的设计挑战,并且需要专业的设计和分析工具。这里将为您介绍六条技巧,来帮助您简化任何射频PCB 设计任务和减轻工作压力! 1、保持完好、精确的射频…...
优化了成本和安装难度后,UWB信标能否取代蓝牙信标?
1 我们做安U3号是要解决什么问题? (1)信标式设计,解决传统UWB基站安装过程繁琐复杂的问题 传统UWB基站在安装过程中遇上的难题: l 安装位置选取问题:UWB基站的准确度与其安装位置有很大关系,…...
深入理解Java虚拟机——垃圾回收算法
1.前言 垃圾回收需要完成的三件事 首先我们需要明白垃圾回收需要完成的三件事: 哪些内存需要回收 堆内存中的对象所使用的内存方法区中的废弃的常量以及不再使用的类型 什么时候回收 当对象死亡方法区中某些内容(常量和类型)不再被使用 如…...
git-rebase和merge
A-----B----C----D master E----F-----G feature 为了把main分支里新增的代码应用在你的feature分支,你有两种方法:merge 和 rebase。 merge git checkout feature git merge main A-----B----C----D master E----F-----G -----* feature (合并master…...
【JavaWeb 用户认证】Cookie、Session、Token、JWT、Interceptor、SpringBoot、Spring Security
Token基本了解:【详细阐述Token的来源】公钥私钥基本了解:【理解公钥】 文章目录 一、Cookie 经典介绍以及使用案例二、Session 经典介绍以及拦截登录案例三、Token MySQL 的基本介绍及其基本使用四、JWT 基本介绍及其基本讲解五、SpringBoot 使用拦截器…...
6个月的测试,来面试居然要15K,我一问连5K都不值
2023年4月份我入职了深圳某家创业公司,刚入职还是很兴奋的,到公司一看我傻了,公司除了我一个自动化测试,公司的测试人员就只有2个开发3个前端1个测试还有2个UI,在粗略了解公司的业务后才发现是一个从零开始的项目&…...
RSA--维纳攻击--代码和题目分析
文章目录 维纳攻击原理:维纳攻击脚本[羊城杯 2020]RRRRRRRSA 1题目描述:题目分析: 收获与体会: 维纳攻击原理: 两位大佬讲得非常清楚(搬运工就是我):https://zhuanlan.zhihu.com/p/…...
飞腾ft2000-麒麟V10-SP1安装Docker、运行gitlab容器
目录 一、安装及配置docker 1、卸载docker相关包及删除相关配置文件 2、安装二进制docker 1.下载软件包 2.解压 3.修改镜像加速地址 4.修改profile文件 5.启动docker 6.docker常用命令 二、安装并启动gitlab镜像 1.安装gitlab镜像 1.查询满足使用需求的gitlab版本 2…...
C++ 的类型转换
目录 1. C语言中的类型转换 2. C强制类型转换 2.1static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 dynamic_cast 3. RTTI(了解) 1. C语言中的类型转换 在 C 语言中,如果 赋值运算符左右两侧类型不同,或者形参与实参类型不…...
【Windows】普通控制台EXE程序转为windows服务方式运行的详细步骤
背景 NSSM(Non-Sucking Service Manager)是一个免费的第三方Windows服务管理器,可以将任何可执行文件转换为Windows服务。官网下载地址为:https://nssm.cc/download 以下是NSSM配置Windows服务的详细步骤和注意事项: …...
NSSCTF [suctf 2019]hardcpp WP 控制流混淆
下载文件,64位主函数非常多循环 去控制流混淆,脚本下载deflat 用法 python 脚本名 文件名 起始地址例如主函数地址是0x4007E0 python deflat.py hardCpp 0x4007E0然后就生成了去混淆的文件 主函数非常大,开始分析逻辑 puts("func(?…...
计算机毕业论文内容参考|基于神经网络的网络安全态势感知技术研究
文章目录 导文文章重点摘要前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍技术分析技术设计技术实现总结与展望导文 基于神经网络的网络安全态势感知技术研究 文章重点 摘要 随着互联网的快速发展,网络攻击的频率和复杂度也在逐年增加。为了更好地保护信息系统…...
Flask框架之Request、Response、Cookies、Session等对象的使用
Request、Response、Cookies、Session等对象的使用 Request对象基本使用参数的获取转换器内置转换器自定义转换器 Response对象基本使用返回模板重定向返回JSON Cookies对象设置cookie获取cookie删除cookie Session会话对象设置SECRET_KEY设置会话获取会话释放会话 Request对象…...
信号与槽机制一
一、信号与槽 1、什么是信号与槽? 信号和槽是用于对象之间的通信,它是Qt的核心机制,在Qt编程中有着广泛的应用。如果想学好Qt,一定要充分掌握信号的槽的概念与使用。 2、信号和槽的代码实例 在Qt中,发送对象、发送的信…...
nodejs 复制文件到指定目录
var fs require(fs), path require(path), exec require(child_process).exec, sourcePath, targetPath; //获取命令行中的路径 process.argv.forEach(function (val, index, array) { if (index 2) { sourcePath val; } if (index 3) { targetPath val; } }); // 定义…...
第八章 使用Apache服务部署静态网站
文章目录 第八章 使用Apache服务部署静态网站一、网站服务程序1、网站服务介绍2、Apache程序介绍 二、配置服务文件参数1、Linux系统中的配置文件2、配置httpd服务程序时最常用的参数以及用途描述 三、SELinux安全子系统1、SELinux介绍2、SELinux服务配置模式3、Semanage命令4、…...
Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用
文章: Three——一、初识Three以及基础的前端场景搭建(结尾含源码)Three——二、加强对三维空间的认识Three——三、动画执行、画布大小、渲染帧率和相机适配体验Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用Three——五、点线模型对象、三角形概念…...
盲目自学网络安全只会成为脚本小子?
前言:我们来看看怎么学才不会成为脚本小子 目录: 一,怎么入门? 1、Web 安全相关概念(2 周)2、熟悉渗透相关工具(3 周)3、渗透实战操作(5 周)4、关注安全圈动…...
文从字顺|程序员须知,如何编写高质量代码
高质量代码是软件开发中至关重要的一部分。高质量的代码不仅可以提高软件的可维护性和可复用性,还可以增强软件的安全性和稳定性。同时,可以降低软件维护成本,提升开发效率,为用户提供更好的使用体验。 写出高质量代码是每个程序…...
PCIe物理层弹性缓存机制(详细)解析-PCIe专题知识(四)
目录 前言一、简介二、详细解析2.1 实例解析2.2 具体实现过程 三、总结四、其他相关链接1、PCI总线及发展历程总结2、PCIe物理层总结-PCIE专题知识(一)3、PCIe数据链路层图文总结-PCIe专题知识(二)4、PCIe物理层链路训练和初始化总…...
分片上传和断点续传的区别?实现思路是什么?
相同: 分片上传和断点续传都是网络传输中常用的重要技术 不同: 分片上传:将一个大文件切分为多个小文件进行上传。这种方式能够加快上传速度,降低服务器压力,特别适用于大型文件的上传。例如,在云存储系统…...
微前端 qiankun@2.10.5 源码分析(二)
微前端 qiankun2.10.5 源码分析(二) 我们继续上一节的内容。 loadApp 方法 找到 src/loader.ts 文件的第 244 行: export async function loadApp<T extends ObjectType>(app: LoadableApp<T>,configuration: FrameworkConfi…...
08异步请求:何种场景下应该使用异步请求?
异步在计算机科学中早就是一个比较常用的词汇,从操作系统的特性( 并发、共享、虚拟、异步)开始,异步就在处理并发操作中起到很大的作用,不仅如此,在软件层面,异步同样也是解决并发请求的一个关键过程,它可以将瞬时激增的请求进行更加灵活的处理,通过异步请求,客户端可…...
【深度学习 | Transformer】Transformers 教程:pipeline一键预测
文章目录 一、前言二、Computer vision2.1 Image classification2.2 Object detection2.3 Image segmentation2.4 Depth estimation 三、NLP3.1 Text classification3.2 Token classification3.3 Question answering3.4 Summarization3.5 Translation3.6 Language modeling3.6.…...
HTMLCSS
1、HTML 1.1 介绍 HTML 是一门语言,所有的网页都是用HTML 这门语言编写出来的,也就是HTML是用来写网页的,像京东,12306等网站有很多网页。 这些都是网页展示出来的效果。而HTML也有专业的解释 HTML(HyperText Markup Language)…...
【安装Nginx】
Linux上安装Nginx 文章目录 Linux上安装NginxUbuntuCentOS查看已安装的软件 Ubuntu 在 Ubuntu 上安装 Nginx 非常简单。只需按照以下步骤操作: 打开终端,更新软件包索引: sudo apt update安装 Nginx: sudo apt install nginx安…...
自己的网站怎么做隐藏内容/常德seo快速排名
程序集生成失败 -- 引用的程序集“Interop.MSScriptControl”没有强名称 为没有源码的DLL文件添加强名称如果项目中引用了其他没有源码的dll文件,并且此dll文件是没有强名称的程序集,则编译时会出现类似 "Assembly generation failed -- 引用的程序…...
网站建设合同 完整版/百度seo排名培训
填写了Name 还是没有 修改步骤如下:...
wordpress改wp admin/软文营销名词解释
题目: 0,1,...n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字,求出这个圆圈里剩下的最后一个数字。 思路: 1、环形链表模拟圆圈 创建一个n个节点的环形链表,然后每次在这个链表中删除…...
在美国做网站如何接入收款工具/软文范例大全800字
ESXi Server上的虚拟机分布于不同的Datastores,现有的服务器能够很好的按照如下的方式进行:每个Datastore运行一个Image,这个程序就是实现这样一个功能!因为是在现有框架增加一些功能,所以这个小程序并没有那么的合理&…...
天天网站建设/西安网站关键词排名
本来不想谈这种招人骂的话题的,哎,不过今天在一个QQ群中遇到了类似的情况,站在现实的角度,说了几句自己学校的不好,然后遇到了一个喷子,而且还不是我们学校的。让我觉的这件事情很奇…...
建设网站那个好/淘宝美工培训
1.模型建立 [dmodel,perf]dacefit(S,Y,regr,corr,theta0) [dmodel,perf]dacefit(S,Y,regr,corr,theta0,lob,upb) 输入参数: S:设计点,一个m*n的矩阵。 Y:一个S的响应值矩阵 m*q regr:回归模型(0阶多项式&#…...