由于用户量上升,现需求项目升级,后台架构要求升级至微服务,综合后决定用spring全家桶!
接下来为大家搭建一个微服务框架,后期可以自己进行扩展。会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新建的项目都是用springboot,推荐使用coding地址下载,因为可以切换分支,后期可以及时更新。
本项目coding地址:https://git.coding.net/yuhaoqiang/SpringCloud.git
Eureka/Consul/Zookeeper:服务发现
Hystrix:断路器
Zuul:智能路由
Ribbon/Feign:客户端负载均衡 (Feign用的更多)
Turbine&hystrix-dashboard:集群监控
Springcloud-config:远程获取配置文
一、搭建consul服务
springclound-consul作为服务发现的核心,第一个搭建,后面的服务都要注册到consul上,意思是告诉consul自己的服务地址是啥。当然还可以用zookeeper或者eureka。
下载安装consul:去下载
根据自己的版本下载对应的安装包 以win64为列,下载解压后会得到一个.exe执行。cmd进入exe同级目录,输入consul agent -dev 就可以运行了, 然后consul的默认端口是8500,直接在浏览器打开http://localhost:8500,就可以看到控制台了
二、搭建config-server服务sc-config-server
注:之前采坑,由于依赖版本有问题导致报错,一直找不到问题,经过一天的努力,才发现问题。所以在这里推荐大家使用spring官方https://start.spring.io/构建项目,或者用idea进行构建。
这里我们用idea构建如下:下一步即可
1.修改pom文件,加入以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
2.修改application.yml文件
server:
port: 8800
spring:
security:
basic:
enabled: true
user:
name: root
password: kaidianapp
application:
name: sc-config-server
cloud:
config:
server:
git:
uri: https://git.coding.net/yuhaoqiang/spring-cloud-demo
searchPaths: '{application}'
consul:
host: localhost
port: 8500
discovery:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
prefer-ip-address: true
3.修改启动类
@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class ScConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ScConfigServerApplication.class, args);
}
}
然后运行启动springboot项目,等启动成功后访问consul的页面,会发现sc-config-server已经注册到上面了,如果启动报错,请检查错误信息。
三、搭建服务提供者服务sc-provider
还是熟悉的配方:新建一个springboot项目...
注意 : 这里除了application.xml,还需要一个bootstrap.yml, 因为bootstrap.yml得加载顺序是在application.xml前边,服务注册和config配置必须放到bootstrap.yml
application.yml 放在了远程仓库:远程配置文件
- 修改pom文件
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> <version>2.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>2.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>2.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.1.1.RELEASE</version> </dependency>
2. 编写配置文件bootstrap.yml application.yml在远程coding仓库
spring: application: name: sc-provider cloud: consul: host: localhost port: 8500 discovery: instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}} prefer-ip-address: true config: discovery: enabled: true service-id: sc-config-server fail-fast: true username: root password: kaidianapp profile: pro
3.编写主类代码
@EnableDiscoveryClient @SpringBootApplication public class ScProviderApplication { public static void main(String[] args) { SpringApplication.run(ScProviderApplication.class, args); } }
4.编写controller进行测试
@RestController @RequestMapping("test") public class IndexController { //返回一个实体 @GetMapping("{msg}") public Mono<String> sayHelloWorld(@PathVariable("msg") String msg) { System.out.println("come on " + msg); return Mono.just("sc-provider receive : " +msg); } //返回一个列表 @GetMapping("list") public Flux<Integer> list() { List<Integer> list = new ArrayList<>(); list.add(13); list.add(55); list.add(2); list.add(555); Flux<Integer> userFlux = Flux.fromIterable(list); return userFlux; } @GetMapping("actuator/health") public Mono<String> actuatorHealth() { return Mono.just("actuator/health : ok "); } }
启动项目进行测试。
四、搭建消费者服务sc-consumer
消费者要访问服务提供者的服务,这里用的是通过RestTemplate/feign请求resetful接口,使用ribbon做客户端负载均衡,hystrix做错误处理,feign和ribbon二选一,案例中ribbon和feign都有,也可以都用。
这里演示feign用法,ribbon用法请参考燕大佬博客:https://blog.csdn.net/yp090416/article/details/81587110
还是熟悉的配方,熟悉的味道,新建springboot项目,添加项目依赖。
1.修改pom文件 加入以下依赖
dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.修改bootstrap.yml文件
spring:
application:
name: sc-consumer
cloud:
consul:
host: localhost
port: 8500
discovery:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
ip-address: true
config:
discovery:
enabled: true
service-id: sc-config-server
fail-fast: true
username: root
password: kaidianapp
profile: con
#新版配置,否则后面dashboard无法找到hystrix.stream
management:
endpoints:
web:
exposure:
include: '*'
ribbon:
eureka:
enabled: false
3.编写主类代码
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
@EnableHystrix
@EnableCircuitBreaker
public class ScConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ScConsumerApplication.class, args);
}
}
application.yml 文件增加以下配置:
feign:
hystrix:
enabled: true
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 60000
准备好一大堆代码- -
3.1 feign配置:
@Configuration
public class MFeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
3.2 feign 接口
@FeignClient(name = "sc-provider",fallback = MFeignClientFallback.class,configuration = MFeignConfig.class)
public interface MFeignClient {
// 这是被请求微服务的地址,也就是provider的地址
@GetMapping(value = "/test/{msg}")
String sayHelloWorld(@PathVariable("msg") String msg);
@GetMapping(value = "/test/list")
List<Integer> list();
@GetMapping(value = "/test/list")
Integer[] array();
}
3.3 feign 断路器回调方法
@Component
public class MFeignClientFallback implements MFeignClient {
@Override
public String sayHelloWorld(String msg) {
return "请求失败:";
}
@Override
public List<Integer> list() {
return new ArrayList<>();
}
@Override
public Integer[] array() {
return new Integer[0];
}
}
3.4 可以用工厂代替,并且打印错误异常信息,方便排查问题(可选择)
@Component
public class MFeignClientFallbackFactory implements FallbackFactory<MFeignClient> {
@Autowired
private MFeignClientFallback mFeignClientFallback;
@Override
public MFeignClient create(Throwable throwable) {
throwable.printStackTrace();
return mFeignClientFallback;
}
}
3.5 feign controller编写
@RestController
public class FeignController {
@Autowired
private MFeignClient feignClient;
@GetMapping("/feign/{wd}")
public Mono<String> sayHelloWorld(@PathVariable("wd") String parm) {
String result = feignClient.sayHelloWorld(parm);
return Mono.just(result);
}
@GetMapping("/feigns/list")
public Flux<Integer> list() {
List<Integer> list = feignClient.list();
Flux<Integer> userFlux = Flux.fromIterable(list);
return userFlux;
}
@GetMapping("/feigns/array")
public Flux<Integer> array() {
Integer[] arrays = feignClient.array();
Flux<Integer> userFlux = Flux.fromArray(arrays);
return userFlux;
}
}
3.6 启动项目 进行测试
这里我们尝试停止sc-provider提供者项目,测试是否进入断路器方法,也可以修改sc-provider项目让方法抛出异常.
停止后:
并且可以看到控制到打印的错误日志:
五、用zuul做路由转发和负载均衡
这些微服务都是隐藏在后端的,用户是看不到,或者不是直接接触,可以用nginx或者zuul进行路由转发和负载均衡,zuul负载均衡默认用的是ribbon。
1.新建项目 修改pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.修改bootstrap.yml
spring:
application:
name: sc-zuul
cloud:
consul:
host: localhost
port: 8500
discovery:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
ip-address: true
config:
discovery:
enabled: true
service-id: sc-config-server
fail-fast: true
username: root
password: kaidianapp
profile: zuul
3.修改启动类
@EnableDiscoveryClient
@EnableZuulProxy
@RefreshScope
@SpringBootApplication
public class ScZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ScZuulApplication.class, args);
}
}
启动springboot项目,访问consul
这时候,我们就要通过zuul访问微服务了,而不是直接去访问微服务。
应该访问地址http://localhost:8400/sc-consumer/feign/list,这块你要换成你的zuul地址。
但是有些人就会说,这样以后用户请求会不会太长,比较反感,所以可以通过配置进行修改访问地址。
修改application.yml文件
server:
port: 8700
zuul:
routes:
sc-consumer: /consumer/**
sc-provider: /provider/**
ribbon:
ReadTimeout: 3000
ConnectTimeout: 3000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 80000
重启项目
六、用hystrix-turbine-dashboard 做集群监控
项目在生产环境中,每个服务的访问量都不通,有些服务的访问量比较大,有时候有些服务挂了,不能继续服务,需要重启的时候,我们并不知道,所以这时候就需要使用hystrix-turbine-dashboard做一个监控,监控所有的微服务,可以看到这个接口实时访问量,和健康状况。
新建一个springboot项目,老套路,加入如下依赖
1.修改pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
<exclusions>
<exclusion>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
</exclusion>
</exclusions>
</dependency>
2.修改application.yml配置文件 注意不是bootstrap.yml
server:
port: 8900
turbine:
aggregator:
clusterConfig: default
appConfig: sc-consumer
clusterNameExpression: "'default'"
spring:
application:
name: sc-monitor
cloud:
consul:
host: localhost
port: 8500
discovery:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
ip-address: true
3.修改主类
@EnableDiscoveryClient
@EnableTurbine
@EnableHystrixDashboard
@SpringBootApplication
public class ScMonitorApplication {
public static void main(String[] args) {
SpringApplication.run(ScMonitorApplication.class, args);
}
}
4.访问测试
这块的端口是8900,访问地址http://localhost:8900/hystrix,看到的是下面的页面。
以上就是大致架构,后面也可以自行加入组件.
下面我们将项目用docker打包发布到服务器上
七. 下载consul 运行在服务器上
1.服务器启动consul
我这里用的centos7 下载Linux版本后 用ftp上传到 服务器并解压
这里使用之前的命令外部会访问不到服务器,如果项目要注册上去填写的服务器ip也会连不到,所以我们要改下命令。
运行服务器启动命令 ./consul agent -dev -client 0.0.0.0 -ui
2.打包项目并上传服务器
idea 用maven 插件 直接打包:
3. 使用Dockerfile构建自己的Docker镜像
保持Dockerfile文件和jar文件放在同一目录
编写Dockerfile文件,如图:
使用 docker build -t config-server . 其中,-t指定镜像名字,命令最后的点(.)表示Dockerfile文件所在路径
执行以下命令,即可使用该镜像启动一个 Docker容器,后台运行可以加 -d
docker run -p 8800:8800 config-server
查看一下consul 看是否注册成功。
八.打包提供者(sc-provider)服务发布
1.打包发布流程请参考上述远程配置服务
2.这里讲个坑,就是消费端发布后consul Service Checks 一直检查不通过,导致服务无法访问。
我们在这里自己更改checks接口,完成验证。
2.1 IndexController 里面增加接口
@GetMapping("actuator/health")
public Mono<String> actuatorHealth() {
return Mono.just("actuator/health : ok ");
}
2.2 修改bootstrap.yml
2.3 修改完成后 打包上传并用docker构建镜像,然后发布验证即可。
2.4 其他服务流程以此类推
至此微服务实战到发布就完成了!!!
如果有问题,请在参考https://blog.csdn.net/yp090416/article/details/81587110或者在下方评论,或者加群讨论 200909980