本文共 16905 字,大约阅读时间需要 56 分钟。
微服务架构是一种架构模式,它提倡将单一应用拆分成一组小的服务(微服务),服务之间相互协调和配合,每个服务独立运行在自己的进程中、服务之间通过轻量级的通信机制协同、每个服务都是围绕具体业务进行构建(是一个独立的工程)、且都能够独立部署;
!!!细节:微服务应用开发和服务所需要的组件;这两个概念需要特别注意,微服务应用程序使用springboot开发,而过程中服务所需组件(服务注册、发现、熔断、路由、配置等)需要有对应的组件的支持奥! !!!细节微服务开发编程模型有springcloud;但是springcloud alibaba 是一个微服务开发的简洁化、一站式的解决方案,它包括了微服务开发中开发分布式应用程序和服务所需要的其他组件,以便开发人员可以基于springcloud编程模型轻松开发分布式应用程序。
//dubbbo的相关自动化配置依赖;//引入zookeeper的客户端工具; zk客户端在这边没有手动创建,它这个客户端被dubbo自动化配置里面使用的 com.alibaba.boot dubbo-spring-boot-starter 0.1.0 com.github.sgroschupf zkclient 0.1 cn.hutool hutool-all 5.1.0
核心了解:RPC原理:是调用远程的方法,但是在本地有一个和该远程方法的相同全类名路径的一个映射接口,通过@Reference注解完成远程引用映射的【这个注解和发布那边的dubbo的@service发布注解相呼应上】; 即@service发布注解+@Reference引用注解; 发布服务连带全类名发布,同样引用匹配同样全类名路径进引用
简单理解dubbo发布某个service服务,是带全类名发布到中心的,同样dubbo引用(调用)的话,也会匹配相同全类名路径来调用该服务的;发布通过注解@service完成某个全路径的service层的服务方法的发布,同样引用通过注解@Reference来匹配同样全路径名的服务方法的引用哟!!
上面微服务架构的理念可知,分布式就是许多单元的集合体,虽然有许多好处,但是随之难点就是这些单元之间的通信、 可靠、均衡、高效、健壮性如何保证;就出现了相应的技术。
springcloud是分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶微服务体系结构中的关键组件
首先需要了解,springcloud是基于springboot的,springboot是创建每一个微服务的,特别注意——springboot的理念: 约定(pom) > 配置(properties) > 编码(业务逻辑编码+注解)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 核心 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< springcloud和springcloud alibaba是不一个概念奥!springcloud可以说是微服的编程模型或者体系架构,而springcloud alibaba是这个体系的一个高效的解决方案;这个方案可以使得开发人员可以使用Spring Cloud编程模型轻松开发分布式应用程序。
简述下微服务模式演进逻辑;(1)基于业务规模化和研发效能等因素的驱动下,从单应用向微服务架构的转型,已经成为数字化转型的趋势;(2)每个服务都是围绕具体业务运行于自己独立进程中,且可以独立部署和构建;这都是微服务的好处,且每个服务可以以集群方式部署,增强了高可用性;服务之间可以通过rpc或者rest来保证通信互调;且各个微服务业务可以用不同语言开发。(3)拆分为微服务单元有这些优势,但是服务发现和负载均衡成为整个微服务架构中核心问题;这问题核心解决就是代理;也只有代理才能完成这样的功能;即为啥服务提供者没有直接让用户调用,而通过中间代理(服务消费者)才能方便完成服务发现和负载均衡调用的功能。(4)第三条可以这样理解:微服务有第二点那些优势,但是需要有服务发现和负载均衡的过程,所以在服务提供方和用户之间需要增加一层代理由代理负责服务发现和负载均衡的功能,用户通过代理间接访问服务提供方。
(1)Nacos是一个易于使用的动态服务发现,配置和服务管理平台,用于构建云本机应用程序。简单说:nacos是Euraka+config的组合体。
(2)nacos可以自动将服务注册到nacos服务器,并且nacos服务器会跟踪服务并且动态刷新服务列表。即Nacos Discovery自动将服务注册到nacos服务器(平台),且会动态刷新服务列表(还有服务实例相关详细的元数据奥!主机、端口、运行状况检查URL)com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
server.port=8081 # 设置本微服的端口 ( 服务提供者对应的端口 )spring.application.name=nacos-provider # 本微服务的服务名称spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 # 本微服务需要注册到的哪个nacos注册中心management.endpoints.web.exposure.include=*注意: 如果您不想使用Nacos进行服务注册和发现,则可以设置spring.cloud.nacos.discovery为false。
@SpringBootApplication@EnableDiscoveryClient ## 核心注解,开启nacos discovery的服务注册和发现public class NacosProviderDemoApplication { public static void main(String[] args) { SpringApplication.run(NacosProviderDemoApplication.class, args); } @RestController public class EchoController { @GetMapping(value = "/echo/{string}") public String echo(@PathVariable String string) { return "Hello Nacos Discovery " + string; } }}
;同时注意消费方的体现一般只有controller层,没有所谓的service层和dao层奥!它只负责调用服务,不是自己创建的服务奥!
@SpringBootApplication@EnableDiscoveryClientpublic class NacosConsumerApp { @RestController public class NacosController{ @Autowired private LoadBalancerClient loadBalancerClient; @Autowired private RestTemplate restTemplate; @Value("${spring.application.name}") private String appName; @GetMapping("/echo/app-name") public String echoAppName(){ //Access through the combination of LoadBalanceClient and RestTemplate ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider"); String path = String.format ("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName); System.out.println("request path:" +path); return restTemplate.getForObject(path,String.class); } } //Instantiate RestTemplate Instance @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(NacosConsumerApp.class,args); }}
nacos组件可以服务注册(服务提供方)、服务发现(服务消费方法)、服务配置文件设置;
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config
#nacos配置#server: # port: 8090#spring.application.name=nacos-config#spring.cloud.nacos.config.server-addr=127.0.0.1:8848#spring.cloud.nacos.config.file-extension=yaml#spring.profiles.active=developspring: application: name: nacos-config-demo # 这是本模块作为服务的服务名,同时也为默认配置的配置名一部分; # 配置名dataid 的前缀名可以通过属性:spring.cloud.nacos.config.prefix来自定义配置也可以。 cloud: nacos: discovery: server-addr: localhost:8848 #Nacos服务注册中心地址 config: server-addr: localhost:8848 #Nacos作为配置中心地址 file-extension: properties #指定yaml格式的配置 # group: DEV_GROUP #指定在哪个group里 # namespace: a32dc940-fbdd-46bc-a36f-2231cbade993 profiles: active: - test#${spring.application.name}-${spring-profile.active}.${spring.cloud.nacos.config.file-extension} 这几个属性组成dataid配置文件名称。其中spring-profile.active是细分项目不同环境时使用不同的配置的方式。# 说明一下:服务的调用,实际上服务提供者作为服务注册到注册中心后,会有服务的消费者来调用,消费者调用本质就是通过在服务注册中心中获取到对应服务提供者的#服务名,然后本质是将服务提供者的服务名作为对应的ip+端口的映射来获取服务提供者提供的rest风格的功能服务。 为啥多出一个服务消费者中间人呢,相当于代理# 这样就可以对服务提供进行一些其他负载呀、高可用呀等操作了。
(1)分布式的微服务架构: 通常最基本两点 ———— 服务注册和发现、服务间的调用; 【注】服务名(ip+端口)+服务路径(/xxx/xxx)核心就这两个发现
细节:(1)服务发现和调用: 使用Euraka的话,方便,因为直接jar包就导入服务器了,注意配置是否注册进中心、是否获取中心服务、注册进中心路径这三个最基本的配置; 最后在主类上加上激活Euraka服务端注解 : @EnableEurekaServer; (1.1) 如果使用zk或者consul,需要下载第三方服务端,然后项目无论消费端发现还是提供端注册都是三步走: (1.1.1)配置中两步:服务名、连接注册中心路径;----核心---(1.1.2)主类上一步:@@EnableDiscoveryClient该注解用于向使用consul或者zookeeper作为注册中心时注册服务和发现服务上面服务发现后:可以通过resttemplate+服务名来完成微服务的调用;这是最简单的方式: 配置类中注册resttemplate;controller中通过服务名访问 @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } ------------------------------------- public static final String INVOKE_URL = "http://consul-provider-payment"; @Resource private RestTemplate restTemplate; @GetMapping(value = "/consumer/payment/consul") public String paymentInfo() { String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class); return result; }cloud-consumerconsul-order80 + cloud-providerconsul-payment8006 看这两个一个服务注册,一个服务发现调用(2)服务调用使用feign;—————— 服务调用核心就是找到微服务路径 ——【服务名+请求路径】 resttemplate是自己基于服务名+路径访问的; 而feign是通过注解 基于 服务名 + 路径来访问的;这样feign完成对远端服务的接口的映射和访问 (2.1)在消费方,要在接口上加上@feignClient(value=“微服务名称”)注解还有注册进容器注解@component, (2.2)还有在主启动类上标注激活注解@EnableFeignClients。@Component@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //目标微服务的名称 不要忘记在消费方的主类上激活feign————@EnableFeignClientspublic interface PaymentFeignService { //Feign接口中的方法要与目标服务中的Controller中的方法完全一致 @GetMapping(value = "/payment/get/{id}") public CommonResultgetPaymentById(@PathVariable("id") Long id); @GetMapping(value = "/payment/feign/timeout") public String paymentFeignTimeout();}上面是feign完成微服务的调用:对比resttemplate,知道微服务名、请求路径,完成web请求,而feign不也是吗,知道微服务名和请求路径的接口,点对点直接的完成指定微服务的controller接口的调用------------------------------------@SpringBootApplication@EnableFeignClients //启动Feign客户端public class OrderFeignMain80 { public static void main(String[] args) { SpringApplication.run(OrderFeignMain80.class,args); }}====================================上面接口就是映射的指定微服务对应的服务接口,我可以本地使用这个映射的接口来调用远端的微服务提供方的服务奥! 就和dubbo的服务调用一样
当微服务之间相互调用过程中,有些服务可能发生异常,所以对这个接口可以设置熔断或者降级处理;
org.springframework.cloud spring-cloud-starter-netflix-hystrix 2.2.1.RELEASE
@HystrixCommand(fallbackMethod = "getHystrixFallBackTeString",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") }) @GetMapping("/hystix/{id}") public String getHystrixTeString(@PathVariable("id") Integer id) { System.out.println("前!!!"); Integer xInteger = 10/(id-1); System.out.println("后!!!"); return "成功!!!"+xInteger; } public String getHystrixFallBackTeString(Integer id) { //备选方法 System.out.println("getHystrixFallBackTeString"); return "异常失败!!!"; }------------------------------------上面的问题:(1)备选方法和目标方法一一对应,会造成代码太多、膨胀(2)备选方法和目标方法在一块,耦合较高上面第一个问题解决办法:可以在整个类上加上一个全局的处理异常的备选方法@RestController@DefaultProperties(defaultFallback="payment_Global_FallbackMethod")public class OrderHystrixController {// 核心细节——————————@DefaultProperties表示对标有@HystrixCommand注解的方法进行全局的降级处理的默认的全局方法。// 就是那些设置降级处理的方法,如果没有明确指定备选方法时,统一用这个默认的全局处理的备选方法。//下面是全局fallback方法 public String payment_Global_FallbackMethod() { return "Global异常处理信息,请稍后再试"; }
#用于服务降级,在注解@FeignClient中添加fallbackFactory属性值feign: hystrix: enabled: true #在Feign中开启Hystrix======================================================这部分是feign对远程指定微服务名下的指定请求路径的映射调用;同时通过fallback 属性对这些接口整体使用降级备选方法的设置@Component@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)public interface PaymentHystrixService { @GetMapping(value = "/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id); @GetMapping(value = "/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id);}-------------------------------------------------------//这就是所以接口分别对应同名的降级的备选方法;@Componentpublic class PaymentFallbackService implements PaymentHystrixService { @Override public String paymentInfo_OK(Integer id) { return "PaymentFallbackService Fallback paymentInfo_OK"; } @Override public String paymentInfo_TimeOut(Integer id) { return "PaymentFallbackService Fallback paymentInfo_TimeOut"; }}
在服务提供端设置降级,这样服务端指定服务如果异常,会有一个备选方法来处理;而消费端设置熔断和降级,这样当消费端调用服务提供方法,如果服务提供方能够自身熔断处理了,那么就返回服务提供方的处理结果,如果不能,就消费方自身的熔断处理备选方法的处理。
当失败调用次数到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制
分布式中设计的技术:(1)服务注册和发现(2)服务调用(3)全局配置(4)熔断和降级、限流(5)分布式事务
消息总线本质是使用mq,即微服务架构中,引入总线原理: 就是在mq中创建一个共用的消息主题——spring cloud bus,让所有配置bus总线的服务都监听该主题,然后该主题中只要产生消息就会被其他服务监听并消费,因此才称为消息总线。在总线上的各个实例,都可以方便地广播一些要让其他连接和监听该主题的实例都知道的消息。也即各服务直接的通信(分布式不同进程之间可以用mq通信)。
由于springcloud Netflix相关组件都多少进入维护模式,spring cloud alibaba 提供了微服务开发的一站式解决方案,此项目包含了开发分布式应用微服务的必需组件,方便开发者通过springcloud编程模型轻松使用这些组件来开发分布式应用服务。
RESTful风格:首先它是一个设计规则和约束条件; REST是representational state transfer 简单理解为资源的表现形式,资源的状态转变的表现形式。网络中每一个资源对应一个url,那么这个资源的状态转变-增删改查状态的表现形式,而http是无状态的,所以为了表现资源的状态转变就通过GET、POST、PUT、DELETE来表示作为资源url的表现形式。 通过这几个状态表现形式使得资源更简介、清晰,更有层次。
主要核心:
(1)是什么? 随着微服务的流行,服务和服务之间的稳定性越来越重要,sentinel以流量为切入点,从流量控制、熔断剪辑、系统负载保护等多个维度保护服务的稳定性。
(2)好处: 有一套web界面可以给我们进行更加颗粒化的配置1.流控(流量控制)、2.速率控制(漏斗算法一样的速率控制)、3.服务熔断、4.服务降级等; sentinel单独一个组件,独立出来,直接图形化界面进行细颗粒的统一配置。使用场景: 秒杀流控(突发流量控制在系统容量可承受范围内)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等sentinel分为两个部分: 核心库(java客户端)不依赖任何框架、库,都能够运行于所有java运行时环境,同时对dubbo和cloud等框架有较好支持; 控制台(dashboard)基于springboot开发,打包后可以直接运行,不需要额外的tomcat等应用容器;
(1)传统限流、熔断和降级处理,直接对监控到的资源进行配置就可以了。一般会有默认的处理,直接异常为 block limiting . . .
(2)自定义异常处理:sentinel组件中,注解@sentinel Resource里面的fallback属性负责Java运行时异常,blockhandler负责sentinel的监控是否符合配置的处理。fallback和blockhandler处理的目标是不同的,fallback只处理java运行时异常的回调或者降级处理方法,blockhandler负责sentinel限流或者熔断规则不满足后的处理方法。Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。之前已经经过历年双十一的验证,2019年为了打造更加完善的生态和普惠技术成果,该技术已经对外开源,未来seata将以社区共建形式进一步完善该技术。
首先一个全局事务id,三个组件:tc(事务协调器),tm(事务管理器),rm(资源管理器)。
TM:负责全局事务的开启,提交和回滚指令的定义。RM:向tc进行分支事务的汇报,驱动分支事务的提交和回滚。TC:协调器,基于TM事务指令进行全事务的提交和回滚,并协调全局事务状态和分支事务状态的同步。即:分支事务的状态驱动由rm,全局事务的状态驱动和维持全局事务和分支事务状态一致的协调由tc,tm只是全局事务的定义或者指令传达者
原理: SeaTa的二阶段事务提交———— 一阶段进行各分支sql的执行操作,并记录下操作前置和后置的事务日志。二阶段全局事务的提交还是回滚定义,有事务协调器来进行协调和子事务的回滚和提交。因为有事务前后的日志记录,所以可以简单方便的回滚或者提交操作。
第一种方式:uuid————满足唯一性,但是趋势递增不满足,因为数据库中索引结构是b+树,在叶子节点中索引递增这样追加索引比插入要高效,且uuid32字节,对应数据库的默认io页(16kb)操作,不太高效。
第二种方式: mysql自增主键——————满足唯一性,且满足趋势递增,但是数据库的高可用和高并发维护的数据库水平扩展不太方便,扩展数据库节点对自增步长有冲突。数据库本身对高并发访问就有局限,所以数据库自增主键对高并发场景不太合适replace into;select inset_last_id()
第三种方式:redis自增主键————incre和increby,满足唯一性,趋势递增,高可用,但是还是有扩展和步长设置冲突问题,而且单独维护一个全局id,要维护一个redis集群,性价比不太高。
redis可用实现全局id,分布式锁; https://www.cnblogs.com/barrywxx/p/11644803.html 分布式锁三种实现方式 分布式锁可用zk或者redis都可以。
第四种方式: 雪花算法;创建一个64位的序列,前42是时间戳,后十是数据中心和节点数,最后十二是每毫秒的序列数。