认识微服务
微服务架构演变:
单体架构:所有功能集中在一个项目中开发,打成一个包部署
分布式架构:就是各功能模块的代码不在同一个项目中写了,到时候修改其中一个过能的代码,对另一个功能完全没有任何影响(如果在一个项目中,修改这个功能的代码,就得将所有功能代码给重新编译)
服务治理
问题:
服务拆分力度
服务集群地址如何维护
服务之间如何实现远程 调用
服务健康状态如何感知
微服务
良好架构设计的分布式架构方案,微服务架构特征:
单一职责:功能单一
面向服务:对外暴露接口(让其他服务调用)
自治:团队独立,技术独立,数据独立,部署独立
隔离降级:服务做好隔离,容器,降级,避免出现级联问题
维护服务节点信息 – 注册中心
微服务配置的修改 – 配置中心
用户访问的微服务 – 服务网关
微服务间调用报错 – 服务保护
微服务入门案例
服务拆分与远程调用
服务拆分
拆分原则
这里我总结了微服务拆分时的几个原则:
- 不同微服务,不要重复开发相同业务
- 微服务数据独立,不要访问其它微服务的数据库
- 微服务可以将自己的业务暴露为接口,供其它微服务调用
入门案例
- 创建父项目
- 导入依赖:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.zjh</groupId><artifactId>cloud-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>cloud-demo</name><description>cloud-demo</description><!--父工程--><packaging>pom</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>8</java.version></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><dependencyManagement><dependencies><!-- springCloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR10</version><type>pom</type><scope>import</scope></dependency><!-- mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope><version>8.0.15</version></dependency><!-- mybatis --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.5</version></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
注意:
这里的环境是jdk8(建议大家和我一样)
- 创建子项目user-service
- 导入依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.zjh</groupId><artifactId>cloud-demo</artifactId><version>0.0.1-SNAPSHOT</version></parent><groupId>com.zjh</groupId><artifactId>user-service</artifactId><version>0.0.1-SNAPSHOT</version><name>user-service</name><description>user-service</description><properties><java.version>8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
- 创建实体类,mapper,service,controller
- 添加mybatis-plus包扫描配置
@SpringBootApplication
@MapperScan("com.zjh.userservice.mapper")
public class UserServiceApplication {public static void main(String[] args) {SpringApplication.run(UserServiceApplication.class, args);}}
- yml配置设置
server:port: 8089
spring:application:name: userservicedatasource:url: jdbc:mysql://localhost:3306/cloud?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: 888888driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truetype-aliases-package: com.zjh.order.pojo
- 创建子项目order-service
和user-service一模一样,唯一的区别是,需要再order-service中添加user-service的依赖
<dependency><groupId>com.zjh</groupId><artifactId>user-service</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
远程调度
远程调用就是在代码中访问到另一个项目的地址,这样就可以通过url来使用对方的方法,从而得到对方的信息
我们这里order中查询中的user属性直接查询时得不到的,需要通过访问user的信息,通过user-service中的getById()来得到,所以我们远程调度user-service项目
步骤:
- 在OrderServiceApplication中将RestTemplate添加到bean
@SpringBootApplication
@MapperScan("com.hwadee.order.mapper")
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
- 调用RestTemplate来实现远程调度
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public Order queryOrderById(Long orderId) {// 1.查询订单Order order = orderMapper.findById(orderId);// 2.利用RestTemplate发起http请求,查询用户// 2.1.url路径String url = "http://localhost:8081/user/" + order.getUserId();// 2.2.发送http请求,实现远程调用User user = restTemplate.getForObject(url, User.class);// 3.封装user到Orderorder.setUser(user);// 4.返回return order;}
}
支持请求:get,post,
存在问题:
- order-service在发起远程调用的时候,该如何得知user-service实例的ip地址和端口?
- 有多个user-service实例地址,order-service调用时该如何选择?
- order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
我们接下来就使用用户中心来看一看这个问题
Eureka注册中心
介绍
eureka工作流程
调用者就是服务消费者,被调用的对象就是服务提供者。注册中心,就是我们将服务提供者的路径给存到注册中心中,当服务消费者每次调用对面的服务,就通过注册中心来访问对面的服务
使用步骤
创建注册中心项目
- 添加依赖
spring-cloud-starter-netflix-eureka-server
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.hwadee</groupId><artifactId>cloud-demo</artifactId><version>0.0.1-SNAPSHOT</version></parent><artifactId>eureka-server</artifactId><dependencies><!--eureka服务端--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId><version>2.2.10.RELEASE</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
- 编写eureka启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
- 编写服务配置文件
server:port: 10086
spring:application:name: eureka-server
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eureka
- 启动服务
启动微服务,然后在浏览器访问:http://127.0.0.1:10086
注册服务
- 在user-service的pom文件中,引入下面的eureka-client依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId><version>2.2.10.RELEASE</version>
</dependency>
- 修改服务配置文件,向eureka注册服务,并添加名字
server:port: 8081spring:application: # 给服务起名字name: user-servicedatasource:url: jdbc:mysql://localhost:3306/hwadee?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver# 向eureka注册服务
eureka:client:service-url:defaultZone: http://127.0.0.1:10086/eurekamybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truetype-aliases-package: com.hadee.user.pojo
使用
- 在order-service的pom文件中,引入下面的eureka-client依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId><version>2.2.10.RELEASE</version>
</dependency>
- 修改服务配置文件,向eureka注册服务,并添加名字
- 添加@LoadBalanced注解
- @LoadBalanced,进行轮循(将同一个名字的服务进行轮询访问)
@Bean
@LoadBalanced
public RestTemplate restTemplate() {return new RestTemplate();
}
- 使用
public Order queryOrderById(Long orderId) {// 1.查询订单Order order = orderMapper.findById(orderId);// 2.利用RestTemplate发起http请求,查询用户// 2.1.url路径String url = "http://user-service/user/" + order.getUserId();// 2.2.发送http请求,实现远程调用User user = restTemplate.getForObject(url, User.class);// 3.封装user到Orderorder.setUser(user);// 4.返回return order;
}
Ribuu负载均衡
Nacos注册中心
nacos介绍和安装
- 安装
- 运行
打开安装的nacos文件夹,在bin文件上建立终端页,输入以下语句启动
sh startup.sh -m standalone
- 访问
nacos注册中心地址: http://127.0.0.1:8848/nacos/index.html#/login (账号密码都是nacos)
如果读取不到,可以尝试将nacos注册中心中的内容删除,然后重新运行项目
简单使用
- 引入依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.5.RELEASE</version><type>pom</type><scope>import</scope>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 添加配置(user和order的.xml都需要)
spring:cloud:nacos:server-addr: 127.0.0.1:8848
Nacos配置管理
将配置放到nacos中
springboot启动加载配置文件顺序
bootstrap.yml
application.properties
application.yml
增加dev,创建开发环境
dev – namespace 开发环境
public – namespace 生产环境
远程调用OpenFeign
- 引入依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.10.RELEASE</version>
</dependency>
- 添加注解@EnableFeignClients
@SpringBootApplication
@MapperScan("com.zjh.orderservice.mapper")
@EnableFeignClients
public class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}
}
- 编写Feign的接口类
@FeignClient("user-service")
public interface UserClient {@GetMapping("/user/{id}")User findById(@PathVariable("id") Long id);
}
- 使用
public Order queryOrderById(Long orderId) {// 1.查询订单Order order = orderMapper.findById(orderId);// 2.设置用户// 2.1 查询用户User user = userClient.findById(order.getUserId());// 2.2 设置userorder.setUser(user);// 3.返回return order;
}
和使用mapper类似,就是调用接口的方法