SpringCloud
微服务架构
每个工程都是独立的模块,工程之间使用更轻量的http
通讯框架 (不建立依赖关系) 每个微服务都有自己的数据库,每个微服务都是完成模块的具体的功能,都是独立的,只需要对外提供一个接口
服务调用方式
RPC
- 基于Socket
- 自定义数据格式
- 速度快,效率搞
- 典型代表:Dubbo ElasticSearch集群间相互调用
Http
- 基于TCP/IP
- 规定数据传输格式
- 缺点是消息封装比较臃肿,传输速度比较慢
- 优点是对服务提供和调用没有任何技术限制,自由灵活,更符合微服务理念
ResTemplate
- estTemplate是Rest的HTTP客户端模板工具类
- 对基于Http的客户端进行封装
- 实现对象与JSON的序列化与反序列化
- 不限定客户端类型,目前常用的3种客户端都支持:HttpClient、OKHttp、JDK原生URLConnection(默认方式)
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCloudDemo2RestemplateApplicationTest {
@Autowired
private RestTemplate restTemplate;
@Test
public void testRestTemplate(){
String url="http://localhost:18081/user/list";
String json=restTemplate.getForObject(url,String.class);
System.out.println(json);
}
}
要先在起步引导类中创建Restemplate的Bean容器,也就是在服务消费者使用ResTemplate调用服务提供者,使用ResTemplate调用的时候,需要先创建并注入到SpringIOC容器中
@SpringBootApplication
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
SpringBoot框架中为我们提供了实现类的注解,所有大部分情况我们都不再需要写sql语句
- @Entity 表示业务实体 标识这是一个Spring要创建的Bean
- @Table (name=“数据库表名”) 映射表名
- @Column(name=" ") 如果数据库字段名与对象名不相同时,要加上此注解,并指定数据库字段名
- @Transient 排除数据库字段,当一个普通java对象属性使用
- @Id 映射当前属性是主键
- @GeneratedValue (strategy=GenerationType.IDENTITY) 主键自增
@Entity //表示业务实体,标识这是spirng要创建的javaBean类
@Table(name = "tb_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;//主键id
private String username;//用户名
private String password;//密码
private String name;//姓名
private Integer age;//年龄
private Integer sex;//性别 1男性,2女性
private Date birthday; //出生日期
private Date created; //创建时间
private Date updated; //更新时间
private String note;//备注
//Get和Set方法
注意:Dao接口继承JpaRespository<T,Id> T: 目标类(实现类) id:主键类型
public interface UserDao extends JpaRepository<User,Integer> {
}
注册中心 Eureka
运行原理
-
ApplicationServicet 服务提供者注册到EurkeServer(注册服务名字) 服务提供者会通过http方式定时向注册中心发送心跳信息(默认90S),表示服务存活
-
ApplicationClient 服务消费者在启动的那一刻,会向注册中心调用需要的服务名单,拿到名单后直接通过ip和地址向服务提供者进行数据调用
-
EurekaServer注册中心会定时向客户端消费者发送生产者名单(默认30S)
注册中心的搭建
<!--eureka-server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
application.yml
server:
port: 7001
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:7001/eureka
在起步引导类中加入@EnableEurekaServer 注解
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务
public class EurekaServerApplication {public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); }}
生产者完成注册
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
application.yml 配置文件
server:
port: 18081
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url:
jdbc:mysql://192.168.13.100/test?seUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
application:
name: user-provider #服务的名字,不同的应用,名字不同,如果是集群,名字需要相同
#指定eureka服务地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
在起步引导类中使用注解开启客户端发现功能 @EnableDiscoveryClient
@EnableEurekaClient //开启客户端发现功能
起步引导类要加上 (二选一)
- @EnableEurekaClient 仅支持 Eureka
- @EnableDiscoveryClient 不仅支持Eureka,还可以支持zookeeper
消费者完成注册
@RequestMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id")Integer id){
//根据服务名获取到实体数据集合
List<ServiceInstance> instances = discoveryClient.getInstances("user-provider");
ServiceInstance serviceInstance = instances.get(0);
//此时获取到的host是主机名,注意:要是想使用ip地址,必须再在服务提供者的配置文件中配置instance属性
String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/find/"+id;
return restTemplate.getForObject(url,User.class);
}
**注意: 要是想使用ip地址,必须再在服务提供者的配置文件中配置instance属性 **
#指定eureka服务地址
eureka:
client:
service-url:
# EurekaServer的地址
defaultZone: http://localhost:7001/eureka
instance:
#指定IP地址
ip-address: 127.0.0.1
#访问服务的时候,推荐使用IP
prefer-ip-address: true
Eureka配置详解
服务注册
- 当我们开启了客户端发现注解@DiscoveryClient 同时导入 enureka-client依赖坐标
- 同时配置Eureka服务注册中心地址在配置文件中
- 服务在启动时,会检测是否有@DiscoveryClient 注解和配置信息
- 如果有,则会向注册中心发起注册请求,携带服务元数据信息(IP, 端口等)
- Eureka 注册中心会把服务的信息保存在Map中
服务续约
-
服务注册完成以后,服务提供者会维持一个心跳,保存服务处于存在状态.这个称之为服务续约(renew)
#租约到期,服务失效时间,默认值90秒 lease-expiration-duration-in-seconds: 150 #租约续约间隔时间,默认30秒 lease-renewal-interval-in-seconds:30
服务超过90秒没有发生心跳,EurekaServer会将服务从列表中移除, 前提是EurekServer关闭了自我保护
获取服务列表
服务消费者启动时,会检测是否获取服务注册信息配置,如果是,则会EurekaServer 服务列表获取只读备份,缓存到本地,每隔30秒,会重新获取并更新数据, 时间可以通过registry-fetch-interval-seconds修改
#每隔30秒获取服务列表(只读备份)
registry-fetch-interval-seconds: 30
失效剔除
server:
#服务中心每隔一段时间(默认60秒) 将清单中的没有续约的服务剔除,单位是毫秒
eviction-interval-timer-in-ms: 5000
自我保护
Eureka 会统计服务实例最近15分组心跳续约的比例是否低于85%,如果低于,则会触发自我保护机制. 服务中心页面会显示提示信息
- 自我保护模式下,不会剔除任何服务实例
- 自我保护模式保证了大多数服务依然可用
- 通过enable-self-preservation 配置可以关停自我保护
#关闭自我保护功能,默认是打开的
enable-self-preservation: false
Spring Cloud Ribbon 负载均衡
Ribbon简介
Ribbon是Netflix发布的负载均衡器,有助于控制Http客户端行为. 为Ribbon配置服务提供者地址列表后,Ribbon就可以基于负载均衡算法,自动帮助服务消费者请求.
Ribbon默认提供的负载均衡算法: 轮循,随机,重试法,加权 还可以自定义一负载均衡算法
入门使用
在消费者的ResTemplate容器方法上加上@LoadBalanced注解 表示客户端开启负载均衡 (无需引入依赖)
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();}}
修改消费者的调用方式,不再需要手动获取Ip和端口,改为通过服务名调用
@RequestMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id")Integer id){
/* List<ServiceInstance> instances = discoveryClient.getInstances("user-provider");
ServiceInstance serviceInstance = instances.get(0);
String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/find/"+id;*/
//使用负载均衡后就不需要再手动获取ip和端口,而是直接通过服务名称调用
String url="http://user-provider/user/find/"+id;
return restTemplate.getForObject(url,User.class);
}
负载均衡策略配置
在消费者中配置更该轮循策略 (真实环境下只选其一)
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
#随机算法
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#重试算法,该算法先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
#加权法,会根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则。
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule
Ribbo负载均衡原理
负载均衡器动态的从服务中心获取到服务提供者的访问地址(host,port),LoadBalancerInterceptor这个类会对ResTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真正服务地址信息,替换服务id
Spring Cloud Hystrix 熔断器
- Hystrix也是Netflix公司的一款组件
- Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库、防止出现级联失败也就是雪崩效应
- Hystrix解决雪崩问题的手段,主要是通过服务降级(兜底),线程隔离
熔断器状态有三种
- closed: 关闭状态 所有请求正常访问
- Open: 打开状态 所有请求都会被降级
- Half Open: 半开状态 Open状态不是永久,打开一会后会进入休眠时间(默认5秒). 休眠时间过后进入半开状态
- 半开状态: 熔断器会判断下一次请求的返回状况,如果成功,熔断器切回Closed状态.如果失败,熔断器切回open状态
熔断器的核心
- 线程隔离: 是指Hystrix 为每一个依赖服务调用一个小的线程,如果线程池用尽,调用立即被拒绝,默认不采用排队
- 服务降级(兜底方法): 优先保证核心服务,而非核心服务不可用或弱可用.触发Hystrix服务降级的情况: 线程池已满,请求超时
快速入门
添加依赖
<!--熔断器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在消费者的起步引导类中添加@EnableCircuitBreaker 注解 表示开启熔断
@EnableCircuitBreaker //开启熔断
在Contoller层添加方法
/**
* 服务降级处理方法
* @param id
* @return
*/
public User failBack(Integer id){
User user=new User();
user.setUsername("服务降级,默认处理");
return user;
}
在Controller的Servlet入口方法上添加注解@HystrixCommand(fallbackMethod=" 服务降级处理方法名")
@RequestMapping(value = "/{id}")
@HystrixCommand(fallbackMethod = "failBack") //方法如果处理出问题,就调用降级处理方法
public User queryById(@PathVariable(value = "id")Integer id){...}
其它熔断配置
- 熔断后休眠时间: sleepWindowInMilliseconds
- 熔断触发最小次数: requestVolumeThreshold
- 熔断触发错误比列阈值: errorThresholdPercentage
- 熔断超时时间: timeoutInMilliseconds
配置熔断策略
hystrix:
command:
default:
circuitBreaker:
#强制打开熔断器,默认false关闭的,测试配置是否生效,打开后,所有的请求都会被拒绝
forceOpen: false
#熔断最小请求次数,默认值是20,允许同时请求的线程数
requestVolumeThreshold: 10
#触发熔断错误比列阈值,默认值百分之50 例如:如果上面是10,经过参数后是5
errorThresholdPercentage: 50
#熔断后休眠时长,默认五秒
sleepWindowInMilliseconds: 10000
execution:
isolation:
thread:
#熔断超时时间 默认1秒
timeoutInMilliseconds: 1000