在此项目中添加配置例如 lombok,也可不加
将api项目打包成jar,步骤如下:创建项目cloud-provider-dept-8001,同上一样,module
父工程查看,多了一个子工程:
8001中的pom文件添加:
<dependencies> <!-- 引入自己定义的api通用包,可以使用Dept部门Entity --> <dependency> <groupId>com.dw</groupId> <artifactId>cloud-api</artifactId> <version>${project.version}</version> </dependency> <!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <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> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>8001项目中编写yml文件:
server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml type-aliases-package: com.dw.entity mapper-locations: - classpath:mybatis/mapper/*.xml spring: application: name: cloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/clouddb01?useUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8 username: root password: root dbcp2: min-idle: 5 initial-size: 5 max-total: 5 max-wait-millis: 200resources包下创建mybatis包,该包下创建mybatis.cfg.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true" /><!-- 二级缓存开启 --> </settings> </configuration>创建数据库:
drop database if exists clouddb01; create DATABASE clouddb01 CHARACTER SET utf8; use clouddb01; create TABLE dept( deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, dname VARCHAR(60), db_source VARCHAR(60) ); INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE()); INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());src/main/java下创建包com.dw.dao,接口DeptDao,service,impl,controller层 写好以及mybaits文件下创建mapper DeptDao.xml映射文件:
创建主启动类,注意位置 运行项目,输入 localhost:8001/dept/list目前为止,微服务的生产者已经创建成功,接下来需要创建微服务中的消费者
创建module,cloud-consumer-dept-80,pom添加配置信息
<dependencies> <dependency><!-- 自己定义的api --> <groupId>com.dw</groupId> <artifactId>cloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- Ribbon相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>端口80 设置:
在src/main/java下创建包com.dw.cfgbeans,并创建ConfigBean
创建controller,并创建主启动:
启动8001 项目和80项目,输入 http://localhost/consumer/dept/get/1
运行成功,说明生成者和消费者都可以正常运行。
Eureaka 构建步骤
Eureka Server提供服务注册服务
各个节点启动后,会在Eureka Server中进行注册,这样的EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可用在界面中直观的看到。
EurekaClient是一个java客户端,用于简化Eureka Server的交互,客户段同时也具备一个内置的、使用沦陷负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server 提供服务注册和发现Service Provider 服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到Service Consumer 服务消费方,从Eureka获取注册的服务列表,从而能够消费服务创建项目module,cloud-eureka-7001,配置pom:
<dependencies> <!--eureka-server服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>配置当前项目application.yml:
server: port: 7001 eureka: instance: hostname: localhost #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 service-url: #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/创建主启动类,并加上@EnableEurekaServer注解,表示当前项目是服务端:
测试,输入 localhost:7001/
出现此页面,7001项目正常运行。
目前,eureka已经创建完成
接下来需要把服务的提供者,注册到Eureka
修改8001 项目,pom文件中添加,看是否有
<!-- 将微服务provider侧注册进eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>修改8001 中的yml文件,添加:
eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka启动类加入EnableEurekaClient注解:
测试,先启动7001,然后启动8001,输入http://localhost:7001/
application中已经有一个应用了,说明8001项目已经注册到了eureka中心。
服务名称修改:
8001项目中的application.yml加上配置:
instance: instance-id: microservicecloud-dept prefer-ip-address: true #访问路径可用显示ip那么在eureka注册中心可见如下信息:
这样相当于给8001服务起了个别名
那么 加上 ip显示提示 true时,鼠标放在服务名字上,左下角显示当前服务所在ip地址:
继续修改8001项目中的application.yml文件,添加如下:
info: app.name: dw-microservicecloud company.name: www.dw.com build.artifactId: $project.artifactId$ build.version: $project.version$那么,点击eureka中的服务链接,显示内容如下:
目前位置,服务已经注册到了eureka中。
8001 yml配置如下:
server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml type-aliases-package: com.dw.entity mapper-locations: - classpath:mybatis/mapper/*.xml spring: application: name: cloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/clouddb01?useUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=GMT%2B8 username: root password: root dbcp2: min-idle: 5 initial-size: 5 max-total: 5 max-wait-millis: 200 eureka: client: #客户端注册进eureka服务列表内 service-url: defaultZone: http://localhost:7001/eureka instance: instance-id: microservicecloud-dept prefer-ip-address: true #访问路径可用显示ip info: app.name: dw-microservicecloud company.name: www.dw.com build.artifactId: $project.artifactId$ build.version: $project.version$ 在8001项目中添加服务发现接口,在controller包的DeptController中添加代码:
这里注意导包:import org.springframework.cloud.client.discovery.DiscoveryClient;
@Resource DiscoveryClient client; /* * 测试 用来查看eureka中所有可见的服务 开发中不需要写这个方法 * * getInstances("") 其中引用 application的名字 例如 我这里时CLOUD-DEPT */ @RequestMapping(value = "/dept/discovery",method = RequestMethod.GET) public Object discovery() { List<String> list =client.getServices(); System.out.println("====="+list); List<ServiceInstance> serList = client.getInstances("CLOUD-DEPT"); for(ServiceInstance element:serList) { System.out.println(element.getServiceId()+"\t"+element.getHost()); } return this.client; }
在 8001 主启动类 中添加注解 @EnableDiscoveryCilent 允许被发现
启动7001 项目 和 8001项目,输入http://localhost:8001/dept/discovery
这里显示的时已经注册到eureka中的服务,同时,控制台也有打印信息:
这里时输出已经注册到eureka中的服务
以上结束8001已经注册服务,那么80项目怎么才可用发现8001服务,接着继续
修改80 工程
找到controller中DeptController_Consumer
添加:
@RequestMapping(value = "/consumer/dept/discovery") public Object discovery() { return restTemplate.getForObject(REST_URL_PREFIX+"/dept/discovery", Object.class); }启动 80 项目,此时 7001 8001 80 都启动,输入http://localhost/consumer/dept/discovery
实现:
浏览器----->80项目----->restTemplate远程调用----->8001----->从eureka中获取所有已注册的项目信息
Eureka集群搭建
新建工程7002 和7003
把7001 项目中内容 copy到 7002 和7003
修改7002 和 7003 主启动类
修改映射配置信息
- 修改7001 7002 7003 yml
启动 7001 7002 7003 8001 输入http://eureka7001.com:7001/
输入http://eureka7002.com:7002/
输入http://eureka7003.com:7003/
现在,集群搭建成功。
Ribbon 负载均衡
spring cloud Ribbon 是基于Netflix Ribbon实现的一套客户端 负载均衡工具。 简单的说,Ribbon是NetFlix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将NetFlix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善额配置项 如连接超时,重试等。就是在配置文件中列出Load Balancer(简称LB)后面的所有机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
重点是客户端的 负载均衡
以上,只有80项目是客户端(消费者),所以要修改80项目,加上负载均衡
修改80的pom文件,添加Ribbon相关的依赖
修改80的yml文件,没有经过eureka,现在要经过eureka访问8001:
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/这样客户端就知道注册中心的位置了。
修改80项目的ConfigBean,添加注解即可:@LoadBalanced
这样 客户端就可以通过负载均衡来访问服务了,但是还没有配置完。
80 项目的主启动类加上注解:@EnableEurekaClient
修改80 项目的controller:要与注册中心的服务名相同
这样,客户端就通过注册中心找到服务端进行功能的调用。
启动7001 7002 7003,启动8001 ,启动客户端80:
输入http://localhost/consumer/dept/get/1
80项目(客户端)通过eureka访问 8001(服务端)提供的功能
浏览器输入url----->80客户端-----> 根据 eureka的 CLOUD-DEPT 找到服务,8001是根据此名 注册到了eureka标志
80在eureka中根据它找到8001,调用8001服务。
Ribbon在工作时分成两步: 第一步先选择EurekaServer,它优先选择在同一个区域内负责较少的server 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址
上图提供了三个服务实例,目前我们只有一个服务实例 8001,还不算真正意义上的客户端的负载均衡。
例如 在卖煎饼果子,现在只有张三一个服务人员在卖,客户没有办法选择去哪排队。
所以,现在要建服务端的集群,即8002 8003,加上8001,一个三个实例。
步骤:
新建8002和8003
8001pom yml 包 各种全部 copy到 8002 8003,并修改其启动类名字,yml各自对应各自的端口号
以及创建数据库,修改各自连接数据库的 表(这里是为了到时候区分,负载均衡访问效果做了此操作,往下看)
创建数据库:2 3
DROP database if EXISTS clouddb02; CREATE DATABASE clouddb02 character set utf8; USE clouddb02; CREATE TABLE dept ( deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, dname VARCHAR(60), db_source VARCHAR(60) ); INSERT into dept(dname,db_source) VALUES('开发部',DATABASE()); INSERT into dept(dname,db_source) VALUES('人事部',DATABASE()); INSERT into dept(dname,db_source) VALUES('财务部',DATABASE()); INSERT into dept(dname,db_source) VALUES('市场部',DATABASE()); INSERT into dept(dname,db_source) VALUES('运维部',DATABASE()); DROP database if EXISTS clouddb03; CREATE DATABASE clouddb03 character set utf8; USE clouddb03; CREATE TABLE dept ( deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, dname VARCHAR(60), db_source VARCHAR(60) ); INSERT into dept(dname,db_source) VALUES('开发部',DATABASE()); INSERT into dept(dname,db_source) VALUES('人事部',DATABASE()); INSERT into dept(dname,db_source) VALUES('财务部',DATABASE()); INSERT into dept(dname,db_source) VALUES('市场部',DATABASE()); INSERT into dept(dname,db_source) VALUES('运维部',DATABASE());测试:
先启动eureka 集群 3个,7001 7002 7003
然后启动 8001 8002 8003,可先输入http://localhost:8001/dept/list,http://localhost:8002/dept/list ,http://localhost:8003/dept/list ,查看能否查出数据,说明三个服务端正常访问
启动80,输入http://localhost/consumer/dept/list,显示结果:
第一次访问:
第二次刷新:
第三次刷新:
那么,这样就达到了效果,默认是轮询规则。
默认是轮询,改变算法,例如使用随机算法:
打开80项目中的配置类,ConfigBean,添加如下:
// RandomRule 随机 @Bean public IRule myRule() { return new RandomRule(); }达到随机访问。
Fegin
Feign就是代替RestTemplate
Feign 是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring cloud 对Feign进行了封装,使其支持了Spring mvc标准注解 和HttpMessageConverters.Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
步骤如下:这里文字描述,不截图了
此时可在80 项目中改,也可新建一个 做区分,掌握其中关键点即可
例如,80项目所有都copy到新的fegin项目目录下
此fegin 的pom中添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>修改api,api工程新建DeptClientService接口,并新增注解 @FeignClient
例如:@FeignClient(value = " CLOUD-DEPT")
然后写上接口方法,同8001
然后api 依照之前方式打包成jar
回到 feign工程,修改controller,注入DeptClientService ,该方法调用即可,之前是restTemplate
// 调用api项目中的service @Autowired DeptClientService deptClientService; @RequestMapping(value = "/consumer/dept/list") public List<Dept> list() { return this.deptClientService.list(); }在 fegin项目启动类加上注解:
@EnableFeignClients(basePackages = {“com.dw”}) @ComponentScan(“com.dw”)
测试 :
运行7001-7003,8001-8003,最后启动regin,输入http://localhost/consumer/dept/list
能正常访问,因为已封装了Ribbon,认采用轮询。
服务雪崩
多个微服务之间相互调用的时候,假设微服务A调用微服务B,微服务B调用微服务C.C又调用其他的服务,这就是“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的服务器资源,进而引起系统崩溃,这就是“雪崩效应”。
熔断机制是应对服务雪崩的一种微服务链路保护机制。当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在springCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的情况。当失败的调用到一定阈值,默认是5秒内20此调用失败会启动熔断机制。
步骤如下:参考主要
之前的8001举例,
添加pom依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>修改controller,这里为了模拟出错 然后返回信息效果写了如下代码:
@RequestMapping(value = "/dept/get/{deptno}",method = RequestMethod.GET) @HystrixCommand(fallbackMethod = "processHystrix_Get") public Dept getById(@PathVariable("deptno")Long deptno) { Dept dept = deptService.findById(deptno); // 故意出错 if(null == dept) { throw new RuntimeException("该id没有对应的信息"); } return dept; } public Dept processHystrix_Get(@PathVariable("deptno")Long deptno) { Dept dept = new Dept(); dept.setDeptno(deptno); dept.setDname("没有对应的信息"); dept.setDb_source("no this db"); return dept; }然后再hystrix主启动类添加 注解**@EnableCircuitBreaker**
测试,启动三个eureka,启动该hystrix项目,再启动80
输入http://localhost/consumer/dept/get/112,一个不存在的id
显示:
说明熔断机制起了作用。
