《Spring Boot实战》学习(五):Spring Cloud 搭建

文章目录

        • 微服务
        • 配置服务
        • 服务发现
        • 路由网关
        • 负载均衡
        • 断路器
        • 示例搭建
          • 父模块搭建
          • 服务发现
          • 配置服务
          • Person服务
          • some 服务
          • UI服务
          • 断路器的监控

微服务

将传统的单块式架构按照定义好的边界切割成独立的组件。Spring cloud为分布式的微服务架构提供了解决方案。

配置服务

Spring Cloud提供了Config Server。可以在git或者文件系统中集中的放置配置文件。

Spring Cloud提供了注解@EnableConfigServer来启用配置服务。

服务发现

Spring Cloud使用Eureka作为服务的注册中心。

使用注解的方式提供了Eureka服务端(@EnableEurekaServer)和客户端(@EnableEurekaClient)

路由网关

使用网关地址将请求代理到不同的服务当中。

Spring Cloud使用Zuul来实现路由网关,支持自动路由映射到在Eureka Server上注册的服务,Spring Cloud提供了注解@EnableZuulProxy来启用路由代理。

负载均衡

Spring CLoud提供了Ribbon和Feign作为客户端的负载均衡。

断路器

断路器主要是为了解决当某个方法调用失败的时候,调用后备方法来替代失败的方法,以达到容错,阻止级联错误等功能。

Spring Cloud使用@EnableCircuitBreaker来启用断路器支持,使用@HystrixCommand的fallbackMethod来指定后备方法。

示例搭建

示例由6个微服务组成。

  • config:配置服务器,给别的服务提供配置
  • discovery:Eureka Server为微服务提供注册
  • person:提供person的REST服务
  • some:为UI模块返回一段字符串
  • UI:作为应用网关,提供外部访问的唯一入口。使用Feign消费person服务,Ribbon消费some服务,且都提供断路器功能
  • monitor:监控UI模块中的断路器
父模块搭建

先建一个父模块,把一些公用的依赖引入父模块

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<groupId>com.xhfgroupId>
	<artifactId>SpringCloudSampleartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<packaging>pompackaging>
	<name>SpringCloudSample-parentname>
	<parent>
		<groupId>org.springframework.cloudgroupId>
		<artifactId>spring-cloud-starter-parentartifactId>
		<version>Angel.SR3version>
		<relativePath />
	parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-actuatorartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-testartifactId>
		dependency>
	dependencies>
	<modules>
		<module>configmodule>
		<module>discoverymodule>
		<module>personmodule>
		<module>somemodule>
		<module>uimodule>
		<module>monitormodule>
	modules>
project>

spring-cloud-starter-parent 是 spring cloud 基础的 jar 包依赖,必须要有的。

服务发现

添加依赖,spring-cloud-starter-eureka-server 作为 eureka 作为服务注册和发现的服务端的依赖添加。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>com.xhfgroupId>
		<artifactId>SpringCloudSampleartifactId>
		<version>0.0.1-SNAPSHOTversion>
	parent>
	<artifactId>discoveryartifactId>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starterartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-eureka-serverartifactId>
		dependency>
	dependencies>
project>

启动类,使用@EnableEurekaServer开启 eurekaServer 的支持。

package com.xhf.discovery;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryApplication {
	public static void main(String[] args) {
		SpringApplication.run(DiscoveryApplication.class, args);
	}	
}

配置文件 application.yml,详情见注解

server:
   port: 8761  #服务端口

eureka:
   instance:
      hostname: localhost  #eureka实例的hostname
   client:
      register-with-eureka: false  #这个服务就不用注册到eureka上了,本身就是注册中心,
      fetch-registry: false #不从注册中心去拉取注册的服务,不需要使用别的服务了
配置服务

依赖引入, spring-cloud-config-server 作为配置服务的依赖,spring-cloud-starter-eureka 作为使用 eureka 服务注册中心的依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>com.xhfgroupId>
		<artifactId>SpringCloudSampleartifactId>
		<version>0.0.1-SNAPSHOTversion>
	parent>
	<artifactId>configartifactId>
	<name>configname>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starterartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-config-serverartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-eurekaartifactId>
		dependency>
	dependencies>
project>

启动类,使用 @EnableConfigServer 开启配置服务器的支持, @EnableEurekaClient 开启作为 eureka server 客户端的支持,这个服务会注册到服务注册中心

package com.xhf.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigApplication {
	public static void main(String[] args) {
		SpringApplication.run(ConfigApplication.class, args);
	}	
}

配置文件有两个,bootstrap.yml 会先于 application.yml加载。通常由于application.yml可能用到配置中心的一些配置,所以会把配置中心的一些配置放到bootstrap.yml当中。方便这些信息先行加载。

application.yml

spring:
   cloud:
      config:
         server:
            native:
               search-locations: classpath:/config  #表示配置中心在本地的config目录
               
server:
   port: 8888  #服务的端口,掉用这个服务提供的rest服务时要用到

bootstrap.yml

spring:
   application:
      name: config  #在eureka中注册的服务名config
   profiles:
      active: native  #配置服务器使用本地存储 
      
eureka:
   instance:
      non-secure-port: ${server.port:8888}  #注册到eureka的端口,非ssl端口,默认为8888
      metadata-map:
         instanceId: ${spring.application.name}:${random.value}  #注册到eureka中的实例ID
   client:
      service-url:
         defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/  # eureka server的地址,找到这个服务,把config服务注册上去   
   
Person服务

依赖引入,spring-cloud-config-client 是作为使用配置服务中心的客户端所需依赖。

<dependencies>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starterartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-config-clientartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-eurekaartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-jpaartifactId>
    dependency>
    <dependency>
        <groupId>org.hsqldbgroupId>
        <artifactId>hsqldbartifactId>
    dependency>
    <dependency>
        <groupId>postgresqlgroupId>
        <artifactId>postgresqlartifactId>
        <version>9.1-901-1.jdbc4version>
    dependency>
dependencies>

启动类,就是开启了eureka client的支持,将服务注册到服务注册中心去。

package com.xhf.person;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class PersonApplication {
	public static void main(String[] args) {
		SpringApplication.run(PersonApplication.class, args);
	}	
}

Rest服务, PersonRepository 是作为数据操作的接口,和spring cloud其实没有太大关系,不详解。

添加一个 /save 的 rest 服务。

package com.xhf.person.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.xhf.person.dao.PersonRepository;
import com.xhf.person.domain.Person;

@RestController
public class PersonController {

	@Autowired
	PersonRepository personRepository;
	
	@RequestMapping(value="/save", method=RequestMethod.POST)
	public List<Person> savePerson(@RequestBody String personName) {	
		Person p = new Person(personName);
		personRepository.save(p);
		List<Person> people = personRepository.findAll(new PageRequest(0,10)).getContent();
		return people;
	}	
}

配置文件有两个

application.yml

spring:
   jpa:
      hibernate:
         ddl-auto: update
           
server:
   port: 8082 #服务端口8082

bootstrap.yml

spring:
   application:
      name: person  #在eureka中注册的服务名person
   cloud:
      config:
         enabled: true  #开启使用配置服务中心
         discovery:
            enabled: true
            service-id: CONFIG  #配置服务中心的id,即config注册到eureka上的服务名称   
      
eureka:
   instance:
      non-secure-port: ${server.port:8082}
   client:
      service-url:
         defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/  #
   
some 服务
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>com.xhfgroupId>
		<artifactId>SpringCloudSampleartifactId>
		<version>0.0.1-SNAPSHOTversion>
	parent>
	<artifactId>someartifactId>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starterartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-config-clientartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-eurekaartifactId>
		dependency>
	dependencies>
project>

启动文件,从配置服务中心获取了一个配置值。然后提供了一个rest接口,或者这个值。

package com.xhf.some;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class SomeApplication {
	
	@Value("${my.message}") //从配置服务中心获取的值
	private String message;
	
	@RequestMapping(value="/getsome")
	public String getSome() {
		return message;
	}
	
	public static void main(String[] args) {
		SpringApplication.run(SomeApplication.class, args);
	}
	
}

配置文件:

application.yml

server:
   port: 8083

bootstrap.yml,和前文的配置基本一直,不再赘述

spring:
   application:
      name: some  #在eureka中注册的服务名person
   cloud:
      config:
         enabled: true
         discovery:
            enabled: true
            service-id: CONFIG   
      
eureka:
   instance:
      non-secure-port: ${server.port:8083}  
   client:
      service-url:
         defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/  
UI服务

UI服务就是提供给用户使用的接口,包括UI交互界面和数据访问的接口。

依赖引入,这里加了一些angular的依赖,不详解,只是为了提供前端界面而已。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>com.xhfgroupId>
		<artifactId>SpringCloudSampleartifactId>
		<version>0.0.1-SNAPSHOTversion>
	parent>
	<artifactId>uiartifactId>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starterartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-hystrixartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-zuulartifactId>
		dependency>		
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-config-clientartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-eurekaartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-feignartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-ribbonartifactId>
		dependency>
		
        <dependency>
            <groupId>org.webjarsgroupId>
            <artifactId>bootstrapartifactId>
        dependency>
        <dependency>
            <groupId>org.webjarsgroupId>
            <artifactId>angularjsartifactId>
            <version>1.3.15version>
        dependency>
        <dependency>
            <groupId>org.webjarsgroupId>
            <artifactId>angular-ui-routerartifactId>
            <version>0.2.13version>
        dependency>
        <dependency>
            <groupId>org.webjarsgroupId>
            <artifactId>jqueryartifactId>
        dependency>
	dependencies>
project>

启动类,

​ @EnableFeignClients开启Feign的支持,这个是用来做rest服务调用的负载均衡的。

​ @EnableCircuitBreaker开启断路器的支持,当远程调用失败的时候可以调用断路器指定的方法。

​ @EnableZuulProxy开启网关代理的支持,作为web服务和客户端之间的一个路由的角色存在。

package com.xhf.ui;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@EnableZuulProxy
public class UiApplication {

	public static void main(String[] args) {
		SpringApplication.run(UiApplication.class, args);
	}
	
}

接下来是对Person服务和Some服务的调用实现。

使用Feign调用服务接口,需要先定一个接口,使用@FeignClient(“person”)进行注解,能够将这个接口映射到eureka中对应的服务。

package com.xhf.ui.service;

import java.util.List;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.xhf.ui.domain.Person;

@FeignClient("person")
public interface PersonService {
	@RequestMapping(
        method=RequestMethod.POST, 
        value="/save", 
        produces=MediaType.APPLICATION_JSON_VALUE, 									 
        consumes=MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	List<Person> save(@RequestBody String name);
}

然后就是服务的调用了, 这里通过@HystrixCommand加入了熔断机制,即服务调用失败后调用指定的处理方法

package com.xhf.ui.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.xhf.ui.domain.Person;

@Service
public class PersonHystrixService {

	@Autowired
	PersonService personService;
	
	@HystrixCommand(fallbackMethod="fallbackSave")
	public List<Person> save(String name) {
		return personService.save(name);
	}
	
	public List<Person> fallbackSave(String name) {
		List<Person> list = new ArrayList<Person>();
		Person p = new Person("Person service 故障");
		list.add(p);
		return list;
	}
}

使用ribbon进行服务的调用,只要注入RestTemplate即可,其余配置spring boot已经做好了。

package com.xhf.ui.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class SomeHystrixService {
	
	@Autowired
	RestTemplate restTemplate;
	
	@HystrixCommand(fallbackMethod="fallbackSome")
	public String getSome() {
		return restTemplate.getForObject("http://some/getsome", String.class);
	}
	
	public String fallbackSome() {
		return "some service 模块故障";
	}	
}

UI服务提供给客户端访问的Rest请求如下,就是一个正常的Rest请求。

package com.xhf.ui.controller;

import java.util.List;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.xhf.ui.domain.Person;
import com.xhf.ui.service.PersonHystrixService;
import com.xhf.ui.service.SomeHystrixService;

@RestController
public class UiController {
	@Autowired
	private SomeHystrixService someHystrixService;
	
	@Autowired
	private PersonHystrixService personHystrixService;

	@RequestMapping("/dispatch")
	public List<Person> sendMessage(@RequestBody String personName) {
		return personHystrixService.save(personName);
	}
	
	@RequestMapping(value = "/getsome",produces={MediaType.TEXT_PLAIN_VALUE})
	public String getSome(){
		return someHystrixService.getSome();
	}
}

配置文件如下,配置项前文都解释过,不再赘述。

这里有一个提示,原书中的示例用的 80 端口。在linux环境下,如果使用1024以下的端口则需要root权限,因为我当前使用的不是root权限,所以权限不足而无法使用80端口,会报权限不够异常。这个问题作为记录备忘。

application.yml

server:
   port: 8090

bootstrap.yml

spring:
   application:
      name: ui 
      
eureka:
   instance:
      non-secure-port: ${server.port:8090}  
   client:
      service-url:
         defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/  
断路器的监控

依赖如下

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>com.xhfgroupId>
		<artifactId>SpringCloudSampleartifactId>
		<version>0.0.1-SNAPSHOTversion>
	parent>
	<artifactId>monitorartifactId>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starterartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
		dependency>
		<dependency>
			<groupId>org.springframework.cloudgroupId>
			<artifactId>spring-cloud-starter-turbineartifactId>
		dependency>
	dependencies>
project>

启动类,开启监控支持

package com.xhf.monitor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@SpringBootApplication
@EnableEurekaClient
@EnableHystrixDashboard
@EnableTurbine
public class MonitorApplication {
	
	public static void main(String[] args) {
		SpringApplication.run(MonitorApplication.class, args);
	}
	
}

配置文件

application.yml

server:
   port: 8989

bootstrap.yml

spring:
   application:
      name: monitor  #在eureka中注册的服务名person
      
eureka:
   instance:
      non-secure-port: ${server.port:8989}  #非ssl端口,默认为8888
   client:
      service-url:
         defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/  # eureka客户端设置的eureka server的地址   
   

你可能感兴趣的:(Spring,学习)