参考:http://blog.csdn.net/a60782885/article/details/69267934
服务发现:(Eureka)
前面提到,Eureka分为服务端和客户端,服务端是服务注册中心,而客户端是提供服务的。
创建服务注册中心(服务端)
首先在pom文件中加入以下依赖:
-
<parent>
-
<span style="white-space:pre"> </span><groupId>org.springframework.boot</groupId>
-
<span style="white-space:pre"> </span><artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.8.RELEASE</version>
-
<relativePath />
-
</parent>
-
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-test</artifactId>
-
<scope>test</scope>
-
</dependency>
-
<span style="white-space:pre"> </span><dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-eureka-server</artifactId>
-
</dependency>
-
</dependencies>
-
-
<dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-dependencies</artifactId>
-
<version>Brixton.SR7</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
在Application文件中增加@EnableEurekaServer注解,表明这是服务端
-
@EnableEurekaServer
-
@SpringBootApplication
-
public class SpringcloudEurekaServerApplication {
-
-
public static void main(String[] args) {
-
SpringApplication.run(SpringcloudEurekaServerApplication.class, args);
-
}
-
}
在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为。在application.properties或yml中禁止客户端注册行为,否则会报错。
-
server.port=1111
-
-
eureka.client.register-with-eureka=false
-
eureka.client.fetch-registry=false
-
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
服务端就写好了,可以访问localhost:1111进行访问。可以看到,这时候是没有服务的。
接下来创建客户端
创建客户端,并在注册中心中注册自己。
还是首先添加依赖:这里和上面不同的地方只有对Eureka的依赖。
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.8.RELEASE</version>
-
<relativePath />
-
</parent>
-
-
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-eureka</artifactId>
-
</dependency>
-
<span style="white-space:pre"> </span><dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-test</artifactId>
-
<scope>test</scope>
-
</dependency>
-
</dependencies>
-
-
<dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-dependencies</artifactId>
-
<version>Brixton.SR7</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
在Application文件中加入@EnableDiscoveryClient注解,表明这是一个客户端。
-
@EnableDiscoveryClient
-
@SpringBootApplication
-
public class SpringcloudEurekaServiceApplication {
-
-
public static void main(String[] args) {
-
SpringApplication.run(SpringcloudEurekaServiceApplication.class, args);
-
}
-
}
然后我们需要向注册中心中注册自己,只需要在application.properties或yml文件中增加配置即可:
-
spring.application.name=compute-service
-
server.port=2222
-
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
defaultZone指定了服务注册中心的位置。
注册了之后我们就可以在客户端编写客户端提供的服务了。比如这里提供了add加法服务。
-
@RestController
-
public class ComputeController {
-
-
private final Logger logger = Logger.getLogger(getClass());
-
-
@Autowired
-
private DiscoveryClient client;
-
-
@RequestMapping(value = "/add" ,method = RequestMethod.GET)
-
public Integer add(@RequestParam Integer a, @RequestParam Integer b) {
-
ServiceInstance instance = client.getLocalServiceInstance();
-
Integer r = a + b;
-
logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r);
-
return r;
-
}
-
}
在提供的服务中,只需要使用DiscoveryClient就可以得到关于调用者的信息。
客户端也完成了,这个时候可以再次访问localhost:1111。
可以发现,客户端已经在服务端上注册了。
我们看一下架构图:
很显然,工作流程是这样的:Service Consumer -> Proxy Server -> Client(Load Balancer) -> Servie Discovery -> Target Service
我们编写好了上图的红色的部分,接下来应该对服务进行负载均衡了,也就是对客户端的负载均衡,我们使用Ribbon来完成这件事。
首先我们启动服务端和两个客户端,客户端分别开在2222和2223端口。即:
开启了两个服务,接下来我们使用Ribbon进行负载均衡。
新建一个项目。
在pom文件中添加如下依赖:
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.3.5.RELEASE</version>
-
<relativePath/>
-
</parent>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-ribbon</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-eureka</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-web</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-test</artifactId>
-
<scope>test</scope>
-
</dependency>
-
</dependencies>
-
<dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-dependencies</artifactId>
-
<version>Brixton.RELEASE</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
使用@EnableDiscoveryClient注解在将其注册在注册中心。并且提供一个返回RestTemplate的bean实例,使用@LoadBalanced注解对这个实例进行负载均衡这样,使用这个实例去获取服务的时候,就会帮我们进行负载均衡了。
-
@SpringBootApplication
-
@EnableDiscoveryClient
-
public class RibbonApplication {
-
-
@Bean
-
@LoadBalanced
-
RestTemplate restTemplate() {
-
return new RestTemplate();
-
}
-
-
public static void main(String[] args) {
-
SpringApplication.run(RibbonApplication.class, args);
-
}
-
}
在application.properties中注册:
-
spring.application.name=ribbon-consumer
-
server.port=3333
-
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
创建了bean实例,我们就可以通过@Autowired来获取这个RestTemplate了,并且使用这个实例去调用服务。
-
@RestController
-
public class ConsumerController {
-
-
@Autowired
-
RestTemplate restTemplate;
-
-
@RequestMapping(value = "/add", method = RequestMethod.GET)
-
public String add() {
-
return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
-
}
-
}
启动这个项目,并且访问多次localhost:3333/add可以发现确实进行了负载均衡。
在微服务架构中,服务被拆分成一个一个的单元,各个单元之间相互依赖调用。如果某个单元由于网络原因或者别的单元故障奔溃,那么会直接导致调用方对外的服务也发生延迟。若此时调用方的请求不断增加,最后就会出现因等待出现故障的依赖方响应而形成任务积压,最终导致自身服务的瘫痪,这样的结构相对于传统的web开发模式来说更加可怕,为了解决这个问题,我们可以采用断路器。
在分布式架构中,当某个服务单元发生故障,使用断路器返回一个错误的相应,而不是进行长时间的等待,这样就不会使得线程被长时间占用,导致服务器的瘫痪。
Spring Cloud提供了Hystrix这么一个组件来实现断路器的功能。
首先启动Eureka服务端和客户端。
对Ribbon的项目进行以下修改:
首先在pom文件中增加以下依赖:
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-hystrix</artifactId>
-
</dependency>
在application中增加@EnableCircuitBreaker注解,表明使用断路器。
-
@SpringBootApplication
-
@EnableDiscoveryClient
-
@EnableCircuitBreaker
-
public class RibbonApplication {
-
-
@Bean
-
@LoadBalanced
-
RestTemplate restTemplate() {
-
return new RestTemplate();
-
}
-
-
public static void main(String[] args) {
-
SpringApplication.run(RibbonApplication.class, args);
-
}
-
}
Hystrix使用消息队列的方式,如果连接的服务崩溃,则异步回调某个方法进行处理。
新增一个Service类,在这个类中定义好如何访问,以及失败之后的返回等。
-
@Service
-
public class ComputeService {
-
-
@Autowired
-
RestTemplate restTemplate;
-
-
@HystrixCommand(fallbackMethod = "addServiceFallback")
-
public String addService() {
-
return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
-
}
-
-
public String addServiceFallback() {
-
return "error";
-
}
-
}
这里重点是@HystrixCommand注解:表明该方法为hystrix包裹,可以对依赖服务进行隔离、降级、快速失败、快速重试等等hystrix相关功能
列举几个属性:
1. fallbackMethod 降级方法
2. commandProperties 普通配置属性,可以配置HystrixCommand对应属性,例如采用线程池还是信号量隔离、熔断器熔断规则等等
3. ignoreExceptions 忽略的异常,默认HystrixBadRequestException不计入失败
4. groupKey() 组名称,默认使用类名称
5. commandKey 命令名称,默认使用方法名
最后修改一下Controller即可:
-
@RestController
-
public class ConsumerController {
-
-
@Autowired
-
private ComputeService computeService;
-
-
@RequestMapping(value = "/add", method = RequestMethod.GET)
-
public String add() {
-
return computeService.addService();
-
}
-
}
对于断路器,针对生产环境,,Netflix还给我们准备了一个非常好用的运维工具, 那就是Hystrix Dashboard和Turbine.可以帮助我们更好的观察。
当客户端关闭时访问:
客户端启动时访问:
成功!