单体架构:所有功能集中在一个项目中开发,打成一个包部署
分布式架构:就是各功能模块的代码不在同一个项目中写了,到时候修改其中一个过能的代码,对另一个功能完全没有任何影响(如果在一个项目中,修改这个功能的代码,就得将所有功能代码给重新编译)
问题:
服务拆分力度
服务集群地址如何维护
服务之间如何实现远程 调用
服务健康状态如何感知
良好架构设计的分布式架构方案,微服务架构特征:
单一职责:功能单一
面向服务:对外暴露接口(让其他服务调用)
自治:团队独立,技术独立,数据独立,部署独立
隔离降级:服务做好隔离,容器,降级,避免出现级联问题
维护服务节点信息 – 注册中心
微服务配置的修改 – 配置中心
用户访问的微服务 – 服务网关
微服务间调用报错 – 服务保护
这里我总结了微服务拆分时的几个原则:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.12.RELEASE
com.zjh
cloud-demo
0.0.1-SNAPSHOT
cloud-demo
cloud-demo
pom
UTF-8
UTF-8
8
org.projectlombok
lombok
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR10
pom
import
mysql
mysql-connector-java
runtime
8.0.15
com.baomidou
mybatis-plus-boot-starter
3.5.5
org.springframework.boot
spring-boot-maven-plugin
注意:
这里的环境是jdk8(建议大家和我一样)
4.0.0
com.zjh
cloud-demo
0.0.1-SNAPSHOT
com.zjh
user-service
0.0.1-SNAPSHOT
user-service
user-service
8
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
com.baomidou
mybatis-plus-boot-starter
org.springframework.boot
spring-boot-maven-plugin
@SpringBootApplication
@MapperScan("com.zjh.userservice.mapper")
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
server:
port: 8089
spring:
application:
name: userservice
datasource:
url: jdbc:mysql://localhost:3306/cloud?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: 888888
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
type-aliases-package: com.zjh.order.pojo
和user-service一模一样,唯一的区别是,需要再order-service中添加user-service的依赖
com.zjh
user-service
0.0.1-SNAPSHOT
远程调用就是在代码中访问到另一个项目的地址,这样就可以通过url来使用对方的方法,从而得到对方的信息
我们这里order中查询中的user属性直接查询时得不到的,需要通过访问user的信息,通过user-service中的getById()来得到,所以我们远程调度user-service项目
步骤:
@SpringBootApplication
@MapperScan("com.hwadee.order.mapper")
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private 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到Order
order.setUser(user);
// 4.返回
return order;
}
}
支持请求:get,post,
存在问题:
我们接下来就使用用户中心来看一看这个问题
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>
@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
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
server:
port: 8081
spring:
application: # 给服务起名字
name: user-service
datasource:
url: jdbc:mysql://localhost:3306/hwadee?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 向eureka注册服务
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
type-aliases-package: com.hadee.user.pojo
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
@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到Order
order.setUser(user);
// 4.返回
return order;
}
打开安装的nacos文件夹,在bin文件上建立终端页,输入以下语句启动
sh startup.sh -m standalone
nacos注册中心地址: http://127.0.0.1:8848/nacos/index.html#/login (账号密码都是nacos)
如果读取不到,可以尝试将nacos注册中心中的内容删除,然后重新运行项目
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.5.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
springboot启动加载配置文件顺序
bootstrap.yml
application.properties
application.yml
dev – namespace 开发环境
public – namespace 生产环境
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.2.10.RELEASEversion>
dependency>
@SpringBootApplication
@MapperScan("com.zjh.orderservice.mapper")
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
@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 设置user
order.setUser(user);
// 3.返回
return order;
}
和使用mapper类似,就是调用接口的方法