一般使用Eureka(注册中心),Feign(声明式服务调用)或Ribbon(服务内部进行负载均衡),zull(网关),Hystrix(熔断器)就可以搭建体系比较完善的微服务架构。直接一张图解释所有。
下面介绍一个各个组件如何使用
代码语言:javascript复制Eureka(注册中心)
注册中心可以说是整个微服务架构的核心,所有的服务元数据全都保存 在我们的注册中心
1.依赖jar
代码语言:javascript复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.配置文件application.properties
代码语言:javascript复制#端口
server.port=8888
#客户端通过ip底层注册,高可用的eureka注册中心准备
eureka.client.preferIpAddress=true
#在注册中心本身进行注册和发现的能力
eureka.client.registerWithEureka=true
#发现抓取注册信息的内容
eureka.client.fetch-registry=true
#提供客户端访问的注册中心地址,http请求的接口
eureka.instance.hostname=127.0.0.1
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8888/eureka
#当前工程提供一个服务名称,根据业务逻辑来的
#相同功能的工程实现的集群,都是同一个名称
spring.application.name=eureka-server
#关闭保护机制
eureka.server.enable-self-preservation=false
ribbon.eureka.enabled=true
注册中心可以配置多个用逗号隔开就好但是我比较懒。
3.开关类
代码语言:javascript复制package com.jmy.cloud.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer // 核心就是这里喽 注册成为Eureka注册中心
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
直接在浏览器输入http://127.0.0.1:8888 就可以看到我们的注册中心提供的web界面了
代码语言:javascript复制服务提供者(Eureka的客户端)
1.依赖jar
代码语言:javascript复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
*因为主要介绍微服务架构所以其他依赖就不再展示,代码已上传到gitHub直接下载使用就好。 *
2.配置文件application.properties
代码语言:javascript复制
2.配置文件application.properties
server.port=
eureka.client.preferIpAddress=true
eureka.client.registerWithEureka=true
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
#相同功能的工程实现的集群,都是同一个名称
spring.application.name=service-provider
# mysql相关配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/16rg?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type: com.alibaba.druid.pool.DruidDataSource
#驼峰命名的对应关系
mybatis.configuration.mapUnderscoreToCamelCase=true
#缓存关掉
mybatis.configuration.cacheEnabled=false
#解决com.netflix.client.ClientException: Load balancer does not have available server for client: service-provider错误
#开启负载均衡策略
ribbon.eureka.enabled=true
注意看最后一条哦,提前替你踩好坑,不得不说我可真是一个良心作者。
3.开关类
代码语言:javascript复制package com.jmy.cloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@MapperScan("com.jmy.cloud.mapper")
@EnableDiscoveryClient // 核心注解 将本服务注册到注册中心(适用于除Eureka之外的其他注册中心)
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
Controller
代码语言:javascript复制package com.jmy.cloud.controller;
import com.jmy.cloud.entity.R;
import com.jmy.cloud.entity.StudentSign;
import com.jmy.cloud.service.StudentSignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
public class StudentSignController {
@Autowired
private StudentSignService studentSignService;
// 签到
@PostMapping("/provider/sign/save")
public R sign(@RequestBody StudentSign studentSign){
return studentSignService.sign(studentSign);
}
// 查询指定学生的查询信息
@RequestMapping("/provider/sign/queryByName")
public R querySignByName(@RequestParam("name") String name){
return studentSignService.querySignByName(name);
}
// 查询所有签到信息
@GetMapping("/provider/sign/queryAll")
public R queryAllSign(){
int i = /;
return studentSignService.queryAllSign();
}
}
Service
代码语言:javascript复制
package com.jmy.cloud.service;
import com.jmy.cloud.entity.R;
import com.jmy.cloud.entity.StudentSign;
import com.jmy.cloud.mapper.StudentSignMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentSignServiceImpl implements StudentSignService {
@Autowired
private StudentSignMapper studentSignMapper;
@Override
public R sign(StudentSign studentSign) {
//每一堂课需要签到 9:30-9:40 第一次
// 10:50-11:00 2:30-2:40 3:50-4:00
StudentSign sign = new StudentSign();
sign.setName(studentSign.getName());
sign.setClassName(studentSign.getClassName());
sign.setType("课前签到");
if (studentSignMapper.sign(sign) == ) {
return R.ok("签到成功");
} else {
return R.fail("签到失败!");
}
}
@Override
public R querySignByName(String name) {
List<StudentSign> stuList = studentSignMapper.querySignByName(name);
if (stuList != null) {
return R.ok(stuList);
} else {
return R.fail("暂无数据!");
}
}
@Override
public R queryAllSign() {
List<StudentSign> signList = studentSignMapper.queryAllSign();
if (signList.size() != ) {
return R.ok(signList);
} else {
return R.fail("暂无数据!");
}
}
}
不要纠结StudentSign和R这两个类,这两个类是我为了方便提前封装好的数据库表对象以及结果类放到了源码的Cloud_Model模块中。
代码语言:javascript复制服务调用者
也是一个要注册到注册中心的服务而已,不过此服务要调用上一个服务 不然我如何演示Feign组件和Ribbon组件呢。。
1.依赖jar
代码语言:javascript复制
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
因为主要介绍微服务架构所以其他依赖就不再展示,代码已上传到gitHub直接下载使用就好。
2.配置文件application.properties
代码语言:javascript复制server.port=
eureka.client.preferIpAddress=true
eureka.client.registerWithEureka=true
eureka.client.fetch-registry=true
eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka
#相同功能的工程实现的集群,都是同一个名称
spring.application.name=service-consumer
#启动feign的熔断机制
feign.hystrix.enabled=true
3.开关类
代码语言:javascript复制package com.jmy;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@EnableFeignClients // 启用feign
@EnableSwagger2 // 启用Swagger
@EnableHystrix // 启用熔断器
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//可以实时显示服务的信息
@Bean
public HystrixMetricsStreamServlet createHMSS(){
return new HystrixMetricsStreamServlet();
}
//注册Servlet到SpringBoot项目
@Bean
public ServletRegistrationBean registS(HystrixMetricsStreamServlet streamServlet){
ServletRegistrationBean registrationBean=new ServletRegistrationBean();
registrationBean.setServlet(streamServlet);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setEnabled(true);
return registrationBean;
}
}
Controller
代码语言:javascript复制
package com.jmy.controller;
import com.jmy.cloud.entity.R;
import com.jmy.cloud.entity.StudentSign;
import com.jmy.service.StudentSignService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@Api(value = "学生签到",tags = "学生签到")
public class StudentSignController {
@Autowired
private StudentSignService studentSignService;
// 签到
@PostMapping("/consumer/sign/save")
@ApiOperation(value = "学生进行签到",notes = "学生签到信息新增")
public R sign(@RequestBody StudentSign studentSign){
return studentSignService.sign(studentSign);
}
// 查询指定学生的查询信息
@RequestMapping("/consumer/sign/queryByName")
@ApiOperation(value = "查询单个学生的签到信息",notes = "查询单个学生的签到信息")
public R querySignByName(@RequestParam("name") String name){
return studentSignService.querySignByName(name);
}
// 查询所有签到信息
@GetMapping("/consumer/sign/queryAll")
@ApiOperation(value = "查询所有学生的签到信息",notes = "查询所有学生的签到信息")
public R queryAllSign(){
return studentSignService.queryAllSign();
}
}
@Api(value = “学生签到”,tags = “学生签到”)
@ApiOperation(value = “学生进行签到”,notes = “学生签到信息新增”)
这两个注解也请不要纠结,这是接口测试工具Swagger的两个核心注解 对于Swagger我会单独在写一篇文章详解。
Feign进行声明式服务调用的核心接口
代码语言:javascript复制
package com.jmy.service;
import com.jmy.cloud.entity.R;
import com.jmy.cloud.entity.StudentSign;
import com.jmy.fallback.MyFallBackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "service-provider",fallbackFactory = MyFallBackFactory.class)
public interface StudentSignService {
@PostMapping("/provider/sign/save")
R sign(StudentSign studentSign);
@GetMapping("/provider/sign/queryByName")
R querySignByName(@RequestParam("name") String name);
@GetMapping("/provider/sign/queryAll")
R queryAllSign();
}
@PostMapping(“/provider/sign/save”)切记路径一定要和被调用服务对外提供的接口路径保持一致,其实内部就是封装了Ribbon发起http请求。
划重点 @PostMapping(“/provider/sign/save”)这和注解在SpringCloud中一定要加不然无参数这个错误会让你怀疑人生。 “message”:"required string parameter ‘name’ is not present
@FeignClient(value = “service-provider”,fallbackFactory = MyFallBackFactory.class) 这个注解是核心value值表示被调用的服务名称fallbackFactory记录着自定义降级类,下面介绍Hystrix组件时会介绍到。
代码语言:javascript复制Zuul网关
Zuul网关有两个核心的功能路由和过滤,路由在配置文件中就有体现 过滤的功能注意看好我自定义的ZuulFliter类
1.依赖jar
代码语言:javascript复制
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.配置文件application.properties
代码语言:javascript复制
server.port=
eureka.client.preferIpAddress=true
eureka.client.registerWithEureka=true
eureka.client.fetch-registry=true
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8888/eureka
#相同功能的工程实现的集群,都是同一个名称
spring.application.name=service-zuul
#zuul网关路由规则
zuul.routes.provider.path=/zuul-provider/**
zuul.routes.provider.serviceId=service-provider
#聪明的你应该发现路由规则了吧 routes后面的关键字可以随意指定但是一定要配套哦
zuul.routes.consumer.path=/zuul-consumer/**
zuul.routes.consumer.serviceId=service-consumer
3.开关类
代码语言:javascript复制
package com.jmy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy // 启动zuul网关
@EnableDiscoveryClient // 连接注册中心
@EnableHystrixDashboard // 启动hystrix仪表盘
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
4.ZuulFilter
代码语言:javascript复制
package com.jmy.filter;
import com.alibaba.fastjson.JSON;
import com.jmy.cloud.entity.R;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class MyZuulFilter extends ZuulFilter {
/**
* 1.过滤器类型 取值如下:
* PRE 预处理 前置过滤器
* ROUTING 处理中 正在执行
* POST 请求结束 后置过滤
* ERROR 错误过滤 一般用于收集错误信息
* */
@Override
public String filterType() {
return "pre";// 使用前置过滤
}
// 过滤级别 返回值越小 过滤级别越高(当有其他ZuulFilter的实现类同时存在时)
@Override
public int filterOrder() {
return ;
}
// 是否开启zuul网关的过滤 (我不开你我写你干嘛。。返回值改为true)
@Override
public boolean shouldFilter() {
return true;
}
// 核心方法 编写过滤细节
@Override
public Object run() throws ZuulException {
System.out.println("前置过滤开始...");
// 获取请求的上下文对象
RequestContext requestContext = RequestContext.getCurrentContext();
// 获取请求对象
HttpServletRequest request = requestContext.getRequest();
// 过滤掉请求参数不包含name的请求
if (request.getParameter("name") == null) {
requestContext.setResponseStatusCode();
requestContext.setSendZuulResponse(false);
HttpServletResponse response = requestContext.getResponse();
response.setContentType("application/json;charset=UTF-8");
try {
response.getWriter().print(JSON.toJSONString(R.fail("参数中必须包含name")));
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
有了zuul网关之后我们调用接口的时候也最好遵循Zuul的路由规则哦 http://localhost:8105/zuulconsumer/consumer/sign/hystrix.stream?name=姜明阳 就像这样。
代码语言:javascript复制Hystrix(熔断器)
我们一个服务的可用性不可能满足100%,偶尔挂掉也是正常但是高并发的场景呀一个服务宕机发生阻塞往往产生连锁反应,一崩全崩,最后服务雪崩,所以这时候我们就需要Hystrix来进行服务降级的策略熔断器类似于我们电路中的开关,直接上图自己感受一下。
1.依赖jar
代码语言:javascript复制
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Hystrix仪表盘(监控)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
我上面讲过我懒了所以我把Zuul和Hystrix写进了一个项目,所以配置文件和开关类和Zuul一毛一样…
降级类
代码语言:javascript复制因为Feign是支持Hystrix的所以我们只需要在配置文件中打开就好啦
#启动feign的熔断机制
feign.hystrix.enabled=true
代码语言:javascript复制package com.jmy.fallback;
import com.jmy.cloud.entity.R;
import com.jmy.cloud.entity.StudentSign;
import com.jmy.service.StudentSignService;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class MyFallBackFactory implements FallbackFactory<StudentSignService> {
@Override
public StudentSignService create(Throwable cause) {
return new StudentSignService() {
@Override
public R sign(StudentSign studentSign) {
return R.fail("签到服务不可用...");
}
@Override
public R querySignByName(String name) {
return R.fail("姓名查询服务不可用...");
}
@Override
public R queryAllSign() {
return R.fail("签到查询服务不可用...");
}
};
}
}
为了展示效果我在服务提供者的查询所有学生的签到信息接口里面写了int i = 1/0; 故意抛出异常展示降级效果
正常请求
服务降级
我为什么要在地址栏拼接上name=姜明阳呢当然是因为我们的ZuulFilter不加的话ZuulFilter就要履行职责喽。
代码语言:javascript复制Hystrix仪表盘(实时监控服务的熔断信息)
在SpringBoot中注册我们的仪表盘界面
代码语言:javascript复制
//可以实时显示服务的信息
@Bean
public HystrixMetricsStreamServlet createHMSS(){
return new HystrixMetricsStreamServlet();
}
//注册Servlet到SpringBoot项目
@Bean
public ServletRegistrationBean registS(HystrixMetricsStreamServlet streamServlet){
ServletRegistrationBean registrationBean=new ServletRegistrationBean();
registrationBean.setServlet(streamServlet);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setEnabled(true);
return registrationBean;
}
向Hystrix仪表盘注册接口信息
打开Hystrix仪表盘
Hystrix仪表盘监视到的接口熔断器的信息
代码语言:javascript复制Ribbon(服务间的负载均衡)
其实Ribbon组件就是封装RestTemplate直接发送Http请求,我就不在我的骨架项目中体现了直接把我之前的项目的Ribbon代码粘过来看一下就好啦。
1.依赖jar
代码语言:javascript复制
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2.开关类注册RestTemplate配置负载均衡策略
代码语言:javascript复制
@SpringBootApplication
//启动客户端进程
//@EnableDiscoveryClient//仅仅具备发现能力
@EnableEurekaClient
//和EnableEurekaServer区别在于,仅仅
//具备发现,和注册的能力
public class StarterRibbon {
public static void main(String[] args) {
SpringApplication.run(StarterRibbon.class, args);
}
//利用ribbon的客户端拦截逻辑实现
//restTemplate的创建
@Bean//相当于bean标签
//方法的返回值,作为spring容器管理的对象存在
@LoadBalanced//ribbon拦截负载均衡逻辑的注解,对于
//当前方法创建的RestTemplate对象发起的所有请求
//进行拦截
public RestTemplate initRestTemplate(){
return new RestTemplate();
}
//自定义负载均衡随机
/*@Bean
public IRule initRandom(){
return new RandomRule(); // 随机
}*/
}
3.发起Http请求
代码语言:javascript复制@Service
public class HelloService {
//在ribbon的组件中的业务逻辑中
//实现通过对拦截逻辑包装的一个restTemplate
//发起向service-hi的访问 后端服务提供者9001/9002负载均衡的
//访问
@Autowired
private RestTemplate restTemplate;
public String sayHello(String name) {
return restTemplate.getForObject
("http://service-hi/sayHi?name=" name,
String.class);
}
}
项目骨架已上传到github有需要自行下载就好附上链接 我是链接点我点我