SpringBoot35-分布式系统开发-spring cloud1

时间:2021-03-08 16:37:43

一,微服务、原生云应用

       微服务(Microservice)是近两年来非常火的概念,它的含义是:使用定义好边界的小的独立组件来做好一件事情。微服务是相对于传统单块式架构而言的。

       单块式架构是一份代码,部署和伸缩都是基于单个单元进行的。它的优点是易于部署,但是面临可用性,可伸缩性差,集中发布的生命周期以及违反单一功能原则。微服务的出现解决了这个问题,它以单个独立的服务来做一个功能,且要做好这个功能。但使用微服务不可避免地将功能按照边界拆分为单个服务,体现出分布式的特征,这时每个微服务直接的通信将是我们要解决的问题。

       spring cloud的出现为我们解决分布式开发常用到的问题给出了完整的解决方案。spring cloud基于spring boot,为我们提供了配置管理,服务发现,断路器,代理服务等我们在做分布式开发时常用问题的解决方案。

      基于spring cloud开发的程序特别适合在Docker或者其他专业Pass(平台即服务,如Cloud Foundry)部署,所以又称作原生云应用(Cloud Native Application)。


二,Spring Cloud快速入门

1,配置服务

      Spring Cloud提供了Config Server它有在分布式系统开发中外部配置的功能。通过Config Server,我们可以集中存储所有应用的配置文件。

      Config Server支持在git或者svn或者在文件系统中放置配置文件。可以使用以下格式来区分不同应用的不同配置文件:

/{application}/{profile}[/{label}]

/{application}-{profile}.yml

/{label}/{application}-{profile}.yml

/{application}-{profile}.properties

/{label}/{application}-{profile}.properties    


    spring cloud提供了注解@EnableConfigServer来启用配置服务。


2,服务发现

        Spring Cloud通过Netflix OSS的Eureka来实现服务发现,服务发现的主要目的是为了让每个服务之间可以互相通信。Eureka Server为微服务注册中心。

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


3,路由网关

      路由网关的主要目的是为了让所有的微服务对外只有一个接口,我们只需访问一个网关地址,即可由网关将我们的请求代理到不同的服务中。

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



4,负载均衡

      spring cloud提供了Ribbon和Feign作为客户端的负载均衡。在Spring Cloud下使用Ribbon直接注入一个RestTemplate对象即可,此RestTemlate已做好负载均衡的配置;而使用Feign只需定义个注解,有@FeignClient注解的接口,然后使用@RequestMapping注解在方法映射远程的REST服务,此方法也是做好负载均衡配置的。


5,断路器

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

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

    spring cloud还给我们提供了一个控制台来监听断路器的运行情况。通过@EnableHystrixDashboard注解开启。


三,实战

       下面是实战部分,主要由6个微服务组成:

config:配置服务,下面的是person-service和some-service提供外部配置。

discovery:Eureka Server为微服务提供注册。

person:为UI模块提供保存person的REST服务。

some:为UI模块返回一段字符串。

UI:作为应用网关,提供外部访问的唯一入口。使用Feign消费person服务,Ribbon消费some服务,且都提供断路器功能。

monitor:监控UI模块中的断路器


1,项目构建

     创建一个mava项目,项目名为springcloud3,打包方式是pom发生,使用modules标签来实现模块化,pom.xml的配置如下:

<?xml version="1.0" encoding="UTF-8"?><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.0</modelVersion>

<groupId>com.jack</groupId>
<artifactId>springcloud3</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<!--模块化-->
<modules>
<module>config</module>
<module>discovery</module>
<module>ui</module>
<module>person</module>
<module>some</module>
<module>monitor</module>
</modules>


<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
</dependency>
</dependencies>


</project>


2,服务发现-discovery(Eureka Server)

1)创建spring boot项目,项目名为discovery,模块需要添加如下依赖:

<?xml version="1.0" encoding="UTF-8"?><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.0</modelVersion>	<groupId>com.jack</groupId>	<artifactId>discovery</artifactId>	<version>0.0.1-SNAPSHOT</version>	<packaging>jar</packaging>	<name>discovery</name>	<description>Demo project for Spring Boot</description>	<parent>		<groupId>org.springframework.boot</groupId>		<artifactId>spring-boot-starter-parent</artifactId>		<version>1.5.8.RELEASE</version>		<relativePath/> <!-- lookup parent from repository -->	</parent>	<properties>		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>		<java.version>1.8</java.version>	</properties>	<dependencies>		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter</artifactId>			<version>1.2.4.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework.boot</groupId>			<artifactId>spring-boot-starter-web</artifactId>		</dependency>		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->		<dependency>			<groupId>org.springframework.boot</groupId>			<artifactId>spring-boot-starter-actuator</artifactId>			<version>1.5.8.RELEASE</version>		</dependency>		<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter-eureka-server</artifactId>			<version>1.3.5.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework.boot</groupId>			<artifactId>spring-boot-starter-test</artifactId>			<scope>test</scope>		</dependency>	</dependencies>	<build>		<plugins>			<plugin>				<groupId>org.springframework.boot</groupId>				<artifactId>spring-boot-maven-plugin</artifactId>			</plugin>		</plugins>	</build></project>

2)主要代码

package com.jack.discovery;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication//开启注册中心@EnableEurekaServerpublic class DiscoveryApplication {	public static void main(String[] args) {		SpringApplication.run(DiscoveryApplication.class, args);	}}

3)配置

application.yml配置如下:

server:  port: 8761  #\u5F53\u524DEureka Server\u670D\u52A1\u7684\u7AEF\u53E3\u4E3A8761eureka:  instance:    hostname: localhost  #\u5F53\u524DEureka Server\u7684hostname\u4E3Alocalhost  client:    register-with-eureka: false  #\u5F53\u524D\u670D\u52A1\u4E0D\u9700\u8981\u5230Eureka Server\u4E0A\u6CE8\u518C    fetch-registry: false  #\u662F\u5426\u9700\u8981\u4ECEEureka Server\u83B7\u53D6\u6CE8\u518C\u6D88\u606F

3,配置-config(config server)

创建一个spring boot项目,项目名为config

1)依赖

    依赖的jar包在pom.xml里面的配置如下:

<?xml version="1.0" encoding="UTF-8"?><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.0</modelVersion>	<groupId>com.jack</groupId>	<artifactId>config</artifactId>	<version>0.0.1-SNAPSHOT</version>	<packaging>jar</packaging>	<name>config</name>	<description>Demo project for Spring Boot</description>	<parent>		<groupId>org.springframework.boot</groupId>		<artifactId>spring-boot-starter-parent</artifactId>		<version>1.5.8.RELEASE</version>		<relativePath/> <!-- lookup parent from repository -->	</parent>	<properties>		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>		<java.version>1.8</java.version>	</properties>	<dependencies>		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter</artifactId>			<version>1.2.4.RELEASE</version>		</dependency>		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->		<dependency>			<groupId>org.springframework.boot</groupId>			<artifactId>spring-boot-starter-actuator</artifactId>			<version>1.5.8.RELEASE</version>		</dependency>		<!--eureka客户端的依赖-->		<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter-eureka</artifactId>			<version>1.3.5.RELEASE</version>		</dependency>		<!--配置服务的依赖-->		<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-server -->		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-config-server</artifactId>			<version>1.3.3.RELEASE</version>		</dependency>		<!--web依赖-->		<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>	<build>		<plugins>			<plugin>				<groupId>org.springframework.boot</groupId>				<artifactId>spring-boot-maven-plugin</artifactId>			</plugin>		</plugins>	</build></project>

2)主要代码如下:

package com.jack.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@EnableEurekaClient@EnableConfigServerpublic class ConfigApplication {	public static void main(String[] args) {		SpringApplication.run(ConfigApplication.class, args);	}}


3)配置

    bootstrap.yml配置如下:

spring:  application:    name: config  #在Erueka Server注册的服务名为config  profiles:    active: native #配置服务器使用本地配置(默认为git配置)eureka:  instance:    non-secure-port: ${server.port:8888} #非SSL端口,若环境变量中server.port有值,则使用环境变量的值,没有则使用8080端口    metadata-map:      instanceId: ${spring.application.name}:${random.value} #配置在Eureka Server的实例ID  client:    service-url:    #Eureka客户端设置Eureka Server的地址      defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/


application.yml配置如下:

spring:  cloud:    config:      server:        native:        #\u914D\u7F6E\u5176\u4ED6\u5E94\u7528\u6240\u9700\u7684\u914D\u7F6E\u6587\u4EF6\u7684\u4F4D\u7F6E\u4F4D\u4E8E\u7C7B\u8DEF\u5F84\u4E0B\u7684config\u76EE\u5F55\u4E0B          search-locations: classpath:/configserver:  port: 8888


4)创建一个目录存放配置文件

     在src/main/resources目录下创建一个config目录,其他应用的配置文件就存放在这里,实现统一配置的功能,如下:

SpringBoot35-分布式系统开发-spring cloud1


    配置文件的规则为:应用名+profile.yml


4,服务模块-person服务

 1)依赖

      本模块需要做数据库操作,需要添加spring-boot-starter-data-jpa依赖(在开发环境下使用hsqldb,在docker生产环节下使用PostgreSQL);本模块还需要config server的配置,pom.xml的配置如下:

<?xml version="1.0" encoding="UTF-8"?><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.0</modelVersion>    <groupId>com.jack</groupId>    <artifactId>person</artifactId>    <version>0.0.1-SNAPSHOT</version>    <packaging>jar</packaging>    <name>person</name>    <description>Demo project for Spring Boot</description>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>1.5.8.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter</artifactId>            <version>1.2.4.RELEASE</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-config -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-config</artifactId>            <version>1.3.3.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>            <version>1.3.5.RELEASE</version>        </dependency>        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-actuator</artifactId>            <version>1.5.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-data-jpa</artifactId>            <version>1.5.8.RELEASE</version>        </dependency>        <dependency>            <groupId>org.hsqldb</groupId>            <artifactId>hsqldb</artifactId>        </dependency>        <!-- https://mvnrepository.com/artifact/postgresql/postgresql -->        <dependency>            <groupId>postgresql</groupId>            <artifactId>postgresql</artifactId>            <version>9.4.1208-jdbc42-atlassian-hosted</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

2,关键代码

package com.jack.person;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClientpublic class PersonApplication {	public static void main(String[] args) {		SpringApplication.run(PersonApplication.class, args);	}}

package com.jack.person.entity;import javax.persistence.*;/** * create by jack 2017/10/3 *///@Entity注解指明这是一个和数据库表映射的实体类@Entitypublic class Person {    /**     * 主键id     * @Id注解指明这个属性映射为数据库的主键     * @GeneratedValue定义主键生成的方式,下面采用的是mysql的自增属性     */    @Id//主键    //@GeneratedValue(strategy= GenerationType.AUTO)使用内存数据库需要注释掉    private Integer id;    /**     * 姓名     */    private String name;    /**     * 年龄     */    private Integer age;    /**     * 地址     */    private String address;    public Person() {        super();    }    public Person(String name) {        this.name = name;    }    public Person(Integer id,String name, Integer age, String address) {        super();        this.id = id;        this.name = name;        this.age = age;        this.address = address;    }    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }}


package com.jack.person.dao;import com.jack.person.entity.Person;import org.springframework.data.jpa.repository.JpaRepository;/** * create by jack 2017/10/3 */public interface PersonRepository extends JpaRepository<Person,Integer> {}

package com.jack.person.controller;import com.jack.person.dao.PersonRepository;import com.jack.person.entity.Person;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 java.util.List;/** * create by jack 2017/11/11 */@RestControllerpublic class PersonController {    @Autowired    private PersonRepository personRepository;    @RequestMapping(value = "/save",method = RequestMethod.POST)    public List<Person> savePerson(@RequestBody String personName){        Person p = new Person(personName);        //需要设置id        p.setId(2);        personRepository.save(p);        List<Person> people = personRepository.findAll(new PageRequest(0,10)).getContent();        return people;    }}


3)配置

bootstrap.yml

spring:  application:    name: person  cloud:    config:      enabled: true      discovery:        enabled: true        service-id: configeureka:  instance:    non-secure-port: ${server.port:8082}  client:    service-url:     defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/


   指定config server服务,将会通过eureka server 发现config server,在开封环境下使用hsqldb(config server下的person.yml):

spring:  jpa:    database: HSQL

   在docker生产环境下使用PostgreSQL(config server下的person-docker.yml)

spring:  jpa:    database: POSTGRESQL  datasource:    platform: postgres    url: jdbc:postgresql://postgres:5432/postgres    username: postgres    password: postgres    driver-class-name: org.postgresql.Driver

application.yml配置如下:

server:  port: 8082spring:  jpa:    hibernate:      ddl-auto: update


5,服务模块-some服务

   创建一个some名的spring boot项目

1)依赖

<?xml version="1.0" encoding="UTF-8"?><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.0</modelVersion>	<groupId>com.jack</groupId>	<artifactId>some</artifactId>	<version>0.0.1-SNAPSHOT</version>	<packaging>jar</packaging>	<name>some</name>	<description>Demo project for Spring Boot</description>	<parent>		<groupId>org.springframework.boot</groupId>		<artifactId>spring-boot-starter-parent</artifactId>		<version>1.5.8.RELEASE</version>		<relativePath/> <!-- lookup parent from repository -->	</parent>	<properties>		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>		<java.version>1.8</java.version>	</properties>	<dependencies>		<!--<dependency>			<groupId>org.springframework.boot</groupId>			<artifactId>spring-boot-starter-web</artifactId>		</dependency>-->		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter</artifactId>			<version>1.2.4.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter-config</artifactId>			<version>1.3.3.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework.cloud</groupId>			<artifactId>spring-cloud-starter-eureka</artifactId>			<version>1.3.5.RELEASE</version>		</dependency>		<dependency>			<groupId>org.springframework.boot</groupId>			<artifactId>spring-boot-starter-test</artifactId>			<scope>test</scope>		</dependency>	</dependencies>	<build>		<plugins>			<plugin>				<groupId>org.springframework.boot</groupId>				<artifactId>spring-boot-maven-plugin</artifactId>			</plugin>		</plugins>	</build></project>

2)关键代码

package com.jack.some;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class SomeApplication {	public static void main(String[] args) {		SpringApplication.run(SomeApplication.class, args);	}}


package com.jack.some.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * create by jack 2017/11/11 */@RestControllerpublic class SomeController {    /**     * 下面是通过@Value注入的值来自于Config Server     */    @Value("${my.message}")    private String message;    @RequestMapping(value = "/getsome")    public String getsome() {        return message;    }}

    此处通过@Value注入的值来自于config server

    在开发环境(config server下的some.yml)

my:  message: Message from Development

   在docker生产环境下(config server下的some-docker.yml)

my:  message: Message from Production

3)配置

bootstrap.yml

spring:  application:    name: some  cloud:    config:      enabled: true      discovery:        enabled: true        service-id: configeureka:  instance:    non-secure-port: ${server.port:8083}  client:    service-url:      defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/

application.yml配置如下:

server:  port: 8083

代码地址: https://github.com/wj903829182/springcloud3


接下来的步骤,见下一篇博客