SpringCloud
SpringCloud是一系列微服务技术的结合体,springCloud对这些技术进行了封装,使用起来更加方便和快捷。
服务架构的演变
微服务
将一个完整的项目拆分成若干个相对独立的模块,模块之间可以相互通信,并进行分布式集群开发,实现了高可用,高并发的特性。
SpringCloud对比Dubbo
- 共同点:
Dubbo与SpringCloud都可以实现分布式集群开发,都是实现微服务的有效工具;
- 相同点 :
Dubbo使用RPC(Remote Process Call 远程过程调用) 底层通过Socket进行通信,传输的时二进制数据,相较于SpringCloud使用的RESTful的HTTP进行调用,传输的数据格式为JSON,Dubbo传输的效率更高,但SpringCloud具有 更高的通用性(JSON数据具有跨语言特性),且具有更加齐全的功能。
Eureka服务治理(注册中心)
Eureka与DUbbo的zookeep功能相似,eurekaServer自己本身也是个服务,需要用户自己搭建:
Eureka内主要分为Server和Client两种角色:EurekaServer负责服务治理,EurekaClient负责将自己注册到EurekaServer上以及调用EurekaServer上所注册的服务(即生产者与消费者同属于EurekaClient);
导入对应jar包:
//EurekaServer选择EurekaServer包 对于EurekaClient选择EurekaClient包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Eureka配置
EurekaServer可以通过把自己注册到其他EurekaServer上实现EurekaServer的集群
//一下为EurekaClinet配置,EurekaServer与其类似
eureka:
client:
service-url:
defaultZone: http:localhost:8761/eureka #EurekaServer的ip&port 当有集群时 此输可指定多个EurekaServer ip
register-with-eureka: true #是否把自己注册到eurekaServer上
fetch-registry: true #是否从eurekaServer拉取注册的服务列表
instance:
hostname: clazz-provider #指定注册到eurekaServer上当前应用的服务名
开启Eureka注解
对于eurekaServer 使用@EnableEurekaServer, 对于
@SpringBootApplication
@EnableAdminServer
@EnableEurekaServer //EurekaClient则选择@EnableEurekaClient
public class SpringbootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAdminApplication.class, args);
}
}
Ribbon负载均衡组件
Ribbon可以实现Eureka内部服务的负载均衡,通过在Eureka服务端配置Ribbon同时可以实现客户端的负载均衡,同时Ribbon还可以相对简化服务间的远程调用过程;
导入jar包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
原始远程调用服务通过RestTemplate和DiscoveryClient实现,需要通过DiscoveryClient获取指定服务的端口与ip并与参数进行字符串的拼接成url,再通过restTemplate发送http请求并封装返回结果实现服务的远程调用。通过Ribbon可以简化部分过程:
- 在对应的RestTemplate对象上加上@LoadBalanced注解
- 将原本的请求URL中的host与port替换为调用服务的名称
//其中clazz-provider为需要调用的服务名 id为参数,Clazz.class为返回值类型
Clazz clazzs = restTemplate.getForObject("http://clazz-provider/clazz/" + id, Clazz.class);
负载均衡策略
ribbon实现的负载均衡共有7中负载均衡策略:
- RoundRobbinRule : 轮询(默认策略)
- RandomRule : 随机
- BestAvailableRule : 最小并发
- AvailabilityFilteringRule : 过滤条件
- ** WeightedResponseTimeRule** : 最小相应时间
- RetryRule : 轮询重试
- ZoneAvoidanceRule : 性能可用性
修改Ribbon负载均衡策略有配置文件和编码实现两种方式:
编码实现: 推荐使用
- 像容器注入IRule的实现类(以上七种)
- 在启动类上添加@RibbonClient注解并用name属性指定负载均衡的服务名,使用configuration属性指定配置了IRule实现类的bean的Cnfiguration配置类
配置文件实现 :
在配置文件添加:
服务提供方的服务名.ribbon.NFloadbalancerRuleClassname: 策略类的全限定类名
OpenFeign
openFeign可以实现声明式服务调用,简化客户端调用服务的配置,原始的服务调用需要RestTemplate和DiscoveryClient来共同实现,Feign可以将其省略:
导入jar包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
- 开启**@EnableFeignClients**注解,在启动类上
- 编写Fiegn调用服务的接口,并加上@FeignClient注解,name属性指定对应的服务名
- 接口方法使用@GetMapping等Restful标记对应方法的url,且方法参数必须以@PathVariable&@RequestParam指定对应服务内的方法参数名一致;
- 在容器中注入该接口,使用接口的方法即可完成远程调用
@FeignClient(name = "clazz-provider")//指定服务名
public interface ClazzFeign {
@GetMapping("/clazz/{id}")
Map<String,Object> getClazz(@PathVariable("id") Integer id);
@GetMapping("/clazz/getByIds")
List<Clazz> getClazzList(@RequestParam("ids") List<Integer> ids);
}
//容器中注入clazzFeign,调该对象即可实现远程调用
Map<String, Object> clazz = clazzFeign.getClazz(Integer.valueOf(student.getClazzId()));
Feign的超时配置
Feign底层利用Ribbon实现服务调用,默认等待调用结果时间为1秒,默认与服务连接超时时间为1秒,可以在配置文件设定自定义值:
Ribbon.ConnectionTimeout=1000
Ribbon.ReadTimeout=1000
Feign的日志记录
Feign支持日志记录,通过在创建配置类,在配置类中编写 返回 Logger.level的对象的Bean,同时在@FeignClient注解的Configuration属性指定该配置类。即可完成Feign的日志记录。
日志分为:
- 操作日志:此种类型的日志会记录所有系统内用户的行为,并将数据保存在数据库(一般可以通过AOP实现)
- 系统日志: 即系统运行时控制台所打印的日志,在生产环境中可以将其设置保存在文件中
Consul&Nacos
Consul与Nacos均属于服务治理组件,其使用方法均与Eureka类似,且该两个组件均有服务端App不需要自己编码实现;
Consul :
Consul服务端启动命令:
consul agent -dev
配置如下:
consul:
discovery:
hostname: localhost #指定当前应用的主机名称
port: 8500 #指定当前应用的端口
instance-id: thisisaid #指定注册到Consul的ID
service-name: clazz-provider #指定注册到Consul的服务名
Nacos
Nacos(Dynamic Naming and Configuration Service)在三个组件中性能最高,是一个注册中心和配置中心
配置如下:
cloud:
nacos:
discovery:
server-addr: http://localhost:8848 #指定nacosServer的位置
Hystrix 熔断器
Hystrix具有延迟和容错,隔离访问的远程服务,防止级联失败而导致的系统雪崩效应的功能;
隔离 :
Hystrix默认会开启服务的线程隔离功能,能对应用内的服务分别分配一个具有一定数量的线程的线程池,当应用内某一个服务崩溃,仅会对自己线程池内的线程占用,从而保证了其他服务线程池内线程不受影响,实现了应用内部的隔离性。
熔断
Hstrix会默认开启服务的熔断功能,能对指定不稳定的服务进行断开连接操作
默认情况下一个服务在5S内发生20次调用失败,Hystrix便会打开断路器,该服务被关闭连接,从而保证应用的相应时间和运行速度,过一段时间后(默认5s)Hystrix断路器会进入半断开状态,一部分对该服务的请求能连接,Hystirx会自动判断连接是否成功来决定是否断开断路器。
//熔断的参数如下
circut.SleepWindowInMillSeconds:断路器计数周期
circut.RequestThreshold: 单位计数周期内请求失败数量的阈值
circut.ErrorThresholdPercent: 单位计数周期内请求失败的比列阈值
Hystrix 熔断监控
Hystrix支持对当前(微服务)应用的运行状态的监控,利用其Hystrix-Dashboard组件
熔断监控端
-
导入jar包:
Hysrtix-Dashboard Turbine Actuator Eureka-Client Web
-
在启动类加上@EnableHystrixDashboard @EnableTurbine
-
配置文件:
spring:
application.name: hystrix-monitor
server:
port: 8769
turbine:
combine-host-port: true
# 配置需要监控的服务名称列表
app-config: clazz-consumer
cluster-name-expression: "'default'"
aggregator:
cluster-config: default
#instanceUrlSuffix: /actuator/hystrix.stream
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
- 进入http://localhost:8769/hystrix/ 输入http://localhost:8769/turbine.stream完成指定应用的监控
熔断被监控端
在指定被监控端:
-
导入jar包
Actuator Hystrix-Dashboard Hystrix
-
注入指定bean:
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetrceStreamServlet");
return registrationBean;
}
GateWay 网关&路由
GateWat能为微服务架构的应用提供一种简单有效且统一的API路由管理方式,同时GateWay作为系统的入口,可以实现封装系统的内部结构,以及与业务无关的公用逻辑的处理;
-
导入jar包:
GateWay Eureka
-
配置文件:
server:
port: 80 #一般指定为80 为浏览器默认IP的端口
spring:
application:
name: GateWay
cloud:
gateway:
routes:
- id: clazz-provider #路由Id
uri: lb://CLAZZ-PROVIDER #动态路由clazz-provider为Eureka内注册的服务名
predicates:
- Path=/clazz/** #路由转发匹配路径
- id: eureka-consumer
uri: lb://CLAZZ-CONSUMER
predicates:
- Path=/student/**
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka #拉取Eureka服务的Id
instance:
hostname: gateWay
其中URI可以有两种配置方式:
- 静态配置 :
即uri为一个定值
uri: http://服务的IP:服务端口
动态配置
uri为从eureka拉取的服务名,其中lb为指定的通信协议(类似于http)
uri: lb://CLAZZ-CONSUMER
同样,gateWay能从eureka拉取指定的服务,其肯定具有自己的负载均衡实现
GateWay自动去除uri中服务名
通过:
discovery:
locator:
enabled: true #GateWay支持自动将URI的一级路径中的ServiceID去除
lower-case-service-id: true #GateWay支持ServiceId小写格式(大写格式失效反之小写失效)
配置GateWay能自动监测请求的URI中一级路径中包含的服务名,能自动去除对应的服务名;
即如下配置一个请求为localhost/clazz/**与localhost/clazz-provider能相同进入该路由规则
gateway:
routes:
- id: clazz-provider #路由Id
uri: lb://CLAZZ-PROVIDER #动态路由
predicates:
- Path=/clazz//** #路由转发匹配路径
filters:
- StripPrefix=1 #局部过滤器 将指定层级的uri路径删除再转发到指定服务a
discovery:
locator:
enabled: true #GateWay支持自动将URI的一级路径中的ServiceID去除
lower-case-service-id: true #GateWay支持ServiceId小写格式(大写格式失效反之小写失效)
GateWay实现过滤功能
GateWay可以实现对请求的统一过滤处理功能,gateWay中的过滤分为前置(pre)过滤和后置(post)过滤,以及局部过滤器和全局过滤器;
局部过滤器 :
局部过滤器可直接在配置文件中指定:过滤器详解
- id: eureka-consumer
uri: lb://CLAZZ-CONSUMER
predicates:
- Path=/testFilter/student/**
filters:
- StripPrefix=1 #局部过滤器 将指定层级的uri路径删除再转发到指定服务
全局过滤器
全局过滤器可以直将实现了GlobalFilter&Ordered接口的过滤器注入gateWay所在应用容器内实现:
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override //执行过滤逻辑
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println(exchange.getRequest().getPath());
System.out.println(Objects.requireNonNull(exchange.getApplicationContext()).getApplicationName());
return chain.filter(exchange);//过滤器放行该请求
}
@Override
public int getOrder() {//返回该过滤器的执行优先级 0开始递增
return 0;
}
}