关于dubbo的那些坑

作为新手,第一次在项目中用到dubbo,经历的坑比较多,在此先记录一下,后面还会慢慢补充。欢迎小伙伴们提出意见和建议~
首先声明一下项目的架构等信息:
开发环境:Windows7、Linux、JDK1.8、Tomcat8(SpringBoot内置)、Redis、MySQL5.5、Nginx、Ngroke;
开发工具:IntelliJ IDEA、HBuilder;
架构情况:前后端分离。
后端:SpringBoot+SpringMVC+SpringDataJPA、SpringSession、SpringSecurity、Dubbo、Swagger2、Lombok;
前端技术有BootStrap、Vue.js、Axios、jQuery等。

废话不多说,直接正题:

坑1:dubbo无法注册服务

踩坑原因:

注解方式@service注册服务时,使用了@Transactional注解

解释:

目前dubbo版本中,被事务代理的业务逻辑对象不能通过使用@Service注解暴露服务,因为被cglib或者Java Proxy代理的类不能被dubbo的annotation扫描到@Service注解。

解决办法:

第一步:将Parant项目中pom.xml的dubbo的版本替换为2.5.7或以上。


   	//其他依赖的版本.....
    2.5.7
  

        com.alibaba
        dubbo
        
          
            org.springframework
            spring
          
          
            org.jboss.netty
            netty
          
        
        ${dubbo.version}
      

第二步:在要注册服务的业务层中com.alibaba.dubbo.config.annotation.Service注解中添加interfaceClass属性,指定要暴露服务的接口。

@org.springframework.stereotype.Service
//在alibaba的Service注解上添加interfaceClass属性,指定要暴露服务的接口。
@Service(interfaceClass = UserService.class)
public class UserServiceImpl implements UserService {
	//代码省略....
}

第三步:在需要添加@Transactional注解的业务层方法上添加@Transactional注解。

坑2:org.springframework.security.AccessDeniedException: Access is denied

踩坑原因:

数据库ROLE表中的权限名称没有加上“ROLE_”前缀。

解释:

当一个未授权的用户访问一个被保护的方法时,就会抛出org.springframework.security.AccessDeniedException: Access is denied。说明权限方面出现了问题。
在SpringSecurity具体配置路径的拦截和所需权限时,并不需要添加“ROLE_”前缀,因为底层已经拼接好了。

 /**
     * SpringSecurity的具体配置
     * @param http 相当于xml中http节点下的配置信息
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        //角色名在底层已经拼接了ROLE_,这里不用添加
        http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll().mvcMatchers("/depts").hasAnyRole("USER");
        //其它路径的拦截可以在这里配置
        //http.authorizeRequests().mvcMatchers("/hello/method02").hasAnyRole("MANAGER");
        //……
		//登录和登出的代码省略...
		//...

    }

但是,当我们从数据库获取用户和对应的权限信息时,需要获取的必须是带前缀的权限名称,这样才能匹配上。

 /**
     * 设置认证信息的提供方,这里使用基于数据库的方式提供认证信息的管理
     * @param auth 用于构建认证提供方
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        JdbcDaoImpl jdbcDao = new JdbcDaoImpl();
        jdbcDao.setDataSource(ApplicationContextUtils.getBean(DruidDataSource.class));
        jdbcDao.setUsersByUsernameQuery("select mobile as username,password,status as enabled from student where mobile=? and isSchoolManager = 1");
        jdbcDao.setAuthoritiesByUsernameQuery("select\n" +
                "                s.mobile as username,r.rname as authority\n" +
                "                from student s\n" +
                "                join user_role ur\n" +
                "                on s.sid=ur.sid and ur.status=1\n" +
                "                join role r\n" +
                "                on r.roleid=ur.roleid\n" +
                "                where s.mobile=? and isSchoolManager = 1");
        auth.userDetailsService(jdbcDao);
    }

解决办法:

将数据库ROLE表内的权限名称加上"ROLE_"前缀,例如“ROLE_STUDENT”。

坑3:前后端分离的结构中,使用SpringSecurity认证账户登录后,后端继续要求登录

踩坑原因:

采用的是axios进行前后端的数据交互。

解释:

前端代码登录操作会保存认证信息到服务器端,同样也会返回cookie信息到前端,后面的所有基于登录的一些数据访问操作需要将这个cookie继续往服务端携带,axios中默认不携带cookie信息到后台,这样每次请求后端都会产生一个新的cookie。

解决办法:

在登录或其他操作的axios中设置withCredentials: true。

axios.get(serverPath + "loginUser", {
			withCredentials: true
		}).then(function(res) {
			console.log("===================");
			console.log(res);
		});
axios.post(serverPath + "checkLoginInfo", {
		mobile: vm.mobile,
		password: vm.password
	}, {
		withCredentials: true,
		headers: { 
			'Content-Type': "application/x-www-form-urlencoded"
		},
		transformRequest: function(data) {
			var ret = '';
			for(var it in data) {
				ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
			}
			return ret
		}
	}).then(function(res) {
	
	});

坑4:在业务层注入远程服务时找不到对应的接口

	//发现这里的UserService接口找不到
    @Reference
    private UserService userService;

踩坑原因:

仔细检查后发现,在此模块的业务层的pom.xml中没有导入UserService接口所在模块的依赖。

解释:

尽管UserService接口所在模块已经将服务暴露出来,但是由于此模块的业务层的pom.xml中没有导入UserService接口所在模块的依赖,因此在此模块中无法找到UserService接口,导包时无法找到,自然就无法注入远程服务。

解决办法:

在此模块的业务层的pom.xml中导入UserService接口所在模块的依赖,然后就能使用import导入UserService接口啦。

坑5:‘org.springframework.data.domain.PageImpl’ could not be instantiated

踩坑原因:

根据这个异常所述,PageImpl不能实例化。
检查我的代码,如下所示,注入远程服务后,通过远程服务调用了getAllStudentsBySchoolId()方法

	//注入远程服务
    @Reference
    private UserService userService;
    
    @Override
    public JsonPageResult getAllStudentsBySchoolId(Integer did, Integer page, Integer pageSize) {
        return userService.getAllStudentsBySchoolId(did,page,pageSize);
    }

而getAllStudentsBySchoolId()方法内时通过将Pageable 作为参数写在repository的方法中来获取了包含分页+排序的结果集的Page对象。并将Page对象作为返回值返回。

		PageRequest pageRequest = new PageRequest(
                page-1, pageSize, new Sort(new Sort.Order(Sort.Direction.DESC, "applyTime")));
        Page students = userDao.findAllByDrivingSchool_Did(did,pageRequest);

这时就报了’org.springframework.data.domain.PageImpl’ could not be instantiated的异常

解释:

使用dubbo分布式时,传递的参数必须实现Serializable接口,否则就会报异常。而Page接口包括其父接口Slice都没有继承过Serializable接口。

解决办法:

可用某个实现了Serializable接口的对象将Page对象包装起来,然后传递包装对象。接收后再拆出得到Page对象。

你可能感兴趣的:(分布式系统)