新手必备系列之从零开始搭建springcloud脚手架
基本微服务架构
1、创建项目
groupId: 项目名称
artifactId: 工程名称
project: 新建项目名称
创建项目后:pom.xml包含创建的
在pom.xml文件中配置:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencies>
<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>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
注意:配置在dependencyManagement里的dependency需要制定其版本,而配置在dependencies中的不需要, 所有生命在dependencies里的依赖都会自动引入,并默认被所有的子项目继承。 dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
2、服务注册中心(Eureka(老版本,demo使用的)、Consul(实际生产)、ZK 常用),作用:服务的发现与治理,负载均衡(自带)有类似负载均衡的算法,对于服务集群配置,该服务能知道调用哪些服务才能平均分配请求的压力;并且,如果哪个服务崩溃,该服务还能快速知道并不再向崩溃的服务发送请求,步骤:
(1)在根目录下面创建一个新的模块(module),命名为demo-eureka,创建启动类 EurekaApplication
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
注意: 创建文件时若不能新建class文件,则设置Sources Root: 通过这个类指定一个文件夹,你告诉IntelliJ IDEA,这个文件夹及其子文件夹中包含的源代码,可以编译为构建过程的一部分。
(2)在resources文件夹下创建 application.yml文件,配置:
spring:
application:
# 应用名称
name: demo-eureka
server:
port: 8080
eureka:
instance:
# Eureka注册中心HOST主机地址,可以采用:1.直接配置IP;2.配置本地域名并修改本地hosts文件
hostname: localhost
client:
# 是否将自己注册到注册中心。因为项目中只有一个注册中心就是自己,所以无需再注册
register-with-eureka: false
# 是否从远程拉取其他注册中心,因为注册中心只有自己所以不需要。如果注册中心有多个,可以相互暴露,相互拉取
fetch-registry: false
service-url:
# 该注册中心连接地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
注意: yml文件的格式一定要配置对,不对是识别不了配置的
(3)在pom.xml配置在dependencies:
<!--增加eureka-server的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
(4)配置完成,启动服务,访问localhost:8080,显示即为配置成功
3、搭建生产者服务,在根目录下创建模块demo-admin
(1)在pom.xml添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
(2)创建启动类 AdminApplication,只要加了注解@EnableEurekaClient 即表示该服务为Eureka的一个服务生产者
@EnableEurekaClient
@SpringBootApplication
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
(3)在resources文件夹下创建 application.yml文件,配置:
server:
port: 9001
spring:
application:
name: demo-admin
eureka:
client:
service-url:
# Eureka注册中心连接地址
# 如果注册中心地址配置的域名,这里使用 http://域名/eureka/ 格式
defaultZone: http://localhost:8080/eureka/
(4)配置完成,启动启动类,观察服务是否已经在Eureka注册上
(5)在 /controller 下创建测试方法(AdminController)
@RestController
public class AdminController {
@GetMapping("/hello/{name}")
public String hello(@PathVariable String name) {
return "hello" + name + "this is demo-admin";
}
}
(6)重启服务,访问: http://localhost:9001/hello/zhangsan
4、demo-admin暴露了Rest接口,那么其他服务想要调用生产者服务的接口,即消费者,在这里我们使用Feign实现,在根目录下面创建demo-auth文件夹
(1) 在pom.xml 文件下配置依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<--开启Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
(2)创建服务的启动类(AuthApplication)
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
注意: 添加@EnableDiscoveryClient注解声明该服务为Eureka中服务消费方;添加@EnableFeignClients开启Feign声明式服务间通信(配合@FeignClient注解使用)
(3)在resources文件夹下创建 application.yml文件,配置:
server:
port: 9002
spring:
application:
name: demo-auth
eureka:
client:
service-url:
# Eureka注册中心连接地址
# 如果注册中心地址配置的域名,这里使用 http://域名/eureka/ 格式
defaultZone: http://localhost:8080/eureka/
(4)创建一个demo的Controller:
@RestController
public class AuthController {
@GetMapping("/hello/{name}")
public String hello(@PathVariable String name) {
return "hello " + name + ", this is demo-auth";
}
}
(5) 访问localhost:9002/hello/zhangsan,正常访问则搭建完成
(6) 在demo-auth下创建feign或者service 文件夹,并创建AuthFeignService接口:
@FeignClient(value = "demo-admin")
public interface AuthFeignService {
/**
* 这里暴露一个Feign接口地址,其中`@GetMapping`中的地址一定对应了`demo-admin`服务中某个Controller中的请求地址(如果`demo-admin`服务中没有这个接口地址就会404)
* 如果其他地方调用了AuthFeignService接口的hello方法,FeignClient将类似通过转发的方式去请求调用`demo-admin`服务中符合的接口地址的方法
* 如果请求传递了参数,需要加@RequestParam注解标识。如果URL中有动态参数,要添加@PathVariable注解
*
* @param name
* @return
*/
@GetMapping("/hello/{name}")
public String hello(@PathVariable(name = "name") String name);
}
注意:
- 如上,这是一个供Feign调用的接口。Feign实现服务端通信有几个基本条件:
- 该服务添加了spring-cloud-starter-openfeign依赖
- 该服务的启动器类上添加了@EnableFeignClients注解
- 该服务提供了一个接口类,并在该接口上添加了@FeignClient注解,并且@FeignClient注解中value属性值必须等于一个已在Eureka中注册的服务名称。
- 在该接口上添加一个调用的方法,该方法必须和被调用服务方Controller中的方法格式相同(除了没有方法具体实现,方法的返回值、参数列表都要相同),并且需要和被调用方Controller接口一样在方法上添加@RequestMapping注解,指明被调用方接口的具体URL。
(7)在AuthController中添加一个接口:
// 导入需要的接口
@Autowired
private AuthFeignService authFeignService;
/**
* 模拟Feign远程调用,这里暴露一个请求接口`/feignHello`
* 这个请求接口将调用`AuthFeignService`(Feign接口)
* Feign接口(`AuthFeignService`接口)将会通过`@FeignClient(value = "demo-admin")`在Eureka注册中心寻找`demo-admin`模块
* Feign接口类似Controller的Rest接口,也暴露一个地址,这个地址对应`demo-admin`模块的一个Controller接口地址
*
* @param name
* @return
*/
@GetMapping("/feignHello/{name}")
public String feignHello(@PathVariable String name) {
return authFeignService.hello(name);
}
注意: 如果authFeignService导入后显示红色,则只需要根据提示取消掉标红,不影响运行的报错
5、配置Hystrix熔断机制
原理: Hystrix主要用于在多个服务间存在通信,而通信的某一方因为压力过大、响应时间过长而导致不可用,Hystrix就会阻止Eureka再给该服务分配请求,从而保证服务的高可用
使用: Hystrix主要配置在存在服务间通信的地方。因为我们使用的Feign,Feign正是用来实现服务间通信的。所以我们只需要在自己创建的Feign接口中配置熔断器即可。因为Feign已经内置了Hystrix,我们只需要配置启用hystrix即可
(1)在demo-auth 的yml文件中添加配置
feign:
hystrix:
# 开启Feign的Hystrix熔断器支持
enabled: true
(2)在demo下面创建文件夹fallback,创建 AuthFeignServiceFallbackImpl 类
@Component
public class AuthFeignServiceFallbackImpl implements AuthFeignService {
@Override
public String hello(String name) {
return "hello " + name + ", this is demo-auth, but request error";
}
}
(2)在demo下面创建文件夹fallback,创建 AuthFeignServiceFallbackImpl 类
@Component
public class AuthFeignServiceFallbackImpl implements AuthFeignService {
@Override
public String hello(String name) {
return "hello " + name + ", this is demo-auth, but request error";
}
}
这个类实现了AuthFeignService远程调用接口,作用就是当AuthFeignService调用的服务不可用时,Hystrix直接执行这个实现类中的方法,而不再执行AuthFeignService中的远程调用方法。
(3)重启demo-auth服务,关闭demo-admin,观察访问结果,返回错误即正常。
!注意:使用熔断的时候,需要在feign接口处配置熔断fallback指定的类,举例:
6、配置Hystrix-DashBoard熔断监控
使用: 通过hystrix-dashboard工具实现对熔断器Hystrix的实时监控。因为我们在demo-auth服务中使用了Feign远程调用,并且使用了Hystrix熔断器,所以我们只需要在demo-auth服务中配置熔断监控
(1)引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
(2)在demo-auth启动类上添加 @EnableHystrixDashboard注解
(3)创建config文件夹,创建类 HystrixDashboardConfig:
@Configuration
public class HystrixDashboardConfig {
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
注意: 在SpringBoot2.x之前的版本中,SpringBoot默认提供了一个接口地址用于访问Hystrix熔断器。但是在SpringBoot2版本中需要手动提供一个Hystrix监控地址。上面的配置就是将Servlet ( HystrixMetricsStreamServlet )注入到Web容器中,类似web.xml中的和
(4)重启demo-auth服务,访问 localhost:9002/hystrix,名称输入 localhost:9002/hystrix.stream,刷新时间可以自己定义,默认两秒,关闭demo-admin服务后,再访问 http://localhost:9002/feignHello/tycoding,进入熔断机制,则可实时监控熔断的情况。
7、配置网关服务demo-gateway,创建demo-gateway文件夹(使用zuul)
(1)在pom文件里引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
(2)创建启动类 GatewayApplication:
// 此注解开启Zuul网关的支持
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
(3)创建配置文件 application.yml:
server:
port: 9003
spring:
application:
name: demo-gateway
eureka:
client:
service-url:
# Eureka注册中心连接地址
# 如果注册中心地址配置的域名,这里使用 http://域名/eureka/ 格式
defaultZone: http://localhost:8080/eureka/
zuul:
routes:
# 路由名称,随意
demo-admin:
# 路由地址
path: /api/admin/**
# 该路由地址对应的服务名称
serviceId: demo-admin
demo-auth:
path: /api/auth/**
serviceId: demo-auth
注意: zuul.routers下可以配置多个路由表,每个路由表中对应了具体的请求路径前缀path,这个请求路径最终会被Zuul路由到某个服务中,所需需要对应配置一个serviceId对应某个服务的名称。这样就实现了请求/api/admin/xx接口Zuul就会将请求转发到demo-admin服务、请求/api/auth/xx接口Zuul就会将请求转发到demo-auth服务
(4)通过网关访问各模块服务,正常访问则搭建完成
!!注意: 如果Zuul路由服务失败不应该给客户端返回404、500等错误,因为客户端直接请求的是demo-zuul路由网关服务,而请求demo-zuul服务是成功的,demo-zuul将请求转发到其他服务属于服务器内部错误,这中错误不应该暴露给客户端。除非是demo-zuul路由网关服务自己挂了,才会返回给客户端404、500等错误
(5)服务通过网关是需要拦截请求是不是合法请求(比如token的验证),我们在此也模拟一下网关 的验证,创建filter 文件夹,创建类LoginFilter
@Component
public class LoginFilter extends ZuulFilter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 过滤器类型:
* pre: 路由前
* routing: 路由时
* post: 路由后
* error: 路由发生错误时
*
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤的顺序
*
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 是否需要过滤
*
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体业务逻辑
*
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
logger.info("{} >>> {}", request.getMethod(), request.getRequestURI().toString());
String token = request.getParameter("token");
if (token == null) {
logger.error("Error! Request Token is Empty");
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
try {
currentContext.getResponse().getWriter().write("Request token is empty");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
(6)重启demo-gateway服务,访问 localhost:9003/api/admin/hello/zhangsan,不能正常访问, localhost:9003/api/admin/hello/zhangsan?token=123可正常访问则搭建完成。
8、搭建配置中心config服务(一般系统利用工具配置(apllo,nacos),这里测试使用模块使用)
(1)在根目录下创建demo-config模块
(2)添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
(3)添加配置文件
server:
port: 8888
spring:
application:
name: demo-config
# 获取本地配置文件,本身支持:本地储存、git远程、SVN
profiles:
active: native
cloud:
config:
server:
# 获取本地配置文件的位置
native:
search-locations: classpath:config/
eureka:
client:
service-url:
# Eureka注册中心连接地址
# 如果注册中心地址配置的域名,这里使用 http://域名/eureka/ 格式
defaultZone: http://localhost:8080/eureka/
注意:由于项目为demo,获取的配置都在本地配置,正式环境可从远程仓库获取:
# 远程读取
# cloud:
# config:
# label: master # 配置仓库的分支
# server:
# git:
# uri: https://github.com/xx/spring-cloud-config # 配置Git仓库地址,如果用Github需要加.git,如果用Gitlab则不需要
# search-paths: respo # 配置仓库路径
# username: # 访问Git仓库的账号
# password: # 访问Git仓库的密码
(3)创建服务的启动类 ConfigApplication
// 开启配置文件服务支持
@EnableConfigServer
@EnableEurekaClient
@SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
(4)将demo-gateway服务的配置文件copy到demo-config服务的/resources/config文件夹下,启动demo-config服务,访问 localhost:8888/config/application-zuul-dev.yml:
成功访问则说明配置文件中的内容已经通过外部接口访问,即搭建成功。
(5)client端的搭建,在根目录下的pom文件中添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
(6)在根目录的resource文件夹下创建application.yml文件,写入配置文件:
spring:
profiles:
active: native
cloud:
config:
uri: http://locahost:8888
name: application-zuul
profile: dev
# label:
参数说明:
- spring.cloud.config.uri: 配置服务中心的网址
- spring.cloud.config.name: 配置文件的前缀名
- spring.cloud.config.label: 配置仓库的分支。如果是本地读取,可不配置
- spring.cloud.config.profile: 配置文件的环境标识
- dev: 开发环境
- test: 测试环境
- prod: 生产环境
重启demo-gateway服务,删除配置,用配置中心配置,访问成功即可。
!!注意: 配置服务器的默认端口是 8888, 如果修改了默认端口,则客户端项目中就不能在application.yml或application.properties中配置spring.cloud.config.uri,必须在bootstrap.yml或是bootstrap.properties中配置,原因是bootstrap开头的配置文件会优先加载和配置。
9、开启 Spring Boot Profile:
如果项目中分别部署了开发、测试、生产环境的配置文件,但如果切换不同的开发环境就可能要修改对应配置文件中的配置 ( spring.cloud.config.profile的参数值 ),而使用Spring Boot Profile就仅需要在启动项目时指定加载的文件,Spring就会自动加载该配置文件来启动项目。举个栗子:java -jar demo-auth-1.0.0-SNAPSHOT.jar --spring.profiles.active=prod其中–spring.prodiles.active=prod就会自动加载application-auth-prod.yml配置文件
10、搭建zipkin链路追踪服务(文章中的服务搭建已过时,具体搭建参考官网或者 https://www.cnblogs.com/danghuijian/p/9482279.html)
11、搭建SpringBootAdmin服务
(1)Server端:创建demo-boot-admin模块
(2)在pom.xml文件夹下添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
(3)在父节点pom文件下添加依赖
<properties>
<spring-boot-admin.version>2.1.5</spring-boot-admin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
注意:在dependencyManagement下添加依赖,需要制定version的版本
(4)添加配置文件:application.yml
server:
port: 9004
spring:
application:
name: demo-boot-admin
zipkin:
base-url: http://localhost:9411
eureka:
client:
service-url:
# Eureka注册中心连接地址
# 如果注册中心地址配置的域名,这里使用 http://域名/eureka/ 格式
defaultZone: http://localhost:8080/eureka/
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: health,info
(5)创建该服务的启动类: BootAdminApplication.java
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
修改application.yml文件,添加配置:
spring:
boot:
admin:
client:
url: http://localhost:9004
(7) 按照demo-eureka、demo-config、demo-zipkin、demo-boot-admin、demo-admin、demo-auth、demo-gateway 顺序依次启动服务。
访问localhost:9004成功访问SpringBootAdmin界面则搭建成功
12、邮箱报警设置
(1)在demo-boot-admin模块pom下新增依赖:
<!--邮箱报警-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
(2)在application.yml文件spring下新增配置:
mail:
host: smtp.163.com
username: ********
password: ********(密码)
boot:
admin:
notify:
mail:
to: *********(到达邮箱)
from: **********(发出邮箱)
注意:如果无法使用,观察自己的163邮箱是否开始SMTP服务,并开始客户端授权密码
如果未打开,打开即可正常使用,亲测可用!
好了,到这里搭建过程已经结束了,整个demo应该还是很全面的,大家有补充的可以留言,有错误的也欢迎大家指正~