从上一节我们看到,我们存在硬编码的问题,虽然我们可以通过把url配置到配置文件中,再从代码中读取配置的方式来解决。但是,每次我们的地址换的时候,配置文件都需要修改,很不方便。所以,我们可以通过服务的注册、发现机制来完成。
微服务注册与发现
最基本的原理图如下:
可以看出:
- 服务提供者将服务注册到注册中心。
- 服务消费者通过注册中心查找服务。
- 查找到服务后进行调用(这里就是无需硬编码url的解决方案)。
- 服务消费者与服务注册中心保存心跳连接,一旦服务提供者的地址发生变更,注册中心会通知服务消费者。
注册中心Eureka
SpringCloud提供了多种注册中心的实现,例如:Eureka、Zookeeper,但是SpringCloud推荐Eureka。
Eureka是Netfix开源的服务发现组件,本身就是一个基于REST的服务。它包含Server端和Client端。Server就是注册中心,而Client就是指服务被注册成client。它的原理图如图:
从上图可以看此,Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表将会存储所有可用服务节点的信息,服务节点的信息可以再界面上直观的看到。
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端内置一个使用轮询(round-robin)负载均衡算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server再多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中巴这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然能够利用缓存中的消息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
编写Eureka Server
第一步,新建一个eureka server的模块,然后编辑pom.xml文件,其中最重要的是加入Eureka服务和Spring Cloud的依赖,如图:
然后,我们我们只需要再运行文件中加入@EnableEurekaServer,申明这是一个Eureka服务。
package com.example.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer //申明这是一个Eureka服务
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
然后我们编辑application.yml文件:
server:
port: 6868 #服务端口
eureka:
client:
registerWithEureka: false #是否将自己注册到Eureka服务中,本身就是所有无需注册
fetchRegistry: false #是否从Eureka中获取注册信息
然后启动程序进行测试:
就可以再控制台中查看信息。
将商品微服务注册到Eureka中
有了注册中心,我们不需要向以前那样去指定具体的地址了。所以,我们需要将商品微服务注册到Eureka服务中。所以,我们需要修改商品微服务的pom.xml文件,引入SpringCloud依赖和Eureka服务的依赖。
然后我们需要修改application.yml文件
server:
port: 8081 #服务端口
spring:
application:
name: xushu-microservice-item #指定服务名
eureka:
client:
registerWithEureka: true #是否将自己注册到Eureka服务中,默认为true
fetchRegistry: true #是否从Eureka中获取注册信息,默认为true
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址
defaultZone: http://127.0.0.1:6868/eureka/
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
然后我们需要再启动类增加@EnableDiscoveryClient注解,至此全部完毕。然后进行测试,可以看到我们的微服务已经注册到Eureka Server中了。
订单系统从Eureka中发现服务
之前我们在订单系统中对地址进行硬编码,现在由于已经将商品的微服务注册到Eureka中,所以只需要从Eureka中发现服务即可。那么,还是需要添加依赖,和商品微服务一样,接着便修改配置文件
server:
port: 8082 #服务端口
spring:
application:
name: xushu-microservice-order #指定服务名
eureka:
client:
registerWithEureka: false #是否将自己注册到Eureka服务中,默认为true
fetchRegistry: true #是否从Eureka中获取注册信息,默认为true
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址
defaultZone: http://127.0.0.1:6868/eureka/
修改ItemService的逻辑:
package com.xushu.microservice.order.service;
import com.xushu.microservice.order.pojo.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Service
public class ItemService {
//Spring框架对RESTful方式的http请求做了封装,来简化操作
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
/**
*调用商品的微服务提供的接口进行查询数据
* @param id
* @return
*/
public Item queryItemById(Long id) {
String serviceId = "xushu-microservice-item";
List<ServiceInstance> instances = this.discoveryClient.getInstances(serviceId);
if(instances.isEmpty()){
return null;
}
// 为了演示,在这里只获取一个实例
ServiceInstance serviceInstance = instances.get(0);
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
return this.restTemplate.getForObject("http://" + url + "/item/" + id, Item.class);
}
}
现在,我们只需要指定服务名称,就能够得到它的实例了。测试如下:
为Eureka添加用户认证
在前面的示例中,我们无需登录即可访问Eureka服务,很不安全。所以我们需要为Eureka添加用户认证。首先我们需要为Eureka Server添加依赖:
<!-- 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后修改application.xml文件:
security:
basic:
enable: true #开启基于HTTP basic的认证
user: #配置用户的账号信息
name: xushu
password: xushu
但是此时服务注册者会出现错误,所以当有服务注册到注册中心时,需要设置如下信息(eureka地址选择你的服务器地址):http://USER:PASSWORD@127.0.0.1:6868/eureka/
小插曲
这里有个小插曲,因为最新的Spring security 会默认开启防csrf攻击,我们需要关闭它,所以我们需要在Eureka Server上写一个配置类,然后手动关闭它:
package com.example.eureka.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
配置如图:
然后就可以了。
Eureka的自我保护模式
如图,Eureka进入了自我保护模式。在默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认为90秒)。但是当忘了分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能非常危险。因为微服务本身是健康的,只是网络故障而已,不应该注销这个服务。
Eureka通过“自我保护模式”来解决这个问题。当Eureka Server节点在短时间内丢失过多客户端时,那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点就会自动退出自我保护模式。
一般进入自我保护模式,无需处理。所以,不建议禁用自我保护模式。
Eureka的高可用
前面的实例中,Eureka服务是一个单点服务,在实际的生产环境中,为了实现高可用,我们需要Eureka集群。
简历Eureka机器很简单,只需要启动多个Eureka服务并且让这些服务彼此注册即可。在这里,我们再新建一个anotereureka模块,然后application.yml文件修改如下(eureka修改也差不多):
测试就会发现有两个eureka服务,多个服务同理。这时,如果一般的功能微服务需要注册到集群,要修改 defaultZone 为多个地址即可。
网友评论