本博文是连贯的博文,请从第一篇开始阅读spring-boot+spring-cloud学习
Eureka
介绍:Spring Cloud Eureka是Srping Cloud Netflix微服务套件中的一部分,基于Netflix Eureka做了二次封装,主要负责完成微服务架构中的服务治理功能。拥有服务注册中心、服务提供者、服务消费者三种角色。
1 快速搭建一个EureKa服务注册中心
上图为项目的结构图
和我们上篇博文写的羡慕恨类似,只是多了一个WebSecurityConfigure类
首先看pom文件
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--注意此处的依赖是SpringBoot2.0以后专用的,如果您使用的SpringBoot版本低于2.0请使用spring-cloud-starter-eureka-server-->
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
配置文件application.yml
spring:
application:
name: sb-eureka #该项目名
#开启权限认证,即登录eureka页面需要输入账号密码
security:
basic:
enabled: true
user:
name: root
password: root
server:
host: localhost
port: 7001
eureka:
client:
#此项目不作为客户端注册,因为该项目是作为注册中心
register-with-eureka: false #是否需要注册
fetch-registry: false #是否需要检索
WebSecurityConfigurer
@EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter{
/**
* 如果您开启了权限验证并且SpringBoot版本为2.0以上的话还需要一个操作,如果不是此布可以忽略
* 因为2.0默认开启了csrf,如果我们现在直接启动Eureka服务的话客户端是注册不上的,所以需要把csrf关闭
* 否则无法往注册中心注册服务
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
super.configure(http);
}
}
入口文件SbEureApplication
@SpringBootApplication
/* @EnableEurekaServer该注解,意思为:该类为eureka注册中心*/
@EnableEurekaServer
public class SbEureApplication {
public static void main(String[] args) {
SpringApplication.run(SbEureApplication.class, args);
}
}
都写好后,执行SbEureApplication .main()方法,执行成功后,在界面输入localhost:7001回车
首次加载该页面,因为有权限验证,所以有一个登录页面,输入我们配置文件中配置的账号密码登录即可进入注册中心页面。
这就是最简单的Eureka注册中心。
有了注册中心后,我们要往注册中心注册服务
2 注册服务编写
新建一个模块sb-provider,将sb-provider文件复制过来
项目结构图如上
pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
入口类ProviderApplication
/*@EnableEurekaClient 修饰服务提供者 */
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}
controller类
@RestController
@RequestMapping("Provider")
public class ProviderController {
@GetMapping("getName")
public String egtName(){
return "张三";
}
}
配置文件
spring:
application:
name: sb-provider
server:
host: localhost
port: 7021
eureka:
client:
#将此项目注册到Eureka服务
register-with-eureka: true
service-url:
defaultZone: http://root:root@localhost:7001/eureka #该地址就是该服务要注册的服务注册中心
运行入口类ProviderApplication.main()方法,
查看Eureka的注册中心界面会发现在DS Replicas下面会列出注册的服务信息。
3 Ribbon实现多服务模式的负载均衡
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端均衡负载工具,它基于Netflix Ribbon实现的。
通常我们的服务是分布式,所以会有多个服务器,所以我们再创建一个模块sb-provider1,将sb-provider模块中的内容都复制到该模块中,同时也注册到sb-eureka中
只需要修改启动类的名字,ProviderController改成Provider1Controller,配置文件的server.port端口号改成7022即可。
此时,刷新eureka页面会发现,SB-PROVIDER的Availability Zones变成了2.即代表啊有两个sb-provider服务。
调用分布式服务需要写一个入口类来控制负载均衡
均衡负载有两种实现方式,
1.在CloudDemoConsumerApplication类上级新建包config,然后新建LoanBalanced类。使用此类注册一个IRule以达到替换Eureka的目的
2 通过自定义注解来解决包扫描的问题
3.1 第一种
新建一个模块sb-consumer
配置文件
spring:
application:
name: sb-consumer
server:
port: 7011
eureka:
client:
register-with-eureka: true
service-url:
defaultZone: http://root:root@localhost:7001/eureka
入口文件SbConsumerApplication
@SpringBootApplication
/*关联我们写的均衡负载类*/
@RibbonClient(name = "sb-provider", configuration = cn.wzl.sb.cosumer.config.LoadBalanced.class)
public class SbConsumerApplication {
@Bean
@LoadBalanced //开启均衡负载
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(SbConsumerApplication.class, args);
}
}
均衡负载类
LoadBalanced
@Configuration
public class LoadBalanced {
@Bean
public IRule ribbonRule() {
//return new RoundRobinRule(); //轮询
// return new WeightedResponseTimeRule(); //加权权重
//return new RetryRule(); //带有重试机制的轮训
return new RandomRule(); //随机
//return new MyBalanceRule(); //自定义规则
}
}
如果要用自己定义的负载类,就自己写一个,如MyBalanceRule
public class MyBalanceRule implements IRule {
private ILoadBalancer loadBalancer;
@Override
/*均衡负载的逻辑*/
public Server choose(Object o) {
List<Server> servers= loadBalancer.getAllServers();
return servers.get(0);
}
@Override
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.loadBalancer=iLoadBalancer;
}
@Override
public ILoadBalancer getLoadBalancer() {
return this.loadBalancer;
}
}
Acontroller
@RestController
@RequestMapping("ConsumerA")
public class AController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("getName")
public Object getName(){
/*参数为服务地址,和返回参数的类型*/
return restTemplate.getForObject("http://sb-provider/Provider/getName",String.class);
}
}
页面访问http://localhost:7011/ConsumerA/getName就可以得到字符串张三或者李四
3.2 第二种
pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
AvoidLoanbalanced:
public class AvoidLoanbalanced {
@Bean
public IRule ribbonRule() {
return new RoundRobinRule(); //轮询
// return new WeightedResponseTimeRule(); //加权权重
//return new RetryRule(); //带有重试机制的轮训
//return new RandomRule(); //随机
//return new MyBalanceRule(); //自定义规则
}
}
ExcludeFromComponentScan:
public @interface ExcludeFromComponentScan {
}
LoadBalanced:
/**通过代码的方式自定义负责均衡策略时需要注意的是,注意避免SpringBoot的包扫描,
* 因为自定义的规则必须在Eureka的规则实例化以后再实例化才会生效
* Created by 99410 on 2018/10/18.
*/
@Configuration
public class LoadBalanced {
@Bean
public IRule ribbonRule() {
//return new RoundRobinRule(); //轮询
// return new WeightedResponseTimeRule(); //加权权重
//return new RetryRule(); //带有重试机制的轮训
return new RandomRule(); //随机
//return new MyBalanceRule(); //自定义规则
}
}
MyBalanceRule:
public class MyBalanceRule implements IRule {
private ILoadBalancer loadBalancer;
@Override
/*均衡负载的逻辑*/
public Server choose(Object o) {
List<Server> servers= loadBalancer.getAllServers();
return servers.get(0);
}
@Override
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.loadBalancer=iLoadBalancer;
}
@Override
public ILoadBalancer getLoadBalancer() {
return this.loadBalancer;
}
}
AController:
@RestController
@RequestMapping("ConsumerA")
public class AController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("getName")
public Object getName(){
/*参数为服务地址,和返回参数的类型*/
return restTemplate.getForObject("http://sb-provider/Provider/getName",String.class);
}
}
SbConsumer1Application:
@SpringBootApplication
/*关联我们写的均衡负载类*/
@RibbonClient(name = "sb-provider", configuration = AvoidLoanbalanced.class)
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, value = ExcludeFromComponentScan.class) })
public class SbConsumer1Application {
@Bean
@LoadBalanced //开启均衡负载
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(SbConsumer1Application.class, args);
}
}
配置文件:application.yml就可以了
spring:
application:
name: sb-consumer1
server:
port: 7012
eureka:
client:
register-with-eureka: true
service-url:
defaultZone: http://root:root@localhost:7001/eureka
页面访问http://localhost:7012/ConsumerA/getName就可以得到字符串张三或者李四
参考博文:http://www.cnblogs.com/zhixiang-org-cn/p/9296766.html
Ribbon的均衡负载和Zookeeper均衡负载的区别
Ribbon重视数据的可用性和可靠性,而zk重视数据的一致性和可靠性。
当服务注册中心的网络发展故障断开时,犹豫所有的服务实例都无法维持心跳,强调一直性和可靠性的zk就会将所有的实例否剔除掉,而ruerba则会因为超过百分之85的实例丢失心跳而触发保护机制,注册中心将会保留此时的所有节点,以实现服务间依然可以进行调用的场景。
4 详细描述Eureka
4.1 服务提供者:
服务注册: 服务提供者启动时,发送REST请求到Eureka Server上,将数据存到一个双层机构的Map中,其中第一层的key是服务名,第二层的key是具体服务的实例名
服务同步: 由于服务注册中心之间因互相注册为服务,服务提供者发送注册请求到一个服务注册中心时,它会将请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步。
服务续约: 服务提供者会维护一个心跳用来持续告诉Eureka Server,我还活着,防止Eureka Server剔除任务。
eureka.instance.lease-renewal-interval-in-seconds=30//服务续约任务的调用间隔时间
eureka.instance.lease-expiration-duration-in-seconds=90//定义服务失效的时间
4.2 服务消费者
获取服务:服务消费者,发送一个REST请求个服务注册中心。来获取上面注册的服务清单,为了性能考虑,Eureka Server会维护一份只读的服务清单来返回给客户端,同事该缓存清单每隔30秒回更新一次
eureka.client.fetch-registry=true//默认是true
eureka.client.registry-fetch-interval-seconds=30//默认30,更新服务清单时间
服务调用: Ribbon默认使用轮询方式调用,从而实现客户端的负载均衡
对于访问实例的选择 : Region和Zone的概念,一个Regin中可以包含多个Zone,每个服务客户端需要被注册到一个Zone中,每 个客户端对应一个Region和一个Zone。服务嗲用时,有限访问同一个Zone中的服务提供方,若访问不到,就访问其他的Zone。
服务下线: 当服务实例进行正常的关闭操作时,触发一个服务下线的Rest请求给Eureka Server。服务中心会将该服务下线,并把下线事件传播出去。
4.3 服务注册中心
失效剔除: 服务不能正常运行时,会会剔除。默认每隔60秒将当前清单超时默认90秒的服务剔除。
自我保护:对于15分钟内心跳低于百分之85的服务,Erureka Server会将这些服务保护起来,让这些服务不过期,如果这期间服务确实挂了,那么客户端很可能拿到已经不存在的服务实例。
euraka.server.enable-self-preservation = false;参数来关闭保护机制。以确保注册中心可以将不可用的实例正确剔除
5 REST API
Eureka注册服务时,调用服务使用的都是Restful api,接下来我们来简单说一下Restful
定义:REST是一种组织Web服务的架构,其只在架构方面提出了一系列约束,他不是我们日常用的某个框架和技术,这只是一种编程的规范和要求。
Rest的约束:
1、使用客户/服务器模型。客户和服务器之间通过一个统一的接口来互相通讯。
2、层次化的系统。在一个REST系统中,客户端并不会固定地与一个服务器打交道。
3、无状态。在一个REST系统中,服务端并不会保存有关客户的任何状态。也就是说,客户端自身负责用户状态的维持,并在每次发送请求时都需要提供足够的信息。
4、可缓存。REST系统需要能够恰当地缓存请求,以尽量减少服务端和客户端之间的信息传输,以提高性能。
5、统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。
所有约束当中,最重要的就是统一接口约束,针对该约束还有四个子约束。
统一接口的子约束
1、每个资源都拥有一个资源标识。每个资源的资源标识可以用来唯一地标明该资源。
2、消息的自描述性。在REST系统中所传递的消息需要能够提供自身如何被处理的足够信息。例如该消息所使用的MIME类型,是否可以被缓存等。
3、资源的自描述性。一个REST系统所返回的资源需要能够描述自身,并提供足够的用于操作该资源的信息,如如何对资源进行添加,删除以及修改等操作。也就是说,一个典型的REST服务不需要额外的文档对如何操作资源进行说明。
4、HATEOAS。即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息,如到底是向哪个URL发送请求等。也就是说,一个典型的REST服务不需要额外的文档标示通过哪些URL访问特定类型的资源,而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作。一个REST服务的客户端也不需要知道任何有关哪里有什么样的资源这种信息。