最近公司的项目有一个是类似于集成功能的一个平台的实现,各个子系统的用户管理都需要在集成的平台上进行操作,因为一些不可抗拒因素,平台对用户管理的操作 是通过ajax发送请求来完成的,这样也出现了一个问题,就是当我一个子系统项目出现了报错500的时候,我本地的数据库仍然修改了用户相关的信息,也就导致了平台数据库的数据和子系统数据库的数据不同步的问题。所以就需要解决分布式事务,所以自己根据慕课网的一些视频资料先来使用Springcloud搭建一个分布式的项目,但是因为大部分的资料都是Springboot1x 所以项目实际的操作过程中还是出现了很多问题 在这里记录一下。
什么是springcloud
springcloud 是很多种组件的整合,基于Sringboot构建的,它的版本号也很有意思 是根据伦敦的地铁站名命名的,而且不同版本的springcloud引用的依赖是不太一样的。在我的这个测试的项目中主要是用到了以下的几个组件
- Eureka(服务注册中心)
- Zuul(网关)
- Hytrix(断路器)
- Feign(声明式web service客户端)
实例
项目的思路也比较明确
- 一个注册中心
registry
- 网关
gloxy
- 两个服务 User 和 Order 然后通过feign 使用User来调用Order服务
-
Hytrix可以单独部署的 我是和注册中心放到了一起
项目结构如图;
可以看到还有一个service 这个包之后用到的时候会再说,
注册中心的创建
首先创建registry
pom文件引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.emp.test</groupId>
<artifactId>service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.emp.test</groupId>
<artifactId>transcation</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
server
的配置也是比较简单的 在yml
文件中修改一下端口号 使用security
来配置一下用户名和密码就可以,详细的配置文件如下
server:
port: 8761
spring:
application:
name: registry
security:
user:
name: zhou
password: 12345678
freemarker:
prefer-file-system-access: false
eureka:
client:
register-with-eureka: false
fetch-registry: false
serviceUrl:
defaultZone: http://zhou:12345678@localhost:${server.port}/eureka/
2.0的security配置也和之前不太一样了 register-with-eureka: false fetch-registry: false
这两个配置 是为了防止server自己注册自己,禁止以下就好了 prefer-file-system-access
是因为有些情况写会因为freemarker的原因 无法显示eureka的首页,当然不显示首页的原因 还可能是因为springcloud启动设置的问题 这个在后边会单独解决一下。
yml
文件配置好之后 再在启动项中 添加
@SpringBootApplication
@EnableEurekaServer
@EnableHystrixDashboard
public class RegistryApplication {
public static void main(String[] args) {
SpringApplication.run(RegistryApplication.class, args);
}
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
getServlet这个方法是因为在2.0在使用的时候 直接启动访问hystrix 会显示一个no connct
的报错,具体的我忘记截图了 项目不会报错 页面上不会显示监控的效果 加上这个方法之后就ok了。
在2.0中因为security默认启用了csrf检验,要在eurekaServer端配置security的csrf检验为false。
所以在repostory中新建一个config文件
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
super.configure(http);
}
}
不然会报错
ERROR 11612 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : *****:***** - was unable to send heartbeat!
这样注册中心就完成了 接下来 完成网关的创建
zuul网关的创建
同样 先引入pom
文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后再配置文件中进行配置
server:
port: 8888
spring:
application:
name: proxy
eureka:
client:
serviceUrl:
defaultZone: http://zhou:12345678@localhost:8761/eureka
zuul:
routes:
userApi:
path: /test/**
stripPrefix: false
serviceId: user
management:
endpoints:
web:
exposure:
include: ["health","info","hystrix.stream"]
配置端口号 application name
和注册中心的路径
zuul
的那个配置是指我/test/**的所有连接 都会被当做是application name为user的服务下的链接地址,management
的配置呢是用于hytrix
的也是用来解决显示没有链接的问题的
然后在启动类中
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
}
服务的创建
我创建了两个服务 一个是User 一个是Order 需要User可以调用Order的服务,用的jpa和h2的内置数据库 没什么可记录的 也可以用jdbc和别的数据库来写自己的服务 主要是在User的服务中心 使用feign
,引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
配置文件
server:
port: 8001
spring:
application:
name: user
eureka:
client:
serviceUrl:
defaultZone: http://zhou:12345678@localhost:8761/eureka/
然后新建一个feignClient的接口 用来调用Order的方法
@FeignClient(value = "order",path = "/order/test")
public interface OrderClient extends OrderService {
@GetMapping("/{id}")
OrderDto getMyOrder(@PathVariable(name = "id")Long id);
}
然后再order的服务中 有一个对应的order/test/{id}
的Requestmapping
就可以了 我写的方法如下
@RequestMapping("/{id}")
public OrderDto getMyOrder(@PathVariable(name = "id")Long id){
Order order = orderService.getOrderById(id);
OrderDto orderDto = new OrderDto();
orderDto.setDetail(order.getDetail());
orderDto.setTitle(order.getTitle());
orderDto.setOrder_id(order.getOrder_id());
return orderDto;
}
可以看到这两个方法中 我都用到了一个OrderDto的类,
这个类就是我放在service中的类
就是把两个服务中 都会用到的实体类 给提取出来 然后在pom文件中引入包就可以了。
当然想法是比较好的 结果实际启动项目的时候就发生了报错 因为在不使用DTO的时候 项目已经顺利跑起来了 那么就是我DTO的引入问题,后来经过一系列的修改和测试 最后在修改了Registry
的启动后好用了,我是把最外的项目transcation
作为server
的parent
项目。
<groupId>com.emp.test</groupId>
<artifactId>transcation</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>service</module>
</modules>
然后加上了
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
然后将registry
中的内容修改为
<parent>
<groupId>com.emp.test</groupId>
<artifactId>transcation</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
然后启动四个项目
一个简单的Sprinboot+Springcloud的项目就搭建完成了