微服务治理:Eureka注册中心、Nacos注册中心、OpenFeign、网关Gateway、配置中心Nacos
Docker
异步通信:MQ技术、SpringAMQP、消费者限流
分布式搜索结果
微服务保护:流量控制、系统保护、熔断降级、服务授权
分布式事务:XA模式、TCC模式、AT模式、Saga模式
分布式缓存:数据持久化、Redis
多级缓存:多级缓存分级、Nginx缓存、Redis缓存、Canal数据同步
可靠消息服务:消息三方确认、惰性队列、延迟队列、镜像集群、仲裁队列
单体架构:所有业务功能集中在一个项目中开发,打包成一个包部署。优点:架构简单、部署成本低。缺点:耦合度高。
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为单独模块开发,成为一个服务。优点:降低服务耦合,有利于服务升级拓展。缺点:要考虑(服务拆分粒度,服务集群地址如何维护、服务之间如何实现远程调用、服务健康状态如何感知)
微服务是一种经过良好架构设计的分布式架构方案,特征:
单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
面向服务:微服务对外暴露业务接口
独立自治:团队独立、技术独立、数据独立、部署独立
强隔离性:服务调用做好隔离、容错、降级、避免出现级联问题
国内知名的微服务实现结构:SpringCloud、Dubbo、SpringCloudAlibaba
SpringCloud整合了:
注册中心:Eureka、Consul
服务远程调用:Feign(http协议)
配置中心:SpringCloudConfig
服务网关:SpringCloudGataway(主)、Zuul
服务监控和保护:Hystrix
SpringCloudAlibaba整合了:
注册中心:Eureka、Nacos
服务远程调用:Feign、Dubbo
配置中心:SpringCloudConfig、Nacos
服务网关:SpringCloudGataway(主)、Zuul
服务监控和保护:Sentinel
服务拆分注意事项
1、不同微服务,不要重复开发相同业务
2、微服务数据独立,不要访问其他微服务的数据库
3、将自己的服务暴露微接口,供其他微服务调用
发起http请求调用其他微服务的接口
SpringBoot提供了@RestTemplate注解
具体步骤
1、在order-service的OrderApplication中注册RestTemplate(写在配置类中)
@MapperScan("cn.itcast.order.mappper")
@RestTemplate
public class OrderApplication{
public static void main(String[] args){
SpringApplication.run(OrderApplication.class,args)
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate()
}
}
2、然后在service类中发送请求
//首先自动注入RestTemplate @Autowired public RestTemplate restTemplate //然后在具体业务代码中发送请求 String url="http://localhost:8080/user/"+order.getId(); User user=restTemplate.getForObject(url,User.class);//第二个参数是返回的类型,会将返回的json反编译成想要的类Eureka注册中心
上述demo存在问题:服务提供者不一定是在一个IP上,若有多个IP应该如何写呢?服务消费者符合获取服务提供者的地址信息?
Eureka作用服务提供者把自己的服务注册到Eureka-server(心跳机制),服务消费者找Eureka-server拉取服务,从几个服务提供者中挑一(负载均衡策略)发送http请求。
搭建Eureka注册中心 1、搭建Eureka服务:3步新建eureka-server项目module
1、依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
2.2.9.RELEASE
2、在启动类上加 @EnableEurakeServe 和@SpringBootApplication注解
3、配置文件yml中写(eureka本身也需要注册,方便以后eureka集群相互之间注册)
server: port:10086 #服务端口 spring: application: #eureka的服务名称 name: eurekaserver eureka: client: service-url: #eureka的地址信息 defaultZone: http://127.0.0.1:10086/eureka2、服务注册:2步
注册user-service带EurekaServer步骤:
1、在user-service项目引入spring-cloud-starter-netflix-eureka-client的依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2.2.9.RELEASE
2、在user-service项目application.yml文件编写配置
spring: application: name: userservive eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka
注:同一服务多次启动:
1、(左下角)找到服务,右键选择“copy configuration” 2、在弹出窗口先改名字,然后点击“enviroment”,在VM potions中填写“-Dserver.Port=8082”//换一个端口才行,不然端口冲突3、服务发现:2步
1、修改OrderService代码,修改访问的url路径,用服务名代替ip、端口
String url="http://userservice/user/"+order.getUserId()
2、在order-service项目的启动类OrderApplication中的RestTemplate添加 均衡负载注解
@Bean
@LoadBalanced//负载均衡注解
public RestTemplate restTemplate(){
return new RestTemplate();
}
Ribbin负载均衡
上述的案例中,order-service发起请求“http://userservice/user/1”并不是真实的地址,会发送到Ribbon,再找Eureka-server返回服务列表到Ribbon,最后轮询一个服务提供者。
@LoadBalanced注解,说明这个请求要被Ribbon拦截处理。
原默认:ZoneAvoidanceRule,以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器金勋个分类,这个Zone可以理解为一个机房、一个机架等。然后再对同一Zone内的服务做轮询。
有两种调整方式
1、代码方式:在order-service项目中启动类OrderApplication类中,定义一个新的方法:
(这样的方式是全局的,只要是order-service发起的访问任何微服务,都会把负载均衡策略改为这样的)
@Bean
public IRule randomRule(){
return new RandomuRule();//改为随机负载
}
2、配置文件方式,在order-service项目的配置文件application.yml中添加新的配置:
(可以指定对某一个微服务的调用时均衡负载规则)
userservice: #指定服务名称 ribbon: NFLoadBalancerRuleClassname: com.netflix.loadbalancer.RandomRule #负载均衡规则饥饿加载
Ribbon默认采用懒加载,即第一次访问时才会创建LoadBalanceClient,请求时间会很长,而饥饿加载则会在项目启动时创建,降低第一次访问的耗时。配置方式:
在order-service项目的配置文件application.yml中添加新的配置:
ribbon: eager-load: enabled: true #启用饥饿加载 clients: #指定饥饿加载的微服务 -userservice -xxxserviceNacos注册中心
Nacos相比Eureka功能更加丰富,国内也更受欢迎。
Nacos安装github地址点左上角tags
下载后解压到非中文路径中,target里面是jar包,conf里面是配置文件(Nacos默认端口是8848),bin里是可执行文件。
在bin目录下的cmd窗口启动命令:startup.cmd -m standalone
登录默认账号密码都是nacos
1、在项目父工程中添加spring-cloud-alibaba的管理依赖
com.alibaba.cloud spring-cloud-alibaba-dependencies 2.2.6.RELEASE pom import
2、注释掉order-service(服务消费者)和user-service(服务提供者)原来的Eureka依赖
3、添加nacos的客户端依赖(消费者、提供者都需要)
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
4、修改user-service和order-service中的application.yml文件,注释Eureka的地址,添加Nacos地址
spring: cloud: nacos: server-addr: localhost:8848 #nacos服务端地址
5、启动并测试
Nacos服务分级存储模型(原本是两级:一层服务,一层实例)
在Nacos分级存储模型中,最上层是服务,往下是集群,最下是提供服务的实例。集群可以理解成 北京机房、上海机房等等。在服务调用时,尽可能调用本地集群。
默认服务是在集群:Default
修改服务提供者user-service的application.yml,添加内容
启动后,如果再修改并启动新的实例,则只会改变新的实例的集群信息。
spring: cloud: nacos: server-addr: localhost:8848 #nacos 服务器地址 discovery: cluster-name: HZ #配置集群名称,可以是机房位置NacosRules负载均衡
想要实现 ”服务消费者调用服务提供者时,优先调用本地服务提供者“
1、需要给order-service设置集群配置,在其application.yml文件添加
spring: cloud: nacos: server-addr: localhost:8848 #nacos 服务器地址 discovery: cluster-name: HZ #配置集群名称,可以是机房位置
2、重启order-service
(这时order-service发起访问并不会优先访问同集群下的user-service,仍然采用的轮询)
3、再order-service中设置负载均衡的 IRule 为 NacosRule,这个规则优先寻找与自己同集群的服务:
userservice: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
4、重启order-service
根据权重负载均衡实际部署中可能出现:服务器设备性能有差异,部分实例所在机器性能好,另一些较差,我们希望性能好的机器承担更多用户请求。
Nacos提供了权重配置来控制访问频率,权重越大访问频率越高。
可以网页上的Nacos控制台,服务列表中,点击详情进行修改。
环境隔离-namespaceNacos中服务存储和数据存储的最外层都是一个名为nameSpace的东西,用来做最外层隔离
结构如下:NameSpace–Group–Service/Data–集群–实例
1、网页Nacos控制台(左侧),新建命名空间。填写新的命名空间信息,会生成一个命名空间ID
2、在order-service的application.yml文件添加:
增加的是namespace
spring: datasource: url: jdbc:mysql://localhost:3306/heima?useSSL=false username: root password: 123 driver-class-name: com.mysql.jdbc.Driver cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: SH # 上海 namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID
**结果:**这时候配置完了,order-service就访问不了user-service了。因此,想要服务能被访问,必须放到相同目录下。
Nacos和Eurake区别nacos会把服务提供者划分成临时实例和非临时实例。
服务检测Nacos
临时实例(默认):心跳检测,检测不到就剔除
非临时实例:不做心跳检测,Nacos主动发请求询问,询问不到不会剔除,会标记为不健康
Eureka
心跳检测
Nacos配置非临时实例:
spring: cloud: nacos: discovery: ephemeral: flase #设置为非临时实例服务获取
Eurake:服务消费者去Eureka注册中心拉取(30秒一次)
Nacos:Nacos注册中心做推送
目的:统一修改配置、实现配置更改热更新
实现操作: Nacos统一配置管理进入nacos控制台,点击左侧配置管理–配置列表,点击右侧➕,会弹出表单:
DataID是配置文件名称,不能叫application.yml,叫这个的话以后所有微服务都来找就分不清了。推荐:微服务名-环境,例如 userservise-dev.yaml Group是分组,默认就好了。 配置内容: (不是放原来application.yml中的内容,因为并不是所有内容需要热更新)写开关类型的配置、模版类型,
点击发布
微服务配置拉取原:项目启动–读取本地配置文件–创建spring容器–加载bean
新:项目启动–读取nacos配置文件–读取本地配置文件–创建spring容器–加载bean
步骤:
1、引入Nacos配置管理客户端依赖:
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config
2、在userservice中的resource目录中加一个bootstrap.yml文件,这个文件是引导文件,优先级比application.yml高。
服务名称+开发环境+后缀名就是我们在nacos控制台配的文件
spring:
application:
name: userservice # 服务名称
profiles:
active: dev #开发环境,这里是dev
cloud:
nacos:
server-addr: localhost:8848 # Nacos地址
config:
file-extension: yaml # 文件后缀名
配置热更新
Nacos中的配置文件变更后,微服务无需重启就可以感知。通过一下两种配置实现:
1、在@Value注入的变量所在的类上添加注解@RefreshScope
(在配置文件中的东西,在具体代码中用Value注解来取用)
2、使用@Configuration注解
这个用在:新建一个类专门用作属性加载。然后在这个类上加@Configuration注解。
@Data
@Configuration(prefix="patttern")//前缀名和变量名拼接之后,在配置文件中匹配上
public class PatternProperties{
private String format;
}
多环境配置共享
微服务启动时会从nacos读取多个配置文件:(都在nacos控制台)
[spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
[spring.application.name].yaml,例如:userservice.yaml
而[spring.application.name].yaml不包含环境,因此可以被多个环境共享。(环境发生改变时,第一个文件会改变,但是第二个不会)因此可以把共享的放在这个文件中。
在idea的运行日志可以看加载了那些yml配置文件
如果这些配置文件中有相同属性,会以 服务名-prifile.yaml>服务名称.yaml>本地 这个优先级。
Nacos集群 搭建基本步骤 1、搭建数据库,初始化表结构 2、下载nacos安装包 3、配置nacos将压缩包解压到任意非中文目录下,进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf:
然后添加内容:
127.0.0.1:8845 127.0.0.1.8846 127.0.0.1.8847
然后修改application.properties文件,添加数据库配置:
# 原文中以下这行去掉注释,说明数据库是哪种 spring.datasource.platform=mysql # 原文中以下这行去掉注释,说明数据库数量 db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=root db.password.0=1234、启动nacos集群
将解压得到的nacos文件夹复制三份,分别命名为:nacos1、nacos2、nacos3
然后分别修改三个文件夹中的application.properties:
nacos1:
server.port=8845
nacos2:
server.port=8846
nacos3:
server.port=8847
分别启动三个nacos节点startup.cmd
5、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; #nacos端口
server_name localhost;
location /nacos {
proxy_pass http://nacos-cluster;
}
}
浏览器访问:http://localhost/nacos即可。
6、源代码配置文件spring:
cloud:
nacos:
server-addr: localhost:80 # Nacos地址
Feign
RestTemplate方式存在的问题:
String url="http://userservice/user/"+order.getUserId(); User user=restTemplate.getForObject(url,User.class);
存在问题:
可读性差,编程体验不统一
复杂的URL难以维护(url的参数难写)
Feign介绍
Feign是一个声明式的http客户端,官方地址
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
Feign集成了ribbon,实现了负载均衡
① 引入依赖
② 添加@EnableFeignClients注解
③ 编写FeignClient接口
④ 使用FeignClient中定义的方法代替RestTemplate
2、添加注解org.springframework.cloud spring-cloud-starter-openfeign
在服务调用者order-service的启动类傻姑娘添加 @EnableFeignClients 注解
3、编写Feign客户端在order-service中新建一个接口,内容如下:
package cn.itcast.order.client;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@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来发送了。
4、测试修改order-service中的OrderService类中的queryOrderById方法,使用Feign客户端代替RestTemplate:
@Autowired
private UserClient userClient;
@Autowired
private OrderMapper orderMapper;
public Order queryOrderById(Long orderId){
//查询订单
Order order=orderMapper.findById(orderId);
//利用Feign发起http请求
User user=userClient.findById(order.getUserId());
//封装user到order
order.setUser(user);
//返回
return order;
}
Feign自定义配置
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 # 日志级别
而日志的级别分为四种:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
先声明一个类,然后声明一个Logger.Level的对象:
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; // 日志级别为BASIC
}
}
如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
如果是局部生效,则把它放到对应的@FeignClient这个注解中:
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class)Feign性能调优
Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
•URLConnection:默认实现,不支持连接池
•Apache HttpClient :支持连接池
•OKHttp:支持连接池
性能优化主要分两点:
1、使用连接池代替默认的URLConnection。
2、日志级别,最好用basic或者none。
以Apache的HttpClient为例
1、引入依赖服务消费者order-service的pom文件中引入Apache的HttpClient依赖:
2、配置连接池io.github.openfeign feign-httpclient
在order-service的application.yml中添加配置:
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数



