随着互联网的发展,网站应用的规模不断扩大。需求的激增,带来的是技术上的压力。系统架构也因此也不断的演
进、升级、迭代。从单一应用,到垂直拆分,到分布式服务,到SOA,以及现在火热的微服务架构。是什么导致了
架构的不断演变,哪些需求需要我们来不断地更新架构,这些架构又是如何解决这些问题的。
在网站需求量较小的时候,我们使用单机式的架构就足以满足访问的需求,只需要一个应用,将所有功能部署在一起,
达到减少成本的目的。这个的优点就是开发速度快,维护成本低等。
当访问量逐渐增大,单一应用无法满足需求,此时为了应对更高的并发和业务需求,我们根据业务功能对系统进行拆
分成不同的模块,这样做的好处是实现了流量分担解决并发问题,而且可以分模块的进行开发和优化。
当访问量进一步增大,这时候应用越来越多,应用之间交互不可以避免,将核心业务抽取出来,形成一个服务中心。
使得应用能够更快的响应。优点是提高了代码的复用等。
SOA(Service Oriented Architecture)面向服务的架构:它是一种设计方法,其中包含多个服务, 服务之间通过相
互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。
微服务架构是使用一套小服务来开发单个应用的方式或途径,每个服务基于单一业务能力构建,运行在自己的进程
中,并使用轻量级机制通信,通常是HTTP API,并能够通过自动化部署机制来独立部署。这些服务可以使用不同的
编程语言实现,以及不同数据存储技术,并保持最低限度的集中式管理。
微服务现在已经慢慢地成为国内互联网公司的主流和趋势,而微服务是分布式的,所以需要远程的服务调用。
主要分为两种,一种是RPC,一种是http。
RPC的代表框架是Dubbo,这个是阿里的一个框架,用起来效率很高。
http的代表框架就是Springcloud,这个名字大家听起来就很熟悉和亲切了,他依然是Spring大家族的一员,
和其他spring家族中的框架一样,能够很好地支持spring家族的其他框架。
如何选用?
因为RPC是基于API的,所以如果你的公司都是java程序员或者某个项目全是用java做的,使用Dubbo会效率更高。
反之,如果是各种语言都有的公司或者项目,使用SpringCloud会更为合适,并且SpringCloud的功能会相对更多。
Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和
反序列化,非常方便。
在springboot项目中演示如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class RestTemplateTest {
@Autowired
private RestTemplate restTemplate;
@Test
public void test(){
//如果要测试需要启动spring boot项目,以便获取数据
String url = "http://localhost/user/8";
User user = restTemplate.getForObject(url, User.class);
System.out.println(user);
}
}
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
顺带提一嘴,SpringCloud比较有意思的是他的版本不像其他的框架都是1.0.5啊这种正规的版本,他的版本是以英国的地铁站名来定义
1.为什么需要Eureka注册中心?
如果不使用Eureka的话,我们用RestTemplate远程调用的时候,服务端就需要暴露自己的地址,不安全。而调用者需要记录服务提供者的地址,将来地址变更的时候,还需要更改,这就非常的不方便了。
而Eureka使用后,服务提供者在Eureka注册后,Eureka就把它记住了。而服务调用者这时候就不需要记住地址,只需要把自己的需求告诉Eureka。Eureka就会把符合你需求的服务告诉你。Eureka还有续约机制,或者叫心跳机制,就是提供者定期通过http方式刷新Eureka中自己的信息。
Eureka代码实现如下:
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class);
}
}
配置文件yaml如下:
server:
port: 8001
spring:
application:
name: eureka-server # 应用名称,会在Eureka中作为服务的id标识
eureka:
client:
service-url: # EurekaServer的地址
defaultZone: http://127.0.0.1:8001/eureka
register-with-eureka: false # 不注册自己,如果是集群就需要注册
fetch-registry: false #不拉取,如果集群拉取
服务提供者代码实现如下(服务注册):
@SpringBootApplication
@MapperScan("com.new.user.mapper")
@EnableDiscoveryClient // 开启Eureka客户端发现功能
public class UserServiceDemoApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceDemoApplication.class, args);
}
}
配置文件如下:
server:
port: 9001
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud
username: root
password: root
application:
#应用名
name: user-service
mybatis:
type-aliases-package: cn.new.user.pojo
eureka:
client:
service-url:
defaultZone: http://localhost:8001/eureka
服务调用者代码如下:
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@
Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
配置文件如下:
server:
port: 8080
spring:
application:
name: consumer
eureka:
client:
service-url: # EurekaServer地址
defaultZone: http://127.0.0.1:8001/eureka
调用演示:
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("{id}")
public User queryById(@PathVariable Long id){
String url = "http://localhost:9001/user/" + id;
//获取eureka中注册的user-service实例列表
List serviceInstanceList =
discoveryClient.getInstances("user-service");
ServiceInstance serviceInstance = serviceInstanceList.get(0);
url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort()
+ "/user/" + id;
return restTemplate.getForObject(url, User.class);
}
}
为什么要保持高可用?
当一台服务端的Eureka出现问题时,你整个服务都不可用了,这就很影响效率,稳定性等,这时候我们要保持服务的高可用。
实现高可用的方式就是集群!
集群后,多个Eureka Server之间会相互注册,内容也会同步到每一个节点,客户端无论访问到那个节点,都能获得所有的信息。
实现集群:
思路:如果有三个Eureka,他们之间要相互注册,假定三个Eureka分别为abc。
a要注册到b和c上。
b要注册到a和c上
c要注册到a和b上
Eureka Server代码实现:
server:
port: ${port:8001}
spring:
application:
# 应用名称,会在eureka中作为服务的id(serviceId)
name: eureka-server
eureka:
client:
service-url:
# eureka服务地址;如果是集群则是其它服务器地址,后面要加/eureka
defaultZone: ${defaultZone:http://127.0.0.1:8001/eureka}
# 是否注册自己,自身不提供服务所以不注册
#register-with-eureka: false
# 是否拉取服务
#fetch-registry: false
就是这个springboot连续启动两次,第一次用默认的8001端口,defaultZone:http://127.0.0.1:8002/eureka启动。
第二次启动在idea的Run/Debug Configurations页面来配置端口号为8002,defaultZone:http://127.0.0.1:8001/eureka,再启动。
服务提供者和服务调用者(也就是统称Eureka客户端)代码如下:
eureka:
client:
service-url: # EurekaServer地址,多个地址以','隔开
defaultZone: http://127.0.0.1:8001/eureka,http://127.0.0.1:8002/eureka
当服务提供者开启集群之后,此时获取的服务列表中就会有多个,这时候就需要用负载均衡来帮助我们自动选择了。
SpringCloud自动帮我们实现了负载均衡,这个组件就是Ribbon。
实例:
先开启两个服务提供者。
在服务调用者的RestTemplate上添加@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
调用:
@GetMapping("{id}")
public User queryById(@PathVariable("id") Long id){
String url = "http://user-service/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return user;
}
就这么简单,完了。当然负载均衡还有很多算法可以选择,比如轮询,随机等,感兴趣的可以自己了解。
简介:
这个组件名称很有意思啊,他的英文直译过来就是豪猪的意思,也不知道为什么这么叫,有知道的评论区告诉我一下,哈哈。
好了,言归正传,Hystrix的作用是为微服务系统提供保护机制。
官方是这样说的:Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败。
具体问题:
雪崩问题,相信大家不会对这个词感到陌生,简单来说就是一个问题,比如一个阻塞了,线程不会释放,越来越多的请求一道来,就会有越来越多的线程会阻塞。
那么Hystrix怎么解决雪崩问题?
主要是
Hystrix为每个以来服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,就是加速了失败的判定时间。
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超
时,则会进行降级处理。
代码实现:
在消费调用者上添加注解:@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
// ...
}
这三个注解可以合并成为一个注解@SpringCloudApplication
当服务调用出现意外或者故障时,框架的编写者想到应该快速失败,因此需要提前编写好失败时的降级处理逻 辑,要使用HystrixCommand来完成。如下:
@RestController
@RequestMapping("/consumer")
@Slf4j
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("{id}")
@HystrixCommand(fallbackMethod = "queryByIdFallback")
public String queryById(@PathVariable Long id){
String url = "http://localhost:9091/user/" + id;
url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
public String queryByIdFallback(Long id){
return "对不起,出错了!";
}
}
说明: @HystrixCommand(fallbackMethod = "queryByIdFallBack"):用来声明一个降级逻辑的方法
刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以可以把Fallback配置加在类 上,实现默认fallback;
@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://localhost:9091/user/" + id;
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 "默认提示:对不起,网络太拥挤了!";
}
}
在服务熔断中,使用的熔断器,也叫断路器,其英文单词为:Circuit Breaker
熔断机制与家里使用的电路熔断原理类似;当如果电路发生短路的时候能立刻熔断电路,避免发生灾难。
在分布式系 统中应用服务熔断后;服务调用方可以自己进行判断哪些服务反应慢或存在大量超时,可以针对这些服务进行主动熔 断,防止整个系统被拖垮。
Hystrix的服务熔断机制,可以实现弹性容错;当服务请求情况好转之后,可以自动重连。通过断路的方式,将后续 请求直接拒绝,一段时间(默认5秒)之后允许部分请求通过,如果调用成功则回到断路器关闭状态,否则继续打 开,拒绝请求的服务。
后半部地址:https://blog.csdn.net/strandtrack/article/details/117087359