一,什么是微服务
微服务的核心就是将传统的一站式应用,根据业务拆分成一个个的服务,彻底去解耦,每一个微服务提供单个业务功能得服务,一个服务做一件事情,从技术角度上看,就是一种小而独立的处理过程,能够自行单独启动或销毁,拥有自己独立的数据库
用官方的话来说
基于springboot提供了一套微服务解决方案,包括服务注册与发现没配置中心,全链路监控 服务网关,负载均衡,熔断器等组件。基于NetFlix的开源组件做高度抽象封装之外,还有一些 选型中立的开源组件
在学习每一门的新技术之前,都离不开官方文档,如一下参考文档
https://springcloud.cc/spring-cloud-netflix.html
中文api文档: https://springcloud.cc/spring-cloud-dalston.html
springcloud中国社区:http://springcloud.cn/
springcloud中文网:https://springcloud.cc
二,谈谈springcloud和springboot之间的区别
springboot是专注于快速方便的开发单个个体微服务
springcloud是关注全局的微服务协调整理治疗框架,他将Springboot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,时间总线,全局锁,决策竞选,分布式会话等等集成服务
springboot可以离开springCloud独立使用,但是springcloud离不开springboot,属于依赖关系
但是在学习springcloud之前,一定需要先了解springboot的使用
三,这里因为是快速入门,因此以EUREKA的简单集群版作为演示
如图所示,以Eureka作为服务注册中心
四,约定 > 配置 > 编码
1,勾选注解生效
文件编码改为utf-8
java编译器改为8
五,父工程(主项目)的构建
1,选择site目录
2,创建项目之后,修改运行方式,将run运行方式设置为run Bashboard运行方式,即修改 .idea 目录下的 wrokspace.xml 文件,在文件中添加:
<option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType" /> </set> </option>
搜索RunDashboard即可
3,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.atgui.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <!--子模块--> <modules> <module>provider-payment-8001</module> <module>consumer-order-80</module> <module>api-commons</module> <module>eureka-server-7001</module> <module>eureka-server-7002</module> <module>provider-payment-8011</module> </modules> <!--不再是默认的jar--> <packaging>pom</packaging> <!-- 统一管理jar包版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.21</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version --> <dependencyManagement> <dependencies> <!--spring boot 2.2.2--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!--spring cloud Hoxton.SR1--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <!--spring cloud alibaba 2.1.0.RELEASE--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <addResources>true</addResources> </configuration> </plugin> </plugins> </build> </project>
六,目录总结构
七,先说说这个通用的api-commons
由于全局只需要这一个对象,因此把他抽取出来
1,pom.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atgui.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-api-commons</artifactId> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.0</version> </dependency> </dependencies> </project>
2,实体类CommonResult
package com.zheng.api.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class CommonResult<T> { private Integer code; //类似404 private String message; //信息描述 private T data; // public CommonResult(Integer code,String message){ this(code,message,null); } }
3,实体类Payment
package com.zheng.api.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @NoArgsConstructor @AllArgsConstructor public class Payment implements Serializable { private long id; private String serial; @Override public String toString() { return "Payment{" + "id=" + id + ", serial='" + serial + '\'' + '}'; } }
八,一个服务注册中心集群由两个服务注册中心组成,分别为eureka-server-7001和eureka-server-7002
1,修改本地端口号路径映射:C:\WINDOWS\system32\drivers\etc\host
在里面加入,如果加不了,则修改属性将只读改为读写
2,新建maven模块,使用的普通的即可。以下需要注意一下,将sdk改成如下
3,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atgui.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>eureka-server-7001</artifactId> <dependencies> <!--eureka的服务端包--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!-- boot web actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <!--引入自己定义的 api 通用包--> <groupId>com.atgui.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
4,application.yml
server: port: 7001 eureka: instance: #eureka服务端的实例名称,最后就是用来注册到注册中心,名字随意 hostname: eureka7001.com client: #false表示不向注册中心注册自己 register-with-eureka: false #false表示自己端就是注册中心,职责是维护实例,并不需要检索服务 fetch-registry: false service-url: #设置与Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址 #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ defaultZone: http://eureka7002.com:7002/eureka/ # defaultZone: http://eureka7002.com:7002/eureka/ #集群指向其他eureka server: # 关闭自我保护机制,保证不可用服务被及时剔除.默认开启保护机制 true enable-self-preservation: false eviction-interval-timer-in-ms: 2000
5,在主启动类上加入注解
//开启eureka注解,代表服务中心 @EnableEurekaServer
6,以上是其中一个注册中心,而另一个注册中心复制一模一样的代码即可,只需要修改application.yml下的端口号即可
九,两个服务提供者,分别是provider-payment-8001和provider-payment-8011
1,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atgui.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>provider-payment-8001</artifactId> <dependencies> <!--包含了sleuth+zipkin;sleuth链路监控展现,P94所需依赖--> <!--<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> --> <!--客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <!--引入自己定义的 api 通用包--> <groupId>com.atgui.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--下面两个一定是绑定在一块出现--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
2,application.yml
server: port: 8001 spring: application: name: payment-service #该名字可以随意起,主要用于注册中心的调用 #该名字可以随意起,主要用于注册中心的调用 datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/springcloud?serverTimezone=GMT%2B8&useSSL=false username: root password: zhs03171812 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.zheng.payment.pojo #引入jar包后 eureka: client: #表示向注册中心注册自己 register-with-eureka: true #false表示自己端就是注册中心,职责是维护实例,并不需要检索服务 fetch-registry: true service-url: #设置与Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址 #defaultZone: http://localhost:7001/eureka #单机版 defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #集群版 instance: instance-id: payment8001 #将主机名替换 prefer-ip-address: true
3,由于服务提供者需要与数据库进行操作,则新建数据库,数据随便插入几个即可,方便用于测试
4,分别新建controller,service,pojo,mapper包
PaymentMapper接口
package com.zheng.payment.mapper; import com.zheng.api.pojo.Payment; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Mapper @Repository public interface PaymentMapper { //插入数据 public int toInsert(Payment payment); //通过id查询数据 public Payment getPaymentById(@Param("id") Long id); }
PaymentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zheng.payment.mapper.PaymentMapper"> <insert id="toInsert" parameterType="com.zheng.api.pojo.Payment" useGeneratedKeys="true" keyProperty="id"> insert into payment (serial) values (#{serial}) </insert> <resultMap id="BaseResultMap" type="com.zheng.api.pojo.Payment"> <id column="id" property="id" jdbcType="BIGINT"></id> <id column="serial" property="serial" jdbcType="VARCHAR"></id> </resultMap> <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap"> select * from payment where id = #{id} </select> </mapper>
PaymentServiceI接口
package com.zheng.payment.service; import com.zheng.api.pojo.Payment; import org.apache.ibatis.annotations.Param; public interface PaymentServiceI { //插入数据 public int toInsert(Payment payment); //通过id查询数据 public Payment getPaymentById(@Param("id") Long id); }
PaymentServiceImpl
package com.zheng.payment.service; import com.zheng.api.pojo.Payment; import com.zheng.payment.mapper.PaymentMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PaymentServiceImpl implements PaymentServiceI { @Autowired private PaymentMapper paymentMapperl; @Override public int toInsert(Payment payment) { return paymentMapperl.toInsert(payment); } @Override public Payment getPaymentById(Long id) { return paymentMapperl.getPaymentById(id); } }
主启动类上加入注解
@EnableEurekaClient @EnableDiscoveryClient
PaymentController
package com.zheng.payment.controller; import com.zheng.api.pojo.CommonResult; import com.zheng.api.pojo.Payment; import com.zheng.payment.service.PaymentServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @Slf4j public class PaymentController { @Autowired private PaymentServiceImpl paymentService; //向外暴露服务 @Autowired private DiscoveryClient discoveryClient; @PostMapping(value = "/payment/toInsert") public CommonResult toInsert(@RequestBody Payment payment){ int result = paymentService.toInsert(payment); log.info("**********插入结果是" + result + "qaq"); //无论数据是否插入成功,都得将数据给返回前端 if(result > 0){ return new CommonResult(200,"数据插入成功",result); }else{ return new CommonResult(444,"插入数据失败",null); } } @GetMapping(value = "/payment/get/{id}") public CommonResult getPaymentById(@PathVariable("id") Long id){ Payment payment = paymentService.getPaymentById(id); log.info("**********查询的结果是" + payment + "qaq"); //无论数据是否插入成功,都得将数据给返回前端 //return new CommonResult(200,"数据查询成功!",payment); if(payment != null){ return new CommonResult(200,"数据查询成功",payment); }else{ return new CommonResult(444,"插入查询失败",null); } } @GetMapping("payment/discovery") public DiscoveryClient getDiscoveryClient(){ List<String> services = discoveryClient.getServices(); services.forEach(e-> System.out.println(e)); List<ServiceInstance> instances = discoveryClient.getInstances("PAYMENT-SERVICE"); instances.forEach(e-> System.out.println(e)); return this.discoveryClient; } @GetMapping(value = "qaq") public String qaq(){ return "hello world"; } }
5,新创建一个新的模块作为服务提供者,只需修改端口号即可,其他代码一致
十,消费者consumer-order-80
1,pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atgui.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer-order-80</artifactId> <dependencies> <!--包含了sleuth+zipkin;sleuth链路监控展现,P94所需依赖--> <!--<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>--> <!-- 引入eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <!--引入自己定义的 api 通用包--> <groupId>com.atgui.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
2,ApplicationContextConfig.class
package com.zheng.order.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration //告诉springboot这是一个配置类,相当于以前在spring中的applicationContext.xml配置文件 public class ApplicationContextConfig { //既然是相当于以前的xml的配置文件,那么就可以在里面注入对象了 @Bean @LoadBalanced //开启负载均衡机制 public RestTemplate getRest(){ return new RestTemplate(); } }
3,主程序类中添加注解
@EnableEurekaClient
4,编写OrderController类
package com.zheng.order.controller; import com.zheng.api.pojo.CommonResult; import com.zheng.api.pojo.Payment; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @Slf4j public class OrderController { @Autowired private RestTemplate restTemplate; public static final String URL = "http://payment-service/"; @GetMapping("/consumer/payment/toInsert") public CommonResult<Payment> toInsert(@RequestBody Payment payment){ return restTemplate.postForObject(URL + "payment/toInsert",payment,CommonResult.class); } @GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPayment(@PathVariable Long id){ return restTemplate.getForObject(URL+"payment/get/" + id,CommonResult.class); } }
十一,测试
新开启eureka两个服务注册中心,在两个开启服务提供者,再开启一个消费者,然后五个微服务全部开启
开始测试,由于修改了本地端口,就可以直接使用本地端口进行测试了
输入:http://eureka7002.com:7002/ 和http://eureka7001.com:7001/
就可以发现服务以及注册到注册中心了
接下来测试客户端能否拿到服务端的数据
测试暴露的服务
测试插入数据
那肯定也保存在数据库中了!
十二,总结
简单的Eureka实现服务中心就成功地部署了!