这里开始spring cloud微服务架构搭建,用的maven还是之前自己的本地安装的,repository仓库也是本地的。
在搭建项目框架之前先简单学习一下spring cloud。
Spring Cloud是一个基于Spring Boot实现的云应用开发工具,是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),具体如下spring cloud组件介绍。
1. Spring Cloud Config
服务配置中心,将所有的服务的配置文件放到本地仓库或者远程仓库,配置中心负责读取仓库的配置文件,其他服务向配置中心读取配置。Spring Cloud Config使得服务的配置统一管理,并可以在不人为重启服务的情况下进行配置文件的刷新。
2. Spring Cloud Netflix
它是通过包装了Netflix公司的微服务组件实现的,也是Spring Cloud核心组件,包括Eureka,Hystrix,Zuul,Archaius。
3. Eureka
服务注册和发现组件
4. Hystrix
熔断器组件。它通过控制服务的API接口的熔断来转移故障,防止微服务系统发生雪崩效应。另外Hystrix能够起到服务限流和服务降级的作用。使用Hystrix Dashboard组件监控单个服务的熔断状态,使用Hystrix Turbine组件可以监控多个服务的熔断器的状态。
5. Zuul
智能路由网关组件。能够起到智能路由和请求过滤的作用,内部服务 API 接口通过 Zuul 网关统一对外暴露,防止内部服务敏感信息对外暴露。也可以实现安全验证,权限控制。
6. Feign
声明式远程调度组件。
7. Ribbon
负载均衡组件
8. Archaius
配置管理API组件,一个基于Java的配置管理库,主要用于多配置的动态获取。
9. Spring Cloud Bus
消息总线组件,常和Spring Cloud Config配合使用,用于动态刷新服务的配置。
10. Spring Cloud Sleuth
服务链路追踪组件,封装了Dapper,Zipkin,Kibina等组件,可以实时监控服务链路调用状况。
11. Spring Cloud Data Flow
大数据操作组件,它是Spring XD的替代品,也是一个混合计算模型,可以通过命令行的方式操作数据流
12. Spring Cloud Consul
该组件是Spring Cloud对Consul的封装,和Eureka类似,它是一个服务注册和发现组件。
13. Spring Cloud Zookeeper
该组件是Spring Cloud对Zookeeper的封装,也是用于服务注册和发现
14. Spring Cloud Stream
数据流操作组件,可以封装 Redis,RabbitMQ,Kafka等组件,实现消息的接受和发送。
15. Spring Cloud CLI
该组件是对Spring Boot CLI的封装,可以让用户以命令行方式快速搭建和运行容器。
16. Spring Cloud Task
该组件基于Spring Tsak,提供任务调度和任务管理的功能。
Spring Cloud拥有很多的项目模块,包含微服务的方方面面,Dubbo是个十分优秀的服务治理和服务调用框架,但缺少了很多的功能模块,例如网关,链路追踪等。
开发风格上,Dubbo倾向于xml配置方式,而Spring Cloud基于Spring Boot,它采用基于注解和JavaBean配置方式的敏捷开发。
通信方式上Spring Cloud大多数基于HTTP Restful风格,服务与服务间完全耦合,因此服务无关乎语言和平台。Dubbo采用远程调用方式,对接口平台和编程语言有强依赖性。
Dubbo和Spring Cloud各有优缺点,Dubbo更易上手,也非常成熟和稳定,Spring Cloud服务框架严格遵守 Martin Fowler 提出的微服务规范。
微服务的概念源于2014年3月Martin Fowler所写的章“Microservices”http://martinfowler.com/articles/microservices.html
微服务(Microservices Architecture)是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
微服务架构的核心思想是,一个应用是由多个小的、相互独立的、微服务组成,这些服务运行在自己的进程中,开发和发布都没有依赖。不同服务通过一些轻量级交互机制来通信,例如 RPC、HTTP 等,服务可独立扩展伸缩,每个服务定义了明确的边界,不同的服务甚至可以采用不同的编程语言来实现,由独立的团队来维护。简单的来说,一个系统的不同模块转变成不同的服务!而且服务可以使用不同的技术加以实现!
那我们在微服务中应该怎样设计呢。以下是微服务的设计指南:
关于为服务的一些取舍:
如下我们使用 euraka 体系的spring cloud.
以下工作都在project->springcloud-erueka-project
打开idea创建project:
选择maven创建普通的maven管理项目:
填写工程的组命GroupId和项目名ArtifactId:
确认信息后点击finish即可:
选择自动导包:
创建好的工程:
检查idea的maven设置file---->settings---->maven:
1. 创建一个module 注册中心eureka-server(包括监控中心)
1.1)新建一个module sc-eureka-server 提供服务注册和发现
右键单击工程名选择Module创建模块:
选择spring boot风格创建spring cloud:
填写模块组名和模块名称:
创建注册中心(包含监控中心),选择cloud discovery的eureka server,这里也能看出spring boot的版本是2.1.1:
确认信息后完成:
创建好的模块目录以及有以下两个文件:
1.2)增加依赖(其实采用spring boot方式创建的spring cloud已经简化了很多工作,这里的依赖也已经完成了,只是我们要知道是什么依赖:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
pom.xml文件内容如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.1.RELEASE
com.kgc1803
sc-eureka-server
0.0.1-SNAPSHOT
sc-eureka-server
Demo project for Spring Boot
1.8
Greenwich.RC2
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
1.3)这里我的所有配置文件均采用application.yml格式(即将application.properties后缀更改成.yml),当然你可不用更改,根据自己的风格爱好就可以,并在注册中心模块配置eureka server。在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.yml
配置文件中增加如下信息:
register-with-eureka: false
fetch-registry: false
application.yml配置文件内容如下,只是要注意,application.yml风格的配置需要注意各个子级需要对应工整,不然后面启动服务是不会有效果的,要求更加严格:
#服务端口
server:
port: 8761
#eureka server 的配置
#register-with-eureka: false表示该eureka 节点不能注册服务
#fetch-registry: false 表示该eureka 节点不能发布(订阅)服务
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 这里默认的路径后面需要加上eureka/才会访问到。
1.4)在主服务启动类上增加eureka server 的注解标签( @EnableEurekaServer )
package com.kgc1803.sceurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class ScEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(ScEurekaServerApplication.class, args);
}
}
1.5)启动测试
访问服务
启动工程后,访问:http://localhost:8761/
可以看到下面的页面,其中还没有发现任何服务。
2. 发布一个服务
2.1)新建一个module sc-eureka-service-provider
创建方法跟上面一样,只是有一点不一样如下选项:
这里的取别如下图:
2.2)检查依赖并增加相应的依赖,spring cloud 发布服务多用 rest 风格,所以需要spring-boot web 包:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
pom.xml配置文件内容:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.1.RELEASE
com.kgc1803
sc-eureka-service-provider
0.0.1-SNAPSHOT
sc-eureka-service-provider
Demo project for Spring Boot
1.8
Greenwich.RC2
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
2.3)在ScEurekaServiceProviderApplication.java服务启动类同级创建service包并编写一个服务类;这里我们使用rest风格的控制器注解:@RestController
DemoService.java服务类:
package com.kgc1803.sceurekaserviceprovider.service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//rest 风格控制器注解
@RestController
public class DemoService {
@RequestMapping("/getInfo")
public String getDemoInfo(){
return "this is a provided service !";
}
}
2.4)在application.yml中完成eureka client 发布服务的配置(客户端(client)向server(服务器端)注册服务的配置)
添加配置
需要配置才能找到 Eureka 服务器。例:
完整配置
server:
port: 8081
#instance.appname 指明注册中的名字
#service-url.defultzone:配置注册中心地址
eureka:
instance:
appname: demo_service_provider
client:
service-url:
defaultZone: http://localhost:8761/eureka/
#应用名字
spring:
application:
name: eureka-provider
其中defaultZone
是一个魔术字符串后备值,为任何不表示首选项的客户端提供服务URL(即它是有用的默认值)。 通过spring.application.name
属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问
2.5)在主启动类中增加 发布服务的注解配置
开启服务注册,在应用主类中通过加上 @EnableEurekaClient,但只有Eureka 可用,你也可以使用@EnableDiscoveryClient。需要配置才能找到Eureka注册中心服务器;实验证明可以不用加。
package com.kgc1803.sceurekaserviceprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ScEurekaServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ScEurekaServiceProviderApplication.class, args);
}
}
访问服务
启动该工程后,再次访问启动工程后:http://localhost:8761/
可以如下图内容,我们定义的服务被成功注册了。
2.6)启动与测试:http://localhost:8081/getInfo
出现如下图所示表名取到了值:
3. 发现(订阅)一个服务
3.1)新建一个module sc-eureka-service-consume(流程跟上面发布服务模块一样)
建好的模块目录:
3.2)增加依赖
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
3.3)在 ScEurekaServiceConsumeApplication.java 启动类的同级目录编写一个控制器(主要用来调用已注册的服务,即上面第二个模块注册的服务);这里需要注入一个RestTemplate工具类对象才可以调用到,但是直接在控制器使用对象是没办法得到值的,为了方便其他控制器(同模块)可以调用这个工具对象,最好的办法就是直接在启动类里面写一个方法产生对象,启动的时候就可以注入了(这里调值与上面的方法不一样,也是唯一最大的区别处)。
//向spring里注入一个RestTemplate对象
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
完整的启动类示例:
package com.kgc1803.sceurekaserviceconsume;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
//在启动类注入@EnableEurekaClient注解
@SpringBootApplication
@EnableEurekaClient
public class ScEurekaServiceConsumeApplication {
public static void main(String[] args) {
SpringApplication.run(ScEurekaServiceConsumeApplication.class, args);
}
//向spring里注入一个RestTemplate对象
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
在编写的控制器中引入RestTemplate对象
@Autowired
private RestTemplate restTemplate;
完整的控制器示例:
package com.kgc1803.sceurekaserviceconsume.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class DemoController {
//映入工具对象RestTemplate
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/test")
public String getEurekaServiceInfo(){
/**
* exchange(url,type,paras,resultType)
* url:请求地址
* type:请求类型 get post
* paras:参数
* resultType:返回值类型
*/
//取sc-eureka-service-provider模块服务类getInfo()中的值,这里获取url地址和方法名
String url = "http://localhost:8081/getInfo";
HttpMethod type = HttpMethod.GET; //请求方式
RequestEntity paras = null;//请求参数,没有请求参数,所以给null值
//对象调用exchange()带四个参数的方法,并返回请求
ResponseEntity responseEntity = restTemplate.exchange(url,type,paras,String.class);
//返回值,注意,这里是返回String类型,所以getBody()就是字符串,如果方法返回是对象或者list集合,getBody()也就对应返回类型。
return responseEntity.getBody();
}
}
3.4)在application.yml中完成eureka client 发布服务的配置,方法同上一个模块:
server:
port: 8082
#客户端(client)向server(服务器端)注册服务的配置
#instance.appname 指明注册中的名字
#service-url.defultzone:配置注册中心地址
eureka:
instance:
appname: demo_service_consume
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka/
#应用名字
spring:
application:
name: eureka-consume
模块结构图:
3.5)启动测试服务类,刷新:http://localhost:8761/ 如下图,两个服务都发布成功。
输入地址:http://localhost:8082/test 测试从控制器根据对象工具取值是否成功:
取到值,测试成功!
拓展总结:
后端的注册中和服务类启动后,注册中心Eureka大概90秒向服务类做一次通信,而服务类大概30秒左右跟注册中心的Erueka回一次通信;即使后端通信断掉了,前端页面显示的依然是注册的服务存在,注册有效。另外,想要把注册中报红色的安全保护机制关掉,只需要在注册中心模块的配置文件中加一个配置即可,如下:
#安全保护机制(端口)关闭
instance:
secure-port-enabled: false
1.创建公共模块 sc-eureka-service-common(使用普通maven创建即可)
创建好的公共模块如下图,这个公共模块需要操作的是完成pojo实体类,但是这里因为和spring cloud并用,所以不用写service接口了,导入相应的jar文件,将公共模块打包发布到仓库:
2. 在pomxml文件增加 json 的依赖:
org.codehaus.jackson
jackson-mapper-asl
1.9.12
打一个jar文件包:
jar
3. 编写User.java实体类:
package com.kgc1803.demo.pojo;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.Serializable;
//注意的是pojo实体类一定要实现序列化接口
public class User implements Serializable {
@JsonProperty
private Integer id;
@JsonProperty
private String userCode;
@JsonProperty
private String userName;
@JsonProperty
private String password;
public User(){}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
4. 公共模块打成 jar 包发布到仓库:
5. 在sc-eureka-service-consume和sc-eureka-service-provider模块的pom.xml文件增加公共的 jar 包:
com.kgc1803
sc-eureka-service-common
1.0-SNAPSHOT
更新:对上面的服务类和控制器进行升级改造和拓展,升级是上面用的是无参数的服务类,在控制器用的是 restTemplate 工具对象调用 exchange(url,type,paras,resultType)方法实现的,这里有四个参数,换成以下 getForObject(url,resultType) 或者是 postForObject(url,参数对象,返回值类型)方法会更简洁(内部做了进一步封装),这里直接把三种类型(基础类型String有参和无参、集合List、对象)示例代码全部码上:
DemoService.java 服务类:
package com.kgc1803.sceurekaserviceprovider.service;
import com.kgc1803.demo.pojo.User;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//rest 风格控制器注解
@RestController
public class DemoService {
/**
* 基本数据类型无参数
* @return
*/
@RequestMapping("/getInfo")
public String getDemoInfo(){
return "this is a provided service !";
}
/**
* 基本数据类型有参数
* @param userCode
* @return
*/
@RequestMapping("/getInfo_02")
public String getDemoInfo_02(String userCode){
return userCode + " , this is a provided cloud 程序 !";
}
/**
* 带参数的List集合类型
* @param userCode
* @return
*/
@RequestMapping("/getlist")
public List getList(String userCode){
List
DemoController.java 控制器类:
package com.kgc1803.sceurekaserviceconsume.controller;
import com.kgc1803.demo.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* 这里测试,传值都是从后端传到前端
* 测试三种类型:基本数据类型的String、List集合、对象
*/
@RestController
public class DemoController {
//映入工具对象RestTemplate
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/test")
public String getEurekaServiceInfo(){
/**
* exchange(url,type,paras,resultType) 请求无参数的getDemoInfo()
* url:请求地址(远程服务地址)
* type:请求类型 get post
* paras:参数
* resultType:返回值类型 String.class
*/
//取sc-eureka-service-provider模块服务类getInfo()中的值,这里获取url地址和方法名
String url = "http://localhost:8081/getInfo";
HttpMethod type = HttpMethod.GET; //请求方式
RequestEntity paras = null;//请求参数,没有请求参数,所以给null值
//对象调用exchange()带四个参数的方法,并返回请求
ResponseEntity responseEntity = restTemplate.exchange(url,type,paras,String.class);
//返回值,注意,这里是返回String类型,所以getBody()就是字符串,如果方法返回是对象或者list集合,getBody()也就对应返回类型。
return responseEntity.getBody();
}
@RequestMapping("/test1")
public String getEurekaServiceInfo_01(){
/**
* 对上面的方法进行升级改造,进一步封装,并调用无参数的getDemoInfo()方法达到同样的效果
* getForObject(url,resultType) ,可以看出,这个方法的提交方式是GET
* url:远程服务地址
* resultType:返回值类型 String.class
*/
String url = "http://localhost:8081/getInfo";
String res = restTemplate.getForObject(url,String.class);
return res;
}
@RequestMapping("/test2")
public String getEurekaServiceInfo_02(){
/**
* 用getForObject(url,resultType)方法调用有参数的getDemoInfo_02()方法
* getForObject(url,resultType) ,可以看出,这个方法的提交方式是GET
* url:远程服务地址;我这里做测试,直接在url将参数固定,利用url传参数:?userCode=zhangsan
* resultType:返回值类型 String.class
*/
String url = "http://localhost:8081/getInfo_02?userCode=zhangsan";
String res = restTemplate.getForObject(url,String.class);
return res;
}
@RequestMapping("/test3")
public List getListInfo(){
/**
* 用getForObject(url,resultType)方法调用有参数的getList()方法
* getForObject(url,resultType) ,可以看出,这个方法的提交方式是GET
* url:远程服务地址;我这里做测试,直接在url将参数固定,利用url传参数:?userCode=zhangsan
* resultType:返回值类型 List.class
* List mapList = restTemplate.getForObject(url,List.class);
* 返回到前端所显示的值:[{"userName":"张三","userCode":"zhangsan"}] 是json 格式的数据
*/
String url = "http://localhost:8081/getlist?userCode=zhangsan";
List mapList = restTemplate.getForObject(url,List.class);
return mapList;
}
@RequestMapping("/test4")
public User getUserInfo(){
/**
* 用postForObject(url,参数对象,返回值类型)方法调用有参数的getUser()方法
* postForObject(url,参数对象,返回值类型)可以看出采用的是POST提交方法
* url:远程服务地址
* user:参数对象
* resultType:返回值类型 User.class
* User u = restTemplate.postForObject(url,user,User.class);
*/
User user = new User();
user.setId(17);
user.setUserCode("zhangsan");
user.setUserName("张三");
user.setPassword("1234");
String url = "http://localhost:8081/getuser"; //远程服务地址
User u = restTemplate.postForObject(url,user,User.class);
return u;
}
}
挨个测试以上控制器的输出结果:
http://localhost:8082/test String 类型的无参数 调用的exchange(url,type,paras,resultType)方法
http://localhost:8082/test1 String 类型的无参数 调用的getForObject(url,resultType)方法
http://localhost:8082/test2 String 类型的带参数 调用的getForObject(url,resultType)方法
http://localhost:8082/test3 集合类型的 List 带参数 调用的getForObject(url,resultType)方法
http://localhost:8082/test4 对象类型 调用的是 postForObject(url,参数对象,返回值类型)方法
至此,IDEA基于Spring Cloud Netflix(2.1.0RC3)等同于(SpringBoot2.1.1正式版)的SpringCloud 微服务架构搭建——实战教程(一)的Spring Cloud Eureka来实现服务治理就基本结束了!