SpringCloud_v1.0
微服务架构
随着技术变革,我们开发软件的复杂性和成本大大提高了。原本的单体架构,代码管理困难,开发效率低。微服务架构把一个软件拆分成若干模块。然后根据模块的不同特性去分配资源,充分利用资源。此外,重新集成这个服务的时候,不会对其他服务造成影响(解耦合)。
分布式、集群
- 分布式系统就是把若干程序部署在不同计算机上,它们通过网络协作完成一个服务。(鸡蛋不能放在一个篮子里,简单来说就是分布式降低计算机崩溃带来的损失)
- 集群就是相同的程序找了几台服务器又部署了几次(如果一个程序在本机部署了多次,叫集中式集群,本机坏掉了就完了;如果是在不同计算器上部署了多次,那才叫分布式集群)
SpringCloud简介
Spring Cloud是一系列框架的整合。它将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包,极大的提高了开发效率。
SpringCloud整合了很多组件:
- Eureka注册中心
- Gateway网关
- Ribbon负载均衡
- Feign服务调用
- Hystrix熔断器
微服务简单模拟
service(提供者)
提供查询用户的接口
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id){
return userService.queryById(id);
}
}
@SpringBootApplication
@MapperScan("com.lxw.user.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
/*
User pojo类通过lombok和mybatis注解进行字段映射
写一个接口UserMapper extends Mapper<User>
yml中配置服务端口在9091
*/
consumer
通过restTemplate访问接口url,查询用户数据
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{id}")
public User queryById(@PathVariable Long id){
String url="http://localhost:9091/user/"+id;
return restTemplate.getForObject(url, User.class);//拿到url地址里面的数据,映射到User类里面 返回到页面上(这里我们没配置,默认是8080端口
}
}
problems
- 在consumer中,我们url写在代码里面,耦合高不方便后期维护,并且consumer需要清楚的知道service地址。
- consumer不知道service的状态,如果服务宕机就出问题
- service只有一台服务,可用性低。改成集群的话consumer还需实现负载均衡(可以理解选一个接口节点去访问)
Eureka注册中心
类似于前面学的ZooKeeper,可以进行服务管理,定期检查服务状态,返回服务列表给消费者,而后消费者再基于负载均衡算法从列表中选择一个进行调用。
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类注解
@EnableDiscoveryClient
//开启Eureka客户端发现功能
配置修改
spring:
application:
#这里服务端的话就写(user-service)
name: consumer-demo
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka
高可用配置
#将Eureka Server作为一个服务注册到其他Eureka Server中,这样多个Eureka Server之间就能够相互发现对方,同步服务,实现Eureka Server集群
# 配置文件中去掉以下两个参数,默认为true!
# 不注册自己
register-with-eureka: false
# 不拉取服务
fetch-registry: false
Eureka客户端和服务端配置
# 服务提供者(服务地址使用ip和续约)
eureka:
instance:
# 更倾向使用ip地址而不是host名
prefer-ip-address: true
# ip地址
ip-address: 127.0.0.1
# 续约间隔
lease-renewal-interval-in-seconds: 30
# 服务失效时间
lease-expiration-duration-in-seconds: 90
# 服务消费者(获取服务地址的频率)
eureka:
client:
# 获取服务地址列表间隔时间
registry-fetch-interval-seconds: 10
# Eureka服务端
eureka:
server:
# 服务失效剔除时间间隔(毫秒)
eviction-interval-timer-in-ms: 60000
# 关闭自我保护模式
enable-self-preservation: false
负载均衡Ribbon
简介:Ribbon提供了轮询、随机两种负载均衡算法(默认轮询),可以用该算法从地址列表中获取一个进行服务调用。
简单应用
/*
在创建RestTemplate的时候加入负载均衡注解
它内部会在RestTemplate中加入LoadBalancerInterceptor这个拦截器,这个拦截器的作用就是负载均衡。
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
熔断器Hystrix
简介:Hystrix是一个延迟和容错库,用于隔离访问远程服务,防止出现级联失败。
雪崩效应
微服务中一个请求一般要调用多个微服务接口实现,一旦有一个微服务接口发生异常,请求阻塞,用户得不到响应后,这个线程就卡在tomcat里,越来越多的线程阻塞就会导致服务器崩溃。
Hystrix通过两种方法解决雪崩效应:
- 服务降级:服务降级是从整个系统的负荷情况考虑,预防某些功能出现负荷过载,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息。即使提供的了有损的服务,但保证了整个系统的稳定性和可用性。
- 服务熔断:类比保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统崩溃,暂时停止对该服务的调用。
具体实现
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
//启动类开启熔断注解
//@SpringBootApplication
//@EnableDiscoveryClient//开启Eureka客户端发现功能
//@EnableCircuitBreaker //开启熔断
@SpringCloudApplication//包含上面三个注解
public class ConsumerApplication {
......
}
降级实现
@RestController
@RequestMapping("/consumer")
@Slf4j
@DefaultProperties(defaultFallback = "defaultFallback")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
//@HystrixCommand(fallbackMethod = "queryByIdFallback")
@HystrixCommand
public String queryById(@PathVariable Long id){
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
// public String queryByIdFallback(Long id){
// log.error("查询用户信息失败 id:{}",id);
// return "网络繁忙,请稍后再试!";
// }
public String defaultFallback(){
return "默认提示:网络繁忙,请稍后再试";
}
}
Feign
Feign也叫伪装,可以把Rest请求进行隐藏,自动根据参数去拼接http请求地址,比较优雅。顺带一提,Feign自带Ribbon负载均衡和hystrix
原生
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, User.class);
实现
//声明当前类是一个Feign客户端,指定服务名
@FeignClient("user-service")
public interface UserClient {
//比如id是123 底层自动拼接http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable Long id);
}