赞
踩
服务架构演变
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
分布式结构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务
微服务是一种经过良好架构设计的分布式架构方案,微服务特征:
SpringCloud
服务拆分注意事项
1.不同微服务,不要开发重复相同业务
2.微服务数据独立,不要访问其他微服务的数据库
3.微服务可以将自己的业务暴露为接口,供其它微服务调用
案例:根据订单id查询订单功能
如上:按需求需要订单模块调用用户模块
原本订单模块的用户信息是Null ,模块相互之间又是隔离的,所以订单模块不能直接调用用户模块,如果订单模块能像客户端一样发起一个对用户模块的请求访问不就得到用户的信息了吗,再和订单模块的结果合并,就是一个完整的订单信息,那又怎样在订单模块发起对用户模块的请求呢?
如下:
2)服务远程调用RestTemplate
修改order-service中的OrderService中的queryOrderById方法:
- @RestController
- @RequestMapping("order")
- public class OrderController {
-
- @Autowired
- private OrderMapper orderMapper;
- @Autowired
- private RestTemplate restTemplate;
-
- @GetMapping("{orderId}")
- public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
- // 1.查询订单
- Order order = orderMapper.findById(orderId);
- //2.获取用户id
- Long userId = order.getUserId();
- //3.利用RestTemplate发送http请求查询用户
- //3.1url路径
- String url = "http://localhost:8081/user/" + userId;
- //3.2发送http:请求,实现远程调用
- User user = restTemplate.getForObject(url, User.class);
- //4.封装user到Order
- order.setUser(user);
- //5.返回
- return order;
- }
- }
提供者与消费者
提供者与消费者角色是相对的
环境的不同访问地址也会不同
集群部署多个时,硬编码只能访问一个,其他无法访问
1.创建项目,引依赖 2.在启动类上加注解 3.配置文件里配置信息
- <!--eureka服务端-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
- </dependency>
- @EnableEurekaServer
- @SpringBootApplication
- public class EurkaApplication {
- }
- server:
- port: 10086 #服务端口
- #以下为了做服务注册
- spring:
- application:
- name: eurekaserver
- eureka:
- client:
- service-url:
- defaultZone: http://localhost:10086/eureka
eureka也是微服务,所以也会将自己注册上
注册user-service步骤如下:
1.在user-service项目引入spring-cloud-starter-nerflix-erueka-client依赖
- <!--eureka依赖-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
- </dependency>
2.在application.yml文件,编写下面的配置:
eureka和spring同级
- application:
- name: userservice
- eureka:
- client:
- service-url:
- dafaultZone: http://localhost:10086/eureka/
服务注册步骤
引入eureka-client依赖
在applicaion.ym中配置eureka地址
在oredier-service完成服务拉取
服务拉取是基于服务名称获取服务列表,然后再对服务列表做负载均衡
1.修改OrderService代码,修改访问的url路径,用服务名代替ip、端口:
String url = "http://userservice/user/" + userId;
2.在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解:
- @Bean
- @LoadBalanced
- public RestTemplate restTemplate(){
- return new RestTemplate();
- }
总结:
1.搭建EurekaServer
2.服务注册
3.服务发现
Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:
通过定义IRule实现可以修改负载均衡规则,有两种方式:
1.代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
- @Bean
- public IRule randomRule(){
- return new RandomRule();
- }
2.配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
- userserve:
- ribbon:
- NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
方式一:针对全局微服务,order访问任何服务器都是
方式二:只针对order访问的某个微服务
懒加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
服务注册到Nacos
1.在cloud-demo父工程中添加spring-cloud-alibaba的管理依赖:
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-dependencies</artifactId>
- <version>2.2.5.RELEASE</version>
- </dependency>
1.在父工程中添加spring-cloud-alibaba的管理依赖
- <!--nacos管理依赖-->
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-dependencies</artifactId>
- <version>2.2.5.RELEASE</version>
- </dependency>
2.注释order-service和user-service中的eureka依赖以及yml中的配置
3.添加nacos客户端依赖:
- <!--nacos客服端依赖-->
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
- <version>2.2.5.RELEASE</version>
- </dependency>
4.修改user和order-service中的application.yml,注释eureka地址,添加nacos地址:
cloue和application同级
- cloud:
- nacos:
- server-addr: localhost:8848 # nacos服务地址
1.Nacon服务搭建
下载安装包、解压、在bin目录下运行:startup.cmd -m standalone
2.Nacos服务注册或者发现
引入nacos.discovery依赖、配置nacos地址spring.cloud.nacos.server-addr
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高
本地集群不可访问时,再访问其他集群
1.修改application.yml,添加如下内容:
- cloud:
- nacos:
- server-addr: localhost:8848 # nacos服务地址
- discovery:
- cluster-name: SH
2.在Nacos控制台可以看到集群变化
1.Naocs服务分级存储模型
2.如何设置实例的集群属性
.修改application.yml文件,添加spring.cloud.nacos.discovery.cluster-name属性即可
NacosRule负载均衡策略
根据权重负载均衡
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离
1.在Nacos控制台可以创建namespace,用来隔离不同环境
2.然后填写一个新的命名空间信息:
3.保存后在控制台看到这个命名空间的id
4.修改order-service的application.yml,添加namespace:
- server:
- port: 8080
- spring:
- datasource:
- url: jdbc:mysql://localhost:3306/heima?useSSL=false
- username: root
- password: 1234
- driver-class-name: com.mysql.jdbc.Driver
- application:
- name: orderservice
- cloud:
- nacos:
- server-addr: localhost:8848 # nacos服务地址
- discovery:
- cluster-name: HZ
- # namespace: 8eae1788-ee70-45b6-b5ad-4500d3d5f902 # 环境
1.Nacos环境隔离
临时实例和非临时实例
服注册到Nacos时,可以选择注册为临时或非临时实例,通过下面的配置来设置:
Nacos与eureka的共同点
2.Nacos与Eureka的区别
- cloud:
- nacos:
- server-addr: localhost:8848 # nacos服务地址
- discovery:
- cluster-name: HZ
- ephemeral: false # 是否为临时实例
统一配置管理
f
添加配置
1.引入Nacos的配置管理客户端依赖:
- <!--nacos配置管理依赖-->
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
- <version>2.2.5.RELEASE</version>
- </dependency>
2.在userservice中的resource目录添加一个bootstrap.yml文件,这个文件时引导文件,优先级高于application.yml
- spring:
- application:
- name: userserver
- profiles:
- active: public # 环境
- cloud:
- nacos:
- server-addr: localhost:8848 # nacos 地址
- config:
- file-extension: yaml # 文件后缀名
配置热更新
Nacos中的配置文件变更后,微服务无需重启就可以感知。不过需要下面两种配置实现:
- @RefreshScope
- @RequestMapping("/user")
- public class UserController {
-
- @Autowired
- private UserService userService;
- @Value("${pattern.dateformat}")
- private String dateformat;
-
- @GetMapping("now")
- public String now(){
- return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
- }
- }
- import lombok.Data;
- import org.springframework.boot.context.properties.ConfigurationProperties;
- import org.springframework.stereotype.Component;
-
- @Data
- @Component
- @ConfigurationProperties(prefix = "pattern")
- public class PatternProperties {
- private String dateformat;
- }
配置共享
RestTemplate方式调用存在的问题
存在问题:
第一步导入依赖
- <!--Fegin客户端依赖-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-openfeign</artifactId>
- </dependency>
第二步在启动类上添加注解@EnableFeignClients
-
- @MapperScan("cn.itcast.order.mapper")
- @SpringBootApplication
- @EnableFeignClients
- public class OrderApplication {
- }
第三步接口声明
- package cn.itcast.order.clients;
- import cn.itcast.order.pojo.User;
- import org.springframework.cloud.openfeign.FeignClient;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
-
- @FeignClient("userserver")
- public interface UserClient {
- @GetMapping("/user/{id}")
- User findById(@PathVariable("id") Long id);
- }
第四步调用
- @Autowired
- private UserClient userClient;
-
- User user = userClient.findById(userId);
Fegin运行自定义配置来覆盖默认配置,可以修改的配置如下:
一般需要配置的就是日志级别.
修改Fegin日志有两种方式:
方式一:配置文件方式
方式二:java代码方式,需要先声明一个Bean:
Feign底层的客户端实现:
因此优化Feign的性能主要包括:
① 使用连接池代替默认的URLConnection
② 日志级别,最好用basic或none
Feign的性能优化-连接池配置
Feign添加HttpClient的支持:
引依赖
- <!--引入HttpClient依赖-->
- <dependency>
- <groupId>io.github.openfeign</groupId>
- <artifactId>feign-httpclient</artifactId>
- </dependency>
配置连接池
- feign:
- httpclient:
- enabled: true # 支持httpclient的开关
- max-connections: 200 # 最大连接数
- max-connections-per-route: 50 # 单个路径最大连接数
Feign的最佳实践
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
方式二(抽取):将FeigenClient抽取为独立模块,并且把接口有关的POJO、默认的Fegin配置都放到这个模块中,提供给所有消费者使用
抽取FeignClient
实现最佳实践方式二的步骤如下:
1.首先创建一个module,命名为feign-api,然后引入feign的starter依赖
2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3.在order-service中引入feign-api的依赖
4.修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api的包
5.重启测试
当定义的FeignClient不在SpringBootApplication扫描包范围时,这些FeignClient无法使用。有两种方式解决:
方式一:指定FeignClient所在包(批量)
方式二:指定FeignClien字节码(指定client)
@EnableFeignClients(clients = UserClient.class)
搭建网关服务
1.创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:
- <dependencies>
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
- <version>2.2.5.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
- </dependencies>
2.编写路由配置及nacos地址
- server:
- port: 10010
- spring:
- application:
- name: gateway
- cloud:
- nacos:
- server-addr: localhost:8848 # nacos地址
- gateway:
- routes:
- - id: user-service # 路由标识,必须唯一
- uri: lb://userserver # 路由的目标地址
- predicates: #路由断言,判断请求是否符合规则
- - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是,则符合
- - id: order-service
- uri: lb://orderservice
- predicates:
- - Path=/order/**
网关路由可以配置的内容包括:
过滤器:针对某个服务
- gateway:
- routes:
- - id: user-service # 路由标识,必须唯一
- uri: lb://userserver # 路由的目标地址
- predicates: #路由断言,判断请求是否符合规则
- - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是,则符合
- filters:
- - AddRequestHeader=Truth,Itcast is freaking aowsome!
默认过滤器:对所有微服务都生效
- gateway:
- routes:
- - id: user-service # 路由标识,必须唯一
- uri: lb://userserver # 路由的目标地址
- predicates: #路由断言,判断请求是否符合规则
- - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是,则符合
- # filters:
- # - AddRequestHeader=Truth,Itcast is freaking aowsome!
- - id: order-service
- uri: lb://orderservice
- predicates:
- - Path=/order/**
- default-filters:
- - AddRequestHeader=Truth,Itcast is freaking aowsome!
过滤器的作用是什么?
defaultFilters的作用是什么?
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFile的作用一样.
区别于GatewayFile通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口
案例
步骤一:自定义过滤器
自定义类,实现GlobalFilter接口,添加@Order注解:
- @Order(-1)
- @Component
- public class AuthorizeFilter implements GlobalFilter {
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
-
- //1.获取请求参数
- ServerHttpRequest request = exchange.getRequest();
- MultiValueMap<String, String> params = request.getQueryParams();
-
- //2.获取参数的authorization参数
- String authorization = params.getFirst("authorization");
- //3.判断参数是否等于admi
- if("admin".equals(authorization)){
- //4.是,放行
- return chain.filter(exchange);
- }
-
- //5.否,拦截
- //5.1设置状态码
- exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
- return exchange.getResponse().setComplete();
- }
- }
跨域:域名不一致就是跨域,主要包括:
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可实现:
Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?
Docker如何解决开发、测试、生产环境有差异的问题
镜像(Image): Docker将应用程序及其所需要的依赖、函数库、环境、配置等文件打包在一起,称为镜像.
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器做隔离,对外不可见。
Docker和DockerHub
Docker是一个CS架构的程序,由两部分组成:
拉取镜像
docker pull nginx
查看镜像
docker images
案例利用docker save 将nginx镜像导出磁盘,然后再通过load加载回款
- docker save --help
-
- docker save -o nginx.tar nginx:latest
-
- docker rmi nginx:latest
-
- docker load -i nginx.tar
练习
- docker pull redis
- docker images
- docker save -o redis.tar redis:latest
- docker rmi redis:latest
- docker load -i redis.tar
案例
步骤一:去docker hub查看Nginx的容器运行命令
docker run --name mn -p 80:80 -d nginx
容器与数据耦合问题
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。
案例:创建一个数据卷,并查看数据卷在宿主机的目录位置
1.创建数据卷
- docker volume create html
-
2.查看所有数据
docker volume ls
3.查看数据卷详细信息卷
- [root@VM-8-6-centos ~]# docker volume inspect html
- [
- {
- "CreatedAt": "2023-10-23T15:14:30+08:00",
- "Driver": "local",
- "Labels": null,
- "Mountpoint": "/var/lib/docker/volumes/html/_data",
- "Name": "html",
- "Options": null,
- "Scope": "local"
- }
- ]
在创建容器是,可以通过-v参数来挂载一个数据卷到某个容器目录
docker run --name mn -p 80:80 -v html:/var/lib/docker/volumes/html/_data -d nginx
- docker inspect html
- cd /var/lib/docker/volumes/html/_data
- vi index.html
- docker run \
- --name mysql \
- -e MYSQL_ROOT_PASSWORD=123 \
- -p 3306:3306 \
- -v /tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf \
- -v /tmp/mysql/data:/var/lib/mysql \
- -d \
- mysql:5.7.25
Dockerfile就是一个文本文件,其中包含一个个的指令,用指令来说明要执行什么操作来构建镜像,每个指令都会形成一层Layer.
- # 指定基础镜像
- FROM ubuntu:16.04
- # 配置环境变量,JDK的安装目录
- ENV JAVA_DIR=/usr/local
-
- # 拷贝jdk和java项目的包
- COPY ./jdk8.tar.gz $JAVA_DIR/
- COPY ./docker-demo.jar /tmp/app.jar
-
- # 安装JDK
- RUN cd $JAVA_DIR \
- && tar -xf ./jdk8.tar.gz \
- && mv ./jdk1.8.0_144 ./java8
-
- # 配置环境变量
- ENV JAVA_HOME=$JAVA_DIR/java8
- ENV PATH=$PATH:$JAVA_HOME/bin
-
- # 暴露端口
- EXPOSE 8090
- # 入口,java项目的启动命令
- ENTRYPOINT java -jar /tmp/app.jar
docker build -t javaweb:1.0 .
初始DockerCompose
简化版镜像仓库
- docker run -d \
- --restart=always \
- --name registry \
- -p 5000:5000 \
- -v registry-data:/var/lib/registry \
- registry
图形化界面
创建docker compose文件docker-compose.yml
- version: '3.0'
- services:
- registry:
- image: registry
- volumes:
- - ./registry-data:/var/lib/registry
- ui:
- image: joxit/docker-registry-ui:static
- ports:
- - 8080:80
- environment:
- - REGISTRY_TITLE=传智教育私有仓库
- - REGISTRY_URL=http://registry:5000
- depends_on:
- - registry
docker-compose up -d
配置Docker信任地址
我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:
- # 打开要修改的文件
- vi /etc/docker/daemon.json
- # 添加内容:
- "insecure-registries":["http://192.168.150.101:8080"]
- # 重加载
- systemctl daemon-reload
- # 重启docker
- systemctl restart docker
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。